Snap for 10453938 from 015bb821e8c49b6c0cbc59437f47735eb17aabdd to mainline-odp-release

Change-Id: I37899f01b342f0fe86e6c52d6d800f9997fe5fd1
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 3186f7e..3ee3b17 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,6 @@
 {
   "git": {
-    "sha1": "ad9d9330cf77b212915c2b175e90063c2cc3ac78"
-  }
-}
+    "sha1": "05046f9a93253be8116d149617e446df84889180"
+  },
+  "path_in_vcs": "quiche"
+}
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 109b229..ecbb83f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -61,7 +61,10 @@
         "liblibc",
         "liblibm",
         "liblog_rust",
+        "liboctets",
         "libring",
+        "libslab",
+        "libsmallvec",
     ],
     prefer_rlib: true,
     // For DnsResolver (Mainline module introduced in Q).
@@ -129,7 +132,10 @@
         "liblibm",
         "liblog_rust",
         "libmio",
+        "liboctets",
         "libring",
+        "libslab",
+        "libsmallvec",
         "liburl",
     ],
     data: [
diff --git a/CODEOWNERS b/CODEOWNERS
deleted file mode 100644
index 020aecf..0000000
--- a/CODEOWNERS
+++ /dev/null
@@ -1 +0,0 @@
-* @cloudflare/protocols
diff --git a/Cargo.lock b/Cargo.lock
index de30126..83b5b35 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,74 +1,62 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-[[package]]
-name = "aho-corasick"
-version = "0.7.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
-dependencies = [
- "memchr",
-]
+version = 3
 
 [[package]]
-name = "ansi_term"
-version = "0.11.0"
+name = "arrayvec"
+version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
-dependencies = [
- "winapi 0.3.9",
-]
-
-[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi",
- "libc",
- "winapi 0.3.9",
-]
+checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
 
 [[package]]
 name = "autocfg"
-version = "1.0.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
 
 [[package]]
 name = "bindgen"
-version = "0.57.0"
+version = "0.60.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd4865004a46a0aafb2a0a5eb19d3c9fc46ee5f063a6cfc605c69ac9ecf5263d"
+checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
 dependencies = [
  "bitflags",
  "cexpr",
  "clang-sys",
- "clap",
- "env_logger",
  "lazy_static",
  "lazycell",
- "log",
  "peeking_take_while",
  "proc-macro2",
  "quote",
  "regex",
  "rustc-hash",
  "shlex",
- "which",
 ]
 
 [[package]]
 name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "boring"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c713ad6d8d7a681a43870ac37b89efd2a08015ceb4b256d82707509c1f0b6bb"
+dependencies = [
+ "bitflags",
+ "boring-sys",
+ "foreign-types",
+ "lazy_static",
+ "libc",
+]
 
 [[package]]
 name = "boring-sys"
-version = "1.1.1"
+version = "2.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2416bce1bcabf0d7995ce0338ec2425b8766a4d5a39d758a3638008911642fc"
+checksum = "7663d3069437a5ccdb2b5f4f481c8b80446daea10fa8503844e89ac65fcdc363"
 dependencies = [
  "bindgen",
  "cmake",
@@ -76,42 +64,36 @@
 
 [[package]]
 name = "bumpalo"
-version = "3.7.0"
+version = "3.12.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631"
+checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
 
 [[package]]
 name = "cc"
-version = "1.0.68"
+version = "1.0.79"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787"
+checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
 
 [[package]]
 name = "cexpr"
-version = "0.4.0"
+version = "0.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
+checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
 dependencies = [
  "nom",
 ]
 
 [[package]]
 name = "cfg-if"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-
-[[package]]
-name = "cfg-if"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
 name = "clang-sys"
-version = "1.2.0"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c"
+checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
 dependencies = [
  "glob",
  "libc",
@@ -119,34 +101,19 @@
 ]
 
 [[package]]
-name = "clap"
-version = "2.33.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
-dependencies = [
- "ansi_term",
- "atty",
- "bitflags",
- "strsim 0.8.0",
- "textwrap",
- "unicode-width",
- "vec_map",
-]
-
-[[package]]
 name = "cmake"
-version = "0.1.45"
+version = "0.1.50"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855"
+checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
 dependencies = [
  "cc",
 ]
 
 [[package]]
 name = "darling"
-version = "0.13.0"
+version = "0.13.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12"
+checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c"
 dependencies = [
  "darling_core",
  "darling_macro",
@@ -154,41 +121,34 @@
 
 [[package]]
 name = "darling_core"
-version = "0.13.0"
+version = "0.13.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3"
+checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610"
 dependencies = [
  "fnv",
  "ident_case",
  "proc-macro2",
  "quote",
- "strsim 0.10.0",
- "syn",
+ "strsim",
+ "syn 1.0.109",
 ]
 
 [[package]]
 name = "darling_macro"
-version = "0.13.0"
+version = "0.13.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc"
+checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835"
 dependencies = [
  "darling_core",
  "quote",
- "syn",
+ "syn 1.0.109",
 ]
 
 [[package]]
-name = "env_logger"
-version = "0.8.3"
+name = "data-encoding"
+version = "2.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f"
-dependencies = [
- "atty",
- "humantime",
- "log",
- "regex",
- "termcolor",
-]
+checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb"
 
 [[package]]
 name = "fnv"
@@ -197,47 +157,43 @@
 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
-name = "fuchsia-zircon"
-version = "0.3.3"
+name = "foreign-types"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
 dependencies = [
- "bitflags",
- "fuchsia-zircon-sys",
+ "foreign-types-macros",
+ "foreign-types-shared",
 ]
 
 [[package]]
-name = "fuchsia-zircon-sys"
-version = "0.3.3"
+name = "foreign-types-macros"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.11",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
 
 [[package]]
 name = "glob"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
 
 [[package]]
 name = "hashbrown"
-version = "0.9.1"
+version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
-
-[[package]]
-name = "hermit-abi"
-version = "0.1.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "humantime"
-version = "2.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 
 [[package]]
 name = "ident_case"
@@ -258,49 +214,30 @@
 
 [[package]]
 name = "indexmap"
-version = "1.6.2"
+version = "1.9.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3"
+checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
 dependencies = [
  "autocfg",
  "hashbrown",
 ]
 
 [[package]]
-name = "iovec"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
-dependencies = [
- "libc",
-]
-
-[[package]]
 name = "itoa"
-version = "0.4.7"
+version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
 
 [[package]]
 name = "js-sys"
-version = "0.3.51"
+version = "0.3.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062"
+checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
 dependencies = [
  "wasm-bindgen",
 ]
 
 [[package]]
-name = "kernel32-sys"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-dependencies = [
- "winapi 0.2.8",
- "winapi-build",
-]
-
-[[package]]
 name = "lazy_static"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -314,104 +251,95 @@
 
 [[package]]
 name = "libc"
-version = "0.2.95"
+version = "0.2.140"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36"
+checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
 
 [[package]]
 name = "libloading"
-version = "0.7.0"
+version = "0.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
+checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
 dependencies = [
- "cfg-if 1.0.0",
- "winapi 0.3.9",
+ "cfg-if",
+ "winapi",
 ]
 
 [[package]]
 name = "libm"
-version = "0.2.1"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
+checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb"
 
 [[package]]
 name = "log"
-version = "0.4.14"
+version = "0.4.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
 ]
 
 [[package]]
 name = "matches"
-version = "0.1.8"
+version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
+checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
 
 [[package]]
 name = "memchr"
-version = "2.4.0"
+version = "2.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "minimal-lexical"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
 
 [[package]]
 name = "mio"
-version = "0.6.23"
+version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
+checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
 dependencies = [
- "cfg-if 0.1.10",
- "fuchsia-zircon",
- "fuchsia-zircon-sys",
- "iovec",
- "kernel32-sys",
  "libc",
  "log",
- "miow",
- "net2",
- "slab",
- "winapi 0.2.8",
-]
-
-[[package]]
-name = "miow"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
-dependencies = [
- "kernel32-sys",
- "net2",
- "winapi 0.2.8",
- "ws2_32-sys",
-]
-
-[[package]]
-name = "net2"
-version = "0.2.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
-dependencies = [
- "cfg-if 0.1.10",
- "libc",
- "winapi 0.3.9",
+ "wasi",
+ "windows-sys",
 ]
 
 [[package]]
 name = "nom"
-version = "5.1.2"
+version = "7.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
 dependencies = [
  "memchr",
- "version_check",
+ "minimal-lexical",
 ]
 
 [[package]]
-name = "once_cell"
-version = "1.7.2"
+name = "num-traits"
+version = "0.2.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
+checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "octets"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a74f2cda724d43a0a63140af89836d4e7db6138ef67c9f96d3a0f0150d05000"
+
+[[package]]
+name = "once_cell"
+version = "1.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
 
 [[package]]
 name = "peeking_take_while"
@@ -427,67 +355,71 @@
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.27"
+version = "1.0.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
+checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
 dependencies = [
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
 name = "qlog"
-version = "0.4.0"
+version = "0.9.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8777d5490145d6907198d48b3a907447689ce80e071b3d8a16a9d9fb3df02bc1"
+checksum = "321df7a3199d152be256a416096136191e88b7716f1e2e4c8c05b9f77ffb648b"
 dependencies = [
  "serde",
  "serde_derive",
  "serde_json",
  "serde_with",
+ "smallvec",
 ]
 
 [[package]]
 name = "quiche"
-version = "0.9.0"
+version = "0.17.1"
 dependencies = [
- "boring-sys",
+ "boring",
  "cmake",
+ "foreign-types-shared",
  "lazy_static",
  "libc",
  "libm",
  "log",
  "mio",
+ "octets",
  "qlog",
  "ring",
+ "sfv",
+ "slab",
+ "smallvec",
  "url",
- "winapi 0.3.9",
+ "winapi",
 ]
 
 [[package]]
 name = "quote"
-version = "1.0.9"
+version = "1.0.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
 dependencies = [
  "proc-macro2",
 ]
 
 [[package]]
 name = "regex"
-version = "1.5.4"
+version = "1.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
+checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
 dependencies = [
- "aho-corasick",
- "memchr",
  "regex-syntax",
 ]
 
 [[package]]
 name = "regex-syntax"
-version = "0.6.25"
+version = "0.6.29"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
+checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
 
 [[package]]
 name = "ring"
@@ -501,7 +433,17 @@
  "spin",
  "untrusted",
  "web-sys",
- "winapi 0.3.9",
+ "winapi",
+]
+
+[[package]]
+name = "rust_decimal"
+version = "1.29.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26bd36b60561ee1fb5ec2817f198b6fd09fa571c897a5e86d1487cfc2b096dfc"
+dependencies = [
+ "arrayvec",
+ "num-traits",
 ]
 
 [[package]]
@@ -511,42 +453,36 @@
 checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
 
 [[package]]
-name = "rustversion"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088"
-
-[[package]]
 name = "ryu"
-version = "1.0.5"
+version = "1.0.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
 
 [[package]]
 name = "serde"
-version = "1.0.126"
+version = "1.0.159"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03"
+checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.126"
+version = "1.0.159"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43"
+checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 2.0.11",
 ]
 
 [[package]]
 name = "serde_json"
-version = "1.0.64"
+version = "1.0.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
+checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
 dependencies = [
  "indexmap",
  "itoa",
@@ -556,38 +492,60 @@
 
 [[package]]
 name = "serde_with"
-version = "1.9.2"
+version = "1.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e3132bd01cfb74aac8b1b10083ad1f38dbf756df3176d5e63dd91e3f62a87f5"
+checksum = "678b5a069e50bf00ecd22d0cd8ddf7c236f68581b03db652061ed5eb13a312ff"
 dependencies = [
- "rustversion",
  "serde",
  "serde_with_macros",
 ]
 
 [[package]]
 name = "serde_with_macros"
-version = "1.4.2"
+version = "1.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1569374bd54623ec8bd592cf22ba6e03c0f177ff55fbc8c29a49e296e7adecf"
+checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082"
 dependencies = [
  "darling",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "sfv"
+version = "0.9.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4f1641177943b4e6faf7622463ae6dfe0f143eb88799e91e2e2e68ede568af5"
+dependencies = [
+ "data-encoding",
+ "indexmap",
+ "rust_decimal",
 ]
 
 [[package]]
 name = "shlex"
-version = "0.1.1"
+version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2"
+checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
 
 [[package]]
 name = "slab"
-version = "0.4.3"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527"
+checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+dependencies = [
+ "serde",
+]
 
 [[package]]
 name = "spin"
@@ -597,91 +555,69 @@
 
 [[package]]
 name = "strsim"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
-
-[[package]]
-name = "strsim"
 version = "0.10.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
 
 [[package]]
 name = "syn"
-version = "1.0.72"
+version = "1.0.109"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
 dependencies = [
  "proc-macro2",
  "quote",
- "unicode-xid",
+ "unicode-ident",
 ]
 
 [[package]]
-name = "termcolor"
-version = "1.1.2"
+name = "syn"
+version = "2.0.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
+checksum = "21e3787bb71465627110e7d87ed4faaa36c1f61042ee67badb9e2ef173accc40"
 dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
-dependencies = [
- "unicode-width",
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
 ]
 
 [[package]]
 name = "tinyvec"
-version = "1.2.0"
+version = "1.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342"
+checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
 dependencies = [
  "tinyvec_macros",
 ]
 
 [[package]]
 name = "tinyvec_macros"
-version = "0.1.0"
+version = "0.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
+checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.5"
+version = "0.3.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0"
-dependencies = [
- "matches",
-]
+checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
 
 [[package]]
 name = "unicode-normalization"
-version = "0.1.19"
+version = "0.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9"
+checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
 dependencies = [
  "tinyvec",
 ]
 
 [[package]]
-name = "unicode-width"
-version = "0.1.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
-
-[[package]]
 name = "untrusted"
 version = "0.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -699,47 +635,41 @@
 ]
 
 [[package]]
-name = "vec_map"
-version = "0.8.2"
+name = "wasi"
+version = "0.11.0+wasi-snapshot-preview1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
-
-[[package]]
-name = "version_check"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.74"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd"
+checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
 dependencies = [
- "cfg-if 1.0.0",
+ "cfg-if",
  "wasm-bindgen-macro",
 ]
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.74"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900"
+checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
 dependencies = [
  "bumpalo",
- "lazy_static",
  "log",
+ "once_cell",
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.74"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4"
+checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -747,49 +677,34 @@
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.74"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97"
+checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
 dependencies = [
  "proc-macro2",
  "quote",
- "syn",
+ "syn 1.0.109",
  "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.74"
+version = "0.2.84"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f"
+checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
 
 [[package]]
 name = "web-sys"
-version = "0.3.51"
+version = "0.3.61"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582"
+checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
 ]
 
 [[package]]
-name = "which"
-version = "3.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "winapi"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
-
-[[package]]
 name = "winapi"
 version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -800,38 +715,79 @@
 ]
 
 [[package]]
-name = "winapi-build"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
-
-[[package]]
 name = "winapi-i686-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
 
 [[package]]
-name = "winapi-util"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
-dependencies = [
- "winapi 0.3.9",
-]
-
-[[package]]
 name = "winapi-x86_64-pc-windows-gnu"
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
 [[package]]
-name = "ws2_32-sys"
-version = "0.2.1"
+name = "windows-sys"
+version = "0.45.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
 dependencies = [
- "winapi 0.2.8",
- "winapi-build",
+ "windows-targets",
 ]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
diff --git a/Cargo.toml b/Cargo.toml
index 62b856a..86457f9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,38 +3,68 @@
 # When uploading crates to the registry Cargo will automatically
 # "normalize" Cargo.toml files for maximal compatibility
 # with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
 #
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
 
 [package]
 edition = "2018"
+rust-version = "1.66"
 name = "quiche"
-version = "0.9.0"
+version = "0.17.1"
 authors = ["Alessandro Ghedini <alessandro@ghedini.me>"]
 build = "src/build.rs"
-include = ["/*.md", "/*.toml", "/CODEOWNERS", "/COPYING", "/benches", "/deps/boringssl/**/*.[chS]", "/deps/boringssl/**/*.asm", "/deps/boringssl/src/**/*.cc", "/deps/boringssl/**/CMakeLists.txt", "/deps/boringssl/**/sources.cmake", "/deps/boringssl/LICENSE", "/examples", "/include", "/quiche.svg", "/src"]
+include = [
+    "/*.md",
+    "/*.toml",
+    "/COPYING",
+    "/deps/boringssl/**/*.[chS]",
+    "/deps/boringssl/**/*.asm",
+    "/deps/boringssl/src/**/*.cc",
+    "/deps/boringssl/**/CMakeLists.txt",
+    "/deps/boringssl/**/sources.cmake",
+    "/deps/boringssl/LICENSE",
+    "/examples",
+    "/include",
+    "/quiche.svg",
+    "/src",
+]
 description = "🥧 Savoury implementation of the QUIC transport protocol and HTTP/3"
 readme = "README.md"
-keywords = ["quic", "http3"]
+keywords = [
+    "quic",
+    "http3",
+]
 categories = ["network-programming"]
 license = "BSD-2-Clause"
 repository = "https://github.com/cloudflare/quiche"
+
 [package.metadata.docs.rs]
 no-default-features = true
-[profile.bench]
-debug = true
-
-[profile.release]
-debug = true
+features = [
+    "boringssl-boring-crate",
+    "qlog",
+]
+rustdoc-args = [
+    "--cfg",
+    "docsrs",
+]
 
 [lib]
-crate-type = ["lib", "staticlib", "cdylib"]
-[dependencies.boring-sys]
-version = "1.0.2"
+crate-type = [
+    "lib",
+    "staticlib",
+    "cdylib",
+]
+
+[dependencies.boring]
+version = "2.0.0"
+optional = true
+
+[dependencies.foreign-types-shared]
+version = "0.3.0"
 optional = true
 
 [dependencies.lazy_static]
@@ -50,27 +80,59 @@
 version = "0.4"
 features = ["std"]
 
+[dependencies.octets]
+version = "0.2"
+
 [dependencies.qlog]
-version = "0.4"
+version = "0.9"
 optional = true
 
 [dependencies.ring]
 version = "0.16"
+
+[dependencies.sfv]
+version = "0.9"
+optional = true
+
+[dependencies.slab]
+version = "0.4"
+
+[dependencies.smallvec]
+version = "1.10"
+features = [
+    "serde",
+    "union",
+]
+
 [dev-dependencies.mio]
-version = "0.6"
+version = "0.8"
+features = [
+    "net",
+    "os-poll",
+]
 
 [dev-dependencies.url]
 version = "1"
+
 [build-dependencies.cmake]
 version = "0.1"
 
 [features]
+boringssl-boring-crate = [
+    "boring",
+    "foreign-types-shared",
+]
 boringssl-vendored = []
 default = ["boringssl-vendored"]
 ffi = []
 fuzzing = []
-ndk-old-gcc = []
 pkg-config-meta = []
+
 [target."cfg(windows)".dependencies.winapi]
 version = "0.3"
-features = ["wincrypt"]
+features = [
+    "wincrypt",
+    "ws2def",
+    "ws2ipdef",
+    "ws2tcpip",
+]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 12bddf8..0eb2417 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
 [package]
 name = "quiche"
-version = "0.9.0"
+version = "0.17.1"
 authors = ["Alessandro Ghedini <alessandro@ghedini.me>"]
 edition = "2018"
 build = "src/build.rs"
@@ -10,12 +10,11 @@
 keywords = ["quic", "http3"]
 categories = ["network-programming"]
 license = "BSD-2-Clause"
+rust-version = "1.66"
 include = [
     "/*.md",
     "/*.toml",
-    "/CODEOWNERS",
     "/COPYING",
-    "/benches",
     "/deps/boringssl/**/*.[chS]",
     "/deps/boringssl/**/*.asm",
     "/deps/boringssl/src/**/*.cc",
@@ -31,23 +30,25 @@
 [features]
 default = ["boringssl-vendored"]
 
-# Build vendored BoringSSL library.
+# Build the vendored BoringSSL library.
 boringssl-vendored = []
 
+# Use the BoringSSL library provided by the boring crate.
+boringssl-boring-crate = ["boring", "foreign-types-shared"]
+
 # Generate pkg-config metadata file for libquiche.
 pkg-config-meta = []
 
 # Equivalent to "--cfg fuzzing", but can also be checked in build.rs.
 fuzzing = []
 
-# For building with Android NDK < 18 and GCC.
-ndk-old-gcc = []
-
-# Expose the FFI API.
+# Build and expose the FFI API.
 ffi = []
 
 [package.metadata.docs.rs]
 no-default-features = true
+features = ["boringssl-boring-crate", "qlog"]
+rustdoc-args = ["--cfg", "docsrs"]
 
 [build-dependencies]
 cmake = "0.1"
@@ -57,22 +58,21 @@
 libc = "0.2"
 libm = "0.2"
 ring = "0.16"
+slab = "0.4"
 lazy_static = "1"
-boring-sys = { version = "1.0.2", optional = true }
-qlog = { version = "0.4", path = "tools/qlog", optional = true }
+octets = { version = "0.2", path = "../octets" }
+boring = { version = "2.0.0", optional = true }
+foreign-types-shared = { version = "0.3.0", optional = true }
+qlog = { version = "0.9", path = "../qlog", optional = true }
+sfv = { version = "0.9", optional = true }
+smallvec = { version = "1.10", features = ["serde", "union"] }
 
 [target."cfg(windows)".dependencies]
-winapi = { version = "0.3", features = ["wincrypt"] }
+winapi = { version = "0.3", features = ["wincrypt", "ws2def", "ws2ipdef", "ws2tcpip"] }
 
 [dev-dependencies]
-mio = "0.6"
+mio = { version = "0.8", features = ["net", "os-poll"] }
 url = "1"
 
-[profile.bench]
-debug = true
-
-[profile.release]
-debug = true
-
 [lib]
 crate-type = ["lib", "staticlib", "cdylib"]
diff --git a/METADATA b/METADATA
index 218f6f3..95eeae8 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/quiche
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
 name: "quiche"
 description: "\360\237\245\247 Savoury implementation of the QUIC transport protocol and HTTP/3"
 third_party {
@@ -7,13 +11,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/quiche/quiche-0.9.0.crate"
+    value: "https://static.crates.io/crates/quiche/quiche-0.17.1.crate"
   }
-  version: "0.9.0"
+  version: "0.17.1"
   license_type: NOTICE
   last_upgrade_date {
-    year: 2021
-    month: 6
-    day: 8
+    year: 2023
+    month: 4
+    day: 7
   }
 }
diff --git a/OWNERS b/OWNERS
index 46fc303..45dc4dd 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1 +1 @@
-include platform/prebuilts/rust:/OWNERS
+include platform/prebuilts/rust:master:/OWNERS
diff --git a/README.md b/README.md
index 2be56f2..ece548c 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
 [![crates.io](https://img.shields.io/crates/v/quiche.svg)](https://crates.io/crates/quiche)
 [![docs.rs](https://docs.rs/quiche/badge.svg)](https://docs.rs/quiche)
 [![license](https://img.shields.io/github/license/cloudflare/quiche.svg)](https://opensource.org/licenses/BSD-2-Clause)
-![build](https://img.shields.io/github/workflow/status/cloudflare/quiche/Stable)
+![build](https://img.shields.io/github/actions/workflow/status/cloudflare/quiche/stable.yml?branch=master)
 
 [quiche] is an implementation of the QUIC transport protocol and HTTP/3 as
 specified by the [IETF]. It provides a low level API for processing QUIC packets
@@ -26,18 +26,22 @@
 [cloudflare-quic.com](https://cloudflare-quic.com) website can be used for
 testing and experimentation.
 
+### Android
+
+Android's DNS resolver uses quiche to [implement DNS over HTTP/3][android-http3].
+
 ### curl
 
 quiche can be [integrated into curl][curl-http3] to provide support for HTTP/3.
 
 ### NGINX (unofficial)
 
-quiche can be [integrated into NGINX][nginx-http3] using an unofficial patch to
+quiche can be [integrated into NGINX](nginx/) using an unofficial patch to
 provide support for HTTP/3.
 
 [cloudflare-http3]: https://blog.cloudflare.com/http3-the-past-present-and-future/
+[android-http3]: https://security.googleblog.com/2022/07/dns-over-http3-in-android.html
 [curl-http3]: https://github.com/curl/curl/blob/master/docs/HTTP3.md#quiche-version
-[nginx-http3]: https://github.com/cloudflare/quiche/tree/master/extras/nginx
 
 Getting Started
 ---------------
@@ -45,20 +49,18 @@
 ### Command-line apps
 
 Before diving into the quiche API, here are a few examples on how to use the
-quiche tools provided as part of the [quiche-apps](tools/apps/) crate.
+quiche tools provided as part of the [quiche-apps](apps/) crate.
 
 After cloning the project according to the command mentioned in the [building](#building) section, the client can be run as follows:
 
 ```bash
- $ cargo run --manifest-path=tools/apps/Cargo.toml --bin quiche-client -- https://cloudflare-quic.com/
+ $ cargo run --bin quiche-client -- https://cloudflare-quic.com/
 ```
 
 while the server can be run as follows:
 
 ```bash
- $ cargo run --manifest-path=tools/apps/Cargo.toml --bin quiche-server -- \
-      --cert tools/apps/src/bin/cert.crt \
-      --key tools/apps/src/bin/cert.key
+ $ cargo run --bin quiche-server -- --cert apps/src/bin/cert.crt --key apps/src/bin/cert.key
 ```
 
 (note that the certificate provided is self-signed and should not be used in
@@ -67,27 +69,55 @@
 Use the `--help` command-line flag to get a more detailed description of each
 tool's options.
 
-### Connection setup
+### Configuring connections
 
 The first step in establishing a QUIC connection using quiche is creating a
-configuration object:
+[`Config`] object:
 
 ```rust
-let config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+config.set_application_protos(&[b"example-proto"]);
+
+// Additional configuration specific to application and use case...
 ```
 
-This is shared among multiple connections and can be used to configure a
-QUIC endpoint.
+The [`Config`] object controls important aspects of the QUIC connection such
+as QUIC version, ALPN IDs, flow control, congestion control, idle timeout
+and other properties or features.
+
+QUIC is a general-purpose transport protocol and there are several
+configuration properties where there is no reasonable default value. For
+example, the permitted number of concurrent streams of any particular type
+is dependent on the application running over QUIC, and other use-case
+specific concerns.
+
+quiche defaults several properties to zero, applications most likely need
+to set these to something else to satisfy their needs using the following:
+
+- [`set_initial_max_streams_bidi()`]
+- [`set_initial_max_streams_uni()`]
+- [`set_initial_max_data()`]
+- [`set_initial_max_stream_data_bidi_local()`]
+- [`set_initial_max_stream_data_bidi_remote()`]
+- [`set_initial_max_stream_data_uni()`]
+
+[`Config`] also holds TLS configuration. This can be changed by mutators on
+the an existing object, or by constructing a TLS context manually and
+creating a configuration using [`with_boring_ssl_ctx()`].
+
+A configuration object can be shared among multiple connections.
+
+### Connection setup
 
 On the client-side the [`connect()`] utility function can be used to create
 a new connection, while [`accept()`] is for servers:
 
 ```rust
 // Client connection.
-let conn = quiche::connect(Some(&server_name), &scid, &mut config)?;
+let conn = quiche::connect(Some(&server_name), &scid, local, peer, &mut config)?;
 
 // Server connection.
-let conn = quiche::accept(&scid, None, &mut config)?;
+let conn = quiche::accept(&scid, None, local, peer, &mut config)?;
 ```
 
 ### Handling incoming packets
@@ -96,10 +126,14 @@
 incoming packets that belong to that connection from the network:
 
 ```rust
-loop {
-    let read = socket.recv(&mut buf).unwrap();
+let to = socket.local_addr().unwrap();
 
-    let read = match conn.recv(&mut buf[..read]) {
+loop {
+    let (read, from) = socket.recv_from(&mut buf).unwrap();
+
+    let recv_info = quiche::RecvInfo { from, to };
+
+    let read = match conn.recv(&mut buf[..read], recv_info) {
         Ok(v) => v,
 
         Err(e) => {
@@ -117,7 +151,7 @@
 
 ```rust
 loop {
-    let write = match conn.send(&mut out) {
+    let (write, send_info) = match conn.send(&mut out) {
         Ok(v) => v,
 
         Err(quiche::Error::Done) => {
@@ -131,7 +165,7 @@
         },
     };
 
-    socket.send(&out[..write]).unwrap();
+    socket.send_to(&out[..write], &send_info.to).unwrap();
 }
 ```
 
@@ -154,7 +188,7 @@
 
 // Send more packets as needed after timeout.
 loop {
-    let write = match conn.send(&mut out) {
+    let (write, send_info) = match conn.send(&mut out) {
         Ok(v) => v,
 
         Err(quiche::Error::Done) => {
@@ -168,10 +202,29 @@
         },
     };
 
-    socket.send(&out[..write]).unwrap();
+    socket.send_to(&out[..write], &send_info.to).unwrap();
 }
 ```
 
+#### Pacing
+
+It is recommended that applications [pace] sending of outgoing packets to
+avoid creating packet bursts that could cause short-term congestion and
+losses in the network.
+
+quiche exposes pacing hints for outgoing packets through the [`at`] field
+of the [`SendInfo`] structure that is returned by the [`send()`] method.
+This field represents the time when a specific packet should be sent into
+the network.
+
+Applications can use these hints by artificially delaying the sending of
+packets through platform-specific mechanisms (such as the [`SO_TXTIME`]
+socket option on Linux), or custom methods (for example by using user-space
+timers).
+
+[pace]: https://datatracker.ietf.org/doc/html/rfc9002#section-7.7
+[`SO_TXTIME`]: https://man7.org/linux/man-pages/man8/tc-etf.8.html
+
 ### Sending and receiving stream data
 
 After some back and forth, the connection will complete its handshake and
@@ -210,6 +263,14 @@
 The quiche [HTTP/3 module] provides a high level API for sending and
 receiving HTTP requests and responses on top of the QUIC transport protocol.
 
+[`Config`]: https://docs.quic.tech/quiche/struct.Config.html
+[`set_initial_max_streams_bidi()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_streams_bidi
+[`set_initial_max_streams_uni()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_streams_uni
+[`set_initial_max_data()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_data
+[`set_initial_max_stream_data_bidi_local()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_local
+[`set_initial_max_stream_data_bidi_remote()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_remote
+[`set_initial_max_stream_data_uni()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_stream_data_uni
+[`with_boring_ssl_ctx()`]: https://docs.quic.tech/quiche/struct.Config.html#method.with_boring_ssl_ctx
 [`connect()`]: https://docs.quic.tech/quiche/fn.connect.html
 [`accept()`]: https://docs.quic.tech/quiche/fn.accept.html
 [`recv()`]: https://docs.quic.tech/quiche/struct.Connection.html#method.recv
@@ -221,11 +282,11 @@
 [`stream_recv()`]: https://docs.quic.tech/quiche/struct.Connection.html#method.stream_recv
 [HTTP/3 module]: https://docs.quic.tech/quiche/h3/index.html
 
-Have a look at the [examples/] directory for more complete examples on how to use
-the quiche API, including examples on how to use quiche in C/C++ applications
-(see below for more information).
+Have a look at the [quiche/examples/] directory for more complete examples on
+how to use the quiche API, including examples on how to use quiche in C/C++
+applications (see below for more information).
 
-[examples/]: examples/
+[examples/]: quiche/examples/
 
 Calling quiche from C/C++
 -------------------------
@@ -242,12 +303,12 @@
 Note that in order to enable the FFI API, the ``ffi`` feature must be enabled (it
 is disabled by default), by passing ``--features ffi`` to ``cargo``.
 
-[thin C API]: https://github.com/cloudflare/quiche/blob/master/include/quiche.h
+[thin C API]: https://github.com/cloudflare/quiche/blob/master/quiche/include/quiche.h
 
 Building
 --------
 
-quiche requires Rust 1.50 or later to build. The latest stable Rust release can
+quiche requires Rust 1.66 or later to build. The latest stable Rust release can
 be installed using [rustup](https://rustup.rs/).
 
 Once the Rust build environment is setup, the quiche source code can be fetched
@@ -288,38 +349,34 @@
 
 ### Building for Android
 
-To build quiche for Android, you need the following:
+Building quiche for Android (NDK version 19 or higher, 21 recommended), can be
+done using [cargo-ndk] (v2.0 or later).
 
-- Install the [Android NDK] (13b or higher), using Android Studio or directly.
-- Set `ANDROID_NDK_HOME` environment variable to NDK path, e.g.
+First the [Android NDK] needs to be installed, either using Android Studio or
+directly, and the `ANDROID_NDK_HOME` environment variable needs to be set to the
+NDK installation path, e.g.:
 
 ```bash
  $ export ANDROID_NDK_HOME=/usr/local/share/android-ndk
 ```
 
-- Install the Rust toolchain for Android architectures needed:
+Then the Rust toolchain for the Android architectures needed can be installed as
+follows:
 
 ```bash
- $ rustup target add aarch64-linux-android arm-linux-androideabi armv7-linux-androideabi i686-linux-android x86_64-linux-android
+ $ rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
 ```
 
 Note that the minimum API level is 21 for all target architectures.
 
-Depending on the NDK version used, you can take one of the following procedures:
-
-[Android NDK]: https://developer.android.com/ndk
-
-#### NDK version >= 19
-
-For NDK version 19 or higher (21 recommended), you can build in a simpler
-way using [cargo-ndk]. You need to install [cargo-ndk] (v2.0 or later) first.
+[cargo-ndk] (v2.0 or later) also needs to be installed:
 
 ```bash
  $ cargo install cargo-ndk
 ```
 
-You can build the quiche library using the following procedure. Note that
-`-t <architecture>` and `-p <NDK version>` are mandatory.
+Finally the quiche library can be built using the following procedure. Note that
+the `-t <architecture>` and `-p <NDK version>` options are mandatory.
 
 ```bash
  $ cargo ndk -t arm64-v8a -p 21 -- build --features ffi
@@ -327,39 +384,10 @@
 
 See [build_android_ndk19.sh] for more information.
 
-Note that building with NDK version 18 appears to be broken.
-
+[Android NDK]: https://developer.android.com/ndk
 [cargo-ndk]: https://docs.rs/crate/cargo-ndk
 [build_android_ndk19.sh]: https://github.com/cloudflare/quiche/blob/master/tools/android/build_android_ndk19.sh
 
-#### NDK version < 18
-
-If you need to use NDK version < 18 (gcc), you can build quiche in the following way.
-
-To prepare the cross-compiling toolchain, run the following command:
-
-```bash
- $ tools/android/setup_android.sh
-```
-
-It will create a standalone toolchain for arm64/arm/x86 architectures under the
-`$TOOLCHAIN_DIR/arch` directory. If you didn't set `TOOLCHAIN_DIR` environment
-variable, the current directory will be used.
-
-After it run successfully, run the following script to build libquiche:
-
-```bash
- $ tools/android/build_android.sh --features ndk-old-gcc
-```
-
-It will build binaries for aarch64, armv7 and i686. You can pass parameters to
-this script for cargo build. For example if you want to build a release binary
-with verbose logs, do the following:
-
-```bash
- $ tools/android/build_android.sh --features ndk-old-gcc --release -vv
-```
-
 ### Building for iOS
 
 To build quiche for iOS, you need the following:
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 1d3032b..d394a8a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,18 +1,17 @@
 // Generated by update_crate_tests.py for tests that depend on this crate.
 {
-  "presubmit": [
+  "imports": [
     {
-      "name": "doh_unit_test"
-    },
+      "path": "packages/modules/DnsResolver"
+    }
+  ],
+  "presubmit": [
     {
       "name": "quiche_device_test_src_lib"
     }
   ],
   "presubmit-rust": [
     {
-      "name": "doh_unit_test"
-    },
-    {
       "name": "quiche_device_test_src_lib"
     }
   ]
diff --git a/clippy.toml b/clippy.toml
deleted file mode 100644
index b599b53..0000000
--- a/clippy.toml
+++ /dev/null
@@ -1 +0,0 @@
-cognitive-complexity-threshold = 100
diff --git a/deps/boringssl/CMakeLists.txt b/deps/boringssl/CMakeLists.txt
index 1645a26..3795e7b 100644
--- a/deps/boringssl/CMakeLists.txt
+++ b/deps/boringssl/CMakeLists.txt
@@ -4,7 +4,7 @@
 
 # This file is created by generate_build_files.py. Do not edit manually.
 
-cmake_minimum_required(VERSION 3.0)
+cmake_minimum_required(VERSION 3.5)
 
 project(BoringSSL LANGUAGES C CXX)
 
@@ -18,12 +18,7 @@
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
   endif()
 
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fno-common")
-  if((CMAKE_C_COMPILER_VERSION VERSION_GREATER "4.8.99") OR CLANG)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
-  else()
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
-  endif()
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fno-common -std=c11")
 endif()
 
 # pthread_rwlock_t requires a feature flag.
@@ -53,7 +48,7 @@
 # builds.
 if(NOT OPENSSL_NO_ASM AND CMAKE_OSX_ARCHITECTURES)
   list(LENGTH CMAKE_OSX_ARCHITECTURES NUM_ARCHES)
-  if(NOT ${NUM_ARCHES} EQUAL 1)
+  if(NOT NUM_ARCHES EQUAL 1)
     message(FATAL_ERROR "Universal binaries not supported.")
   endif()
   list(GET CMAKE_OSX_ARCHITECTURES 0 CMAKE_SYSTEM_PROCESSOR)
@@ -62,36 +57,36 @@
 if(OPENSSL_NO_ASM)
   add_definitions(-DOPENSSL_NO_ASM)
   set(ARCH "generic")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
   set(ARCH "x86_64")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "amd64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64")
   set(ARCH "x86_64")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
   # cmake reports AMD64 on Windows, but we might be building for 32-bit.
   if(CMAKE_SIZEOF_VOID_P EQUAL 8)
     set(ARCH "x86_64")
   else()
     set(ARCH "x86")
   endif()
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86")
   set(ARCH "x86")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i386")
   set(ARCH "x86")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i686")
   set(ARCH "x86")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
   set(ARCH "aarch64")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
   set(ARCH "aarch64")
 # Apple A12 Bionic chipset which is added in iPhone XS/XS Max/XR uses arm64e architecture.
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64e")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64e")
   set(ARCH "aarch64")
-elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm*")
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm*")
   set(ARCH "arm")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "mips")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "mips")
   # Just to avoid the “unknown processor” error.
   set(ARCH "generic")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64le")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le")
   set(ARCH "ppc64le")
 else()
   message(FATAL_ERROR "Unknown processor:" ${CMAKE_SYSTEM_PROCESSOR})
@@ -344,9 +339,9 @@
   win-x86_64/crypto/test/trampoline-x86_64.asm
 )
 
-if(APPLE AND ${ARCH} STREQUAL "aarch64")
+if(APPLE AND ARCH STREQUAL "aarch64")
   set(CRYPTO_ARCH_SOURCES ${CRYPTO_ios_aarch64_SOURCES})
-elseif(APPLE AND ${ARCH} STREQUAL "arm")
+elseif(APPLE AND ARCH STREQUAL "arm")
   set(CRYPTO_ARCH_SOURCES ${CRYPTO_ios_arm_SOURCES})
 elseif(APPLE)
   set(CRYPTO_ARCH_SOURCES ${CRYPTO_mac_${ARCH}_SOURCES})
@@ -373,6 +368,7 @@
   src/crypto/asn1/a_object.c
   src/crypto/asn1/a_octet.c
   src/crypto/asn1/a_print.c
+  src/crypto/asn1/a_strex.c
   src/crypto/asn1/a_strnid.c
   src/crypto/asn1/a_time.c
   src/crypto/asn1/a_type.c
@@ -467,7 +463,6 @@
   src/crypto/ex_data.c
   src/crypto/fipsmodule/bcm.c
   src/crypto/fipsmodule/fips_shared_support.c
-  src/crypto/fipsmodule/is_fips.c
   src/crypto/hkdf/hkdf.c
   src/crypto/hpke/hpke.c
   src/crypto/hrss/hrss.c
@@ -514,13 +509,13 @@
   src/crypto/trust_token/voprf.c
   src/crypto/x509/a_digest.c
   src/crypto/x509/a_sign.c
-  src/crypto/x509/a_strex.c
   src/crypto/x509/a_verify.c
   src/crypto/x509/algorithm.c
   src/crypto/x509/asn1_gen.c
   src/crypto/x509/by_dir.c
   src/crypto/x509/by_file.c
   src/crypto/x509/i2d_pr.c
+  src/crypto/x509/name_print.c
   src/crypto/x509/rsa_pss.c
   src/crypto/x509/t_crl.c
   src/crypto/x509/t_req.c
@@ -534,7 +529,6 @@
   src/crypto/x509/x509_ext.c
   src/crypto/x509/x509_lu.c
   src/crypto/x509/x509_obj.c
-  src/crypto/x509/x509_r2x.c
   src/crypto/x509/x509_req.c
   src/crypto/x509/x509_set.c
   src/crypto/x509/x509_trs.c
@@ -604,6 +598,8 @@
   src/ssl/d1_srtp.cc
   src/ssl/dtls_method.cc
   src/ssl/dtls_record.cc
+  src/ssl/encrypted_client_hello.cc
+  src/ssl/extensions.cc
   src/ssl/handoff.cc
   src/ssl/handshake.cc
   src/ssl/handshake_client.cc
@@ -626,7 +622,6 @@
   src/ssl/ssl_versions.cc
   src/ssl/ssl_x509.cc
   src/ssl/t1_enc.cc
-  src/ssl/t1_lib.cc
   src/ssl/tls13_both.cc
   src/ssl/tls13_client.cc
   src/ssl/tls13_enc.cc
@@ -645,6 +640,7 @@
   src/tool/digest.cc
   src/tool/fd.cc
   src/tool/file.cc
+  src/tool/generate_ech.cc
   src/tool/generate_ed25519.cc
   src/tool/genrsa.cc
   src/tool/pkcs12.cc
diff --git a/deps/boringssl/err_data.c b/deps/boringssl/err_data.c
index 7103cb1..4f4e973 100644
--- a/deps/boringssl/err_data.c
+++ b/deps/boringssl/err_data.c
@@ -76,54 +76,54 @@
     0xc3b00f7,
     0xc3b88e3,
     0x10320854,
-    0x103295b6,
-    0x103315c2,
-    0x103395db,
-    0x103415ee,
+    0x103295ca,
+    0x103315d6,
+    0x103395ef,
+    0x10341602,
     0x10348f34,
     0x10350c6d,
-    0x10359601,
-    0x1036162b,
-    0x1036963e,
-    0x1037165d,
-    0x10379676,
-    0x1038168b,
-    0x103896a9,
-    0x103916b8,
-    0x103996d4,
-    0x103a16ef,
-    0x103a96fe,
-    0x103b171a,
-    0x103b9735,
-    0x103c175b,
+    0x10359615,
+    0x1036163f,
+    0x10369652,
+    0x10371671,
+    0x1037968a,
+    0x1038169f,
+    0x103896bd,
+    0x103916cc,
+    0x103996e8,
+    0x103a1703,
+    0x103a9712,
+    0x103b172e,
+    0x103b9749,
+    0x103c176f,
     0x103c80f7,
-    0x103d176c,
-    0x103d9780,
-    0x103e179f,
-    0x103e97ae,
-    0x103f17c5,
-    0x103f97d8,
+    0x103d1780,
+    0x103d9794,
+    0x103e17b3,
+    0x103e97c2,
+    0x103f17d9,
+    0x103f97ec,
     0x10400c31,
-    0x104097eb,
-    0x10411809,
-    0x1041981c,
-    0x10421836,
-    0x10429846,
-    0x1043185a,
-    0x10439870,
-    0x10441888,
-    0x1044989d,
-    0x104518b1,
-    0x104598c3,
+    0x104097ff,
+    0x1041181d,
+    0x10419830,
+    0x1042184a,
+    0x1042985a,
+    0x1043186e,
+    0x10439884,
+    0x1044189c,
+    0x104498b1,
+    0x104518c5,
+    0x104598d7,
     0x1046060a,
     0x1046895c,
-    0x104718d8,
-    0x104798ef,
-    0x10481904,
-    0x10489912,
+    0x104718ec,
+    0x10479903,
+    0x10481918,
+    0x10489926,
     0x10490e80,
-    0x1049974c,
-    0x104a1616,
+    0x10499760,
+    0x104a162a,
     0x14320c14,
     0x14328c22,
     0x14330c31,
@@ -140,50 +140,51 @@
     0x18358feb,
     0x18361000,
     0x18369014,
-    0x18371038,
-    0x1837904e,
-    0x18381062,
-    0x18389072,
+    0x1837104c,
+    0x18379062,
+    0x18381076,
+    0x18389086,
     0x18390a82,
-    0x18399082,
-    0x183a10a8,
-    0x183a90ce,
+    0x18399096,
+    0x183a10bc,
+    0x183a90e2,
     0x183b0c8c,
-    0x183b911d,
-    0x183c112f,
-    0x183c913a,
-    0x183d114a,
-    0x183d915b,
-    0x183e116c,
-    0x183e917e,
-    0x183f11a7,
-    0x183f91c0,
-    0x184011d8,
+    0x183b9131,
+    0x183c1143,
+    0x183c914e,
+    0x183d115e,
+    0x183d916f,
+    0x183e1180,
+    0x183e9192,
+    0x183f11bb,
+    0x183f91d4,
+    0x184011ec,
     0x184086e2,
-    0x184110f1,
-    0x184190bc,
-    0x184210db,
+    0x18411105,
+    0x184190d0,
+    0x184210ef,
     0x18428c79,
-    0x18431097,
-    0x18439103,
+    0x184310ab,
+    0x18439117,
     0x18440fc9,
-    0x20321212,
-    0x203291ff,
-    0x2432121e,
+    0x18449038,
+    0x20321226,
+    0x20329213,
+    0x24321232,
     0x243289a2,
-    0x24331230,
-    0x2433923d,
-    0x2434124a,
-    0x2434925c,
-    0x2435126b,
-    0x24359288,
-    0x24361295,
-    0x243692a3,
-    0x243712b1,
-    0x243792bf,
-    0x243812c8,
-    0x243892d5,
-    0x243912e8,
+    0x24331244,
+    0x24339251,
+    0x2434125e,
+    0x24349270,
+    0x2435127f,
+    0x2435929c,
+    0x243612a9,
+    0x243692b7,
+    0x243712c5,
+    0x243792d3,
+    0x243812dc,
+    0x243892e9,
+    0x243912fc,
     0x28320c61,
     0x28328c8c,
     0x28330c31,
@@ -192,47 +193,47 @@
     0x283480b9,
     0x283500f7,
     0x28358c79,
-    0x2c3230db,
-    0x2c3292ff,
-    0x2c3330e9,
-    0x2c33b0fb,
-    0x2c34310f,
-    0x2c34b121,
-    0x2c35313c,
-    0x2c35b14e,
-    0x2c36317e,
+    0x2c32326b,
+    0x2c329313,
+    0x2c333279,
+    0x2c33b28b,
+    0x2c34329f,
+    0x2c34b2b1,
+    0x2c3532cc,
+    0x2c35b2de,
+    0x2c36330e,
     0x2c36833a,
-    0x2c37318b,
-    0x2c37b1b7,
-    0x2c3831dc,
-    0x2c38b1f3,
-    0x2c393211,
-    0x2c39b221,
-    0x2c3a3233,
-    0x2c3ab247,
-    0x2c3b3258,
-    0x2c3bb277,
-    0x2c3c1311,
-    0x2c3c9327,
-    0x2c3d328b,
-    0x2c3d9340,
-    0x2c3e32a8,
-    0x2c3eb2b6,
-    0x2c3f32ce,
-    0x2c3fb2e6,
-    0x2c403310,
-    0x2c409212,
-    0x2c413321,
-    0x2c41b334,
-    0x2c4211d8,
-    0x2c42b345,
+    0x2c37331b,
+    0x2c37b347,
+    0x2c38336c,
+    0x2c38b383,
+    0x2c3933a1,
+    0x2c39b3b1,
+    0x2c3a33c3,
+    0x2c3ab3d7,
+    0x2c3b33e8,
+    0x2c3bb407,
+    0x2c3c1325,
+    0x2c3c933b,
+    0x2c3d341b,
+    0x2c3d9354,
+    0x2c3e3438,
+    0x2c3eb446,
+    0x2c3f345e,
+    0x2c3fb476,
+    0x2c4034a0,
+    0x2c409226,
+    0x2c4134b1,
+    0x2c41b4c4,
+    0x2c4211ec,
+    0x2c42b4d5,
     0x2c43072f,
-    0x2c43b269,
-    0x2c4431ca,
-    0x2c44b2f3,
-    0x2c453161,
-    0x2c45b19d,
-    0x2c463201,
+    0x2c43b3f9,
+    0x2c44335a,
+    0x2c44b483,
+    0x2c4532f1,
+    0x2c45b32d,
+    0x2c463391,
     0x30320000,
     0x30328015,
     0x3033001f,
@@ -368,248 +369,261 @@
     0x3c418d74,
     0x3c420e80,
     0x3c428e0a,
-    0x403219a4,
-    0x403299ba,
-    0x403319e8,
-    0x403399f2,
-    0x40341a09,
-    0x40349a27,
-    0x40351a37,
-    0x40359a49,
-    0x40361a56,
-    0x40369a62,
-    0x40371a77,
-    0x40379a89,
-    0x40381a94,
-    0x40389aa6,
+    0x403219b8,
+    0x403299ce,
+    0x403319fc,
+    0x40339a06,
+    0x40341a1d,
+    0x40349a3b,
+    0x40351a4b,
+    0x40359a5d,
+    0x40361a6a,
+    0x40369a76,
+    0x40371a8b,
+    0x40379a9d,
+    0x40381aa8,
+    0x40389aba,
     0x40390f34,
-    0x40399ab6,
-    0x403a1ac9,
-    0x403a9aea,
-    0x403b1afb,
-    0x403b9b0b,
+    0x40399aca,
+    0x403a1add,
+    0x403a9afe,
+    0x403b1b0f,
+    0x403b9b1f,
     0x403c0071,
     0x403c8090,
-    0x403d1b6c,
-    0x403d9b82,
-    0x403e1b91,
-    0x403e9bc9,
-    0x403f1be3,
-    0x403f9c0b,
-    0x40401c20,
-    0x40409c34,
-    0x40411c6f,
-    0x40419c8a,
-    0x40421ca3,
-    0x40429cb6,
-    0x40431cca,
-    0x40439ce2,
-    0x40441cf9,
+    0x403d1b80,
+    0x403d9b96,
+    0x403e1ba5,
+    0x403e9bdd,
+    0x403f1bf7,
+    0x403f9c1f,
+    0x40401c34,
+    0x40409c48,
+    0x40411c83,
+    0x40419c9e,
+    0x40421cb7,
+    0x40429cca,
+    0x40431cde,
+    0x40439d0c,
+    0x40441d23,
     0x404480b9,
-    0x40451d0e,
-    0x40459d20,
-    0x40461d44,
-    0x40469d64,
-    0x40471d72,
-    0x40479d99,
-    0x40481e0a,
-    0x40489e3d,
-    0x40491e54,
-    0x40499e6e,
-    0x404a1e85,
-    0x404a9ea3,
-    0x404b1ebb,
-    0x404b9ee8,
-    0x404c1efe,
-    0x404c9f10,
-    0x404d1f31,
-    0x404d9f6a,
-    0x404e1f7e,
-    0x404e9f8b,
-    0x404f1fd2,
-    0x404fa018,
-    0x4050206f,
-    0x4050a083,
-    0x405120b6,
-    0x405220d3,
-    0x4052a0f7,
-    0x4053210f,
-    0x4053a122,
-    0x40542137,
-    0x4054a15a,
-    0x40552185,
-    0x4055a1c2,
-    0x405621cf,
-    0x4056a1e8,
-    0x40572200,
-    0x4057a213,
-    0x40582228,
-    0x4058a24f,
-    0x4059227e,
-    0x4059a2ab,
-    0x405a22bf,
-    0x405aa2cf,
-    0x405b22e7,
-    0x405ba2f8,
-    0x405c230b,
-    0x405ca34a,
-    0x405d2357,
-    0x405da37c,
-    0x405e23ba,
+    0x40451d38,
+    0x40459d4a,
+    0x40461d6e,
+    0x40469d8e,
+    0x40471d9c,
+    0x40479dc3,
+    0x40481e34,
+    0x40489eee,
+    0x40491f05,
+    0x40499f1f,
+    0x404a1f36,
+    0x404a9f54,
+    0x404b1f6c,
+    0x404b9f99,
+    0x404c1faf,
+    0x404c9fc1,
+    0x404d1fe2,
+    0x404da01b,
+    0x404e202f,
+    0x404ea03c,
+    0x404f20d6,
+    0x404fa14c,
+    0x405021a3,
+    0x4050a1b7,
+    0x405121ea,
+    0x405221fa,
+    0x4052a21e,
+    0x40532236,
+    0x4053a249,
+    0x4054225e,
+    0x4054a281,
+    0x405522ac,
+    0x4055a2e9,
+    0x4056230e,
+    0x4056a327,
+    0x4057233f,
+    0x4057a352,
+    0x40582367,
+    0x4058a38e,
+    0x405923bd,
+    0x4059a3ea,
+    0x405a23fe,
+    0x405aa40e,
+    0x405b2426,
+    0x405ba437,
+    0x405c244a,
+    0x405ca489,
+    0x405d2496,
+    0x405da4bb,
+    0x405e24f9,
     0x405e8ac0,
-    0x405f23db,
-    0x405fa3e8,
-    0x406023f6,
-    0x4060a418,
-    0x40612479,
-    0x4061a4b1,
-    0x406224c8,
-    0x4062a4d9,
-    0x40632526,
-    0x4063a53b,
-    0x40642552,
-    0x4064a57e,
-    0x40652599,
-    0x4065a5b0,
-    0x406625c8,
-    0x4066a5f2,
-    0x4067261d,
-    0x4067a662,
-    0x406826aa,
-    0x4068a6cb,
-    0x406926fd,
-    0x4069a72b,
-    0x406a274c,
-    0x406aa76c,
-    0x406b28f4,
-    0x406ba917,
-    0x406c292d,
-    0x406cac1e,
-    0x406d2c4d,
-    0x406dac75,
-    0x406e2ca3,
-    0x406eacf0,
-    0x406f2d49,
-    0x406fad81,
-    0x40702d94,
-    0x4070adb1,
+    0x405f2534,
+    0x405fa541,
+    0x4060254f,
+    0x4060a571,
+    0x406125d2,
+    0x4061a60a,
+    0x40622621,
+    0x4062a632,
+    0x4063267f,
+    0x4063a694,
+    0x406426ab,
+    0x4064a6d7,
+    0x406526f2,
+    0x4065a709,
+    0x40662721,
+    0x4066a74b,
+    0x40672776,
+    0x4067a7bb,
+    0x40682803,
+    0x4068a824,
+    0x40692856,
+    0x4069a884,
+    0x406a28a5,
+    0x406aa8c5,
+    0x406b2a4d,
+    0x406baa70,
+    0x406c2a86,
+    0x406cad90,
+    0x406d2dbf,
+    0x406dade7,
+    0x406e2e15,
+    0x406eae62,
+    0x406f2ebb,
+    0x406faef3,
+    0x40702f06,
+    0x4070af23,
     0x4071080f,
-    0x4071adc3,
-    0x40722dd6,
-    0x4072ae0c,
-    0x40732e24,
-    0x40739511,
-    0x40742e38,
-    0x4074ae52,
-    0x40752e63,
-    0x4075ae77,
-    0x40762e85,
-    0x407692d5,
-    0x40772eaa,
-    0x4077aecc,
-    0x40782ee7,
-    0x4078af20,
-    0x40792f37,
-    0x4079af4d,
-    0x407a2f79,
-    0x407aaf8c,
-    0x407b2fa1,
-    0x407bafb3,
-    0x407c2fe4,
-    0x407cafed,
-    0x407d26e6,
-    0x407da028,
-    0x407e2efc,
-    0x407ea25f,
-    0x407f1d86,
-    0x407f9ed2,
-    0x40801fe2,
-    0x40809dae,
-    0x408120e5,
-    0x40819fbc,
-    0x40822c8e,
-    0x40829b17,
-    0x4083223a,
-    0x4083a563,
-    0x40841dc2,
-    0x4084a297,
-    0x4085231c,
-    0x4085a440,
-    0x4086239c,
-    0x4086a042,
-    0x40872cd4,
-    0x4087a48e,
-    0x40881b55,
-    0x4088a675,
-    0x40891ba4,
-    0x40899b31,
-    0x408a2965,
-    0x408a9929,
-    0x408b2fc8,
-    0x408bad5e,
-    0x408c232c,
-    0x408c9961,
-    0x408d1e23,
-    0x408d9df4,
-    0x408e1f53,
-    0x408ea1a2,
-    0x408f2689,
-    0x408fa45c,
-    0x4090263e,
-    0x4090a36e,
-    0x4091294d,
-    0x40919987,
-    0x40921bf1,
-    0x4092ad0f,
-    0x40932def,
-    0x4093a053,
-    0x40941dd6,
-    0x4094a97e,
-    0x409524ea,
-    0x4095af59,
-    0x40962cbb,
-    0x40969ffb,
-    0x4097209e,
-    0x40979fa2,
-    0x40981c51,
-    0x4098a4fe,
-    0x40992d2b,
-    0x4099a0c6,
-    0x409a2168,
-    0x409a9945,
-    0x41f4281f,
-    0x41f928b1,
-    0x41fe27a4,
-    0x41feaa5a,
-    0x41ff2b6f,
-    0x42032838,
-    0x4208285a,
-    0x4208a896,
-    0x42092788,
-    0x4209a8d0,
-    0x420a27df,
-    0x420aa7bf,
-    0x420b27ff,
-    0x420ba878,
-    0x420c2b8b,
-    0x420ca98e,
-    0x420d2a41,
-    0x420daa78,
-    0x42122a92,
-    0x42172b52,
-    0x4217aad4,
-    0x421c2af6,
-    0x421f2ab1,
-    0x42212c03,
-    0x42262b35,
-    0x422b2be1,
-    0x422baa1c,
-    0x422c2bc3,
-    0x422ca9cf,
-    0x422d29a8,
-    0x422daba2,
-    0x422e29fb,
-    0x42302b11,
+    0x4071af35,
+    0x40722f48,
+    0x4072af7e,
+    0x40732f96,
+    0x40739525,
+    0x40742faa,
+    0x4074afc4,
+    0x40752fd5,
+    0x4075afe9,
+    0x40762ff7,
+    0x407692e9,
+    0x4077301c,
+    0x4077b05c,
+    0x40783077,
+    0x4078b0b0,
+    0x407930c7,
+    0x4079b0dd,
+    0x407a3109,
+    0x407ab11c,
+    0x407b3131,
+    0x407bb143,
+    0x407c3174,
+    0x407cb17d,
+    0x407d283f,
+    0x407da15c,
+    0x407e308c,
+    0x407ea39e,
+    0x407f1db0,
+    0x407f9f83,
+    0x408020e6,
+    0x40809dd8,
+    0x4081220c,
+    0x4081a08a,
+    0x40822e00,
+    0x40829b2b,
+    0x40832379,
+    0x4083a6bc,
+    0x40841dec,
+    0x4084a3d6,
+    0x4085245b,
+    0x4085a599,
+    0x408624db,
+    0x4086a176,
+    0x40872e46,
+    0x4087a5e7,
+    0x40881b69,
+    0x4088a7ce,
+    0x40891bb8,
+    0x40899b45,
+    0x408a2abe,
+    0x408a993d,
+    0x408b3158,
+    0x408baed0,
+    0x408c246b,
+    0x408c9975,
+    0x408d1ed4,
+    0x408d9e1e,
+    0x408e2004,
+    0x408ea2c9,
+    0x408f27e2,
+    0x408fa5b5,
+    0x40902797,
+    0x4090a4ad,
+    0x40912aa6,
+    0x4091999b,
+    0x40921c05,
+    0x4092ae81,
+    0x40932f61,
+    0x4093a187,
+    0x40941e00,
+    0x4094aad7,
+    0x40952643,
+    0x4095b0e9,
+    0x40962e2d,
+    0x4096a0ff,
+    0x409721d2,
+    0x4097a053,
+    0x40981c65,
+    0x4098a657,
+    0x40992e9d,
+    0x4099a2f6,
+    0x409a228f,
+    0x409a9959,
+    0x409b1e5a,
+    0x409b9e85,
+    0x409c303e,
+    0x409c9ead,
+    0x409d20bb,
+    0x409da0a0,
+    0x409e1cf6,
+    0x409ea134,
+    0x409f211c,
+    0x409f9e4d,
+    0x40a0251a,
+    0x40a0a06d,
+    0x41f42978,
+    0x41f92a0a,
+    0x41fe28fd,
+    0x41feabb3,
+    0x41ff2ce1,
+    0x42032991,
+    0x420829b3,
+    0x4208a9ef,
+    0x420928e1,
+    0x4209aa29,
+    0x420a2938,
+    0x420aa918,
+    0x420b2958,
+    0x420ba9d1,
+    0x420c2cfd,
+    0x420caae7,
+    0x420d2b9a,
+    0x420dabd1,
+    0x42122c04,
+    0x42172cc4,
+    0x4217ac46,
+    0x421c2c68,
+    0x421f2c23,
+    0x42212d75,
+    0x42262ca7,
+    0x422b2d53,
+    0x422bab75,
+    0x422c2d35,
+    0x422cab28,
+    0x422d2b01,
+    0x422dad14,
+    0x422e2b54,
+    0x42302c83,
+    0x4230abeb,
     0x4432073a,
     0x44328749,
     0x44330755,
@@ -627,106 +641,107 @@
     0x4439080f,
     0x4439881d,
     0x443a0830,
-    0x483212ff,
-    0x48329311,
-    0x48331327,
-    0x48339340,
-    0x4c321365,
-    0x4c329375,
-    0x4c331388,
-    0x4c3393a8,
+    0x48321313,
+    0x48329325,
+    0x4833133b,
+    0x48339354,
+    0x4c321379,
+    0x4c329389,
+    0x4c33139c,
+    0x4c3393bc,
     0x4c3400b9,
     0x4c3480f7,
-    0x4c3513b4,
-    0x4c3593c2,
-    0x4c3613de,
-    0x4c369404,
-    0x4c371413,
-    0x4c379421,
-    0x4c381436,
-    0x4c389442,
-    0x4c391462,
-    0x4c39948c,
-    0x4c3a14a5,
-    0x4c3a94be,
+    0x4c3513c8,
+    0x4c3593d6,
+    0x4c3613f2,
+    0x4c369418,
+    0x4c371427,
+    0x4c379435,
+    0x4c38144a,
+    0x4c389456,
+    0x4c391476,
+    0x4c3994a0,
+    0x4c3a14b9,
+    0x4c3a94d2,
     0x4c3b060a,
-    0x4c3b94d7,
-    0x4c3c14e9,
-    0x4c3c94f8,
-    0x4c3d1511,
+    0x4c3b94eb,
+    0x4c3c14fd,
+    0x4c3c950c,
+    0x4c3d1525,
     0x4c3d8c54,
-    0x4c3e157e,
-    0x4c3e9520,
-    0x4c3f15a0,
-    0x4c3f92d5,
-    0x4c401536,
-    0x4c409351,
-    0x4c41156e,
-    0x4c4193f1,
-    0x4c42155a,
-    0x50323357,
-    0x5032b366,
-    0x50333371,
-    0x5033b381,
-    0x5034339a,
-    0x5034b3b4,
-    0x503533c2,
-    0x5035b3d8,
-    0x503633ea,
-    0x5036b400,
-    0x50373419,
-    0x5037b42c,
-    0x50383444,
-    0x5038b455,
-    0x5039346a,
-    0x5039b47e,
-    0x503a349e,
-    0x503ab4b4,
-    0x503b34cc,
-    0x503bb4de,
-    0x503c34fa,
-    0x503cb511,
-    0x503d352a,
-    0x503db540,
-    0x503e354d,
-    0x503eb563,
-    0x503f3575,
+    0x4c3e1592,
+    0x4c3e9534,
+    0x4c3f15b4,
+    0x4c3f92e9,
+    0x4c40154a,
+    0x4c409365,
+    0x4c411582,
+    0x4c419405,
+    0x4c42156e,
+    0x503234e7,
+    0x5032b4f6,
+    0x50333501,
+    0x5033b511,
+    0x5034352a,
+    0x5034b544,
+    0x50353552,
+    0x5035b568,
+    0x5036357a,
+    0x5036b590,
+    0x503735a9,
+    0x5037b5bc,
+    0x503835d4,
+    0x5038b5e5,
+    0x503935fa,
+    0x5039b60e,
+    0x503a362e,
+    0x503ab644,
+    0x503b365c,
+    0x503bb66e,
+    0x503c368a,
+    0x503cb6a1,
+    0x503d36ba,
+    0x503db6d0,
+    0x503e36dd,
+    0x503eb6f3,
+    0x503f3705,
     0x503f8388,
-    0x50403588,
-    0x5040b598,
-    0x504135b2,
-    0x5041b5c1,
-    0x504235db,
-    0x5042b5f8,
-    0x50433608,
-    0x5043b618,
-    0x50443627,
+    0x50403718,
+    0x5040b728,
+    0x50413742,
+    0x5041b751,
+    0x5042376b,
+    0x5042b788,
+    0x50433798,
+    0x5043b7a8,
+    0x504437c5,
     0x5044843e,
-    0x5045363b,
-    0x5045b659,
-    0x5046366c,
-    0x5046b682,
-    0x50473694,
-    0x5047b6a9,
-    0x504836cf,
-    0x5048b6dd,
-    0x504936f0,
-    0x5049b705,
-    0x504a371b,
-    0x504ab72b,
-    0x504b374b,
-    0x504bb75e,
-    0x504c3781,
-    0x504cb7af,
-    0x504d37c1,
-    0x504db7de,
-    0x504e37f9,
-    0x504eb815,
-    0x504f3827,
-    0x504fb83e,
-    0x5050384d,
+    0x504537d9,
+    0x5045b7f7,
+    0x5046380a,
+    0x5046b820,
+    0x50473832,
+    0x5047b847,
+    0x5048386d,
+    0x5048b87b,
+    0x5049388e,
+    0x5049b8a3,
+    0x504a38b9,
+    0x504ab8c9,
+    0x504b38e9,
+    0x504bb8fc,
+    0x504c391f,
+    0x504cb94d,
+    0x504d395f,
+    0x504db97c,
+    0x504e3997,
+    0x504eb9b3,
+    0x504f39c5,
+    0x504fb9dc,
+    0x505039eb,
     0x505086fe,
-    0x50513860,
+    0x505139fe,
+    0x5051b7b7,
     0x58320f72,
     0x68320f34,
     0x68328c8c,
@@ -767,22 +782,22 @@
     0x783d8b59,
     0x783e0aaf,
     0x783e8a61,
-    0x7c3211ee,
-    0x80321404,
+    0x7c321202,
+    0x80321418,
     0x80328090,
-    0x803330aa,
+    0x8033323a,
     0x803380b9,
-    0x803430b9,
-    0x8034b021,
-    0x8035303f,
-    0x8035b0cd,
-    0x80363081,
-    0x8036b030,
-    0x80373073,
-    0x8037b00e,
-    0x80383094,
-    0x8038b050,
-    0x80393065,
+    0x80343249,
+    0x8034b1b1,
+    0x803531cf,
+    0x8035b25d,
+    0x80363211,
+    0x8036b1c0,
+    0x80373203,
+    0x8037b19e,
+    0x80383224,
+    0x8038b1e0,
+    0x803931f5,
 };
 
 const size_t kOpenSSLReasonValuesLen = sizeof(kOpenSSLReasonValues) / sizeof(kOpenSSLReasonValues[0]);
@@ -1004,6 +1019,7 @@
     "EXPECTING_AN_RSA_KEY\0"
     "EXPECTING_A_DSA_KEY\0"
     "ILLEGAL_OR_UNSUPPORTED_PADDING_MODE\0"
+    "INVALID_BUFFER_SIZE\0"
     "INVALID_DIGEST_LENGTH\0"
     "INVALID_DIGEST_TYPE\0"
     "INVALID_KEYBITS\0"
@@ -1158,6 +1174,7 @@
     "CLIENTHELLO_TLSEXT\0"
     "CONNECTION_REJECTED\0"
     "CONNECTION_TYPE_NOT_SET\0"
+    "COULD_NOT_PARSE_HINTS\0"
     "CUSTOM_EXTENSION_ERROR\0"
     "DATA_LENGTH_TOO_LONG\0"
     "DECRYPTION_FAILED\0"
@@ -1172,6 +1189,10 @@
     "DUPLICATE_SIGNATURE_ALGORITHM\0"
     "EARLY_DATA_NOT_IN_USE\0"
     "ECC_CERT_NOT_FOR_SIGNING\0"
+    "ECH_REJECTED\0"
+    "ECH_SERVER_CONFIG_AND_PRIVATE_KEY_MISMATCH\0"
+    "ECH_SERVER_CONFIG_UNSUPPORTED_EXTENSION\0"
+    "ECH_SERVER_WOULD_HAVE_NO_RETRY_CONFIGS\0"
     "EMPTY_HELLO_RETRY_REQUEST\0"
     "EMS_STATE_INCONSISTENT\0"
     "ENCRYPTED_LENGTH_TOO_LONG\0"
@@ -1189,10 +1210,15 @@
     "HTTP_REQUEST\0"
     "INAPPROPRIATE_FALLBACK\0"
     "INCONSISTENT_CLIENT_HELLO\0"
+    "INCONSISTENT_ECH_NEGOTIATION\0"
     "INVALID_ALPN_PROTOCOL\0"
+    "INVALID_ALPN_PROTOCOL_LIST\0"
+    "INVALID_CLIENT_HELLO_INNER\0"
     "INVALID_COMMAND\0"
     "INVALID_COMPRESSION_LIST\0"
     "INVALID_DELEGATED_CREDENTIAL\0"
+    "INVALID_ECH_CONFIG_LIST\0"
+    "INVALID_ECH_PUBLIC_NAME\0"
     "INVALID_MESSAGE\0"
     "INVALID_OUTER_RECORD_TYPE\0"
     "INVALID_SCT_LIST\0"
@@ -1201,7 +1227,6 @@
     "INVALID_TICKET_KEYS_LENGTH\0"
     "KEY_USAGE_BIT_INCORRECT\0"
     "LENGTH_MISMATCH\0"
-    "MISSING_ALPN\0"
     "MISSING_EXTENSION\0"
     "MISSING_KEY_SHARE\0"
     "MISSING_RSA_CERTIFICATE\0"
@@ -1213,6 +1238,7 @@
     "NEGOTIATED_BOTH_NPN_AND_ALPN\0"
     "NEGOTIATED_TB_WITHOUT_EMS_OR_RI\0"
     "NESTED_GROUP\0"
+    "NO_APPLICATION_PROTOCOL\0"
     "NO_CERTIFICATES_RETURNED\0"
     "NO_CERTIFICATE_ASSIGNED\0"
     "NO_CERTIFICATE_SET\0"
@@ -1237,6 +1263,7 @@
     "OLD_SESSION_CIPHER_NOT_RETURNED\0"
     "OLD_SESSION_PRF_HASH_MISMATCH\0"
     "OLD_SESSION_VERSION_NOT_RETURNED\0"
+    "OUTER_EXTENSION_NOT_FOUND\0"
     "PARSE_TLSEXT\0"
     "PATH_TOO_LONG\0"
     "PEER_DID_NOT_RETURN_A_CERTIFICATE\0"
@@ -1297,6 +1324,7 @@
     "TLSV1_ALERT_DECODE_ERROR\0"
     "TLSV1_ALERT_DECRYPTION_FAILED\0"
     "TLSV1_ALERT_DECRYPT_ERROR\0"
+    "TLSV1_ALERT_ECH_REQUIRED\0"
     "TLSV1_ALERT_EXPORT_RESTRICTION\0"
     "TLSV1_ALERT_INAPPROPRIATE_FALLBACK\0"
     "TLSV1_ALERT_INSUFFICIENT_SECURITY\0"
@@ -1336,6 +1364,7 @@
     "UNKNOWN_STATE\0"
     "UNSAFE_LEGACY_RENEGOTIATION_DISABLED\0"
     "UNSUPPORTED_COMPRESSION_ALGORITHM\0"
+    "UNSUPPORTED_ECH_SERVER_CONFIG\0"
     "UNSUPPORTED_ELLIPTIC_CURVE\0"
     "UNSUPPORTED_PROTOCOL\0"
     "UNSUPPORTED_PROTOCOL_FOR_CUSTOM_KEY\0"
@@ -1430,6 +1459,7 @@
     "INVALID_PURPOSE\0"
     "INVALID_SECTION\0"
     "INVALID_SYNTAX\0"
+    "INVALID_VALUE\0"
     "ISSUER_DECODE_ERROR\0"
     "NEED_ORGANIZATION_AND_NUMBERS\0"
     "NO_CONFIG_DATABASE\0"
diff --git a/deps/boringssl/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/deps/boringssl/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index 566330f..dcef3c5 100644
--- a/deps/boringssl/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/deps/boringssl/ios-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -14,6 +14,7 @@
 #endif
 #include <openssl/arm_arch.h>
 
+#if __ARM_MAX_ARCH__>=7
 .text
 
 .globl	_gcm_init_v8
@@ -64,8 +65,48 @@
 	ext	v17.16b,v22.16b,v22.16b,#8		//Karatsuba pre-processing
 	eor	v17.16b,v17.16b,v22.16b
 	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
-	st1	{v21.2d,v22.2d},[x0]		//store Htable[1..2]
+	st1	{v21.2d,v22.2d},[x0],#32	//store Htable[1..2]
+	//calculate H^3 and H^4
+	pmull	v0.1q,v20.1d, v22.1d
+	pmull	v5.1q,v22.1d,v22.1d
+	pmull2	v2.1q,v20.2d, v22.2d
+	pmull2	v7.1q,v22.2d,v22.2d
+	pmull	v1.1q,v16.1d,v17.1d
+	pmull	v6.1q,v17.1d,v17.1d
 
+	ext	v16.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	ext	v17.16b,v5.16b,v7.16b,#8
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v16.16b
+	eor	v4.16b,v5.16b,v7.16b
+	eor	v6.16b,v6.16b,v17.16b
+	eor	v1.16b,v1.16b,v18.16b
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase
+	eor	v6.16b,v6.16b,v4.16b
+	pmull	v4.1q,v5.1d,v19.1d
+
+	ins	v2.d[0],v1.d[1]
+	ins	v7.d[0],v6.d[1]
+	ins	v1.d[1],v0.d[0]
+	ins	v6.d[1],v5.d[0]
+	eor	v0.16b,v1.16b,v18.16b
+	eor	v5.16b,v6.16b,v4.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase
+	ext	v4.16b,v5.16b,v5.16b,#8
+	pmull	v0.1q,v0.1d,v19.1d
+	pmull	v5.1q,v5.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v4.16b,v4.16b,v7.16b
+	eor	v20.16b, v0.16b,v18.16b		//H^3
+	eor	v22.16b,v5.16b,v4.16b		//H^4
+
+	ext	v16.16b,v20.16b, v20.16b,#8		//Karatsuba pre-processing
+	ext	v17.16b,v22.16b,v22.16b,#8
+	eor	v16.16b,v16.16b,v20.16b
+	eor	v17.16b,v17.16b,v22.16b
+	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
+	st1	{v20.2d,v21.2d,v22.2d},[x0]		//store Htable[3..5]
 	ret
 
 .globl	_gcm_gmult_v8
@@ -117,6 +158,8 @@
 .align	4
 _gcm_ghash_v8:
 	AARCH64_VALID_CALL_TARGET
+	cmp	x3,#64
+	b.hs	Lgcm_ghash_v8_4x
 	ld1	{v0.2d},[x0]		//load [rotated] Xi
 						//"[rotated]" means that
 						//loaded value would have
@@ -243,7 +286,288 @@
 
 	ret
 
+
+.align	4
+gcm_ghash_v8_4x:
+Lgcm_ghash_v8_4x:
+	ld1	{v0.2d},[x0]		//load [rotated] Xi
+	ld1	{v20.2d,v21.2d,v22.2d},[x1],#48	//load twisted H, ..., H^2
+	movi	v19.16b,#0xe1
+	ld1	{v26.2d,v27.2d,v28.2d},[x1]	//load twisted H^3, ..., H^4
+	shl	v19.2d,v19.2d,#57		//compose 0xc2.0 constant
+
+	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
+#ifndef __ARMEB__
+	rev64	v0.16b,v0.16b
+	rev64	v5.16b,v5.16b
+	rev64	v6.16b,v6.16b
+	rev64	v7.16b,v7.16b
+	rev64	v4.16b,v4.16b
+#endif
+	ext	v25.16b,v7.16b,v7.16b,#8
+	ext	v24.16b,v6.16b,v6.16b,#8
+	ext	v23.16b,v5.16b,v5.16b,#8
+
+	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
+	eor	v7.16b,v7.16b,v25.16b
+	pmull2	v31.1q,v20.2d,v25.2d
+	pmull	v30.1q,v21.1d,v7.1d
+
+	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
+	eor	v6.16b,v6.16b,v24.16b
+	pmull2	v24.1q,v22.2d,v24.2d
+	pmull2	v6.1q,v21.2d,v6.2d
+
+	eor	v29.16b,v29.16b,v16.16b
+	eor	v31.16b,v31.16b,v24.16b
+	eor	v30.16b,v30.16b,v6.16b
+
+	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+	pmull2	v23.1q,v26.2d,v23.2d
+	pmull	v5.1q,v27.1d,v5.1d
+
+	eor	v29.16b,v29.16b,v7.16b
+	eor	v31.16b,v31.16b,v23.16b
+	eor	v30.16b,v30.16b,v5.16b
+
+	subs	x3,x3,#128
+	b.lo	Ltail4x
+
+	b	Loop4x
+
+.align	4
+Loop4x:
+	eor	v16.16b,v4.16b,v0.16b
+	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
+	ext	v3.16b,v16.16b,v16.16b,#8
+#ifndef __ARMEB__
+	rev64	v5.16b,v5.16b
+	rev64	v6.16b,v6.16b
+	rev64	v7.16b,v7.16b
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v28.2d,v3.2d
+	ext	v25.16b,v7.16b,v7.16b,#8
+	pmull2	v1.1q,v27.2d,v16.2d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	ext	v24.16b,v6.16b,v6.16b,#8
+	eor	v1.16b,v1.16b,v30.16b
+	ext	v23.16b,v5.16b,v5.16b,#8
+
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
+	eor	v7.16b,v7.16b,v25.16b
+	eor	v1.16b,v1.16b,v17.16b
+	pmull2	v31.1q,v20.2d,v25.2d
+	eor	v1.16b,v1.16b,v18.16b
+	pmull	v30.1q,v21.1d,v7.1d
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
+	eor	v6.16b,v6.16b,v24.16b
+	pmull2	v24.1q,v22.2d,v24.2d
+	eor	v0.16b,v1.16b,v18.16b
+	pmull2	v6.1q,v21.2d,v6.2d
+
+	eor	v29.16b,v29.16b,v16.16b
+	eor	v31.16b,v31.16b,v24.16b
+	eor	v30.16b,v30.16b,v6.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+	eor	v18.16b,v18.16b,v2.16b
+	pmull2	v23.1q,v26.2d,v23.2d
+	pmull	v5.1q,v27.1d,v5.1d
+
+	eor	v0.16b,v0.16b,v18.16b
+	eor	v29.16b,v29.16b,v7.16b
+	eor	v31.16b,v31.16b,v23.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+	eor	v30.16b,v30.16b,v5.16b
+
+	subs	x3,x3,#64
+	b.hs	Loop4x
+
+Ltail4x:
+	eor	v16.16b,v4.16b,v0.16b
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v28.2d,v3.2d
+	pmull2	v1.1q,v27.2d,v16.2d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	eor	v1.16b,v1.16b,v30.16b
+
+	adds	x3,x3,#64
+	b.eq	Ldone4x
+
+	cmp	x3,#32
+	b.lo	Lone
+	b.eq	Ltwo
+Lthree:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	ld1	{v4.2d,v5.2d,v6.2d},[x2]
+	eor	v1.16b,v1.16b,v18.16b
+#ifndef	__ARMEB__
+	rev64	v5.16b,v5.16b
+	rev64	v6.16b,v6.16b
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	ext	v24.16b,v6.16b,v6.16b,#8
+	ext	v23.16b,v5.16b,v5.16b,#8
+	eor	v0.16b,v1.16b,v18.16b
+
+	pmull	v29.1q,v20.1d,v24.1d		//H·Ii+2
+	eor	v6.16b,v6.16b,v24.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	pmull2	v31.1q,v20.2d,v24.2d
+	pmull	v30.1q,v21.1d,v6.1d
+	eor	v0.16b,v0.16b,v18.16b
+	pmull	v7.1q,v22.1d,v23.1d		//H^2·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+	pmull2	v23.1q,v22.2d,v23.2d
+	eor	v16.16b,v4.16b,v0.16b
+	pmull2	v5.1q,v21.2d,v5.2d
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	eor	v29.16b,v29.16b,v7.16b
+	eor	v31.16b,v31.16b,v23.16b
+	eor	v30.16b,v30.16b,v5.16b
+
+	pmull	v0.1q,v26.1d,v3.1d		//H^3·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v26.2d,v3.2d
+	pmull	v1.1q,v27.1d,v16.1d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	eor	v1.16b,v1.16b,v30.16b
+	b	Ldone4x
+
+.align	4
+Ltwo:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	ld1	{v4.2d,v5.2d},[x2]
+	eor	v1.16b,v1.16b,v18.16b
+#ifndef	__ARMEB__
+	rev64	v5.16b,v5.16b
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	ext	v23.16b,v5.16b,v5.16b,#8
+	eor	v0.16b,v1.16b,v18.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v0.16b,v0.16b,v18.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+	pmull	v29.1q,v20.1d,v23.1d		//H·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+
+	eor	v16.16b,v4.16b,v0.16b
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	pmull2	v31.1q,v20.2d,v23.2d
+	pmull	v30.1q,v21.1d,v5.1d
+
+	pmull	v0.1q,v22.1d,v3.1d		//H^2·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v22.2d,v3.2d
+	pmull2	v1.1q,v21.2d,v16.2d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	eor	v1.16b,v1.16b,v30.16b
+	b	Ldone4x
+
+.align	4
+Lone:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	ld1	{v4.2d},[x2]
+	eor	v1.16b,v1.16b,v18.16b
+#ifndef	__ARMEB__
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	eor	v0.16b,v1.16b,v18.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v0.16b,v0.16b,v18.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+	eor	v16.16b,v4.16b,v0.16b
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	pmull	v0.1q,v20.1d,v3.1d
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v20.2d,v3.2d
+	pmull	v1.1q,v21.1d,v16.1d
+
+Ldone4x:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	eor	v1.16b,v1.16b,v18.16b
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	eor	v0.16b,v1.16b,v18.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v0.16b,v0.16b,v18.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+#ifndef __ARMEB__
+	rev64	v0.16b,v0.16b
+#endif
+	st1	{v0.2d},[x0]		//write out Xi
+
+	ret
+
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
+#endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/deps/boringssl/ios-arm/crypto/fipsmodule/ghashv8-armx32.S b/deps/boringssl/ios-arm/crypto/fipsmodule/ghashv8-armx32.S
index 4a7497f..dcac580 100644
--- a/deps/boringssl/ios-arm/crypto/fipsmodule/ghashv8-armx32.S
+++ b/deps/boringssl/ios-arm/crypto/fipsmodule/ghashv8-armx32.S
@@ -14,6 +14,7 @@
 #endif
 #include <openssl/arm_arch.h>
 
+#if __ARM_MAX_ARCH__>=7
 .text
 
 .code	32
@@ -68,8 +69,7 @@
 	vext.8	q9,q14,q14,#8		@ Karatsuba pre-processing
 	veor	q9,q9,q14
 	vext.8	q13,q8,q9,#8		@ pack Karatsuba pre-processed
-	vst1.64	{q13,q14},[r0]		@ store Htable[1..2]
-
+	vst1.64	{q13,q14},[r0]!	@ store Htable[1..2]
 	bx	lr
 
 .globl	_gcm_gmult_v8
@@ -256,4 +256,5 @@
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
+#endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/deps/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/deps/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index 62e5884..9480a38 100644
--- a/deps/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/deps/boringssl/linux-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -15,6 +15,7 @@
 #endif
 #include <openssl/arm_arch.h>
 
+#if __ARM_MAX_ARCH__>=7
 .text
 .arch	armv8-a+crypto
 .globl	gcm_init_v8
@@ -65,8 +66,48 @@
 	ext	v17.16b,v22.16b,v22.16b,#8		//Karatsuba pre-processing
 	eor	v17.16b,v17.16b,v22.16b
 	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
-	st1	{v21.2d,v22.2d},[x0]		//store Htable[1..2]
+	st1	{v21.2d,v22.2d},[x0],#32	//store Htable[1..2]
+	//calculate H^3 and H^4
+	pmull	v0.1q,v20.1d, v22.1d
+	pmull	v5.1q,v22.1d,v22.1d
+	pmull2	v2.1q,v20.2d, v22.2d
+	pmull2	v7.1q,v22.2d,v22.2d
+	pmull	v1.1q,v16.1d,v17.1d
+	pmull	v6.1q,v17.1d,v17.1d
 
+	ext	v16.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	ext	v17.16b,v5.16b,v7.16b,#8
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v16.16b
+	eor	v4.16b,v5.16b,v7.16b
+	eor	v6.16b,v6.16b,v17.16b
+	eor	v1.16b,v1.16b,v18.16b
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase
+	eor	v6.16b,v6.16b,v4.16b
+	pmull	v4.1q,v5.1d,v19.1d
+
+	ins	v2.d[0],v1.d[1]
+	ins	v7.d[0],v6.d[1]
+	ins	v1.d[1],v0.d[0]
+	ins	v6.d[1],v5.d[0]
+	eor	v0.16b,v1.16b,v18.16b
+	eor	v5.16b,v6.16b,v4.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase
+	ext	v4.16b,v5.16b,v5.16b,#8
+	pmull	v0.1q,v0.1d,v19.1d
+	pmull	v5.1q,v5.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v4.16b,v4.16b,v7.16b
+	eor	v20.16b, v0.16b,v18.16b		//H^3
+	eor	v22.16b,v5.16b,v4.16b		//H^4
+
+	ext	v16.16b,v20.16b, v20.16b,#8		//Karatsuba pre-processing
+	ext	v17.16b,v22.16b,v22.16b,#8
+	eor	v16.16b,v16.16b,v20.16b
+	eor	v17.16b,v17.16b,v22.16b
+	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
+	st1	{v20.2d,v21.2d,v22.2d},[x0]		//store Htable[3..5]
 	ret
 .size	gcm_init_v8,.-gcm_init_v8
 .globl	gcm_gmult_v8
@@ -118,6 +159,8 @@
 .align	4
 gcm_ghash_v8:
 	AARCH64_VALID_CALL_TARGET
+	cmp	x3,#64
+	b.hs	.Lgcm_ghash_v8_4x
 	ld1	{v0.2d},[x0]		//load [rotated] Xi
 						//"[rotated]" means that
 						//loaded value would have
@@ -244,9 +287,290 @@
 
 	ret
 .size	gcm_ghash_v8,.-gcm_ghash_v8
+.type	gcm_ghash_v8_4x,%function
+.align	4
+gcm_ghash_v8_4x:
+.Lgcm_ghash_v8_4x:
+	ld1	{v0.2d},[x0]		//load [rotated] Xi
+	ld1	{v20.2d,v21.2d,v22.2d},[x1],#48	//load twisted H, ..., H^2
+	movi	v19.16b,#0xe1
+	ld1	{v26.2d,v27.2d,v28.2d},[x1]	//load twisted H^3, ..., H^4
+	shl	v19.2d,v19.2d,#57		//compose 0xc2.0 constant
+
+	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
+#ifndef __ARMEB__
+	rev64	v0.16b,v0.16b
+	rev64	v5.16b,v5.16b
+	rev64	v6.16b,v6.16b
+	rev64	v7.16b,v7.16b
+	rev64	v4.16b,v4.16b
+#endif
+	ext	v25.16b,v7.16b,v7.16b,#8
+	ext	v24.16b,v6.16b,v6.16b,#8
+	ext	v23.16b,v5.16b,v5.16b,#8
+
+	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
+	eor	v7.16b,v7.16b,v25.16b
+	pmull2	v31.1q,v20.2d,v25.2d
+	pmull	v30.1q,v21.1d,v7.1d
+
+	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
+	eor	v6.16b,v6.16b,v24.16b
+	pmull2	v24.1q,v22.2d,v24.2d
+	pmull2	v6.1q,v21.2d,v6.2d
+
+	eor	v29.16b,v29.16b,v16.16b
+	eor	v31.16b,v31.16b,v24.16b
+	eor	v30.16b,v30.16b,v6.16b
+
+	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+	pmull2	v23.1q,v26.2d,v23.2d
+	pmull	v5.1q,v27.1d,v5.1d
+
+	eor	v29.16b,v29.16b,v7.16b
+	eor	v31.16b,v31.16b,v23.16b
+	eor	v30.16b,v30.16b,v5.16b
+
+	subs	x3,x3,#128
+	b.lo	.Ltail4x
+
+	b	.Loop4x
+
+.align	4
+.Loop4x:
+	eor	v16.16b,v4.16b,v0.16b
+	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
+	ext	v3.16b,v16.16b,v16.16b,#8
+#ifndef __ARMEB__
+	rev64	v5.16b,v5.16b
+	rev64	v6.16b,v6.16b
+	rev64	v7.16b,v7.16b
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v28.2d,v3.2d
+	ext	v25.16b,v7.16b,v7.16b,#8
+	pmull2	v1.1q,v27.2d,v16.2d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	ext	v24.16b,v6.16b,v6.16b,#8
+	eor	v1.16b,v1.16b,v30.16b
+	ext	v23.16b,v5.16b,v5.16b,#8
+
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
+	eor	v7.16b,v7.16b,v25.16b
+	eor	v1.16b,v1.16b,v17.16b
+	pmull2	v31.1q,v20.2d,v25.2d
+	eor	v1.16b,v1.16b,v18.16b
+	pmull	v30.1q,v21.1d,v7.1d
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
+	eor	v6.16b,v6.16b,v24.16b
+	pmull2	v24.1q,v22.2d,v24.2d
+	eor	v0.16b,v1.16b,v18.16b
+	pmull2	v6.1q,v21.2d,v6.2d
+
+	eor	v29.16b,v29.16b,v16.16b
+	eor	v31.16b,v31.16b,v24.16b
+	eor	v30.16b,v30.16b,v6.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+	eor	v18.16b,v18.16b,v2.16b
+	pmull2	v23.1q,v26.2d,v23.2d
+	pmull	v5.1q,v27.1d,v5.1d
+
+	eor	v0.16b,v0.16b,v18.16b
+	eor	v29.16b,v29.16b,v7.16b
+	eor	v31.16b,v31.16b,v23.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+	eor	v30.16b,v30.16b,v5.16b
+
+	subs	x3,x3,#64
+	b.hs	.Loop4x
+
+.Ltail4x:
+	eor	v16.16b,v4.16b,v0.16b
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v28.2d,v3.2d
+	pmull2	v1.1q,v27.2d,v16.2d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	eor	v1.16b,v1.16b,v30.16b
+
+	adds	x3,x3,#64
+	b.eq	.Ldone4x
+
+	cmp	x3,#32
+	b.lo	.Lone
+	b.eq	.Ltwo
+.Lthree:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	ld1	{v4.2d,v5.2d,v6.2d},[x2]
+	eor	v1.16b,v1.16b,v18.16b
+#ifndef	__ARMEB__
+	rev64	v5.16b,v5.16b
+	rev64	v6.16b,v6.16b
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	ext	v24.16b,v6.16b,v6.16b,#8
+	ext	v23.16b,v5.16b,v5.16b,#8
+	eor	v0.16b,v1.16b,v18.16b
+
+	pmull	v29.1q,v20.1d,v24.1d		//H·Ii+2
+	eor	v6.16b,v6.16b,v24.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	pmull2	v31.1q,v20.2d,v24.2d
+	pmull	v30.1q,v21.1d,v6.1d
+	eor	v0.16b,v0.16b,v18.16b
+	pmull	v7.1q,v22.1d,v23.1d		//H^2·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+	pmull2	v23.1q,v22.2d,v23.2d
+	eor	v16.16b,v4.16b,v0.16b
+	pmull2	v5.1q,v21.2d,v5.2d
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	eor	v29.16b,v29.16b,v7.16b
+	eor	v31.16b,v31.16b,v23.16b
+	eor	v30.16b,v30.16b,v5.16b
+
+	pmull	v0.1q,v26.1d,v3.1d		//H^3·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v26.2d,v3.2d
+	pmull	v1.1q,v27.1d,v16.1d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	eor	v1.16b,v1.16b,v30.16b
+	b	.Ldone4x
+
+.align	4
+.Ltwo:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	ld1	{v4.2d,v5.2d},[x2]
+	eor	v1.16b,v1.16b,v18.16b
+#ifndef	__ARMEB__
+	rev64	v5.16b,v5.16b
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	ext	v23.16b,v5.16b,v5.16b,#8
+	eor	v0.16b,v1.16b,v18.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v0.16b,v0.16b,v18.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+	pmull	v29.1q,v20.1d,v23.1d		//H·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+
+	eor	v16.16b,v4.16b,v0.16b
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	pmull2	v31.1q,v20.2d,v23.2d
+	pmull	v30.1q,v21.1d,v5.1d
+
+	pmull	v0.1q,v22.1d,v3.1d		//H^2·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v22.2d,v3.2d
+	pmull2	v1.1q,v21.2d,v16.2d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	eor	v1.16b,v1.16b,v30.16b
+	b	.Ldone4x
+
+.align	4
+.Lone:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	ld1	{v4.2d},[x2]
+	eor	v1.16b,v1.16b,v18.16b
+#ifndef	__ARMEB__
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	eor	v0.16b,v1.16b,v18.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v0.16b,v0.16b,v18.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+	eor	v16.16b,v4.16b,v0.16b
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	pmull	v0.1q,v20.1d,v3.1d
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v20.2d,v3.2d
+	pmull	v1.1q,v21.1d,v16.1d
+
+.Ldone4x:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	eor	v1.16b,v1.16b,v18.16b
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	eor	v0.16b,v1.16b,v18.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v0.16b,v0.16b,v18.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+#ifndef __ARMEB__
+	rev64	v0.16b,v0.16b
+#endif
+	st1	{v0.2d},[x0]		//write out Xi
+
+	ret
+.size	gcm_ghash_v8_4x,.-gcm_ghash_v8_4x
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
 #endif
+#endif
 #endif  // !OPENSSL_NO_ASM
 .section	.note.GNU-stack,"",%progbits
diff --git a/deps/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S b/deps/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
index b97457b..096dfb7 100644
--- a/deps/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
+++ b/deps/boringssl/linux-arm/crypto/fipsmodule/ghashv8-armx32.S
@@ -15,6 +15,7 @@
 #endif
 #include <openssl/arm_arch.h>
 
+#if __ARM_MAX_ARCH__>=7
 .text
 .fpu	neon
 .code	32
@@ -67,8 +68,7 @@
 	vext.8	q9,q14,q14,#8		@ Karatsuba pre-processing
 	veor	q9,q9,q14
 	vext.8	q13,q8,q9,#8		@ pack Karatsuba pre-processed
-	vst1.64	{q13,q14},[r0]		@ store Htable[1..2]
-
+	vst1.64	{q13,q14},[r0]!	@ store Htable[1..2]
 	bx	lr
 .size	gcm_init_v8,.-gcm_init_v8
 .globl	gcm_gmult_v8
@@ -252,5 +252,6 @@
 .align	2
 .align	2
 #endif
+#endif
 #endif  // !OPENSSL_NO_ASM
 .section	.note.GNU-stack,"",%progbits
diff --git a/deps/boringssl/src/CMakeLists.txt b/deps/boringssl/src/CMakeLists.txt
index f58e853..6a5a6aa 100644
--- a/deps/boringssl/src/CMakeLists.txt
+++ b/deps/boringssl/src/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.3)
+cmake_minimum_required(VERSION 3.5)
 
 # Defer enabling C and CXX languages.
 project(BoringSSL NONE)
@@ -117,7 +117,7 @@
 if(CMAKE_COMPILER_IS_GNUCXX OR CLANG)
   # Note clang-cl is odd and sets both CLANG and MSVC. We base our configuration
   # primarily on our normal Clang one.
-  set(C_CXX_FLAGS "-Werror -Wformat=2 -Wsign-compare -Wmissing-field-initializers -Wwrite-strings -Wvla")
+  set(C_CXX_FLAGS "-Werror -Wformat=2 -Wsign-compare -Wmissing-field-initializers -Wwrite-strings -Wvla -Wshadow")
   if(MSVC)
     # clang-cl sets different default warnings than clang. It also treats -Wall
     # as -Weverything, to match MSVC. Instead -W3 is the alias for -Wall.
@@ -172,11 +172,6 @@
   if(CLANG)
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wmissing-prototypes")
   endif()
-
-  if(CMAKE_COMPILER_IS_GNUCXX AND "4.8" VERSION_GREATER CMAKE_C_COMPILER_VERSION)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-array-bounds")
-  endif()
-
 elseif(MSVC)
   set(MSVC_DISABLED_WARNINGS_LIST
       "C4061" # enumerator 'identifier' in switch of enum 'enumeration' is not
@@ -254,18 +249,8 @@
   add_definitions("-D_STL_EXTRA_DISABLED_WARNINGS=4774 4987")
 endif()
 
-if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_C_COMPILER_VERSION VERSION_GREATER "4.7.99") OR
-   CLANG)
-  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wshadow")
-  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow")
-endif()
-
 if(CMAKE_COMPILER_IS_GNUCXX)
-  if((CMAKE_C_COMPILER_VERSION VERSION_GREATER "4.8.99") OR CLANG)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
-  else()
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
-  endif()
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
 endif()
 
 # pthread_rwlock_t requires a feature flag.
@@ -409,8 +394,7 @@
 
 function(go_executable dest package)
   set(godeps "${CMAKE_SOURCE_DIR}/util/godeps.go")
-  if(${CMAKE_VERSION} VERSION_LESS "3.7" OR
-     NOT ${CMAKE_GENERATOR} STREQUAL "Ninja")
+  if(CMAKE_VERSION VERSION_LESS "3.7" OR NOT CMAKE_GENERATOR STREQUAL "Ninja")
     # The DEPFILE parameter to add_custom_command is new as of CMake 3.7 and
     # only works with Ninja. Query the sources at configure time. Additionally,
     # everything depends on go.mod. That affects what external packages to use.
@@ -452,7 +436,7 @@
 # builds.
 if(NOT OPENSSL_NO_ASM AND CMAKE_OSX_ARCHITECTURES)
   list(LENGTH CMAKE_OSX_ARCHITECTURES NUM_ARCHES)
-  if(NOT ${NUM_ARCHES} EQUAL 1)
+  if(NOT NUM_ARCHES EQUAL 1)
     message(FATAL_ERROR "Universal binaries not supported.")
   endif()
   list(GET CMAKE_OSX_ARCHITECTURES 0 CMAKE_SYSTEM_PROCESSOR)
@@ -465,44 +449,44 @@
 if(OPENSSL_NO_ASM)
   add_definitions(-DOPENSSL_NO_ASM)
   set(ARCH "generic")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
   set(ARCH "x86_64")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "amd64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "amd64")
   set(ARCH "x86_64")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64")
   # cmake reports AMD64 on Windows, but we might be building for 32-bit.
   if(CMAKE_SIZEOF_VOID_P EQUAL 8)
     set(ARCH "x86_64")
   else()
     set(ARCH "x86")
   endif()
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86")
   set(ARCH "x86")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i386")
   set(ARCH "x86")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i686")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "i686")
   set(ARCH "x86")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
   set(ARCH "aarch64")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ARM64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM64")
   set(ARCH "aarch64")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
   set(ARCH "aarch64")
 # Apple A12 Bionic chipset which is added in iPhone XS/XS Max/XR uses arm64e architecture.
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm64e")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64e")
   set(ARCH "aarch64")
-elseif(${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm*")
+elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm*")
   set(ARCH "arm")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "mips")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "mips")
   # Just to avoid the “unknown processor” error.
   set(ARCH "generic")
-elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ppc64le")
+elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "ppc64le")
   set(ARCH "ppc64le")
 else()
   message(FATAL_ERROR "Unknown processor:" ${CMAKE_SYSTEM_PROCESSOR})
 endif()
 
-if(ANDROID AND NOT ANDROID_NDK_REVISION AND ${ARCH} STREQUAL "arm")
+if(ANDROID AND NOT ANDROID_NDK_REVISION AND ARCH STREQUAL "arm")
   # The third-party Android-NDK CMake files somehow fail to set the -march flag
   # for assembly files. Without this flag, the compiler believes that it's
   # building for ARMv5.
diff --git a/deps/boringssl/src/crypto/CMakeLists.txt b/deps/boringssl/src/crypto/CMakeLists.txt
index cde92b5..990df00 100644
--- a/deps/boringssl/src/crypto/CMakeLists.txt
+++ b/deps/boringssl/src/crypto/CMakeLists.txt
@@ -2,7 +2,7 @@
 
 if(NOT OPENSSL_NO_ASM)
   if(UNIX)
-    if(${ARCH} STREQUAL "aarch64")
+    if(ARCH STREQUAL "aarch64")
       # The "armx" Perl scripts look for "64" in the style argument
       # in order to decide whether to generate 32- or 64-bit asm.
       if(APPLE)
@@ -10,16 +10,16 @@
       else()
         set(PERLASM_STYLE linux64)
       endif()
-    elseif(${ARCH} STREQUAL "arm")
+    elseif(ARCH STREQUAL "arm")
       if(APPLE)
         set(PERLASM_STYLE ios32)
       else()
         set(PERLASM_STYLE linux32)
       endif()
-    elseif(${ARCH} STREQUAL "ppc64le")
+    elseif(ARCH STREQUAL "ppc64le")
       set(PERLASM_STYLE linux64le)
     else()
-      if(${ARCH} STREQUAL "x86")
+      if(ARCH STREQUAL "x86")
         set(PERLASM_FLAGS "-fPIC -DOPENSSL_IA32_SSE2")
       endif()
       if(APPLE)
@@ -47,12 +47,12 @@
       endforeach()
     endif()
   else()
-    if(${ARCH} STREQUAL "aarch64")
+    if(ARCH STREQUAL "aarch64")
       set(PERLASM_STYLE win64)
       set(ASM_EXT S)
       enable_language(ASM)
     else()
-      if(${ARCH} STREQUAL "x86_64")
+      if(ARCH STREQUAL "x86_64")
         set(PERLASM_STYLE nasm)
       else()
         set(PERLASM_STYLE win32n)
@@ -69,7 +69,7 @@
 
 function(perlasm dest src)
   get_filename_component(dir ${dest} DIRECTORY)
-  if ("${dir}" STREQUAL "")
+  if(dir STREQUAL "")
     set(dir ".")
   endif()
 
@@ -104,7 +104,7 @@
   )
 endif()
 
-if(${ARCH} STREQUAL "arm")
+if(ARCH STREQUAL "arm")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -115,7 +115,7 @@
   )
 endif()
 
-if(${ARCH} STREQUAL "aarch64")
+if(ARCH STREQUAL "aarch64")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -124,7 +124,7 @@
   )
 endif()
 
-if(${ARCH} STREQUAL "ppc64le")
+if(ARCH STREQUAL "ppc64le")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -132,7 +132,7 @@
   )
 endif()
 
-if(${ARCH} STREQUAL "x86")
+if(ARCH STREQUAL "x86")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -141,7 +141,7 @@
   )
 endif()
 
-if(${ARCH} STREQUAL "x86_64")
+if(ARCH STREQUAL "x86_64")
   set(
     CRYPTO_ARCH_SOURCES
 
@@ -211,6 +211,7 @@
   asn1/a_object.c
   asn1/a_octet.c
   asn1/a_print.c
+  asn1/a_strex.c
   asn1/a_strnid.c
   asn1/a_time.c
   asn1/a_type.c
@@ -350,13 +351,13 @@
   trust_token/voprf.c
   x509/a_digest.c
   x509/a_sign.c
-  x509/a_strex.c
   x509/a_verify.c
   x509/algorithm.c
   x509/asn1_gen.c
   x509/by_dir.c
   x509/by_file.c
   x509/i2d_pr.c
+  x509/name_print.c
   x509/rsa_pss.c
   x509/t_crl.c
   x509/t_req.c
@@ -370,7 +371,6 @@
   x509/x509_ext.c
   x509/x509_lu.c
   x509/x509_obj.c
-  x509/x509_r2x.c
   x509/x509_req.c
   x509/x509_set.c
   x509/x509_trs.c
@@ -502,6 +502,7 @@
   cipher_extra/cipher_test.cc
   cmac/cmac_test.cc
   compiler_test.cc
+  conf/conf_test.cc
   constant_time_test.cc
   cpu-arm-linux_test.cc
   crypto_test.cc
diff --git a/deps/boringssl/src/crypto/asn1/a_bitstr.c b/deps/boringssl/src/crypto/asn1/a_bitstr.c
index b945cb1..d45a73e 100644
--- a/deps/boringssl/src/crypto/asn1/a_bitstr.c
+++ b/deps/boringssl/src/crypto/asn1/a_bitstr.c
@@ -65,64 +65,69 @@
 #include "../internal.h"
 
 
-int ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, unsigned char *d, int len)
+int ASN1_BIT_STRING_set(ASN1_BIT_STRING *x, const unsigned char *d, int len)
 {
     return ASN1_STRING_set(x, d, len);
 }
 
+static int asn1_bit_string_length(const ASN1_BIT_STRING *str,
+                                  uint8_t *out_padding_bits) {
+    int len = str->length;
+    if (str->flags & ASN1_STRING_FLAG_BITS_LEFT) {
+        // If the string is already empty, it cannot have padding bits.
+        *out_padding_bits = len == 0 ? 0 : str->flags & 0x07;
+        return len;
+    }
+
+    // TODO(davidben): If we move this logic to |ASN1_BIT_STRING_set_bit|, can
+    // we remove this representation?
+    while (len > 0 && str->data[len - 1] == 0) {
+        len--;
+    }
+    uint8_t padding_bits = 0;
+    if (len > 0) {
+        uint8_t last = str->data[len - 1];
+        assert(last != 0);
+        for (; padding_bits < 7; padding_bits++) {
+            if (last & (1 << padding_bits)) {
+                break;
+            }
+        }
+    }
+    *out_padding_bits = padding_bits;
+    return len;
+}
+
+int ASN1_BIT_STRING_num_bytes(const ASN1_BIT_STRING *str, size_t *out) {
+    uint8_t padding_bits;
+    int len = asn1_bit_string_length(str, &padding_bits);
+    if (padding_bits != 0) {
+        return 0;
+    }
+    *out = len;
+    return 1;
+}
+
 int i2c_ASN1_BIT_STRING(const ASN1_BIT_STRING *a, unsigned char **pp)
 {
-    int ret, j, bits, len;
-    unsigned char *p, *d;
+    if (a == NULL) {
+        return 0;
+    }
 
-    if (a == NULL)
-        return (0);
+    uint8_t bits;
+    int len = asn1_bit_string_length(a, &bits);
+    int ret = 1 + len;
+    if (pp == NULL) {
+        return ret;
+    }
 
-    len = a->length;
-
+    uint8_t *p = *pp;
+    *(p++) = bits;
+    OPENSSL_memcpy(p, a->data, len);
     if (len > 0) {
-        if (a->flags & ASN1_STRING_FLAG_BITS_LEFT) {
-            bits = (int)a->flags & 0x07;
-        } else {
-            for (; len > 0; len--) {
-                if (a->data[len - 1])
-                    break;
-            }
-            j = a->data[len - 1];
-            if (j & 0x01)
-                bits = 0;
-            else if (j & 0x02)
-                bits = 1;
-            else if (j & 0x04)
-                bits = 2;
-            else if (j & 0x08)
-                bits = 3;
-            else if (j & 0x10)
-                bits = 4;
-            else if (j & 0x20)
-                bits = 5;
-            else if (j & 0x40)
-                bits = 6;
-            else if (j & 0x80)
-                bits = 7;
-            else
-                bits = 0;       /* should not happen */
-        }
-    } else
-        bits = 0;
-
-    ret = 1 + len;
-    if (pp == NULL)
-        return (ret);
-
-    p = *pp;
-
-    *(p++) = (unsigned char)bits;
-    d = a->data;
-    OPENSSL_memcpy(p, d, len);
+        p[len - 1] &= (0xff << bits);
+    }
     p += len;
-    if (len > 0)
-        p[-1] &= (0xff << bits);
     *pp = p;
     return (ret);
 }
@@ -251,7 +256,7 @@
  * 'len' is the length of 'flags'.
  */
 int ASN1_BIT_STRING_check(const ASN1_BIT_STRING *a,
-                          unsigned char *flags, int flags_len)
+                          const unsigned char *flags, int flags_len)
 {
     int i, ok;
     /* Check if there is one bit set at all. */
diff --git a/deps/boringssl/src/crypto/asn1/a_bool.c b/deps/boringssl/src/crypto/asn1/a_bool.c
index 34bbd15..64ae9e5 100644
--- a/deps/boringssl/src/crypto/asn1/a_bool.c
+++ b/deps/boringssl/src/crypto/asn1/a_bool.c
@@ -78,7 +78,7 @@
     }
 
     ASN1_put_object(&p, 0, 1, V_ASN1_BOOLEAN, V_ASN1_UNIVERSAL);
-    *p = (unsigned char)a;
+    *p = a ? 0xff : 0x00;
 
     /*
      * If a new buffer was allocated, just return it back.
diff --git a/deps/boringssl/src/crypto/asn1/a_d2i_fp.c b/deps/boringssl/src/crypto/asn1/a_d2i_fp.c
index fd423e2..d0d6d03 100644
--- a/deps/boringssl/src/crypto/asn1/a_d2i_fp.c
+++ b/deps/boringssl/src/crypto/asn1/a_d2i_fp.c
@@ -78,7 +78,6 @@
     return ret;
 }
 
-#ifndef OPENSSL_NO_FP_API
 void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)
 {
     BIO *b = BIO_new_fp(in, BIO_NOCLOSE);
@@ -90,4 +89,3 @@
     BIO_free(b);
     return ret;
 }
-#endif
diff --git a/deps/boringssl/src/crypto/asn1/a_gentm.c b/deps/boringssl/src/crypto/asn1/a_gentm.c
index 5fcb65b..3e6f14e 100644
--- a/deps/boringssl/src/crypto/asn1/a_gentm.c
+++ b/deps/boringssl/src/crypto/asn1/a_gentm.c
@@ -62,7 +62,7 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-#include "asn1_locl.h"
+#include "internal.h"
 
 int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d)
 {
@@ -237,6 +237,11 @@
             goto err;
     }
 
+    if (ts->tm_year < 0 - 1900 || ts->tm_year > 9999 - 1900) {
+        OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_TIME_VALUE);
+        goto err;
+    }
+
     p = (char *)tmps->data;
     if ((p == NULL) || ((size_t)tmps->length < len)) {
         p = OPENSSL_malloc(len);
diff --git a/deps/boringssl/src/crypto/asn1/a_mbstr.c b/deps/boringssl/src/crypto/asn1/a_mbstr.c
index 1bbcd1b..5853b72 100644
--- a/deps/boringssl/src/crypto/asn1/a_mbstr.c
+++ b/deps/boringssl/src/crypto/asn1/a_mbstr.c
@@ -63,11 +63,9 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-#include "asn1_locl.h"
+#include "internal.h"
 #include "../bytestring/internal.h"
 
-static int is_printable(uint32_t value);
-
 /*
  * These functions take a string in UTF8, ASCII or multibyte form and a mask
  * of permissible ASN1 string types. It then works out the minimal type
@@ -153,7 +151,7 @@
         }
 
         /* Update which output formats are still possible. */
-        if ((mask & B_ASN1_PRINTABLESTRING) && !is_printable(c)) {
+        if ((mask & B_ASN1_PRINTABLESTRING) && !asn1_is_printable(c)) {
             mask &= ~B_ASN1_PRINTABLESTRING;
         }
         if ((mask & B_ASN1_IA5STRING) && (c > 127)) {
@@ -208,11 +206,14 @@
         encode_func = cbb_add_utf32_be;
         size_estimate = 4 * nchar;
         outform = MBSTRING_UNIV;
-    } else {
+    } else if (mask & B_ASN1_UTF8STRING) {
         str_type = V_ASN1_UTF8STRING;
         outform = MBSTRING_UTF8;
         encode_func = cbb_add_utf8;
         size_estimate = utf8_len;
+    } else {
+        OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_CHARACTERS);
+        return -1;
     }
 
     if (!out)
@@ -282,24 +283,16 @@
     return -1;
 }
 
-/* Return 1 if the character is permitted in a PrintableString */
-static int is_printable(uint32_t value)
+int asn1_is_printable(uint32_t value)
 {
-    int ch;
-    if (value > 0x7f)
+    if (value > 0x7f) {
         return 0;
-    ch = (int)value;
-    /*
-     * Note: we can't use 'isalnum' because certain accented characters may
-     * count as alphanumeric in some environments.
-     */
-    if ((ch >= 'a') && (ch <= 'z'))
-        return 1;
-    if ((ch >= 'A') && (ch <= 'Z'))
-        return 1;
-    if ((ch >= '0') && (ch <= '9'))
-        return 1;
-    if ((ch == ' ') || strchr("'()+,-./:=?", ch))
-        return 1;
-    return 0;
+    }
+    /* Note we cannot use |isalnum| because it is locale-dependent. */
+    return ('a' <= value && value <= 'z') ||  //
+           ('A' <= value && value <= 'Z') ||  //
+           ('0' <= value && value <= '9') ||  //
+           value == ' ' || value == '\'' || value == '(' || value == ')' ||
+           value == '+' || value == ',' || value == '-' || value == '.' ||
+           value == '/' || value == ':' || value == '=' || value == '?';
 }
diff --git a/deps/boringssl/src/crypto/asn1/a_object.c b/deps/boringssl/src/crypto/asn1/a_object.c
index bf386dd..de27e87 100644
--- a/deps/boringssl/src/crypto/asn1/a_object.c
+++ b/deps/boringssl/src/crypto/asn1/a_object.c
@@ -64,6 +64,7 @@
 #include <openssl/obj.h>
 
 #include "../internal.h"
+#include "internal.h"
 
 
 int i2d_ASN1_OBJECT(const ASN1_OBJECT *a, unsigned char **pp)
@@ -180,16 +181,13 @@
         }
     }
 
-    /*
-     * only the ASN1_OBJECTs from the 'table' will have values for ->sn or
-     * ->ln
-     */
     if ((a == NULL) || ((*a) == NULL) ||
         !((*a)->flags & ASN1_OBJECT_FLAG_DYNAMIC)) {
         if ((ret = ASN1_OBJECT_new()) == NULL)
             return (NULL);
-    } else
+    } else {
         ret = (*a);
+    }
 
     p = *pp;
     /* detach data from object */
@@ -208,12 +206,17 @@
         ret->flags |= ASN1_OBJECT_FLAG_DYNAMIC_DATA;
     }
     OPENSSL_memcpy(data, p, length);
+    /* If there are dynamic strings, free them here, and clear the flag */
+    if ((ret->flags & ASN1_OBJECT_FLAG_DYNAMIC_STRINGS) != 0) {
+        OPENSSL_free((char *)ret->sn);
+        OPENSSL_free((char *)ret->ln);
+        ret->flags &= ~ASN1_OBJECT_FLAG_DYNAMIC_STRINGS;
+    }
     /* reattach data to object, after which it remains const */
     ret->data = data;
     ret->length = length;
     ret->sn = NULL;
     ret->ln = NULL;
-    /* ret->flags=ASN1_OBJECT_FLAG_DYNAMIC; we know it is dynamic */
     p += length;
 
     if (a != NULL)
@@ -263,7 +266,7 @@
         OPENSSL_free(a);
 }
 
-ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data, int len,
+ASN1_OBJECT *ASN1_OBJECT_create(int nid, const unsigned char *data, int len,
                                 const char *sn, const char *ln)
 {
     ASN1_OBJECT o;
diff --git a/deps/boringssl/src/crypto/asn1/a_print.c b/deps/boringssl/src/crypto/asn1/a_print.c
index 2104521..c7efede 100644
--- a/deps/boringssl/src/crypto/asn1/a_print.c
+++ b/deps/boringssl/src/crypto/asn1/a_print.c
@@ -56,38 +56,28 @@
 
 #include <openssl/asn1.h>
 
-#include <openssl/err.h>
-#include <openssl/mem.h>
+#include <string.h>
+
+#include "internal.h"
+
 
 int ASN1_PRINTABLE_type(const unsigned char *s, int len)
 {
-    int c;
-    int ia5 = 0;
-    int t61 = 0;
-
-    if (len <= 0)
-        len = -1;
-    if (s == NULL)
-        return (V_ASN1_PRINTABLESTRING);
-
-    while ((*s) && (len-- != 0)) {
-        c = *(s++);
-        if (!(((c >= 'a') && (c <= 'z')) ||
-              ((c >= 'A') && (c <= 'Z')) ||
-              (c == ' ') ||
-              ((c >= '0') && (c <= '9')) ||
-              (c == ' ') || (c == '\'') ||
-              (c == '(') || (c == ')') ||
-              (c == '+') || (c == ',') ||
-              (c == '-') || (c == '.') ||
-              (c == '/') || (c == ':') || (c == '=') || (c == '?')))
-            ia5 = 1;
-        if (c & 0x80)
-            t61 = 1;
+    if (len < 0) {
+        len = strlen((const char *)s);
     }
-    if (t61)
-        return (V_ASN1_T61STRING);
-    if (ia5)
-        return (V_ASN1_IA5STRING);
-    return (V_ASN1_PRINTABLESTRING);
+
+    int printable = 1;
+    for (int i = 0; i < len; i++) {
+        unsigned char c = s[i];
+        if (c & 0x80) {
+            /* No need to continue iterating. */
+            return V_ASN1_T61STRING;
+        }
+        if (!asn1_is_printable(c)) {
+            printable = 0;
+        }
+    }
+
+    return printable ? V_ASN1_PRINTABLESTRING : V_ASN1_IA5STRING;
 }
diff --git a/deps/boringssl/src/crypto/asn1/a_strex.c b/deps/boringssl/src/crypto/asn1/a_strex.c
new file mode 100644
index 0000000..7829d67
--- /dev/null
+++ b/deps/boringssl/src/crypto/asn1/a_strex.c
@@ -0,0 +1,650 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/asn1.h>
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <openssl/bio.h>
+#include <openssl/mem.h>
+
+#include "charmap.h"
+#include "internal.h"
+
+
+// These flags must be distinct from |ESC_FLAGS| and fit in a byte.
+
+// Character is a valid PrintableString character
+#define CHARTYPE_PRINTABLESTRING 0x10
+// Character needs escaping if it is the first character
+#define CHARTYPE_FIRST_ESC_2253 0x20
+// Character needs escaping if it is the last character
+#define CHARTYPE_LAST_ESC_2253 0x40
+
+#define CHARTYPE_BS_ESC         (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
+
+#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
+                  ASN1_STRFLGS_ESC_QUOTE | \
+                  ASN1_STRFLGS_ESC_CTRL | \
+                  ASN1_STRFLGS_ESC_MSB)
+
+static int maybe_write(BIO *out, const void *buf, int len)
+{
+    /* If |out| is NULL, ignore the output but report the length. */
+    return out == NULL || BIO_write(out, buf, len) == len;
+}
+
+/*
+ * This function handles display of strings, one character at a time. It is
+ * passed an unsigned long for each character because it could come from 2 or
+ * even 4 byte forms.
+ */
+
+#define HEX_SIZE(type) (sizeof(type)*2)
+
+static int do_esc_char(uint32_t c, unsigned char flags, char *do_quotes,
+                       BIO *out)
+{
+    unsigned char chflgs, chtmp;
+    char tmphex[HEX_SIZE(uint32_t) + 3];
+
+    if (c > 0xffff) {
+        BIO_snprintf(tmphex, sizeof tmphex, "\\W%08" PRIX32, c);
+        if (!maybe_write(out, tmphex, 10))
+            return -1;
+        return 10;
+    }
+    if (c > 0xff) {
+        BIO_snprintf(tmphex, sizeof tmphex, "\\U%04" PRIX32, c);
+        if (!maybe_write(out, tmphex, 6))
+            return -1;
+        return 6;
+    }
+    chtmp = (unsigned char)c;
+    if (chtmp > 0x7f)
+        chflgs = flags & ASN1_STRFLGS_ESC_MSB;
+    else
+        chflgs = char_type[chtmp] & flags;
+    if (chflgs & CHARTYPE_BS_ESC) {
+        /* If we don't escape with quotes, signal we need quotes */
+        if (chflgs & ASN1_STRFLGS_ESC_QUOTE) {
+            if (do_quotes)
+                *do_quotes = 1;
+            if (!maybe_write(out, &chtmp, 1))
+                return -1;
+            return 1;
+        }
+        if (!maybe_write(out, "\\", 1))
+            return -1;
+        if (!maybe_write(out, &chtmp, 1))
+            return -1;
+        return 2;
+    }
+    if (chflgs & (ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB)) {
+        BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
+        if (!maybe_write(out, tmphex, 3))
+            return -1;
+        return 3;
+    }
+    /*
+     * If we get this far and do any escaping at all must escape the escape
+     * character itself: backslash.
+     */
+    if (chtmp == '\\' && flags & ESC_FLAGS) {
+        if (!maybe_write(out, "\\\\", 2))
+            return -1;
+        return 2;
+    }
+    if (!maybe_write(out, &chtmp, 1))
+        return -1;
+    return 1;
+}
+
+#define BUF_TYPE_WIDTH_MASK     0x7
+#define BUF_TYPE_CONVUTF8       0x8
+
+/*
+ * This function sends each character in a buffer to do_esc_char(). It
+ * interprets the content formats and converts to or from UTF8 as
+ * appropriate.
+ */
+
+static int do_buf(unsigned char *buf, int buflen,
+                  int type, unsigned char flags, char *quotes, BIO *out)
+{
+    int i, outlen, len, charwidth;
+    unsigned char orflags, *p, *q;
+    uint32_t c;
+    p = buf;
+    q = buf + buflen;
+    outlen = 0;
+    charwidth = type & BUF_TYPE_WIDTH_MASK;
+
+    switch (charwidth) {
+    case 4:
+        if (buflen & 3) {
+            OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_UNIVERSALSTRING);
+            return -1;
+        }
+        break;
+    case 2:
+        if (buflen & 1) {
+            OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_BMPSTRING);
+            return -1;
+        }
+        break;
+    default:
+        break;
+    }
+
+    while (p != q) {
+        if (p == buf && flags & ASN1_STRFLGS_ESC_2253)
+            orflags = CHARTYPE_FIRST_ESC_2253;
+        else
+            orflags = 0;
+        /* TODO(davidben): Replace this with |cbs_get_ucs2_be|, etc., to check
+         * for invalid codepoints. */
+        switch (charwidth) {
+        case 4:
+            c = ((uint32_t)*p++) << 24;
+            c |= ((uint32_t)*p++) << 16;
+            c |= ((uint32_t)*p++) << 8;
+            c |= *p++;
+            break;
+
+        case 2:
+            c = ((uint32_t)*p++) << 8;
+            c |= *p++;
+            break;
+
+        case 1:
+            c = *p++;
+            break;
+
+        case 0:
+            i = UTF8_getc(p, buflen, &c);
+            if (i < 0)
+                return -1;      /* Invalid UTF8String */
+            buflen -= i;
+            p += i;
+            break;
+        default:
+            return -1;          /* invalid width */
+        }
+        if (p == q && flags & ASN1_STRFLGS_ESC_2253)
+            orflags = CHARTYPE_LAST_ESC_2253;
+        if (type & BUF_TYPE_CONVUTF8) {
+            unsigned char utfbuf[6];
+            int utflen;
+            utflen = UTF8_putc(utfbuf, sizeof utfbuf, c);
+            for (i = 0; i < utflen; i++) {
+                /*
+                 * We don't need to worry about setting orflags correctly
+                 * because if utflen==1 its value will be correct anyway
+                 * otherwise each character will be > 0x7f and so the
+                 * character will never be escaped on first and last.
+                 */
+                len = do_esc_char(utfbuf[i], (unsigned char)(flags | orflags),
+                                  quotes, out);
+                if (len < 0)
+                    return -1;
+                outlen += len;
+            }
+        } else {
+            len = do_esc_char(c, (unsigned char)(flags | orflags), quotes, out);
+            if (len < 0)
+                return -1;
+            outlen += len;
+        }
+    }
+    return outlen;
+}
+
+/* This function hex dumps a buffer of characters */
+
+static int do_hex_dump(BIO *out, unsigned char *buf, int buflen)
+{
+    static const char hexdig[] = "0123456789ABCDEF";
+    unsigned char *p, *q;
+    char hextmp[2];
+    if (out) {
+        p = buf;
+        q = buf + buflen;
+        while (p != q) {
+            hextmp[0] = hexdig[*p >> 4];
+            hextmp[1] = hexdig[*p & 0xf];
+            if (!maybe_write(out, hextmp, 2))
+                return -1;
+            p++;
+        }
+    }
+    return buflen << 1;
+}
+
+/*
+ * "dump" a string. This is done when the type is unknown, or the flags
+ * request it. We can either dump the content octets or the entire DER
+ * encoding. This uses the RFC 2253 #01234 format.
+ */
+
+static int do_dump(unsigned long lflags, BIO *out, const ASN1_STRING *str)
+{
+    if (!maybe_write(out, "#", 1)) {
+        return -1;
+    }
+
+    /* If we don't dump DER encoding just dump content octets */
+    if (!(lflags & ASN1_STRFLGS_DUMP_DER)) {
+        int outlen = do_hex_dump(out, str->data, str->length);
+        if (outlen < 0) {
+            return -1;
+        }
+        return outlen + 1;
+    }
+
+    /*
+     * Placing the ASN1_STRING in a temporary ASN1_TYPE allows the DER encoding
+     * to readily obtained.
+     */
+    ASN1_TYPE t;
+    t.type = str->type;
+    /* Negative INTEGER and ENUMERATED values are the only case where
+     * |ASN1_STRING| and |ASN1_TYPE| types do not match.
+     *
+     * TODO(davidben): There are also some type fields which, in |ASN1_TYPE|, do
+     * not correspond to |ASN1_STRING|. It is unclear whether those are allowed
+     * in |ASN1_STRING| at all, or what the space of allowed types is.
+     * |ASN1_item_ex_d2i| will never produce such a value so, for now, we say
+     * this is an invalid input. But this corner of the library in general
+     * should be more robust. */
+    if (t.type == V_ASN1_NEG_INTEGER) {
+      t.type = V_ASN1_INTEGER;
+    } else if (t.type == V_ASN1_NEG_ENUMERATED) {
+      t.type = V_ASN1_ENUMERATED;
+    }
+    t.value.asn1_string = (ASN1_STRING *)str;
+    unsigned char *der_buf = NULL;
+    int der_len = i2d_ASN1_TYPE(&t, &der_buf);
+    if (der_len < 0) {
+        return -1;
+    }
+    int outlen = do_hex_dump(out, der_buf, der_len);
+    OPENSSL_free(der_buf);
+    if (outlen < 0) {
+        return -1;
+    }
+    return outlen + 1;
+}
+
+/*
+ * Lookup table to convert tags to character widths, 0 = UTF8 encoded, -1 is
+ * used for non string types otherwise it is the number of bytes per
+ * character
+ */
+
+static const signed char tag2nbyte[] = {
+    -1, -1, -1, -1, -1,         /* 0-4 */
+    -1, -1, -1, -1, -1,         /* 5-9 */
+    -1, -1, 0, -1,              /* 10-13 */
+    -1, -1, -1, -1,             /* 15-17 */
+    1, 1, 1,                    /* 18-20 */
+    -1, 1, 1, 1,                /* 21-24 */
+    -1, 1, -1,                  /* 25-27 */
+    4, -1, 2                    /* 28-30 */
+};
+
+/*
+ * This is the main function, print out an ASN1_STRING taking note of various
+ * escape and display options. Returns number of characters written or -1 if
+ * an error occurred.
+ */
+
+int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str, unsigned long lflags)
+{
+    int outlen, len;
+    int type;
+    char quotes;
+    unsigned char flags;
+    quotes = 0;
+    /* Keep a copy of escape flags */
+    flags = (unsigned char)(lflags & ESC_FLAGS);
+
+    type = str->type;
+
+    outlen = 0;
+
+    if (lflags & ASN1_STRFLGS_SHOW_TYPE) {
+        const char *tagname;
+        tagname = ASN1_tag2str(type);
+        outlen += strlen(tagname);
+        if (!maybe_write(out, tagname, outlen) || !maybe_write(out, ":", 1))
+            return -1;
+        outlen++;
+    }
+
+    /* Decide what to do with type, either dump content or display it */
+
+    /* Dump everything */
+    if (lflags & ASN1_STRFLGS_DUMP_ALL)
+        type = -1;
+    /* Ignore the string type */
+    else if (lflags & ASN1_STRFLGS_IGNORE_TYPE)
+        type = 1;
+    else {
+        /* Else determine width based on type */
+        if ((type > 0) && (type < 31))
+            type = tag2nbyte[type];
+        else
+            type = -1;
+        if ((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN))
+            type = 1;
+    }
+
+    if (type == -1) {
+        len = do_dump(lflags, out, str);
+        if (len < 0)
+            return -1;
+        outlen += len;
+        return outlen;
+    }
+
+    if (lflags & ASN1_STRFLGS_UTF8_CONVERT) {
+        /*
+         * Note: if string is UTF8 and we want to convert to UTF8 then we
+         * just interpret it as 1 byte per character to avoid converting
+         * twice.
+         */
+        if (!type)
+            type = 1;
+        else
+            type |= BUF_TYPE_CONVUTF8;
+    }
+
+    len = do_buf(str->data, str->length, type, flags, &quotes, NULL);
+    if (len < 0)
+        return -1;
+    outlen += len;
+    if (quotes)
+        outlen += 2;
+    if (!out)
+        return outlen;
+    if (quotes && !maybe_write(out, "\"", 1))
+        return -1;
+    if (do_buf(str->data, str->length, type, flags, NULL, out) < 0)
+        return -1;
+    if (quotes && !maybe_write(out, "\"", 1))
+        return -1;
+    return outlen;
+}
+
+int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str,
+                            unsigned long flags)
+{
+    BIO *bio = NULL;
+    if (fp != NULL) {
+        /* If |fp| is NULL, this function returns the number of bytes without
+         * writing. */
+        bio = BIO_new_fp(fp, BIO_NOCLOSE);
+        if (bio == NULL) {
+            return -1;
+        }
+    }
+    int ret = ASN1_STRING_print_ex(bio, str, flags);
+    BIO_free(bio);
+    return ret;
+}
+
+int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in)
+{
+    ASN1_STRING stmp, *str = &stmp;
+    int mbflag, type, ret;
+    if (!in)
+        return -1;
+    type = in->type;
+    if ((type < 0) || (type > 30))
+        return -1;
+    mbflag = tag2nbyte[type];
+    if (mbflag == -1)
+        return -1;
+    mbflag |= MBSTRING_FLAG;
+    stmp.data = NULL;
+    stmp.length = 0;
+    stmp.flags = 0;
+    ret = ASN1_mbstring_copy(&str, in->data, in->length, mbflag,
+                             B_ASN1_UTF8STRING);
+    if (ret < 0)
+        return ret;
+    *out = stmp.data;
+    return stmp.length;
+}
+
+int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v)
+{
+    int i, n;
+    char buf[80];
+    const char *p;
+
+    if (v == NULL)
+        return (0);
+    n = 0;
+    p = (const char *)v->data;
+    for (i = 0; i < v->length; i++) {
+        if ((p[i] > '~') || ((p[i] < ' ') &&
+                             (p[i] != '\n') && (p[i] != '\r')))
+            buf[n] = '.';
+        else
+            buf[n] = p[i];
+        n++;
+        if (n >= 80) {
+            if (BIO_write(bp, buf, n) <= 0)
+                return (0);
+            n = 0;
+        }
+    }
+    if (n > 0)
+        if (BIO_write(bp, buf, n) <= 0)
+            return (0);
+    return (1);
+}
+
+int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
+{
+    if (tm->type == V_ASN1_UTCTIME)
+        return ASN1_UTCTIME_print(bp, tm);
+    if (tm->type == V_ASN1_GENERALIZEDTIME)
+        return ASN1_GENERALIZEDTIME_print(bp, tm);
+    BIO_write(bp, "Bad time value", 14);
+    return (0);
+}
+
+static const char *const mon[12] = {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
+{
+    char *v;
+    int gmt = 0;
+    int i;
+    int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
+    char *f = NULL;
+    int f_len = 0;
+
+    i = tm->length;
+    v = (char *)tm->data;
+
+    if (i < 12)
+        goto err;
+    if (v[i - 1] == 'Z')
+        gmt = 1;
+    for (i = 0; i < 12; i++)
+        if ((v[i] > '9') || (v[i] < '0'))
+            goto err;
+    y = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] -
+                                                                        '0');
+    M = (v[4] - '0') * 10 + (v[5] - '0');
+    if ((M > 12) || (M < 1))
+        goto err;
+    d = (v[6] - '0') * 10 + (v[7] - '0');
+    h = (v[8] - '0') * 10 + (v[9] - '0');
+    m = (v[10] - '0') * 10 + (v[11] - '0');
+    if (tm->length >= 14 &&
+        (v[12] >= '0') && (v[12] <= '9') &&
+        (v[13] >= '0') && (v[13] <= '9')) {
+        s = (v[12] - '0') * 10 + (v[13] - '0');
+        /* Check for fractions of seconds. */
+        if (tm->length >= 15 && v[14] == '.') {
+            int l = tm->length;
+            f = &v[14];         /* The decimal point. */
+            f_len = 1;
+            while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
+                ++f_len;
+        }
+    }
+
+    if (BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
+                   mon[M - 1], d, h, m, s, f_len, f, y,
+                   (gmt) ? " GMT" : "") <= 0)
+        return (0);
+    else
+        return (1);
+ err:
+    BIO_write(bp, "Bad time value", 14);
+    return (0);
+}
+
+// consume_two_digits is a helper function for ASN1_UTCTIME_print. If |*v|,
+// assumed to be |*len| bytes long, has two leading digits, updates |*out| with
+// their value, updates |v| and |len|, and returns one. Otherwise, returns
+// zero.
+static int consume_two_digits(int* out, const char **v, int *len) {
+  if (*len < 2|| !isdigit((*v)[0]) || !isdigit((*v)[1])) {
+    return 0;
+  }
+  *out = ((*v)[0] - '0') * 10 + ((*v)[1] - '0');
+  *len -= 2;
+  *v += 2;
+  return 1;
+}
+
+// consume_zulu_timezone is a helper function for ASN1_UTCTIME_print. If |*v|,
+// assumed to be |*len| bytes long, starts with "Z" then it updates |*v| and
+// |*len| and returns one. Otherwise returns zero.
+static int consume_zulu_timezone(const char **v, int *len) {
+  if (*len == 0 || (*v)[0] != 'Z') {
+    return 0;
+  }
+
+  *len -= 1;
+  *v += 1;
+  return 1;
+}
+
+int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm) {
+  const char *v = (const char *)tm->data;
+  int len = tm->length;
+  int Y = 0, M = 0, D = 0, h = 0, m = 0, s = 0;
+
+  // YYMMDDhhmm are required to be present.
+  if (!consume_two_digits(&Y, &v, &len) ||
+      !consume_two_digits(&M, &v, &len) ||
+      !consume_two_digits(&D, &v, &len) ||
+      !consume_two_digits(&h, &v, &len) ||
+      !consume_two_digits(&m, &v, &len)) {
+    goto err;
+  }
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires seconds
+  // to be present, but historically this code has forgiven its absence.
+  consume_two_digits(&s, &v, &len);
+
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, specifies this
+  // interpretation of the year.
+  if (Y < 50) {
+    Y += 2000;
+  } else {
+    Y += 1900;
+  }
+  if (M > 12 || M == 0) {
+    goto err;
+  }
+  if (D > 31 || D == 0) {
+    goto err;
+  }
+  if (h > 23 || m > 59 || s > 60) {
+    goto err;
+  }
+
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires the "Z"
+  // to be present, but historically this code has forgiven its absence.
+  const int is_gmt = consume_zulu_timezone(&v, &len);
+
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, does not permit
+  // the specification of timezones using the +hhmm / -hhmm syntax, which is
+  // the only other thing that might legitimately be found at the end.
+  if (len) {
+    goto err;
+  }
+
+  return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s", mon[M - 1], D, h, m, s, Y,
+                    is_gmt ? " GMT" : "") > 0;
+
+err:
+  BIO_write(bp, "Bad time value", 14);
+  return 0;
+}
diff --git a/deps/boringssl/src/crypto/asn1/a_strnid.c b/deps/boringssl/src/crypto/asn1/a_strnid.c
index efbf0fa..f7ad084 100644
--- a/deps/boringssl/src/crypto/asn1/a_strnid.c
+++ b/deps/boringssl/src/crypto/asn1/a_strnid.c
@@ -69,53 +69,17 @@
 static STACK_OF(ASN1_STRING_TABLE) *stable = NULL;
 static void st_free(ASN1_STRING_TABLE *tbl);
 
-/*
- * This is the global mask for the mbstring functions: this is use to mask
- * out certain types (such as BMPString and UTF8String) because certain
- * software (e.g. Netscape) has problems with them.
- */
-
-static unsigned long global_mask = B_ASN1_UTF8STRING;
-
 void ASN1_STRING_set_default_mask(unsigned long mask)
 {
-    global_mask = mask;
 }
 
 unsigned long ASN1_STRING_get_default_mask(void)
 {
-    return global_mask;
+    return B_ASN1_UTF8STRING;
 }
 
-/*
- * This function sets the default to various "flavours" of configuration.
- * based on an ASCII string. Currently this is: MASK:XXXX : a numerical mask
- * value. nobmp : Don't use BMPStrings (just Printable, T61). pkix : PKIX
- * recommendation in RFC2459. utf8only : only use UTF8Strings (RFC2459
- * recommendation for 2004). default: the default value, Printable, T61, BMP.
- */
-
 int ASN1_STRING_set_default_mask_asc(const char *p)
 {
-    unsigned long mask;
-    char *end;
-    if (!strncmp(p, "MASK:", 5)) {
-        if (!p[5])
-            return 0;
-        mask = strtoul(p + 5, &end, 0);
-        if (*end)
-            return 0;
-    } else if (!strcmp(p, "nombstr"))
-        mask = ~((unsigned long)(B_ASN1_BMPSTRING | B_ASN1_UTF8STRING));
-    else if (!strcmp(p, "pkix"))
-        mask = ~((unsigned long)B_ASN1_T61STRING);
-    else if (!strcmp(p, "utf8only"))
-        mask = B_ASN1_UTF8STRING;
-    else if (!strcmp(p, "default"))
-        mask = 0xFFFFFFFFL;
-    else
-        return 0;
-    ASN1_STRING_set_default_mask(mask);
     return 1;
 }
 
@@ -139,13 +103,12 @@
     if (tbl) {
         mask = tbl->mask;
         if (!(tbl->flags & STABLE_NO_MASK))
-            mask &= global_mask;
+            mask &= B_ASN1_UTF8STRING;
         ret = ASN1_mbstring_ncopy(out, in, inlen, inform, mask,
                                   tbl->minsize, tbl->maxsize);
-    } else
-        ret =
-            ASN1_mbstring_copy(out, in, inlen, inform,
-                               DIRSTRING_TYPE & global_mask);
+    } else {
+        ret = ASN1_mbstring_copy(out, in, inlen, inform, B_ASN1_UTF8STRING);
+    }
     if (ret <= 0)
         return NULL;
     return *out;
@@ -155,7 +118,7 @@
  * Now the tables and helper functions for the string table:
  */
 
-/* size limits: this stuff is taken straight from RFC3280 */
+/* size limits: this stuff is taken straight from RFC 3280 */
 
 #define ub_name                         32768
 #define ub_common_name                  64
diff --git a/deps/boringssl/src/crypto/asn1/a_time.c b/deps/boringssl/src/crypto/asn1/a_time.c
index 98a9c3e..ad7f784 100644
--- a/deps/boringssl/src/crypto/asn1/a_time.c
+++ b/deps/boringssl/src/crypto/asn1/a_time.c
@@ -63,7 +63,7 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-#include "asn1_locl.h"
+#include "internal.h"
 
 /*
  * This is an implementation of the ASN1 Time structure which is: Time ::=
@@ -200,7 +200,7 @@
     return 0;
 }
 
-int ASN1_TIME_diff(int *pday, int *psec,
+int ASN1_TIME_diff(int *out_days, int *out_seconds,
                    const ASN1_TIME *from, const ASN1_TIME *to)
 {
     struct tm tm_from, tm_to;
@@ -208,5 +208,5 @@
         return 0;
     if (!asn1_time_to_tm(&tm_to, to))
         return 0;
-    return OPENSSL_gmtime_diff(pday, psec, &tm_from, &tm_to);
+    return OPENSSL_gmtime_diff(out_days, out_seconds, &tm_from, &tm_to);
 }
diff --git a/deps/boringssl/src/crypto/asn1/a_type.c b/deps/boringssl/src/crypto/asn1/a_type.c
index f320e49..c63030b 100644
--- a/deps/boringssl/src/crypto/asn1/a_type.c
+++ b/deps/boringssl/src/crypto/asn1/a_type.c
@@ -61,23 +61,33 @@
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 
-#include "asn1_locl.h"
+#include "internal.h"
 
 
 int ASN1_TYPE_get(const ASN1_TYPE *a)
 {
-    if ((a->value.ptr != NULL) || (a->type == V_ASN1_NULL))
-        return (a->type);
-    else
-        return (0);
+    if (a->type == V_ASN1_BOOLEAN || a->type == V_ASN1_NULL ||
+        a->value.ptr != NULL) {
+        return a->type;
+    }
+    return 0;
+}
+
+const void *asn1_type_value_as_pointer(const ASN1_TYPE *a)
+{
+    if (a->type == V_ASN1_BOOLEAN) {
+        return a->value.boolean ? (void *)0xff : NULL;
+    }
+    if (a->type == V_ASN1_NULL) {
+        return NULL;
+    }
+    return a->value.ptr;
 }
 
 void ASN1_TYPE_set(ASN1_TYPE *a, int type, void *value)
 {
-    if (a->value.ptr != NULL) {
-        ASN1_TYPE **tmp_a = &a;
-        ASN1_primitive_free((ASN1_VALUE **)tmp_a, NULL);
-    }
+    ASN1_TYPE **tmp_a = &a;
+    ASN1_primitive_free((ASN1_VALUE **)tmp_a, NULL);
     a->type = type;
     if (type == V_ASN1_BOOLEAN)
         a->value.boolean = value ? 0xff : 0;
diff --git a/deps/boringssl/src/crypto/asn1/a_utctm.c b/deps/boringssl/src/crypto/asn1/a_utctm.c
index d5bd0e4..21ea2cc 100644
--- a/deps/boringssl/src/crypto/asn1/a_utctm.c
+++ b/deps/boringssl/src/crypto/asn1/a_utctm.c
@@ -62,7 +62,7 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-#include "asn1_locl.h"
+#include "internal.h"
 
 
 int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d)
@@ -262,42 +262,3 @@
         return -1;
     return 0;
 }
-
-#if 0
-time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s)
-{
-    struct tm tm;
-    int offset;
-
-    OPENSSL_memset(&tm, '\0', sizeof tm);
-
-# define g2(p) (((p)[0]-'0')*10+(p)[1]-'0')
-    tm.tm_year = g2(s->data);
-    if (tm.tm_year < 50)
-        tm.tm_year += 100;
-    tm.tm_mon = g2(s->data + 2) - 1;
-    tm.tm_mday = g2(s->data + 4);
-    tm.tm_hour = g2(s->data + 6);
-    tm.tm_min = g2(s->data + 8);
-    tm.tm_sec = g2(s->data + 10);
-    if (s->data[12] == 'Z')
-        offset = 0;
-    else {
-        offset = g2(s->data + 13) * 60 + g2(s->data + 15);
-        if (s->data[12] == '-')
-            offset = -offset;
-    }
-# undef g2
-
-    return mktime(&tm) - offset * 60; /* FIXME: mktime assumes the current
-                                       * timezone instead of UTC, and unless
-                                       * we rewrite OpenSSL in Lisp we cannot
-                                       * locally change the timezone without
-                                       * possibly interfering with other
-                                       * parts of the program. timegm, which
-                                       * uses UTC, is non-standard. Also
-                                       * time_t is inappropriate for general
-                                       * UTC times because it may a 32 bit
-                                       * type. */
-}
-#endif
diff --git a/deps/boringssl/src/crypto/asn1/a_utf8.c b/deps/boringssl/src/crypto/asn1/a_utf8.c
index 119ccf9..922a780 100644
--- a/deps/boringssl/src/crypto/asn1/a_utf8.c
+++ b/deps/boringssl/src/crypto/asn1/a_utf8.c
@@ -59,7 +59,7 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-#include "asn1_locl.h"
+#include "internal.h"
 
 /* UTF8 utilities */
 
diff --git a/deps/boringssl/src/crypto/asn1/asn1_lib.c b/deps/boringssl/src/crypto/asn1/asn1_lib.c
index db8afac..b13a82a 100644
--- a/deps/boringssl/src/crypto/asn1/asn1_lib.c
+++ b/deps/boringssl/src/crypto/asn1/asn1_lib.c
@@ -370,8 +370,7 @@
 
 void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len)
 {
-    if (str->data)
-        OPENSSL_free(str->data);
+    OPENSSL_free(str->data);
     str->data = data;
     str->length = len;
 }
diff --git a/deps/boringssl/src/crypto/asn1/asn1_par.c b/deps/boringssl/src/crypto/asn1/asn1_par.c
index b1a01ed..282ad23 100644
--- a/deps/boringssl/src/crypto/asn1/asn1_par.c
+++ b/deps/boringssl/src/crypto/asn1/asn1_par.c
@@ -72,7 +72,7 @@
     };
 
     if ((tag == V_ASN1_NEG_INTEGER) || (tag == V_ASN1_NEG_ENUMERATED))
-        tag &= ~0x100;
+        tag &= ~V_ASN1_NEG;
 
     if (tag < 0 || tag > 30)
         return "(unknown)";
diff --git a/deps/boringssl/src/crypto/asn1/asn1_test.cc b/deps/boringssl/src/crypto/asn1/asn1_test.cc
index 7b09ba5..28a5998 100644
--- a/deps/boringssl/src/crypto/asn1/asn1_test.cc
+++ b/deps/boringssl/src/crypto/asn1/asn1_test.cc
@@ -15,6 +15,7 @@
 #include <limits.h>
 #include <stdio.h>
 
+#include <string>
 #include <vector>
 
 #include <gtest/gtest.h>
@@ -26,6 +27,7 @@
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 #include <openssl/span.h>
+#include <openssl/x509v3.h>
 
 #include "../test/test_util.h"
 
@@ -99,20 +101,23 @@
 template <typename T>
 void TestSerialize(T obj, int (*i2d_func)(T a, uint8_t **pp),
                    bssl::Span<const uint8_t> expected) {
-  int len = static_cast<int>(expected.size());
-  ASSERT_EQ(i2d_func(obj, nullptr), len);
+  // Test the allocating version first. It is easiest to debug.
+  uint8_t *ptr = nullptr;
+  int len = i2d_func(obj, &ptr);
+  ASSERT_GT(len, 0);
+  EXPECT_EQ(Bytes(expected), Bytes(ptr, len));
+  OPENSSL_free(ptr);
 
-  std::vector<uint8_t> buf(expected.size());
-  uint8_t *ptr = buf.data();
-  ASSERT_EQ(i2d_func(obj, &ptr), len);
+  len = i2d_func(obj, nullptr);
+  ASSERT_GT(len, 0);
+  EXPECT_EQ(len, static_cast<int>(expected.size()));
+
+  std::vector<uint8_t> buf(len);
+  ptr = buf.data();
+  len = i2d_func(obj, &ptr);
+  ASSERT_EQ(len, static_cast<int>(expected.size()));
   EXPECT_EQ(ptr, buf.data() + buf.size());
   EXPECT_EQ(Bytes(expected), Bytes(buf));
-
-  // Test the allocating version.
-  ptr = nullptr;
-  ASSERT_EQ(i2d_func(obj, &ptr), len);
-  EXPECT_EQ(Bytes(expected), Bytes(ptr, expected.size()));
-  OPENSSL_free(ptr);
 }
 
 TEST(ASN1Test, SerializeObject) {
@@ -125,11 +130,943 @@
 TEST(ASN1Test, SerializeBoolean) {
   static const uint8_t kTrue[] = {0x01, 0x01, 0xff};
   TestSerialize(0xff, i2d_ASN1_BOOLEAN, kTrue);
+  // Other constants are also correctly encoded as TRUE.
+  TestSerialize(1, i2d_ASN1_BOOLEAN, kTrue);
+  TestSerialize(0x100, i2d_ASN1_BOOLEAN, kTrue);
 
   static const uint8_t kFalse[] = {0x01, 0x01, 0x00};
   TestSerialize(0x00, i2d_ASN1_BOOLEAN, kFalse);
 }
 
+// The templates go through a different codepath, so test them separately.
+TEST(ASN1Test, SerializeEmbeddedBoolean) {
+  bssl::UniquePtr<BASIC_CONSTRAINTS> val(BASIC_CONSTRAINTS_new());
+  ASSERT_TRUE(val);
+
+  // BasicConstraints defaults to FALSE, so the encoding should be empty.
+  static const uint8_t kLeaf[] = {0x30, 0x00};
+  val->ca = 0;
+  TestSerialize(val.get(), i2d_BASIC_CONSTRAINTS, kLeaf);
+
+  // TRUE should always be encoded as 0xff, independent of what value the caller
+  // placed in the |ASN1_BOOLEAN|.
+  static const uint8_t kCA[] = {0x30, 0x03, 0x01, 0x01, 0xff};
+  val->ca = 0xff;
+  TestSerialize(val.get(), i2d_BASIC_CONSTRAINTS, kCA);
+  val->ca = 1;
+  TestSerialize(val.get(), i2d_BASIC_CONSTRAINTS, kCA);
+  val->ca = 0x100;
+  TestSerialize(val.get(), i2d_BASIC_CONSTRAINTS, kCA);
+}
+
+TEST(ASN1Test, ASN1Type) {
+  const struct {
+    int type;
+    std::vector<uint8_t> der;
+  } kTests[] = {
+      // BOOLEAN { TRUE }
+      {V_ASN1_BOOLEAN, {0x01, 0x01, 0xff}},
+      // BOOLEAN { FALSE }
+      {V_ASN1_BOOLEAN, {0x01, 0x01, 0x00}},
+      // OCTET_STRING { "a" }
+      {V_ASN1_OCTET_STRING, {0x04, 0x01, 0x61}},
+      // BIT_STRING { `01` `00` }
+      {V_ASN1_BIT_STRING, {0x03, 0x02, 0x01, 0x00}},
+      // INTEGER { -1 }
+      {V_ASN1_INTEGER, {0x02, 0x01, 0xff}},
+      // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2 }
+      {V_ASN1_OBJECT,
+       {0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7,
+        0x09, 0x02}},
+      // NULL {}
+      {V_ASN1_NULL, {0x05, 0x00}},
+      // SEQUENCE {}
+      {V_ASN1_SEQUENCE, {0x30, 0x00}},
+      // SET {}
+      {V_ASN1_SET, {0x31, 0x00}},
+      // [0] { UTF8String { "a" } }
+      {V_ASN1_OTHER, {0xa0, 0x03, 0x0c, 0x01, 0x61}},
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(Bytes(t.der));
+
+    // The input should successfully parse.
+    const uint8_t *ptr = t.der.data();
+    bssl::UniquePtr<ASN1_TYPE> val(d2i_ASN1_TYPE(nullptr, &ptr, t.der.size()));
+    ASSERT_TRUE(val);
+
+    EXPECT_EQ(ASN1_TYPE_get(val.get()), t.type);
+    EXPECT_EQ(val->type, t.type);
+    TestSerialize(val.get(), i2d_ASN1_TYPE, t.der);
+  }
+}
+
+// Test that reading |value.ptr| from a FALSE |ASN1_TYPE| behaves correctly. The
+// type historically supported this, so maintain the invariant in case external
+// code relies on it.
+TEST(ASN1Test, UnusedBooleanBits) {
+  // OCTET_STRING { "a" }
+  static const uint8_t kDER[] = {0x04, 0x01, 0x61};
+  const uint8_t *ptr = kDER;
+  bssl::UniquePtr<ASN1_TYPE> val(d2i_ASN1_TYPE(nullptr, &ptr, sizeof(kDER)));
+  ASSERT_TRUE(val);
+  EXPECT_EQ(V_ASN1_OCTET_STRING, val->type);
+  EXPECT_TRUE(val->value.ptr);
+
+  // Set |val| to a BOOLEAN containing FALSE.
+  ASN1_TYPE_set(val.get(), V_ASN1_BOOLEAN, NULL);
+  EXPECT_EQ(V_ASN1_BOOLEAN, val->type);
+  EXPECT_FALSE(val->value.ptr);
+}
+
+TEST(ASN1Test, ASN1ObjectReuse) {
+  // 1.2.840.113554.4.1.72585.2, an arbitrary unknown OID.
+  static const uint8_t kOID[] = {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12,
+                                 0x04, 0x01, 0x84, 0xb7, 0x09, 0x02};
+  ASN1_OBJECT *obj = ASN1_OBJECT_create(NID_undef, kOID, sizeof(kOID),
+                                        "short name", "long name");
+  ASSERT_TRUE(obj);
+
+  // OBJECT_IDENTIFIER { 1.3.101.112 }
+  static const uint8_t kDER[] = {0x06, 0x03, 0x2b, 0x65, 0x70};
+  const uint8_t *ptr = kDER;
+  EXPECT_TRUE(d2i_ASN1_OBJECT(&obj, &ptr, sizeof(kDER)));
+  EXPECT_EQ(NID_ED25519, OBJ_obj2nid(obj));
+  ASN1_OBJECT_free(obj);
+
+  // Repeat the test, this time overriding a static |ASN1_OBJECT|.
+  obj = OBJ_nid2obj(NID_rsaEncryption);
+  ptr = kDER;
+  EXPECT_TRUE(d2i_ASN1_OBJECT(&obj, &ptr, sizeof(kDER)));
+  EXPECT_EQ(NID_ED25519, OBJ_obj2nid(obj));
+  ASN1_OBJECT_free(obj);
+}
+
+TEST(ASN1Test, BitString) {
+  const size_t kNotWholeBytes = static_cast<size_t>(-1);
+  const struct {
+    std::vector<uint8_t> in;
+    size_t num_bytes;
+  } kValidInputs[] = {
+      // Empty bit string
+      {{0x03, 0x01, 0x00}, 0},
+      // 0b1
+      {{0x03, 0x02, 0x07, 0x80}, kNotWholeBytes},
+      // 0b1010
+      {{0x03, 0x02, 0x04, 0xa0}, kNotWholeBytes},
+      // 0b1010101
+      {{0x03, 0x02, 0x01, 0xaa}, kNotWholeBytes},
+      // 0b10101010
+      {{0x03, 0x02, 0x00, 0xaa}, 1},
+      // Bits 0 and 63 are set
+      {{0x03, 0x09, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, 8},
+      // 64 zero bits
+      {{0x03, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, 8},
+  };
+  for (const auto &test : kValidInputs) {
+    SCOPED_TRACE(Bytes(test.in));
+    // The input should parse and round-trip correctly.
+    const uint8_t *ptr = test.in.data();
+    bssl::UniquePtr<ASN1_BIT_STRING> val(
+        d2i_ASN1_BIT_STRING(nullptr, &ptr, test.in.size()));
+    ASSERT_TRUE(val);
+    TestSerialize(val.get(), i2d_ASN1_BIT_STRING, test.in);
+
+    // Check the byte count.
+    size_t num_bytes;
+    if (test.num_bytes == kNotWholeBytes) {
+      EXPECT_FALSE(ASN1_BIT_STRING_num_bytes(val.get(), &num_bytes));
+    } else {
+      ASSERT_TRUE(ASN1_BIT_STRING_num_bytes(val.get(), &num_bytes));
+      EXPECT_EQ(num_bytes, test.num_bytes);
+    }
+  }
+
+  const std::vector<uint8_t> kInvalidInputs[] = {
+      // Wrong tag
+      {0x04, 0x01, 0x00},
+      // Missing leading byte
+      {0x03, 0x00},
+      // Leading byte too high
+      {0x03, 0x02, 0x08, 0x00},
+      {0x03, 0x02, 0xff, 0x00},
+      // TODO(https://crbug.com/boringssl/354): Reject these inputs.
+      // Empty bit strings must have a zero leading byte.
+      // {0x03, 0x01, 0x01},
+      // Unused bits must all be zero.
+      // {0x03, 0x02, 0x06, 0xc1 /* 0b11000001 */},
+  };
+  for (const auto &test : kInvalidInputs) {
+    SCOPED_TRACE(Bytes(test));
+    const uint8_t *ptr = test.data();
+    bssl::UniquePtr<ASN1_BIT_STRING> val(
+        d2i_ASN1_BIT_STRING(nullptr, &ptr, test.size()));
+    EXPECT_FALSE(val);
+  }
+}
+
+TEST(ASN1Test, SetBit) {
+  bssl::UniquePtr<ASN1_BIT_STRING> val(ASN1_BIT_STRING_new());
+  ASSERT_TRUE(val);
+  static const uint8_t kBitStringEmpty[] = {0x03, 0x01, 0x00};
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitStringEmpty);
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 0));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 100));
+
+  // Set a few bits via |ASN1_BIT_STRING_set_bit|.
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 0, 1));
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 1, 1));
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 2, 0));
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 3, 1));
+  static const uint8_t kBitString1101[] = {0x03, 0x02, 0x04, 0xd0};
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitString1101);
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 0));
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 1));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 2));
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 3));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 4));
+
+  // Bits that were set may be cleared.
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 1, 0));
+  static const uint8_t kBitString1001[] = {0x03, 0x02, 0x04, 0x90};
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitString1001);
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 0));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 1));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 2));
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 3));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 4));
+
+  // Clearing trailing bits truncates the string.
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 3, 0));
+  static const uint8_t kBitString1[] = {0x03, 0x02, 0x07, 0x80};
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitString1);
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 0));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 1));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 2));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 3));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 4));
+
+  // Bits may be set beyond the end of the string.
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 63, 1));
+  static const uint8_t kBitStringLong[] = {0x03, 0x09, 0x00, 0x80, 0x00, 0x00,
+                                           0x00, 0x00, 0x00, 0x00, 0x01};
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitStringLong);
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 0));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 62));
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 63));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 64));
+
+  // The string can be truncated back down again.
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 63, 0));
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitString1);
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 0));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 62));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 63));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 64));
+
+  // |ASN1_BIT_STRING_set_bit| also truncates when starting from a parsed
+  // string.
+  const uint8_t *ptr = kBitStringLong;
+  val.reset(d2i_ASN1_BIT_STRING(nullptr, &ptr, sizeof(kBitStringLong)));
+  ASSERT_TRUE(val);
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitStringLong);
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 63, 0));
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitString1);
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 0));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 62));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 63));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 64));
+
+  // A parsed bit string preserves trailing zero bits.
+  static const uint8_t kBitString10010[] = {0x03, 0x02, 0x03, 0x90};
+  ptr = kBitString10010;
+  val.reset(d2i_ASN1_BIT_STRING(nullptr, &ptr, sizeof(kBitString10010)));
+  ASSERT_TRUE(val);
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitString10010);
+  // But |ASN1_BIT_STRING_set_bit| will truncate it even if otherwise a no-op.
+  ASSERT_TRUE(ASN1_BIT_STRING_set_bit(val.get(), 0, 1));
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitString1001);
+  EXPECT_EQ(1, ASN1_BIT_STRING_get_bit(val.get(), 0));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 62));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 63));
+  EXPECT_EQ(0, ASN1_BIT_STRING_get_bit(val.get(), 64));
+
+  // By default, a BIT STRING implicitly truncates trailing zeros.
+  val.reset(ASN1_BIT_STRING_new());
+  ASSERT_TRUE(val);
+  static const uint8_t kZeros[64] = {0};
+  ASSERT_TRUE(ASN1_STRING_set(val.get(), kZeros, sizeof(kZeros)));
+  TestSerialize(val.get(), i2d_ASN1_BIT_STRING, kBitStringEmpty);
+}
+
+TEST(ASN1Test, StringToUTF8) {
+  static const struct {
+    std::vector<uint8_t> in;
+    int type;
+    const char *expected;
+  } kTests[] = {
+      // Non-minimal, two-byte UTF-8.
+      {{0xc0, 0x81}, V_ASN1_UTF8STRING, nullptr},
+      // Non-minimal, three-byte UTF-8.
+      {{0xe0, 0x80, 0x81}, V_ASN1_UTF8STRING, nullptr},
+      // Non-minimal, four-byte UTF-8.
+      {{0xf0, 0x80, 0x80, 0x81}, V_ASN1_UTF8STRING, nullptr},
+      // Truncated, four-byte UTF-8.
+      {{0xf0, 0x80, 0x80}, V_ASN1_UTF8STRING, nullptr},
+      // Low-surrogate value.
+      {{0xed, 0xa0, 0x80}, V_ASN1_UTF8STRING, nullptr},
+      // High-surrogate value.
+      {{0xed, 0xb0, 0x81}, V_ASN1_UTF8STRING, nullptr},
+      // Initial BOMs should be rejected from UCS-2 and UCS-4.
+      {{0xfe, 0xff, 0, 88}, V_ASN1_BMPSTRING, nullptr},
+      {{0, 0, 0xfe, 0xff, 0, 0, 0, 88}, V_ASN1_UNIVERSALSTRING, nullptr},
+      // Otherwise, BOMs should pass through.
+      {{0, 88, 0xfe, 0xff}, V_ASN1_BMPSTRING, "X\xef\xbb\xbf"},
+      {{0, 0, 0, 88, 0, 0, 0xfe, 0xff}, V_ASN1_UNIVERSALSTRING,
+       "X\xef\xbb\xbf"},
+      // The maximum code-point should pass though.
+      {{0, 16, 0xff, 0xfd}, V_ASN1_UNIVERSALSTRING, "\xf4\x8f\xbf\xbd"},
+      // Values outside the Unicode space should not.
+      {{0, 17, 0, 0}, V_ASN1_UNIVERSALSTRING, nullptr},
+      // Non-characters should be rejected.
+      {{0, 1, 0xff, 0xff}, V_ASN1_UNIVERSALSTRING, nullptr},
+      {{0, 1, 0xff, 0xfe}, V_ASN1_UNIVERSALSTRING, nullptr},
+      {{0, 0, 0xfd, 0xd5}, V_ASN1_UNIVERSALSTRING, nullptr},
+      // BMPString is UCS-2, not UTF-16, so surrogate pairs are invalid.
+      {{0xd8, 0, 0xdc, 1}, V_ASN1_BMPSTRING, nullptr},
+  };
+
+  for (const auto &test : kTests) {
+    SCOPED_TRACE(Bytes(test.in));
+    SCOPED_TRACE(test.type);
+    bssl::UniquePtr<ASN1_STRING> s(ASN1_STRING_type_new(test.type));
+    ASSERT_TRUE(s);
+    ASSERT_TRUE(ASN1_STRING_set(s.get(), test.in.data(), test.in.size()));
+
+    uint8_t *utf8;
+    const int utf8_len = ASN1_STRING_to_UTF8(&utf8, s.get());
+    EXPECT_EQ(utf8_len < 0, test.expected == nullptr);
+    if (utf8_len >= 0) {
+      if (test.expected != nullptr) {
+        EXPECT_EQ(Bytes(test.expected), Bytes(utf8, utf8_len));
+      }
+      OPENSSL_free(utf8);
+    } else {
+      ERR_clear_error();
+    }
+  }
+}
+
+static std::string ASN1StringToStdString(const ASN1_STRING *str) {
+  return std::string(ASN1_STRING_get0_data(str),
+                     ASN1_STRING_get0_data(str) + ASN1_STRING_length(str));
+}
+
+TEST(ASN1Test, SetTime) {
+  static const struct {
+    time_t time;
+    const char *generalized;
+    const char *utc;
+  } kTests[] = {
+    {-631152001, "19491231235959Z", nullptr},
+    {-631152000, "19500101000000Z", "500101000000Z"},
+    {0, "19700101000000Z", "700101000000Z"},
+    {981173106, "20010203040506Z", "010203040506Z"},
+#if defined(OPENSSL_64_BIT)
+    // TODO(https://crbug.com/boringssl/416): These cases overflow 32-bit
+    // |time_t| and do not consistently work on 32-bit platforms. For now,
+    // disable the tests on 32-bit. Re-enable them once the bug is fixed.
+    {2524607999, "20491231235959Z", "491231235959Z"},
+    {2524608000, "20500101000000Z", nullptr},
+    // Test boundary conditions.
+    {-62167219200, "00000101000000Z", nullptr},
+    {-62167219201, nullptr, nullptr},
+    {253402300799, "99991231235959Z", nullptr},
+    {253402300800, nullptr, nullptr},
+#endif
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(t.time);
+#if defined(OPENSSL_WINDOWS)
+    // Windows |time_t| functions can only handle 1970 through 3000. See
+    // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/gmtime-s-gmtime32-s-gmtime64-s?view=msvc-160
+    if (t.time < 0 || int64_t{t.time} > 32535215999) {
+      continue;
+    }
+#endif
+
+    bssl::UniquePtr<ASN1_UTCTIME> utc(ASN1_UTCTIME_set(nullptr, t.time));
+    if (t.utc) {
+      ASSERT_TRUE(utc);
+      EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(utc.get()));
+      EXPECT_EQ(t.utc, ASN1StringToStdString(utc.get()));
+    } else {
+      EXPECT_FALSE(utc);
+    }
+
+    bssl::UniquePtr<ASN1_GENERALIZEDTIME> generalized(
+        ASN1_GENERALIZEDTIME_set(nullptr, t.time));
+    if (t.generalized) {
+      ASSERT_TRUE(generalized);
+      EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(generalized.get()));
+      EXPECT_EQ(t.generalized, ASN1StringToStdString(generalized.get()));
+    } else {
+      EXPECT_FALSE(generalized);
+    }
+
+    bssl::UniquePtr<ASN1_TIME> choice(ASN1_TIME_set(nullptr, t.time));
+    if (t.generalized) {
+      ASSERT_TRUE(choice);
+      if (t.utc) {
+        EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(choice.get()));
+        EXPECT_EQ(t.utc, ASN1StringToStdString(choice.get()));
+      } else {
+        EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(choice.get()));
+        EXPECT_EQ(t.generalized, ASN1StringToStdString(choice.get()));
+      }
+    } else {
+      EXPECT_FALSE(choice);
+    }
+  }
+}
+
+static std::vector<uint8_t> StringToVector(const std::string &str) {
+  return std::vector<uint8_t>(str.begin(), str.end());
+}
+
+TEST(ASN1Test, StringPrintEx) {
+  const struct {
+    int type;
+    std::vector<uint8_t> data;
+    int str_flags;
+    unsigned long flags;
+    std::string expected;
+  } kTests[] = {
+      // A string like "hello" is never escaped or quoted.
+      // |ASN1_STRFLGS_ESC_QUOTE| only introduces quotes when needed. Note
+      // OpenSSL interprets T61String as Latin-1.
+      {V_ASN1_T61STRING, StringToVector("hello"), 0, 0, "hello"},
+      {V_ASN1_T61STRING, StringToVector("hello"), 0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB,
+       "hello"},
+      {V_ASN1_T61STRING, StringToVector("hello"), 0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB |
+           ASN1_STRFLGS_ESC_QUOTE,
+       "hello"},
+
+      // By default, 8-bit characters are printed without escaping.
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       0,
+       std::string(1, '\0') + "\n\x80\xff,+\"\\<>;"},
+
+      // Flags control different escapes. Note that any escape flag will cause
+      // blackslashes to be escaped.
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_2253,
+       std::string(1, '\0') + "\n\x80\xff\\,\\+\\\"\\\\\\<\\>\\;"},
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_CTRL,
+       "\\00\\0A\x80\xff,+\"\\\\<>;"},
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_MSB,
+       std::string(1, '\0') + "\n\\80\\FF,+\"\\\\<>;"},
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB,
+       "\\00\\0A\\80\\FF\\,\\+\\\"\\\\\\<\\>\\;"},
+
+      // When quoted, fewer characters need to be escaped in RFC 2253.
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, ',', '+', '"', '\\', '<', '>', ';'},
+       0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB |
+           ASN1_STRFLGS_ESC_QUOTE,
+       "\"\\00\\0A\\80\\FF,+\\\"\\\\<>;\""},
+
+      // If no characters benefit from quotes, no quotes are added.
+      {V_ASN1_T61STRING,
+       {0, '\n', 0x80, 0xff, '"', '\\'},
+       0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB |
+           ASN1_STRFLGS_ESC_QUOTE,
+       "\\00\\0A\\80\\FF\\\"\\\\"},
+
+      // RFC 2253 only escapes spaces at the start and end of a string.
+      {V_ASN1_T61STRING, StringToVector("   "), 0, ASN1_STRFLGS_ESC_2253,
+       "\\  \\ "},
+      {V_ASN1_T61STRING, StringToVector("   "), 0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_QUOTE, "\"   \""},
+
+      // RFC 2253 only escapes # at the start of a string.
+      {V_ASN1_T61STRING, StringToVector("###"), 0, ASN1_STRFLGS_ESC_2253,
+       "\\###"},
+      {V_ASN1_T61STRING, StringToVector("###"), 0,
+       ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_QUOTE, "\"###\""},
+
+      // By default, strings are decoded and Unicode code points are
+      // individually escaped.
+      {V_ASN1_UTF8STRING, StringToVector("a\xc2\x80\xc4\x80\xf0\x90\x80\x80"),
+       0, ASN1_STRFLGS_ESC_MSB, "a\\80\\U0100\\W00010000"},
+      {V_ASN1_BMPSTRING,
+       {0x00, 'a', 0x00, 0x80, 0x01, 0x00},
+       0,
+       ASN1_STRFLGS_ESC_MSB,
+       "a\\80\\U0100"},
+      {V_ASN1_UNIVERSALSTRING,
+       {0x00, 0x00, 0x00, 'a',   //
+        0x00, 0x00, 0x00, 0x80,  //
+        0x00, 0x00, 0x01, 0x00,  //
+        0x00, 0x01, 0x00, 0x00},
+       0,
+       ASN1_STRFLGS_ESC_MSB,
+       "a\\80\\U0100\\W00010000"},
+
+      // |ASN1_STRFLGS_UTF8_CONVERT| normalizes everything to UTF-8 and then
+      // escapes individual bytes.
+      {V_ASN1_IA5STRING, StringToVector("a\x80"), 0,
+       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT, "a\\C2\\80"},
+      {V_ASN1_T61STRING, StringToVector("a\x80"), 0,
+       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT, "a\\C2\\80"},
+      {V_ASN1_UTF8STRING, StringToVector("a\xc2\x80\xc4\x80\xf0\x90\x80\x80"),
+       0, ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT,
+       "a\\C2\\80\\C4\\80\\F0\\90\\80\\80"},
+      {V_ASN1_BMPSTRING,
+       {0x00, 'a', 0x00, 0x80, 0x01, 0x00},
+       0,
+       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT,
+       "a\\C2\\80\\C4\\80"},
+      {V_ASN1_UNIVERSALSTRING,
+       {0x00, 0x00, 0x00, 'a',   //
+        0x00, 0x00, 0x00, 0x80,  //
+        0x00, 0x00, 0x01, 0x00,  //
+        0x00, 0x01, 0x00, 0x00},
+       0,
+       ASN1_STRFLGS_ESC_MSB | ASN1_STRFLGS_UTF8_CONVERT,
+       "a\\C2\\80\\C4\\80\\F0\\90\\80\\80"},
+
+      // The same as above, but without escaping the UTF-8 encoding.
+      {V_ASN1_IA5STRING, StringToVector("a\x80"), 0, ASN1_STRFLGS_UTF8_CONVERT,
+       "a\xc2\x80"},
+      {V_ASN1_T61STRING, StringToVector("a\x80"), 0, ASN1_STRFLGS_UTF8_CONVERT,
+       "a\xc2\x80"},
+      {V_ASN1_UTF8STRING, StringToVector("a\xc2\x80\xc4\x80\xf0\x90\x80\x80"),
+       0, ASN1_STRFLGS_UTF8_CONVERT, "a\xc2\x80\xc4\x80\xf0\x90\x80\x80"},
+      {V_ASN1_BMPSTRING,
+       {0x00, 'a', 0x00, 0x80, 0x01, 0x00},
+       0,
+       ASN1_STRFLGS_UTF8_CONVERT,
+       "a\xc2\x80\xc4\x80"},
+      {V_ASN1_UNIVERSALSTRING,
+       {0x00, 0x00, 0x00, 'a',   //
+        0x00, 0x00, 0x00, 0x80,  //
+        0x00, 0x00, 0x01, 0x00,  //
+        0x00, 0x01, 0x00, 0x00},
+       0,
+       ASN1_STRFLGS_UTF8_CONVERT,
+       "a\xc2\x80\xc4\x80\xf0\x90\x80\x80"},
+
+      // Types that cannot be decoded are, by default, treated as a byte string.
+      {V_ASN1_OCTET_STRING, {0xff}, 0, 0, "\xff"},
+      {-1, {0xff}, 0, 0, "\xff"},
+      {100, {0xff}, 0, 0, "\xff"},
+
+      // |ASN1_STRFLGS_UTF8_CONVERT| still converts these bytes to UTF-8.
+      //
+      // TODO(davidben): This seems like a bug. Although it's unclear because
+      // the non-RFC-2253 options aren't especially sound. Can we just remove
+      // them?
+      {V_ASN1_OCTET_STRING, {0xff}, 0, ASN1_STRFLGS_UTF8_CONVERT, "\xc3\xbf"},
+      {-1, {0xff}, 0, ASN1_STRFLGS_UTF8_CONVERT, "\xc3\xbf"},
+      {100, {0xff}, 0, ASN1_STRFLGS_UTF8_CONVERT, "\xc3\xbf"},
+
+      // |ASN1_STRFLGS_IGNORE_TYPE| causes the string type to be ignored, so it
+      // is always treated as a byte string, even if it is not a valid encoding.
+      {V_ASN1_UTF8STRING, {0xff}, 0, ASN1_STRFLGS_IGNORE_TYPE, "\xff"},
+      {V_ASN1_BMPSTRING, {0xff}, 0, ASN1_STRFLGS_IGNORE_TYPE, "\xff"},
+      {V_ASN1_UNIVERSALSTRING, {0xff}, 0, ASN1_STRFLGS_IGNORE_TYPE, "\xff"},
+
+      // |ASN1_STRFLGS_SHOW_TYPE| prepends the type name.
+      {V_ASN1_UTF8STRING, {'a'}, 0, ASN1_STRFLGS_SHOW_TYPE, "UTF8STRING:a"},
+      {-1, {'a'}, 0, ASN1_STRFLGS_SHOW_TYPE, "(unknown):a"},
+      {100, {'a'}, 0, ASN1_STRFLGS_SHOW_TYPE, "(unknown):a"},
+
+      // |ASN1_STRFLGS_DUMP_ALL| and |ASN1_STRFLGS_DUMP_UNKNOWN| cause
+      // non-string types to be printed in hex, though without the DER wrapper
+      // by default.
+      {V_ASN1_UTF8STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_UNKNOWN, "\\U2603"},
+      {V_ASN1_UTF8STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_ALL, "#E29883"},
+      {V_ASN1_OCTET_STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_UNKNOWN, "#E29883"},
+      {V_ASN1_OCTET_STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_ALL, "#E29883"},
+
+      // |ASN1_STRFLGS_DUMP_DER| includes the entire element.
+      {V_ASN1_UTF8STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER, "#0C03E29883"},
+      {V_ASN1_OCTET_STRING, StringToVector("\xe2\x98\x83"), 0,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER, "#0403E29883"},
+      {V_ASN1_BIT_STRING,
+       {0x80},
+       ASN1_STRING_FLAG_BITS_LEFT | 4,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
+       "#03020480"},
+      // INTEGER { 1 }
+      {V_ASN1_INTEGER,
+       {0x01},
+       0,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
+       "#020101"},
+      // INTEGER { -1 }
+      {V_ASN1_NEG_INTEGER,
+       {0x01},
+       0,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
+       "#0201FF"},
+      // ENUMERATED { 1 }
+      {V_ASN1_ENUMERATED,
+       {0x01},
+       0,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
+       "#0A0101"},
+      // ENUMERATED { -1 }
+      {V_ASN1_NEG_ENUMERATED,
+       {0x01},
+       0,
+       ASN1_STRFLGS_DUMP_ALL | ASN1_STRFLGS_DUMP_DER,
+       "#0A01FF"},
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(t.type);
+    SCOPED_TRACE(Bytes(t.data));
+    SCOPED_TRACE(t.str_flags);
+    SCOPED_TRACE(t.flags);
+
+    bssl::UniquePtr<ASN1_STRING> str(ASN1_STRING_type_new(t.type));
+    ASSERT_TRUE(ASN1_STRING_set(str.get(), t.data.data(), t.data.size()));
+    str->flags = t.str_flags;
+
+    // If the |BIO| is null, it should measure the size.
+    int len = ASN1_STRING_print_ex(nullptr, str.get(), t.flags);
+    EXPECT_EQ(len, static_cast<int>(t.expected.size()));
+
+    // Measuring the size should also work for the |FILE| version
+    len = ASN1_STRING_print_ex_fp(nullptr, str.get(), t.flags);
+    EXPECT_EQ(len, static_cast<int>(t.expected.size()));
+
+    // Actually print the string.
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+    ASSERT_TRUE(bio);
+    len = ASN1_STRING_print_ex(bio.get(), str.get(), t.flags);
+    EXPECT_EQ(len, static_cast<int>(t.expected.size()));
+
+    const uint8_t *bio_contents;
+    size_t bio_len;
+    ASSERT_TRUE(BIO_mem_contents(bio.get(), &bio_contents, &bio_len));
+    EXPECT_EQ(t.expected, std::string(bio_contents, bio_contents + bio_len));
+  }
+
+  const struct {
+    int type;
+    std::vector<uint8_t> data;
+    int str_flags;
+    unsigned long flags;
+  } kUnprintableTests[] = {
+      // When decoding strings, invalid codepoints are errors.
+      {V_ASN1_UTF8STRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
+      {V_ASN1_BMPSTRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
+      {V_ASN1_BMPSTRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
+      {V_ASN1_UNIVERSALSTRING, {0xff}, 0, ASN1_STRFLGS_ESC_MSB},
+  };
+  for (const auto &t : kUnprintableTests) {
+    SCOPED_TRACE(t.type);
+    SCOPED_TRACE(Bytes(t.data));
+    SCOPED_TRACE(t.str_flags);
+    SCOPED_TRACE(t.flags);
+
+    bssl::UniquePtr<ASN1_STRING> str(ASN1_STRING_type_new(t.type));
+    ASSERT_TRUE(ASN1_STRING_set(str.get(), t.data.data(), t.data.size()));
+    str->flags = t.str_flags;
+
+    // If the |BIO| is null, it should measure the size.
+    int len = ASN1_STRING_print_ex(nullptr, str.get(), t.flags);
+    EXPECT_EQ(len, -1);
+    ERR_clear_error();
+
+    // Measuring the size should also work for the |FILE| version
+    len = ASN1_STRING_print_ex_fp(nullptr, str.get(), t.flags);
+    EXPECT_EQ(len, -1);
+    ERR_clear_error();
+
+    // Actually print the string.
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+    ASSERT_TRUE(bio);
+    len = ASN1_STRING_print_ex(bio.get(), str.get(), t.flags);
+    EXPECT_EQ(len, -1);
+    ERR_clear_error();
+  }
+}
+
+TEST(ASN1Test, MBString) {
+  const unsigned long kAll = B_ASN1_PRINTABLESTRING | B_ASN1_IA5STRING |
+                             B_ASN1_T61STRING | B_ASN1_BMPSTRING |
+                             B_ASN1_UNIVERSALSTRING | B_ASN1_UTF8STRING;
+
+  const struct {
+    int format;
+    std::vector<uint8_t> in;
+    unsigned long mask;
+    int expected_type;
+    std::vector<uint8_t> expected_data;
+    int num_codepoints;
+  } kTests[] = {
+      // Given a choice of formats, we pick the smallest that fits.
+      {MBSTRING_UTF8, {}, kAll, V_ASN1_PRINTABLESTRING, {}, 0},
+      {MBSTRING_UTF8, {'a'}, kAll, V_ASN1_PRINTABLESTRING, {'a'}, 1},
+      {MBSTRING_UTF8,
+       {'a', 'A', '0', '\'', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'},
+       kAll,
+       V_ASN1_PRINTABLESTRING,
+       {'a', 'A', '0', '\'', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'},
+       14},
+      {MBSTRING_UTF8, {'*'}, kAll, V_ASN1_IA5STRING, {'*'}, 1},
+      {MBSTRING_UTF8, {'\n'}, kAll, V_ASN1_IA5STRING, {'\n'}, 1},
+      {MBSTRING_UTF8,
+       {0xc2, 0x80 /* U+0080 */},
+       kAll,
+       V_ASN1_T61STRING,
+       {0x80},
+       1},
+      {MBSTRING_UTF8,
+       {0xc4, 0x80 /* U+0100 */},
+       kAll,
+       V_ASN1_BMPSTRING,
+       {0x01, 0x00},
+       1},
+      {MBSTRING_UTF8,
+       {0xf0, 0x90, 0x80, 0x80 /* U+10000 */},
+       kAll,
+       V_ASN1_UNIVERSALSTRING,
+       {0x00, 0x01, 0x00, 0x00},
+       1},
+      {MBSTRING_UTF8,
+       {0xf0, 0x90, 0x80, 0x80 /* U+10000 */},
+       kAll & ~B_ASN1_UNIVERSALSTRING,
+       V_ASN1_UTF8STRING,
+       {0xf0, 0x90, 0x80, 0x80},
+       1},
+
+      // NUL is not printable. It should also not terminate iteration.
+      {MBSTRING_UTF8, {0}, kAll, V_ASN1_IA5STRING, {0}, 1},
+      {MBSTRING_UTF8, {0, 'a'}, kAll, V_ASN1_IA5STRING, {0, 'a'}, 2},
+
+      // When a particular format is specified, we use it.
+      {MBSTRING_UTF8,
+       {'a'},
+       B_ASN1_PRINTABLESTRING,
+       V_ASN1_PRINTABLESTRING,
+       {'a'},
+       1},
+      {MBSTRING_UTF8, {'a'}, B_ASN1_IA5STRING, V_ASN1_IA5STRING, {'a'}, 1},
+      {MBSTRING_UTF8, {'a'}, B_ASN1_T61STRING, V_ASN1_T61STRING, {'a'}, 1},
+      {MBSTRING_UTF8, {'a'}, B_ASN1_UTF8STRING, V_ASN1_UTF8STRING, {'a'}, 1},
+      {MBSTRING_UTF8,
+       {'a'},
+       B_ASN1_BMPSTRING,
+       V_ASN1_BMPSTRING,
+       {0x00, 'a'},
+       1},
+      {MBSTRING_UTF8,
+       {'a'},
+       B_ASN1_UNIVERSALSTRING,
+       V_ASN1_UNIVERSALSTRING,
+       {0x00, 0x00, 0x00, 'a'},
+       1},
+
+      // A long string with characters of many widths, to test sizes are
+      // measured in code points.
+      {MBSTRING_UTF8,
+       {
+           'a',                     //
+           0xc2, 0x80,              // U+0080
+           0xc4, 0x80,              // U+0100
+           0xf0, 0x90, 0x80, 0x80,  // U+10000
+       },
+       B_ASN1_UNIVERSALSTRING,
+       V_ASN1_UNIVERSALSTRING,
+       {
+           0x00, 0x00, 0x00, 'a',   //
+           0x00, 0x00, 0x00, 0x80,  //
+           0x00, 0x00, 0x01, 0x00,  //
+           0x00, 0x01, 0x00, 0x00,  //
+       },
+       4},
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(t.format);
+    SCOPED_TRACE(Bytes(t.in));
+    SCOPED_TRACE(t.mask);
+
+    // Passing in nullptr should do a dry run.
+    EXPECT_EQ(t.expected_type,
+              ASN1_mbstring_copy(nullptr, t.in.data(), t.in.size(), t.format,
+                                 t.mask));
+
+    // Test allocating a new object.
+    ASN1_STRING *str = nullptr;
+    EXPECT_EQ(
+        t.expected_type,
+        ASN1_mbstring_copy(&str, t.in.data(), t.in.size(), t.format, t.mask));
+    ASSERT_TRUE(str);
+    EXPECT_EQ(t.expected_type, ASN1_STRING_type(str));
+    EXPECT_EQ(Bytes(t.expected_data),
+              Bytes(ASN1_STRING_get0_data(str), ASN1_STRING_length(str)));
+
+    // Test writing into an existing object.
+    ASN1_STRING_free(str);
+    str = ASN1_STRING_new();
+    ASSERT_TRUE(str);
+    ASN1_STRING *old_str = str;
+    EXPECT_EQ(
+        t.expected_type,
+        ASN1_mbstring_copy(&str, t.in.data(), t.in.size(), t.format, t.mask));
+    ASSERT_EQ(old_str, str);
+    EXPECT_EQ(t.expected_type, ASN1_STRING_type(str));
+    EXPECT_EQ(Bytes(t.expected_data),
+              Bytes(ASN1_STRING_get0_data(str), ASN1_STRING_length(str)));
+    ASN1_STRING_free(str);
+    str = nullptr;
+
+    // minsize and maxsize should be enforced, even in a dry run.
+    EXPECT_EQ(t.expected_type,
+              ASN1_mbstring_ncopy(nullptr, t.in.data(), t.in.size(), t.format,
+                                  t.mask, /*minsize=*/t.num_codepoints,
+                                  /*maxsize=*/t.num_codepoints));
+
+    EXPECT_EQ(t.expected_type,
+              ASN1_mbstring_ncopy(&str, t.in.data(), t.in.size(), t.format,
+                                  t.mask, /*minsize=*/t.num_codepoints,
+                                  /*maxsize=*/t.num_codepoints));
+    ASSERT_TRUE(str);
+    EXPECT_EQ(t.expected_type, ASN1_STRING_type(str));
+    EXPECT_EQ(Bytes(t.expected_data),
+              Bytes(ASN1_STRING_get0_data(str), ASN1_STRING_length(str)));
+    ASN1_STRING_free(str);
+    str = nullptr;
+
+    EXPECT_EQ(-1, ASN1_mbstring_ncopy(
+                      nullptr, t.in.data(), t.in.size(), t.format, t.mask,
+                      /*minsize=*/t.num_codepoints + 1, /*maxsize=*/0));
+    ERR_clear_error();
+    EXPECT_EQ(-1, ASN1_mbstring_ncopy(
+                      &str, t.in.data(), t.in.size(), t.format, t.mask,
+                      /*minsize=*/t.num_codepoints + 1, /*maxsize=*/0));
+    EXPECT_FALSE(str);
+    ERR_clear_error();
+    if (t.num_codepoints > 1) {
+      EXPECT_EQ(-1, ASN1_mbstring_ncopy(
+                        nullptr, t.in.data(), t.in.size(), t.format, t.mask,
+                        /*minsize=*/0, /*maxsize=*/t.num_codepoints - 1));
+      ERR_clear_error();
+      EXPECT_EQ(-1, ASN1_mbstring_ncopy(
+                        &str, t.in.data(), t.in.size(), t.format, t.mask,
+                        /*minsize=*/0, /*maxsize=*/t.num_codepoints - 1));
+      EXPECT_FALSE(str);
+      ERR_clear_error();
+    }
+  }
+
+  const struct {
+    int format;
+    std::vector<uint8_t> in;
+    unsigned long mask;
+  } kInvalidTests[] = {
+      // Invalid encodings are rejected.
+      {MBSTRING_UTF8, {0xff}, B_ASN1_UTF8STRING},
+      {MBSTRING_BMP, {0xff}, B_ASN1_UTF8STRING},
+      {MBSTRING_UNIV, {0xff}, B_ASN1_UTF8STRING},
+
+      // Lone surrogates are not code points.
+      {MBSTRING_UTF8, {0xed, 0xa0, 0x80}, B_ASN1_UTF8STRING},
+      {MBSTRING_BMP, {0xd8, 0x00}, B_ASN1_UTF8STRING},
+      {MBSTRING_UNIV, {0x00, 0x00, 0xd8, 0x00}, B_ASN1_UTF8STRING},
+
+      // The input does not fit in the allowed output types.
+      {MBSTRING_UTF8, {'\n'}, B_ASN1_PRINTABLESTRING},
+      {MBSTRING_UTF8,
+       {0xc2, 0x80 /* U+0080 */},
+       B_ASN1_PRINTABLESTRING | B_ASN1_IA5STRING},
+      {MBSTRING_UTF8,
+       {0xc4, 0x80 /* U+0100 */},
+       B_ASN1_PRINTABLESTRING | B_ASN1_IA5STRING | B_ASN1_T61STRING},
+      {MBSTRING_UTF8,
+       {0xf0, 0x90, 0x80, 0x80 /* U+10000 */},
+       B_ASN1_PRINTABLESTRING | B_ASN1_IA5STRING | B_ASN1_T61STRING |
+           B_ASN1_BMPSTRING},
+
+      // Unrecognized bits are ignored.
+      {MBSTRING_UTF8, {'\n'}, B_ASN1_PRINTABLESTRING | B_ASN1_SEQUENCE},
+  };
+  for (const auto &t : kInvalidTests) {
+    SCOPED_TRACE(t.format);
+    SCOPED_TRACE(Bytes(t.in));
+    SCOPED_TRACE(t.mask);
+
+    EXPECT_EQ(-1, ASN1_mbstring_copy(nullptr, t.in.data(), t.in.size(),
+                                     t.format, t.mask));
+    ERR_clear_error();
+
+    ASN1_STRING *str = nullptr;
+    EXPECT_EQ(-1, ASN1_mbstring_copy(&str, t.in.data(), t.in.size(),
+                                     t.format, t.mask));
+    ERR_clear_error();
+    EXPECT_EQ(nullptr, str);
+  }
+}
+
+// Test that multi-string types correctly encode negative ENUMERATED.
+// Multi-string types cannot contain INTEGER, so we only test ENUMERATED.
+TEST(ASN1Test, NegativeEnumeratedMultistring) {
+  static const uint8_t kMinusOne[] = {0x0a, 0x01, 0xff};  // ENUMERATED { -1 }
+  // |ASN1_PRINTABLE| is a multi-string type that allows ENUMERATED.
+  const uint8_t *p = kMinusOne;
+  bssl::UniquePtr<ASN1_STRING> str(
+      d2i_ASN1_PRINTABLE(nullptr, &p, sizeof(kMinusOne)));
+  ASSERT_TRUE(str);
+  TestSerialize(str.get(), i2d_ASN1_PRINTABLE, kMinusOne);
+}
+
+TEST(ASN1Test, PrintableType) {
+  const struct {
+    std::vector<uint8_t> in;
+    int result;
+  } kTests[] = {
+      {{}, V_ASN1_PRINTABLESTRING},
+      {{'a', 'A', '0', '\'', '(', ')', '+', ',', '-', '.', '/', ':', '=', '?'},
+       V_ASN1_PRINTABLESTRING},
+      {{'*'}, V_ASN1_IA5STRING},
+      {{'\0'}, V_ASN1_IA5STRING},
+      {{'\0', 'a'}, V_ASN1_IA5STRING},
+      {{0, 1, 2, 3, 125, 126, 127}, V_ASN1_IA5STRING},
+      {{0, 1, 2, 3, 125, 126, 127, 128}, V_ASN1_T61STRING},
+      {{128, 0, 1, 2, 3, 125, 126, 127}, V_ASN1_T61STRING},
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(Bytes(t.in));
+    EXPECT_EQ(t.result, ASN1_PRINTABLE_type(t.in.data(), t.in.size()));
+  }
+}
+
 // The ASN.1 macros do not work on Windows shared library builds, where usage of
 // |OPENSSL_EXPORT| is a bit stricter.
 #if !defined(OPENSSL_WINDOWS) || !defined(BORINGSSL_SHARED_LIBRARY)
diff --git a/deps/boringssl/src/crypto/x509/charmap.h b/deps/boringssl/src/crypto/asn1/charmap.h
similarity index 100%
rename from deps/boringssl/src/crypto/x509/charmap.h
rename to deps/boringssl/src/crypto/asn1/charmap.h
diff --git a/deps/boringssl/src/crypto/asn1/asn1_locl.h b/deps/boringssl/src/crypto/asn1/internal.h
similarity index 80%
rename from deps/boringssl/src/crypto/asn1/asn1_locl.h
rename to deps/boringssl/src/crypto/asn1/internal.h
index bf90ea2..f40aa86 100644
--- a/deps/boringssl/src/crypto/asn1/asn1_locl.h
+++ b/deps/boringssl/src/crypto/asn1/internal.h
@@ -1,4 +1,3 @@
-/* asn1t.h */
 /*
  * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project
  * 2006.
@@ -87,6 +86,26 @@
 
 /* Internal ASN1 structures and functions: not for application use */
 
+/* These are used internally in the ASN1_OBJECT to keep track of
+ * whether the names and data need to be free()ed */
+#define ASN1_OBJECT_FLAG_DYNAMIC 0x01         /* internal use */
+#define ASN1_OBJECT_FLAG_DYNAMIC_STRINGS 0x04 /* internal use */
+#define ASN1_OBJECT_FLAG_DYNAMIC_DATA 0x08    /* internal use */
+
+/* An asn1_object_st (aka |ASN1_OBJECT|) represents an ASN.1 OBJECT IDENTIFIER.
+ * Note: Mutating an |ASN1_OBJECT| is only permitted when initializing it. The
+ * library maintains a table of static |ASN1_OBJECT|s, which may be referenced
+ * by non-const |ASN1_OBJECT| pointers. Code which receives an |ASN1_OBJECT|
+ * pointer externally must assume it is immutable, even if the pointer is not
+ * const. */
+struct asn1_object_st {
+  const char *sn, *ln;
+  int nid;
+  int length;
+  const unsigned char *data; /* data remains const after init */
+  int flags;                 /* Should we free this one */
+};
+
 int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d);
 int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d);
 
@@ -126,6 +145,15 @@
 int asn1_enc_save(ASN1_VALUE **pval, const unsigned char *in, int inlen,
                   const ASN1_ITEM *it);
 
+/* asn1_type_value_as_pointer returns |a|'s value in pointer form. This is
+ * usually the value object but, for BOOLEAN values, is 0 or 0xff cast to
+ * a pointer. */
+const void *asn1_type_value_as_pointer(const ASN1_TYPE *a);
+
+/* asn1_is_printable returns one if |value| is a valid Unicode codepoint for an
+ * ASN.1 PrintableString, and zero otherwise. */
+int asn1_is_printable(uint32_t value);
+
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/deps/boringssl/src/crypto/asn1/tasn_dec.c b/deps/boringssl/src/crypto/asn1/tasn_dec.c
index 99a9714..0d123cc 100644
--- a/deps/boringssl/src/crypto/asn1/tasn_dec.c
+++ b/deps/boringssl/src/crypto/asn1/tasn_dec.c
@@ -65,7 +65,7 @@
 #include <openssl/mem.h>
 
 #include "../internal.h"
-#include "asn1_locl.h"
+#include "internal.h"
 
 /*
  * Constructed types with a recursive definition (such as can be found in PKCS7)
diff --git a/deps/boringssl/src/crypto/asn1/tasn_enc.c b/deps/boringssl/src/crypto/asn1/tasn_enc.c
index 1323439..142de6d 100644
--- a/deps/boringssl/src/crypto/asn1/tasn_enc.c
+++ b/deps/boringssl/src/crypto/asn1/tasn_enc.c
@@ -63,7 +63,7 @@
 #include <openssl/mem.h>
 
 #include "../internal.h"
-#include "asn1_locl.h"
+#include "internal.h"
 
 
 static int asn1_i2d_ex_primitive(ASN1_VALUE **pval, unsigned char **out,
@@ -295,11 +295,12 @@
 
         if (flags & ASN1_TFLG_SET_OF) {
             isset = 1;
-            /* 2 means we reorder */
-            if (flags & ASN1_TFLG_SEQUENCE_OF)
-                isset = 2;
-        } else
+            /* Historically, types with both bits set were mutated when
+             * serialized to apply the sort. We no longer support this. */
+            assert((flags & ASN1_TFLG_SEQUENCE_OF) == 0);
+        } else {
             isset = 0;
+        }
 
         /*
          * Work out inner tag value: if EXPLICIT or no tagging use underlying
@@ -378,7 +379,6 @@
 typedef struct {
     unsigned char *data;
     int length;
-    ASN1_VALUE *field;
 } DER_ENC;
 
 static int der_cmp(const void *a, const void *b)
@@ -433,7 +433,6 @@
         skitem = sk_ASN1_VALUE_value(sk, i);
         tder->data = p;
         tder->length = ASN1_item_ex_i2d(&skitem, &p, item, -1, iclass);
-        tder->field = skitem;
     }
 
     /* Now sort them */
@@ -445,11 +444,6 @@
         p += tder->length;
     }
     *out = p;
-    /* If do_sort is 2 then reorder the STACK */
-    if (do_sort == 2) {
-        for (i = 0, tder = derlst; i < sk_ASN1_VALUE_num(sk); i++, tder++)
-            (void)sk_ASN1_VALUE_set(sk, i, tder->field);
-    }
     OPENSSL_free(derlst);
     OPENSSL_free(tmpdat);
     return 1;
@@ -531,6 +525,20 @@
         /* If MSTRING type set the underlying type */
         strtmp = (ASN1_STRING *)*pval;
         utype = strtmp->type;
+        /* Negative INTEGER and ENUMERATED values use |ASN1_STRING| type values
+         * that do not match their corresponding utype values. INTEGERs cannot
+         * participate in MSTRING types, but ENUMERATEDs can.
+         *
+         * TODO(davidben): Is this a bug? Although arguably one of the MSTRING
+         * types should contain more values, rather than less. See
+         * https://crbug.com/boringssl/412. But it is not possible to fit all
+         * possible ANY values into an |ASN1_STRING|, so matching the spec here
+         * is somewhat hopeless. */
+        if (utype == V_ASN1_NEG_INTEGER) {
+            utype = V_ASN1_INTEGER;
+        } else if (utype == V_ASN1_NEG_ENUMERATED) {
+            utype = V_ASN1_ENUMERATED;
+        }
         *putype = utype;
     } else if (it->utype == V_ASN1_ANY) {
         /* If ANY set type and pointer to value */
@@ -569,7 +577,7 @@
             if (!*tbool && !it->size)
                 return -1;
         }
-        c = (unsigned char)*tbool;
+        c = *tbool ? 0xff : 0x00;
         cont = &c;
         len = 1;
         break;
diff --git a/deps/boringssl/src/crypto/asn1/tasn_fre.c b/deps/boringssl/src/crypto/asn1/tasn_fre.c
index a1e7315..2f5032d 100644
--- a/deps/boringssl/src/crypto/asn1/tasn_fre.c
+++ b/deps/boringssl/src/crypto/asn1/tasn_fre.c
@@ -61,7 +61,7 @@
 #include <openssl/asn1t.h>
 #include <openssl/mem.h>
 
-#include "asn1_locl.h"
+#include "internal.h"
 
 /* Free up an ASN1 structure */
 
@@ -192,7 +192,7 @@
         ASN1_TYPE *typ = (ASN1_TYPE *)*pval;
         utype = typ->type;
         pval = &typ->value.asn1_value;
-        if (!*pval)
+        if (utype != V_ASN1_BOOLEAN && !*pval)
             return;
     } else if (it->itype == ASN1_ITYPE_MSTRING) {
         utype = -1;
diff --git a/deps/boringssl/src/crypto/asn1/tasn_new.c b/deps/boringssl/src/crypto/asn1/tasn_new.c
index dc864da..346887b 100644
--- a/deps/boringssl/src/crypto/asn1/tasn_new.c
+++ b/deps/boringssl/src/crypto/asn1/tasn_new.c
@@ -63,7 +63,7 @@
 #include <openssl/mem.h>
 #include <openssl/obj.h>
 
-#include "asn1_locl.h"
+#include "internal.h"
 #include "../internal.h"
 
 
@@ -271,7 +271,6 @@
 static int ASN1_primitive_new(ASN1_VALUE **pval, const ASN1_ITEM *it)
 {
     ASN1_TYPE *typ;
-    ASN1_STRING *str;
     int utype;
 
     if (!it)
@@ -308,10 +307,7 @@
         break;
 
     default:
-        str = ASN1_STRING_type_new(utype);
-        if (it->itype == ASN1_ITYPE_MSTRING && str)
-            str->flags |= ASN1_STRING_FLAG_MSTRING;
-        *pval = (ASN1_VALUE *)str;
+        *pval = (ASN1_VALUE *)ASN1_STRING_type_new(utype);
         break;
     }
     if (*pval)
diff --git a/deps/boringssl/src/crypto/asn1/tasn_utl.c b/deps/boringssl/src/crypto/asn1/tasn_utl.c
index f0288b4..24ad8c3 100644
--- a/deps/boringssl/src/crypto/asn1/tasn_utl.c
+++ b/deps/boringssl/src/crypto/asn1/tasn_utl.c
@@ -66,7 +66,7 @@
 #include <openssl/thread.h>
 
 #include "../internal.h"
-#include "asn1_locl.h"
+#include "internal.h"
 
 
 /* Utility functions for manipulating fields and offsets */
diff --git a/deps/boringssl/src/crypto/asn1/time_support.c b/deps/boringssl/src/crypto/asn1/time_support.c
index 3efd43e..e748ad7 100644
--- a/deps/boringssl/src/crypto/asn1/time_support.c
+++ b/deps/boringssl/src/crypto/asn1/time_support.c
@@ -59,7 +59,7 @@
 #define _POSIX_C_SOURCE 201410L  /* for gmtime_r */
 #endif
 
-#include "asn1_locl.h"
+#include "internal.h"
 
 #include <time.h>
 
diff --git a/deps/boringssl/src/crypto/bio/bio_mem.c b/deps/boringssl/src/crypto/bio/bio_mem.c
index 08dd6e9..f40a9a7 100644
--- a/deps/boringssl/src/crypto/bio/bio_mem.c
+++ b/deps/boringssl/src/crypto/bio/bio_mem.c
@@ -116,17 +116,11 @@
 }
 
 static int mem_free(BIO *bio) {
-  BUF_MEM *b;
-
-  if (bio == NULL) {
-    return 0;
-  }
-
   if (!bio->shutdown || !bio->init || bio->ptr == NULL) {
     return 1;
   }
 
-  b = (BUF_MEM *)bio->ptr;
+  BUF_MEM *b = (BUF_MEM *)bio->ptr;
   if (bio->flags & BIO_FLAGS_MEM_RDONLY) {
     b->data = NULL;
   }
diff --git a/deps/boringssl/src/crypto/bio/connect.c b/deps/boringssl/src/crypto/bio/connect.c
index b8afa61..3b65acf 100644
--- a/deps/boringssl/src/crypto/bio/connect.c
+++ b/deps/boringssl/src/crypto/bio/connect.c
@@ -320,7 +320,7 @@
   bio->init = 0;
   bio->num = -1;
   bio->flags = 0;
-  bio->ptr = (char *)BIO_CONNECT_new();
+  bio->ptr = BIO_CONNECT_new();
   return bio->ptr != NULL;
 }
 
@@ -340,10 +340,6 @@
 }
 
 static int conn_free(BIO *bio) {
-  if (bio == NULL) {
-    return 0;
-  }
-
   if (bio->shutdown) {
     conn_close_socket(bio);
   }
diff --git a/deps/boringssl/src/crypto/bio/fd.c b/deps/boringssl/src/crypto/bio/fd.c
index d4e6918..349ee9d 100644
--- a/deps/boringssl/src/crypto/bio/fd.c
+++ b/deps/boringssl/src/crypto/bio/fd.c
@@ -146,10 +146,6 @@
 }
 
 static int fd_free(BIO *bio) {
-  if (bio == NULL) {
-    return 0;
-  }
-
   if (bio->shutdown) {
     if (bio->init) {
       BORINGSSL_CLOSE(bio->num);
diff --git a/deps/boringssl/src/crypto/bio/file.c b/deps/boringssl/src/crypto/bio/file.c
index 15feb9d..835d661 100644
--- a/deps/boringssl/src/crypto/bio/file.c
+++ b/deps/boringssl/src/crypto/bio/file.c
@@ -126,13 +126,7 @@
   return ret;
 }
 
-static int file_new(BIO *bio) { return 1; }
-
 static int file_free(BIO *bio) {
-  if (bio == NULL) {
-    return 0;
-  }
-
   if (!bio->shutdown) {
     return 1;
   }
@@ -279,7 +273,7 @@
     BIO_TYPE_FILE,   "FILE pointer",
     file_write,      file_read,
     NULL /* puts */, file_gets,
-    file_ctrl,       file_new,
+    file_ctrl,       NULL /* create */,
     file_free,       NULL /* callback_ctrl */,
 };
 
diff --git a/deps/boringssl/src/crypto/bio/pair.c b/deps/boringssl/src/crypto/bio/pair.c
index 03f60b7..a1a9c9c 100644
--- a/deps/boringssl/src/crypto/bio/pair.c
+++ b/deps/boringssl/src/crypto/bio/pair.c
@@ -127,12 +127,7 @@
 }
 
 static int bio_free(BIO *bio) {
-  struct bio_bio_st *b;
-
-  if (bio == NULL) {
-    return 0;
-  }
-  b = bio->ptr;
+  struct bio_bio_st *b = bio->ptr;
 
   assert(b != NULL);
 
diff --git a/deps/boringssl/src/crypto/bio/socket.c b/deps/boringssl/src/crypto/bio/socket.c
index 081ce01..679959e 100644
--- a/deps/boringssl/src/crypto/bio/socket.c
+++ b/deps/boringssl/src/crypto/bio/socket.c
@@ -81,19 +81,7 @@
 }
 #endif
 
-static int sock_new(BIO *bio) {
-  bio->init = 0;
-  bio->num = 0;
-  bio->ptr = NULL;
-  bio->flags = 0;
-  return 1;
-}
-
 static int sock_free(BIO *bio) {
-  if (bio == NULL) {
-    return 0;
-  }
-
   if (bio->shutdown) {
     if (bio->init) {
       closesocket(bio->num);
@@ -105,17 +93,15 @@
 }
 
 static int sock_read(BIO *b, char *out, int outl) {
-  int ret = 0;
-
   if (out == NULL) {
     return 0;
   }
 
   bio_clear_socket_error();
 #if defined(OPENSSL_WINDOWS)
-  ret = recv(b->num, out, outl, 0);
+  int ret = recv(b->num, out, outl, 0);
 #else
-  ret = read(b->num, out, outl);
+  int ret = read(b->num, out, outl);
 #endif
   BIO_clear_retry_flags(b);
   if (ret <= 0) {
@@ -186,7 +172,7 @@
     BIO_TYPE_SOCKET, "socket",
     sock_write,      sock_read,
     NULL /* puts */, NULL /* gets, */,
-    sock_ctrl,       sock_new,
+    sock_ctrl,       NULL /* create */,
     sock_free,       NULL /* callback_ctrl */,
 };
 
diff --git a/deps/boringssl/src/crypto/bytestring/bytestring_test.cc b/deps/boringssl/src/crypto/bytestring/bytestring_test.cc
index eafb0de..0877a2e 100644
--- a/deps/boringssl/src/crypto/bytestring/bytestring_test.cc
+++ b/deps/boringssl/src/crypto/bytestring/bytestring_test.cc
@@ -115,6 +115,28 @@
   EXPECT_FALSE(CBS_get_u24_length_prefixed(&data, &prefixed));
 }
 
+TEST(CBSTest, GetUntilFirst) {
+  static const uint8_t kData[] = {0, 1, 2, 3, 0, 1, 2, 3};
+  CBS data;
+  CBS_init(&data, kData, sizeof(kData));
+
+  CBS prefix;
+  EXPECT_FALSE(CBS_get_until_first(&data, &prefix, 4));
+  EXPECT_EQ(CBS_data(&data), kData);
+  EXPECT_EQ(CBS_len(&data), sizeof(kData));
+
+  ASSERT_TRUE(CBS_get_until_first(&data, &prefix, 0));
+  EXPECT_EQ(CBS_len(&prefix), 0u);
+  EXPECT_EQ(CBS_data(&data), kData);
+  EXPECT_EQ(CBS_len(&data), sizeof(kData));
+
+  ASSERT_TRUE(CBS_get_until_first(&data, &prefix, 2));
+  EXPECT_EQ(CBS_data(&prefix), kData);
+  EXPECT_EQ(CBS_len(&prefix), 2u);
+  EXPECT_EQ(CBS_data(&data), kData + 2);
+  EXPECT_EQ(CBS_len(&data), sizeof(kData) - 2);
+}
+
 TEST(CBSTest, GetASN1) {
   static const uint8_t kData1[] = {0x30, 2, 1, 2};
   static const uint8_t kData2[] = {0x30, 3, 1, 2};
@@ -322,11 +344,11 @@
 }
 
 TEST(CBBTest, Basic) {
-  static const uint8_t kExpected[] = {1,   2,    3,    4,    5,    6,   7,
-                                      8,   9,    0xa,  0xb,  0xc,  0xd, 0xe,
-                                      0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 3, 2,
-                                      10,  9,    8,    7,    0x12, 0x11, 0x10,
-                                      0xf, 0xe,  0xd,  0xc,  0xb};
+  static const uint8_t kExpected[] = {
+      0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+      0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+      0x03, 0x02, 0x0a, 0x09, 0x08, 0x07, 0x12, 0x11, 0x10, 0x0f,
+      0x0e, 0x0d, 0x0c, 0x0b, 0x00, 0x00, 0x00, 0x00};
   uint8_t *buf;
   size_t buf_len;
 
@@ -335,6 +357,7 @@
   cbb.Reset();
 
   ASSERT_TRUE(CBB_init(cbb.get(), 0));
+  ASSERT_TRUE(CBB_add_zeros(cbb.get(), 0));
   ASSERT_TRUE(CBB_add_u8(cbb.get(), 1));
   ASSERT_TRUE(CBB_add_u16(cbb.get(), 0x203));
   ASSERT_TRUE(CBB_add_u24(cbb.get(), 0x40506));
@@ -344,6 +367,7 @@
   ASSERT_TRUE(CBB_add_u16le(cbb.get(), 0x203));
   ASSERT_TRUE(CBB_add_u32le(cbb.get(), 0x708090a));
   ASSERT_TRUE(CBB_add_u64le(cbb.get(), 0xb0c0d0e0f101112));
+  ASSERT_TRUE(CBB_add_zeros(cbb.get(), 4));
   ASSERT_TRUE(CBB_finish(cbb.get(), &buf, &buf_len));
 
   bssl::UniquePtr<uint8_t> scoper(buf);
diff --git a/deps/boringssl/src/crypto/bytestring/cbb.c b/deps/boringssl/src/crypto/bytestring/cbb.c
index efb89c7..12587cd 100644
--- a/deps/boringssl/src/crypto/bytestring/cbb.c
+++ b/deps/boringssl/src/crypto/bytestring/cbb.c
@@ -404,6 +404,15 @@
   return 1;
 }
 
+int CBB_add_zeros(CBB *cbb, size_t len) {
+  uint8_t *out;
+  if (!CBB_add_space(cbb, &out, len)) {
+    return 0;
+  }
+  OPENSSL_memset(out, 0, len);
+  return 1;
+}
+
 int CBB_add_space(CBB *cbb, uint8_t **out_data, size_t len) {
   if (!CBB_flush(cbb) ||
       !cbb_buffer_add(cbb->base, out_data, len)) {
diff --git a/deps/boringssl/src/crypto/bytestring/cbs.c b/deps/boringssl/src/crypto/bytestring/cbs.c
index 5590ec8..803c97a 100644
--- a/deps/boringssl/src/crypto/bytestring/cbs.c
+++ b/deps/boringssl/src/crypto/bytestring/cbs.c
@@ -216,6 +216,14 @@
   return cbs_get_length_prefixed(cbs, out, 3);
 }
 
+int CBS_get_until_first(CBS *cbs, CBS *out, uint8_t c) {
+  const uint8_t *split = OPENSSL_memchr(CBS_data(cbs), c, CBS_len(cbs));
+  if (split == NULL) {
+    return 0;
+  }
+  return CBS_get_bytes(cbs, out, split - CBS_data(cbs));
+}
+
 // parse_base128_integer reads a big-endian base-128 integer from |cbs| and sets
 // |*out| to the result. This is the encoding used in DER for both high tag
 // number form and OID components.
diff --git a/deps/boringssl/src/crypto/cipher_extra/aead_test.cc b/deps/boringssl/src/crypto/cipher_extra/aead_test.cc
index bf02e78..9e5dcee 100644
--- a/deps/boringssl/src/crypto/cipher_extra/aead_test.cc
+++ b/deps/boringssl/src/crypto/cipher_extra/aead_test.cc
@@ -125,10 +125,6 @@
      "aes_128_cbc_sha1_tls_implicit_iv_tests.txt",
      kLimitedImplementation | RequiresADLength(11)},
 
-    {"AES_128_CBC_SHA256_TLS", EVP_aead_aes_128_cbc_sha256_tls,
-     "aes_128_cbc_sha256_tls_tests.txt",
-     kLimitedImplementation | RequiresADLength(11)},
-
     {"AES_256_CBC_SHA1_TLS", EVP_aead_aes_256_cbc_sha1_tls,
      "aes_256_cbc_sha1_tls_tests.txt",
      kLimitedImplementation | RequiresADLength(11)},
@@ -138,14 +134,6 @@
      "aes_256_cbc_sha1_tls_implicit_iv_tests.txt",
      kLimitedImplementation | RequiresADLength(11)},
 
-    {"AES_256_CBC_SHA256_TLS", EVP_aead_aes_256_cbc_sha256_tls,
-     "aes_256_cbc_sha256_tls_tests.txt",
-     kLimitedImplementation | RequiresADLength(11)},
-
-    {"AES_256_CBC_SHA384_TLS", EVP_aead_aes_256_cbc_sha384_tls,
-     "aes_256_cbc_sha384_tls_tests.txt",
-     kLimitedImplementation | RequiresADLength(11)},
-
     {"DES_EDE3_CBC_SHA1_TLS", EVP_aead_des_ede3_cbc_sha1_tls,
      "des_ede3_cbc_sha1_tls_tests.txt",
      kLimitedImplementation | RequiresADLength(11)},
@@ -808,6 +796,7 @@
                             : sizeof(ad_buf) - 1;
 
   uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH];
+  OPENSSL_memset(nonce, 'N', sizeof(nonce));
   const size_t nonce_len = EVP_AEAD_nonce_length(aead());
   ASSERT_LE(nonce_len, sizeof(nonce));
 
diff --git a/deps/boringssl/src/crypto/cipher_extra/cipher_extra.c b/deps/boringssl/src/crypto/cipher_extra/cipher_extra.c
index b132265..786a5d5 100644
--- a/deps/boringssl/src/crypto/cipher_extra/cipher_extra.c
+++ b/deps/boringssl/src/crypto/cipher_extra/cipher_extra.c
@@ -89,6 +89,10 @@
 }
 
 const EVP_CIPHER *EVP_get_cipherbyname(const char *name) {
+  if (name == NULL) {
+    return NULL;
+  }
+
   if (OPENSSL_strcasecmp(name, "rc4") == 0) {
     return EVP_rc4();
   } else if (OPENSSL_strcasecmp(name, "des-cbc") == 0) {
diff --git a/deps/boringssl/src/crypto/cipher_extra/cipher_test.cc b/deps/boringssl/src/crypto/cipher_extra/cipher_test.cc
index af7e0e7..57fdc8a 100644
--- a/deps/boringssl/src/crypto/cipher_extra/cipher_test.cc
+++ b/deps/boringssl/src/crypto/cipher_extra/cipher_test.cc
@@ -65,11 +65,14 @@
 #include <openssl/cipher.h>
 #include <openssl/err.h>
 #include <openssl/nid.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
 #include <openssl/span.h>
 
 #include "../test/file_test.h"
 #include "../test/test_util.h"
 #include "../test/wycheproof_util.h"
+#include "./internal.h"
 
 
 static const EVP_CIPHER *GetCipher(const std::string &name) {
@@ -474,3 +477,50 @@
         }
       });
 }
+
+TEST(CipherTest, SHA1WithSecretSuffix) {
+  uint8_t buf[SHA_CBLOCK * 4];
+  RAND_bytes(buf, sizeof(buf));
+  // Hashing should run in time independent of the bytes.
+  CONSTTIME_SECRET(buf, sizeof(buf));
+
+  // Exhaustively testing interesting cases in this function is cubic in the
+  // block size, so we test in 3-byte increments.
+  constexpr size_t kSkip = 3;
+  // This value should be less than 8 to test the edge case when the 8-byte
+  // length wraps to the next block.
+  static_assert(kSkip < 8, "kSkip is too large");
+
+  // |EVP_sha1_final_with_secret_suffix| is sensitive to the public length of
+  // the partial block previously hashed. In TLS, this is the HMAC prefix, the
+  // header, and the public minimum padding length.
+  for (size_t prefix = 0; prefix < SHA_CBLOCK; prefix += kSkip) {
+    SCOPED_TRACE(prefix);
+    // The first block is treated differently, so we run with up to three
+    // blocks of length variability.
+    for (size_t max_len = 0; max_len < 3 * SHA_CBLOCK; max_len += kSkip) {
+      SCOPED_TRACE(max_len);
+      for (size_t len = 0; len <= max_len; len += kSkip) {
+        SCOPED_TRACE(len);
+
+        uint8_t expected[SHA_DIGEST_LENGTH];
+        SHA1(buf, prefix + len, expected);
+        CONSTTIME_DECLASSIFY(expected, sizeof(expected));
+
+        // Make a copy of the secret length to avoid interfering with the loop.
+        size_t secret_len = len;
+        CONSTTIME_SECRET(&secret_len, sizeof(secret_len));
+
+        SHA_CTX ctx;
+        SHA1_Init(&ctx);
+        SHA1_Update(&ctx, buf, prefix);
+        uint8_t computed[SHA_DIGEST_LENGTH];
+        ASSERT_TRUE(EVP_sha1_final_with_secret_suffix(
+            &ctx, computed, buf + prefix, secret_len, max_len));
+
+        CONSTTIME_DECLASSIFY(computed, sizeof(computed));
+        EXPECT_EQ(Bytes(expected), Bytes(computed));
+      }
+    }
+  }
+}
diff --git a/deps/boringssl/src/crypto/cipher_extra/e_tls.c b/deps/boringssl/src/crypto/cipher_extra/e_tls.c
index c812b6b..6d84f7f 100644
--- a/deps/boringssl/src/crypto/cipher_extra/e_tls.c
+++ b/deps/boringssl/src/crypto/cipher_extra/e_tls.c
@@ -343,7 +343,7 @@
   if (EVP_CIPHER_CTX_mode(&tls_ctx->cipher_ctx) == EVP_CIPH_CBC_MODE &&
       EVP_tls_cbc_record_digest_supported(tls_ctx->hmac_ctx.md)) {
     if (!EVP_tls_cbc_digest_record(tls_ctx->hmac_ctx.md, mac, &mac_len,
-                                   ad_fixed, out, data_plus_mac_len, total,
+                                   ad_fixed, out, data_len, total,
                                    tls_ctx->mac_key, tls_ctx->mac_key_len)) {
       OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_BAD_DECRYPT);
       return 0;
@@ -406,14 +406,6 @@
                        EVP_sha1(), 1);
 }
 
-static int aead_aes_128_cbc_sha256_tls_init(EVP_AEAD_CTX *ctx,
-                                            const uint8_t *key, size_t key_len,
-                                            size_t tag_len,
-                                            enum evp_aead_direction_t dir) {
-  return aead_tls_init(ctx, key, key_len, tag_len, dir, EVP_aes_128_cbc(),
-                       EVP_sha256(), 0);
-}
-
 static int aead_aes_256_cbc_sha1_tls_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
                                           size_t key_len, size_t tag_len,
                                           enum evp_aead_direction_t dir) {
@@ -428,22 +420,6 @@
                        EVP_sha1(), 1);
 }
 
-static int aead_aes_256_cbc_sha256_tls_init(EVP_AEAD_CTX *ctx,
-                                            const uint8_t *key, size_t key_len,
-                                            size_t tag_len,
-                                            enum evp_aead_direction_t dir) {
-  return aead_tls_init(ctx, key, key_len, tag_len, dir, EVP_aes_256_cbc(),
-                       EVP_sha256(), 0);
-}
-
-static int aead_aes_256_cbc_sha384_tls_init(EVP_AEAD_CTX *ctx,
-                                            const uint8_t *key, size_t key_len,
-                                            size_t tag_len,
-                                            enum evp_aead_direction_t dir) {
-  return aead_tls_init(ctx, key, key_len, tag_len, dir, EVP_aes_256_cbc(),
-                       EVP_sha384(), 0);
-}
-
 static int aead_des_ede3_cbc_sha1_tls_init(EVP_AEAD_CTX *ctx,
                                            const uint8_t *key, size_t key_len,
                                            size_t tag_len,
@@ -513,23 +489,6 @@
     aead_tls_tag_len,
 };
 
-static const EVP_AEAD aead_aes_128_cbc_sha256_tls = {
-    SHA256_DIGEST_LENGTH + 16,  // key len (SHA256 + AES128)
-    16,                         // nonce len (IV)
-    16 + SHA256_DIGEST_LENGTH,  // overhead (padding + SHA256)
-    SHA256_DIGEST_LENGTH,       // max tag length
-    0,                          // seal_scatter_supports_extra_in
-
-    NULL,  // init
-    aead_aes_128_cbc_sha256_tls_init,
-    aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
-    NULL,  // open_gather
-    NULL,  // get_iv
-    aead_tls_tag_len,
-};
-
 static const EVP_AEAD aead_aes_256_cbc_sha1_tls = {
     SHA_DIGEST_LENGTH + 32,  // key len (SHA1 + AES256)
     16,                      // nonce len (IV)
@@ -564,40 +523,6 @@
     aead_tls_tag_len,
 };
 
-static const EVP_AEAD aead_aes_256_cbc_sha256_tls = {
-    SHA256_DIGEST_LENGTH + 32,  // key len (SHA256 + AES256)
-    16,                         // nonce len (IV)
-    16 + SHA256_DIGEST_LENGTH,  // overhead (padding + SHA256)
-    SHA256_DIGEST_LENGTH,       // max tag length
-    0,                          // seal_scatter_supports_extra_in
-
-    NULL,  // init
-    aead_aes_256_cbc_sha256_tls_init,
-    aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
-    NULL,  // open_gather
-    NULL,  // get_iv
-    aead_tls_tag_len,
-};
-
-static const EVP_AEAD aead_aes_256_cbc_sha384_tls = {
-    SHA384_DIGEST_LENGTH + 32,  // key len (SHA384 + AES256)
-    16,                         // nonce len (IV)
-    16 + SHA384_DIGEST_LENGTH,  // overhead (padding + SHA384)
-    SHA384_DIGEST_LENGTH,       // max tag length
-    0,                          // seal_scatter_supports_extra_in
-
-    NULL,  // init
-    aead_aes_256_cbc_sha384_tls_init,
-    aead_tls_cleanup,
-    aead_tls_open,
-    aead_tls_seal_scatter,
-    NULL,  // open_gather
-    NULL,  // get_iv
-    aead_tls_tag_len,
-};
-
 static const EVP_AEAD aead_des_ede3_cbc_sha1_tls = {
     SHA_DIGEST_LENGTH + 24,  // key len (SHA1 + 3DES)
     8,                       // nonce len (IV)
@@ -657,10 +582,6 @@
   return &aead_aes_128_cbc_sha1_tls_implicit_iv;
 }
 
-const EVP_AEAD *EVP_aead_aes_128_cbc_sha256_tls(void) {
-  return &aead_aes_128_cbc_sha256_tls;
-}
-
 const EVP_AEAD *EVP_aead_aes_256_cbc_sha1_tls(void) {
   return &aead_aes_256_cbc_sha1_tls;
 }
@@ -669,14 +590,6 @@
   return &aead_aes_256_cbc_sha1_tls_implicit_iv;
 }
 
-const EVP_AEAD *EVP_aead_aes_256_cbc_sha256_tls(void) {
-  return &aead_aes_256_cbc_sha256_tls;
-}
-
-const EVP_AEAD *EVP_aead_aes_256_cbc_sha384_tls(void) {
-  return &aead_aes_256_cbc_sha384_tls;
-}
-
 const EVP_AEAD *EVP_aead_des_ede3_cbc_sha1_tls(void) {
   return &aead_des_ede3_cbc_sha1_tls;
 }
diff --git a/deps/boringssl/src/crypto/cipher_extra/internal.h b/deps/boringssl/src/crypto/cipher_extra/internal.h
index c2af48e..a2ec30b 100644
--- a/deps/boringssl/src/crypto/cipher_extra/internal.h
+++ b/deps/boringssl/src/crypto/cipher_extra/internal.h
@@ -99,6 +99,17 @@
 // which EVP_tls_cbc_digest_record supports.
 int EVP_tls_cbc_record_digest_supported(const EVP_MD *md);
 
+// EVP_sha1_final_with_secret_suffix computes the result of hashing |len| bytes
+// from |in| to |ctx| and writes the resulting hash to |out|. |len| is treated
+// as secret and must be at most |max_len|, which is treated as public. |in|
+// must point to a buffer of at least |max_len| bytes. It returns one on success
+// and zero if inputs are too long.
+//
+// This function is exported for unit tests.
+OPENSSL_EXPORT int EVP_sha1_final_with_secret_suffix(
+    SHA_CTX *ctx, uint8_t out[SHA_DIGEST_LENGTH], const uint8_t *in, size_t len,
+    size_t max_len);
+
 // EVP_tls_cbc_digest_record computes the MAC of a decrypted, padded TLS
 // record.
 //
@@ -108,8 +119,8 @@
 //   md_out_size: the number of output bytes is written here.
 //   header: the 13-byte, TLS record header.
 //   data: the record data itself
-//   data_plus_mac_size: the secret, reported length of the data and MAC
-//     once the padding has been removed.
+//   data_size: the secret, reported length of the data once the padding and MAC
+//     have been removed.
 //   data_plus_mac_plus_padding_size: the public length of the whole
 //     record, including padding.
 //
@@ -119,7 +130,7 @@
 // padding too. )
 int EVP_tls_cbc_digest_record(const EVP_MD *md, uint8_t *md_out,
                               size_t *md_out_size, const uint8_t header[13],
-                              const uint8_t *data, size_t data_plus_mac_size,
+                              const uint8_t *data, size_t data_size,
                               size_t data_plus_mac_plus_padding_size,
                               const uint8_t *mac_secret,
                               unsigned mac_secret_length);
diff --git a/deps/boringssl/src/crypto/cipher_extra/tls_cbc.c b/deps/boringssl/src/crypto/cipher_extra/tls_cbc.c
index 5e97a1c..e1e95d4 100644
--- a/deps/boringssl/src/crypto/cipher_extra/tls_cbc.c
+++ b/deps/boringssl/src/crypto/cipher_extra/tls_cbc.c
@@ -62,15 +62,6 @@
 #include "../fipsmodule/cipher/internal.h"
 
 
-// MAX_HASH_BIT_COUNT_BYTES is the maximum number of bytes in the hash's length
-// field. (SHA-384/512 have 128-bit length.)
-#define MAX_HASH_BIT_COUNT_BYTES 16
-
-// MAX_HASH_BLOCK_SIZE is the maximum hash block size that we'll support.
-// Currently SHA-384/512 has a 128-byte block size and that's the largest
-// supported by TLS.)
-#define MAX_HASH_BLOCK_SIZE 128
-
 int EVP_tls_cbc_remove_padding(crypto_word_t *out_padding_ok, size_t *out_len,
                                const uint8_t *in, size_t in_len,
                                size_t block_size, size_t mac_size) {
@@ -183,134 +174,110 @@
   OPENSSL_memcpy(out, rotated_mac, md_size);
 }
 
-// u32toBE serialises an unsigned, 32-bit number (n) as four bytes at (p) in
-// big-endian order. The value of p is advanced by four.
-#define u32toBE(n, p)                \
-  do {                               \
-    *((p)++) = (uint8_t)((n) >> 24); \
-    *((p)++) = (uint8_t)((n) >> 16); \
-    *((p)++) = (uint8_t)((n) >> 8);  \
-    *((p)++) = (uint8_t)((n));       \
-  } while (0)
-
-// u64toBE serialises an unsigned, 64-bit number (n) as eight bytes at (p) in
-// big-endian order. The value of p is advanced by eight.
-#define u64toBE(n, p)                \
-  do {                               \
-    *((p)++) = (uint8_t)((n) >> 56); \
-    *((p)++) = (uint8_t)((n) >> 48); \
-    *((p)++) = (uint8_t)((n) >> 40); \
-    *((p)++) = (uint8_t)((n) >> 32); \
-    *((p)++) = (uint8_t)((n) >> 24); \
-    *((p)++) = (uint8_t)((n) >> 16); \
-    *((p)++) = (uint8_t)((n) >> 8);  \
-    *((p)++) = (uint8_t)((n));       \
-  } while (0)
-
-typedef union {
-  SHA_CTX sha1;
-  SHA256_CTX sha256;
-  SHA512_CTX sha512;
-} HASH_CTX;
-
-static void tls1_sha1_transform(HASH_CTX *ctx, const uint8_t *block) {
-  SHA1_Transform(&ctx->sha1, block);
-}
-
-static void tls1_sha256_transform(HASH_CTX *ctx, const uint8_t *block) {
-  SHA256_Transform(&ctx->sha256, block);
-}
-
-static void tls1_sha512_transform(HASH_CTX *ctx, const uint8_t *block) {
-  SHA512_Transform(&ctx->sha512, block);
-}
-
-// These functions serialize the state of a hash and thus perform the standard
-// "final" operation without adding the padding and length that such a function
-// typically does.
-static void tls1_sha1_final_raw(HASH_CTX *ctx, uint8_t *md_out) {
-  SHA_CTX *sha1 = &ctx->sha1;
-  u32toBE(sha1->h[0], md_out);
-  u32toBE(sha1->h[1], md_out);
-  u32toBE(sha1->h[2], md_out);
-  u32toBE(sha1->h[3], md_out);
-  u32toBE(sha1->h[4], md_out);
-}
-
-static void tls1_sha256_final_raw(HASH_CTX *ctx, uint8_t *md_out) {
-  SHA256_CTX *sha256 = &ctx->sha256;
-  for (unsigned i = 0; i < 8; i++) {
-    u32toBE(sha256->h[i], md_out);
+int EVP_sha1_final_with_secret_suffix(SHA_CTX *ctx,
+                                      uint8_t out[SHA_DIGEST_LENGTH],
+                                      const uint8_t *in, size_t len,
+                                      size_t max_len) {
+  // Bound the input length so |total_bits| below fits in four bytes. This is
+  // redundant with TLS record size limits. This also ensures |input_idx| below
+  // does not overflow.
+  size_t max_len_bits = max_len << 3;
+  if (ctx->Nh != 0 ||
+      (max_len_bits >> 3) != max_len ||  // Overflow
+      ctx->Nl + max_len_bits < max_len_bits ||
+      ctx->Nl + max_len_bits > UINT32_MAX) {
+    return 0;
   }
-}
 
-static void tls1_sha512_final_raw(HASH_CTX *ctx, uint8_t *md_out) {
-  SHA512_CTX *sha512 = &ctx->sha512;
-  for (unsigned i = 0; i < 8; i++) {
-    u64toBE(sha512->h[i], md_out);
+  // We need to hash the following into |ctx|:
+  //
+  // - ctx->data[:ctx->num]
+  // - in[:len]
+  // - A 0x80 byte
+  // - However many zero bytes are needed to pad up to a block.
+  // - Eight bytes of length.
+  size_t num_blocks = (ctx->num + len + 1 + 8 + SHA_CBLOCK - 1) >> 6;
+  size_t last_block = num_blocks - 1;
+  size_t max_blocks = (ctx->num + max_len + 1 + 8 + SHA_CBLOCK - 1) >> 6;
+
+  // The bounds above imply |total_bits| fits in four bytes.
+  size_t total_bits = ctx->Nl + (len << 3);
+  uint8_t length_bytes[4];
+  length_bytes[0] = (uint8_t)(total_bits >> 24);
+  length_bytes[1] = (uint8_t)(total_bits >> 16);
+  length_bytes[2] = (uint8_t)(total_bits >> 8);
+  length_bytes[3] = (uint8_t)total_bits;
+
+  // We now construct and process each expected block in constant-time.
+  uint8_t block[SHA_CBLOCK] = {0};
+  uint32_t result[5] = {0};
+  // input_idx is the index into |in| corresponding to the current block.
+  // However, we allow this index to overflow beyond |max_len|, to simplify the
+  // 0x80 byte.
+  size_t input_idx = 0;
+  for (size_t i = 0; i < max_blocks; i++) {
+    // Fill |block| with data from the partial block in |ctx| and |in|. We copy
+    // as if we were hashing up to |max_len| and then zero the excess later.
+    size_t block_start = 0;
+    if (i == 0) {
+      OPENSSL_memcpy(block, ctx->data, ctx->num);
+      block_start = ctx->num;
+    }
+    if (input_idx < max_len) {
+      size_t to_copy = SHA_CBLOCK - block_start;
+      if (to_copy > max_len - input_idx) {
+        to_copy = max_len - input_idx;
+      }
+      OPENSSL_memcpy(block + block_start, in + input_idx, to_copy);
+    }
+
+    // Zero any bytes beyond |len| and add the 0x80 byte.
+    for (size_t j = block_start; j < SHA_CBLOCK; j++) {
+      // input[idx] corresponds to block[j].
+      size_t idx = input_idx + j - block_start;
+      // The barriers on |len| are not strictly necessary. However, without
+      // them, GCC compiles this code by incorporating |len| into the loop
+      // counter and subtracting it out later. This is still constant-time, but
+      // it frustrates attempts to validate this.
+      uint8_t is_in_bounds = constant_time_lt_8(idx, value_barrier_w(len));
+      uint8_t is_padding_byte = constant_time_eq_8(idx, value_barrier_w(len));
+      block[j] &= is_in_bounds;
+      block[j] |= 0x80 & is_padding_byte;
+    }
+
+    input_idx += SHA_CBLOCK - block_start;
+
+    // Fill in the length if this is the last block.
+    crypto_word_t is_last_block = constant_time_eq_w(i, last_block);
+    for (size_t j = 0; j < 4; j++) {
+      block[SHA_CBLOCK - 4 + j] |= is_last_block & length_bytes[j];
+    }
+
+    // Process the block and save the hash state if it is the final value.
+    SHA1_Transform(ctx, block);
+    for (size_t j = 0; j < 5; j++) {
+      result[j] |= is_last_block & ctx->h[j];
+    }
   }
+
+  // Write the output.
+  for (size_t i = 0; i < 5; i++) {
+    CRYPTO_store_u32_be(out + 4 * i, result[i]);
+  }
+  return 1;
 }
 
 int EVP_tls_cbc_record_digest_supported(const EVP_MD *md) {
-  switch (EVP_MD_type(md)) {
-    case NID_sha1:
-    case NID_sha256:
-    case NID_sha384:
-      return 1;
-
-    default:
-      return 0;
-  }
+  return EVP_MD_type(md) == NID_sha1;
 }
 
 int EVP_tls_cbc_digest_record(const EVP_MD *md, uint8_t *md_out,
                               size_t *md_out_size, const uint8_t header[13],
-                              const uint8_t *data, size_t data_plus_mac_size,
+                              const uint8_t *data, size_t data_size,
                               size_t data_plus_mac_plus_padding_size,
                               const uint8_t *mac_secret,
                               unsigned mac_secret_length) {
-  HASH_CTX md_state;
-  void (*md_final_raw)(HASH_CTX *ctx, uint8_t *md_out);
-  void (*md_transform)(HASH_CTX *ctx, const uint8_t *block);
-  unsigned md_size, md_block_size = 64, md_block_shift = 6;
-  // md_length_size is the number of bytes in the length field that terminates
-  // the hash.
-  unsigned md_length_size = 8;
-
-  // Bound the acceptable input so we can forget about many possible overflows
-  // later in this function. This is redundant with the record size limits in
-  // TLS.
-  if (data_plus_mac_plus_padding_size >= 1024 * 1024) {
-    assert(0);
-    return 0;
-  }
-
-  switch (EVP_MD_type(md)) {
-    case NID_sha1:
-      SHA1_Init(&md_state.sha1);
-      md_final_raw = tls1_sha1_final_raw;
-      md_transform = tls1_sha1_transform;
-      md_size = SHA_DIGEST_LENGTH;
-      break;
-
-    case NID_sha256:
-      SHA256_Init(&md_state.sha256);
-      md_final_raw = tls1_sha256_final_raw;
-      md_transform = tls1_sha256_transform;
-      md_size = SHA256_DIGEST_LENGTH;
-      break;
-
-    case NID_sha384:
-      SHA384_Init(&md_state.sha512);
-      md_final_raw = tls1_sha512_final_raw;
-      md_transform = tls1_sha512_transform;
-      md_size = SHA384_DIGEST_LENGTH;
-      md_block_size = 128;
-      md_block_shift = 7;
-      md_length_size = 16;
-      break;
-
-    default:
+  if (EVP_MD_type(md) != NID_sha1) {
       // EVP_tls_cbc_record_digest_supported should have been called first to
       // check that the hash function is supported.
       assert(0);
@@ -318,175 +285,54 @@
       return 0;
   }
 
-  assert(md_length_size <= MAX_HASH_BIT_COUNT_BYTES);
-  assert(md_block_size <= MAX_HASH_BLOCK_SIZE);
-  assert(md_block_size == (1u << md_block_shift));
-  assert(md_size <= EVP_MAX_MD_SIZE);
-
-  static const size_t kHeaderLength = 13;
-
-  // kVarianceBlocks is the number of blocks of the hash that we have to
-  // calculate in constant time because they could be altered by the
-  // padding value.
-  //
-  // TLSv1 has MACs up to 48 bytes long (SHA-384) and the padding is not
-  // required to be minimal. Therefore we say that the final |kVarianceBlocks|
-  // blocks can vary based on the padding and on the hash used. This value
-  // must be derived from public information.
-  const size_t kVarianceBlocks =
-     ( 255 + 1 + // maximum padding bytes + padding length
-       md_size + // length of hash's output
-       md_block_size - 1 // ceiling
-     ) / md_block_size
-     + 1; // the 0x80 marker and the encoded message length could or not
-          // require an extra block; since the exact value depends on the
-          // message length; thus, one extra block is always added to run
-          // in constant time.
-
-  // From now on we're dealing with the MAC, which conceptually has 13
-  // bytes of `header' before the start of the data.
-  size_t len = data_plus_mac_plus_padding_size + kHeaderLength;
-  // max_mac_bytes contains the maximum bytes of bytes in the MAC, including
-  // |header|, assuming that there's no padding.
-  size_t max_mac_bytes = len - md_size - 1;
-  // num_blocks is the maximum number of hash blocks.
-  size_t num_blocks =
-      (max_mac_bytes + 1 + md_length_size + md_block_size - 1) / md_block_size;
-  // In order to calculate the MAC in constant time we have to handle
-  // the final blocks specially because the padding value could cause the
-  // end to appear somewhere in the final |kVarianceBlocks| blocks and we
-  // can't leak where. However, |num_starting_blocks| worth of data can
-  // be hashed right away because no padding value can affect whether
-  // they are plaintext.
-  size_t num_starting_blocks = 0;
-  // k is the starting byte offset into the conceptual header||data where
-  // we start processing.
-  size_t k = 0;
-  // mac_end_offset is the index just past the end of the data to be MACed.
-  size_t mac_end_offset = data_plus_mac_size + kHeaderLength - md_size;
-  // c is the index of the 0x80 byte in the final hash block that contains
-  // application data.
-  size_t c = mac_end_offset & (md_block_size - 1);
-  // index_a is the hash block number that contains the 0x80 terminating value.
-  size_t index_a = mac_end_offset >> md_block_shift;
-  // index_b is the hash block number that contains the 64-bit hash length, in
-  // bits.
-  size_t index_b = (mac_end_offset + md_length_size) >> md_block_shift;
-
-  if (num_blocks > kVarianceBlocks) {
-    num_starting_blocks = num_blocks - kVarianceBlocks;
-    k = md_block_size * num_starting_blocks;
+  if (mac_secret_length > SHA_CBLOCK) {
+    // HMAC pads small keys with zeros and hashes large keys down. This function
+    // should never reach the large key case.
+    assert(0);
+    return 0;
   }
 
-  // bits is the hash-length in bits. It includes the additional hash
-  // block for the masked HMAC key.
-  size_t bits = 8 * mac_end_offset;  // at most 18 bits to represent
-
   // Compute the initial HMAC block.
-  bits += 8 * md_block_size;
-  // hmac_pad is the masked HMAC key.
-  uint8_t hmac_pad[MAX_HASH_BLOCK_SIZE];
-  OPENSSL_memset(hmac_pad, 0, md_block_size);
-  assert(mac_secret_length <= sizeof(hmac_pad));
+  uint8_t hmac_pad[SHA_CBLOCK];
+  OPENSSL_memset(hmac_pad, 0, sizeof(hmac_pad));
   OPENSSL_memcpy(hmac_pad, mac_secret, mac_secret_length);
-  for (size_t i = 0; i < md_block_size; i++) {
+  for (size_t i = 0; i < SHA_CBLOCK; i++) {
     hmac_pad[i] ^= 0x36;
   }
 
-  md_transform(&md_state, hmac_pad);
+  SHA_CTX ctx;
+  SHA1_Init(&ctx);
+  SHA1_Update(&ctx, hmac_pad, SHA_CBLOCK);
+  SHA1_Update(&ctx, header, 13);
 
-  // The length check means |bits| fits in four bytes.
-  uint8_t length_bytes[MAX_HASH_BIT_COUNT_BYTES];
-  OPENSSL_memset(length_bytes, 0, md_length_size - 4);
-  length_bytes[md_length_size - 4] = (uint8_t)(bits >> 24);
-  length_bytes[md_length_size - 3] = (uint8_t)(bits >> 16);
-  length_bytes[md_length_size - 2] = (uint8_t)(bits >> 8);
-  length_bytes[md_length_size - 1] = (uint8_t)bits;
-
-  if (k > 0) {
-    // k is a multiple of md_block_size.
-    uint8_t first_block[MAX_HASH_BLOCK_SIZE];
-    OPENSSL_memcpy(first_block, header, 13);
-    OPENSSL_memcpy(first_block + 13, data, md_block_size - 13);
-    md_transform(&md_state, first_block);
-    for (size_t i = 1; i < k / md_block_size; i++) {
-      md_transform(&md_state, data + md_block_size * i - 13);
-    }
+  // There are at most 256 bytes of padding, so we can compute the public
+  // minimum length for |data_size|.
+  size_t min_data_size = 0;
+  if (data_plus_mac_plus_padding_size > SHA_DIGEST_LENGTH + 256) {
+    min_data_size = data_plus_mac_plus_padding_size - SHA_DIGEST_LENGTH - 256;
   }
 
-  uint8_t mac_out[EVP_MAX_MD_SIZE];
-  OPENSSL_memset(mac_out, 0, sizeof(mac_out));
+  // Hash the public minimum length directly. This reduces the number of blocks
+  // that must be computed in constant-time.
+  SHA1_Update(&ctx, data, min_data_size);
 
-  // We now process the final hash blocks. For each block, we construct
-  // it in constant time. If the |i==index_a| then we'll include the 0x80
-  // bytes and zero pad etc. For each block we selectively copy it, in
-  // constant time, to |mac_out|.
-  for (size_t i = num_starting_blocks;
-       i <= num_starting_blocks + kVarianceBlocks; i++) {
-    uint8_t block[MAX_HASH_BLOCK_SIZE];
-    uint8_t is_block_a = constant_time_eq_8(i, index_a);
-    uint8_t is_block_b = constant_time_eq_8(i, index_b);
-    for (size_t j = 0; j < md_block_size; j++) {
-      uint8_t b = 0;
-      if (k < kHeaderLength) {
-        b = header[k];
-      } else if (k < data_plus_mac_plus_padding_size + kHeaderLength) {
-        b = data[k - kHeaderLength];
-      }
-      k++;
-
-      uint8_t is_past_c = is_block_a & constant_time_ge_8(j, c);
-      uint8_t is_past_cp1 = is_block_a & constant_time_ge_8(j, c + 1);
-      // If this is the block containing the end of the
-      // application data, and we are at the offset for the
-      // 0x80 value, then overwrite b with 0x80.
-      b = constant_time_select_8(is_past_c, 0x80, b);
-      // If this the the block containing the end of the
-      // application data and we're past the 0x80 value then
-      // just write zero.
-      b = b & ~is_past_cp1;
-      // If this is index_b (the final block), but not
-      // index_a (the end of the data), then the 64-bit
-      // length didn't fit into index_a and we're having to
-      // add an extra block of zeros.
-      b &= ~is_block_b | is_block_a;
-
-      // The final bytes of one of the blocks contains the
-      // length.
-      if (j >= md_block_size - md_length_size) {
-        // If this is index_b, write a length byte.
-        b = constant_time_select_8(
-            is_block_b, length_bytes[j - (md_block_size - md_length_size)], b);
-      }
-      block[j] = b;
-    }
-
-    md_transform(&md_state, block);
-    md_final_raw(&md_state, block);
-    // If this is index_b, copy the hash value to |mac_out|.
-    for (size_t j = 0; j < md_size; j++) {
-      mac_out[j] |= block[j] & is_block_b;
-    }
-  }
-
-  EVP_MD_CTX md_ctx;
-  EVP_MD_CTX_init(&md_ctx);
-  if (!EVP_DigestInit_ex(&md_ctx, md, NULL /* engine */)) {
-    EVP_MD_CTX_cleanup(&md_ctx);
+  // Hash the remaining data without leaking |data_size|.
+  uint8_t mac_out[SHA_DIGEST_LENGTH];
+  if (!EVP_sha1_final_with_secret_suffix(
+          &ctx, mac_out, data + min_data_size, data_size - min_data_size,
+          data_plus_mac_plus_padding_size - min_data_size)) {
     return 0;
   }
 
   // Complete the HMAC in the standard manner.
-  for (size_t i = 0; i < md_block_size; i++) {
+  SHA1_Init(&ctx);
+  for (size_t i = 0; i < SHA_CBLOCK; i++) {
     hmac_pad[i] ^= 0x6a;
   }
 
-  EVP_DigestUpdate(&md_ctx, hmac_pad, md_block_size);
-  EVP_DigestUpdate(&md_ctx, mac_out, md_size);
-  unsigned md_out_size_u;
-  EVP_DigestFinal(&md_ctx, md_out, &md_out_size_u);
-  *md_out_size = md_out_size_u;
-  EVP_MD_CTX_cleanup(&md_ctx);
-
+  SHA1_Update(&ctx, hmac_pad, SHA_CBLOCK);
+  SHA1_Update(&ctx, mac_out, SHA_DIGEST_LENGTH);
+  SHA1_Final(md_out, &ctx);
+  *md_out_size = SHA_DIGEST_LENGTH;
   return 1;
 }
diff --git a/deps/boringssl/src/crypto/conf/conf.c b/deps/boringssl/src/crypto/conf/conf.c
index 7070ca8..c1e4e96 100644
--- a/deps/boringssl/src/crypto/conf/conf.c
+++ b/deps/boringssl/src/crypto/conf/conf.c
@@ -68,6 +68,7 @@
 #include "conf_def.h"
 #include "internal.h"
 #include "../internal.h"
+#include "../lhash/internal.h"
 
 
 DEFINE_LHASH_OF(CONF_VALUE)
@@ -76,12 +77,16 @@
   LHASH_OF(CONF_VALUE) *data;
 };
 
+static const char kDefaultSectionName[] = "default";
+
 // The maximum length we can grow a value to after variable expansion. 64k
 // should be more than enough for all reasonable uses.
 #define MAX_CONF_VALUE_LENGTH 65536
 
 static uint32_t conf_value_hash(const CONF_VALUE *v) {
-  return (lh_strhash(v->section) << 2) ^ lh_strhash(v->name);
+  const uint32_t section_hash = v->section ? OPENSSL_strhash(v->section) : 0;
+  const uint32_t name_hash = v->name ? OPENSSL_strhash(v->name) : 0;
+  return (section_hash << 2) ^ name_hash;
 }
 
 static int conf_value_cmp(const CONF_VALUE *a, const CONF_VALUE *b) {
@@ -155,12 +160,14 @@
   OPENSSL_free(value);
 }
 
+static void value_free_arg(CONF_VALUE *value, void *arg) { value_free(value); }
+
 void NCONF_free(CONF *conf) {
   if (conf == NULL || conf->data == NULL) {
     return;
   }
 
-  lh_CONF_VALUE_doall(conf->data, value_free);
+  lh_CONF_VALUE_doall_arg(conf->data, value_free_arg, NULL);
   lh_CONF_VALUE_free(conf->data);
   OPENSSL_free(conf);
 }
@@ -390,6 +397,10 @@
                              const char *name) {
   CONF_VALUE template, *value;
 
+  if (section == NULL) {
+    section = kDefaultSectionName;
+  }
+
   OPENSSL_memset(&template, 0, sizeof(template));
   template.section = (char *) section;
   template.name = (char *) name;
@@ -538,7 +549,7 @@
     goto err;
   }
 
-  section = OPENSSL_strdup("default");
+  section = OPENSSL_strdup(kDefaultSectionName);
   if (section == NULL) {
     OPENSSL_PUT_ERROR(CONF, ERR_R_MALLOC_FAILURE);
     goto err;
diff --git a/deps/boringssl/src/crypto/conf/conf_test.cc b/deps/boringssl/src/crypto/conf/conf_test.cc
new file mode 100644
index 0000000..65938e1
--- /dev/null
+++ b/deps/boringssl/src/crypto/conf/conf_test.cc
@@ -0,0 +1,44 @@
+/* Copyright (c) 2021, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/bio.h>
+#include <openssl/conf.h>
+
+#include <gtest/gtest.h>
+
+
+TEST(ConfTest, Parse) {
+  // Check that basic parsing works. (We strongly recommend that people don't
+  // use the [N]CONF functions.)
+
+  static const char kConf[] = R"(
+# Comment
+
+key=value
+
+[section_name]
+key=value2
+)";
+
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kConf, sizeof(kConf) - 1));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<CONF> conf(NCONF_new(nullptr));
+  ASSERT_TRUE(conf);
+  ASSERT_TRUE(NCONF_load_bio(conf.get(), bio.get(), nullptr));
+  EXPECT_TRUE(NCONF_get_section(conf.get(), "section_name"));
+  EXPECT_FALSE(NCONF_get_section(conf.get(), "other_section"));
+  EXPECT_STREQ(NCONF_get_string(conf.get(), nullptr, "key"), "value");
+  EXPECT_STREQ(NCONF_get_string(conf.get(), "section_name", "key"), "value2");
+  EXPECT_STREQ(NCONF_get_string(conf.get(), "other_section", "key"), nullptr);
+}
diff --git a/deps/boringssl/src/crypto/cpu-arm-linux.c b/deps/boringssl/src/crypto/cpu-arm-linux.c
index c9d771f..962a4a5 100644
--- a/deps/boringssl/src/crypto/cpu-arm-linux.c
+++ b/deps/boringssl/src/crypto/cpu-arm-linux.c
@@ -175,7 +175,13 @@
     hwcap = crypto_get_arm_hwcap_from_cpuinfo(&cpuinfo);
   }
 
-  // Clear NEON support if known broken.
+  // Clear NEON support if known broken. Note, if NEON is available statically,
+  // the non-NEON code is dropped and this workaround is a no-op.
+  //
+  // TODO(davidben): The Android NDK now builds with NEON statically available
+  // by default. Cronet still has some consumers that support NEON-less devices
+  // (b/150371744). Get metrics on whether they still see this CPU and, if not,
+  // remove this check entirely.
   g_has_broken_neon = crypto_cpuinfo_has_broken_neon(&cpuinfo);
   if (g_has_broken_neon) {
     hwcap &= ~HWCAP_NEON;
@@ -186,7 +192,10 @@
     OPENSSL_armcap_P |= ARMV7_NEON;
 
     // Some ARMv8 Android devices don't expose AT_HWCAP2. Fall back to
-    // /proc/cpuinfo. See https://crbug.com/596156.
+    // /proc/cpuinfo. See https://crbug.com/boringssl/46. As of February 2021,
+    // this is now rare (see Chrome's Net.NeedsHWCAP2Workaround metric), but AES
+    // and PMULL extensions are very useful, so we still carry the workaround
+    // for now.
     unsigned long hwcap2 = 0;
     if (getauxval != NULL) {
       hwcap2 = getauxval(AT_HWCAP2);
diff --git a/deps/boringssl/src/crypto/cpu-arm.c b/deps/boringssl/src/crypto/cpu-arm.c
index ef395ea..e8596ac 100644
--- a/deps/boringssl/src/crypto/cpu-arm.c
+++ b/deps/boringssl/src/crypto/cpu-arm.c
@@ -22,15 +22,15 @@
 
 extern uint32_t OPENSSL_armcap_P;
 
-char CRYPTO_is_NEON_capable_at_runtime(void) {
+int CRYPTO_is_NEON_capable_at_runtime(void) {
   return (OPENSSL_armcap_P & ARMV7_NEON) != 0;
 }
 
-int CRYPTO_is_ARMv8_AES_capable(void) {
+int CRYPTO_is_ARMv8_AES_capable_at_runtime(void) {
   return (OPENSSL_armcap_P & ARMV8_AES) != 0;
 }
 
-int CRYPTO_is_ARMv8_PMULL_capable(void) {
+int CRYPTO_is_ARMv8_PMULL_capable_at_runtime(void) {
   return (OPENSSL_armcap_P & ARMV8_PMULL) != 0;
 }
 
diff --git a/deps/boringssl/src/crypto/crypto_test.cc b/deps/boringssl/src/crypto/crypto_test.cc
index f6c2374..bf0bcd7 100644
--- a/deps/boringssl/src/crypto/crypto_test.cc
+++ b/deps/boringssl/src/crypto/crypto_test.cc
@@ -19,6 +19,8 @@
 
 #include <openssl/base.h>
 #include <openssl/crypto.h>
+#include <openssl/cipher.h>
+#include <openssl/mem.h>
 
 #include <gtest/gtest.h>
 
@@ -33,3 +35,47 @@
   EXPECT_EQ(expected,
             std::string(OPENSSL_VERSION_TEXT).substr(0, strlen(expected)));
 }
+
+TEST(CryptoTest, Strndup) {
+  bssl::UniquePtr<char> str(OPENSSL_strndup(nullptr, 0));
+  EXPECT_TRUE(str);
+  EXPECT_STREQ("", str.get());
+}
+
+#if defined(BORINGSSL_FIPS_COUNTERS)
+TEST(CryptoTest, FIPSCountersEVP) {
+  constexpr struct {
+    const EVP_CIPHER *(*cipher)();
+    fips_counter_t counter;
+  } kTests[] = {
+    {
+        EVP_aes_128_gcm,
+        fips_counter_evp_aes_128_gcm,
+    },
+    {
+        EVP_aes_256_gcm,
+        fips_counter_evp_aes_256_gcm,
+    },
+    {
+        EVP_aes_128_ctr,
+        fips_counter_evp_aes_128_ctr,
+    },
+    {
+        EVP_aes_256_ctr,
+        fips_counter_evp_aes_256_ctr,
+    },
+  };
+
+  uint8_t key[EVP_MAX_KEY_LENGTH] = {0};
+  uint8_t iv[EVP_MAX_IV_LENGTH] = {1};
+
+  for (const auto& test : kTests) {
+    const size_t before = FIPS_read_counter(test.counter);
+
+    bssl::ScopedEVP_CIPHER_CTX ctx;
+    ASSERT_TRUE(EVP_EncryptInit_ex(ctx.get(), test.cipher(), /*engine=*/nullptr,
+                                   key, iv));
+    ASSERT_GT(FIPS_read_counter(test.counter), before);
+  }
+}
+#endif  // BORINGSSL_FIPS_COUNTERS
diff --git a/deps/boringssl/src/crypto/curve25519/curve25519.c b/deps/boringssl/src/crypto/curve25519/curve25519.c
index 232f6e0..ea48810 100644
--- a/deps/boringssl/src/crypto/curve25519/curve25519.c
+++ b/deps/boringssl/src/crypto/curve25519/curve25519.c
@@ -820,7 +820,7 @@
 //
 // Preconditions:
 //   a[31] <= 127
-void x25519_ge_scalarmult_base(ge_p3 *h, const uint8_t *a) {
+void x25519_ge_scalarmult_base(ge_p3 *h, const uint8_t a[32]) {
   signed char e[64];
   signed char carry;
   ge_p1p1 r;
diff --git a/deps/boringssl/src/crypto/curve25519/internal.h b/deps/boringssl/src/crypto/curve25519/internal.h
index 01be307..76ff78f 100644
--- a/deps/boringssl/src/crypto/curve25519/internal.h
+++ b/deps/boringssl/src/crypto/curve25519/internal.h
@@ -106,7 +106,7 @@
 } ge_cached;
 
 void x25519_ge_tobytes(uint8_t s[32], const ge_p2 *h);
-int x25519_ge_frombytes_vartime(ge_p3 *h, const uint8_t *s);
+int x25519_ge_frombytes_vartime(ge_p3 *h, const uint8_t s[32]);
 void x25519_ge_p3_to_cached(ge_cached *r, const ge_p3 *p);
 void x25519_ge_p1p1_to_p2(ge_p2 *r, const ge_p1p1 *p);
 void x25519_ge_p1p1_to_p3(ge_p3 *r, const ge_p1p1 *p);
diff --git a/deps/boringssl/src/crypto/digest_extra/digest_extra.c b/deps/boringssl/src/crypto/digest_extra/digest_extra.c
index 311c5cb..8cbb28e 100644
--- a/deps/boringssl/src/crypto/digest_extra/digest_extra.c
+++ b/deps/boringssl/src/crypto/digest_extra/digest_extra.c
@@ -58,11 +58,12 @@
 
 #include <string.h>
 
-#include <openssl/asn1.h>
 #include <openssl/blake2.h>
 #include <openssl/bytestring.h>
+#include <openssl/obj.h>
 #include <openssl/nid.h>
 
+#include "../asn1/internal.h"
 #include "../internal.h"
 #include "../fipsmodule/digest/internal.h"
 
@@ -82,6 +83,7 @@
     {NID_sha256, EVP_sha256, SN_sha256, LN_sha256},
     {NID_sha384, EVP_sha384, SN_sha384, LN_sha384},
     {NID_sha512, EVP_sha512, SN_sha512, LN_sha512},
+    {NID_sha512_256, EVP_sha512_256, SN_sha512_256, LN_sha512_256},
     {NID_md5_sha1, EVP_md5_sha1, SN_md5_sha1, LN_md5_sha1},
     // As a remnant of signing |EVP_MD|s, OpenSSL returned the corresponding
     // hash function when given a signature OID. To avoid unintended lax parsing
@@ -152,13 +154,14 @@
 }
 
 const EVP_MD *EVP_get_digestbyobj(const ASN1_OBJECT *obj) {
-  // Handle objects with no corresponding OID.
+  // Handle objects with no corresponding OID. Note we don't use |OBJ_obj2nid|
+  // here to avoid pulling in the OID table.
   if (obj->nid != NID_undef) {
     return EVP_get_digestbynid(obj->nid);
   }
 
   CBS cbs;
-  CBS_init(&cbs, obj->data, obj->length);
+  CBS_init(&cbs, OBJ_get0_data(obj), OBJ_length(obj));
   return cbs_to_md(&cbs);
 }
 
diff --git a/deps/boringssl/src/crypto/digest_extra/digest_test.cc b/deps/boringssl/src/crypto/digest_extra/digest_test.cc
index 80b5106..12858e2 100644
--- a/deps/boringssl/src/crypto/digest_extra/digest_test.cc
+++ b/deps/boringssl/src/crypto/digest_extra/digest_test.cc
@@ -163,7 +163,7 @@
   bssl::ScopedEVP_MD_CTX ctx;
 
   // Test the input provided.
-  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), NULL));
+  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr));
   for (size_t i = 0; i < test->repeat; i++) {
     ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), test->input, strlen(test->input)));
   }
@@ -173,8 +173,8 @@
   CompareDigest(test, digest.get(), digest_len);
 
   // Test the input one character at a time.
-  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), NULL));
-  ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), NULL, 0));
+  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr));
+  ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), nullptr, 0));
   for (size_t i = 0; i < test->repeat; i++) {
     for (const char *p = test->input; *p; p++) {
       ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), p, 1));
@@ -185,7 +185,7 @@
   CompareDigest(test, digest.get(), digest_len);
 
   // Test with unaligned input.
-  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), NULL));
+  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr));
   std::vector<char> unaligned(strlen(test->input) + 1);
   char *ptr = unaligned.data();
   if ((reinterpret_cast<uintptr_t>(ptr) & 1) == 0) {
@@ -199,7 +199,7 @@
   CompareDigest(test, digest.get(), digest_len);
 
   // Make a copy of the digest in the initial state.
-  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), NULL));
+  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr));
   bssl::ScopedEVP_MD_CTX copy;
   ASSERT_TRUE(EVP_MD_CTX_copy_ex(copy.get(), ctx.get()));
   for (size_t i = 0; i < test->repeat; i++) {
@@ -220,6 +220,27 @@
   ASSERT_TRUE(EVP_DigestFinal_ex(copy.get(), digest.get(), &digest_len));
   CompareDigest(test, digest.get(), digest_len);
 
+  // Move the digest from the initial state.
+  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr));
+  copy = std::move(ctx);
+  for (size_t i = 0; i < test->repeat; i++) {
+    ASSERT_TRUE(EVP_DigestUpdate(copy.get(), test->input, strlen(test->input)));
+  }
+  ASSERT_TRUE(EVP_DigestFinal_ex(copy.get(), digest.get(), &digest_len));
+  CompareDigest(test, digest.get(), digest_len);
+
+  // Move the digest with half the input provided.
+  ASSERT_TRUE(EVP_DigestInit_ex(ctx.get(), test->md.func(), nullptr));
+  ASSERT_TRUE(EVP_DigestUpdate(ctx.get(), test->input, half));
+  copy = std::move(ctx);
+  ASSERT_TRUE(EVP_DigestUpdate(copy.get(), test->input + half,
+                               strlen(test->input) - half));
+  for (size_t i = 1; i < test->repeat; i++) {
+    ASSERT_TRUE(EVP_DigestUpdate(copy.get(), test->input, strlen(test->input)));
+  }
+  ASSERT_TRUE(EVP_DigestFinal_ex(copy.get(), digest.get(), &digest_len));
+  CompareDigest(test, digest.get(), digest_len);
+
   // Test the one-shot function.
   if (test->md.one_shot_func && test->repeat == 1) {
     uint8_t *out = test->md.one_shot_func((const uint8_t *)test->input,
diff --git a/deps/boringssl/src/crypto/err/err.c b/deps/boringssl/src/crypto/err/err.c
index 7973a0e..4aab75b 100644
--- a/deps/boringssl/src/crypto/err/err.c
+++ b/deps/boringssl/src/crypto/err/err.c
@@ -368,84 +368,6 @@
   errno = 0;
 }
 
-char *ERR_error_string(uint32_t packed_error, char *ret) {
-  static char buf[ERR_ERROR_STRING_BUF_LEN];
-
-  if (ret == NULL) {
-    // TODO(fork): remove this.
-    ret = buf;
-  }
-
-#if !defined(NDEBUG)
-  // This is aimed to help catch callers who don't provide
-  // |ERR_ERROR_STRING_BUF_LEN| bytes of space.
-  OPENSSL_memset(ret, 0, ERR_ERROR_STRING_BUF_LEN);
-#endif
-
-  return ERR_error_string_n(packed_error, ret, ERR_ERROR_STRING_BUF_LEN);
-}
-
-char *ERR_error_string_n(uint32_t packed_error, char *buf, size_t len) {
-  char lib_buf[64], reason_buf[64];
-  const char *lib_str, *reason_str;
-  unsigned lib, reason;
-
-  if (len == 0) {
-    return NULL;
-  }
-
-  lib = ERR_GET_LIB(packed_error);
-  reason = ERR_GET_REASON(packed_error);
-
-  lib_str = ERR_lib_error_string(packed_error);
-  reason_str = ERR_reason_error_string(packed_error);
-
-  if (lib_str == NULL) {
-    BIO_snprintf(lib_buf, sizeof(lib_buf), "lib(%u)", lib);
-    lib_str = lib_buf;
-  }
-
- if (reason_str == NULL) {
-    BIO_snprintf(reason_buf, sizeof(reason_buf), "reason(%u)", reason);
-    reason_str = reason_buf;
-  }
-
-  BIO_snprintf(buf, len, "error:%08" PRIx32 ":%s:OPENSSL_internal:%s",
-               packed_error, lib_str, reason_str);
-
-  if (strlen(buf) == len - 1) {
-    // output may be truncated; make sure we always have 5 colon-separated
-    // fields, i.e. 4 colons.
-    static const unsigned num_colons = 4;
-    unsigned i;
-    char *s = buf;
-
-    if (len <= num_colons) {
-      // In this situation it's not possible to ensure that the correct number
-      // of colons are included in the output.
-      return buf;
-    }
-
-    for (i = 0; i < num_colons; i++) {
-      char *colon = strchr(s, ':');
-      char *last_pos = &buf[len - 1] - num_colons + i;
-
-      if (colon == NULL || colon > last_pos) {
-        // set colon |i| at last possible position (buf[len-1] is the
-        // terminating 0). If we're setting this colon, then all whole of the
-        // rest of the string must be colons in order to have the correct
-        // number.
-        OPENSSL_memset(last_pos, ':', num_colons - i);
-        break;
-      }
-
-      s = colon + 1;
-    }
-  }
-
-  return buf;
-}
-
 // err_string_cmp is a compare function for searching error values with
 // |bsearch| in |err_string_lookup|.
 static int err_string_cmp(const void *a, const void *b) {
@@ -530,7 +452,7 @@
     "User defined functions",       // ERR_LIB_USER
 };
 
-const char *ERR_lib_error_string(uint32_t packed_error) {
+static const char *err_lib_error_string(uint32_t packed_error) {
   const uint32_t lib = ERR_GET_LIB(packed_error);
 
   if (lib >= ERR_NUM_LIBS) {
@@ -539,11 +461,16 @@
   return kLibraryNames[lib];
 }
 
+const char *ERR_lib_error_string(uint32_t packed_error) {
+  const char *ret = err_lib_error_string(packed_error);
+  return ret == NULL ? "unknown library" : ret;
+}
+
 const char *ERR_func_error_string(uint32_t packed_error) {
   return "OPENSSL_internal";
 }
 
-const char *ERR_reason_error_string(uint32_t packed_error) {
+static const char *err_reason_error_string(uint32_t packed_error) {
   const uint32_t lib = ERR_GET_LIB(packed_error);
   const uint32_t reason = ERR_GET_REASON(packed_error);
 
@@ -579,6 +506,86 @@
                            kOpenSSLReasonValuesLen, kOpenSSLReasonStringData);
 }
 
+const char *ERR_reason_error_string(uint32_t packed_error) {
+  const char *ret = err_reason_error_string(packed_error);
+  return ret == NULL ? "unknown error" : ret;
+}
+
+char *ERR_error_string(uint32_t packed_error, char *ret) {
+  static char buf[ERR_ERROR_STRING_BUF_LEN];
+
+  if (ret == NULL) {
+    // TODO(fork): remove this.
+    ret = buf;
+  }
+
+#if !defined(NDEBUG)
+  // This is aimed to help catch callers who don't provide
+  // |ERR_ERROR_STRING_BUF_LEN| bytes of space.
+  OPENSSL_memset(ret, 0, ERR_ERROR_STRING_BUF_LEN);
+#endif
+
+  return ERR_error_string_n(packed_error, ret, ERR_ERROR_STRING_BUF_LEN);
+}
+
+char *ERR_error_string_n(uint32_t packed_error, char *buf, size_t len) {
+  if (len == 0) {
+    return NULL;
+  }
+
+  unsigned lib = ERR_GET_LIB(packed_error);
+  unsigned reason = ERR_GET_REASON(packed_error);
+
+  const char *lib_str = err_lib_error_string(packed_error);
+  const char *reason_str = err_reason_error_string(packed_error);
+
+  char lib_buf[64], reason_buf[64];
+  if (lib_str == NULL) {
+    BIO_snprintf(lib_buf, sizeof(lib_buf), "lib(%u)", lib);
+    lib_str = lib_buf;
+  }
+
+ if (reason_str == NULL) {
+    BIO_snprintf(reason_buf, sizeof(reason_buf), "reason(%u)", reason);
+    reason_str = reason_buf;
+  }
+
+  BIO_snprintf(buf, len, "error:%08" PRIx32 ":%s:OPENSSL_internal:%s",
+               packed_error, lib_str, reason_str);
+
+  if (strlen(buf) == len - 1) {
+    // output may be truncated; make sure we always have 5 colon-separated
+    // fields, i.e. 4 colons.
+    static const unsigned num_colons = 4;
+    unsigned i;
+    char *s = buf;
+
+    if (len <= num_colons) {
+      // In this situation it's not possible to ensure that the correct number
+      // of colons are included in the output.
+      return buf;
+    }
+
+    for (i = 0; i < num_colons; i++) {
+      char *colon = strchr(s, ':');
+      char *last_pos = &buf[len - 1] - num_colons + i;
+
+      if (colon == NULL || colon > last_pos) {
+        // set colon |i| at last possible position (buf[len-1] is the
+        // terminating 0). If we're setting this colon, then all whole of the
+        // rest of the string must be colons in order to have the correct
+        // number.
+        OPENSSL_memset(last_pos, ':', num_colons - i);
+        break;
+      }
+
+      s = colon + 1;
+    }
+  }
+
+  return buf;
+}
+
 void ERR_print_errors_cb(ERR_print_errors_callback_t callback, void *ctx) {
   char buf[ERR_ERROR_STRING_BUF_LEN];
   char buf2[1024];
diff --git a/deps/boringssl/src/crypto/err/err_test.cc b/deps/boringssl/src/crypto/err/err_test.cc
index 41bcc78..b41f8dd 100644
--- a/deps/boringssl/src/crypto/err/err_test.cc
+++ b/deps/boringssl/src/crypto/err/err_test.cc
@@ -283,3 +283,13 @@
   // A buffer length of zero should not touch the buffer.
   ERR_error_string_n(err, nullptr, 0);
 }
+
+// Error-printing functions should return something with unknown errors.
+TEST(ErrTest, UnknownError) {
+  uint32_t err = ERR_PACK(0xff, 0xfff);
+  EXPECT_TRUE(ERR_lib_error_string(err));
+  EXPECT_TRUE(ERR_reason_error_string(err));
+  char buf[128];
+  ERR_error_string_n(err, buf, sizeof(buf));
+  EXPECT_NE(0u, strlen(buf));
+}
diff --git a/deps/boringssl/src/crypto/evp/evp.c b/deps/boringssl/src/crypto/evp/evp.c
index 653d657..bb31645 100644
--- a/deps/boringssl/src/crypto/evp/evp.c
+++ b/deps/boringssl/src/crypto/evp/evp.c
@@ -429,6 +429,15 @@
                            0, (void *)out_md);
 }
 
+void *EVP_PKEY_get0(const EVP_PKEY *pkey) {
+  // Node references, but never calls this function, so for now we return NULL.
+  // If other projects require complete support, call |EVP_PKEY_get0_RSA|, etc.,
+  // rather than reading |pkey->pkey.ptr| directly. This avoids problems if our
+  // internal representation does not match the type the caller expects from
+  // OpenSSL.
+  return NULL;
+}
+
 void OpenSSL_add_all_algorithms(void) {}
 
 void OPENSSL_add_all_algorithms_conf(void) {}
diff --git a/deps/boringssl/src/crypto/fipsmodule/CMakeLists.txt b/deps/boringssl/src/crypto/fipsmodule/CMakeLists.txt
index 83cf3f7..73f8a02 100644
--- a/deps/boringssl/src/crypto/fipsmodule/CMakeLists.txt
+++ b/deps/boringssl/src/crypto/fipsmodule/CMakeLists.txt
@@ -1,6 +1,6 @@
 include_directories(../../include)
 
-if(${ARCH} STREQUAL "x86_64")
+if(ARCH STREQUAL "x86_64")
   set(
     BCM_ASM_SOURCES
 
@@ -22,7 +22,7 @@
   )
 endif()
 
-if(${ARCH} STREQUAL "x86")
+if(ARCH STREQUAL "x86")
   set(
     BCM_ASM_SOURCES
 
@@ -40,7 +40,7 @@
   )
 endif()
 
-if(${ARCH} STREQUAL "arm")
+if(ARCH STREQUAL "arm")
   set(
     BCM_ASM_SOURCES
 
@@ -56,7 +56,7 @@
   )
 endif()
 
-if(${ARCH} STREQUAL "aarch64")
+if(ARCH STREQUAL "aarch64")
   set(
     BCM_ASM_SOURCES
 
@@ -71,7 +71,7 @@
   )
 endif()
 
-if(${ARCH} STREQUAL "ppc64le")
+if(ARCH STREQUAL "ppc64le")
   set(
     BCM_ASM_SOURCES
 
@@ -160,7 +160,7 @@
     bcm.c
   )
 
-  if(${ARCH} STREQUAL "aarch64")
+  if(ARCH STREQUAL "aarch64")
     # Perlasm output on Aarch64 needs to pass through the C preprocessor before
     # it can be parsed by delocate.
     foreach(asm ${BCM_ASM_SOURCES})
@@ -199,12 +199,15 @@
   set_target_properties(bcm_hashunset PROPERTIES LINKER_LANGUAGE C)
 
   set(MAYBE_INJECT_HASH_SHA256_FLAG "")
-  if (ARCH STREQUAL "aarch64")
+  # If building with OPENSSL_NO_ASM then ARCH will be "generic", but we still
+  # need to use SHA-256. Since this only matters for FIPS, we only need to
+  # worry about the Linux spelling of AArch64.
+  if (ARCH STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64")
     set(MAYBE_INJECT_HASH_SHA256_FLAG "-sha256")
   endif()
 
   go_executable(inject_hash
-	        boringssl.googlesource.com/boringssl/util/fipstools/inject_hash)
+                boringssl.googlesource.com/boringssl/util/fipstools/inject_hash)
   add_custom_command(
     OUTPUT bcm.o
     COMMAND ./inject_hash -o bcm.o -in-archive $<TARGET_FILE:bcm_hashunset> ${MAYBE_INJECT_HASH_SHA256_FLAG}
@@ -223,7 +226,6 @@
     OBJECT
 
     fips_shared_support.c
-    is_fips.c
   )
 
   add_dependencies(fipsmodule global_target)
@@ -240,7 +242,6 @@
     OBJECT
 
     fips_shared_support.c
-    is_fips.c
   )
 
   add_dependencies(fipsmodule global_target)
@@ -273,7 +274,6 @@
 
     bcm.c
     fips_shared_support.c
-    is_fips.c
 
     ${BCM_ASM_SOURCES}
   )
diff --git a/deps/boringssl/src/crypto/fipsmodule/bcm.c b/deps/boringssl/src/crypto/fipsmodule/bcm.c
index 601c4a8..3a1ad15 100644
--- a/deps/boringssl/src/crypto/fipsmodule/bcm.c
+++ b/deps/boringssl/src/crypto/fipsmodule/bcm.c
@@ -97,6 +97,7 @@
 #include "rsa/padding.c"
 #include "rsa/rsa.c"
 #include "rsa/rsa_impl.c"
+#include "self_check/fips.c"
 #include "self_check/self_check.c"
 #include "sha/sha1-altivec.c"
 #include "sha/sha1.c"
diff --git a/deps/boringssl/src/crypto/fipsmodule/bn/bn_test.cc b/deps/boringssl/src/crypto/fipsmodule/bn/bn_test.cc
index 9791437..72ec8c2 100644
--- a/deps/boringssl/src/crypto/fipsmodule/bn/bn_test.cc
+++ b/deps/boringssl/src/crypto/fipsmodule/bn/bn_test.cc
@@ -556,6 +556,19 @@
   EXPECT_BIGNUMS_EQUAL("A / B", quotient.get(), ret.get());
   EXPECT_BIGNUMS_EQUAL("A % B", remainder.get(), ret2.get());
 
+  ASSERT_TRUE(BN_copy(ret.get(), a.get()));
+  ASSERT_TRUE(BN_copy(ret2.get(), b.get()));
+  ASSERT_TRUE(BN_div(ret.get(), ret2.get(), ret.get(), ret2.get(), ctx));
+  EXPECT_BIGNUMS_EQUAL("A / B (in-place)", quotient.get(), ret.get());
+  EXPECT_BIGNUMS_EQUAL("A % B (in-place)", remainder.get(), ret2.get());
+
+  ASSERT_TRUE(BN_copy(ret2.get(), a.get()));
+  ASSERT_TRUE(BN_copy(ret.get(), b.get()));
+  ASSERT_TRUE(BN_div(ret.get(), ret2.get(), ret2.get(), ret.get(), ctx));
+  EXPECT_BIGNUMS_EQUAL("A / B (in-place, swapped)", quotient.get(), ret.get());
+  EXPECT_BIGNUMS_EQUAL("A % B (in-place, swapped)", remainder.get(),
+                       ret2.get());
+
   ASSERT_TRUE(BN_mul(ret.get(), quotient.get(), b.get(), ctx));
   ASSERT_TRUE(BN_add(ret.get(), ret.get(), remainder.get()));
   EXPECT_BIGNUMS_EQUAL("Quotient * B + Remainder", a.get(), ret.get());
@@ -600,9 +613,17 @@
     }
   }
 
-  ASSERT_TRUE(bn_div_consttime(ret.get(), ret2.get(), a.get(), b.get(), ctx));
+  ASSERT_TRUE(bn_div_consttime(ret.get(), ret2.get(), a.get(), b.get(),
+                               /*divisor_min_bits=*/0, ctx));
   EXPECT_BIGNUMS_EQUAL("A / B (constant-time)", quotient.get(), ret.get());
   EXPECT_BIGNUMS_EQUAL("A % B (constant-time)", remainder.get(), ret2.get());
+
+  ASSERT_TRUE(bn_div_consttime(ret.get(), ret2.get(), a.get(), b.get(),
+                               /*divisor_min_bits=*/BN_num_bits(b.get()), ctx));
+  EXPECT_BIGNUMS_EQUAL("A / B (constant-time, public width)", quotient.get(),
+                       ret.get());
+  EXPECT_BIGNUMS_EQUAL("A % B (constant-time, public width)", remainder.get(),
+                       ret2.get());
 }
 
 static void TestModMul(BIGNUMFileTest *t, BN_CTX *ctx) {
diff --git a/deps/boringssl/src/crypto/fipsmodule/bn/div.c b/deps/boringssl/src/crypto/fipsmodule/bn/div.c
index 333c770..02b9931 100644
--- a/deps/boringssl/src/crypto/fipsmodule/bn/div.c
+++ b/deps/boringssl/src/crypto/fipsmodule/bn/div.c
@@ -285,8 +285,10 @@
   // pointer to the 'top' of snum
   wnump = &(snum->d[num_n - 1]);
 
-  // Setup to 'res'
-  res->neg = (numerator->neg ^ divisor->neg);
+  // Setup |res|. |numerator| and |res| may alias, so we save |numerator->neg|
+  // for later.
+  const int numerator_neg = numerator->neg;
+  res->neg = (numerator_neg ^ divisor->neg);
   if (!bn_wexpand(res, loop + 1)) {
     goto err;
   }
@@ -379,14 +381,11 @@
   bn_set_minimal_width(snum);
 
   if (rem != NULL) {
-    // Keep a copy of the neg flag in numerator because if |rem| == |numerator|
-    // |BN_rshift| will overwrite it.
-    int neg = numerator->neg;
     if (!BN_rshift(rem, snum, norm_shift)) {
       goto err;
     }
     if (!BN_is_zero(rem)) {
-      rem->neg = neg;
+      rem->neg = numerator_neg;
     }
   }
 
@@ -457,7 +456,7 @@
 
 int bn_div_consttime(BIGNUM *quotient, BIGNUM *remainder,
                      const BIGNUM *numerator, const BIGNUM *divisor,
-                     BN_CTX *ctx) {
+                     unsigned divisor_min_bits, BN_CTX *ctx) {
   if (BN_is_negative(numerator) || BN_is_negative(divisor)) {
     OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER);
     return 0;
@@ -497,8 +496,26 @@
   r->neg = 0;
 
   // Incorporate |numerator| into |r|, one bit at a time, reducing after each
-  // step. At the start of each loop iteration, |r| < |divisor|
-  for (int i = numerator->width - 1; i >= 0; i--) {
+  // step. We maintain the invariant that |0 <= r < divisor| and
+  // |q * divisor + r = n| where |n| is the portion of |numerator| incorporated
+  // so far.
+  //
+  // First, we short-circuit the loop: if we know |divisor| has at least
+  // |divisor_min_bits| bits, the top |divisor_min_bits - 1| can be incorporated
+  // without reductions. This significantly speeds up |RSA_check_key|. For
+  // simplicity, we round down to a whole number of words.
+  assert(divisor_min_bits <= BN_num_bits(divisor));
+  int initial_words = 0;
+  if (divisor_min_bits > 0) {
+    initial_words = (divisor_min_bits - 1) / BN_BITS2;
+    if (initial_words > numerator->width) {
+      initial_words = numerator->width;
+    }
+    OPENSSL_memcpy(r->d, numerator->d + numerator->width - initial_words,
+                   initial_words * sizeof(BN_ULONG));
+  }
+
+  for (int i = numerator->width - initial_words - 1; i >= 0; i--) {
     for (int bit = BN_BITS2 - 1; bit >= 0; bit--) {
       // Incorporate the next bit of the numerator, by computing
       // r = 2*r or 2*r + 1. Note the result fits in one more word. We store the
diff --git a/deps/boringssl/src/crypto/fipsmodule/bn/gcd_extra.c b/deps/boringssl/src/crypto/fipsmodule/bn/gcd_extra.c
index 30540e3..53ab170 100644
--- a/deps/boringssl/src/crypto/fipsmodule/bn/gcd_extra.c
+++ b/deps/boringssl/src/crypto/fipsmodule/bn/gcd_extra.c
@@ -157,10 +157,11 @@
   BN_CTX_start(ctx);
   unsigned shift;
   BIGNUM *gcd = BN_CTX_get(ctx);
-  int ret = gcd != NULL &&
+  int ret = gcd != NULL &&  //
             bn_mul_consttime(r, a, b, ctx) &&
             bn_gcd_consttime(gcd, &shift, a, b, ctx) &&
-            bn_div_consttime(r, NULL, r, gcd, ctx) &&
+            // |gcd| has a secret bit width.
+            bn_div_consttime(r, NULL, r, gcd, /*divisor_min_bits=*/0, ctx) &&
             bn_rshift_secret_shift(r, r, shift, ctx);
   BN_CTX_end(ctx);
   return ret;
diff --git a/deps/boringssl/src/crypto/fipsmodule/bn/internal.h b/deps/boringssl/src/crypto/fipsmodule/bn/internal.h
index 623e0c6..cab9a81 100644
--- a/deps/boringssl/src/crypto/fipsmodule/bn/internal.h
+++ b/deps/boringssl/src/crypto/fipsmodule/bn/internal.h
@@ -297,7 +297,7 @@
 void bn_mul_comba8(BN_ULONG r[16], const BN_ULONG a[8], const BN_ULONG b[8]);
 
 // bn_sqr_comba8 sets |r| to |a|^2.
-void bn_sqr_comba8(BN_ULONG r[16], const BN_ULONG a[4]);
+void bn_sqr_comba8(BN_ULONG r[16], const BN_ULONG a[8]);
 
 // bn_sqr_comba4 sets |r| to |a|^2.
 void bn_sqr_comba4(BN_ULONG r[8], const BN_ULONG a[4]);
@@ -552,12 +552,15 @@
 // bn_div_consttime behaves like |BN_div|, but it rejects negative inputs and
 // treats both inputs, including their magnitudes, as secret. It is, as a
 // result, much slower than |BN_div| and should only be used for rare operations
-// where Montgomery reduction is not available.
+// where Montgomery reduction is not available. |divisor_min_bits| is a
+// public lower bound for |BN_num_bits(divisor)|. When |divisor|'s bit width is
+// public, this can speed up the operation.
 //
 // Note that |quotient->width| will be set pessimally to |numerator->width|.
 OPENSSL_EXPORT int bn_div_consttime(BIGNUM *quotient, BIGNUM *remainder,
                                     const BIGNUM *numerator,
-                                    const BIGNUM *divisor, BN_CTX *ctx);
+                                    const BIGNUM *divisor,
+                                    unsigned divisor_min_bits, BN_CTX *ctx);
 
 // bn_is_relatively_prime checks whether GCD(|x|, |y|) is one. On success, it
 // returns one and sets |*out_relatively_prime| to one if the GCD was one and
diff --git a/deps/boringssl/src/crypto/fipsmodule/bn/prime.c b/deps/boringssl/src/crypto/fipsmodule/bn/prime.c
index 262822f..2e58cae 100644
--- a/deps/boringssl/src/crypto/fipsmodule/bn/prime.c
+++ b/deps/boringssl/src/crypto/fipsmodule/bn/prime.c
@@ -115,10 +115,6 @@
 #include "../../internal.h"
 
 
-// The quick sieve algorithm approach to weeding out primes is Philip
-// Zimmermann's, as implemented in PGP.  I have had a read of his comments and
-// implemented my own version.
-
 // kPrimes contains the first 1024 primes.
 static const uint16_t kPrimes[] = {
     2,    3,    5,    7,    11,   13,   17,   19,   23,   29,   31,   37,
diff --git a/deps/boringssl/src/crypto/fipsmodule/cipher/cipher.c b/deps/boringssl/src/crypto/fipsmodule/cipher/cipher.c
index c50c6c5..51c96b4 100644
--- a/deps/boringssl/src/crypto/fipsmodule/cipher/cipher.c
+++ b/deps/boringssl/src/crypto/fipsmodule/cipher/cipher.c
@@ -57,6 +57,7 @@
 #include <openssl/cipher.h>
 
 #include <assert.h>
+#include <limits.h>
 #include <string.h>
 
 #include <openssl/err.h>
@@ -224,7 +225,6 @@
 
   ctx->buf_len = 0;
   ctx->final_used = 0;
-  ctx->block_mask = ctx->cipher->block_size - 1;
   return 1;
 }
 
@@ -238,16 +238,31 @@
   return EVP_CipherInit_ex(ctx, cipher, impl, key, iv, 0);
 }
 
+// block_remainder returns the number of bytes to remove from |len| to get a
+// multiple of |ctx|'s block size.
+static int block_remainder(const EVP_CIPHER_CTX *ctx, int len) {
+  // |block_size| must be a power of two.
+  assert(ctx->cipher->block_size != 0);
+  assert((ctx->cipher->block_size & (ctx->cipher->block_size - 1)) == 0);
+  return len & (ctx->cipher->block_size - 1);
+}
+
 int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
                       const uint8_t *in, int in_len) {
-  int i, j, bl;
+  // Ciphers that use blocks may write up to |bl| extra bytes. Ensure the output
+  // does not overflow |*out_len|.
+  int bl = ctx->cipher->block_size;
+  if (bl > 1 && in_len > INT_MAX - bl) {
+    OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW);
+    return 0;
+  }
 
   if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
-    i = ctx->cipher->cipher(ctx, out, in, in_len);
-    if (i < 0) {
+    int ret = ctx->cipher->cipher(ctx, out, in, in_len);
+    if (ret < 0) {
       return 0;
     } else {
-      *out_len = i;
+      *out_len = ret;
     }
     return 1;
   }
@@ -257,7 +272,7 @@
     return in_len == 0;
   }
 
-  if (ctx->buf_len == 0 && (in_len & ctx->block_mask) == 0) {
+  if (ctx->buf_len == 0 && block_remainder(ctx, in_len) == 0) {
     if (ctx->cipher->cipher(ctx, out, in, in_len)) {
       *out_len = in_len;
       return 1;
@@ -267,8 +282,7 @@
     }
   }
 
-  i = ctx->buf_len;
-  bl = ctx->cipher->block_size;
+  int i = ctx->buf_len;
   assert(bl <= (int)sizeof(ctx->buf));
   if (i != 0) {
     if (bl - i > in_len) {
@@ -277,7 +291,7 @@
       *out_len = 0;
       return 1;
     } else {
-      j = bl - i;
+      int j = bl - i;
       OPENSSL_memcpy(&ctx->buf[i], in, j);
       if (!ctx->cipher->cipher(ctx, out, ctx->buf, bl)) {
         return 0;
@@ -291,7 +305,7 @@
     *out_len = 0;
   }
 
-  i = in_len & ctx->block_mask;
+  i = block_remainder(ctx, in_len);
   in_len -= i;
   if (in_len > 0) {
     if (!ctx->cipher->cipher(ctx, out, in, in_len)) {
@@ -353,8 +367,13 @@
 
 int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
                       const uint8_t *in, int in_len) {
-  int fix_len;
-  unsigned int b;
+  // Ciphers that use blocks may write up to |bl| extra bytes. Ensure the output
+  // does not overflow |*out_len|.
+  unsigned int b = ctx->cipher->block_size;
+  if (b > 1 && in_len > INT_MAX - (int)b) {
+    OPENSSL_PUT_ERROR(CIPHER, ERR_R_OVERFLOW);
+    return 0;
+  }
 
   if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
     int r = ctx->cipher->cipher(ctx, out, in, in_len);
@@ -376,15 +395,12 @@
     return EVP_EncryptUpdate(ctx, out, out_len, in, in_len);
   }
 
-  b = ctx->cipher->block_size;
   assert(b <= sizeof(ctx->final));
-
+  int fix_len = 0;
   if (ctx->final_used) {
     OPENSSL_memcpy(out, ctx->final, b);
     out += b;
     fix_len = 1;
-  } else {
-    fix_len = 0;
   }
 
   if (!EVP_EncryptUpdate(ctx, out, out_len, in, in_len)) {
diff --git a/deps/boringssl/src/crypto/fipsmodule/cipher/e_aes.c b/deps/boringssl/src/crypto/fipsmodule/cipher/e_aes.c
index 6df2b7b..f77133f 100644
--- a/deps/boringssl/src/crypto/fipsmodule/cipher/e_aes.c
+++ b/deps/boringssl/src/crypto/fipsmodule/cipher/e_aes.c
@@ -141,10 +141,22 @@
 
 static int aes_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key,
                         const uint8_t *iv, int enc) {
-  int ret, mode;
+  int ret;
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
+  const int mode = ctx->cipher->flags & EVP_CIPH_MODE_MASK;
 
-  mode = ctx->cipher->flags & EVP_CIPH_MODE_MASK;
+  if (mode == EVP_CIPH_CTR_MODE) {
+    switch (ctx->key_len) {
+      case 16:
+        boringssl_fips_inc_counter(fips_counter_evp_aes_128_ctr);
+        break;
+
+      case 32:
+        boringssl_fips_inc_counter(fips_counter_evp_aes_256_ctr);
+        break;
+    }
+  }
+
   if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE) && !enc) {
     if (hwaes_capable()) {
       ret = aes_hw_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
@@ -353,6 +365,17 @@
   if (!iv && !key) {
     return 1;
   }
+
+  switch (ctx->key_len) {
+    case 16:
+      boringssl_fips_inc_counter(fips_counter_evp_aes_128_gcm);
+      break;
+
+    case 32:
+      boringssl_fips_inc_counter(fips_counter_evp_aes_256_gcm);
+      break;
+  }
+
   if (key) {
     OPENSSL_memset(&gctx->gcm, 0, sizeof(gctx->gcm));
     gctx->ctr = aes_ctr_set_key(&gctx->ks.ks, &gctx->gcm.gcm_key, NULL, key,
diff --git a/deps/boringssl/src/crypto/fipsmodule/digest/digest.c b/deps/boringssl/src/crypto/fipsmodule/digest/digest.c
index 6b0c198..059d72c 100644
--- a/deps/boringssl/src/crypto/fipsmodule/digest/digest.c
+++ b/deps/boringssl/src/crypto/fipsmodule/digest/digest.c
@@ -68,6 +68,8 @@
 
 int EVP_MD_type(const EVP_MD *md) { return md->type; }
 
+int EVP_MD_nid(const EVP_MD *md) { return EVP_MD_type(md); }
+
 uint32_t EVP_MD_flags(const EVP_MD *md) { return md->flags; }
 
 size_t EVP_MD_size(const EVP_MD *md) { return md->md_size; }
@@ -177,6 +179,13 @@
   return 1;
 }
 
+void EVP_MD_CTX_move(EVP_MD_CTX *out, EVP_MD_CTX *in) {
+  EVP_MD_CTX_cleanup(out);
+  // While not guaranteed, |EVP_MD_CTX| is currently safe to move with |memcpy|.
+  OPENSSL_memcpy(out, in, sizeof(EVP_MD_CTX));
+  EVP_MD_CTX_init(in);
+}
+
 int EVP_MD_CTX_copy(EVP_MD_CTX *out, const EVP_MD_CTX *in) {
   EVP_MD_CTX_init(out);
   return EVP_MD_CTX_copy_ex(out, in);
diff --git a/deps/boringssl/src/crypto/fipsmodule/digest/digests.c b/deps/boringssl/src/crypto/fipsmodule/digest/digests.c
index 16daeba..f006ebb 100644
--- a/deps/boringssl/src/crypto/fipsmodule/digest/digests.c
+++ b/deps/boringssl/src/crypto/fipsmodule/digest/digests.c
@@ -247,13 +247,21 @@
   CHECK(SHA512_256_Init(ctx->md_data));
 }
 
+static void sha512_256_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+  CHECK(SHA512_256_Update(ctx->md_data, data, count));
+}
+
+static void sha512_256_final(EVP_MD_CTX *ctx, uint8_t *md) {
+  CHECK(SHA512_256_Final(md, ctx->md_data));
+}
+
 DEFINE_METHOD_FUNCTION(EVP_MD, EVP_sha512_256) {
   out->type = NID_sha512_256;
   out->md_size = SHA512_256_DIGEST_LENGTH;
   out->flags = 0;
   out->init = sha512_256_init;
-  out->update = sha512_update;
-  out->final = sha512_final;
+  out->update = sha512_256_update;
+  out->final = sha512_256_final;
   out->block_size = 128;
   out->ctx_size = sizeof(SHA512_CTX);
 }
diff --git a/deps/boringssl/src/crypto/fipsmodule/digest/md32_common.h b/deps/boringssl/src/crypto/fipsmodule/digest/md32_common.h
index 07d39d9..129ec48 100644
--- a/deps/boringssl/src/crypto/fipsmodule/digest/md32_common.h
+++ b/deps/boringssl/src/crypto/fipsmodule/digest/md32_common.h
@@ -46,6 +46,9 @@
  * OF THE POSSIBILITY OF SUCH DAMAGE.
  * ==================================================================== */
 
+#ifndef OPENSSL_HEADER_DIGEST_MD32_COMMON_H
+#define OPENSSL_HEADER_DIGEST_MD32_COMMON_H
+
 #include <openssl/base.h>
 
 #include <assert.h>
@@ -59,22 +62,15 @@
 
 // This is a generic 32-bit "collector" for message digest algorithms. It
 // collects input character stream into chunks of 32-bit values and invokes the
-// block function that performs the actual hash calculations. To make use of
-// this mechanism, the following macros must be defined before including
-// md32_common.h.
+// block function that performs the actual hash calculations.
 //
-// One of |DATA_ORDER_IS_BIG_ENDIAN| or |DATA_ORDER_IS_LITTLE_ENDIAN| must be
-// defined to specify the byte order of the input stream.
-//
-// |HASH_CBLOCK| must be defined as the integer block size, in bytes.
-//
-// |HASH_CTX| must be defined as the name of the context structure, which must
-// have at least the following members:
+// To make use of this mechanism, the hash context should be defined with the
+// following parameters.
 //
 //     typedef struct <name>_state_st {
 //       uint32_t h[<chaining length> / sizeof(uint32_t)];
 //       uint32_t Nl, Nh;
-//       uint8_t data[HASH_CBLOCK];
+//       uint8_t data[<block size>];
 //       unsigned num;
 //       ...
 //     } <NAME>_CTX;
@@ -83,186 +79,117 @@
 // any truncation (e.g. 64 for SHA-224 and SHA-256, 128 for SHA-384 and
 // SHA-512).
 //
-// |HASH_UPDATE| must be defined as the name of the "Update" function to
-// generate.
-//
-// |HASH_TRANSFORM| must be defined as the  the name of the "Transform"
-// function to generate.
-//
-// |HASH_FINAL| must be defined as the name of "Final" function to generate.
-//
-// |HASH_BLOCK_DATA_ORDER| must be defined as the name of the "Block" function.
-// That function must be implemented manually. It must be capable of operating
-// on *unaligned* input data in its original (data) byte order. It must have
-// this signature:
-//
-//     void HASH_BLOCK_DATA_ORDER(uint32_t *state, const uint8_t *data,
-//                                size_t num);
-//
-// It must update the hash state |state| with |num| blocks of data from |data|,
-// where each block is |HASH_CBLOCK| bytes; i.e. |data| points to a array of
-// |HASH_CBLOCK * num| bytes. |state| points to the |h| member of a |HASH_CTX|,
-// and so will have |<chaining length> / sizeof(uint32_t)| elements.
-//
-// |HASH_MAKE_STRING(c, s)| must be defined as a block statement that converts
-// the hash state |c->h| into the output byte order, storing the result in |s|.
+// |h| is the hash state and is updated by a function of type
+// |crypto_md32_block_func|. |data| is the partial unprocessed block and has
+// |num| bytes. |Nl| and |Nh| maintain the number of bits processed so far.
 
-#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)
-#error "DATA_ORDER must be defined!"
-#endif
+// A crypto_md32_block_func should incorporate |num_blocks| of input from |data|
+// into |state|. It is assumed the caller has sized |state| and |data| for the
+// hash function.
+typedef void (*crypto_md32_block_func)(uint32_t *state, const uint8_t *data,
+                                       size_t num_blocks);
 
-#ifndef HASH_CBLOCK
-#error "HASH_CBLOCK must be defined!"
-#endif
-#ifndef HASH_CTX
-#error "HASH_CTX must be defined!"
-#endif
-
-#ifndef HASH_UPDATE
-#error "HASH_UPDATE must be defined!"
-#endif
-#ifndef HASH_TRANSFORM
-#error "HASH_TRANSFORM must be defined!"
-#endif
-#ifndef HASH_FINAL
-#error "HASH_FINAL must be defined!"
-#endif
-
-#ifndef HASH_BLOCK_DATA_ORDER
-#error "HASH_BLOCK_DATA_ORDER must be defined!"
-#endif
-
-#ifndef HASH_MAKE_STRING
-#error "HASH_MAKE_STRING must be defined!"
-#endif
-
-#if defined(DATA_ORDER_IS_BIG_ENDIAN)
-
-#define HOST_c2l(c, l)                     \
-  do {                                     \
-    (l) = (((uint32_t)(*((c)++))) << 24);  \
-    (l) |= (((uint32_t)(*((c)++))) << 16); \
-    (l) |= (((uint32_t)(*((c)++))) << 8);  \
-    (l) |= (((uint32_t)(*((c)++))));       \
-  } while (0)
-
-#define HOST_l2c(l, c)                        \
-  do {                                        \
-    *((c)++) = (uint8_t)(((l) >> 24) & 0xff); \
-    *((c)++) = (uint8_t)(((l) >> 16) & 0xff); \
-    *((c)++) = (uint8_t)(((l) >> 8) & 0xff);  \
-    *((c)++) = (uint8_t)(((l)) & 0xff);       \
-  } while (0)
-
-#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
-
-#define HOST_c2l(c, l)                     \
-  do {                                     \
-    (l) = (((uint32_t)(*((c)++))));        \
-    (l) |= (((uint32_t)(*((c)++))) << 8);  \
-    (l) |= (((uint32_t)(*((c)++))) << 16); \
-    (l) |= (((uint32_t)(*((c)++))) << 24); \
-  } while (0)
-
-#define HOST_l2c(l, c)                        \
-  do {                                        \
-    *((c)++) = (uint8_t)(((l)) & 0xff);       \
-    *((c)++) = (uint8_t)(((l) >> 8) & 0xff);  \
-    *((c)++) = (uint8_t)(((l) >> 16) & 0xff); \
-    *((c)++) = (uint8_t)(((l) >> 24) & 0xff); \
-  } while (0)
-
-#endif  // DATA_ORDER
-
-int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len) {
-  const uint8_t *data = data_;
-
+// crypto_md32_update adds |len| bytes from |in| to the digest. |data| must be a
+// buffer of length |block_size| with the first |*num| bytes containing a
+// partial block. This function combines the partial block with |in| and
+// incorporates any complete blocks into the digest state |h|. It then updates
+// |data| and |*num| with the new partial block and updates |*Nh| and |*Nl| with
+// the data consumed.
+static inline void crypto_md32_update(crypto_md32_block_func block_func,
+                                      uint32_t *h, uint8_t *data,
+                                      size_t block_size, unsigned *num,
+                                      uint32_t *Nh, uint32_t *Nl,
+                                      const uint8_t *in, size_t len) {
   if (len == 0) {
-    return 1;
+    return;
   }
 
-  uint32_t l = c->Nl + (((uint32_t)len) << 3);
-  if (l < c->Nl) {
+  uint32_t l = *Nl + (((uint32_t)len) << 3);
+  if (l < *Nl) {
     // Handle carries.
-    c->Nh++;
+    (*Nh)++;
   }
-  c->Nh += (uint32_t)(len >> 29);
-  c->Nl = l;
+  *Nh += (uint32_t)(len >> 29);
+  *Nl = l;
 
-  size_t n = c->num;
+  size_t n = *num;
   if (n != 0) {
-    if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) {
-      OPENSSL_memcpy(c->data + n, data, HASH_CBLOCK - n);
-      HASH_BLOCK_DATA_ORDER(c->h, c->data, 1);
-      n = HASH_CBLOCK - n;
-      data += n;
+    if (len >= block_size || len + n >= block_size) {
+      OPENSSL_memcpy(data + n, in, block_size - n);
+      block_func(h, data, 1);
+      n = block_size - n;
+      in += n;
       len -= n;
-      c->num = 0;
-      // Keep |c->data| zeroed when unused.
-      OPENSSL_memset(c->data, 0, HASH_CBLOCK);
+      *num = 0;
+      // Keep |data| zeroed when unused.
+      OPENSSL_memset(data, 0, block_size);
     } else {
-      OPENSSL_memcpy(c->data + n, data, len);
-      c->num += (unsigned)len;
-      return 1;
+      OPENSSL_memcpy(data + n, in, len);
+      *num += (unsigned)len;
+      return;
     }
   }
 
-  n = len / HASH_CBLOCK;
+  n = len / block_size;
   if (n > 0) {
-    HASH_BLOCK_DATA_ORDER(c->h, data, n);
-    n *= HASH_CBLOCK;
-    data += n;
+    block_func(h, in, n);
+    n *= block_size;
+    in += n;
     len -= n;
   }
 
   if (len != 0) {
-    c->num = (unsigned)len;
-    OPENSSL_memcpy(c->data, data, len);
+    *num = (unsigned)len;
+    OPENSSL_memcpy(data, in, len);
   }
-  return 1;
 }
 
-
-void HASH_TRANSFORM(HASH_CTX *c, const uint8_t data[HASH_CBLOCK]) {
-  HASH_BLOCK_DATA_ORDER(c->h, data, 1);
-}
-
-
-int HASH_FINAL(uint8_t out[HASH_DIGEST_LENGTH], HASH_CTX *c) {
-  // |c->data| always has room for at least one byte. A full block would have
+// crypto_md32_final incorporates the partial block and trailing length into the
+// digest state |h|. The trailing length is encoded in little-endian if
+// |is_big_endian| is zero and big-endian otherwise. |data| must be a buffer of
+// length |block_size| with the first |*num| bytes containing a partial block.
+// |Nh| and |Nl| contain the total number of bits processed. On return, this
+// function clears the partial block in |data| and
+// |*num|.
+//
+// This function does not serialize |h| into a final digest. This is the
+// responsibility of the caller.
+static inline void crypto_md32_final(crypto_md32_block_func block_func,
+                                     uint32_t *h, uint8_t *data,
+                                     size_t block_size, unsigned *num,
+                                     uint32_t Nh, uint32_t Nl,
+                                     int is_big_endian) {
+  // |data| always has room for at least one byte. A full block would have
   // been consumed.
-  size_t n = c->num;
-  assert(n < HASH_CBLOCK);
-  c->data[n] = 0x80;
+  size_t n = *num;
+  assert(n < block_size);
+  data[n] = 0x80;
   n++;
 
   // Fill the block with zeros if there isn't room for a 64-bit length.
-  if (n > (HASH_CBLOCK - 8)) {
-    OPENSSL_memset(c->data + n, 0, HASH_CBLOCK - n);
+  if (n > block_size - 8) {
+    OPENSSL_memset(data + n, 0, block_size - n);
     n = 0;
-    HASH_BLOCK_DATA_ORDER(c->h, c->data, 1);
+    block_func(h, data, 1);
   }
-  OPENSSL_memset(c->data + n, 0, HASH_CBLOCK - 8 - n);
+  OPENSSL_memset(data + n, 0, block_size - 8 - n);
 
   // Append a 64-bit length to the block and process it.
-  uint8_t *p = c->data + HASH_CBLOCK - 8;
-#if defined(DATA_ORDER_IS_BIG_ENDIAN)
-  HOST_l2c(c->Nh, p);
-  HOST_l2c(c->Nl, p);
-#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
-  HOST_l2c(c->Nl, p);
-  HOST_l2c(c->Nh, p);
-#endif
-  assert(p == c->data + HASH_CBLOCK);
-  HASH_BLOCK_DATA_ORDER(c->h, c->data, 1);
-  c->num = 0;
-  OPENSSL_memset(c->data, 0, HASH_CBLOCK);
-
-  HASH_MAKE_STRING(c, out);
-  return 1;
+  if (is_big_endian) {
+    CRYPTO_store_u32_be(data + block_size - 8, Nh);
+    CRYPTO_store_u32_be(data + block_size - 4, Nl);
+  } else {
+    CRYPTO_store_u32_le(data + block_size - 8, Nl);
+    CRYPTO_store_u32_le(data + block_size - 4, Nh);
+  }
+  block_func(h, data, 1);
+  *num = 0;
+  OPENSSL_memset(data, 0, block_size);
 }
 
 
 #if defined(__cplusplus)
 }  // extern C
 #endif
+
+#endif  // OPENSSL_HEADER_DIGEST_MD32_COMMON_H
diff --git a/deps/boringssl/src/crypto/fipsmodule/ec/ec.c b/deps/boringssl/src/crypto/fipsmodule/ec/ec.c
index c976341..1f03e15 100644
--- a/deps/boringssl/src/crypto/fipsmodule/ec/ec.c
+++ b/deps/boringssl/src/crypto/fipsmodule/ec/ec.c
@@ -1232,6 +1232,10 @@
 
 void EC_GROUP_set_asn1_flag(EC_GROUP *group, int flag) {}
 
+int EC_GROUP_get_asn1_flag(const EC_GROUP *group) {
+  return OPENSSL_EC_NAMED_CURVE;
+}
+
 const EC_METHOD *EC_GROUP_method_of(const EC_GROUP *group) {
   // This function exists purely to give callers a way to call
   // |EC_METHOD_get_field_type|. cryptography.io crashes if |EC_GROUP_method_of|
diff --git a/deps/boringssl/src/crypto/fipsmodule/ec/ec_key.c b/deps/boringssl/src/crypto/fipsmodule/ec/ec_key.c
index bc09e0e..7a6daab 100644
--- a/deps/boringssl/src/crypto/fipsmodule/ec/ec_key.c
+++ b/deps/boringssl/src/crypto/fipsmodule/ec/ec_key.c
@@ -171,7 +171,6 @@
   EC_GROUP_free(r->group);
   EC_POINT_free(r->pub_key);
   ec_wrapped_scalar_free(r->priv_key);
-  BN_free(r->fixed_k);
 
   CRYPTO_free_ex_data(g_ec_ex_data_class_bss_get(), r, &r->ex_data);
 
diff --git a/deps/boringssl/src/crypto/fipsmodule/ec/internal.h b/deps/boringssl/src/crypto/fipsmodule/ec/internal.h
index 18aabb0..289c3aa 100644
--- a/deps/boringssl/src/crypto/fipsmodule/ec/internal.h
+++ b/deps/boringssl/src/crypto/fipsmodule/ec/internal.h
@@ -729,10 +729,6 @@
   EC_POINT *pub_key;
   EC_WRAPPED_SCALAR *priv_key;
 
-  // fixed_k may contain a specific value of 'k', to be used in ECDSA signing.
-  // This is only for the FIPS power-on tests.
-  BIGNUM *fixed_k;
-
   unsigned int enc_flag;
   point_conversion_form_t conv_form;
 
diff --git a/deps/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa.c b/deps/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa.c
index 096b615..591f1bc 100644
--- a/deps/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa.c
+++ b/deps/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa.c
@@ -61,9 +61,10 @@
 #include <openssl/sha.h>
 #include <openssl/type_check.h>
 
+#include "../../internal.h"
 #include "../bn/internal.h"
 #include "../ec/internal.h"
-#include "../../internal.h"
+#include "internal.h"
 
 
 // digest_to_scalar interprets |digest_len| bytes from |digest| as a scalar for
@@ -198,68 +199,104 @@
   return 1;
 }
 
-static int ecdsa_sign_setup(const EC_KEY *eckey, EC_SCALAR *out_kinv_mont,
-                            EC_SCALAR *out_r, const uint8_t *digest,
-                            size_t digest_len, const EC_SCALAR *priv_key) {
+static ECDSA_SIG *ecdsa_sign_impl(const EC_GROUP *group, int *out_retry,
+                                  const EC_SCALAR *priv_key, const EC_SCALAR *k,
+                                  const uint8_t *digest, size_t digest_len) {
+  *out_retry = 0;
+
   // Check that the size of the group order is FIPS compliant (FIPS 186-4
   // B.5.2).
-  const EC_GROUP *group = EC_KEY_get0_group(eckey);
   const BIGNUM *order = EC_GROUP_get0_order(group);
   if (BN_num_bits(order) < 160) {
     OPENSSL_PUT_ERROR(ECDSA, EC_R_INVALID_GROUP_ORDER);
-    return 0;
+    return NULL;
   }
 
-  int ret = 0;
-  EC_SCALAR k;
+  // Compute r, the x-coordinate of k * generator.
   EC_RAW_POINT tmp_point;
-  do {
-    // Include the private key and message digest in the k generation.
-    if (eckey->fixed_k != NULL) {
-      if (!ec_bignum_to_scalar(group, &k, eckey->fixed_k)) {
-        goto err;
-      }
-      if (ec_scalar_is_zero(group, &k)) {
-        OPENSSL_PUT_ERROR(ECDSA, ERR_R_INTERNAL_ERROR);
-        goto err;
-      }
-    } else {
-      // Pass a SHA512 hash of the private key and digest as additional data
-      // into the RBG. This is a hardening measure against entropy failure.
-      OPENSSL_STATIC_ASSERT(SHA512_DIGEST_LENGTH >= 32,
-                            "additional_data is too large for SHA-512");
-      SHA512_CTX sha;
-      uint8_t additional_data[SHA512_DIGEST_LENGTH];
-      SHA512_Init(&sha);
-      SHA512_Update(&sha, priv_key->words, order->width * sizeof(BN_ULONG));
-      SHA512_Update(&sha, digest, digest_len);
-      SHA512_Final(additional_data, &sha);
-      if (!ec_random_nonzero_scalar(group, &k, additional_data)) {
-        goto err;
-      }
-    }
+  EC_SCALAR r;
+  if (!ec_point_mul_scalar_base(group, &tmp_point, k) ||
+      !ec_get_x_coordinate_as_scalar(group, &r, &tmp_point)) {
+    return NULL;
+  }
 
-    // Compute k^-1 in the Montgomery domain. This is |ec_scalar_to_montgomery|
-    // followed by |ec_scalar_inv0_montgomery|, but |ec_scalar_inv0_montgomery|
-    // followed by |ec_scalar_from_montgomery| is equivalent and slightly more
-    // efficient. Note k is non-zero, so the inverse must exist.
-    ec_scalar_inv0_montgomery(group, out_kinv_mont, &k);
-    ec_scalar_from_montgomery(group, out_kinv_mont, out_kinv_mont);
+  if (ec_scalar_is_zero(group, &r)) {
+    *out_retry = 1;
+    return NULL;
+  }
 
-    // Compute r, the x-coordinate of generator * k.
-    if (!ec_point_mul_scalar_base(group, &tmp_point, &k) ||
-        !ec_get_x_coordinate_as_scalar(group, out_r, &tmp_point)) {
-      goto err;
-    }
-  } while (ec_scalar_is_zero(group, out_r));
+  // s = priv_key * r. Note if only one parameter is in the Montgomery domain,
+  // |ec_scalar_mod_mul_montgomery| will compute the answer in the normal
+  // domain.
+  EC_SCALAR s;
+  ec_scalar_to_montgomery(group, &s, &r);
+  ec_scalar_mul_montgomery(group, &s, priv_key, &s);
 
-  ret = 1;
+  // s = m + priv_key * r.
+  EC_SCALAR tmp;
+  digest_to_scalar(group, &tmp, digest, digest_len);
+  ec_scalar_add(group, &s, &s, &tmp);
 
-err:
-  OPENSSL_cleanse(&k, sizeof(k));
+  // s = k^-1 * (m + priv_key * r). First, we compute k^-1 in the Montgomery
+  // domain. This is |ec_scalar_to_montgomery| followed by
+  // |ec_scalar_inv0_montgomery|, but |ec_scalar_inv0_montgomery| followed by
+  // |ec_scalar_from_montgomery| is equivalent and slightly more efficient.
+  // Then, as above, only one parameter is in the Montgomery domain, so the
+  // result is in the normal domain. Finally, note k is non-zero (or computing r
+  // would fail), so the inverse must exist.
+  ec_scalar_inv0_montgomery(group, &tmp, k);     // tmp = k^-1 R^2
+  ec_scalar_from_montgomery(group, &tmp, &tmp);  // tmp = k^-1 R
+  ec_scalar_mul_montgomery(group, &s, &s, &tmp);
+  if (ec_scalar_is_zero(group, &s)) {
+    *out_retry = 1;
+    return NULL;
+  }
+
+  ECDSA_SIG *ret = ECDSA_SIG_new();
+  if (ret == NULL ||  //
+      !bn_set_words(ret->r, r.words, order->width) ||
+      !bn_set_words(ret->s, s.words, order->width)) {
+    ECDSA_SIG_free(ret);
+    return NULL;
+  }
   return ret;
 }
 
+ECDSA_SIG *ecdsa_sign_with_nonce_for_known_answer_test(const uint8_t *digest,
+                                                       size_t digest_len,
+                                                       const EC_KEY *eckey,
+                                                       const uint8_t *nonce,
+                                                       size_t nonce_len) {
+  if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
+    OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED);
+    return NULL;
+  }
+
+  const EC_GROUP *group = EC_KEY_get0_group(eckey);
+  if (group == NULL || eckey->priv_key == NULL) {
+    OPENSSL_PUT_ERROR(ECDSA, ERR_R_PASSED_NULL_PARAMETER);
+    return NULL;
+  }
+  const EC_SCALAR *priv_key = &eckey->priv_key->scalar;
+
+  EC_SCALAR k;
+  if (!ec_scalar_from_bytes(group, &k, nonce, nonce_len)) {
+    return NULL;
+  }
+  int retry_ignored;
+  return ecdsa_sign_impl(group, &retry_ignored, priv_key, &k, digest,
+                         digest_len);
+}
+
+// This function is only exported for testing and is not called in production
+// code.
+ECDSA_SIG *ECDSA_sign_with_nonce_and_leak_private_key_for_testing(
+    const uint8_t *digest, size_t digest_len, const EC_KEY *eckey,
+    const uint8_t *nonce, size_t nonce_len) {
+  return ecdsa_sign_with_nonce_for_known_answer_test(digest, digest_len, eckey,
+                                                     nonce, nonce_len);
+}
+
 ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len,
                          const EC_KEY *eckey) {
   if (eckey->ecdsa_meth && eckey->ecdsa_meth->sign) {
@@ -275,54 +312,28 @@
   const BIGNUM *order = EC_GROUP_get0_order(group);
   const EC_SCALAR *priv_key = &eckey->priv_key->scalar;
 
-  int ok = 0;
-  ECDSA_SIG *ret = ECDSA_SIG_new();
-  EC_SCALAR kinv_mont, r_mont, s, m, tmp;
-  if (ret == NULL) {
-    OPENSSL_PUT_ERROR(ECDSA, ERR_R_MALLOC_FAILURE);
-    return NULL;
-  }
+  // Pass a SHA512 hash of the private key and digest as additional data
+  // into the RBG. This is a hardening measure against entropy failure.
+  OPENSSL_STATIC_ASSERT(SHA512_DIGEST_LENGTH >= 32,
+                        "additional_data is too large for SHA-512");
+  SHA512_CTX sha;
+  uint8_t additional_data[SHA512_DIGEST_LENGTH];
+  SHA512_Init(&sha);
+  SHA512_Update(&sha, priv_key->words, order->width * sizeof(BN_ULONG));
+  SHA512_Update(&sha, digest, digest_len);
+  SHA512_Final(additional_data, &sha);
 
-  digest_to_scalar(group, &m, digest, digest_len);
   for (;;) {
-    if (!ecdsa_sign_setup(eckey, &kinv_mont, &r_mont, digest, digest_len,
-                          priv_key) ||
-        !bn_set_words(ret->r, r_mont.words, order->width)) {
-      goto err;
+    EC_SCALAR k;
+    if (!ec_random_nonzero_scalar(group, &k, additional_data)) {
+      return NULL;
     }
 
-    // Compute priv_key * r (mod order). Note if only one parameter is in the
-    // Montgomery domain, |ec_scalar_mod_mul_montgomery| will compute the answer
-    // in the normal domain.
-    ec_scalar_to_montgomery(group, &r_mont, &r_mont);
-    ec_scalar_mul_montgomery(group, &s, priv_key, &r_mont);
-
-    // Compute tmp = m + priv_key * r.
-    ec_scalar_add(group, &tmp, &m, &s);
-
-    // Finally, multiply s by k^-1. That was retained in Montgomery form, so the
-    // same technique as the previous multiplication works.
-    ec_scalar_mul_montgomery(group, &s, &tmp, &kinv_mont);
-    if (!bn_set_words(ret->s, s.words, order->width)) {
-      goto err;
-    }
-    if (!BN_is_zero(ret->s)) {
-      // s != 0 => we have a valid signature
-      break;
+    int retry;
+    ECDSA_SIG *sig =
+        ecdsa_sign_impl(group, &retry, priv_key, &k, digest, digest_len);
+    if (sig != NULL || !retry) {
+      return sig;
     }
   }
-
-  ok = 1;
-
-err:
-  if (!ok) {
-    ECDSA_SIG_free(ret);
-    ret = NULL;
-  }
-  OPENSSL_cleanse(&kinv_mont, sizeof(kinv_mont));
-  OPENSSL_cleanse(&r_mont, sizeof(r_mont));
-  OPENSSL_cleanse(&s, sizeof(s));
-  OPENSSL_cleanse(&tmp, sizeof(tmp));
-  OPENSSL_cleanse(&m, sizeof(m));
-  return ret;
 }
diff --git a/deps/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc b/deps/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc
index 4c95df9..18fdb83 100644
--- a/deps/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc
+++ b/deps/boringssl/src/crypto/fipsmodule/ecdsa/ecdsa_test.cc
@@ -66,6 +66,7 @@
 
 #include "../ec/internal.h"
 #include "../../test/file_test.h"
+#include "../../test/test_util.h"
 
 
 static bssl::UniquePtr<BIGNUM> HexToBIGNUM(const char *hex) {
@@ -228,6 +229,15 @@
         ECDSA_sign(0, digest, 20, signature.data(), &sig_len, eckey.get()));
     signature.resize(sig_len);
 
+    // ECDSA signing should be non-deterministic. This does not verify k is
+    // generated securely but at least checks it was randomized at all.
+    sig_len = ECDSA_size(eckey.get());
+    std::vector<uint8_t> signature2(sig_len);
+    ASSERT_TRUE(
+        ECDSA_sign(0, digest, 20, signature2.data(), &sig_len, eckey.get()));
+    signature2.resize(sig_len);
+    EXPECT_NE(Bytes(signature), Bytes(signature2));
+
     // Verify the signature.
     EXPECT_TRUE(ECDSA_verify(0, digest, 20, signature.data(), signature.size(),
                              eckey.get()));
@@ -424,8 +434,8 @@
       ASSERT_TRUE(x);
       bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y");
       ASSERT_TRUE(y);
-      bssl::UniquePtr<BIGNUM> k = GetBIGNUM(t, "K");
-      ASSERT_TRUE(k);
+      std::vector<uint8_t> k;
+      ASSERT_TRUE(t->GetBytes(&k, "K"));
       bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R");
       ASSERT_TRUE(r);
       bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S");
@@ -444,10 +454,9 @@
       ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get()));
       ASSERT_TRUE(EC_KEY_check_key(key.get()));
 
-      // Set the fixed k for testing purposes.
-      key->fixed_k = k.release();
       bssl::UniquePtr<ECDSA_SIG> sig(
-          ECDSA_do_sign(digest.data(), digest.size(), key.get()));
+          ECDSA_sign_with_nonce_and_leak_private_key_for_testing(
+              digest.data(), digest.size(), key.get(), k.data(), k.size()));
       ASSERT_TRUE(sig);
 
       EXPECT_EQ(0, BN_cmp(r.get(), sig->r));
diff --git a/deps/boringssl/src/crypto/fipsmodule/ecdsa/internal.h b/deps/boringssl/src/crypto/fipsmodule/ecdsa/internal.h
new file mode 100644
index 0000000..5115dfa
--- /dev/null
+++ b/deps/boringssl/src/crypto/fipsmodule/ecdsa/internal.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2021, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_CRYPTO_FIPSMODULE_ECDSA_INTERNAL_H
+#define OPENSSL_HEADER_CRYPTO_FIPSMODULE_ECDSA_INTERNAL_H
+
+#include <openssl/base.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// ecdsa_sign_with_nonce_for_known_answer_test behaves like |ECDSA_do_sign| but
+// takes a fixed nonce. This function is used as part of known-answer tests in
+// the FIPS module.
+ECDSA_SIG *ecdsa_sign_with_nonce_for_known_answer_test(const uint8_t *digest,
+                                                       size_t digest_len,
+                                                       const EC_KEY *eckey,
+                                                       const uint8_t *nonce,
+                                                       size_t nonce_len);
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif  // OPENSSL_HEADER_CRYPTO_FIPSMODULE_ECDSA_INTERNAL_H
diff --git a/deps/boringssl/src/crypto/fipsmodule/is_fips.c b/deps/boringssl/src/crypto/fipsmodule/is_fips.c
deleted file mode 100644
index 2f8e408..0000000
--- a/deps/boringssl/src/crypto/fipsmodule/is_fips.c
+++ /dev/null
@@ -1,29 +0,0 @@
-/* Copyright (c) 2017, Google Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
-
-#include <openssl/crypto.h>
-
-
-// This file exists in order to give the fipsmodule target, in non-FIPS mode,
-// something to compile.
-
-int FIPS_mode(void) {
-#if defined(BORINGSSL_FIPS) && !defined(OPENSSL_ASAN)
-  return 1;
-#else
-  return 0;
-#endif
-}
-
-int FIPS_mode_set(int on) { return on == FIPS_mode(); }
diff --git a/deps/boringssl/src/crypto/fipsmodule/md4/md4.c b/deps/boringssl/src/crypto/fipsmodule/md4/md4.c
index cc2a631..a505d05 100644
--- a/deps/boringssl/src/crypto/fipsmodule/md4/md4.c
+++ b/deps/boringssl/src/crypto/fipsmodule/md4/md4.c
@@ -60,6 +60,7 @@
 #include <string.h>
 
 #include "../../internal.h"
+#include "../digest/md32_common.h"
 
 
 uint8_t *MD4(const uint8_t *data, size_t len, uint8_t out[MD4_DIGEST_LENGTH]) {
@@ -71,7 +72,7 @@
   return out;
 }
 
-// Implemented from RFC1186 The MD4 Message-Digest Algorithm.
+// Implemented from RFC 1186 The MD4 Message-Digest Algorithm.
 
 int MD4_Init(MD4_CTX *md4) {
   OPENSSL_memset(md4, 0, sizeof(MD4_CTX));
@@ -84,29 +85,26 @@
 
 void md4_block_data_order(uint32_t *state, const uint8_t *data, size_t num);
 
-#define DATA_ORDER_IS_LITTLE_ENDIAN
+void MD4_Transform(MD4_CTX *c, const uint8_t data[MD4_CBLOCK]) {
+  md4_block_data_order(c->h, data, 1);
+}
 
-#define HASH_CTX MD4_CTX
-#define HASH_CBLOCK 64
-#define HASH_DIGEST_LENGTH 16
-#define HASH_UPDATE MD4_Update
-#define HASH_TRANSFORM MD4_Transform
-#define HASH_FINAL MD4_Final
-#define HASH_MAKE_STRING(c, s) \
-  do {                         \
-    uint32_t ll;               \
-    ll = (c)->h[0];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[1];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[2];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[3];            \
-    HOST_l2c(ll, (s));         \
-  } while (0)
-#define HASH_BLOCK_DATA_ORDER md4_block_data_order
+int MD4_Update(MD4_CTX *c, const void *data, size_t len) {
+  crypto_md32_update(&md4_block_data_order, c->h, c->data, MD4_CBLOCK, &c->num,
+                     &c->Nh, &c->Nl, data, len);
+  return 1;
+}
 
-#include "../digest/md32_common.h"
+int MD4_Final(uint8_t out[MD4_DIGEST_LENGTH], MD4_CTX *c) {
+  crypto_md32_final(&md4_block_data_order, c->h, c->data, MD4_CBLOCK, &c->num,
+                    c->Nh, c->Nl, /*is_big_endian=*/0);
+
+  CRYPTO_store_u32_le(out, c->h[0]);
+  CRYPTO_store_u32_le(out + 4, c->h[1]);
+  CRYPTO_store_u32_le(out + 8, c->h[2]);
+  CRYPTO_store_u32_le(out + 12, c->h[3]);
+  return 1;
+}
 
 // As pointed out by Wei Dai <weidai@eskimo.com>, the above can be
 // simplified to the code below.  Wei attributes these optimizations
@@ -136,7 +134,7 @@
   } while (0)
 
 void md4_block_data_order(uint32_t *state, const uint8_t *data, size_t num) {
-  uint32_t A, B, C, D, l;
+  uint32_t A, B, C, D;
   uint32_t X0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15;
 
   A = state[0];
@@ -145,53 +143,53 @@
   D = state[3];
 
   for (; num--;) {
-    HOST_c2l(data, l);
-    X0 = l;
-    HOST_c2l(data, l);
-    X1 = l;
+    X0 = CRYPTO_load_u32_le(data);
+    data += 4;
+    X1 = CRYPTO_load_u32_le(data);
+    data += 4;
     // Round 0
     R0(A, B, C, D, X0, 3, 0);
-    HOST_c2l(data, l);
-    X2 = l;
+    X2 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(D, A, B, C, X1, 7, 0);
-    HOST_c2l(data, l);
-    X3 = l;
+    X3 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(C, D, A, B, X2, 11, 0);
-    HOST_c2l(data, l);
-    X4 = l;
+    X4 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(B, C, D, A, X3, 19, 0);
-    HOST_c2l(data, l);
-    X5 = l;
+    X5 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(A, B, C, D, X4, 3, 0);
-    HOST_c2l(data, l);
-    X6 = l;
+    X6 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(D, A, B, C, X5, 7, 0);
-    HOST_c2l(data, l);
-    X7 = l;
+    X7 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(C, D, A, B, X6, 11, 0);
-    HOST_c2l(data, l);
-    X8 = l;
+    X8 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(B, C, D, A, X7, 19, 0);
-    HOST_c2l(data, l);
-    X9 = l;
+    X9 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(A, B, C, D, X8, 3, 0);
-    HOST_c2l(data, l);
-    X10 = l;
+    X10 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(D, A, B, C, X9, 7, 0);
-    HOST_c2l(data, l);
-    X11 = l;
+    X11 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(C, D, A, B, X10, 11, 0);
-    HOST_c2l(data, l);
-    X12 = l;
+    X12 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(B, C, D, A, X11, 19, 0);
-    HOST_c2l(data, l);
-    X13 = l;
+    X13 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(A, B, C, D, X12, 3, 0);
-    HOST_c2l(data, l);
-    X14 = l;
+    X14 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(D, A, B, C, X13, 7, 0);
-    HOST_c2l(data, l);
-    X15 = l;
+    X15 = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(C, D, A, B, X14, 11, 0);
     R0(B, C, D, A, X15, 19, 0);
     // Round 1
@@ -236,15 +234,6 @@
   }
 }
 
-#undef DATA_ORDER_IS_LITTLE_ENDIAN
-#undef HASH_CTX
-#undef HASH_CBLOCK
-#undef HASH_DIGEST_LENGTH
-#undef HASH_UPDATE
-#undef HASH_TRANSFORM
-#undef HASH_FINAL
-#undef HASH_MAKE_STRING
-#undef HASH_BLOCK_DATA_ORDER
 #undef F
 #undef G
 #undef H
@@ -252,5 +241,3 @@
 #undef R0
 #undef R1
 #undef R2
-#undef HOST_c2l
-#undef HOST_l2c
diff --git a/deps/boringssl/src/crypto/fipsmodule/md5/md5.c b/deps/boringssl/src/crypto/fipsmodule/md5/md5.c
index a48d704..eba34bc 100644
--- a/deps/boringssl/src/crypto/fipsmodule/md5/md5.c
+++ b/deps/boringssl/src/crypto/fipsmodule/md5/md5.c
@@ -60,8 +60,9 @@
 
 #include <openssl/mem.h>
 
-#include "internal.h"
 #include "../../internal.h"
+#include "../digest/md32_common.h"
+#include "internal.h"
 
 
 uint8_t *MD5(const uint8_t *data, size_t len, uint8_t out[MD5_DIGEST_LENGTH]) {
@@ -89,30 +90,26 @@
                                  size_t num);
 #endif
 
+void MD5_Transform(MD5_CTX *c, const uint8_t data[MD5_CBLOCK]) {
+  md5_block_data_order(c->h, data, 1);
+}
 
-#define DATA_ORDER_IS_LITTLE_ENDIAN
+int MD5_Update(MD5_CTX *c, const void *data, size_t len) {
+  crypto_md32_update(&md5_block_data_order, c->h, c->data, MD5_CBLOCK, &c->num,
+                     &c->Nh, &c->Nl, data, len);
+  return 1;
+}
 
-#define HASH_CTX MD5_CTX
-#define HASH_CBLOCK 64
-#define HASH_DIGEST_LENGTH 16
-#define HASH_UPDATE MD5_Update
-#define HASH_TRANSFORM MD5_Transform
-#define HASH_FINAL MD5_Final
-#define HASH_MAKE_STRING(c, s) \
-  do {                         \
-    uint32_t ll;               \
-    ll = (c)->h[0];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[1];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[2];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[3];            \
-    HOST_l2c(ll, (s));         \
-  } while (0)
-#define HASH_BLOCK_DATA_ORDER md5_block_data_order
+int MD5_Final(uint8_t out[MD5_DIGEST_LENGTH], MD5_CTX *c) {
+  crypto_md32_final(&md5_block_data_order, c->h, c->data, MD5_CBLOCK, &c->num,
+                    c->Nh, c->Nl, /*is_big_endian=*/0);
 
-#include "../digest/md32_common.h"
+  CRYPTO_store_u32_le(out, c->h[0]);
+  CRYPTO_store_u32_le(out + 4, c->h[1]);
+  CRYPTO_store_u32_le(out + 8, c->h[2]);
+  CRYPTO_store_u32_le(out + 12, c->h[3]);
+  return 1;
+}
 
 // As pointed out by Wei Dai <weidai@eskimo.com>, the above can be
 // simplified to the code below.  Wei attributes these optimizations
@@ -158,7 +155,7 @@
 #endif
 static void md5_block_data_order(uint32_t *state, const uint8_t *data,
                                  size_t num) {
-  uint32_t A, B, C, D, l;
+  uint32_t A, B, C, D;
   uint32_t XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12,
       XX13, XX14, XX15;
 #define X(i) XX##i
@@ -169,53 +166,53 @@
   D = state[3];
 
   for (; num--;) {
-    HOST_c2l(data, l);
-    X(0) = l;
-    HOST_c2l(data, l);
-    X(1) = l;
+    X(0) = CRYPTO_load_u32_le(data);
+    data += 4;
+    X(1) = CRYPTO_load_u32_le(data);
+    data += 4;
     // Round 0
     R0(A, B, C, D, X(0), 7, 0xd76aa478L);
-    HOST_c2l(data, l);
-    X(2) = l;
+    X(2) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(D, A, B, C, X(1), 12, 0xe8c7b756L);
-    HOST_c2l(data, l);
-    X(3) = l;
+    X(3) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(C, D, A, B, X(2), 17, 0x242070dbL);
-    HOST_c2l(data, l);
-    X(4) = l;
+    X(4) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(B, C, D, A, X(3), 22, 0xc1bdceeeL);
-    HOST_c2l(data, l);
-    X(5) = l;
+    X(5) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(A, B, C, D, X(4), 7, 0xf57c0fafL);
-    HOST_c2l(data, l);
-    X(6) = l;
+    X(6) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(D, A, B, C, X(5), 12, 0x4787c62aL);
-    HOST_c2l(data, l);
-    X(7) = l;
+    X(7) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(C, D, A, B, X(6), 17, 0xa8304613L);
-    HOST_c2l(data, l);
-    X(8) = l;
+    X(8) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(B, C, D, A, X(7), 22, 0xfd469501L);
-    HOST_c2l(data, l);
-    X(9) = l;
+    X(9) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(A, B, C, D, X(8), 7, 0x698098d8L);
-    HOST_c2l(data, l);
-    X(10) = l;
+    X(10) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(D, A, B, C, X(9), 12, 0x8b44f7afL);
-    HOST_c2l(data, l);
-    X(11) = l;
+    X(11) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(C, D, A, B, X(10), 17, 0xffff5bb1L);
-    HOST_c2l(data, l);
-    X(12) = l;
+    X(12) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(B, C, D, A, X(11), 22, 0x895cd7beL);
-    HOST_c2l(data, l);
-    X(13) = l;
+    X(13) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(A, B, C, D, X(12), 7, 0x6b901122L);
-    HOST_c2l(data, l);
-    X(14) = l;
+    X(14) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(D, A, B, C, X(13), 12, 0xfd987193L);
-    HOST_c2l(data, l);
-    X(15) = l;
+    X(15) = CRYPTO_load_u32_le(data);
+    data += 4;
     R0(C, D, A, B, X(14), 17, 0xa679438eL);
     R0(B, C, D, A, X(15), 22, 0x49b40821L);
     // Round 1
@@ -279,15 +276,6 @@
 #undef X
 #endif
 
-#undef DATA_ORDER_IS_LITTLE_ENDIAN
-#undef HASH_CTX
-#undef HASH_CBLOCK
-#undef HASH_DIGEST_LENGTH
-#undef HASH_UPDATE
-#undef HASH_TRANSFORM
-#undef HASH_FINAL
-#undef HASH_MAKE_STRING
-#undef HASH_BLOCK_DATA_ORDER
 #undef F
 #undef G
 #undef H
@@ -297,5 +285,3 @@
 #undef R1
 #undef R2
 #undef R3
-#undef HOST_c2l
-#undef HOST_l2c
diff --git a/deps/boringssl/src/crypto/fipsmodule/modes/cbc.c b/deps/boringssl/src/crypto/fipsmodule/modes/cbc.c
index 3f1d777..192580e 100644
--- a/deps/boringssl/src/crypto/fipsmodule/modes/cbc.c
+++ b/deps/boringssl/src/crypto/fipsmodule/modes/cbc.c
@@ -52,20 +52,25 @@
 #include <openssl/type_check.h>
 
 #include "internal.h"
+#include "../../internal.h"
 
 
 void CRYPTO_cbc128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
                            const AES_KEY *key, uint8_t ivec[16],
                            block128_f block) {
+  assert(key != NULL && ivec != NULL);
+  if (len == 0) {
+    // Avoid |ivec| == |iv| in the |memcpy| below, which is not legal in C.
+    return;
+  }
+
+  assert(in != NULL && out != NULL);
   size_t n;
   const uint8_t *iv = ivec;
-
-  assert(key != NULL && ivec != NULL);
-  assert(len == 0 || (in != NULL && out != NULL));
-
   while (len >= 16) {
-    for (n = 0; n < 16; n += sizeof(size_t)) {
-      store_word_le(out + n, load_word_le(in + n) ^ load_word_le(iv + n));
+    for (n = 0; n < 16; n += sizeof(crypto_word_t)) {
+      CRYPTO_store_word_le(
+          out + n, CRYPTO_load_word_le(in + n) ^ CRYPTO_load_word_le(iv + n));
     }
     (*block)(out, out, key);
     iv = out;
@@ -97,30 +102,36 @@
 void CRYPTO_cbc128_decrypt(const uint8_t *in, uint8_t *out, size_t len,
                            const AES_KEY *key, uint8_t ivec[16],
                            block128_f block) {
-  size_t n;
-  union {
-    size_t t[16 / sizeof(size_t)];
-    uint8_t c[16];
-  } tmp;
-
   assert(key != NULL && ivec != NULL);
-  assert(len == 0 || (in != NULL && out != NULL));
+  if (len == 0) {
+    // Avoid |ivec| == |iv| in the |memcpy| below, which is not legal in C.
+    return;
+  }
+
+  assert(in != NULL && out != NULL);
 
   const uintptr_t inptr = (uintptr_t) in;
   const uintptr_t outptr = (uintptr_t) out;
   // If |in| and |out| alias, |in| must be ahead.
   assert(inptr >= outptr || inptr + len <= outptr);
 
+  size_t n;
+  union {
+    crypto_word_t t[16 / sizeof(crypto_word_t)];
+    uint8_t c[16];
+  } tmp;
+
   if ((inptr >= 32 && outptr <= inptr - 32) || inptr < outptr) {
     // If |out| is at least two blocks behind |in| or completely disjoint, there
     // is no need to decrypt to a temporary block.
-    OPENSSL_STATIC_ASSERT(16 % sizeof(size_t) == 0,
+    OPENSSL_STATIC_ASSERT(16 % sizeof(crypto_word_t) == 0,
                           "block cannot be evenly divided into words");
     const uint8_t *iv = ivec;
     while (len >= 16) {
       (*block)(in, out, key);
-      for (n = 0; n < 16; n += sizeof(size_t)) {
-        store_word_le(out + n, load_word_le(out + n) ^ load_word_le(iv + n));
+      for (n = 0; n < 16; n += sizeof(crypto_word_t)) {
+        CRYPTO_store_word_le(out + n, CRYPTO_load_word_le(out + n) ^
+                                          CRYPTO_load_word_le(iv + n));
       }
       iv = in;
       len -= 16;
@@ -129,16 +140,16 @@
     }
     OPENSSL_memcpy(ivec, iv, 16);
   } else {
-    OPENSSL_STATIC_ASSERT(16 % sizeof(size_t) == 0,
+    OPENSSL_STATIC_ASSERT(16 % sizeof(crypto_word_t) == 0,
                           "block cannot be evenly divided into words");
 
     while (len >= 16) {
       (*block)(in, tmp.c, key);
-      for (n = 0; n < 16; n += sizeof(size_t)) {
-        size_t c = load_word_le(in + n);
-        store_word_le(out + n,
-                      tmp.t[n / sizeof(size_t)] ^ load_word_le(ivec + n));
-        store_word_le(ivec + n, c);
+      for (n = 0; n < 16; n += sizeof(crypto_word_t)) {
+        crypto_word_t c = CRYPTO_load_word_le(in + n);
+        CRYPTO_store_word_le(out + n, tmp.t[n / sizeof(crypto_word_t)] ^
+                                          CRYPTO_load_word_le(ivec + n));
+        CRYPTO_store_word_le(ivec + n, c);
       }
       len -= 16;
       in += 16;
diff --git a/deps/boringssl/src/crypto/fipsmodule/modes/cfb.c b/deps/boringssl/src/crypto/fipsmodule/modes/cfb.c
index 8ca9004..283a107 100644
--- a/deps/boringssl/src/crypto/fipsmodule/modes/cfb.c
+++ b/deps/boringssl/src/crypto/fipsmodule/modes/cfb.c
@@ -72,10 +72,11 @@
     }
     while (len >= 16) {
       (*block)(ivec, ivec, key);
-      for (; n < 16; n += sizeof(size_t)) {
-        size_t tmp = load_word_le(ivec + n) ^ load_word_le(in + n);
-        store_word_le(ivec + n, tmp);
-        store_word_le(out + n, tmp);
+      for (; n < 16; n += sizeof(crypto_word_t)) {
+        crypto_word_t tmp =
+            CRYPTO_load_word_le(ivec + n) ^ CRYPTO_load_word_le(in + n);
+        CRYPTO_store_word_le(ivec + n, tmp);
+        CRYPTO_store_word_le(out + n, tmp);
       }
       len -= 16;
       out += 16;
@@ -101,10 +102,10 @@
     }
     while (len >= 16) {
       (*block)(ivec, ivec, key);
-      for (; n < 16; n += sizeof(size_t)) {
-        size_t t = load_word_le(in + n);
-        store_word_le(out + n, load_word_le(ivec + n) ^ t);
-        store_word_le(ivec + n, t);
+      for (; n < 16; n += sizeof(crypto_word_t)) {
+        crypto_word_t t = CRYPTO_load_word_le(in + n);
+        CRYPTO_store_word_le(out + n, CRYPTO_load_word_le(ivec + n) ^ t);
+        CRYPTO_store_word_le(ivec + n, t);
       }
       len -= 16;
       out += 16;
diff --git a/deps/boringssl/src/crypto/fipsmodule/modes/ctr.c b/deps/boringssl/src/crypto/fipsmodule/modes/ctr.c
index 8b0e059..cea79ad 100644
--- a/deps/boringssl/src/crypto/fipsmodule/modes/ctr.c
+++ b/deps/boringssl/src/crypto/fipsmodule/modes/ctr.c
@@ -52,6 +52,7 @@
 #include <string.h>
 
 #include "internal.h"
+#include "../../internal.h"
 
 
 // NOTE: the IV/counter CTR mode is big-endian.  The code itself
@@ -69,8 +70,8 @@
   } while (n);
 }
 
-OPENSSL_STATIC_ASSERT(16 % sizeof(size_t) == 0,
-                      "block cannot be divided into size_t");
+OPENSSL_STATIC_ASSERT(16 % sizeof(crypto_word_t) == 0,
+                      "block cannot be divided into crypto_word_t");
 
 // The input encrypted as though 128bit counter mode is being used.  The extra
 // state information to record how much of the 128bit block we have used is
@@ -102,9 +103,9 @@
   while (len >= 16) {
     (*block)(ivec, ecount_buf, key);
     ctr128_inc(ivec);
-    for (n = 0; n < 16; n += sizeof(size_t)) {
-      store_word_le(out + n,
-                    load_word_le(in + n) ^ load_word_le(ecount_buf + n));
+    for (n = 0; n < 16; n += sizeof(crypto_word_t)) {
+      CRYPTO_store_word_le(out + n, CRYPTO_load_word_le(in + n) ^
+                                        CRYPTO_load_word_le(ecount_buf + n));
     }
     len -= 16;
     out += 16;
@@ -152,7 +153,7 @@
     n = (n + 1) % 16;
   }
 
-  ctr32 = GETU32(ivec + 12);
+  ctr32 = CRYPTO_load_u32_be(ivec + 12);
   while (len >= 16) {
     size_t blocks = len / 16;
     // 1<<28 is just a not-so-small yet not-so-large number...
@@ -172,7 +173,7 @@
     }
     (*func)(in, out, blocks, key, ivec);
     // (*func) does not update ivec, caller does:
-    PUTU32(ivec + 12, ctr32);
+    CRYPTO_store_u32_be(ivec + 12, ctr32);
     // ... overflow was detected, propogate carry.
     if (ctr32 == 0) {
       ctr96_inc(ivec);
@@ -186,7 +187,7 @@
     OPENSSL_memset(ecount_buf, 0, 16);
     (*func)(ecount_buf, ecount_buf, 1, key, ivec);
     ++ctr32;
-    PUTU32(ivec + 12, ctr32);
+    CRYPTO_store_u32_be(ivec + 12, ctr32);
     if (ctr32 == 0) {
       ctr96_inc(ivec);
     }
diff --git a/deps/boringssl/src/crypto/fipsmodule/modes/gcm.c b/deps/boringssl/src/crypto/fipsmodule/modes/gcm.c
index 14fff86..b010cd5 100644
--- a/deps/boringssl/src/crypto/fipsmodule/modes/gcm.c
+++ b/deps/boringssl/src/crypto/fipsmodule/modes/gcm.c
@@ -73,7 +73,7 @@
 
 #if defined(GHASH_ASM_X86_64) || defined(GHASH_ASM_X86)
 static inline void gcm_reduce_1bit(u128 *V) {
-  if (sizeof(size_t) == 8) {
+  if (sizeof(crypto_word_t) == 8) {
     uint64_t T = UINT64_C(0xe100000000000000) & (0 - (V->hi & 1));
     V->hi = (V->lo << 63) | (V->hi >> 1);
     V->lo = (V->lo >> 1) ^ T;
@@ -377,9 +377,10 @@
       (*block)(ctx->Yi.c, ctx->EKi.c, key);
       ++ctr;
       ctx->Yi.d[3] = CRYPTO_bswap4(ctr);
-      for (size_t i = 0; i < 16; i += sizeof(size_t)) {
-        store_word_le(out + i,
-                      load_word_le(in + i) ^ ctx->EKi.t[i / sizeof(size_t)]);
+      for (size_t i = 0; i < 16; i += sizeof(crypto_word_t)) {
+        CRYPTO_store_word_le(out + i,
+                             CRYPTO_load_word_le(in + i) ^
+                                 ctx->EKi.t[i / sizeof(crypto_word_t)]);
       }
       out += 16;
       in += 16;
@@ -394,9 +395,10 @@
       (*block)(ctx->Yi.c, ctx->EKi.c, key);
       ++ctr;
       ctx->Yi.d[3] = CRYPTO_bswap4(ctr);
-      for (size_t i = 0; i < 16; i += sizeof(size_t)) {
-        store_word_le(out + i,
-                      load_word_le(in + i) ^ ctx->EKi.t[i / sizeof(size_t)]);
+      for (size_t i = 0; i < 16; i += sizeof(crypto_word_t)) {
+        CRYPTO_store_word_le(out + i,
+                             CRYPTO_load_word_le(in + i) ^
+                                 ctx->EKi.t[i / sizeof(crypto_word_t)]);
       }
       out += 16;
       in += 16;
@@ -468,9 +470,10 @@
       (*block)(ctx->Yi.c, ctx->EKi.c, key);
       ++ctr;
       ctx->Yi.d[3] = CRYPTO_bswap4(ctr);
-      for (size_t i = 0; i < 16; i += sizeof(size_t)) {
-        store_word_le(out + i,
-                      load_word_le(in + i) ^ ctx->EKi.t[i / sizeof(size_t)]);
+      for (size_t i = 0; i < 16; i += sizeof(crypto_word_t)) {
+        CRYPTO_store_word_le(out + i,
+                             CRYPTO_load_word_le(in + i) ^
+                                 ctx->EKi.t[i / sizeof(crypto_word_t)]);
       }
       out += 16;
       in += 16;
@@ -485,9 +488,10 @@
       (*block)(ctx->Yi.c, ctx->EKi.c, key);
       ++ctr;
       ctx->Yi.d[3] = CRYPTO_bswap4(ctr);
-      for (size_t i = 0; i < 16; i += sizeof(size_t)) {
-        store_word_le(out + i,
-                      load_word_le(in + i) ^ ctx->EKi.t[i / sizeof(size_t)]);
+      for (size_t i = 0; i < 16; i += sizeof(crypto_word_t)) {
+        CRYPTO_store_word_le(out + i,
+                             CRYPTO_load_word_le(in + i) ^
+                                 ctx->EKi.t[i / sizeof(crypto_word_t)]);
       }
       out += 16;
       in += 16;
diff --git a/deps/boringssl/src/crypto/fipsmodule/modes/gcm_nohw.c b/deps/boringssl/src/crypto/fipsmodule/modes/gcm_nohw.c
index f8618b8..92d5441 100644
--- a/deps/boringssl/src/crypto/fipsmodule/modes/gcm_nohw.c
+++ b/deps/boringssl/src/crypto/fipsmodule/modes/gcm_nohw.c
@@ -193,7 +193,7 @@
 #endif  // BORINGSSL_HAS_UINT128
 
 void gcm_init_nohw(u128 Htable[16], const uint64_t Xi[2]) {
-  // We implement GHASH in terms of POLYVAL, as described in RFC8452. This
+  // We implement GHASH in terms of POLYVAL, as described in RFC 8452. This
   // avoids a shift by 1 in the multiplication, needed to account for bit
   // reversal losing a bit after multiplication, that is,
   // rev128(X) * rev128(Y) = rev255(X*Y).
diff --git a/deps/boringssl/src/crypto/fipsmodule/modes/gcm_test.cc b/deps/boringssl/src/crypto/fipsmodule/modes/gcm_test.cc
index 031b06c..02ba2d1 100644
--- a/deps/boringssl/src/crypto/fipsmodule/modes/gcm_test.cc
+++ b/deps/boringssl/src/crypto/fipsmodule/modes/gcm_test.cc
@@ -125,7 +125,7 @@
       UINT64_C(0x66e94bd4ef8a2c3b),
       UINT64_C(0x884cfa59ca342b2e),
   };
-  static const size_t kBlockCounts[] = {1, 2, 3, 4, 7, 8, 15, 16, 31, 32};
+  static const size_t kBlockCounts[] = {1, 2, 3, 4, 5, 6, 7, 8, 15, 16, 31, 32};
   uint8_t buf[16 * 32];
   OPENSSL_memset(buf, 42, sizeof(buf));
 
diff --git a/deps/boringssl/src/crypto/fipsmodule/modes/internal.h b/deps/boringssl/src/crypto/fipsmodule/modes/internal.h
index 2693fa6..2fea558 100644
--- a/deps/boringssl/src/crypto/fipsmodule/modes/internal.h
+++ b/deps/boringssl/src/crypto/fipsmodule/modes/internal.h
@@ -64,27 +64,6 @@
 #endif
 
 
-static inline uint32_t GETU32(const void *in) {
-  uint32_t v;
-  OPENSSL_memcpy(&v, in, sizeof(v));
-  return CRYPTO_bswap4(v);
-}
-
-static inline void PUTU32(void *out, uint32_t v) {
-  v = CRYPTO_bswap4(v);
-  OPENSSL_memcpy(out, &v, sizeof(v));
-}
-
-static inline size_t load_word_le(const void *in) {
-  size_t v;
-  OPENSSL_memcpy(&v, in, sizeof(v));
-  return v;
-}
-
-static inline void store_word_le(void *out, size_t v) {
-  OPENSSL_memcpy(out, &v, sizeof(v));
-}
-
 // block128_f is the type of an AES block cipher implementation.
 //
 // Unlike upstream OpenSSL, it and the other functions in this file hard-code
@@ -171,7 +150,7 @@
     uint64_t u[2];
     uint32_t d[4];
     uint8_t c[16];
-    size_t t[16 / sizeof(size_t)];
+    crypto_word_t t[16 / sizeof(crypto_word_t)];
   } Yi, EKi, EK0, len, Xi;
 
   // Note that the order of |Xi| and |gcm_key| is fixed by the MOVBE-based,
diff --git a/deps/boringssl/src/crypto/fipsmodule/modes/ofb.c b/deps/boringssl/src/crypto/fipsmodule/modes/ofb.c
index 4c70ce6..9d73d8a 100644
--- a/deps/boringssl/src/crypto/fipsmodule/modes/ofb.c
+++ b/deps/boringssl/src/crypto/fipsmodule/modes/ofb.c
@@ -60,7 +60,8 @@
 void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
                            const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            block128_f block) {
-  assert(in && out && key && ivec && num);
+  assert(key != NULL && ivec != NULL && num != NULL);
+  assert(len == 0 || (in != NULL && out != NULL));
 
   unsigned n = *num;
 
diff --git a/deps/boringssl/src/crypto/fipsmodule/rand/internal.h b/deps/boringssl/src/crypto/fipsmodule/rand/internal.h
index 598a17b..127e5d1 100644
--- a/deps/boringssl/src/crypto/fipsmodule/rand/internal.h
+++ b/deps/boringssl/src/crypto/fipsmodule/rand/internal.h
@@ -45,12 +45,10 @@
 // for seeding a DRBG, to |out_entropy|. It sets |*out_used_cpu| to one if the
 // entropy came directly from the CPU and zero if it came from the OS. It
 // actively obtains entropy from the CPU/OS and so should not be called from
-// within the FIPS module if |BORINGSSL_FIPS_PASSIVE_ENTROPY| is defined.
+// within the FIPS module.
 void CRYPTO_get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len,
                              int *out_used_cpu);
 
-#if defined(BORINGSSL_FIPS_PASSIVE_ENTROPY)
-
 // RAND_load_entropy supplies |entropy_len| bytes of entropy to the module. The
 // |from_cpu| parameter is true iff the entropy was obtained directly from the
 // CPU.
@@ -61,23 +59,22 @@
 // when the module has stopped because it has run out of entropy.
 void RAND_need_entropy(size_t bytes_needed);
 
-#endif  // BORINGSSL_FIPS_PASSIVE_ENTROPY
 #endif  // BORINGSSL_FIPS
 
 // CRYPTO_sysrand fills |len| bytes at |buf| with entropy from the operating
 // system.
 void CRYPTO_sysrand(uint8_t *buf, size_t len);
 
-#if defined(OPENSSL_URANDOM)
-// CRYPTO_init_sysrand initializes long-lived resources needed to draw entropy
-// from the operating system.
-void CRYPTO_init_sysrand(void);
-
 // CRYPTO_sysrand_for_seed fills |len| bytes at |buf| with entropy from the
 // operating system. It may draw from the |GRND_RANDOM| pool on Android,
 // depending on the vendor's configuration.
 void CRYPTO_sysrand_for_seed(uint8_t *buf, size_t len);
 
+#if defined(OPENSSL_URANDOM)
+// CRYPTO_init_sysrand initializes long-lived resources needed to draw entropy
+// from the operating system.
+void CRYPTO_init_sysrand(void);
+
 // CRYPTO_sysrand_if_available fills |len| bytes at |buf| with entropy from the
 // operating system, or early /dev/urandom data, and returns 1, _if_ the entropy
 // pool is initialized or if getrandom() is not available and not in FIPS mode.
@@ -87,10 +84,6 @@
 #else
 OPENSSL_INLINE void CRYPTO_init_sysrand(void) {}
 
-OPENSSL_INLINE void CRYPTO_sysrand_for_seed(uint8_t *buf, size_t len) {
-  CRYPTO_sysrand(buf, len);
-}
-
 OPENSSL_INLINE int CRYPTO_sysrand_if_available(uint8_t *buf, size_t len) {
   CRYPTO_sysrand(buf, len);
   return 1;
diff --git a/deps/boringssl/src/crypto/fipsmodule/rand/rand.c b/deps/boringssl/src/crypto/fipsmodule/rand/rand.c
index aa0f05b..29c43ae 100644
--- a/deps/boringssl/src/crypto/fipsmodule/rand/rand.c
+++ b/deps/boringssl/src/crypto/fipsmodule/rand/rand.c
@@ -83,16 +83,18 @@
 // called when the whole process is exiting.
 DEFINE_BSS_GET(struct rand_thread_state *, thread_states_list);
 DEFINE_STATIC_MUTEX(thread_states_list_lock);
+DEFINE_STATIC_MUTEX(state_clear_all_lock);
 
 static void rand_thread_state_clear_all(void) __attribute__((destructor));
 static void rand_thread_state_clear_all(void) {
   CRYPTO_STATIC_MUTEX_lock_write(thread_states_list_lock_bss_get());
+  CRYPTO_STATIC_MUTEX_lock_write(state_clear_all_lock_bss_get());
   for (struct rand_thread_state *cur = *thread_states_list_bss_get();
        cur != NULL; cur = cur->next) {
     CTR_DRBG_clear(&cur->drbg);
   }
-  // |thread_states_list_lock is deliberately left locked so that any threads
-  // that are still running will hang if they try to call |RAND_bytes|.
+  // The locks are deliberately left locked so that any threads that are still
+  // running will hang if they try to call |RAND_bytes|.
 }
 #endif
 
@@ -176,8 +178,6 @@
 #endif
 }
 
-#if defined(BORINGSSL_FIPS_PASSIVE_ENTROPY)
-
 // In passive entropy mode, entropy is supplied from outside of the module via
 // |RAND_load_entropy| and is stored in global instance of the following
 // structure.
@@ -240,17 +240,6 @@
   CRYPTO_STATIC_MUTEX_unlock_write(entropy_buffer_lock_bss_get());
 }
 
-#else
-
-// In the active case, |get_seed_entropy| simply calls |CRYPTO_get_seed_entropy|
-// in order to obtain entropy from the CPU or OS.
-static void get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len,
-                            int *out_used_cpu) {
-  CRYPTO_get_seed_entropy(out_entropy, out_entropy_len, out_used_cpu);
-}
-
-#endif  // !BORINGSSL_FIPS_PASSIVE_ENTROPY
-
 // rand_get_seed fills |seed| with entropy and sets |*out_used_cpu| to one if
 // that entropy came directly from the CPU and zero otherwise.
 static void rand_get_seed(struct rand_thread_state *state,
@@ -304,7 +293,7 @@
                           int *out_used_cpu) {
   // If not in FIPS mode, we don't overread from the system entropy source and
   // we don't depend only on the hardware RDRAND.
-  CRYPTO_sysrand(seed, CTR_DRBG_ENTROPY_LEN);
+  CRYPTO_sysrand_for_seed(seed, CTR_DRBG_ENTROPY_LEN);
   *out_used_cpu = 0;
 }
 
@@ -415,7 +404,7 @@
     // bug on ppc64le. glibc may implement pthread locks by wrapping user code
     // in a hardware transaction, but, on some older versions of glibc and the
     // kernel, syscalls made with |syscall| did not abort the transaction.
-    CRYPTO_STATIC_MUTEX_lock_read(thread_states_list_lock_bss_get());
+    CRYPTO_STATIC_MUTEX_lock_read(state_clear_all_lock_bss_get());
 #endif
     if (!CTR_DRBG_reseed(&state->drbg, seed, NULL, 0)) {
       abort();
@@ -424,7 +413,7 @@
     state->fork_generation = fork_generation;
   } else {
 #if defined(BORINGSSL_FIPS)
-    CRYPTO_STATIC_MUTEX_lock_read(thread_states_list_lock_bss_get());
+    CRYPTO_STATIC_MUTEX_lock_read(state_clear_all_lock_bss_get());
 #endif
   }
 
@@ -453,7 +442,7 @@
   }
 
 #if defined(BORINGSSL_FIPS)
-  CRYPTO_STATIC_MUTEX_unlock_read(thread_states_list_lock_bss_get());
+  CRYPTO_STATIC_MUTEX_unlock_read(state_clear_all_lock_bss_get());
 #endif
 }
 
diff --git a/deps/boringssl/src/crypto/fipsmodule/rand/urandom.c b/deps/boringssl/src/crypto/fipsmodule/rand/urandom.c
index 3def3aa..fa0a333 100644
--- a/deps/boringssl/src/crypto/fipsmodule/rand/urandom.c
+++ b/deps/boringssl/src/crypto/fipsmodule/rand/urandom.c
@@ -62,6 +62,15 @@
 #include <sys/random.h>
 #endif
 
+#if defined(OPENSSL_FREEBSD)
+#define URANDOM_BLOCKS_FOR_ENTROPY
+#if __FreeBSD__ >= 12
+// getrandom is supported in FreeBSD 12 and up.
+#define FREEBSD_GETRANDOM
+#include <sys/random.h>
+#endif
+#endif
+
 #include <openssl/thread.h>
 #include <openssl/mem.h>
 
@@ -176,6 +185,11 @@
   }
 #endif
 
+#if defined(FREEBSD_GETRANDOM)
+  *urandom_fd_bss_get() = kHaveGetrandom;
+  return;
+#endif
+
   // Android FIPS builds must support getrandom.
 #if defined(BORINGSSL_FIPS) && defined(OPENSSL_ANDROID)
   perror("getrandom not found");
@@ -256,11 +270,11 @@
     return;
   }
 
-#if defined(BORINGSSL_FIPS)
-  // In FIPS mode we ensure that the kernel has sufficient entropy before
-  // continuing. This is automatically handled by getrandom, which requires
-  // that the entropy pool has been initialised, but for urandom we have to
-  // poll.
+#if defined(BORINGSSL_FIPS) && !defined(URANDOM_BLOCKS_FOR_ENTROPY)
+  // In FIPS mode on platforms where urandom doesn't block at startup, we ensure
+  // that the kernel has sufficient entropy before continuing. This is
+  // automatically handled by getrandom, which requires that the entropy pool
+  // has been initialised, but for urandom we have to poll.
   for (;;) {
     int entropy_bits;
     if (ioctl(fd, RNDGETENTCNT, &entropy_bits)) {
@@ -277,7 +291,7 @@
 
     usleep(250000);
   }
-#endif  // BORINGSSL_FIPS
+#endif  // BORINGSSL_FIPS && !URANDOM_BLOCKS_FOR_ENTROPY
 }
 
 // fill_with_entropy writes |len| bytes of entropy into |out|. It returns one
@@ -291,11 +305,14 @@
     return 1;
   }
 
-#if defined(USE_NR_getrandom)
+#if defined(USE_NR_getrandom) || defined(FREEBSD_GETRANDOM)
   int getrandom_flags = 0;
   if (!block) {
     getrandom_flags |= GRND_NONBLOCK;
   }
+#endif
+
+#if defined (USE_NR_getrandom)
   if (seed) {
     getrandom_flags |= *extra_getrandom_flags_for_seed_bss_get();
   }
@@ -315,6 +332,8 @@
     if (*urandom_fd_bss_get() == kHaveGetrandom) {
 #if defined(USE_NR_getrandom)
       r = boringssl_getrandom(out, len, getrandom_flags);
+#elif defined(FREEBSD_GETRANDOM)
+      r = getrandom(out, len, getrandom_flags);
 #elif defined(OPENSSL_MACOS)
       if (__builtin_available(macos 10.12, *)) {
         // |getentropy| can only request 256 bytes at a time.
@@ -348,6 +367,10 @@
   return 1;
 }
 
+void CRYPTO_init_sysrand(void) {
+  CRYPTO_once(rand_once_bss_get(), init_once);
+}
+
 // CRYPTO_sysrand puts |requested| random bytes into |out|.
 void CRYPTO_sysrand(uint8_t *out, size_t requested) {
   if (!fill_with_entropy(out, requested, /*block=*/1, /*seed=*/0)) {
@@ -356,18 +379,12 @@
   }
 }
 
-void CRYPTO_init_sysrand(void) {
-  CRYPTO_once(rand_once_bss_get(), init_once);
-}
-
-#if defined(BORINGSSL_FIPS)
 void CRYPTO_sysrand_for_seed(uint8_t *out, size_t requested) {
   if (!fill_with_entropy(out, requested, /*block=*/1, /*seed=*/1)) {
     perror("entropy fill failed");
     abort();
   }
 }
-#endif  // BORINGSSL_FIPS
 
 int CRYPTO_sysrand_if_available(uint8_t *out, size_t requested) {
   if (fill_with_entropy(out, requested, /*block=*/0, /*seed=*/0)) {
diff --git a/deps/boringssl/src/crypto/fipsmodule/rsa/rsa.c b/deps/boringssl/src/crypto/fipsmodule/rsa/rsa.c
index ae63e1a..fd84cba 100644
--- a/deps/boringssl/src/crypto/fipsmodule/rsa/rsa.c
+++ b/deps/boringssl/src/crypto/fipsmodule/rsa/rsa.c
@@ -458,18 +458,18 @@
 };
 
 int RSA_add_pkcs1_prefix(uint8_t **out_msg, size_t *out_msg_len,
-                         int *is_alloced, int hash_nid, const uint8_t *msg,
-                         size_t msg_len) {
+                         int *is_alloced, int hash_nid, const uint8_t *digest,
+                         size_t digest_len) {
   unsigned i;
 
   if (hash_nid == NID_md5_sha1) {
     // Special case: SSL signature, just check the length.
-    if (msg_len != SSL_SIG_LENGTH) {
+    if (digest_len != SSL_SIG_LENGTH) {
       OPENSSL_PUT_ERROR(RSA, RSA_R_INVALID_MESSAGE_LENGTH);
       return 0;
     }
 
-    *out_msg = (uint8_t*) msg;
+    *out_msg = (uint8_t *)digest;
     *out_msg_len = SSL_SIG_LENGTH;
     *is_alloced = 0;
     return 1;
@@ -481,7 +481,7 @@
       continue;
     }
 
-    if (msg_len != sig_prefix->hash_len) {
+    if (digest_len != sig_prefix->hash_len) {
       OPENSSL_PUT_ERROR(RSA, RSA_R_INVALID_MESSAGE_LENGTH);
       return 0;
     }
@@ -491,7 +491,7 @@
     unsigned signed_msg_len;
     uint8_t *signed_msg;
 
-    signed_msg_len = prefix_len + msg_len;
+    signed_msg_len = prefix_len + digest_len;
     if (signed_msg_len < prefix_len) {
       OPENSSL_PUT_ERROR(RSA, RSA_R_TOO_LONG);
       return 0;
@@ -504,7 +504,7 @@
     }
 
     OPENSSL_memcpy(signed_msg, prefix, prefix_len);
-    OPENSSL_memcpy(signed_msg + prefix_len, msg, msg_len);
+    OPENSSL_memcpy(signed_msg + prefix_len, digest, digest_len);
 
     *out_msg = signed_msg;
     *out_msg_len = signed_msg_len;
@@ -517,8 +517,8 @@
   return 0;
 }
 
-int RSA_sign(int hash_nid, const uint8_t *in, unsigned in_len, uint8_t *out,
-             unsigned *out_len, RSA *rsa) {
+int RSA_sign(int hash_nid, const uint8_t *digest, unsigned digest_len,
+             uint8_t *out, unsigned *out_len, RSA *rsa) {
   const unsigned rsa_size = RSA_size(rsa);
   int ret = 0;
   uint8_t *signed_msg = NULL;
@@ -527,11 +527,12 @@
   size_t size_t_out_len;
 
   if (rsa->meth->sign) {
-    return rsa->meth->sign(hash_nid, in, in_len, out, out_len, rsa);
+    return rsa->meth->sign(hash_nid, digest, digest_len, out, out_len, rsa);
   }
 
   if (!RSA_add_pkcs1_prefix(&signed_msg, &signed_msg_len,
-                            &signed_msg_is_alloced, hash_nid, in, in_len) ||
+                            &signed_msg_is_alloced, hash_nid, digest,
+                            digest_len) ||
       !RSA_sign_raw(rsa, &size_t_out_len, out, rsa_size, signed_msg,
                     signed_msg_len, RSA_PKCS1_PADDING)) {
     goto err;
@@ -548,9 +549,9 @@
 }
 
 int RSA_sign_pss_mgf1(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out,
-                      const uint8_t *in, size_t in_len, const EVP_MD *md,
-                      const EVP_MD *mgf1_md, int salt_len) {
-  if (in_len != EVP_MD_size(md)) {
+                      const uint8_t *digest, size_t digest_len,
+                      const EVP_MD *md, const EVP_MD *mgf1_md, int salt_len) {
+  if (digest_len != EVP_MD_size(md)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_INVALID_MESSAGE_LENGTH);
     return 0;
   }
@@ -562,15 +563,15 @@
     return 0;
   }
 
-  int ret =
-      RSA_padding_add_PKCS1_PSS_mgf1(rsa, padded, in, md, mgf1_md, salt_len) &&
-      RSA_sign_raw(rsa, out_len, out, max_out, padded, padded_len,
-                   RSA_NO_PADDING);
+  int ret = RSA_padding_add_PKCS1_PSS_mgf1(rsa, padded, digest, md, mgf1_md,
+                                           salt_len) &&
+            RSA_sign_raw(rsa, out_len, out, max_out, padded, padded_len,
+                         RSA_NO_PADDING);
   OPENSSL_free(padded);
   return ret;
 }
 
-int RSA_verify(int hash_nid, const uint8_t *msg, size_t msg_len,
+int RSA_verify(int hash_nid, const uint8_t *digest, size_t digest_len,
                const uint8_t *sig, size_t sig_len, RSA *rsa) {
   if (rsa->n == NULL || rsa->e == NULL) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_VALUE_MISSING);
@@ -584,7 +585,7 @@
   size_t signed_msg_len = 0, len;
   int signed_msg_is_alloced = 0;
 
-  if (hash_nid == NID_md5_sha1 && msg_len != SSL_SIG_LENGTH) {
+  if (hash_nid == NID_md5_sha1 && digest_len != SSL_SIG_LENGTH) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_INVALID_MESSAGE_LENGTH);
     return 0;
   }
@@ -601,7 +602,8 @@
   }
 
   if (!RSA_add_pkcs1_prefix(&signed_msg, &signed_msg_len,
-                            &signed_msg_is_alloced, hash_nid, msg, msg_len)) {
+                            &signed_msg_is_alloced, hash_nid, digest,
+                            digest_len)) {
     goto out;
   }
 
@@ -622,10 +624,10 @@
   return ret;
 }
 
-int RSA_verify_pss_mgf1(RSA *rsa, const uint8_t *msg, size_t msg_len,
+int RSA_verify_pss_mgf1(RSA *rsa, const uint8_t *digest, size_t digest_len,
                         const EVP_MD *md, const EVP_MD *mgf1_md, int salt_len,
                         const uint8_t *sig, size_t sig_len) {
-  if (msg_len != EVP_MD_size(md)) {
+  if (digest_len != EVP_MD_size(md)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_INVALID_MESSAGE_LENGTH);
     return 0;
   }
@@ -647,7 +649,7 @@
     goto err;
   }
 
-  ret = RSA_verify_PKCS1_PSS_mgf1(rsa, msg, md, mgf1_md, em, salt_len);
+  ret = RSA_verify_PKCS1_PSS_mgf1(rsa, digest, md, mgf1_md, em, salt_len);
 
 err:
   OPENSSL_free(em);
@@ -655,7 +657,8 @@
 }
 
 static int check_mod_inverse(int *out_ok, const BIGNUM *a, const BIGNUM *ainv,
-                             const BIGNUM *m, BN_CTX *ctx) {
+                             const BIGNUM *m, unsigned m_min_bits,
+                             BN_CTX *ctx) {
   if (BN_is_negative(ainv) || BN_cmp(ainv, m) >= 0) {
     *out_ok = 0;
     return 1;
@@ -668,7 +671,7 @@
   BIGNUM *tmp = BN_CTX_get(ctx);
   int ret = tmp != NULL &&
             bn_mul_consttime(tmp, a, ainv, ctx) &&
-            bn_div_consttime(NULL, tmp, tmp, m, ctx);
+            bn_div_consttime(NULL, tmp, tmp, m, m_min_bits, ctx);
   if (ret) {
     *out_ok = BN_is_one(tmp);
   }
@@ -748,10 +751,15 @@
   // simply check that d * e is one mod p-1 and mod q-1. Note d and e were bound
   // by earlier checks in this function.
   if (!bn_usub_consttime(&pm1, key->p, BN_value_one()) ||
-      !bn_usub_consttime(&qm1, key->q, BN_value_one()) ||
-      !bn_mul_consttime(&de, key->d, key->e, ctx) ||
-      !bn_div_consttime(NULL, &tmp, &de, &pm1, ctx) ||
-      !bn_div_consttime(NULL, &de, &de, &qm1, ctx)) {
+      !bn_usub_consttime(&qm1, key->q, BN_value_one())) {
+    OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
+    goto out;
+  }
+  const unsigned pm1_bits = BN_num_bits(&pm1);
+  const unsigned qm1_bits = BN_num_bits(&qm1);
+  if (!bn_mul_consttime(&de, key->d, key->e, ctx) ||
+      !bn_div_consttime(NULL, &tmp, &de, &pm1, pm1_bits, ctx) ||
+      !bn_div_consttime(NULL, &de, &de, &qm1, qm1_bits, ctx)) {
     OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
     goto out;
   }
@@ -770,9 +778,12 @@
 
   if (has_crt_values) {
     int dmp1_ok, dmq1_ok, iqmp_ok;
-    if (!check_mod_inverse(&dmp1_ok, key->e, key->dmp1, &pm1, ctx) ||
-        !check_mod_inverse(&dmq1_ok, key->e, key->dmq1, &qm1, ctx) ||
-        !check_mod_inverse(&iqmp_ok, key->q, key->iqmp, key->p, ctx)) {
+    if (!check_mod_inverse(&dmp1_ok, key->e, key->dmp1, &pm1, pm1_bits, ctx) ||
+        !check_mod_inverse(&dmq1_ok, key->e, key->dmq1, &qm1, qm1_bits, ctx) ||
+        // |p| is odd, so |pm1| and |p| have the same bit width. If they didn't,
+        // we only need a lower bound anyway.
+        !check_mod_inverse(&iqmp_ok, key->q, key->iqmp, key->p, pm1_bits,
+                           ctx)) {
       OPENSSL_PUT_ERROR(RSA, ERR_LIB_BN);
       goto out;
     }
diff --git a/deps/boringssl/src/crypto/fipsmodule/rsa/rsa_impl.c b/deps/boringssl/src/crypto/fipsmodule/rsa/rsa_impl.c
index 2f76e9e..a6865c0 100644
--- a/deps/boringssl/src/crypto/fipsmodule/rsa/rsa_impl.c
+++ b/deps/boringssl/src/crypto/fipsmodule/rsa/rsa_impl.c
@@ -79,9 +79,8 @@
     return 0;
   }
 
-  unsigned rsa_bits = BN_num_bits(rsa->n);
-
-  if (rsa_bits > 16 * 1024) {
+  unsigned n_bits = BN_num_bits(rsa->n);
+  if (n_bits > 16 * 1024) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_MODULUS_TOO_LARGE);
     return 0;
   }
@@ -96,17 +95,21 @@
   // [2] https://www.imperialviolet.org/2012/03/17/rsados.html
   // [3] https://msdn.microsoft.com/en-us/library/aa387685(VS.85).aspx
   static const unsigned kMaxExponentBits = 33;
-
-  if (BN_num_bits(rsa->e) > kMaxExponentBits) {
+  unsigned e_bits = BN_num_bits(rsa->e);
+  if (e_bits > kMaxExponentBits ||
+      // Additionally reject e = 1 or even e. e must be odd to be relatively
+      // prime with phi(n).
+      e_bits < 2 ||
+      !BN_is_odd(rsa->e)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_E_VALUE);
     return 0;
   }
 
-  // Verify |n > e|. Comparing |rsa_bits| to |kMaxExponentBits| is a small
+  // Verify |n > e|. Comparing |n_bits| to |kMaxExponentBits| is a small
   // shortcut to comparing |n| and |e| directly. In reality, |kMaxExponentBits|
   // is much smaller than the minimum RSA key size that any application should
   // accept.
-  if (rsa_bits <= kMaxExponentBits) {
+  if (n_bits <= kMaxExponentBits) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_KEY_SIZE_TOO_SMALL);
     return 0;
   }
@@ -1259,12 +1262,14 @@
     // values for d.
   } while (BN_cmp(rsa->d, pow2_prime_bits) <= 0);
 
+  assert(BN_num_bits(pm1) == (unsigned)prime_bits);
+  assert(BN_num_bits(qm1) == (unsigned)prime_bits);
   if (// Calculate n.
       !bn_mul_consttime(rsa->n, rsa->p, rsa->q, ctx) ||
       // Calculate d mod (p-1).
-      !bn_div_consttime(NULL, rsa->dmp1, rsa->d, pm1, ctx) ||
+      !bn_div_consttime(NULL, rsa->dmp1, rsa->d, pm1, prime_bits, ctx) ||
       // Calculate d mod (q-1)
-      !bn_div_consttime(NULL, rsa->dmq1, rsa->d, qm1, ctx)) {
+      !bn_div_consttime(NULL, rsa->dmq1, rsa->d, qm1, prime_bits, ctx)) {
     goto bn_err;
   }
   bn_set_minimal_width(rsa->n);
diff --git a/deps/boringssl/src/crypto/fipsmodule/self_check/fips.c b/deps/boringssl/src/crypto/fipsmodule/self_check/fips.c
new file mode 100644
index 0000000..d55c493
--- /dev/null
+++ b/deps/boringssl/src/crypto/fipsmodule/self_check/fips.c
@@ -0,0 +1,79 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/crypto.h>
+
+#include "../../internal.h"
+#include "../delocate.h"
+
+
+int FIPS_mode(void) {
+#if defined(BORINGSSL_FIPS) && !defined(OPENSSL_ASAN)
+  return 1;
+#else
+  return 0;
+#endif
+}
+
+int FIPS_mode_set(int on) { return on == FIPS_mode(); }
+
+#if defined(BORINGSSL_FIPS_COUNTERS)
+
+size_t FIPS_read_counter(enum fips_counter_t counter) {
+  if (counter < 0 || counter > fips_counter_max) {
+    abort();
+  }
+
+  const size_t *array =
+      CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_FIPS_COUNTERS);
+  if (!array) {
+    return 0;
+  }
+
+  return array[counter];
+}
+
+void boringssl_fips_inc_counter(enum fips_counter_t counter) {
+  if (counter < 0 || counter > fips_counter_max) {
+    abort();
+  }
+
+  size_t *array =
+      CRYPTO_get_thread_local(OPENSSL_THREAD_LOCAL_FIPS_COUNTERS);
+  if (!array) {
+    const size_t num_bytes = sizeof(size_t) * (fips_counter_max + 1);
+    array = OPENSSL_malloc(num_bytes);
+    if (!array) {
+      return;
+    }
+
+    OPENSSL_memset(array, 0, num_bytes);
+    if (!CRYPTO_set_thread_local(OPENSSL_THREAD_LOCAL_FIPS_COUNTERS, array,
+                                 OPENSSL_free)) {
+      // |OPENSSL_free| has already been called by |CRYPTO_set_thread_local|.
+      return;
+    }
+  }
+
+  array[counter]++;
+}
+
+#else
+
+size_t FIPS_read_counter(enum fips_counter_t counter) { return 0; }
+
+// boringssl_fips_inc_counter is a no-op, inline function in internal.h in this
+// case. That should let the compiler optimise away the callsites.
+
+#endif
diff --git a/deps/boringssl/src/crypto/fipsmodule/self_check/self_check.c b/deps/boringssl/src/crypto/fipsmodule/self_check/self_check.c
index 638500b..2a58cd3 100644
--- a/deps/boringssl/src/crypto/fipsmodule/self_check/self_check.c
+++ b/deps/boringssl/src/crypto/fipsmodule/self_check/self_check.c
@@ -32,13 +32,20 @@
 
 #include "../../internal.h"
 #include "../ec/internal.h"
+#include "../ecdsa/internal.h"
 #include "../rand/internal.h"
 #include "../tls/internal.h"
 
 
 // MSVC wants to put a NUL byte at the end of non-char arrays and so cannot
-// compile this.
-#if !defined(_MSC_VER)
+// compile the real logic.
+#if defined(_MSC_VER)
+
+int BORINGSSL_self_test(void) {
+  return 0;
+}
+
+#else
 
 #if defined(BORINGSSL_FIPS) && defined(OPENSSL_ANDROID)
 // FIPS builds on Android will test for flag files, named after the module hash,
@@ -727,14 +734,12 @@
   // ECDSA Sign/Verify KAT
 
   // The 'k' value for ECDSA is fixed to avoid an entropy draw.
-  ec_key->fixed_k = BN_new();
-  if (ec_key->fixed_k == NULL ||
-      !BN_set_word(ec_key->fixed_k, 42)) {
-    fprintf(stderr, "Out of memory\n");
-    goto err;
-  }
+  uint8_t ecdsa_k[32] = {0};
+  ecdsa_k[31] = 42;
 
-  sig = ECDSA_do_sign(kPlaintextSHA256, sizeof(kPlaintextSHA256), ec_key);
+  sig = ecdsa_sign_with_nonce_for_known_answer_test(
+      kPlaintextSHA256, sizeof(kPlaintextSHA256), ec_key, ecdsa_k,
+      sizeof(ecdsa_k));
 
   uint8_t ecdsa_r_bytes[sizeof(kECDSASigR)];
   uint8_t ecdsa_s_bytes[sizeof(kECDSASigS)];
diff --git a/deps/boringssl/src/crypto/fipsmodule/sha/sha1.c b/deps/boringssl/src/crypto/fipsmodule/sha/sha1.c
index 3b76194..c629308 100644
--- a/deps/boringssl/src/crypto/fipsmodule/sha/sha1.c
+++ b/deps/boringssl/src/crypto/fipsmodule/sha/sha1.c
@@ -60,8 +60,9 @@
 
 #include <openssl/mem.h>
 
-#include "internal.h"
 #include "../../internal.h"
+#include "../digest/md32_common.h"
+#include "internal.h"
 
 
 int SHA1_Init(SHA_CTX *sha) {
@@ -83,30 +84,33 @@
   return out;
 }
 
-#define DATA_ORDER_IS_BIG_ENDIAN
+#if !defined(SHA1_ASM)
+static void sha1_block_data_order(uint32_t *state, const uint8_t *data,
+                                  size_t num);
+#endif
 
-#define HASH_CTX                SHA_CTX
-#define HASH_CBLOCK             64
-#define HASH_DIGEST_LENGTH      20
-#define HASH_MAKE_STRING(c, s) \
-  do {                         \
-    uint32_t ll;               \
-    ll = (c)->h[0];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[1];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[2];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[3];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[4];            \
-    HOST_l2c(ll, (s));         \
-  } while (0)
+void SHA1_Transform(SHA_CTX *c, const uint8_t data[SHA_CBLOCK]) {
+  sha1_block_data_order(c->h, data, 1);
+}
 
-#define HASH_UPDATE SHA1_Update
-#define HASH_TRANSFORM SHA1_Transform
-#define HASH_FINAL SHA1_Final
-#define HASH_BLOCK_DATA_ORDER sha1_block_data_order
+int SHA1_Update(SHA_CTX *c, const void *data, size_t len) {
+  crypto_md32_update(&sha1_block_data_order, c->h, c->data, SHA_CBLOCK, &c->num,
+                     &c->Nh, &c->Nl, data, len);
+  return 1;
+}
+
+int SHA1_Final(uint8_t out[SHA_DIGEST_LENGTH], SHA_CTX *c) {
+  crypto_md32_final(&sha1_block_data_order, c->h, c->data, SHA_CBLOCK, &c->num,
+                    c->Nh, c->Nl, /*is_big_endian=*/1);
+
+  CRYPTO_store_u32_be(out, c->h[0]);
+  CRYPTO_store_u32_be(out + 4, c->h[1]);
+  CRYPTO_store_u32_be(out + 8, c->h[2]);
+  CRYPTO_store_u32_be(out + 12, c->h[3]);
+  CRYPTO_store_u32_be(out + 16, c->h[4]);
+  return 1;
+}
+
 #define ROTATE(a, n) (((a) << (n)) | ((a) >> (32 - (n))))
 #define Xupdate(a, ix, ia, ib, ic, id) \
   do {                                 \
@@ -114,13 +118,6 @@
     (ix) = (a) = ROTATE((a), 1);       \
   } while (0)
 
-#if !defined(SHA1_ASM)
-static void sha1_block_data_order(uint32_t *state, const uint8_t *data,
-                                  size_t num);
-#endif
-
-#include "../digest/md32_common.h"
-
 #define K_00_19 0x5a827999UL
 #define K_20_39 0x6ed9eba1UL
 #define K_40_59 0x8f1bbcdcUL
@@ -193,7 +190,7 @@
 #if !defined(SHA1_ASM)
 static void sha1_block_data_order(uint32_t *state, const uint8_t *data,
                                   size_t num) {
-  register uint32_t A, B, C, D, E, T, l;
+  register uint32_t A, B, C, D, E, T;
   uint32_t XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10,
       XX11, XX12, XX13, XX14, XX15;
 
@@ -204,52 +201,52 @@
   E = state[4];
 
   for (;;) {
-    HOST_c2l(data, l);
-    X(0) = l;
-    HOST_c2l(data, l);
-    X(1) = l;
+    X(0) = CRYPTO_load_u32_be(data);
+    data += 4;
+    X(1) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(0, A, B, C, D, E, T, X(0));
-    HOST_c2l(data, l);
-    X(2) = l;
+    X(2) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(1, T, A, B, C, D, E, X(1));
-    HOST_c2l(data, l);
-    X(3) = l;
+    X(3) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(2, E, T, A, B, C, D, X(2));
-    HOST_c2l(data, l);
-    X(4) = l;
+    X(4) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(3, D, E, T, A, B, C, X(3));
-    HOST_c2l(data, l);
-    X(5) = l;
+    X(5) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(4, C, D, E, T, A, B, X(4));
-    HOST_c2l(data, l);
-    X(6) = l;
+    X(6) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(5, B, C, D, E, T, A, X(5));
-    HOST_c2l(data, l);
-    X(7) = l;
+    X(7) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(6, A, B, C, D, E, T, X(6));
-    HOST_c2l(data, l);
-    X(8) = l;
+    X(8) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(7, T, A, B, C, D, E, X(7));
-    HOST_c2l(data, l);
-    X(9) = l;
+    X(9) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(8, E, T, A, B, C, D, X(8));
-    HOST_c2l(data, l);
-    X(10) = l;
+    X(10) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(9, D, E, T, A, B, C, X(9));
-    HOST_c2l(data, l);
-    X(11) = l;
+    X(11) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(10, C, D, E, T, A, B, X(10));
-    HOST_c2l(data, l);
-    X(12) = l;
+    X(12) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(11, B, C, D, E, T, A, X(11));
-    HOST_c2l(data, l);
-    X(13) = l;
+    X(13) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(12, A, B, C, D, E, T, X(12));
-    HOST_c2l(data, l);
-    X(14) = l;
+    X(14) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(13, T, A, B, C, D, E, X(13));
-    HOST_c2l(data, l);
-    X(15) = l;
+    X(15) = CRYPTO_load_u32_be(data);
+    data += 4;
     BODY_00_15(14, E, T, A, B, C, D, X(14));
     BODY_00_15(15, D, E, T, A, B, C, X(15));
 
@@ -341,15 +338,6 @@
 }
 #endif
 
-#undef DATA_ORDER_IS_BIG_ENDIAN
-#undef HASH_CTX
-#undef HASH_CBLOCK
-#undef HASH_DIGEST_LENGTH
-#undef HASH_MAKE_STRING
-#undef HASH_UPDATE
-#undef HASH_TRANSFORM
-#undef HASH_FINAL
-#undef HASH_BLOCK_DATA_ORDER
 #undef ROTATE
 #undef Xupdate
 #undef K_00_19
@@ -367,5 +355,3 @@
 #undef BODY_40_59
 #undef BODY_60_79
 #undef X
-#undef HOST_c2l
-#undef HOST_l2c
diff --git a/deps/boringssl/src/crypto/fipsmodule/sha/sha256.c b/deps/boringssl/src/crypto/fipsmodule/sha/sha256.c
index 0e42446..4394f4a 100644
--- a/deps/boringssl/src/crypto/fipsmodule/sha/sha256.c
+++ b/deps/boringssl/src/crypto/fipsmodule/sha/sha256.c
@@ -60,8 +60,9 @@
 
 #include <openssl/mem.h>
 
-#include "internal.h"
 #include "../../internal.h"
+#include "../digest/md32_common.h"
+#include "internal.h"
 
 
 int SHA224_Init(SHA256_CTX *sha) {
@@ -112,71 +113,60 @@
   return out;
 }
 
-int SHA224_Update(SHA256_CTX *ctx, const void *data, size_t len) {
-  return SHA256_Update(ctx, data, len);
-}
-
-int SHA224_Final(uint8_t out[SHA224_DIGEST_LENGTH], SHA256_CTX *ctx) {
-  // SHA224_Init sets |ctx->md_len| to |SHA224_DIGEST_LENGTH|, so this has a
-  // smaller output.
-  return SHA256_Final(out, ctx);
-}
-
-#define DATA_ORDER_IS_BIG_ENDIAN
-
-#define HASH_CTX SHA256_CTX
-#define HASH_CBLOCK 64
-#define HASH_DIGEST_LENGTH 32
-
-// Note that FIPS180-2 discusses "Truncation of the Hash Function Output."
-// default: case below covers for it. It's not clear however if it's permitted
-// to truncate to amount of bytes not divisible by 4. I bet not, but if it is,
-// then default: case shall be extended. For reference. Idea behind separate
-// cases for pre-defined lenghts is to let the compiler decide if it's
-// appropriate to unroll small loops.
-//
-// TODO(davidben): The small |md_len| case is one of the few places a low-level
-// hash 'final' function can fail. This should never happen.
-#define HASH_MAKE_STRING(c, s)                              \
-  do {                                                      \
-    uint32_t ll;                                            \
-    unsigned int nn;                                        \
-    switch ((c)->md_len) {                                  \
-      case SHA224_DIGEST_LENGTH:                            \
-        for (nn = 0; nn < SHA224_DIGEST_LENGTH / 4; nn++) { \
-          ll = (c)->h[nn];                                  \
-          HOST_l2c(ll, (s));                                \
-        }                                                   \
-        break;                                              \
-      case SHA256_DIGEST_LENGTH:                            \
-        for (nn = 0; nn < SHA256_DIGEST_LENGTH / 4; nn++) { \
-          ll = (c)->h[nn];                                  \
-          HOST_l2c(ll, (s));                                \
-        }                                                   \
-        break;                                              \
-      default:                                              \
-        if ((c)->md_len > SHA256_DIGEST_LENGTH) {           \
-          return 0;                                         \
-        }                                                   \
-        for (nn = 0; nn < (c)->md_len / 4; nn++) {          \
-          ll = (c)->h[nn];                                  \
-          HOST_l2c(ll, (s));                                \
-        }                                                   \
-        break;                                              \
-    }                                                       \
-  } while (0)
-
-
-#define HASH_UPDATE SHA256_Update
-#define HASH_TRANSFORM SHA256_Transform
-#define HASH_FINAL SHA256_Final
-#define HASH_BLOCK_DATA_ORDER sha256_block_data_order
 #ifndef SHA256_ASM
 static void sha256_block_data_order(uint32_t *state, const uint8_t *in,
                                     size_t num);
 #endif
 
-#include "../digest/md32_common.h"
+void SHA256_Transform(SHA256_CTX *c, const uint8_t data[SHA256_CBLOCK]) {
+  sha256_block_data_order(c->h, data, 1);
+}
+
+int SHA256_Update(SHA256_CTX *c, const void *data, size_t len) {
+  crypto_md32_update(&sha256_block_data_order, c->h, c->data, SHA256_CBLOCK,
+                     &c->num, &c->Nh, &c->Nl, data, len);
+  return 1;
+}
+
+int SHA224_Update(SHA256_CTX *ctx, const void *data, size_t len) {
+  return SHA256_Update(ctx, data, len);
+}
+
+static int sha256_final_impl(uint8_t *out, SHA256_CTX *c) {
+  crypto_md32_final(&sha256_block_data_order, c->h, c->data, SHA256_CBLOCK,
+                    &c->num, c->Nh, c->Nl, /*is_big_endian=*/1);
+
+  // TODO(davidben): This overflow check one of the few places a low-level hash
+  // 'final' function can fail. SHA-512 does not have a corresponding check.
+  // These functions already misbehave if the caller arbitrarily mutates |c|, so
+  // can we assume one of |SHA256_Init| or |SHA224_Init| was used?
+  if (c->md_len > SHA256_DIGEST_LENGTH) {
+    return 0;
+  }
+
+  assert(c->md_len % 4 == 0);
+  const size_t out_words = c->md_len / 4;
+  for (size_t i = 0; i < out_words; i++) {
+    CRYPTO_store_u32_be(out, c->h[i]);
+    out += 4;
+  }
+  return 1;
+}
+
+int SHA256_Final(uint8_t out[SHA256_DIGEST_LENGTH], SHA256_CTX *c) {
+  // Ideally we would assert |sha->md_len| is |SHA256_DIGEST_LENGTH| to match
+  // the size hint, but calling code often pairs |SHA224_Init| with
+  // |SHA256_Final| and expects |sha->md_len| to carry the size over.
+  //
+  // TODO(davidben): Add an assert and fix code to match them up.
+  return sha256_final_impl(out, c);
+}
+int SHA224_Final(uint8_t out[SHA224_DIGEST_LENGTH], SHA256_CTX *ctx) {
+  // SHA224_Init sets |ctx->md_len| to |SHA224_DIGEST_LENGTH|, so this has a
+  // smaller output.
+  assert(ctx->md_len == SHA224_DIGEST_LENGTH);
+  return sha256_final_impl(out, ctx);
+}
 
 #ifndef SHA256_ASM
 static const uint32_t K256[64] = {
@@ -241,55 +231,53 @@
     g = state[6];
     h = state[7];
 
-    uint32_t l;
-
-    HOST_c2l(data, l);
-    T1 = X[0] = l;
+    T1 = X[0] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(0, a, b, c, d, e, f, g, h);
-    HOST_c2l(data, l);
-    T1 = X[1] = l;
+    T1 = X[1] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(1, h, a, b, c, d, e, f, g);
-    HOST_c2l(data, l);
-    T1 = X[2] = l;
+    T1 = X[2] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(2, g, h, a, b, c, d, e, f);
-    HOST_c2l(data, l);
-    T1 = X[3] = l;
+    T1 = X[3] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(3, f, g, h, a, b, c, d, e);
-    HOST_c2l(data, l);
-    T1 = X[4] = l;
+    T1 = X[4] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(4, e, f, g, h, a, b, c, d);
-    HOST_c2l(data, l);
-    T1 = X[5] = l;
+    T1 = X[5] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(5, d, e, f, g, h, a, b, c);
-    HOST_c2l(data, l);
-    T1 = X[6] = l;
+    T1 = X[6] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(6, c, d, e, f, g, h, a, b);
-    HOST_c2l(data, l);
-    T1 = X[7] = l;
+    T1 = X[7] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(7, b, c, d, e, f, g, h, a);
-    HOST_c2l(data, l);
-    T1 = X[8] = l;
+    T1 = X[8] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(8, a, b, c, d, e, f, g, h);
-    HOST_c2l(data, l);
-    T1 = X[9] = l;
+    T1 = X[9] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(9, h, a, b, c, d, e, f, g);
-    HOST_c2l(data, l);
-    T1 = X[10] = l;
+    T1 = X[10] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(10, g, h, a, b, c, d, e, f);
-    HOST_c2l(data, l);
-    T1 = X[11] = l;
+    T1 = X[11] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(11, f, g, h, a, b, c, d, e);
-    HOST_c2l(data, l);
-    T1 = X[12] = l;
+    T1 = X[12] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(12, e, f, g, h, a, b, c, d);
-    HOST_c2l(data, l);
-    T1 = X[13] = l;
+    T1 = X[13] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(13, d, e, f, g, h, a, b, c);
-    HOST_c2l(data, l);
-    T1 = X[14] = l;
+    T1 = X[14] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(14, c, d, e, f, g, h, a, b);
-    HOST_c2l(data, l);
-    T1 = X[15] = l;
+    T1 = X[15] = CRYPTO_load_u32_be(data);
+    data += 4;
     ROUND_00_15(15, b, c, d, e, f, g, h, a);
 
     for (i = 16; i < 64; i += 8) {
@@ -321,15 +309,6 @@
   sha256_block_data_order(state, data, num_blocks);
 }
 
-#undef DATA_ORDER_IS_BIG_ENDIAN
-#undef HASH_CTX
-#undef HASH_CBLOCK
-#undef HASH_DIGEST_LENGTH
-#undef HASH_MAKE_STRING
-#undef HASH_UPDATE
-#undef HASH_TRANSFORM
-#undef HASH_FINAL
-#undef HASH_BLOCK_DATA_ORDER
 #undef ROTATE
 #undef Sigma0
 #undef Sigma1
@@ -339,5 +318,3 @@
 #undef Maj
 #undef ROUND_00_15
 #undef ROUND_16_63
-#undef HOST_c2l
-#undef HOST_l2c
diff --git a/deps/boringssl/src/crypto/fipsmodule/sha/sha512.c b/deps/boringssl/src/crypto/fipsmodule/sha/sha512.c
index fd02574..befdd52 100644
--- a/deps/boringssl/src/crypto/fipsmodule/sha/sha512.c
+++ b/deps/boringssl/src/crypto/fipsmodule/sha/sha512.c
@@ -70,6 +70,8 @@
 // this writing, so there is no need for a common collector/padding
 // implementation yet.
 
+static int sha512_final_impl(uint8_t *out, SHA512_CTX *sha);
+
 int SHA384_Init(SHA512_CTX *sha) {
   sha->h[0] = UINT64_C(0xcbbb9d5dc1059ed8);
   sha->h[1] = UINT64_C(0x629a292a367cd507);
@@ -146,8 +148,8 @@
                     uint8_t out[SHA512_256_DIGEST_LENGTH]) {
   SHA512_CTX ctx;
   SHA512_256_Init(&ctx);
-  SHA512_Update(&ctx, data, len);
-  SHA512_Final(out, &ctx);
+  SHA512_256_Update(&ctx, data, len);
+  SHA512_256_Final(out, &ctx);
   OPENSSL_cleanse(&ctx, sizeof(ctx));
   return out;
 }
@@ -160,8 +162,9 @@
 
 int SHA384_Final(uint8_t out[SHA384_DIGEST_LENGTH], SHA512_CTX *sha) {
   // |SHA384_Init| sets |sha->md_len| to |SHA384_DIGEST_LENGTH|, so this has a
-  // |smaller output.
-  return SHA512_Final(out, sha);
+  // smaller output.
+  assert(sha->md_len == SHA384_DIGEST_LENGTH);
+  return sha512_final_impl(out, sha);
 }
 
 int SHA384_Update(SHA512_CTX *sha, const void *data, size_t len) {
@@ -172,11 +175,11 @@
   return SHA512_Update(sha, data, len);
 }
 
-int SHA512_256_Final(uint8_t out[SHA512_256_DIGEST_LENGTH],
-                                    SHA512_CTX *sha) {
+int SHA512_256_Final(uint8_t out[SHA512_256_DIGEST_LENGTH], SHA512_CTX *sha) {
   // |SHA512_256_Init| sets |sha->md_len| to |SHA512_256_DIGEST_LENGTH|, so this
   // has a |smaller output.
-  return SHA512_Final(out, sha);
+  assert(sha->md_len == SHA512_256_DIGEST_LENGTH);
+  return sha512_final_impl(out, sha);
 }
 
 void SHA512_Transform(SHA512_CTX *c, const uint8_t block[SHA512_CBLOCK]) {
@@ -232,6 +235,15 @@
 }
 
 int SHA512_Final(uint8_t out[SHA512_DIGEST_LENGTH], SHA512_CTX *sha) {
+  // Ideally we would assert |sha->md_len| is |SHA512_DIGEST_LENGTH| to match
+  // the size hint, but calling code often pairs |SHA384_Init| with
+  // |SHA512_Final| and expects |sha->md_len| to carry the size over.
+  //
+  // TODO(davidben): Add an assert and fix code to match them up.
+  return sha512_final_impl(out, sha);
+}
+
+static int sha512_final_impl(uint8_t *out, SHA512_CTX *sha) {
   uint8_t *p = sha->p;
   size_t n = sha->num;
 
@@ -244,22 +256,8 @@
   }
 
   OPENSSL_memset(p + n, 0, sizeof(sha->p) - 16 - n);
-  p[sizeof(sha->p) - 1] = (uint8_t)(sha->Nl);
-  p[sizeof(sha->p) - 2] = (uint8_t)(sha->Nl >> 8);
-  p[sizeof(sha->p) - 3] = (uint8_t)(sha->Nl >> 16);
-  p[sizeof(sha->p) - 4] = (uint8_t)(sha->Nl >> 24);
-  p[sizeof(sha->p) - 5] = (uint8_t)(sha->Nl >> 32);
-  p[sizeof(sha->p) - 6] = (uint8_t)(sha->Nl >> 40);
-  p[sizeof(sha->p) - 7] = (uint8_t)(sha->Nl >> 48);
-  p[sizeof(sha->p) - 8] = (uint8_t)(sha->Nl >> 56);
-  p[sizeof(sha->p) - 9] = (uint8_t)(sha->Nh);
-  p[sizeof(sha->p) - 10] = (uint8_t)(sha->Nh >> 8);
-  p[sizeof(sha->p) - 11] = (uint8_t)(sha->Nh >> 16);
-  p[sizeof(sha->p) - 12] = (uint8_t)(sha->Nh >> 24);
-  p[sizeof(sha->p) - 13] = (uint8_t)(sha->Nh >> 32);
-  p[sizeof(sha->p) - 14] = (uint8_t)(sha->Nh >> 40);
-  p[sizeof(sha->p) - 15] = (uint8_t)(sha->Nh >> 48);
-  p[sizeof(sha->p) - 16] = (uint8_t)(sha->Nh >> 56);
+  CRYPTO_store_u64_be(p + sizeof(sha->p) - 16, sha->Nh);
+  CRYPTO_store_u64_be(p + sizeof(sha->p) - 8, sha->Nl);
 
   sha512_block_data_order(sha->h, p, 1);
 
@@ -272,9 +270,8 @@
   assert(sha->md_len % 8 == 0);
   const size_t out_words = sha->md_len / 8;
   for (size_t i = 0; i < out_words; i++) {
-    const uint64_t t = CRYPTO_bswap8(sha->h[i]);
-    memcpy(out, &t, sizeof(t));
-    out += sizeof(t);
+    CRYPTO_store_u64_be(out, sha->h[i]);
+    out += 8;
   }
 
   return 1;
@@ -356,12 +353,6 @@
 #define ROTR(x, s) (((x) >> s) | (x) << (64 - s))
 #endif
 
-static inline uint64_t load_u64_be(const void *ptr) {
-  uint64_t ret;
-  OPENSSL_memcpy(&ret, ptr, sizeof(ret));
-  return CRYPTO_bswap8(ret);
-}
-
 #define Sigma0(x) (ROTR((x), 28) ^ ROTR((x), 34) ^ ROTR((x), 39))
 #define Sigma1(x) (ROTR((x), 14) ^ ROTR((x), 18) ^ ROTR((x), 41))
 #define sigma0(x) (ROTR((x), 1) ^ ROTR((x), 8) ^ ((x) >> 7))
@@ -392,7 +383,7 @@
     F[7] = state[7];
 
     for (i = 0; i < 16; i++, F--) {
-      T = load_u64_be(in + i * 8);
+      T = CRYPTO_load_u64_be(in + i * 8);
       F[0] = A;
       F[4] = E;
       F[8] = T;
@@ -464,37 +455,37 @@
     g = state[6];
     h = state[7];
 
-    T1 = X[0] = load_u64_be(in);
+    T1 = X[0] = CRYPTO_load_u64_be(in);
     ROUND_00_15(0, a, b, c, d, e, f, g, h);
-    T1 = X[1] = load_u64_be(in + 8);
+    T1 = X[1] = CRYPTO_load_u64_be(in + 8);
     ROUND_00_15(1, h, a, b, c, d, e, f, g);
-    T1 = X[2] = load_u64_be(in + 2 * 8);
+    T1 = X[2] = CRYPTO_load_u64_be(in + 2 * 8);
     ROUND_00_15(2, g, h, a, b, c, d, e, f);
-    T1 = X[3] = load_u64_be(in + 3 * 8);
+    T1 = X[3] = CRYPTO_load_u64_be(in + 3 * 8);
     ROUND_00_15(3, f, g, h, a, b, c, d, e);
-    T1 = X[4] = load_u64_be(in + 4 * 8);
+    T1 = X[4] = CRYPTO_load_u64_be(in + 4 * 8);
     ROUND_00_15(4, e, f, g, h, a, b, c, d);
-    T1 = X[5] = load_u64_be(in + 5 * 8);
+    T1 = X[5] = CRYPTO_load_u64_be(in + 5 * 8);
     ROUND_00_15(5, d, e, f, g, h, a, b, c);
-    T1 = X[6] = load_u64_be(in + 6 * 8);
+    T1 = X[6] = CRYPTO_load_u64_be(in + 6 * 8);
     ROUND_00_15(6, c, d, e, f, g, h, a, b);
-    T1 = X[7] = load_u64_be(in + 7 * 8);
+    T1 = X[7] = CRYPTO_load_u64_be(in + 7 * 8);
     ROUND_00_15(7, b, c, d, e, f, g, h, a);
-    T1 = X[8] = load_u64_be(in + 8 * 8);
+    T1 = X[8] = CRYPTO_load_u64_be(in + 8 * 8);
     ROUND_00_15(8, a, b, c, d, e, f, g, h);
-    T1 = X[9] = load_u64_be(in + 9 * 8);
+    T1 = X[9] = CRYPTO_load_u64_be(in + 9 * 8);
     ROUND_00_15(9, h, a, b, c, d, e, f, g);
-    T1 = X[10] = load_u64_be(in + 10 * 8);
+    T1 = X[10] = CRYPTO_load_u64_be(in + 10 * 8);
     ROUND_00_15(10, g, h, a, b, c, d, e, f);
-    T1 = X[11] = load_u64_be(in + 11 * 8);
+    T1 = X[11] = CRYPTO_load_u64_be(in + 11 * 8);
     ROUND_00_15(11, f, g, h, a, b, c, d, e);
-    T1 = X[12] = load_u64_be(in + 12 * 8);
+    T1 = X[12] = CRYPTO_load_u64_be(in + 12 * 8);
     ROUND_00_15(12, e, f, g, h, a, b, c, d);
-    T1 = X[13] = load_u64_be(in + 13 * 8);
+    T1 = X[13] = CRYPTO_load_u64_be(in + 13 * 8);
     ROUND_00_15(13, d, e, f, g, h, a, b, c);
-    T1 = X[14] = load_u64_be(in + 14 * 8);
+    T1 = X[14] = CRYPTO_load_u64_be(in + 14 * 8);
     ROUND_00_15(14, c, d, e, f, g, h, a, b);
-    T1 = X[15] = load_u64_be(in + 15 * 8);
+    T1 = X[15] = CRYPTO_load_u64_be(in + 15 * 8);
     ROUND_00_15(15, b, c, d, e, f, g, h, a);
 
     for (i = 16; i < 80; i += 16) {
diff --git a/deps/boringssl/src/crypto/hpke/hpke.c b/deps/boringssl/src/crypto/hpke/hpke.c
index ee03e53..222e329 100644
--- a/deps/boringssl/src/crypto/hpke/hpke.c
+++ b/deps/boringssl/src/crypto/hpke/hpke.c
@@ -12,70 +12,77 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+#include <openssl/hpke.h>
+
 #include <assert.h>
 #include <string.h>
 
 #include <openssl/aead.h>
 #include <openssl/bytestring.h>
+#include <openssl/curve25519.h>
 #include <openssl/digest.h>
 #include <openssl/err.h>
-#include <openssl/evp.h>
+#include <openssl/evp_errors.h>
 #include <openssl/hkdf.h>
+#include <openssl/rand.h>
 #include <openssl/sha.h>
 
 #include "../internal.h"
-#include "internal.h"
 
 
-// This file implements draft-irtf-cfrg-hpke-07.
+// This file implements draft-irtf-cfrg-hpke-08.
 
-#define KEM_CONTEXT_LEN (2 * X25519_PUBLIC_VALUE_LEN)
+#define MAX_SEED_LEN X25519_PRIVATE_KEY_LEN
+#define MAX_SHARED_SECRET_LEN SHA256_DIGEST_LENGTH
 
-// HPKE KEM scheme IDs.
-#define HPKE_DHKEM_X25519_HKDF_SHA256 0x0020
+struct evp_hpke_kem_st {
+  uint16_t id;
+  size_t public_key_len;
+  size_t private_key_len;
+  size_t seed_len;
+  int (*init_key)(EVP_HPKE_KEY *key, const uint8_t *priv_key,
+                  size_t priv_key_len);
+  int (*generate_key)(EVP_HPKE_KEY *key);
+  int (*encap_with_seed)(const EVP_HPKE_KEM *kem, uint8_t *out_shared_secret,
+                         size_t *out_shared_secret_len, uint8_t *out_enc,
+                         size_t *out_enc_len, size_t max_enc,
+                         const uint8_t *peer_public_key,
+                         size_t peer_public_key_len, const uint8_t *seed,
+                         size_t seed_len);
+  int (*decap)(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret,
+               size_t *out_shared_secret_len, const uint8_t *enc,
+               size_t enc_len);
+};
 
-// This is strlen("HPKE") + 3 * sizeof(uint16_t).
-#define HPKE_SUITE_ID_LEN 10
+struct evp_hpke_kdf_st {
+  uint16_t id;
+  // We only support HKDF-based KDFs.
+  const EVP_MD *(*hkdf_md_func)(void);
+};
 
-#define HPKE_MODE_BASE 0
-#define HPKE_MODE_PSK 1
+struct evp_hpke_aead_st {
+  uint16_t id;
+  const EVP_AEAD *(*aead_func)(void);
+};
 
-static const char kHpkeRfcId[] = "HPKE-07";
+
+// Low-level labeled KDF functions.
+
+static const char kHpkeVersionId[] = "HPKE-v1";
 
 static int add_label_string(CBB *cbb, const char *label) {
   return CBB_add_bytes(cbb, (const uint8_t *)label, strlen(label));
 }
 
-// The suite_id for the KEM is defined as concat("KEM", I2OSP(kem_id, 2)). Note
-// that the suite_id used outside of the KEM also includes the kdf_id and
-// aead_id.
-static const uint8_t kX25519SuiteID[] = {
-    'K', 'E', 'M', HPKE_DHKEM_X25519_HKDF_SHA256 >> 8,
-    HPKE_DHKEM_X25519_HKDF_SHA256 & 0x00ff};
-
-// The suite_id for non-KEM pieces of HPKE is defined as concat("HPKE",
-// I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)).
-static int hpke_build_suite_id(uint8_t out[HPKE_SUITE_ID_LEN], uint16_t kdf_id,
-                               uint16_t aead_id) {
-  CBB cbb;
-  int ret = CBB_init_fixed(&cbb, out, HPKE_SUITE_ID_LEN) &&
-            add_label_string(&cbb, "HPKE") &&
-            CBB_add_u16(&cbb, HPKE_DHKEM_X25519_HKDF_SHA256) &&
-            CBB_add_u16(&cbb, kdf_id) &&
-            CBB_add_u16(&cbb, aead_id);
-  CBB_cleanup(&cbb);
-  return ret;
-}
-
 static int hpke_labeled_extract(const EVP_MD *hkdf_md, uint8_t *out_key,
                                 size_t *out_len, const uint8_t *salt,
                                 size_t salt_len, const uint8_t *suite_id,
                                 size_t suite_id_len, const char *label,
                                 const uint8_t *ikm, size_t ikm_len) {
-  // labeledIKM = concat("RFCXXXX ", suite_id, label, IKM)
+  // labeledIKM = concat("HPKE-v1", suite_id, label, IKM)
   CBB labeled_ikm;
   int ok = CBB_init(&labeled_ikm, 0) &&
-           add_label_string(&labeled_ikm, kHpkeRfcId) &&
+           add_label_string(&labeled_ikm, kHpkeVersionId) &&
            CBB_add_bytes(&labeled_ikm, suite_id, suite_id_len) &&
            add_label_string(&labeled_ikm, label) &&
            CBB_add_bytes(&labeled_ikm, ikm, ikm_len) &&
@@ -90,11 +97,11 @@
                                size_t prk_len, const uint8_t *suite_id,
                                size_t suite_id_len, const char *label,
                                const uint8_t *info, size_t info_len) {
-  // labeledInfo = concat(I2OSP(L, 2), "RFCXXXX ", suite_id, label, info)
+  // labeledInfo = concat(I2OSP(L, 2), "HPKE-v1", suite_id, label, info)
   CBB labeled_info;
   int ok = CBB_init(&labeled_info, 0) &&
            CBB_add_u16(&labeled_info, out_len) &&
-           add_label_string(&labeled_info, kHpkeRfcId) &&
+           add_label_string(&labeled_info, kHpkeVersionId) &&
            CBB_add_bytes(&labeled_info, suite_id, suite_id_len) &&
            add_label_string(&labeled_info, label) &&
            CBB_add_bytes(&labeled_info, info, info_len) &&
@@ -104,102 +111,280 @@
   return ok;
 }
 
-static int hpke_extract_and_expand(const EVP_MD *hkdf_md, uint8_t *out_key,
-                                   size_t out_len,
-                                   const uint8_t dh[X25519_PUBLIC_VALUE_LEN],
-                                   const uint8_t kem_context[KEM_CONTEXT_LEN]) {
+
+// KEM implementations.
+
+// dhkem_extract_and_expand implements the ExtractAndExpand operation in the
+// DHKEM construction. See section 4.1 of draft-irtf-cfrg-hpke-08.
+static int dhkem_extract_and_expand(uint16_t kem_id, const EVP_MD *hkdf_md,
+                                    uint8_t *out_key, size_t out_len,
+                                    const uint8_t *dh, size_t dh_len,
+                                    const uint8_t *kem_context,
+                                    size_t kem_context_len) {
+  // concat("KEM", I2OSP(kem_id, 2))
+  uint8_t suite_id[5] = {'K', 'E', 'M', kem_id >> 8, kem_id & 0xff};
   uint8_t prk[EVP_MAX_MD_SIZE];
   size_t prk_len;
-  static const char kEaePrkLabel[] = "eae_prk";
-  if (!hpke_labeled_extract(hkdf_md, prk, &prk_len, NULL, 0, kX25519SuiteID,
-                            sizeof(kX25519SuiteID), kEaePrkLabel, dh,
-                            X25519_PUBLIC_VALUE_LEN)) {
+  return hpke_labeled_extract(hkdf_md, prk, &prk_len, NULL, 0, suite_id,
+                              sizeof(suite_id), "eae_prk", dh, dh_len) &&
+         hpke_labeled_expand(hkdf_md, out_key, out_len, prk, prk_len, suite_id,
+                             sizeof(suite_id), "shared_secret", kem_context,
+                             kem_context_len);
+}
+
+static int x25519_init_key(EVP_HPKE_KEY *key, const uint8_t *priv_key,
+                           size_t priv_key_len) {
+  if (priv_key_len != X25519_PRIVATE_KEY_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     return 0;
   }
-  static const char kPRKExpandLabel[] = "shared_secret";
-  if (!hpke_labeled_expand(hkdf_md, out_key, out_len, prk, prk_len,
-                           kX25519SuiteID, sizeof(kX25519SuiteID),
-                           kPRKExpandLabel, kem_context, KEM_CONTEXT_LEN)) {
+
+  OPENSSL_memcpy(key->private_key, priv_key, priv_key_len);
+  X25519_public_from_private(key->public_key, priv_key);
+  return 1;
+}
+
+static int x25519_generate_key(EVP_HPKE_KEY *key) {
+  X25519_keypair(key->public_key, key->private_key);
+  return 1;
+}
+
+static int x25519_encap_with_seed(
+    const EVP_HPKE_KEM *kem, uint8_t *out_shared_secret,
+    size_t *out_shared_secret_len, uint8_t *out_enc, size_t *out_enc_len,
+    size_t max_enc, const uint8_t *peer_public_key, size_t peer_public_key_len,
+    const uint8_t *seed, size_t seed_len) {
+  if (max_enc < X25519_PUBLIC_VALUE_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
+    return 0;
+  }
+  if (seed_len != X25519_PRIVATE_KEY_LEN) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+    return 0;
+  }
+  X25519_public_from_private(out_enc, seed);
+
+  uint8_t dh[X25519_SHARED_KEY_LEN];
+  if (peer_public_key_len != X25519_PUBLIC_VALUE_LEN ||
+      !X25519(dh, seed, peer_public_key)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
+    return 0;
+  }
+
+  uint8_t kem_context[2 * X25519_PUBLIC_VALUE_LEN];
+  OPENSSL_memcpy(kem_context, out_enc, X25519_PUBLIC_VALUE_LEN);
+  OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, peer_public_key,
+                 X25519_PUBLIC_VALUE_LEN);
+  if (!dhkem_extract_and_expand(kem->id, EVP_sha256(), out_shared_secret,
+                                SHA256_DIGEST_LENGTH, dh, sizeof(dh),
+                                kem_context, sizeof(kem_context))) {
+    return 0;
+  }
+
+  *out_enc_len = X25519_PUBLIC_VALUE_LEN;
+  *out_shared_secret_len = SHA256_DIGEST_LENGTH;
+  return 1;
+}
+
+static int x25519_decap(const EVP_HPKE_KEY *key, uint8_t *out_shared_secret,
+                        size_t *out_shared_secret_len, const uint8_t *enc,
+                        size_t enc_len) {
+  uint8_t dh[X25519_SHARED_KEY_LEN];
+  if (enc_len != X25519_PUBLIC_VALUE_LEN ||
+      !X25519(dh, key->private_key, enc)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
+    return 0;
+  }
+
+  uint8_t kem_context[2 * X25519_PUBLIC_VALUE_LEN];
+  OPENSSL_memcpy(kem_context, enc, X25519_PUBLIC_VALUE_LEN);
+  OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, key->public_key,
+                 X25519_PUBLIC_VALUE_LEN);
+  if (!dhkem_extract_and_expand(key->kem->id, EVP_sha256(), out_shared_secret,
+                                SHA256_DIGEST_LENGTH, dh, sizeof(dh),
+                                kem_context, sizeof(kem_context))) {
+    return 0;
+  }
+
+  *out_shared_secret_len = SHA256_DIGEST_LENGTH;
+  return 1;
+}
+
+const EVP_HPKE_KEM *EVP_hpke_x25519_hkdf_sha256(void) {
+  static const EVP_HPKE_KEM kKEM = {
+      /*id=*/EVP_HPKE_DHKEM_X25519_HKDF_SHA256,
+      /*public_key_len=*/X25519_PUBLIC_VALUE_LEN,
+      /*private_key_len=*/X25519_PRIVATE_KEY_LEN,
+      /*seed_len=*/X25519_PRIVATE_KEY_LEN,
+      x25519_init_key,
+      x25519_generate_key,
+      x25519_encap_with_seed,
+      x25519_decap,
+  };
+  return &kKEM;
+}
+
+uint16_t EVP_HPKE_KEM_id(const EVP_HPKE_KEM *kem) { return kem->id; }
+
+void EVP_HPKE_KEY_zero(EVP_HPKE_KEY *key) {
+  OPENSSL_memset(key, 0, sizeof(EVP_HPKE_KEY));
+}
+
+void EVP_HPKE_KEY_cleanup(EVP_HPKE_KEY *key) {
+  // Nothing to clean up for now, but we may introduce a cleanup process in the
+  // future.
+}
+
+EVP_HPKE_KEY *EVP_HPKE_KEY_new(void) {
+  EVP_HPKE_KEY *key = OPENSSL_malloc(sizeof(EVP_HPKE_KEY));
+  if (key == NULL) {
+    OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  EVP_HPKE_KEY_zero(key);
+  return key;
+}
+
+void EVP_HPKE_KEY_free(EVP_HPKE_KEY *key) {
+  if (key != NULL) {
+    EVP_HPKE_KEY_cleanup(key);
+    OPENSSL_free(key);
+  }
+}
+
+int EVP_HPKE_KEY_copy(EVP_HPKE_KEY *dst, const EVP_HPKE_KEY *src) {
+  // For now, |EVP_HPKE_KEY| is trivially copyable.
+  OPENSSL_memcpy(dst, src, sizeof(EVP_HPKE_KEY));
+  return 1;
+}
+
+int EVP_HPKE_KEY_init(EVP_HPKE_KEY *key, const EVP_HPKE_KEM *kem,
+                      const uint8_t *priv_key, size_t priv_key_len) {
+  EVP_HPKE_KEY_zero(key);
+  key->kem = kem;
+  if (!kem->init_key(key, priv_key, priv_key_len)) {
+    key->kem = NULL;
     return 0;
   }
   return 1;
 }
 
-const EVP_AEAD *EVP_HPKE_get_aead(uint16_t aead_id) {
-  switch (aead_id) {
-    case EVP_HPKE_AEAD_AES_GCM_128:
-      return EVP_aead_aes_128_gcm();
-    case EVP_HPKE_AEAD_AES_GCM_256:
-      return EVP_aead_aes_256_gcm();
-    case EVP_HPKE_AEAD_CHACHA20POLY1305:
-      return EVP_aead_chacha20_poly1305();
-  }
-  OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
-  return NULL;
-}
-
-const EVP_MD *EVP_HPKE_get_hkdf_md(uint16_t kdf_id) {
-  switch (kdf_id) {
-    case EVP_HPKE_HKDF_SHA256:
-      return EVP_sha256();
-    case EVP_HPKE_HKDF_SHA384:
-      return EVP_sha384();
-    case EVP_HPKE_HKDF_SHA512:
-      return EVP_sha512();
-  }
-  OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
-  return NULL;
-}
-
-static int hpke_key_schedule(EVP_HPKE_CTX *hpke, uint8_t mode,
-                             const uint8_t *shared_secret,
-                             size_t shared_secret_len, const uint8_t *info,
-                             size_t info_len, const uint8_t *psk,
-                             size_t psk_len, const uint8_t *psk_id,
-                             size_t psk_id_len) {
-  // Verify the PSK inputs.
-  switch (mode) {
-    case HPKE_MODE_BASE:
-      // This is an internal error, unreachable from the caller.
-      assert(psk_len == 0 && psk_id_len == 0);
-      break;
-    case HPKE_MODE_PSK:
-      if (psk_len == 0 || psk_id_len == 0) {
-        OPENSSL_PUT_ERROR(EVP, EVP_R_EMPTY_PSK);
-        return 0;
-      }
-      break;
-    default:
-      return 0;
-  }
-
-  // Attempt to get an EVP_AEAD*.
-  const EVP_AEAD *aead = EVP_HPKE_get_aead(hpke->aead_id);
-  if (aead == NULL) {
+int EVP_HPKE_KEY_generate(EVP_HPKE_KEY *key, const EVP_HPKE_KEM *kem) {
+  EVP_HPKE_KEY_zero(key);
+  key->kem = kem;
+  if (!kem->generate_key(key)) {
+    key->kem = NULL;
     return 0;
   }
+  return 1;
+}
 
+const EVP_HPKE_KEM *EVP_HPKE_KEY_kem(const EVP_HPKE_KEY *key) {
+  return key->kem;
+}
+
+int EVP_HPKE_KEY_public_key(const EVP_HPKE_KEY *key, uint8_t *out,
+                            size_t *out_len, size_t max_out) {
+  if (max_out < key->kem->public_key_len) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
+    return 0;
+  }
+  OPENSSL_memcpy(out, key->public_key, key->kem->public_key_len);
+  *out_len = key->kem->public_key_len;
+  return 1;
+}
+
+int EVP_HPKE_KEY_private_key(const EVP_HPKE_KEY *key, uint8_t *out,
+                            size_t *out_len, size_t max_out) {
+  if (max_out < key->kem->private_key_len) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_BUFFER_SIZE);
+    return 0;
+  }
+  OPENSSL_memcpy(out, key->private_key, key->kem->private_key_len);
+  *out_len = key->kem->private_key_len;
+  return 1;
+}
+
+
+// Supported KDFs and AEADs.
+
+const EVP_HPKE_KDF *EVP_hpke_hkdf_sha256(void) {
+  static const EVP_HPKE_KDF kKDF = {EVP_HPKE_HKDF_SHA256, &EVP_sha256};
+  return &kKDF;
+}
+
+uint16_t EVP_HPKE_KDF_id(const EVP_HPKE_KDF *kdf) { return kdf->id; }
+
+const EVP_HPKE_AEAD *EVP_hpke_aes_128_gcm(void) {
+  static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_AES_128_GCM,
+                                      &EVP_aead_aes_128_gcm};
+  return &kAEAD;
+}
+
+const EVP_HPKE_AEAD *EVP_hpke_aes_256_gcm(void) {
+  static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_AES_256_GCM,
+                                      &EVP_aead_aes_256_gcm};
+  return &kAEAD;
+}
+
+const EVP_HPKE_AEAD *EVP_hpke_chacha20_poly1305(void) {
+  static const EVP_HPKE_AEAD kAEAD = {EVP_HPKE_CHACHA20_POLY1305,
+                                      &EVP_aead_chacha20_poly1305};
+  return &kAEAD;
+}
+
+uint16_t EVP_HPKE_AEAD_id(const EVP_HPKE_AEAD *aead) { return aead->id; }
+
+const EVP_AEAD *EVP_HPKE_AEAD_aead(const EVP_HPKE_AEAD *aead) {
+  return aead->aead_func();
+}
+
+
+// HPKE implementation.
+
+// This is strlen("HPKE") + 3 * sizeof(uint16_t).
+#define HPKE_SUITE_ID_LEN 10
+
+// The suite_id for non-KEM pieces of HPKE is defined as concat("HPKE",
+// I2OSP(kem_id, 2), I2OSP(kdf_id, 2), I2OSP(aead_id, 2)).
+static int hpke_build_suite_id(const EVP_HPKE_CTX *ctx,
+                               uint8_t out[HPKE_SUITE_ID_LEN]) {
+  CBB cbb;
+  int ret = CBB_init_fixed(&cbb, out, HPKE_SUITE_ID_LEN) &&
+            add_label_string(&cbb, "HPKE") &&
+            CBB_add_u16(&cbb, EVP_HPKE_DHKEM_X25519_HKDF_SHA256) &&
+            CBB_add_u16(&cbb, ctx->kdf->id) &&
+            CBB_add_u16(&cbb, ctx->aead->id);
+  CBB_cleanup(&cbb);
+  return ret;
+}
+
+#define HPKE_MODE_BASE 0
+
+static int hpke_key_schedule(EVP_HPKE_CTX *ctx, const uint8_t *shared_secret,
+                             size_t shared_secret_len, const uint8_t *info,
+                             size_t info_len) {
   uint8_t suite_id[HPKE_SUITE_ID_LEN];
-  if (!hpke_build_suite_id(suite_id, hpke->kdf_id, hpke->aead_id)) {
+  if (!hpke_build_suite_id(ctx, suite_id)) {
     return 0;
   }
 
   // psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id)
-  static const char kPskIdHashLabel[] = "psk_id_hash";
+  // TODO(davidben): Precompute this value and store it with the EVP_HPKE_KDF.
+  const EVP_MD *hkdf_md = ctx->kdf->hkdf_md_func();
   uint8_t psk_id_hash[EVP_MAX_MD_SIZE];
   size_t psk_id_hash_len;
-  if (!hpke_labeled_extract(hpke->hkdf_md, psk_id_hash, &psk_id_hash_len, NULL,
-                            0, suite_id, sizeof(suite_id), kPskIdHashLabel,
-                            psk_id, psk_id_len)) {
+  if (!hpke_labeled_extract(hkdf_md, psk_id_hash, &psk_id_hash_len, NULL, 0,
+                            suite_id, sizeof(suite_id), "psk_id_hash", NULL,
+                            0)) {
     return 0;
   }
 
   // info_hash = LabeledExtract("", "info_hash", info)
-  static const char kInfoHashLabel[] = "info_hash";
   uint8_t info_hash[EVP_MAX_MD_SIZE];
   size_t info_hash_len;
-  if (!hpke_labeled_extract(hpke->hkdf_md, info_hash, &info_hash_len, NULL, 0,
-                            suite_id, sizeof(suite_id), kInfoHashLabel, info,
+  if (!hpke_labeled_extract(hkdf_md, info_hash, &info_hash_len, NULL, 0,
+                            suite_id, sizeof(suite_id), "info_hash", info,
                             info_len)) {
     return 0;
   }
@@ -209,7 +394,7 @@
   size_t context_len;
   CBB context_cbb;
   if (!CBB_init_fixed(&context_cbb, context, sizeof(context)) ||
-      !CBB_add_u8(&context_cbb, mode) ||
+      !CBB_add_u8(&context_cbb, HPKE_MODE_BASE) ||
       !CBB_add_bytes(&context_cbb, psk_id_hash, psk_id_hash_len) ||
       !CBB_add_bytes(&context_cbb, info_hash, info_hash_len) ||
       !CBB_finish(&context_cbb, NULL, &context_len)) {
@@ -217,97 +402,44 @@
   }
 
   // secret = LabeledExtract(shared_secret, "secret", psk)
-  static const char kSecretExtractLabel[] = "secret";
   uint8_t secret[EVP_MAX_MD_SIZE];
   size_t secret_len;
-  if (!hpke_labeled_extract(hpke->hkdf_md, secret, &secret_len, shared_secret,
+  if (!hpke_labeled_extract(hkdf_md, secret, &secret_len, shared_secret,
                             shared_secret_len, suite_id, sizeof(suite_id),
-                            kSecretExtractLabel, psk, psk_len)) {
+                            "secret", NULL, 0)) {
     return 0;
   }
 
   // key = LabeledExpand(secret, "key", key_schedule_context, Nk)
-  static const char kKeyExpandLabel[] = "key";
+  const EVP_AEAD *aead = EVP_HPKE_AEAD_aead(ctx->aead);
   uint8_t key[EVP_AEAD_MAX_KEY_LENGTH];
   const size_t kKeyLen = EVP_AEAD_key_length(aead);
-  if (!hpke_labeled_expand(hpke->hkdf_md, key, kKeyLen, secret, secret_len,
-                           suite_id, sizeof(suite_id), kKeyExpandLabel, context,
-                           context_len)) {
-    return 0;
-  }
-
-  // Initialize the HPKE context's AEAD context, storing a copy of |key|.
-  if (!EVP_AEAD_CTX_init(&hpke->aead_ctx, aead, key, kKeyLen, 0, NULL)) {
+  if (!hpke_labeled_expand(hkdf_md, key, kKeyLen, secret, secret_len, suite_id,
+                           sizeof(suite_id), "key", context, context_len) ||
+      !EVP_AEAD_CTX_init(&ctx->aead_ctx, aead, key, kKeyLen,
+                         EVP_AEAD_DEFAULT_TAG_LENGTH, NULL)) {
     return 0;
   }
 
   // base_nonce = LabeledExpand(secret, "base_nonce", key_schedule_context, Nn)
-  static const char kNonceExpandLabel[] = "base_nonce";
-  if (!hpke_labeled_expand(hpke->hkdf_md, hpke->base_nonce,
+  if (!hpke_labeled_expand(hkdf_md, ctx->base_nonce,
                            EVP_AEAD_nonce_length(aead), secret, secret_len,
-                           suite_id, sizeof(suite_id), kNonceExpandLabel,
-                           context, context_len)) {
+                           suite_id, sizeof(suite_id), "base_nonce", context,
+                           context_len)) {
     return 0;
   }
 
   // exporter_secret = LabeledExpand(secret, "exp", key_schedule_context, Nh)
-  static const char kExporterSecretExpandLabel[] = "exp";
-  if (!hpke_labeled_expand(hpke->hkdf_md, hpke->exporter_secret,
-                           EVP_MD_size(hpke->hkdf_md), secret, secret_len,
-                           suite_id, sizeof(suite_id),
-                           kExporterSecretExpandLabel, context, context_len)) {
+  if (!hpke_labeled_expand(hkdf_md, ctx->exporter_secret, EVP_MD_size(hkdf_md),
+                           secret, secret_len, suite_id, sizeof(suite_id),
+                           "exp", context, context_len)) {
     return 0;
   }
 
   return 1;
 }
 
-// The number of bytes written to |out_shared_secret| is the size of the KEM's
-// KDF (currently we only support SHA256).
-static int hpke_encap(EVP_HPKE_CTX *hpke,
-                      uint8_t out_shared_secret[SHA256_DIGEST_LENGTH],
-                      const uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN],
-                      const uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN],
-                      const uint8_t ephemeral_public[X25519_PUBLIC_VALUE_LEN]) {
-  uint8_t dh[X25519_PUBLIC_VALUE_LEN];
-  if (!X25519(dh, ephemeral_private, public_key_r)) {
-    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
-    return 0;
-  }
-
-  uint8_t kem_context[KEM_CONTEXT_LEN];
-  OPENSSL_memcpy(kem_context, ephemeral_public, X25519_PUBLIC_VALUE_LEN);
-  OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, public_key_r,
-                 X25519_PUBLIC_VALUE_LEN);
-  if (!hpke_extract_and_expand(EVP_sha256(), out_shared_secret,
-                               SHA256_DIGEST_LENGTH, dh, kem_context)) {
-    return 0;
-  }
-  return 1;
-}
-
-static int hpke_decap(const EVP_HPKE_CTX *hpke,
-                      uint8_t out_shared_secret[SHA256_DIGEST_LENGTH],
-                      const uint8_t enc[X25519_PUBLIC_VALUE_LEN],
-                      const uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN],
-                      const uint8_t secret_key_r[X25519_PRIVATE_KEY_LEN]) {
-  uint8_t dh[X25519_PUBLIC_VALUE_LEN];
-  if (!X25519(dh, secret_key_r, enc)) {
-    OPENSSL_PUT_ERROR(EVP, EVP_R_INVALID_PEER_KEY);
-    return 0;
-  }
-  uint8_t kem_context[KEM_CONTEXT_LEN];
-  OPENSSL_memcpy(kem_context, enc, X25519_PUBLIC_VALUE_LEN);
-  OPENSSL_memcpy(kem_context + X25519_PUBLIC_VALUE_LEN, public_key_r,
-                 X25519_PUBLIC_VALUE_LEN);
-  if (!hpke_extract_and_expand(EVP_sha256(), out_shared_secret,
-                               SHA256_DIGEST_LENGTH, dh, kem_context)) {
-    return 0;
-  }
-  return 1;
-}
-
-void EVP_HPKE_CTX_init(EVP_HPKE_CTX *ctx) {
+void EVP_HPKE_CTX_zero(EVP_HPKE_CTX *ctx) {
   OPENSSL_memset(ctx, 0, sizeof(EVP_HPKE_CTX));
   EVP_AEAD_CTX_zero(&ctx->aead_ctx);
 }
@@ -316,217 +448,171 @@
   EVP_AEAD_CTX_cleanup(&ctx->aead_ctx);
 }
 
-int EVP_HPKE_CTX_setup_base_s_x25519(
-    EVP_HPKE_CTX *hpke, uint8_t out_enc[X25519_PUBLIC_VALUE_LEN],
-    uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len) {
-  // The GenerateKeyPair() step technically belongs in the KEM's Encap()
-  // function, but we've moved it up a layer to make it easier for tests to
-  // inject an ephemeral keypair.
-  uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN];
-  X25519_keypair(out_enc, ephemeral_private);
-  return EVP_HPKE_CTX_setup_base_s_x25519_for_test(
-      hpke, kdf_id, aead_id, peer_public_value, info, info_len,
-      ephemeral_private, out_enc);
+EVP_HPKE_CTX *EVP_HPKE_CTX_new(void) {
+  EVP_HPKE_CTX *ctx = OPENSSL_malloc(sizeof(EVP_HPKE_CTX));
+  if (ctx == NULL) {
+    OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
+    return NULL;
+  }
+  EVP_HPKE_CTX_zero(ctx);
+  return ctx;
 }
 
-int EVP_HPKE_CTX_setup_base_s_x25519_for_test(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len,
-    const uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN],
-    const uint8_t ephemeral_public[X25519_PUBLIC_VALUE_LEN]) {
-  hpke->is_sender = 1;
-  hpke->kdf_id = kdf_id;
-  hpke->aead_id = aead_id;
-  hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id);
-  if (hpke->hkdf_md == NULL) {
-    return 0;
+void EVP_HPKE_CTX_free(EVP_HPKE_CTX *ctx) {
+  if (ctx != NULL) {
+    EVP_HPKE_CTX_cleanup(ctx);
+    OPENSSL_free(ctx);
   }
-  uint8_t shared_secret[SHA256_DIGEST_LENGTH];
-  if (!hpke_encap(hpke, shared_secret, peer_public_value, ephemeral_private,
-                  ephemeral_public) ||
-      !hpke_key_schedule(hpke, HPKE_MODE_BASE, shared_secret,
-                         sizeof(shared_secret), info, info_len, NULL, 0, NULL,
-                         0)) {
+}
+
+int EVP_HPKE_CTX_setup_sender(EVP_HPKE_CTX *ctx, uint8_t *out_enc,
+                              size_t *out_enc_len, size_t max_enc,
+                              const EVP_HPKE_KEM *kem, const EVP_HPKE_KDF *kdf,
+                              const EVP_HPKE_AEAD *aead,
+                              const uint8_t *peer_public_key,
+                              size_t peer_public_key_len, const uint8_t *info,
+                              size_t info_len) {
+  uint8_t seed[MAX_SEED_LEN];
+  RAND_bytes(seed, kem->seed_len);
+  return EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
+      ctx, out_enc, out_enc_len, max_enc, kem, kdf, aead, peer_public_key,
+      peer_public_key_len, info, info_len, seed, kem->seed_len);
+}
+
+int EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
+    EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
+    const EVP_HPKE_KEM *kem, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
+    const uint8_t *peer_public_key, size_t peer_public_key_len,
+    const uint8_t *info, size_t info_len, const uint8_t *seed,
+    size_t seed_len) {
+  EVP_HPKE_CTX_zero(ctx);
+  ctx->is_sender = 1;
+  ctx->kdf = kdf;
+  ctx->aead = aead;
+  uint8_t shared_secret[MAX_SHARED_SECRET_LEN];
+  size_t shared_secret_len;
+  if (!kem->encap_with_seed(kem, shared_secret, &shared_secret_len, out_enc,
+                            out_enc_len, max_enc, peer_public_key,
+                            peer_public_key_len, seed, seed_len) ||
+      !hpke_key_schedule(ctx, shared_secret, shared_secret_len, info,
+                         info_len)) {
+    EVP_HPKE_CTX_cleanup(ctx);
     return 0;
   }
   return 1;
 }
 
-int EVP_HPKE_CTX_setup_base_r_x25519(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t enc[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t public_key[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t private_key[X25519_PRIVATE_KEY_LEN], const uint8_t *info,
-    size_t info_len) {
-  hpke->is_sender = 0;
-  hpke->kdf_id = kdf_id;
-  hpke->aead_id = aead_id;
-  hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id);
-  if (hpke->hkdf_md == NULL) {
-    return 0;
-  }
-  uint8_t shared_secret[SHA256_DIGEST_LENGTH];
-  if (!hpke_decap(hpke, shared_secret, enc, public_key, private_key) ||
-      !hpke_key_schedule(hpke, HPKE_MODE_BASE, shared_secret,
-                         sizeof(shared_secret), info, info_len, NULL, 0, NULL,
-                         0)) {
+int EVP_HPKE_CTX_setup_recipient(EVP_HPKE_CTX *ctx, const EVP_HPKE_KEY *key,
+                                 const EVP_HPKE_KDF *kdf,
+                                 const EVP_HPKE_AEAD *aead, const uint8_t *enc,
+                                 size_t enc_len, const uint8_t *info,
+                                 size_t info_len) {
+  EVP_HPKE_CTX_zero(ctx);
+  ctx->is_sender = 0;
+  ctx->kdf = kdf;
+  ctx->aead = aead;
+  uint8_t shared_secret[MAX_SHARED_SECRET_LEN];
+  size_t shared_secret_len;
+  if (!key->kem->decap(key, shared_secret, &shared_secret_len, enc, enc_len) ||
+      !hpke_key_schedule(ctx, shared_secret, sizeof(shared_secret), info,
+                         info_len)) {
+    EVP_HPKE_CTX_cleanup(ctx);
     return 0;
   }
   return 1;
 }
 
-int EVP_HPKE_CTX_setup_psk_s_x25519(
-    EVP_HPKE_CTX *hpke, uint8_t out_enc[X25519_PUBLIC_VALUE_LEN],
-    uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len, const uint8_t *psk, size_t psk_len,
-    const uint8_t *psk_id, size_t psk_id_len) {
-  // The GenerateKeyPair() step technically belongs in the KEM's Encap()
-  // function, but we've moved it up a layer to make it easier for tests to
-  // inject an ephemeral keypair.
-  uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN];
-  X25519_keypair(out_enc, ephemeral_private);
-  return EVP_HPKE_CTX_setup_psk_s_x25519_for_test(
-      hpke, kdf_id, aead_id, peer_public_value, info, info_len, psk, psk_len,
-      psk_id, psk_id_len, ephemeral_private, out_enc);
-}
-
-int EVP_HPKE_CTX_setup_psk_s_x25519_for_test(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len, const uint8_t *psk, size_t psk_len,
-    const uint8_t *psk_id, size_t psk_id_len,
-    const uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN],
-    const uint8_t ephemeral_public[X25519_PUBLIC_VALUE_LEN]) {
-  hpke->is_sender = 1;
-  hpke->kdf_id = kdf_id;
-  hpke->aead_id = aead_id;
-  hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id);
-  if (hpke->hkdf_md == NULL) {
-    return 0;
-  }
-  uint8_t shared_secret[SHA256_DIGEST_LENGTH];
-  if (!hpke_encap(hpke, shared_secret, peer_public_value, ephemeral_private,
-                  ephemeral_public) ||
-      !hpke_key_schedule(hpke, HPKE_MODE_PSK, shared_secret,
-                         sizeof(shared_secret), info, info_len, psk, psk_len,
-                         psk_id, psk_id_len)) {
-    return 0;
-  }
-  return 1;
-}
-
-int EVP_HPKE_CTX_setup_psk_r_x25519(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t enc[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t public_key[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t private_key[X25519_PRIVATE_KEY_LEN], const uint8_t *info,
-    size_t info_len, const uint8_t *psk, size_t psk_len, const uint8_t *psk_id,
-    size_t psk_id_len) {
-  hpke->is_sender = 0;
-  hpke->kdf_id = kdf_id;
-  hpke->aead_id = aead_id;
-  hpke->hkdf_md = EVP_HPKE_get_hkdf_md(kdf_id);
-  if (hpke->hkdf_md == NULL) {
-    return 0;
-  }
-  uint8_t shared_secret[SHA256_DIGEST_LENGTH];
-  if (!hpke_decap(hpke, shared_secret, enc, public_key, private_key) ||
-      !hpke_key_schedule(hpke, HPKE_MODE_PSK, shared_secret,
-                         sizeof(shared_secret), info, info_len, psk, psk_len,
-                         psk_id, psk_id_len)) {
-    return 0;
-  }
-  return 1;
-}
-
-static void hpke_nonce(const EVP_HPKE_CTX *hpke, uint8_t *out_nonce,
+static void hpke_nonce(const EVP_HPKE_CTX *ctx, uint8_t *out_nonce,
                        size_t nonce_len) {
   assert(nonce_len >= 8);
 
-  // Write padded big-endian bytes of |hpke->seq| to |out_nonce|.
+  // Write padded big-endian bytes of |ctx->seq| to |out_nonce|.
   OPENSSL_memset(out_nonce, 0, nonce_len);
-  uint64_t seq_copy = hpke->seq;
+  uint64_t seq_copy = ctx->seq;
   for (size_t i = 0; i < 8; i++) {
     out_nonce[nonce_len - i - 1] = seq_copy & 0xff;
     seq_copy >>= 8;
   }
 
-  // XOR the encoded sequence with the |hpke->base_nonce|.
+  // XOR the encoded sequence with the |ctx->base_nonce|.
   for (size_t i = 0; i < nonce_len; i++) {
-    out_nonce[i] ^= hpke->base_nonce[i];
+    out_nonce[i] ^= ctx->base_nonce[i];
   }
 }
 
-size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *hpke) {
-  assert(hpke->is_sender);
-  return EVP_AEAD_max_overhead(hpke->aead_ctx.aead);
-}
-
-int EVP_HPKE_CTX_open(EVP_HPKE_CTX *hpke, uint8_t *out, size_t *out_len,
+int EVP_HPKE_CTX_open(EVP_HPKE_CTX *ctx, uint8_t *out, size_t *out_len,
                       size_t max_out_len, const uint8_t *in, size_t in_len,
                       const uint8_t *ad, size_t ad_len) {
-  if (hpke->is_sender) {
+  if (ctx->is_sender) {
     OPENSSL_PUT_ERROR(EVP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
-  if (hpke->seq == UINT64_MAX) {
+  if (ctx->seq == UINT64_MAX) {
     OPENSSL_PUT_ERROR(EVP, ERR_R_OVERFLOW);
     return 0;
   }
 
   uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH];
-  const size_t nonce_len = EVP_AEAD_nonce_length(hpke->aead_ctx.aead);
-  hpke_nonce(hpke, nonce, nonce_len);
+  const size_t nonce_len = EVP_AEAD_nonce_length(ctx->aead_ctx.aead);
+  hpke_nonce(ctx, nonce, nonce_len);
 
-  if (!EVP_AEAD_CTX_open(&hpke->aead_ctx, out, out_len, max_out_len, nonce,
+  if (!EVP_AEAD_CTX_open(&ctx->aead_ctx, out, out_len, max_out_len, nonce,
                          nonce_len, in, in_len, ad, ad_len)) {
     return 0;
   }
-  hpke->seq++;
+  ctx->seq++;
   return 1;
 }
 
-int EVP_HPKE_CTX_seal(EVP_HPKE_CTX *hpke, uint8_t *out, size_t *out_len,
+int EVP_HPKE_CTX_seal(EVP_HPKE_CTX *ctx, uint8_t *out, size_t *out_len,
                       size_t max_out_len, const uint8_t *in, size_t in_len,
                       const uint8_t *ad, size_t ad_len) {
-  if (!hpke->is_sender) {
+  if (!ctx->is_sender) {
     OPENSSL_PUT_ERROR(EVP, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
     return 0;
   }
-  if (hpke->seq == UINT64_MAX) {
+  if (ctx->seq == UINT64_MAX) {
     OPENSSL_PUT_ERROR(EVP, ERR_R_OVERFLOW);
     return 0;
   }
 
   uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH];
-  const size_t nonce_len = EVP_AEAD_nonce_length(hpke->aead_ctx.aead);
-  hpke_nonce(hpke, nonce, nonce_len);
+  const size_t nonce_len = EVP_AEAD_nonce_length(ctx->aead_ctx.aead);
+  hpke_nonce(ctx, nonce, nonce_len);
 
-  if (!EVP_AEAD_CTX_seal(&hpke->aead_ctx, out, out_len, max_out_len, nonce,
+  if (!EVP_AEAD_CTX_seal(&ctx->aead_ctx, out, out_len, max_out_len, nonce,
                          nonce_len, in, in_len, ad, ad_len)) {
     return 0;
   }
-  hpke->seq++;
+  ctx->seq++;
   return 1;
 }
 
-int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *hpke, uint8_t *out,
+int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *ctx, uint8_t *out,
                         size_t secret_len, const uint8_t *context,
                         size_t context_len) {
   uint8_t suite_id[HPKE_SUITE_ID_LEN];
-  if (!hpke_build_suite_id(suite_id, hpke->kdf_id, hpke->aead_id)) {
+  if (!hpke_build_suite_id(ctx, suite_id)) {
     return 0;
   }
-  static const char kExportExpandLabel[] = "sec";
-  if (!hpke_labeled_expand(hpke->hkdf_md, out, secret_len,
-                           hpke->exporter_secret, EVP_MD_size(hpke->hkdf_md),
-                           suite_id, sizeof(suite_id), kExportExpandLabel,
-                           context, context_len)) {
+  const EVP_MD *hkdf_md = ctx->kdf->hkdf_md_func();
+  if (!hpke_labeled_expand(hkdf_md, out, secret_len, ctx->exporter_secret,
+                           EVP_MD_size(hkdf_md), suite_id, sizeof(suite_id),
+                           "sec", context, context_len)) {
     return 0;
   }
   return 1;
 }
+
+size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *ctx) {
+  assert(ctx->is_sender);
+  return EVP_AEAD_max_overhead(EVP_AEAD_CTX_aead(&ctx->aead_ctx));
+}
+
+const EVP_HPKE_AEAD *EVP_HPKE_CTX_aead(const EVP_HPKE_CTX *ctx) {
+  return ctx->aead;
+}
+
+const EVP_HPKE_KDF *EVP_HPKE_CTX_kdf(const EVP_HPKE_CTX *ctx) {
+  return ctx->kdf;
+}
diff --git a/deps/boringssl/src/crypto/hpke/hpke_test.cc b/deps/boringssl/src/crypto/hpke/hpke_test.cc
index c007b3d..a7bfe75 100644
--- a/deps/boringssl/src/crypto/hpke/hpke_test.cc
+++ b/deps/boringssl/src/crypto/hpke/hpke_test.cc
@@ -12,6 +12,8 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+#include <openssl/hpke.h>
+
 #include <cstdint>
 #include <limits>
 #include <string>
@@ -24,20 +26,25 @@
 #include <openssl/digest.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
+#include <openssl/rand.h>
 #include <openssl/sha.h>
 #include <openssl/span.h>
 
 #include "../test/file_test.h"
 #include "../test/test_util.h"
-#include "internal.h"
 
 
 namespace bssl {
 namespace {
 
-enum class HPKEMode {
-  kBase = 0,
-  kPSK = 1,
+const decltype(&EVP_hpke_aes_128_gcm) kAllAEADs[] = {
+    &EVP_hpke_aes_128_gcm,
+    &EVP_hpke_aes_256_gcm,
+    &EVP_hpke_chacha20_poly1305,
+};
+
+const decltype(&EVP_hpke_hkdf_sha256) kAllKDFs[] = {
+    &EVP_hpke_hkdf_sha256,
 };
 
 // HPKETestVector corresponds to one array member in the published
@@ -50,80 +57,104 @@
   bool ReadFromFileTest(FileTest *t);
 
   void Verify() const {
+    const EVP_HPKE_KEM *kem = EVP_hpke_x25519_hkdf_sha256();
+    const EVP_HPKE_AEAD *aead = GetAEAD();
+    ASSERT_TRUE(aead);
+    const EVP_HPKE_KDF *kdf = GetKDF();
+    ASSERT_TRUE(kdf);
+
+    // Test the sender.
     ScopedEVP_HPKE_CTX sender_ctx;
-    ScopedEVP_HPKE_CTX receiver_ctx;
+    uint8_t enc[EVP_HPKE_MAX_ENC_LENGTH];
+    size_t enc_len;
+    ASSERT_TRUE(EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
+        sender_ctx.get(), enc, &enc_len, sizeof(enc), kem, kdf, aead,
+        public_key_r_.data(), public_key_r_.size(), info_.data(), info_.size(),
+        secret_key_e_.data(), secret_key_e_.size()));
+    EXPECT_EQ(Bytes(enc, enc_len), Bytes(public_key_e_));
+    VerifySender(sender_ctx.get());
 
-    switch (mode_) {
-      case HPKEMode::kBase:
-        ASSERT_GT(secret_key_e_.size(), 0u);
-        ASSERT_EQ(psk_.size(), 0u);
-        ASSERT_EQ(psk_id_.size(), 0u);
+    // Test the recipient.
+    ScopedEVP_HPKE_KEY base_key;
+    ASSERT_TRUE(EVP_HPKE_KEY_init(base_key.get(), kem, secret_key_r_.data(),
+                                  secret_key_r_.size()));
+    for (bool copy : {false, true}) {
+      SCOPED_TRACE(copy);
+      const EVP_HPKE_KEY *key = base_key.get();
+      ScopedEVP_HPKE_KEY key_copy;
+      if (copy) {
+        ASSERT_TRUE(EVP_HPKE_KEY_copy(key_copy.get(), base_key.get()));
+        key = key_copy.get();
+      }
 
-        // Set up the sender.
-        ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519_for_test(
-            sender_ctx.get(), kdf_id_, aead_id_, public_key_r_.data(),
-            info_.data(), info_.size(), secret_key_e_.data(),
-            public_key_e_.data()));
+      uint8_t public_key[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
+      size_t public_key_len;
+      ASSERT_TRUE(EVP_HPKE_KEY_public_key(key, public_key, &public_key_len,
+                                          sizeof(public_key)));
+      EXPECT_EQ(Bytes(public_key, public_key_len), Bytes(public_key_r_));
 
-        // Set up the receiver.
-        ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519(
-            receiver_ctx.get(), kdf_id_, aead_id_, public_key_e_.data(),
-            public_key_r_.data(), secret_key_r_.data(), info_.data(),
-            info_.size()));
-        break;
+      uint8_t private_key[EVP_HPKE_MAX_PRIVATE_KEY_LENGTH];
+      size_t private_key_len;
+      ASSERT_TRUE(EVP_HPKE_KEY_private_key(key, private_key, &private_key_len,
+                                           sizeof(private_key)));
+      EXPECT_EQ(Bytes(private_key, private_key_len), Bytes(secret_key_r_));
 
-      case HPKEMode::kPSK:
-        ASSERT_GT(secret_key_e_.size(), 0u);
-        ASSERT_GT(psk_.size(), 0u);
-        ASSERT_GT(psk_id_.size(), 0u);
+      // Set up the recipient.
+      ScopedEVP_HPKE_CTX recipient_ctx;
+      ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(recipient_ctx.get(), key, kdf,
+                                               aead, enc, enc_len, info_.data(),
+                                               info_.size()));
 
-        // Set up the sender.
-        ASSERT_TRUE(EVP_HPKE_CTX_setup_psk_s_x25519_for_test(
-            sender_ctx.get(), kdf_id_, aead_id_, public_key_r_.data(),
-            info_.data(), info_.size(), psk_.data(), psk_.size(),
-            psk_id_.data(), psk_id_.size(), secret_key_e_.data(),
-            public_key_e_.data()));
-
-        // Set up the receiver.
-        ASSERT_TRUE(EVP_HPKE_CTX_setup_psk_r_x25519(
-            receiver_ctx.get(), kdf_id_, aead_id_, public_key_e_.data(),
-            public_key_r_.data(), secret_key_r_.data(), info_.data(),
-            info_.size(), psk_.data(), psk_.size(), psk_id_.data(),
-            psk_id_.size()));
-        break;
-      default:
-        FAIL() << "Unsupported mode";
-        return;
+      VerifyRecipient(recipient_ctx.get());
     }
-
-    VerifyEncryptions(sender_ctx.get(), receiver_ctx.get());
-    VerifyExports(sender_ctx.get());
-    VerifyExports(receiver_ctx.get());
   }
 
  private:
-  void VerifyEncryptions(EVP_HPKE_CTX *sender_ctx,
-                         EVP_HPKE_CTX *receiver_ctx) const {
+  const EVP_HPKE_AEAD *GetAEAD() const {
+    for (const auto aead : kAllAEADs) {
+      if (EVP_HPKE_AEAD_id(aead()) == aead_id_) {
+        return aead();
+      }
+    }
+    return nullptr;
+  }
+
+  const EVP_HPKE_KDF *GetKDF() const {
+    for (const auto kdf : kAllKDFs) {
+      if (EVP_HPKE_KDF_id(kdf()) == kdf_id_) {
+        return kdf();
+      }
+    }
+    return nullptr;
+  }
+
+  void VerifySender(EVP_HPKE_CTX *ctx) const {
     for (const Encryption &task : encryptions_) {
       std::vector<uint8_t> encrypted(task.plaintext.size() +
-                                     EVP_HPKE_CTX_max_overhead(sender_ctx));
+                                     EVP_HPKE_CTX_max_overhead(ctx));
       size_t encrypted_len;
-      ASSERT_TRUE(EVP_HPKE_CTX_seal(
-          sender_ctx, encrypted.data(), &encrypted_len, encrypted.size(),
-          task.plaintext.data(), task.plaintext.size(), task.aad.data(),
-          task.aad.size()));
+      ASSERT_TRUE(EVP_HPKE_CTX_seal(ctx, encrypted.data(), &encrypted_len,
+                                    encrypted.size(), task.plaintext.data(),
+                                    task.plaintext.size(), task.aad.data(),
+                                    task.aad.size()));
 
       ASSERT_EQ(Bytes(encrypted.data(), encrypted_len), Bytes(task.ciphertext));
+    }
+    VerifyExports(ctx);
+  }
 
+  void VerifyRecipient(EVP_HPKE_CTX *ctx) const {
+    for (const Encryption &task : encryptions_) {
       std::vector<uint8_t> decrypted(task.ciphertext.size());
       size_t decrypted_len;
-      ASSERT_TRUE(EVP_HPKE_CTX_open(
-          receiver_ctx, decrypted.data(), &decrypted_len, decrypted.size(),
-          task.ciphertext.data(), task.ciphertext.size(), task.aad.data(),
-          task.aad.size()));
+      ASSERT_TRUE(EVP_HPKE_CTX_open(ctx, decrypted.data(), &decrypted_len,
+                                    decrypted.size(), task.ciphertext.data(),
+                                    task.ciphertext.size(), task.aad.data(),
+                                    task.aad.size()));
 
       ASSERT_EQ(Bytes(decrypted.data(), decrypted_len), Bytes(task.plaintext));
     }
+    VerifyExports(ctx);
   }
 
   void VerifyExports(EVP_HPKE_CTX *ctx) const {
@@ -149,7 +180,6 @@
     std::vector<uint8_t> exported_value;
   };
 
-  HPKEMode mode_;
   uint16_t kdf_id_;
   uint16_t aead_id_;
   std::vector<uint8_t> context_;
@@ -160,8 +190,6 @@
   std::vector<uint8_t> secret_key_r_;
   std::vector<Encryption> encryptions_;
   std::vector<Export> exports_;
-  std::vector<uint8_t> psk_;     // Empty when mode is not PSK.
-  std::vector<uint8_t> psk_id_;  // Empty when mode is not PSK.
 };
 
 // Match FileTest's naming scheme for duplicated attribute names.
@@ -197,13 +225,10 @@
 
 
 bool HPKETestVector::ReadFromFileTest(FileTest *t) {
-  uint8_t mode_tmp;
-  if (!FileTestReadInt(t, &mode_tmp, "mode")) {
-    return false;
-  }
-  mode_ = static_cast<HPKEMode>(mode_tmp);
-
-  if (!FileTestReadInt(t, &kdf_id_, "kdf_id") ||
+  uint8_t mode = 0;
+  if (!FileTestReadInt(t, &mode, "mode") ||
+      mode != 0 /* mode_base */ ||
+      !FileTestReadInt(t, &kdf_id_, "kdf_id") ||
       !FileTestReadInt(t, &aead_id_, "aead_id") ||
       !t->GetBytes(&info_, "info") ||
       !t->GetBytes(&secret_key_r_, "skRm") ||
@@ -213,13 +238,6 @@
     return false;
   }
 
-  if (mode_ == HPKEMode::kPSK) {
-    if (!t->GetBytes(&psk_, "psk") ||
-        !t->GetBytes(&psk_id_, "psk_id")) {
-      return false;
-    }
-  }
-
   for (int i = 1; t->HasAttribute(BuildAttrName("aad", i)); i++) {
     Encryption encryption;
     if (!t->GetBytes(&encryption.aad, BuildAttrName("aad", i)) ||
@@ -257,11 +275,6 @@
 // generates new keys for each context. Test this codepath by checking we can
 // decrypt our own messages.
 TEST(HPKETest, RoundTrip) {
-  uint16_t kdf_ids[] = {EVP_HPKE_HKDF_SHA256, EVP_HPKE_HKDF_SHA384,
-                        EVP_HPKE_HKDF_SHA512};
-  uint16_t aead_ids[] = {EVP_HPKE_AEAD_AES_GCM_128, EVP_HPKE_AEAD_AES_GCM_256,
-                         EVP_HPKE_AEAD_CHACHA20POLY1305};
-
   const uint8_t info_a[] = {1, 1, 2, 3, 5, 8};
   const uint8_t info_b[] = {42, 42, 42};
   const uint8_t ad_a[] = {1, 2, 4, 8, 16};
@@ -269,31 +282,40 @@
   Span<const uint8_t> info_values[] = {{nullptr, 0}, info_a, info_b};
   Span<const uint8_t> ad_values[] = {{nullptr, 0}, ad_a, ad_b};
 
-  // Generate the receiver's keypair.
-  uint8_t secret_key_r[X25519_PRIVATE_KEY_LEN];
+  // Generate the recipient's keypair.
+  ScopedEVP_HPKE_KEY key;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
   uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN];
-  X25519_keypair(public_key_r, secret_key_r);
+  size_t public_key_r_len;
+  ASSERT_TRUE(EVP_HPKE_KEY_public_key(key.get(), public_key_r,
+                                      &public_key_r_len, sizeof(public_key_r)));
 
-  for (uint16_t kdf_id : kdf_ids) {
-    for (uint16_t aead_id : aead_ids) {
+  for (const auto kdf : kAllKDFs) {
+    SCOPED_TRACE(EVP_HPKE_KDF_id(kdf()));
+    for (const auto aead : kAllAEADs) {
+      SCOPED_TRACE(EVP_HPKE_AEAD_id(aead()));
       for (const Span<const uint8_t> &info : info_values) {
+        SCOPED_TRACE(Bytes(info));
         for (const Span<const uint8_t> &ad : ad_values) {
+          SCOPED_TRACE(Bytes(ad));
           // Set up the sender.
           ScopedEVP_HPKE_CTX sender_ctx;
           uint8_t enc[X25519_PUBLIC_VALUE_LEN];
-          ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519(
-              sender_ctx.get(), enc, kdf_id, aead_id, public_key_r, info.data(),
-              info.size()));
+          size_t enc_len;
+          ASSERT_TRUE(EVP_HPKE_CTX_setup_sender(
+              sender_ctx.get(), enc, &enc_len, sizeof(enc),
+              EVP_hpke_x25519_hkdf_sha256(), kdf(), aead(), public_key_r,
+              public_key_r_len, info.data(), info.size()));
 
-          // Set up the receiver.
-          ScopedEVP_HPKE_CTX receiver_ctx;
-          ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519(
-              receiver_ctx.get(), kdf_id, aead_id, enc, public_key_r,
-              secret_key_r, info.data(), info.size()));
+          // Set up the recipient.
+          ScopedEVP_HPKE_CTX recipient_ctx;
+          ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(
+              recipient_ctx.get(), key.get(), kdf(), aead(), enc, enc_len,
+              info.data(), info.size()));
 
           const char kCleartextPayload[] = "foobar";
 
-          // Have sender encrypt message for the receiver.
+          // Have sender encrypt message for the recipient.
           std::vector<uint8_t> ciphertext(
               sizeof(kCleartextPayload) +
               EVP_HPKE_CTX_max_overhead(sender_ctx.get()));
@@ -304,10 +326,10 @@
               reinterpret_cast<const uint8_t *>(kCleartextPayload),
               sizeof(kCleartextPayload), ad.data(), ad.size()));
 
-          // Have receiver decrypt the message.
+          // Have recipient decrypt the message.
           std::vector<uint8_t> cleartext(ciphertext.size());
           size_t cleartext_len;
-          ASSERT_TRUE(EVP_HPKE_CTX_open(receiver_ctx.get(), cleartext.data(),
+          ASSERT_TRUE(EVP_HPKE_CTX_open(recipient_ctx.get(), cleartext.data(),
                                         &cleartext_len, cleartext.size(),
                                         ciphertext.data(), ciphertext_len,
                                         ad.data(), ad.size()));
@@ -331,55 +353,50 @@
       0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8,
   };
 
-  // Generate a valid keypair for the receiver.
-  uint8_t secret_key_r[X25519_PRIVATE_KEY_LEN];
-  uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN];
-  X25519_keypair(public_key_r, secret_key_r);
+  ScopedEVP_HPKE_KEY key;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
 
-  uint16_t kdf_ids[] = {EVP_HPKE_HKDF_SHA256, EVP_HPKE_HKDF_SHA384,
-                        EVP_HPKE_HKDF_SHA512};
-  uint16_t aead_ids[] = {EVP_HPKE_AEAD_AES_GCM_128, EVP_HPKE_AEAD_AES_GCM_256,
-                         EVP_HPKE_AEAD_CHACHA20POLY1305};
-
-  for (uint16_t kdf_id : kdf_ids) {
-    for (uint16_t aead_id : aead_ids) {
-      // Set up the sender, passing in kSmallOrderPoint as |peer_public_value|.
+  for (const auto kdf : kAllKDFs) {
+    SCOPED_TRACE(EVP_HPKE_KDF_id(kdf()));
+    for (const auto aead : kAllAEADs) {
+      SCOPED_TRACE(EVP_HPKE_AEAD_id(aead()));
+      // Set up the sender, passing in kSmallOrderPoint as |peer_public_key|.
       ScopedEVP_HPKE_CTX sender_ctx;
       uint8_t enc[X25519_PUBLIC_VALUE_LEN];
-      ASSERT_FALSE(EVP_HPKE_CTX_setup_base_s_x25519(
-          sender_ctx.get(), enc, kdf_id, aead_id, kSmallOrderPoint, nullptr,
-          0));
+      size_t enc_len;
+      ASSERT_FALSE(EVP_HPKE_CTX_setup_sender(
+          sender_ctx.get(), enc, &enc_len, sizeof(enc),
+          EVP_hpke_x25519_hkdf_sha256(), kdf(), aead(), kSmallOrderPoint,
+          sizeof(kSmallOrderPoint), nullptr, 0));
 
-      // Set up the receiver, passing in kSmallOrderPoint as |enc|.
-      ScopedEVP_HPKE_CTX receiver_ctx;
-      ASSERT_FALSE(EVP_HPKE_CTX_setup_base_r_x25519(
-          receiver_ctx.get(), kdf_id, aead_id, kSmallOrderPoint, public_key_r,
-          secret_key_r, nullptr, 0));
+      // Set up the recipient, passing in kSmallOrderPoint as |enc|.
+      ScopedEVP_HPKE_CTX recipient_ctx;
+      ASSERT_FALSE(EVP_HPKE_CTX_setup_recipient(
+          recipient_ctx.get(), key.get(), kdf(), aead(), kSmallOrderPoint,
+          sizeof(kSmallOrderPoint), nullptr, 0));
     }
   }
 }
 
-// Test that Seal() fails when the context has been initialized as a receiver.
-TEST(HPKETest, ReceiverInvalidSeal) {
+// Test that Seal() fails when the context has been initialized as a recipient.
+TEST(HPKETest, RecipientInvalidSeal) {
   const uint8_t kMockEnc[X25519_PUBLIC_VALUE_LEN] = {0xff};
   const char kCleartextPayload[] = "foobar";
 
-  // Generate the receiver's keypair.
-  uint8_t secret_key_r[X25519_PRIVATE_KEY_LEN];
-  uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN];
-  X25519_keypair(public_key_r, secret_key_r);
+  ScopedEVP_HPKE_KEY key;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
 
-  // Set up the receiver.
-  ScopedEVP_HPKE_CTX receiver_ctx;
-  ASSERT_TRUE(EVP_HPKE_CTX_setup_base_r_x25519(
-      receiver_ctx.get(), EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
-      kMockEnc, public_key_r, secret_key_r, nullptr, 0));
+  // Set up the recipient.
+  ScopedEVP_HPKE_CTX recipient_ctx;
+  ASSERT_TRUE(EVP_HPKE_CTX_setup_recipient(
+      recipient_ctx.get(), key.get(), EVP_hpke_hkdf_sha256(),
+      EVP_hpke_aes_128_gcm(), kMockEnc, sizeof(kMockEnc), nullptr, 0));
 
-  // Call Seal() on the receiver.
+  // Call Seal() on the recipient.
   size_t ciphertext_len;
   uint8_t ciphertext[100];
   ASSERT_FALSE(EVP_HPKE_CTX_seal(
-      receiver_ctx.get(), ciphertext, &ciphertext_len, sizeof(ciphertext),
+      recipient_ctx.get(), ciphertext, &ciphertext_len, sizeof(ciphertext),
       reinterpret_cast<const uint8_t *>(kCleartextPayload),
       sizeof(kCleartextPayload), nullptr, 0));
 }
@@ -389,7 +406,7 @@
   const uint8_t kMockCiphertext[100] = {0xff};
   const size_t kMockCiphertextLen = 80;
 
-  // Generate the receiver's keypair.
+  // Generate the recipient's keypair.
   uint8_t secret_key_r[X25519_PRIVATE_KEY_LEN];
   uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN];
   X25519_keypair(public_key_r, secret_key_r);
@@ -397,9 +414,11 @@
   // Set up the sender.
   ScopedEVP_HPKE_CTX sender_ctx;
   uint8_t enc[X25519_PUBLIC_VALUE_LEN];
-  ASSERT_TRUE(EVP_HPKE_CTX_setup_base_s_x25519(
-      sender_ctx.get(), enc, EVP_HPKE_HKDF_SHA256, EVP_HPKE_AEAD_AES_GCM_128,
-      public_key_r, nullptr, 0));
+  size_t enc_len;
+  ASSERT_TRUE(EVP_HPKE_CTX_setup_sender(
+      sender_ctx.get(), enc, &enc_len, sizeof(enc),
+      EVP_hpke_x25519_hkdf_sha256(), EVP_hpke_hkdf_sha256(),
+      EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0));
 
   // Call Open() on the sender.
   uint8_t cleartext[128];
@@ -409,58 +428,78 @@
                                  kMockCiphertextLen, nullptr, 0));
 }
 
-// Test that the PSK variants of Setup functions fail when any of the PSK inputs
-// are empty.
-TEST(HPKETest, EmptyPSK) {
-  const uint8_t kMockEnc[X25519_PUBLIC_VALUE_LEN] = {0xff};
-  const std::vector<uint8_t> kPSKValues[] = {std::vector<uint8_t>(100, 0xff),
-                                             {}};
-
-  // Generate the receiver's keypair.
+TEST(HPKETest, SetupSenderBufferTooSmall) {
   uint8_t secret_key_r[X25519_PRIVATE_KEY_LEN];
   uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN];
   X25519_keypair(public_key_r, secret_key_r);
 
-  // Vary the PSK and PSKID inputs for the sender and receiver, trying all four
-  // permutations of empty and nonempty inputs.
+  ScopedEVP_HPKE_CTX sender_ctx;
+  uint8_t enc[X25519_PUBLIC_VALUE_LEN - 1];
+  size_t enc_len;
+  ASSERT_FALSE(EVP_HPKE_CTX_setup_sender(
+      sender_ctx.get(), enc, &enc_len, sizeof(enc),
+      EVP_hpke_x25519_hkdf_sha256(), EVP_hpke_hkdf_sha256(),
+      EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0));
+  uint32_t err = ERR_get_error();
+  EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+  EXPECT_EQ(EVP_R_INVALID_BUFFER_SIZE, ERR_GET_REASON(err));
+  ERR_clear_error();
+}
 
-  for (const auto &psk : kPSKValues) {
-    for (const auto &psk_id : kPSKValues) {
-      const bool kExpectSuccess = psk.size() > 0 && psk_id.size() > 0;
+TEST(HPKETest, SetupSenderBufferTooLarge) {
+  uint8_t secret_key_r[X25519_PRIVATE_KEY_LEN];
+  uint8_t public_key_r[X25519_PUBLIC_VALUE_LEN];
+  X25519_keypair(public_key_r, secret_key_r);
 
-      ASSERT_EQ(ERR_get_error(), 0u);
+  // Too large of an output buffer is fine because the function reports the
+  // actual length.
+  ScopedEVP_HPKE_CTX sender_ctx;
+  uint8_t enc[X25519_PUBLIC_VALUE_LEN + 1];
+  size_t enc_len;
+  EXPECT_TRUE(EVP_HPKE_CTX_setup_sender(
+      sender_ctx.get(), enc, &enc_len, sizeof(enc),
+      EVP_hpke_x25519_hkdf_sha256(), EVP_hpke_hkdf_sha256(),
+      EVP_hpke_aes_128_gcm(), public_key_r, sizeof(public_key_r), nullptr, 0));
+  EXPECT_EQ(size_t{X25519_PUBLIC_VALUE_LEN}, enc_len);
+}
 
-      ScopedEVP_HPKE_CTX sender_ctx;
-      uint8_t enc[X25519_PUBLIC_VALUE_LEN];
-      ASSERT_EQ(EVP_HPKE_CTX_setup_psk_s_x25519(
-                    sender_ctx.get(), enc, EVP_HPKE_HKDF_SHA256,
-                    EVP_HPKE_AEAD_AES_GCM_128, public_key_r, nullptr, 0,
-                    psk.data(), psk.size(), psk_id.data(), psk_id.size()),
-                kExpectSuccess);
+TEST(HPKETest, SetupRecipientWrongLengthEnc) {
+  ScopedEVP_HPKE_KEY key;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
 
-      if (!kExpectSuccess) {
-        uint32_t err = ERR_get_error();
-        EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
-        EXPECT_EQ(EVP_R_EMPTY_PSK, ERR_GET_REASON(err));
-      }
-      ERR_clear_error();
+  const uint8_t bogus_enc[X25519_PUBLIC_VALUE_LEN + 5] = {0xff};
 
-      ScopedEVP_HPKE_CTX receiver_ctx;
-      ASSERT_EQ(
-          EVP_HPKE_CTX_setup_psk_r_x25519(
-              receiver_ctx.get(), EVP_HPKE_HKDF_SHA256,
-              EVP_HPKE_AEAD_AES_GCM_128, kMockEnc, public_key_r, secret_key_r,
-              nullptr, 0, psk.data(), psk.size(), psk_id.data(), psk_id.size()),
-          kExpectSuccess);
+  ScopedEVP_HPKE_CTX recipient_ctx;
+  ASSERT_FALSE(EVP_HPKE_CTX_setup_recipient(
+      recipient_ctx.get(), key.get(), EVP_hpke_hkdf_sha256(),
+      EVP_hpke_aes_128_gcm(), bogus_enc, sizeof(bogus_enc), nullptr, 0));
+  uint32_t err = ERR_get_error();
+  EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+  EXPECT_EQ(EVP_R_INVALID_PEER_KEY, ERR_GET_REASON(err));
+  ERR_clear_error();
+}
 
-      if (!kExpectSuccess) {
-        uint32_t err = ERR_get_error();
-        EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
-        EXPECT_EQ(EVP_R_EMPTY_PSK, ERR_GET_REASON(err));
-      }
-      ERR_clear_error();
-    }
-  }
+TEST(HPKETest, SetupSenderWrongLengthPeerPublicValue) {
+  const uint8_t bogus_public_key_r[X25519_PRIVATE_KEY_LEN + 5] = {0xff};
+  ScopedEVP_HPKE_CTX sender_ctx;
+  uint8_t enc[X25519_PUBLIC_VALUE_LEN];
+  size_t enc_len;
+  ASSERT_FALSE(EVP_HPKE_CTX_setup_sender(
+      sender_ctx.get(), enc, &enc_len, sizeof(enc),
+      EVP_hpke_x25519_hkdf_sha256(), EVP_hpke_hkdf_sha256(),
+      EVP_hpke_aes_128_gcm(), bogus_public_key_r, sizeof(bogus_public_key_r),
+      nullptr, 0));
+  uint32_t err = ERR_get_error();
+  EXPECT_EQ(ERR_LIB_EVP, ERR_GET_LIB(err));
+  EXPECT_EQ(EVP_R_INVALID_PEER_KEY, ERR_GET_REASON(err));
+  ERR_clear_error();
+}
+
+TEST(HPKETest, InvalidRecipientKey) {
+  const uint8_t private_key[X25519_PUBLIC_VALUE_LEN + 5] = {0xff};
+  ScopedEVP_HPKE_KEY key;
+  EXPECT_FALSE(EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(),
+                                 private_key, sizeof(private_key)));
 }
 
 TEST(HPKETest, InternalParseIntSafe) {
diff --git a/deps/boringssl/src/crypto/hpke/internal.h b/deps/boringssl/src/crypto/hpke/internal.h
deleted file mode 100644
index 3d2f4ba..0000000
--- a/deps/boringssl/src/crypto/hpke/internal.h
+++ /dev/null
@@ -1,246 +0,0 @@
-/* Copyright (c) 2020, Google Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
-
-#ifndef OPENSSL_HEADER_CRYPTO_HPKE_INTERNAL_H
-#define OPENSSL_HEADER_CRYPTO_HPKE_INTERNAL_H
-
-#include <openssl/aead.h>
-#include <openssl/base.h>
-#include <openssl/curve25519.h>
-#include <openssl/digest.h>
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-
-// Hybrid Public Key Encryption.
-//
-// Hybrid Public Key Encryption (HPKE) enables a sender to encrypt messages to a
-// receiver with a public key. Optionally, the sender may authenticate its
-// possession of a pre-shared key to the recipient.
-//
-// See https://tools.ietf.org/html/draft-irtf-cfrg-hpke-07.
-
-// EVP_HPKE_AEAD_* are AEAD identifiers.
-#define EVP_HPKE_AEAD_AES_GCM_128 0x0001
-#define EVP_HPKE_AEAD_AES_GCM_256 0x0002
-#define EVP_HPKE_AEAD_CHACHA20POLY1305 0x0003
-
-// EVP_HPKE_HKDF_* are HKDF identifiers.
-#define EVP_HPKE_HKDF_SHA256 0x0001
-#define EVP_HPKE_HKDF_SHA384 0x0002
-#define EVP_HPKE_HKDF_SHA512 0x0003
-
-// EVP_HPKE_MAX_OVERHEAD contains the largest value that
-// |EVP_HPKE_CTX_max_overhead| would ever return for any context.
-#define EVP_HPKE_MAX_OVERHEAD EVP_AEAD_MAX_OVERHEAD
-
-
-// Encryption contexts.
-
-// An |EVP_HPKE_CTX| is an HPKE encryption context.
-typedef struct evp_hpke_ctx_st {
-  const EVP_MD *hkdf_md;
-  EVP_AEAD_CTX aead_ctx;
-  uint16_t kdf_id;
-  uint16_t aead_id;
-  uint8_t base_nonce[EVP_AEAD_MAX_NONCE_LENGTH];
-  uint8_t exporter_secret[EVP_MAX_MD_SIZE];
-  uint64_t seq;
-  int is_sender;
-} EVP_HPKE_CTX;
-
-// EVP_HPKE_CTX_init initializes an already-allocated |EVP_HPKE_CTX|. The caller
-// should then use one of the |EVP_HPKE_CTX_setup_*| functions.
-//
-// It is safe, but not necessary to call |EVP_HPKE_CTX_cleanup| in this state.
-OPENSSL_EXPORT void EVP_HPKE_CTX_init(EVP_HPKE_CTX *ctx);
-
-// EVP_HPKE_CTX_cleanup releases memory referenced by |ctx|. |ctx| must have
-// been initialized with |EVP_HPKE_CTX_init|.
-OPENSSL_EXPORT void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx);
-
-
-// Setting up HPKE contexts.
-//
-// In each of the following functions, |hpke| must have been initialized with
-// |EVP_HPKE_CTX_init|. |kdf_id| selects the KDF for non-KEM HPKE operations and
-// must be one of the |EVP_HPKE_HKDF_*| constants. |aead_id| selects the AEAD
-// for the "open" and "seal" operations and must be one of the |EVP_HPKE_AEAD_*|
-// constants.
-
-// EVP_HPKE_CTX_setup_base_s_x25519 sets up |hpke| as a sender context that can
-// encrypt for the private key corresponding to |peer_public_value| (the
-// recipient's public key). It returns one on success, and zero otherwise. Note
-// that this function will fail if |peer_public_value| is invalid.
-//
-// This function writes the encapsulated shared secret to |out_enc|.
-OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519(
-    EVP_HPKE_CTX *hpke, uint8_t out_enc[X25519_PUBLIC_VALUE_LEN],
-    uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len);
-
-// EVP_HPKE_CTX_setup_base_s_x25519_for_test behaves like
-// |EVP_HPKE_CTX_setup_base_s_x25519|, but takes a pre-generated ephemeral
-// sender key.
-OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_s_x25519_for_test(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len,
-    const uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN],
-    const uint8_t ephemeral_public[X25519_PUBLIC_VALUE_LEN]);
-
-// EVP_HPKE_CTX_setup_base_r_x25519 sets up |hpke| as a recipient context that
-// can decrypt messages. |private_key| is the recipient's private key, and |enc|
-// is the encapsulated shared secret from the sender. Note that this function
-// will fail if |enc| is invalid.
-OPENSSL_EXPORT int EVP_HPKE_CTX_setup_base_r_x25519(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t enc[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t public_key[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t private_key[X25519_PRIVATE_KEY_LEN], const uint8_t *info,
-    size_t info_len);
-
-// EVP_HPKE_CTX_setup_psk_s_x25519 sets up |hpke| as a sender context that can
-// encrypt for the private key corresponding to |peer_public_value| (the
-// recipient's public key) and authenticate its possession of a PSK. It returns
-// one on success, and zero otherwise. Note that this function will fail if
-// |peer_public_value| is invalid.
-//
-// The PSK and its ID must be provided in |psk| and |psk_id|, respectively. Both
-// must be nonempty (|psk_len| and |psk_id_len| must be non-zero), or this
-// function will fail.
-//
-// This function writes the encapsulated shared secret to |out_enc|.
-OPENSSL_EXPORT int EVP_HPKE_CTX_setup_psk_s_x25519(
-    EVP_HPKE_CTX *hpke, uint8_t out_enc[X25519_PUBLIC_VALUE_LEN],
-    uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len, const uint8_t *psk, size_t psk_len,
-    const uint8_t *psk_id, size_t psk_id_len);
-
-// EVP_HPKE_CTX_setup_psk_s_x25519_for_test behaves like
-// |EVP_HPKE_CTX_setup_psk_s_x25519|, but takes a pre-generated ephemeral sender
-// key.
-OPENSSL_EXPORT int EVP_HPKE_CTX_setup_psk_s_x25519_for_test(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t peer_public_value[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t *info, size_t info_len, const uint8_t *psk, size_t psk_len,
-    const uint8_t *psk_id, size_t psk_id_len,
-    const uint8_t ephemeral_private[X25519_PRIVATE_KEY_LEN],
-    const uint8_t ephemeral_public[X25519_PUBLIC_VALUE_LEN]);
-
-// EVP_HPKE_CTX_setup_psk_r_x25519 sets up |hpke| as a recipient context that
-// can decrypt messages. Future open (decrypt) operations will fail if the
-// sender does not possess the PSK indicated by |psk| and |psk_id|.
-// |private_key| is the recipient's private key, and |enc| is the encapsulated
-// shared secret from the sender. If |enc| is invalid, this function will fail.
-//
-// The PSK and its ID must be provided in |psk| and |psk_id|, respectively. Both
-// must be nonempty (|psk_len| and |psk_id_len| must be non-zero), or this
-// function will fail.
-OPENSSL_EXPORT int EVP_HPKE_CTX_setup_psk_r_x25519(
-    EVP_HPKE_CTX *hpke, uint16_t kdf_id, uint16_t aead_id,
-    const uint8_t enc[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t public_key[X25519_PUBLIC_VALUE_LEN],
-    const uint8_t private_key[X25519_PRIVATE_KEY_LEN], const uint8_t *info,
-    size_t info_len, const uint8_t *psk, size_t psk_len, const uint8_t *psk_id,
-    size_t psk_id_len);
-
-
-// Using an HPKE context.
-
-// EVP_HPKE_CTX_open uses the HPKE context |hpke| to authenticate |in_len| bytes
-// from |in| and |ad_len| bytes from |ad| and to decrypt at most |in_len| bytes
-// into |out|. It returns one on success, and zero otherwise.
-//
-// This operation will fail if the |hpke| context is not set up as a receiver.
-//
-// Note that HPKE encryption is stateful and ordered. The sender's first call to
-// |EVP_HPKE_CTX_seal| must correspond to the recipient's first call to
-// |EVP_HPKE_CTX_open|, etc.
-//
-// At most |in_len| bytes are written to |out|. In order to ensure success,
-// |max_out_len| should be at least |in_len|. On successful return, |*out_len|
-// is set to the actual number of bytes written.
-OPENSSL_EXPORT int EVP_HPKE_CTX_open(EVP_HPKE_CTX *hpke, uint8_t *out,
-                                     size_t *out_len, size_t max_out_len,
-                                     const uint8_t *in, size_t in_len,
-                                     const uint8_t *ad, size_t ad_len);
-
-// EVP_HPKE_CTX_seal uses the HPKE context |hpke| to encrypt and authenticate
-// |in_len| bytes of ciphertext |in| and authenticate |ad_len| bytes from |ad|,
-// writing the result to |out|. It returns one on success and zero otherwise.
-//
-// This operation will fail if the |hpke| context is not set up as a sender.
-//
-// Note that HPKE encryption is stateful and ordered. The sender's first call to
-// |EVP_HPKE_CTX_seal| must correspond to the recipient's first call to
-// |EVP_HPKE_CTX_open|, etc.
-//
-// At most, |max_out_len| encrypted bytes are written to |out|. On successful
-// return, |*out_len| is set to the actual number of bytes written.
-//
-// To ensure success, |max_out_len| should be |in_len| plus the result of
-// |EVP_HPKE_CTX_max_overhead| or |EVP_HPKE_MAX_OVERHEAD|.
-OPENSSL_EXPORT int EVP_HPKE_CTX_seal(EVP_HPKE_CTX *hpke, uint8_t *out,
-                                     size_t *out_len, size_t max_out_len,
-                                     const uint8_t *in, size_t in_len,
-                                     const uint8_t *ad, size_t ad_len);
-
-// EVP_HPKE_CTX_export uses the HPKE context |hpke| to export a secret of
-// |secret_len| bytes into |out|. This function uses |context_len| bytes from
-// |context| as a context string for the secret. This is necessary to separate
-// different uses of exported secrets and bind relevant caller-specific context
-// into the output. It returns one on success and zero otherwise.
-OPENSSL_EXPORT int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *hpke, uint8_t *out,
-                                       size_t secret_len,
-                                       const uint8_t *context,
-                                       size_t context_len);
-
-// EVP_HPKE_CTX_max_overhead returns the maximum number of additional bytes
-// added by sealing data with |EVP_HPKE_CTX_seal|. The |hpke| context must be
-// set up as a sender.
-OPENSSL_EXPORT size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *hpke);
-
-// EVP_HPKE_get_aead returns the AEAD corresponding to |aead_id|, or NULL if
-// |aead_id| is not a known AEAD identifier.
-OPENSSL_EXPORT const EVP_AEAD *EVP_HPKE_get_aead(uint16_t aead_id);
-
-// EVP_HPKE_get_hkdf_md returns the hash function associated with |kdf_id|, or
-// NULL if |kdf_id| is not a known KDF identifier that uses HKDF.
-OPENSSL_EXPORT const EVP_MD *EVP_HPKE_get_hkdf_md(uint16_t kdf_id);
-
-
-#if defined(__cplusplus)
-}  // extern C
-#endif
-
-#if !defined(BORINGSSL_NO_CXX)
-extern "C++" {
-
-BSSL_NAMESPACE_BEGIN
-
-using ScopedEVP_HPKE_CTX =
-    internal::StackAllocated<EVP_HPKE_CTX, void, EVP_HPKE_CTX_init,
-                             EVP_HPKE_CTX_cleanup>;
-
-BSSL_NAMESPACE_END
-
-}  // extern C++
-#endif
-
-#endif  // OPENSSL_HEADER_CRYPTO_HPKE_INTERNAL_H
diff --git a/deps/boringssl/src/crypto/hrss/asm/poly_rq_mul.S b/deps/boringssl/src/crypto/hrss/asm/poly_rq_mul.S
index 835d716..c37d7d0 100644
--- a/deps/boringssl/src/crypto/hrss/asm/poly_rq_mul.S
+++ b/deps/boringssl/src/crypto/hrss/asm/poly_rq_mul.S
@@ -26,23 +26,6 @@
 # This file was generated by poly_rq_mul.py
 .text
 .align 32
-mask_low9words:
-.word 0xffff
-.word 0xffff
-.word 0xffff
-.word 0xffff
-.word 0xffff
-.word 0xffff
-.word 0xffff
-.word 0xffff
-.word 0xffff
-.word 0x0
-.word 0x0
-.word 0x0
-.word 0x0
-.word 0x0
-.word 0x0
-.word 0x0
 const3:
 .word 3
 .word 3
@@ -327,15 +310,21 @@
 .cfi_def_cfa_register rbp
 push %r12
 .cfi_offset r12, -24
-mov %rsp, %r8
-andq $-32, %rsp
-subq $6144, %rsp
-mov %rsp, %rax
-subq $6144, %rsp
-mov %rsp, %r11
-subq $12288, %rsp
-mov %rsp, %r12
-subq $512, %rsp
+# This function originally used a significant amount of stack space. As an
+# alternative, the needed scratch space is now passed in as the 4th argument.
+# The amount of scratch space used must thus be kept in sync with
+# POLY_MUL_RQ_SCRATCH_SPACE in internal.h.
+#
+# Setting RSP to point into the given scratch space upsets the ABI tests
+# therefore all references to RSP are switched to R8.
+mov %rcx, %r8
+addq $6144+12288+512+9408+32, %r8
+mov %r8, %rax
+subq $6144, %r8
+mov %r8, %r11
+subq $12288, %r8
+mov %r8, %r12
+subq $512, %r8
 vmovdqa const3(%rip), %ymm3
 vmovdqu 0(%rsi), %ymm0
 vmovdqu 88(%rsi), %ymm1
@@ -377,38 +366,38 @@
 vmovdqa %ymm15, 5856(%rax)
 vpaddw %ymm14, %ymm15, %ymm14
 vmovdqa %ymm14, 5952(%rax)
-vmovdqa %ymm0, 0(%rsp)
-vmovdqa %ymm1, 32(%rsp)
-vmovdqa %ymm2, 64(%rsp)
-vmovdqa %ymm12, 96(%rsp)
-vmovdqa %ymm8, 128(%rsp)
-vmovdqa %ymm9, 160(%rsp)
-vmovdqa %ymm10, 192(%rsp)
-vmovdqa %ymm11, 224(%rsp)
+vmovdqa %ymm0, 0(%r8)
+vmovdqa %ymm1, 32(%r8)
+vmovdqa %ymm2, 64(%r8)
+vmovdqa %ymm12, 96(%r8)
+vmovdqa %ymm8, 128(%r8)
+vmovdqa %ymm9, 160(%r8)
+vmovdqa %ymm10, 192(%r8)
+vmovdqa %ymm11, 224(%r8)
 vmovdqu 704(%rsi), %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm1
-vpaddw 128(%rsp), %ymm4, %ymm2
+vpaddw 0(%r8), %ymm0, %ymm1
+vpaddw 128(%r8), %ymm4, %ymm2
 vpaddw %ymm2, %ymm1, %ymm8
 vpsubw %ymm2, %ymm1, %ymm12
-vmovdqa %ymm0, 256(%rsp)
+vmovdqa %ymm0, 256(%r8)
 vmovdqu 792(%rsi), %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm1
-vpaddw 160(%rsp), %ymm5, %ymm2
+vpaddw 32(%r8), %ymm0, %ymm1
+vpaddw 160(%r8), %ymm5, %ymm2
 vpaddw %ymm2, %ymm1, %ymm9
 vpsubw %ymm2, %ymm1, %ymm13
-vmovdqa %ymm0, 288(%rsp)
+vmovdqa %ymm0, 288(%r8)
 vmovdqu 880(%rsi), %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm1
-vpaddw 192(%rsp), %ymm6, %ymm2
+vpaddw 64(%r8), %ymm0, %ymm1
+vpaddw 192(%r8), %ymm6, %ymm2
 vpaddw %ymm2, %ymm1, %ymm10
 vpsubw %ymm2, %ymm1, %ymm14
-vmovdqa %ymm0, 320(%rsp)
+vmovdqa %ymm0, 320(%r8)
 vmovdqu 968(%rsi), %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm1
-vpaddw 224(%rsp), %ymm7, %ymm2
+vpaddw 96(%r8), %ymm0, %ymm1
+vpaddw 224(%r8), %ymm7, %ymm2
 vpaddw %ymm2, %ymm1, %ymm11
 vpsubw %ymm2, %ymm1, %ymm15
-vmovdqa %ymm0, 352(%rsp)
+vmovdqa %ymm0, 352(%r8)
 vmovdqa %ymm8, 864(%rax)
 vmovdqa %ymm9, 960(%rax)
 vpaddw %ymm8, %ymm9, %ymm0
@@ -437,35 +426,35 @@
 vmovdqa %ymm1, 2400(%rax)
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 2496(%rax)
-vmovdqa 256(%rsp), %ymm0
+vmovdqa 256(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm0
+vpaddw 0(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm4, %ymm1
-vpaddw 128(%rsp), %ymm1, %ymm1
+vpaddw 128(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm8
 vpsubw %ymm1, %ymm0, %ymm12
-vmovdqa 288(%rsp), %ymm0
+vmovdqa 288(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm0
+vpaddw 32(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm5, %ymm1
-vpaddw 160(%rsp), %ymm1, %ymm1
+vpaddw 160(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm9
 vpsubw %ymm1, %ymm0, %ymm13
-vmovdqa 320(%rsp), %ymm0
+vmovdqa 320(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm0
+vpaddw 64(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm6, %ymm1
-vpaddw 192(%rsp), %ymm1, %ymm1
+vpaddw 192(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm10
 vpsubw %ymm1, %ymm0, %ymm14
-vmovdqa 352(%rsp), %ymm0
+vmovdqa 352(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm0
+vpaddw 96(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm7, %ymm1
-vpaddw 224(%rsp), %ymm1, %ymm1
+vpaddw 224(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm11
 vpsubw %ymm1, %ymm0, %ymm15
@@ -498,29 +487,29 @@
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 4224(%rax)
 vpmullw %ymm3, %ymm4, %ymm0
-vpaddw 256(%rsp), %ymm0, %ymm0
+vpaddw 256(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 128(%rsp), %ymm0, %ymm0
+vpaddw 128(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm12
+vpaddw 0(%r8), %ymm0, %ymm12
 vpmullw %ymm3, %ymm5, %ymm0
-vpaddw 288(%rsp), %ymm0, %ymm0
+vpaddw 288(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 160(%rsp), %ymm0, %ymm0
+vpaddw 160(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm13
+vpaddw 32(%r8), %ymm0, %ymm13
 vpmullw %ymm3, %ymm6, %ymm0
-vpaddw 320(%rsp), %ymm0, %ymm0
+vpaddw 320(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 192(%rsp), %ymm0, %ymm0
+vpaddw 192(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm14
+vpaddw 64(%r8), %ymm0, %ymm14
 vpmullw %ymm3, %ymm7, %ymm0
-vpaddw 352(%rsp), %ymm0, %ymm0
+vpaddw 352(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 224(%rsp), %ymm0, %ymm0
+vpaddw 224(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm15
+vpaddw 96(%r8), %ymm0, %ymm15
 vmovdqa %ymm12, 4320(%rax)
 vmovdqa %ymm13, 4416(%rax)
 vpaddw %ymm12, %ymm13, %ymm0
@@ -575,38 +564,38 @@
 vmovdqa %ymm15, 5888(%rax)
 vpaddw %ymm14, %ymm15, %ymm14
 vmovdqa %ymm14, 5984(%rax)
-vmovdqa %ymm0, 0(%rsp)
-vmovdqa %ymm1, 32(%rsp)
-vmovdqa %ymm2, 64(%rsp)
-vmovdqa %ymm12, 96(%rsp)
-vmovdqa %ymm8, 128(%rsp)
-vmovdqa %ymm9, 160(%rsp)
-vmovdqa %ymm10, 192(%rsp)
-vmovdqa %ymm11, 224(%rsp)
+vmovdqa %ymm0, 0(%r8)
+vmovdqa %ymm1, 32(%r8)
+vmovdqa %ymm2, 64(%r8)
+vmovdqa %ymm12, 96(%r8)
+vmovdqa %ymm8, 128(%r8)
+vmovdqa %ymm9, 160(%r8)
+vmovdqa %ymm10, 192(%r8)
+vmovdqa %ymm11, 224(%r8)
 vmovdqu 736(%rsi), %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm1
-vpaddw 128(%rsp), %ymm4, %ymm2
+vpaddw 0(%r8), %ymm0, %ymm1
+vpaddw 128(%r8), %ymm4, %ymm2
 vpaddw %ymm2, %ymm1, %ymm8
 vpsubw %ymm2, %ymm1, %ymm12
-vmovdqa %ymm0, 256(%rsp)
+vmovdqa %ymm0, 256(%r8)
 vmovdqu 824(%rsi), %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm1
-vpaddw 160(%rsp), %ymm5, %ymm2
+vpaddw 32(%r8), %ymm0, %ymm1
+vpaddw 160(%r8), %ymm5, %ymm2
 vpaddw %ymm2, %ymm1, %ymm9
 vpsubw %ymm2, %ymm1, %ymm13
-vmovdqa %ymm0, 288(%rsp)
+vmovdqa %ymm0, 288(%r8)
 vmovdqu 912(%rsi), %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm1
-vpaddw 192(%rsp), %ymm6, %ymm2
+vpaddw 64(%r8), %ymm0, %ymm1
+vpaddw 192(%r8), %ymm6, %ymm2
 vpaddw %ymm2, %ymm1, %ymm10
 vpsubw %ymm2, %ymm1, %ymm14
-vmovdqa %ymm0, 320(%rsp)
+vmovdqa %ymm0, 320(%r8)
 vmovdqu 1000(%rsi), %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm1
-vpaddw 224(%rsp), %ymm7, %ymm2
+vpaddw 96(%r8), %ymm0, %ymm1
+vpaddw 224(%r8), %ymm7, %ymm2
 vpaddw %ymm2, %ymm1, %ymm11
 vpsubw %ymm2, %ymm1, %ymm15
-vmovdqa %ymm0, 352(%rsp)
+vmovdqa %ymm0, 352(%r8)
 vmovdqa %ymm8, 896(%rax)
 vmovdqa %ymm9, 992(%rax)
 vpaddw %ymm8, %ymm9, %ymm0
@@ -635,35 +624,35 @@
 vmovdqa %ymm1, 2432(%rax)
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 2528(%rax)
-vmovdqa 256(%rsp), %ymm0
+vmovdqa 256(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm0
+vpaddw 0(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm4, %ymm1
-vpaddw 128(%rsp), %ymm1, %ymm1
+vpaddw 128(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm8
 vpsubw %ymm1, %ymm0, %ymm12
-vmovdqa 288(%rsp), %ymm0
+vmovdqa 288(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm0
+vpaddw 32(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm5, %ymm1
-vpaddw 160(%rsp), %ymm1, %ymm1
+vpaddw 160(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm9
 vpsubw %ymm1, %ymm0, %ymm13
-vmovdqa 320(%rsp), %ymm0
+vmovdqa 320(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm0
+vpaddw 64(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm6, %ymm1
-vpaddw 192(%rsp), %ymm1, %ymm1
+vpaddw 192(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm10
 vpsubw %ymm1, %ymm0, %ymm14
-vmovdqa 352(%rsp), %ymm0
+vmovdqa 352(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm0
+vpaddw 96(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm7, %ymm1
-vpaddw 224(%rsp), %ymm1, %ymm1
+vpaddw 224(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm11
 vpsubw %ymm1, %ymm0, %ymm15
@@ -696,29 +685,29 @@
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 4256(%rax)
 vpmullw %ymm3, %ymm4, %ymm0
-vpaddw 256(%rsp), %ymm0, %ymm0
+vpaddw 256(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 128(%rsp), %ymm0, %ymm0
+vpaddw 128(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm12
+vpaddw 0(%r8), %ymm0, %ymm12
 vpmullw %ymm3, %ymm5, %ymm0
-vpaddw 288(%rsp), %ymm0, %ymm0
+vpaddw 288(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 160(%rsp), %ymm0, %ymm0
+vpaddw 160(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm13
+vpaddw 32(%r8), %ymm0, %ymm13
 vpmullw %ymm3, %ymm6, %ymm0
-vpaddw 320(%rsp), %ymm0, %ymm0
+vpaddw 320(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 192(%rsp), %ymm0, %ymm0
+vpaddw 192(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm14
+vpaddw 64(%r8), %ymm0, %ymm14
 vpmullw %ymm3, %ymm7, %ymm0
-vpaddw 352(%rsp), %ymm0, %ymm0
+vpaddw 352(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 224(%rsp), %ymm0, %ymm0
+vpaddw 224(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm15
+vpaddw 96(%r8), %ymm0, %ymm15
 vmovdqa %ymm12, 4352(%rax)
 vmovdqa %ymm13, 4448(%rax)
 vpaddw %ymm12, %ymm13, %ymm0
@@ -740,8 +729,20 @@
 vmovdqu 1120(%rsi), %ymm4
 vmovdqu 1208(%rsi), %ymm5
 vmovdqu 1296(%rsi), %ymm6
-vmovdqu 1384(%rsi), %ymm7
-vpand mask_low9words(%rip), %ymm7, %ymm7
+
+# Only 18 bytes more can be read, but vmovdqu reads 32.
+# Copy 18 bytes to the red zone and zero pad to 32 bytes.
+xor %r9, %r9
+movq %r9, -16(%rsp)
+movq %r9, -8(%rsp)
+movq 1384(%rsi), %r9
+movq %r9, -32(%rsp)
+movq 1384+8(%rsi), %r9
+movq %r9, -24(%rsp)
+movw 1384+16(%rsi), %r9w
+movw %r9w, -16(%rsp)
+vmovdqu -32(%rsp), %ymm7
+
 vmovdqu 416(%rsi), %ymm8
 vmovdqu 504(%rsi), %ymm9
 vmovdqu 592(%rsi), %ymm10
@@ -774,38 +775,38 @@
 vmovdqa %ymm15, 5920(%rax)
 vpaddw %ymm14, %ymm15, %ymm14
 vmovdqa %ymm14, 6016(%rax)
-vmovdqa %ymm0, 0(%rsp)
-vmovdqa %ymm1, 32(%rsp)
-vmovdqa %ymm2, 64(%rsp)
-vmovdqa %ymm12, 96(%rsp)
-vmovdqa %ymm8, 128(%rsp)
-vmovdqa %ymm9, 160(%rsp)
-vmovdqa %ymm10, 192(%rsp)
-vmovdqa %ymm11, 224(%rsp)
+vmovdqa %ymm0, 0(%r8)
+vmovdqa %ymm1, 32(%r8)
+vmovdqa %ymm2, 64(%r8)
+vmovdqa %ymm12, 96(%r8)
+vmovdqa %ymm8, 128(%r8)
+vmovdqa %ymm9, 160(%r8)
+vmovdqa %ymm10, 192(%r8)
+vmovdqa %ymm11, 224(%r8)
 vmovdqu 768(%rsi), %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm1
-vpaddw 128(%rsp), %ymm4, %ymm2
+vpaddw 0(%r8), %ymm0, %ymm1
+vpaddw 128(%r8), %ymm4, %ymm2
 vpaddw %ymm2, %ymm1, %ymm8
 vpsubw %ymm2, %ymm1, %ymm12
-vmovdqa %ymm0, 256(%rsp)
+vmovdqa %ymm0, 256(%r8)
 vmovdqu 856(%rsi), %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm1
-vpaddw 160(%rsp), %ymm5, %ymm2
+vpaddw 32(%r8), %ymm0, %ymm1
+vpaddw 160(%r8), %ymm5, %ymm2
 vpaddw %ymm2, %ymm1, %ymm9
 vpsubw %ymm2, %ymm1, %ymm13
-vmovdqa %ymm0, 288(%rsp)
+vmovdqa %ymm0, 288(%r8)
 vmovdqu 944(%rsi), %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm1
-vpaddw 192(%rsp), %ymm6, %ymm2
+vpaddw 64(%r8), %ymm0, %ymm1
+vpaddw 192(%r8), %ymm6, %ymm2
 vpaddw %ymm2, %ymm1, %ymm10
 vpsubw %ymm2, %ymm1, %ymm14
-vmovdqa %ymm0, 320(%rsp)
+vmovdqa %ymm0, 320(%r8)
 vmovdqu 1032(%rsi), %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm1
-vpaddw 224(%rsp), %ymm7, %ymm2
+vpaddw 96(%r8), %ymm0, %ymm1
+vpaddw 224(%r8), %ymm7, %ymm2
 vpaddw %ymm2, %ymm1, %ymm11
 vpsubw %ymm2, %ymm1, %ymm15
-vmovdqa %ymm0, 352(%rsp)
+vmovdqa %ymm0, 352(%r8)
 vmovdqa %ymm8, 928(%rax)
 vmovdqa %ymm9, 1024(%rax)
 vpaddw %ymm8, %ymm9, %ymm0
@@ -834,35 +835,35 @@
 vmovdqa %ymm1, 2464(%rax)
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 2560(%rax)
-vmovdqa 256(%rsp), %ymm0
+vmovdqa 256(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm0
+vpaddw 0(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm4, %ymm1
-vpaddw 128(%rsp), %ymm1, %ymm1
+vpaddw 128(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm8
 vpsubw %ymm1, %ymm0, %ymm12
-vmovdqa 288(%rsp), %ymm0
+vmovdqa 288(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm0
+vpaddw 32(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm5, %ymm1
-vpaddw 160(%rsp), %ymm1, %ymm1
+vpaddw 160(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm9
 vpsubw %ymm1, %ymm0, %ymm13
-vmovdqa 320(%rsp), %ymm0
+vmovdqa 320(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm0
+vpaddw 64(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm6, %ymm1
-vpaddw 192(%rsp), %ymm1, %ymm1
+vpaddw 192(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm10
 vpsubw %ymm1, %ymm0, %ymm14
-vmovdqa 352(%rsp), %ymm0
+vmovdqa 352(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm0
+vpaddw 96(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm7, %ymm1
-vpaddw 224(%rsp), %ymm1, %ymm1
+vpaddw 224(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm11
 vpsubw %ymm1, %ymm0, %ymm15
@@ -895,29 +896,29 @@
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 4288(%rax)
 vpmullw %ymm3, %ymm4, %ymm0
-vpaddw 256(%rsp), %ymm0, %ymm0
+vpaddw 256(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 128(%rsp), %ymm0, %ymm0
+vpaddw 128(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm12
+vpaddw 0(%r8), %ymm0, %ymm12
 vpmullw %ymm3, %ymm5, %ymm0
-vpaddw 288(%rsp), %ymm0, %ymm0
+vpaddw 288(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 160(%rsp), %ymm0, %ymm0
+vpaddw 160(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm13
+vpaddw 32(%r8), %ymm0, %ymm13
 vpmullw %ymm3, %ymm6, %ymm0
-vpaddw 320(%rsp), %ymm0, %ymm0
+vpaddw 320(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 192(%rsp), %ymm0, %ymm0
+vpaddw 192(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm14
+vpaddw 64(%r8), %ymm0, %ymm14
 vpmullw %ymm3, %ymm7, %ymm0
-vpaddw 352(%rsp), %ymm0, %ymm0
+vpaddw 352(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 224(%rsp), %ymm0, %ymm0
+vpaddw 224(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm15
+vpaddw 96(%r8), %ymm0, %ymm15
 vmovdqa %ymm12, 4384(%rax)
 vmovdqa %ymm13, 4480(%rax)
 vpaddw %ymm12, %ymm13, %ymm0
@@ -972,38 +973,38 @@
 vmovdqa %ymm15, 5856(%r11)
 vpaddw %ymm14, %ymm15, %ymm14
 vmovdqa %ymm14, 5952(%r11)
-vmovdqa %ymm0, 0(%rsp)
-vmovdqa %ymm1, 32(%rsp)
-vmovdqa %ymm2, 64(%rsp)
-vmovdqa %ymm12, 96(%rsp)
-vmovdqa %ymm8, 128(%rsp)
-vmovdqa %ymm9, 160(%rsp)
-vmovdqa %ymm10, 192(%rsp)
-vmovdqa %ymm11, 224(%rsp)
+vmovdqa %ymm0, 0(%r8)
+vmovdqa %ymm1, 32(%r8)
+vmovdqa %ymm2, 64(%r8)
+vmovdqa %ymm12, 96(%r8)
+vmovdqa %ymm8, 128(%r8)
+vmovdqa %ymm9, 160(%r8)
+vmovdqa %ymm10, 192(%r8)
+vmovdqa %ymm11, 224(%r8)
 vmovdqu 704(%rdx), %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm1
-vpaddw 128(%rsp), %ymm4, %ymm2
+vpaddw 0(%r8), %ymm0, %ymm1
+vpaddw 128(%r8), %ymm4, %ymm2
 vpaddw %ymm2, %ymm1, %ymm8
 vpsubw %ymm2, %ymm1, %ymm12
-vmovdqa %ymm0, 256(%rsp)
+vmovdqa %ymm0, 256(%r8)
 vmovdqu 792(%rdx), %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm1
-vpaddw 160(%rsp), %ymm5, %ymm2
+vpaddw 32(%r8), %ymm0, %ymm1
+vpaddw 160(%r8), %ymm5, %ymm2
 vpaddw %ymm2, %ymm1, %ymm9
 vpsubw %ymm2, %ymm1, %ymm13
-vmovdqa %ymm0, 288(%rsp)
+vmovdqa %ymm0, 288(%r8)
 vmovdqu 880(%rdx), %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm1
-vpaddw 192(%rsp), %ymm6, %ymm2
+vpaddw 64(%r8), %ymm0, %ymm1
+vpaddw 192(%r8), %ymm6, %ymm2
 vpaddw %ymm2, %ymm1, %ymm10
 vpsubw %ymm2, %ymm1, %ymm14
-vmovdqa %ymm0, 320(%rsp)
+vmovdqa %ymm0, 320(%r8)
 vmovdqu 968(%rdx), %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm1
-vpaddw 224(%rsp), %ymm7, %ymm2
+vpaddw 96(%r8), %ymm0, %ymm1
+vpaddw 224(%r8), %ymm7, %ymm2
 vpaddw %ymm2, %ymm1, %ymm11
 vpsubw %ymm2, %ymm1, %ymm15
-vmovdqa %ymm0, 352(%rsp)
+vmovdqa %ymm0, 352(%r8)
 vmovdqa %ymm8, 864(%r11)
 vmovdqa %ymm9, 960(%r11)
 vpaddw %ymm8, %ymm9, %ymm0
@@ -1032,35 +1033,35 @@
 vmovdqa %ymm1, 2400(%r11)
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 2496(%r11)
-vmovdqa 256(%rsp), %ymm0
+vmovdqa 256(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm0
+vpaddw 0(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm4, %ymm1
-vpaddw 128(%rsp), %ymm1, %ymm1
+vpaddw 128(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm8
 vpsubw %ymm1, %ymm0, %ymm12
-vmovdqa 288(%rsp), %ymm0
+vmovdqa 288(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm0
+vpaddw 32(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm5, %ymm1
-vpaddw 160(%rsp), %ymm1, %ymm1
+vpaddw 160(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm9
 vpsubw %ymm1, %ymm0, %ymm13
-vmovdqa 320(%rsp), %ymm0
+vmovdqa 320(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm0
+vpaddw 64(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm6, %ymm1
-vpaddw 192(%rsp), %ymm1, %ymm1
+vpaddw 192(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm10
 vpsubw %ymm1, %ymm0, %ymm14
-vmovdqa 352(%rsp), %ymm0
+vmovdqa 352(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm0
+vpaddw 96(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm7, %ymm1
-vpaddw 224(%rsp), %ymm1, %ymm1
+vpaddw 224(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm11
 vpsubw %ymm1, %ymm0, %ymm15
@@ -1093,29 +1094,29 @@
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 4224(%r11)
 vpmullw %ymm3, %ymm4, %ymm0
-vpaddw 256(%rsp), %ymm0, %ymm0
+vpaddw 256(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 128(%rsp), %ymm0, %ymm0
+vpaddw 128(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm12
+vpaddw 0(%r8), %ymm0, %ymm12
 vpmullw %ymm3, %ymm5, %ymm0
-vpaddw 288(%rsp), %ymm0, %ymm0
+vpaddw 288(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 160(%rsp), %ymm0, %ymm0
+vpaddw 160(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm13
+vpaddw 32(%r8), %ymm0, %ymm13
 vpmullw %ymm3, %ymm6, %ymm0
-vpaddw 320(%rsp), %ymm0, %ymm0
+vpaddw 320(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 192(%rsp), %ymm0, %ymm0
+vpaddw 192(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm14
+vpaddw 64(%r8), %ymm0, %ymm14
 vpmullw %ymm3, %ymm7, %ymm0
-vpaddw 352(%rsp), %ymm0, %ymm0
+vpaddw 352(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 224(%rsp), %ymm0, %ymm0
+vpaddw 224(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm15
+vpaddw 96(%r8), %ymm0, %ymm15
 vmovdqa %ymm12, 4320(%r11)
 vmovdqa %ymm13, 4416(%r11)
 vpaddw %ymm12, %ymm13, %ymm0
@@ -1170,38 +1171,38 @@
 vmovdqa %ymm15, 5888(%r11)
 vpaddw %ymm14, %ymm15, %ymm14
 vmovdqa %ymm14, 5984(%r11)
-vmovdqa %ymm0, 0(%rsp)
-vmovdqa %ymm1, 32(%rsp)
-vmovdqa %ymm2, 64(%rsp)
-vmovdqa %ymm12, 96(%rsp)
-vmovdqa %ymm8, 128(%rsp)
-vmovdqa %ymm9, 160(%rsp)
-vmovdqa %ymm10, 192(%rsp)
-vmovdqa %ymm11, 224(%rsp)
+vmovdqa %ymm0, 0(%r8)
+vmovdqa %ymm1, 32(%r8)
+vmovdqa %ymm2, 64(%r8)
+vmovdqa %ymm12, 96(%r8)
+vmovdqa %ymm8, 128(%r8)
+vmovdqa %ymm9, 160(%r8)
+vmovdqa %ymm10, 192(%r8)
+vmovdqa %ymm11, 224(%r8)
 vmovdqu 736(%rdx), %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm1
-vpaddw 128(%rsp), %ymm4, %ymm2
+vpaddw 0(%r8), %ymm0, %ymm1
+vpaddw 128(%r8), %ymm4, %ymm2
 vpaddw %ymm2, %ymm1, %ymm8
 vpsubw %ymm2, %ymm1, %ymm12
-vmovdqa %ymm0, 256(%rsp)
+vmovdqa %ymm0, 256(%r8)
 vmovdqu 824(%rdx), %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm1
-vpaddw 160(%rsp), %ymm5, %ymm2
+vpaddw 32(%r8), %ymm0, %ymm1
+vpaddw 160(%r8), %ymm5, %ymm2
 vpaddw %ymm2, %ymm1, %ymm9
 vpsubw %ymm2, %ymm1, %ymm13
-vmovdqa %ymm0, 288(%rsp)
+vmovdqa %ymm0, 288(%r8)
 vmovdqu 912(%rdx), %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm1
-vpaddw 192(%rsp), %ymm6, %ymm2
+vpaddw 64(%r8), %ymm0, %ymm1
+vpaddw 192(%r8), %ymm6, %ymm2
 vpaddw %ymm2, %ymm1, %ymm10
 vpsubw %ymm2, %ymm1, %ymm14
-vmovdqa %ymm0, 320(%rsp)
+vmovdqa %ymm0, 320(%r8)
 vmovdqu 1000(%rdx), %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm1
-vpaddw 224(%rsp), %ymm7, %ymm2
+vpaddw 96(%r8), %ymm0, %ymm1
+vpaddw 224(%r8), %ymm7, %ymm2
 vpaddw %ymm2, %ymm1, %ymm11
 vpsubw %ymm2, %ymm1, %ymm15
-vmovdqa %ymm0, 352(%rsp)
+vmovdqa %ymm0, 352(%r8)
 vmovdqa %ymm8, 896(%r11)
 vmovdqa %ymm9, 992(%r11)
 vpaddw %ymm8, %ymm9, %ymm0
@@ -1230,35 +1231,35 @@
 vmovdqa %ymm1, 2432(%r11)
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 2528(%r11)
-vmovdqa 256(%rsp), %ymm0
+vmovdqa 256(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm0
+vpaddw 0(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm4, %ymm1
-vpaddw 128(%rsp), %ymm1, %ymm1
+vpaddw 128(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm8
 vpsubw %ymm1, %ymm0, %ymm12
-vmovdqa 288(%rsp), %ymm0
+vmovdqa 288(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm0
+vpaddw 32(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm5, %ymm1
-vpaddw 160(%rsp), %ymm1, %ymm1
+vpaddw 160(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm9
 vpsubw %ymm1, %ymm0, %ymm13
-vmovdqa 320(%rsp), %ymm0
+vmovdqa 320(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm0
+vpaddw 64(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm6, %ymm1
-vpaddw 192(%rsp), %ymm1, %ymm1
+vpaddw 192(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm10
 vpsubw %ymm1, %ymm0, %ymm14
-vmovdqa 352(%rsp), %ymm0
+vmovdqa 352(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm0
+vpaddw 96(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm7, %ymm1
-vpaddw 224(%rsp), %ymm1, %ymm1
+vpaddw 224(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm11
 vpsubw %ymm1, %ymm0, %ymm15
@@ -1291,29 +1292,29 @@
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 4256(%r11)
 vpmullw %ymm3, %ymm4, %ymm0
-vpaddw 256(%rsp), %ymm0, %ymm0
+vpaddw 256(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 128(%rsp), %ymm0, %ymm0
+vpaddw 128(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm12
+vpaddw 0(%r8), %ymm0, %ymm12
 vpmullw %ymm3, %ymm5, %ymm0
-vpaddw 288(%rsp), %ymm0, %ymm0
+vpaddw 288(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 160(%rsp), %ymm0, %ymm0
+vpaddw 160(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm13
+vpaddw 32(%r8), %ymm0, %ymm13
 vpmullw %ymm3, %ymm6, %ymm0
-vpaddw 320(%rsp), %ymm0, %ymm0
+vpaddw 320(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 192(%rsp), %ymm0, %ymm0
+vpaddw 192(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm14
+vpaddw 64(%r8), %ymm0, %ymm14
 vpmullw %ymm3, %ymm7, %ymm0
-vpaddw 352(%rsp), %ymm0, %ymm0
+vpaddw 352(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 224(%rsp), %ymm0, %ymm0
+vpaddw 224(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm15
+vpaddw 96(%r8), %ymm0, %ymm15
 vmovdqa %ymm12, 4352(%r11)
 vmovdqa %ymm13, 4448(%r11)
 vpaddw %ymm12, %ymm13, %ymm0
@@ -1335,8 +1336,20 @@
 vmovdqu 1120(%rdx), %ymm4
 vmovdqu 1208(%rdx), %ymm5
 vmovdqu 1296(%rdx), %ymm6
-vmovdqu 1384(%rdx), %ymm7
-vpand mask_low9words(%rip), %ymm7, %ymm7
+
+# Only 18 bytes more can be read, but vmovdqu reads 32.
+# Copy 18 bytes to the red zone and zero pad to 32 bytes.
+xor %r9, %r9
+movq %r9, -16(%rsp)
+movq %r9, -8(%rsp)
+movq 1384(%rdx), %r9
+movq %r9, -32(%rsp)
+movq 1384+8(%rdx), %r9
+movq %r9, -24(%rsp)
+movw 1384+16(%rdx), %r9w
+movw %r9w, -16(%rsp)
+vmovdqu -32(%rsp), %ymm7
+
 vmovdqu 416(%rdx), %ymm8
 vmovdqu 504(%rdx), %ymm9
 vmovdqu 592(%rdx), %ymm10
@@ -1369,38 +1382,38 @@
 vmovdqa %ymm15, 5920(%r11)
 vpaddw %ymm14, %ymm15, %ymm14
 vmovdqa %ymm14, 6016(%r11)
-vmovdqa %ymm0, 0(%rsp)
-vmovdqa %ymm1, 32(%rsp)
-vmovdqa %ymm2, 64(%rsp)
-vmovdqa %ymm12, 96(%rsp)
-vmovdqa %ymm8, 128(%rsp)
-vmovdqa %ymm9, 160(%rsp)
-vmovdqa %ymm10, 192(%rsp)
-vmovdqa %ymm11, 224(%rsp)
+vmovdqa %ymm0, 0(%r8)
+vmovdqa %ymm1, 32(%r8)
+vmovdqa %ymm2, 64(%r8)
+vmovdqa %ymm12, 96(%r8)
+vmovdqa %ymm8, 128(%r8)
+vmovdqa %ymm9, 160(%r8)
+vmovdqa %ymm10, 192(%r8)
+vmovdqa %ymm11, 224(%r8)
 vmovdqu 768(%rdx), %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm1
-vpaddw 128(%rsp), %ymm4, %ymm2
+vpaddw 0(%r8), %ymm0, %ymm1
+vpaddw 128(%r8), %ymm4, %ymm2
 vpaddw %ymm2, %ymm1, %ymm8
 vpsubw %ymm2, %ymm1, %ymm12
-vmovdqa %ymm0, 256(%rsp)
+vmovdqa %ymm0, 256(%r8)
 vmovdqu 856(%rdx), %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm1
-vpaddw 160(%rsp), %ymm5, %ymm2
+vpaddw 32(%r8), %ymm0, %ymm1
+vpaddw 160(%r8), %ymm5, %ymm2
 vpaddw %ymm2, %ymm1, %ymm9
 vpsubw %ymm2, %ymm1, %ymm13
-vmovdqa %ymm0, 288(%rsp)
+vmovdqa %ymm0, 288(%r8)
 vmovdqu 944(%rdx), %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm1
-vpaddw 192(%rsp), %ymm6, %ymm2
+vpaddw 64(%r8), %ymm0, %ymm1
+vpaddw 192(%r8), %ymm6, %ymm2
 vpaddw %ymm2, %ymm1, %ymm10
 vpsubw %ymm2, %ymm1, %ymm14
-vmovdqa %ymm0, 320(%rsp)
+vmovdqa %ymm0, 320(%r8)
 vmovdqu 1032(%rdx), %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm1
-vpaddw 224(%rsp), %ymm7, %ymm2
+vpaddw 96(%r8), %ymm0, %ymm1
+vpaddw 224(%r8), %ymm7, %ymm2
 vpaddw %ymm2, %ymm1, %ymm11
 vpsubw %ymm2, %ymm1, %ymm15
-vmovdqa %ymm0, 352(%rsp)
+vmovdqa %ymm0, 352(%r8)
 vmovdqa %ymm8, 928(%r11)
 vmovdqa %ymm9, 1024(%r11)
 vpaddw %ymm8, %ymm9, %ymm0
@@ -1429,35 +1442,35 @@
 vmovdqa %ymm1, 2464(%r11)
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 2560(%r11)
-vmovdqa 256(%rsp), %ymm0
+vmovdqa 256(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm0
+vpaddw 0(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm4, %ymm1
-vpaddw 128(%rsp), %ymm1, %ymm1
+vpaddw 128(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm8
 vpsubw %ymm1, %ymm0, %ymm12
-vmovdqa 288(%rsp), %ymm0
+vmovdqa 288(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm0
+vpaddw 32(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm5, %ymm1
-vpaddw 160(%rsp), %ymm1, %ymm1
+vpaddw 160(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm9
 vpsubw %ymm1, %ymm0, %ymm13
-vmovdqa 320(%rsp), %ymm0
+vmovdqa 320(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm0
+vpaddw 64(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm6, %ymm1
-vpaddw 192(%rsp), %ymm1, %ymm1
+vpaddw 192(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm10
 vpsubw %ymm1, %ymm0, %ymm14
-vmovdqa 352(%rsp), %ymm0
+vmovdqa 352(%r8), %ymm0
 vpsllw $2, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm0
+vpaddw 96(%r8), %ymm0, %ymm0
 vpsllw $2, %ymm7, %ymm1
-vpaddw 224(%rsp), %ymm1, %ymm1
+vpaddw 224(%r8), %ymm1, %ymm1
 vpsllw $1, %ymm1, %ymm1
 vpaddw %ymm1, %ymm0, %ymm11
 vpsubw %ymm1, %ymm0, %ymm15
@@ -1490,29 +1503,29 @@
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 4288(%r11)
 vpmullw %ymm3, %ymm4, %ymm0
-vpaddw 256(%rsp), %ymm0, %ymm0
+vpaddw 256(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 128(%rsp), %ymm0, %ymm0
+vpaddw 128(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 0(%rsp), %ymm0, %ymm12
+vpaddw 0(%r8), %ymm0, %ymm12
 vpmullw %ymm3, %ymm5, %ymm0
-vpaddw 288(%rsp), %ymm0, %ymm0
+vpaddw 288(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 160(%rsp), %ymm0, %ymm0
+vpaddw 160(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 32(%rsp), %ymm0, %ymm13
+vpaddw 32(%r8), %ymm0, %ymm13
 vpmullw %ymm3, %ymm6, %ymm0
-vpaddw 320(%rsp), %ymm0, %ymm0
+vpaddw 320(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 192(%rsp), %ymm0, %ymm0
+vpaddw 192(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 64(%rsp), %ymm0, %ymm14
+vpaddw 64(%r8), %ymm0, %ymm14
 vpmullw %ymm3, %ymm7, %ymm0
-vpaddw 352(%rsp), %ymm0, %ymm0
+vpaddw 352(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 224(%rsp), %ymm0, %ymm0
+vpaddw 224(%r8), %ymm0, %ymm0
 vpmullw %ymm3, %ymm0, %ymm0
-vpaddw 96(%rsp), %ymm0, %ymm15
+vpaddw 96(%r8), %ymm0, %ymm15
 vmovdqa %ymm12, 4384(%r11)
 vmovdqa %ymm13, 4480(%r11)
 vpaddw %ymm12, %ymm13, %ymm0
@@ -1527,12 +1540,12 @@
 vmovdqa %ymm1, 5056(%r11)
 vpaddw %ymm0, %ymm1, %ymm0
 vmovdqa %ymm0, 5152(%r11)
-subq $9408, %rsp
+subq $9408, %r8
 mov $4, %ecx
 karatsuba_loop_4eced63f144beffcb0247f9c6f67d165:
-mov %rsp, %r9
-mov %rsp, %r10
-subq $32, %rsp
+mov %r8, %r9
+mov %r8, %r10
+subq $32, %r8
 vmovdqa 0(%rax), %ymm0
 vmovdqa 192(%rax), %ymm1
 vmovdqa 384(%rax), %ymm2
@@ -1573,7 +1586,7 @@
 vpunpckhwd 1248(%rax), %ymm2, %ymm1
 vpunpcklwd 1440(%rax), %ymm3, %ymm2
 vpunpckhwd 1440(%rax), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -1625,7 +1638,7 @@
 vmovdqa %ymm15, 416(%r9)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 448(%r9)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 224(%r9)
 vpermq $78, %ymm11, %ymm11
@@ -1671,7 +1684,7 @@
 vpunpckhwd 1280(%rax), %ymm2, %ymm1
 vpunpcklwd 1472(%rax), %ymm3, %ymm2
 vpunpckhwd 1472(%rax), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -1723,7 +1736,7 @@
 vmovdqa %ymm15, 928(%r9)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 960(%r9)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 736(%r9)
 vpermq $78, %ymm11, %ymm11
@@ -1769,7 +1782,7 @@
 vpunpckhwd 1312(%rax), %ymm2, %ymm1
 vpunpcklwd 1504(%rax), %ymm3, %ymm2
 vpunpckhwd 1504(%rax), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -1815,11 +1828,9 @@
 vmovdqa %ymm15, 1344(%r9)
 vinserti128 $0, %xmm7, %ymm0, %ymm15
 vmovdqa %ymm15, 1376(%r9)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 1248(%r9)
-addq $32, %rsp
-subq $32, %rsp
 vmovdqa 0(%r11), %ymm0
 vmovdqa 192(%r11), %ymm1
 vmovdqa 384(%r11), %ymm2
@@ -1860,7 +1871,7 @@
 vpunpckhwd 1248(%r11), %ymm2, %ymm1
 vpunpcklwd 1440(%r11), %ymm3, %ymm2
 vpunpckhwd 1440(%r11), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -1912,7 +1923,7 @@
 vmovdqa %ymm15, 1824(%r9)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 1856(%r9)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 1632(%r9)
 vpermq $78, %ymm11, %ymm11
@@ -1958,7 +1969,7 @@
 vpunpckhwd 1280(%r11), %ymm2, %ymm1
 vpunpcklwd 1472(%r11), %ymm3, %ymm2
 vpunpckhwd 1472(%r11), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -2010,7 +2021,7 @@
 vmovdqa %ymm15, 2336(%r9)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 2368(%r9)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 2144(%r9)
 vpermq $78, %ymm11, %ymm11
@@ -2056,7 +2067,7 @@
 vpunpckhwd 1312(%r11), %ymm2, %ymm1
 vpunpcklwd 1504(%r11), %ymm3, %ymm2
 vpunpckhwd 1504(%r11), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -2102,10 +2113,10 @@
 vmovdqa %ymm15, 2752(%r9)
 vinserti128 $0, %xmm7, %ymm0, %ymm15
 vmovdqa %ymm15, 2784(%r9)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 2656(%r9)
-addq $32, %rsp
+addq $32, %r8
 innerloop_4eced63f144beffcb0247f9c6f67d165:
 vmovdqa 0(%r9), %ymm0
 vmovdqa 1408(%r9), %ymm6
@@ -2638,17 +2649,17 @@
 vpaddw 512(%r9), %ymm5, %ymm5
 vpaddw 1920(%r9), %ymm11, %ymm11
 vpmullw %ymm0, %ymm6, %ymm12
-vmovdqa %ymm12, 5888(%rsp)
+vmovdqa %ymm12, 5888(%r8)
 vpmullw %ymm0, %ymm7, %ymm13
 vpmullw %ymm1, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 5920(%rsp)
+vmovdqa %ymm13, 5920(%r8)
 vpmullw %ymm0, %ymm8, %ymm12
 vpmullw %ymm1, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm2, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 5952(%rsp)
+vmovdqa %ymm12, 5952(%r8)
 vpmullw %ymm0, %ymm9, %ymm13
 vpmullw %ymm1, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -2656,7 +2667,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm3, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 5984(%rsp)
+vmovdqa %ymm13, 5984(%r8)
 vpmullw %ymm0, %ymm10, %ymm12
 vpmullw %ymm1, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -2666,7 +2677,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6016(%rsp)
+vmovdqa %ymm12, 6016(%r8)
 vpmullw %ymm0, %ymm11, %ymm13
 vpmullw %ymm1, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -2678,7 +2689,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm5, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6048(%rsp)
+vmovdqa %ymm13, 6048(%r8)
 vpmullw %ymm1, %ymm11, %ymm12
 vpmullw %ymm2, %ymm10, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -2688,7 +2699,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm5, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6080(%rsp)
+vmovdqa %ymm12, 6080(%r8)
 vpmullw %ymm2, %ymm11, %ymm13
 vpmullw %ymm3, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -2696,19 +2707,19 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm5, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6112(%rsp)
+vmovdqa %ymm13, 6112(%r8)
 vpmullw %ymm3, %ymm11, %ymm12
 vpmullw %ymm4, %ymm10, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm5, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6144(%rsp)
+vmovdqa %ymm12, 6144(%r8)
 vpmullw %ymm4, %ymm11, %ymm13
 vpmullw %ymm5, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6176(%rsp)
+vmovdqa %ymm13, 6176(%r8)
 vpmullw %ymm5, %ymm11, %ymm12
-vmovdqa %ymm12, 6208(%rsp)
+vmovdqa %ymm12, 6208(%r8)
 vmovdqa 192(%r9), %ymm0
 vmovdqa 1600(%r9), %ymm6
 vpaddw 544(%r9), %ymm0, %ymm0
@@ -2730,17 +2741,17 @@
 vpaddw 672(%r9), %ymm4, %ymm4
 vpaddw 2080(%r9), %ymm10, %ymm10
 vpmullw %ymm0, %ymm6, %ymm12
-vmovdqa %ymm12, 6272(%rsp)
+vmovdqa %ymm12, 6272(%r8)
 vpmullw %ymm0, %ymm7, %ymm13
 vpmullw %ymm1, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6304(%rsp)
+vmovdqa %ymm13, 6304(%r8)
 vpmullw %ymm0, %ymm8, %ymm12
 vpmullw %ymm1, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm2, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6336(%rsp)
+vmovdqa %ymm12, 6336(%r8)
 vpmullw %ymm0, %ymm9, %ymm13
 vpmullw %ymm1, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -2748,7 +2759,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm3, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6368(%rsp)
+vmovdqa %ymm13, 6368(%r8)
 vpmullw %ymm0, %ymm10, %ymm12
 vpmullw %ymm1, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -2758,7 +2769,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6400(%rsp)
+vmovdqa %ymm12, 6400(%r8)
 vpmullw %ymm1, %ymm10, %ymm13
 vpmullw %ymm2, %ymm9, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -2766,19 +2777,19 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm4, %ymm7, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6432(%rsp)
+vmovdqa %ymm13, 6432(%r8)
 vpmullw %ymm2, %ymm10, %ymm12
 vpmullw %ymm3, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm8, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6464(%rsp)
+vmovdqa %ymm12, 6464(%r8)
 vpmullw %ymm3, %ymm10, %ymm13
 vpmullw %ymm4, %ymm9, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6496(%rsp)
+vmovdqa %ymm13, 6496(%r8)
 vpmullw %ymm4, %ymm10, %ymm12
-vmovdqa %ymm12, 6528(%rsp)
+vmovdqa %ymm12, 6528(%r8)
 vpaddw 0(%r9), %ymm0, %ymm0
 vpaddw 1408(%r9), %ymm6, %ymm6
 vpaddw 352(%r9), %ymm0, %ymm0
@@ -2810,9 +2821,9 @@
 vpaddw %ymm15, %ymm12, %ymm12
 vpmullw %ymm5, %ymm6, %ymm15
 vpaddw %ymm15, %ymm12, %ymm12
-vpsubw 6048(%rsp), %ymm12, %ymm12
-vpsubw 6432(%rsp), %ymm12, %ymm12
-vmovdqa %ymm12, 6240(%rsp)
+vpsubw 6048(%r8), %ymm12, %ymm12
+vpsubw 6432(%r8), %ymm12, %ymm12
+vmovdqa %ymm12, 6240(%r8)
 vpmullw %ymm5, %ymm7, %ymm12
 vpmullw %ymm5, %ymm8, %ymm13
 vpmullw %ymm5, %ymm9, %ymm14
@@ -2862,134 +2873,134 @@
 vpmullw %ymm1, %ymm6, %ymm5
 vpaddw %ymm5, %ymm8, %ymm8
 vpmullw %ymm0, %ymm6, %ymm7
-vmovdqa 6080(%rsp), %ymm0
-vpsubw 6272(%rsp), %ymm0, %ymm0
+vmovdqa 6080(%r8), %ymm0
+vpsubw 6272(%r8), %ymm0, %ymm0
 vpsubw %ymm0, %ymm12, %ymm6
-vpsubw 6464(%rsp), %ymm6, %ymm6
-vmovdqa %ymm6, 6272(%rsp)
+vpsubw 6464(%r8), %ymm6, %ymm6
+vmovdqa %ymm6, 6272(%r8)
 vpaddw %ymm7, %ymm0, %ymm0
-vpsubw 5888(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 6080(%rsp)
-vmovdqa 6112(%rsp), %ymm1
-vpsubw 6304(%rsp), %ymm1, %ymm1
+vpsubw 5888(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 6080(%r8)
+vmovdqa 6112(%r8), %ymm1
+vpsubw 6304(%r8), %ymm1, %ymm1
 vpsubw %ymm1, %ymm13, %ymm7
-vpsubw 6496(%rsp), %ymm7, %ymm7
-vmovdqa %ymm7, 6304(%rsp)
+vpsubw 6496(%r8), %ymm7, %ymm7
+vmovdqa %ymm7, 6304(%r8)
 vpaddw %ymm8, %ymm1, %ymm1
-vpsubw 5920(%rsp), %ymm1, %ymm1
-vmovdqa %ymm1, 6112(%rsp)
-vmovdqa 6144(%rsp), %ymm2
-vpsubw 6336(%rsp), %ymm2, %ymm2
+vpsubw 5920(%r8), %ymm1, %ymm1
+vmovdqa %ymm1, 6112(%r8)
+vmovdqa 6144(%r8), %ymm2
+vpsubw 6336(%r8), %ymm2, %ymm2
 vpsubw %ymm2, %ymm14, %ymm8
-vpsubw 6528(%rsp), %ymm8, %ymm8
-vmovdqa %ymm8, 6336(%rsp)
+vpsubw 6528(%r8), %ymm8, %ymm8
+vmovdqa %ymm8, 6336(%r8)
 vpaddw %ymm9, %ymm2, %ymm2
-vpsubw 5952(%rsp), %ymm2, %ymm2
-vmovdqa %ymm2, 6144(%rsp)
-vmovdqa 6176(%rsp), %ymm3
-vpsubw 6368(%rsp), %ymm3, %ymm3
+vpsubw 5952(%r8), %ymm2, %ymm2
+vmovdqa %ymm2, 6144(%r8)
+vmovdqa 6176(%r8), %ymm3
+vpsubw 6368(%r8), %ymm3, %ymm3
 vpsubw %ymm3, %ymm15, %ymm9
-vmovdqa %ymm9, 6368(%rsp)
+vmovdqa %ymm9, 6368(%r8)
 vpaddw %ymm10, %ymm3, %ymm3
-vpsubw 5984(%rsp), %ymm3, %ymm3
-vmovdqa %ymm3, 6176(%rsp)
-vmovdqa 6208(%rsp), %ymm4
-vpsubw 6400(%rsp), %ymm4, %ymm4
+vpsubw 5984(%r8), %ymm3, %ymm3
+vmovdqa %ymm3, 6176(%r8)
+vmovdqa 6208(%r8), %ymm4
+vpsubw 6400(%r8), %ymm4, %ymm4
 vpaddw %ymm11, %ymm4, %ymm4
-vpsubw 6016(%rsp), %ymm4, %ymm4
-vmovdqa %ymm4, 6208(%rsp)
-vmovdqa 6208(%rsp), %ymm0
+vpsubw 6016(%r8), %ymm4, %ymm4
+vmovdqa %ymm4, 6208(%r8)
+vmovdqa 6208(%r8), %ymm0
 vpsubw 3136(%r10), %ymm0, %ymm0
 vpsubw 3840(%r10), %ymm0, %ymm0
 vmovdqa %ymm0, 3488(%r10)
 vmovdqa 3168(%r10), %ymm0
 vpsubw 3520(%r10), %ymm0, %ymm0
-vmovdqa 6240(%rsp), %ymm1
+vmovdqa 6240(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 3872(%r10), %ymm1, %ymm1
 vpsubw 2816(%r10), %ymm0, %ymm0
-vpaddw 5888(%rsp), %ymm0, %ymm0
+vpaddw 5888(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3168(%r10)
 vmovdqa %ymm1, 3520(%r10)
 vmovdqa 3200(%r10), %ymm0
 vpsubw 3552(%r10), %ymm0, %ymm0
-vmovdqa 6272(%rsp), %ymm1
+vmovdqa 6272(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 3904(%r10), %ymm1, %ymm1
 vpsubw 2848(%r10), %ymm0, %ymm0
-vpaddw 5920(%rsp), %ymm0, %ymm0
+vpaddw 5920(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3200(%r10)
 vmovdqa %ymm1, 3552(%r10)
 vmovdqa 3232(%r10), %ymm0
 vpsubw 3584(%r10), %ymm0, %ymm0
-vmovdqa 6304(%rsp), %ymm1
+vmovdqa 6304(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 3936(%r10), %ymm1, %ymm1
 vpsubw 2880(%r10), %ymm0, %ymm0
-vpaddw 5952(%rsp), %ymm0, %ymm0
+vpaddw 5952(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3232(%r10)
 vmovdqa %ymm1, 3584(%r10)
 vmovdqa 3264(%r10), %ymm0
 vpsubw 3616(%r10), %ymm0, %ymm0
-vmovdqa 6336(%rsp), %ymm1
+vmovdqa 6336(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 3968(%r10), %ymm1, %ymm1
 vpsubw 2912(%r10), %ymm0, %ymm0
-vpaddw 5984(%rsp), %ymm0, %ymm0
+vpaddw 5984(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3264(%r10)
 vmovdqa %ymm1, 3616(%r10)
 vmovdqa 3296(%r10), %ymm0
 vpsubw 3648(%r10), %ymm0, %ymm0
-vmovdqa 6368(%rsp), %ymm1
+vmovdqa 6368(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 4000(%r10), %ymm1, %ymm1
 vpsubw 2944(%r10), %ymm0, %ymm0
-vpaddw 6016(%rsp), %ymm0, %ymm0
+vpaddw 6016(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3296(%r10)
 vmovdqa %ymm1, 3648(%r10)
 vmovdqa 3328(%r10), %ymm0
 vpsubw 3680(%r10), %ymm0, %ymm0
-vmovdqa 6400(%rsp), %ymm1
+vmovdqa 6400(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 4032(%r10), %ymm1, %ymm1
 vpsubw 2976(%r10), %ymm0, %ymm0
-vpaddw 6048(%rsp), %ymm0, %ymm0
+vpaddw 6048(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3328(%r10)
 vmovdqa %ymm1, 3680(%r10)
 vmovdqa 3360(%r10), %ymm0
 vpsubw 3712(%r10), %ymm0, %ymm0
-vmovdqa 6432(%rsp), %ymm1
+vmovdqa 6432(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 4064(%r10), %ymm1, %ymm1
 vpsubw 3008(%r10), %ymm0, %ymm0
-vpaddw 6080(%rsp), %ymm0, %ymm0
+vpaddw 6080(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3360(%r10)
 vmovdqa %ymm1, 3712(%r10)
 vmovdqa 3392(%r10), %ymm0
 vpsubw 3744(%r10), %ymm0, %ymm0
-vmovdqa 6464(%rsp), %ymm1
+vmovdqa 6464(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 4096(%r10), %ymm1, %ymm1
 vpsubw 3040(%r10), %ymm0, %ymm0
-vpaddw 6112(%rsp), %ymm0, %ymm0
+vpaddw 6112(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3392(%r10)
 vmovdqa %ymm1, 3744(%r10)
 vmovdqa 3424(%r10), %ymm0
 vpsubw 3776(%r10), %ymm0, %ymm0
-vmovdqa 6496(%rsp), %ymm1
+vmovdqa 6496(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 4128(%r10), %ymm1, %ymm1
 vpsubw 3072(%r10), %ymm0, %ymm0
-vpaddw 6144(%rsp), %ymm0, %ymm0
+vpaddw 6144(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3424(%r10)
 vmovdqa %ymm1, 3776(%r10)
 vmovdqa 3456(%r10), %ymm0
 vpsubw 3808(%r10), %ymm0, %ymm0
-vmovdqa 6528(%rsp), %ymm1
+vmovdqa 6528(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 4160(%r10), %ymm1, %ymm1
 vpsubw 3104(%r10), %ymm0, %ymm0
-vpaddw 6176(%rsp), %ymm0, %ymm0
+vpaddw 6176(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3456(%r10)
 vmovdqa %ymm1, 3808(%r10)
 neg %ecx
@@ -3002,160 +3013,160 @@
 sub $1408, %r10
 vmovdqa 0(%r9), %ymm0
 vpaddw 704(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6592(%rsp)
+vmovdqa %ymm0, 6592(%r8)
 vmovdqa 1408(%r9), %ymm0
 vpaddw 2112(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7296(%rsp)
+vmovdqa %ymm0, 7296(%r8)
 vmovdqa 32(%r9), %ymm0
 vpaddw 736(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6624(%rsp)
+vmovdqa %ymm0, 6624(%r8)
 vmovdqa 1440(%r9), %ymm0
 vpaddw 2144(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7328(%rsp)
+vmovdqa %ymm0, 7328(%r8)
 vmovdqa 64(%r9), %ymm0
 vpaddw 768(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6656(%rsp)
+vmovdqa %ymm0, 6656(%r8)
 vmovdqa 1472(%r9), %ymm0
 vpaddw 2176(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7360(%rsp)
+vmovdqa %ymm0, 7360(%r8)
 vmovdqa 96(%r9), %ymm0
 vpaddw 800(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6688(%rsp)
+vmovdqa %ymm0, 6688(%r8)
 vmovdqa 1504(%r9), %ymm0
 vpaddw 2208(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7392(%rsp)
+vmovdqa %ymm0, 7392(%r8)
 vmovdqa 128(%r9), %ymm0
 vpaddw 832(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6720(%rsp)
+vmovdqa %ymm0, 6720(%r8)
 vmovdqa 1536(%r9), %ymm0
 vpaddw 2240(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7424(%rsp)
+vmovdqa %ymm0, 7424(%r8)
 vmovdqa 160(%r9), %ymm0
 vpaddw 864(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6752(%rsp)
+vmovdqa %ymm0, 6752(%r8)
 vmovdqa 1568(%r9), %ymm0
 vpaddw 2272(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7456(%rsp)
+vmovdqa %ymm0, 7456(%r8)
 vmovdqa 192(%r9), %ymm0
 vpaddw 896(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6784(%rsp)
+vmovdqa %ymm0, 6784(%r8)
 vmovdqa 1600(%r9), %ymm0
 vpaddw 2304(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7488(%rsp)
+vmovdqa %ymm0, 7488(%r8)
 vmovdqa 224(%r9), %ymm0
 vpaddw 928(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6816(%rsp)
+vmovdqa %ymm0, 6816(%r8)
 vmovdqa 1632(%r9), %ymm0
 vpaddw 2336(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7520(%rsp)
+vmovdqa %ymm0, 7520(%r8)
 vmovdqa 256(%r9), %ymm0
 vpaddw 960(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6848(%rsp)
+vmovdqa %ymm0, 6848(%r8)
 vmovdqa 1664(%r9), %ymm0
 vpaddw 2368(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7552(%rsp)
+vmovdqa %ymm0, 7552(%r8)
 vmovdqa 288(%r9), %ymm0
 vpaddw 992(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6880(%rsp)
+vmovdqa %ymm0, 6880(%r8)
 vmovdqa 1696(%r9), %ymm0
 vpaddw 2400(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7584(%rsp)
+vmovdqa %ymm0, 7584(%r8)
 vmovdqa 320(%r9), %ymm0
 vpaddw 1024(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6912(%rsp)
+vmovdqa %ymm0, 6912(%r8)
 vmovdqa 1728(%r9), %ymm0
 vpaddw 2432(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7616(%rsp)
+vmovdqa %ymm0, 7616(%r8)
 vmovdqa 352(%r9), %ymm0
 vpaddw 1056(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6944(%rsp)
+vmovdqa %ymm0, 6944(%r8)
 vmovdqa 1760(%r9), %ymm0
 vpaddw 2464(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7648(%rsp)
+vmovdqa %ymm0, 7648(%r8)
 vmovdqa 384(%r9), %ymm0
 vpaddw 1088(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 6976(%rsp)
+vmovdqa %ymm0, 6976(%r8)
 vmovdqa 1792(%r9), %ymm0
 vpaddw 2496(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7680(%rsp)
+vmovdqa %ymm0, 7680(%r8)
 vmovdqa 416(%r9), %ymm0
 vpaddw 1120(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7008(%rsp)
+vmovdqa %ymm0, 7008(%r8)
 vmovdqa 1824(%r9), %ymm0
 vpaddw 2528(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7712(%rsp)
+vmovdqa %ymm0, 7712(%r8)
 vmovdqa 448(%r9), %ymm0
 vpaddw 1152(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7040(%rsp)
+vmovdqa %ymm0, 7040(%r8)
 vmovdqa 1856(%r9), %ymm0
 vpaddw 2560(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7744(%rsp)
+vmovdqa %ymm0, 7744(%r8)
 vmovdqa 480(%r9), %ymm0
 vpaddw 1184(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7072(%rsp)
+vmovdqa %ymm0, 7072(%r8)
 vmovdqa 1888(%r9), %ymm0
 vpaddw 2592(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7776(%rsp)
+vmovdqa %ymm0, 7776(%r8)
 vmovdqa 512(%r9), %ymm0
 vpaddw 1216(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7104(%rsp)
+vmovdqa %ymm0, 7104(%r8)
 vmovdqa 1920(%r9), %ymm0
 vpaddw 2624(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7808(%rsp)
+vmovdqa %ymm0, 7808(%r8)
 vmovdqa 544(%r9), %ymm0
 vpaddw 1248(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7136(%rsp)
+vmovdqa %ymm0, 7136(%r8)
 vmovdqa 1952(%r9), %ymm0
 vpaddw 2656(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7840(%rsp)
+vmovdqa %ymm0, 7840(%r8)
 vmovdqa 576(%r9), %ymm0
 vpaddw 1280(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7168(%rsp)
+vmovdqa %ymm0, 7168(%r8)
 vmovdqa 1984(%r9), %ymm0
 vpaddw 2688(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7872(%rsp)
+vmovdqa %ymm0, 7872(%r8)
 vmovdqa 608(%r9), %ymm0
 vpaddw 1312(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7200(%rsp)
+vmovdqa %ymm0, 7200(%r8)
 vmovdqa 2016(%r9), %ymm0
 vpaddw 2720(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7904(%rsp)
+vmovdqa %ymm0, 7904(%r8)
 vmovdqa 640(%r9), %ymm0
 vpaddw 1344(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7232(%rsp)
+vmovdqa %ymm0, 7232(%r8)
 vmovdqa 2048(%r9), %ymm0
 vpaddw 2752(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7936(%rsp)
+vmovdqa %ymm0, 7936(%r8)
 vmovdqa 672(%r9), %ymm0
 vpaddw 1376(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7264(%rsp)
+vmovdqa %ymm0, 7264(%r8)
 vmovdqa 2080(%r9), %ymm0
 vpaddw 2784(%r9), %ymm0, %ymm0
-vmovdqa %ymm0, 7968(%rsp)
-vmovdqa 6592(%rsp), %ymm0
-vmovdqa 7296(%rsp), %ymm6
-vmovdqa 6624(%rsp), %ymm1
-vmovdqa 7328(%rsp), %ymm7
-vmovdqa 6656(%rsp), %ymm2
-vmovdqa 7360(%rsp), %ymm8
-vmovdqa 6688(%rsp), %ymm3
-vmovdqa 7392(%rsp), %ymm9
-vmovdqa 6720(%rsp), %ymm4
-vmovdqa 7424(%rsp), %ymm10
-vmovdqa 6752(%rsp), %ymm5
-vmovdqa 7456(%rsp), %ymm11
+vmovdqa %ymm0, 7968(%r8)
+vmovdqa 6592(%r8), %ymm0
+vmovdqa 7296(%r8), %ymm6
+vmovdqa 6624(%r8), %ymm1
+vmovdqa 7328(%r8), %ymm7
+vmovdqa 6656(%r8), %ymm2
+vmovdqa 7360(%r8), %ymm8
+vmovdqa 6688(%r8), %ymm3
+vmovdqa 7392(%r8), %ymm9
+vmovdqa 6720(%r8), %ymm4
+vmovdqa 7424(%r8), %ymm10
+vmovdqa 6752(%r8), %ymm5
+vmovdqa 7456(%r8), %ymm11
 vpmullw %ymm0, %ymm6, %ymm12
-vmovdqa %ymm12, 8000(%rsp)
+vmovdqa %ymm12, 8000(%r8)
 vpmullw %ymm0, %ymm7, %ymm13
 vpmullw %ymm1, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8032(%rsp)
+vmovdqa %ymm13, 8032(%r8)
 vpmullw %ymm0, %ymm8, %ymm12
 vpmullw %ymm1, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm2, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8064(%rsp)
+vmovdqa %ymm12, 8064(%r8)
 vpmullw %ymm0, %ymm9, %ymm13
 vpmullw %ymm1, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3163,7 +3174,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm3, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8096(%rsp)
+vmovdqa %ymm13, 8096(%r8)
 vpmullw %ymm0, %ymm10, %ymm12
 vpmullw %ymm1, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -3173,7 +3184,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8128(%rsp)
+vmovdqa %ymm12, 8128(%r8)
 vpmullw %ymm0, %ymm11, %ymm13
 vpmullw %ymm1, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3185,7 +3196,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm5, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8160(%rsp)
+vmovdqa %ymm13, 8160(%r8)
 vpmullw %ymm1, %ymm11, %ymm12
 vpmullw %ymm2, %ymm10, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -3195,7 +3206,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm5, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8192(%rsp)
+vmovdqa %ymm12, 8192(%r8)
 vpmullw %ymm2, %ymm11, %ymm13
 vpmullw %ymm3, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3203,41 +3214,41 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm5, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8224(%rsp)
+vmovdqa %ymm13, 8224(%r8)
 vpmullw %ymm3, %ymm11, %ymm12
 vpmullw %ymm4, %ymm10, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm5, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8256(%rsp)
+vmovdqa %ymm12, 8256(%r8)
 vpmullw %ymm4, %ymm11, %ymm13
 vpmullw %ymm5, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8288(%rsp)
+vmovdqa %ymm13, 8288(%r8)
 vpmullw %ymm5, %ymm11, %ymm12
-vmovdqa %ymm12, 8320(%rsp)
-vmovdqa 6784(%rsp), %ymm0
-vmovdqa 7488(%rsp), %ymm6
-vmovdqa 6816(%rsp), %ymm1
-vmovdqa 7520(%rsp), %ymm7
-vmovdqa 6848(%rsp), %ymm2
-vmovdqa 7552(%rsp), %ymm8
-vmovdqa 6880(%rsp), %ymm3
-vmovdqa 7584(%rsp), %ymm9
-vmovdqa 6912(%rsp), %ymm4
-vmovdqa 7616(%rsp), %ymm10
+vmovdqa %ymm12, 8320(%r8)
+vmovdqa 6784(%r8), %ymm0
+vmovdqa 7488(%r8), %ymm6
+vmovdqa 6816(%r8), %ymm1
+vmovdqa 7520(%r8), %ymm7
+vmovdqa 6848(%r8), %ymm2
+vmovdqa 7552(%r8), %ymm8
+vmovdqa 6880(%r8), %ymm3
+vmovdqa 7584(%r8), %ymm9
+vmovdqa 6912(%r8), %ymm4
+vmovdqa 7616(%r8), %ymm10
 vpmullw %ymm0, %ymm6, %ymm12
-vmovdqa %ymm12, 8384(%rsp)
+vmovdqa %ymm12, 8384(%r8)
 vpmullw %ymm0, %ymm7, %ymm13
 vpmullw %ymm1, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8416(%rsp)
+vmovdqa %ymm13, 8416(%r8)
 vpmullw %ymm0, %ymm8, %ymm12
 vpmullw %ymm1, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm2, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8448(%rsp)
+vmovdqa %ymm12, 8448(%r8)
 vpmullw %ymm0, %ymm9, %ymm13
 vpmullw %ymm1, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3245,7 +3256,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm3, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8480(%rsp)
+vmovdqa %ymm13, 8480(%r8)
 vpmullw %ymm0, %ymm10, %ymm12
 vpmullw %ymm1, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -3255,7 +3266,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8512(%rsp)
+vmovdqa %ymm12, 8512(%r8)
 vpmullw %ymm1, %ymm10, %ymm13
 vpmullw %ymm2, %ymm9, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3263,29 +3274,29 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm4, %ymm7, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8544(%rsp)
+vmovdqa %ymm13, 8544(%r8)
 vpmullw %ymm2, %ymm10, %ymm12
 vpmullw %ymm3, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm8, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8576(%rsp)
+vmovdqa %ymm12, 8576(%r8)
 vpmullw %ymm3, %ymm10, %ymm13
 vpmullw %ymm4, %ymm9, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8608(%rsp)
+vmovdqa %ymm13, 8608(%r8)
 vpmullw %ymm4, %ymm10, %ymm12
-vmovdqa %ymm12, 8640(%rsp)
-vpaddw 6592(%rsp), %ymm0, %ymm0
-vpaddw 7296(%rsp), %ymm6, %ymm6
-vpaddw 6624(%rsp), %ymm1, %ymm1
-vpaddw 7328(%rsp), %ymm7, %ymm7
-vpaddw 6656(%rsp), %ymm2, %ymm2
-vpaddw 7360(%rsp), %ymm8, %ymm8
-vpaddw 6688(%rsp), %ymm3, %ymm3
-vpaddw 7392(%rsp), %ymm9, %ymm9
-vpaddw 6720(%rsp), %ymm4, %ymm4
-vpaddw 7424(%rsp), %ymm10, %ymm10
+vmovdqa %ymm12, 8640(%r8)
+vpaddw 6592(%r8), %ymm0, %ymm0
+vpaddw 7296(%r8), %ymm6, %ymm6
+vpaddw 6624(%r8), %ymm1, %ymm1
+vpaddw 7328(%r8), %ymm7, %ymm7
+vpaddw 6656(%r8), %ymm2, %ymm2
+vpaddw 7360(%r8), %ymm8, %ymm8
+vpaddw 6688(%r8), %ymm3, %ymm3
+vpaddw 7392(%r8), %ymm9, %ymm9
+vpaddw 6720(%r8), %ymm4, %ymm4
+vpaddw 7424(%r8), %ymm10, %ymm10
 vpmullw %ymm0, %ymm11, %ymm12
 vpmullw %ymm1, %ymm10, %ymm15
 vpaddw %ymm15, %ymm12, %ymm12
@@ -3297,9 +3308,9 @@
 vpaddw %ymm15, %ymm12, %ymm12
 vpmullw %ymm5, %ymm6, %ymm15
 vpaddw %ymm15, %ymm12, %ymm12
-vpsubw 8160(%rsp), %ymm12, %ymm12
-vpsubw 8544(%rsp), %ymm12, %ymm12
-vmovdqa %ymm12, 8352(%rsp)
+vpsubw 8160(%r8), %ymm12, %ymm12
+vpsubw 8544(%r8), %ymm12, %ymm12
+vmovdqa %ymm12, 8352(%r8)
 vpmullw %ymm5, %ymm7, %ymm12
 vpmullw %ymm5, %ymm8, %ymm13
 vpmullw %ymm5, %ymm9, %ymm14
@@ -3349,66 +3360,66 @@
 vpmullw %ymm1, %ymm6, %ymm5
 vpaddw %ymm5, %ymm8, %ymm8
 vpmullw %ymm0, %ymm6, %ymm7
-vmovdqa 8192(%rsp), %ymm0
-vpsubw 8384(%rsp), %ymm0, %ymm0
+vmovdqa 8192(%r8), %ymm0
+vpsubw 8384(%r8), %ymm0, %ymm0
 vpsubw %ymm0, %ymm12, %ymm6
-vpsubw 8576(%rsp), %ymm6, %ymm6
-vmovdqa %ymm6, 8384(%rsp)
+vpsubw 8576(%r8), %ymm6, %ymm6
+vmovdqa %ymm6, 8384(%r8)
 vpaddw %ymm7, %ymm0, %ymm0
-vpsubw 8000(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8192(%rsp)
-vmovdqa 8224(%rsp), %ymm1
-vpsubw 8416(%rsp), %ymm1, %ymm1
+vpsubw 8000(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8192(%r8)
+vmovdqa 8224(%r8), %ymm1
+vpsubw 8416(%r8), %ymm1, %ymm1
 vpsubw %ymm1, %ymm13, %ymm7
-vpsubw 8608(%rsp), %ymm7, %ymm7
-vmovdqa %ymm7, 8416(%rsp)
+vpsubw 8608(%r8), %ymm7, %ymm7
+vmovdqa %ymm7, 8416(%r8)
 vpaddw %ymm8, %ymm1, %ymm1
-vpsubw 8032(%rsp), %ymm1, %ymm1
-vmovdqa %ymm1, 8224(%rsp)
-vmovdqa 8256(%rsp), %ymm2
-vpsubw 8448(%rsp), %ymm2, %ymm2
+vpsubw 8032(%r8), %ymm1, %ymm1
+vmovdqa %ymm1, 8224(%r8)
+vmovdqa 8256(%r8), %ymm2
+vpsubw 8448(%r8), %ymm2, %ymm2
 vpsubw %ymm2, %ymm14, %ymm8
-vpsubw 8640(%rsp), %ymm8, %ymm8
-vmovdqa %ymm8, 8448(%rsp)
+vpsubw 8640(%r8), %ymm8, %ymm8
+vmovdqa %ymm8, 8448(%r8)
 vpaddw %ymm9, %ymm2, %ymm2
-vpsubw 8064(%rsp), %ymm2, %ymm2
-vmovdqa %ymm2, 8256(%rsp)
-vmovdqa 8288(%rsp), %ymm3
-vpsubw 8480(%rsp), %ymm3, %ymm3
+vpsubw 8064(%r8), %ymm2, %ymm2
+vmovdqa %ymm2, 8256(%r8)
+vmovdqa 8288(%r8), %ymm3
+vpsubw 8480(%r8), %ymm3, %ymm3
 vpsubw %ymm3, %ymm15, %ymm9
-vmovdqa %ymm9, 8480(%rsp)
+vmovdqa %ymm9, 8480(%r8)
 vpaddw %ymm10, %ymm3, %ymm3
-vpsubw 8096(%rsp), %ymm3, %ymm3
-vmovdqa %ymm3, 8288(%rsp)
-vmovdqa 8320(%rsp), %ymm4
-vpsubw 8512(%rsp), %ymm4, %ymm4
+vpsubw 8096(%r8), %ymm3, %ymm3
+vmovdqa %ymm3, 8288(%r8)
+vmovdqa 8320(%r8), %ymm4
+vpsubw 8512(%r8), %ymm4, %ymm4
 vpaddw %ymm11, %ymm4, %ymm4
-vpsubw 8128(%rsp), %ymm4, %ymm4
-vmovdqa %ymm4, 8320(%rsp)
-vmovdqa 6944(%rsp), %ymm0
-vmovdqa 7648(%rsp), %ymm6
-vmovdqa 6976(%rsp), %ymm1
-vmovdqa 7680(%rsp), %ymm7
-vmovdqa 7008(%rsp), %ymm2
-vmovdqa 7712(%rsp), %ymm8
-vmovdqa 7040(%rsp), %ymm3
-vmovdqa 7744(%rsp), %ymm9
-vmovdqa 7072(%rsp), %ymm4
-vmovdqa 7776(%rsp), %ymm10
-vmovdqa 7104(%rsp), %ymm5
-vmovdqa 7808(%rsp), %ymm11
+vpsubw 8128(%r8), %ymm4, %ymm4
+vmovdqa %ymm4, 8320(%r8)
+vmovdqa 6944(%r8), %ymm0
+vmovdqa 7648(%r8), %ymm6
+vmovdqa 6976(%r8), %ymm1
+vmovdqa 7680(%r8), %ymm7
+vmovdqa 7008(%r8), %ymm2
+vmovdqa 7712(%r8), %ymm8
+vmovdqa 7040(%r8), %ymm3
+vmovdqa 7744(%r8), %ymm9
+vmovdqa 7072(%r8), %ymm4
+vmovdqa 7776(%r8), %ymm10
+vmovdqa 7104(%r8), %ymm5
+vmovdqa 7808(%r8), %ymm11
 vpmullw %ymm0, %ymm6, %ymm12
-vmovdqa %ymm12, 8704(%rsp)
+vmovdqa %ymm12, 8704(%r8)
 vpmullw %ymm0, %ymm7, %ymm13
 vpmullw %ymm1, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8736(%rsp)
+vmovdqa %ymm13, 8736(%r8)
 vpmullw %ymm0, %ymm8, %ymm12
 vpmullw %ymm1, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm2, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8768(%rsp)
+vmovdqa %ymm12, 8768(%r8)
 vpmullw %ymm0, %ymm9, %ymm13
 vpmullw %ymm1, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3416,7 +3427,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm3, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8800(%rsp)
+vmovdqa %ymm13, 8800(%r8)
 vpmullw %ymm0, %ymm10, %ymm12
 vpmullw %ymm1, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -3426,7 +3437,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8832(%rsp)
+vmovdqa %ymm12, 8832(%r8)
 vpmullw %ymm0, %ymm11, %ymm13
 vpmullw %ymm1, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3438,7 +3449,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm5, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8864(%rsp)
+vmovdqa %ymm13, 8864(%r8)
 vpmullw %ymm1, %ymm11, %ymm12
 vpmullw %ymm2, %ymm10, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -3448,7 +3459,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm5, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8896(%rsp)
+vmovdqa %ymm12, 8896(%r8)
 vpmullw %ymm2, %ymm11, %ymm13
 vpmullw %ymm3, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3456,41 +3467,41 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm5, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8928(%rsp)
+vmovdqa %ymm13, 8928(%r8)
 vpmullw %ymm3, %ymm11, %ymm12
 vpmullw %ymm4, %ymm10, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm5, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 8960(%rsp)
+vmovdqa %ymm12, 8960(%r8)
 vpmullw %ymm4, %ymm11, %ymm13
 vpmullw %ymm5, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 8992(%rsp)
+vmovdqa %ymm13, 8992(%r8)
 vpmullw %ymm5, %ymm11, %ymm12
-vmovdqa %ymm12, 9024(%rsp)
-vmovdqa 7136(%rsp), %ymm0
-vmovdqa 7840(%rsp), %ymm6
-vmovdqa 7168(%rsp), %ymm1
-vmovdqa 7872(%rsp), %ymm7
-vmovdqa 7200(%rsp), %ymm2
-vmovdqa 7904(%rsp), %ymm8
-vmovdqa 7232(%rsp), %ymm3
-vmovdqa 7936(%rsp), %ymm9
-vmovdqa 7264(%rsp), %ymm4
-vmovdqa 7968(%rsp), %ymm10
+vmovdqa %ymm12, 9024(%r8)
+vmovdqa 7136(%r8), %ymm0
+vmovdqa 7840(%r8), %ymm6
+vmovdqa 7168(%r8), %ymm1
+vmovdqa 7872(%r8), %ymm7
+vmovdqa 7200(%r8), %ymm2
+vmovdqa 7904(%r8), %ymm8
+vmovdqa 7232(%r8), %ymm3
+vmovdqa 7936(%r8), %ymm9
+vmovdqa 7264(%r8), %ymm4
+vmovdqa 7968(%r8), %ymm10
 vpmullw %ymm0, %ymm6, %ymm12
-vmovdqa %ymm12, 9088(%rsp)
+vmovdqa %ymm12, 9088(%r8)
 vpmullw %ymm0, %ymm7, %ymm13
 vpmullw %ymm1, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 9120(%rsp)
+vmovdqa %ymm13, 9120(%r8)
 vpmullw %ymm0, %ymm8, %ymm12
 vpmullw %ymm1, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm2, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 9152(%rsp)
+vmovdqa %ymm12, 9152(%r8)
 vpmullw %ymm0, %ymm9, %ymm13
 vpmullw %ymm1, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3498,7 +3509,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm3, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 9184(%rsp)
+vmovdqa %ymm13, 9184(%r8)
 vpmullw %ymm0, %ymm10, %ymm12
 vpmullw %ymm1, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -3508,7 +3519,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 9216(%rsp)
+vmovdqa %ymm12, 9216(%r8)
 vpmullw %ymm1, %ymm10, %ymm13
 vpmullw %ymm2, %ymm9, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3516,29 +3527,29 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm4, %ymm7, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 9248(%rsp)
+vmovdqa %ymm13, 9248(%r8)
 vpmullw %ymm2, %ymm10, %ymm12
 vpmullw %ymm3, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm8, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 9280(%rsp)
+vmovdqa %ymm12, 9280(%r8)
 vpmullw %ymm3, %ymm10, %ymm13
 vpmullw %ymm4, %ymm9, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 9312(%rsp)
+vmovdqa %ymm13, 9312(%r8)
 vpmullw %ymm4, %ymm10, %ymm12
-vmovdqa %ymm12, 9344(%rsp)
-vpaddw 6944(%rsp), %ymm0, %ymm0
-vpaddw 7648(%rsp), %ymm6, %ymm6
-vpaddw 6976(%rsp), %ymm1, %ymm1
-vpaddw 7680(%rsp), %ymm7, %ymm7
-vpaddw 7008(%rsp), %ymm2, %ymm2
-vpaddw 7712(%rsp), %ymm8, %ymm8
-vpaddw 7040(%rsp), %ymm3, %ymm3
-vpaddw 7744(%rsp), %ymm9, %ymm9
-vpaddw 7072(%rsp), %ymm4, %ymm4
-vpaddw 7776(%rsp), %ymm10, %ymm10
+vmovdqa %ymm12, 9344(%r8)
+vpaddw 6944(%r8), %ymm0, %ymm0
+vpaddw 7648(%r8), %ymm6, %ymm6
+vpaddw 6976(%r8), %ymm1, %ymm1
+vpaddw 7680(%r8), %ymm7, %ymm7
+vpaddw 7008(%r8), %ymm2, %ymm2
+vpaddw 7712(%r8), %ymm8, %ymm8
+vpaddw 7040(%r8), %ymm3, %ymm3
+vpaddw 7744(%r8), %ymm9, %ymm9
+vpaddw 7072(%r8), %ymm4, %ymm4
+vpaddw 7776(%r8), %ymm10, %ymm10
 vpmullw %ymm0, %ymm11, %ymm12
 vpmullw %ymm1, %ymm10, %ymm15
 vpaddw %ymm15, %ymm12, %ymm12
@@ -3550,9 +3561,9 @@
 vpaddw %ymm15, %ymm12, %ymm12
 vpmullw %ymm5, %ymm6, %ymm15
 vpaddw %ymm15, %ymm12, %ymm12
-vpsubw 8864(%rsp), %ymm12, %ymm12
-vpsubw 9248(%rsp), %ymm12, %ymm12
-vmovdqa %ymm12, 9056(%rsp)
+vpsubw 8864(%r8), %ymm12, %ymm12
+vpsubw 9248(%r8), %ymm12, %ymm12
+vmovdqa %ymm12, 9056(%r8)
 vpmullw %ymm5, %ymm7, %ymm12
 vpmullw %ymm5, %ymm8, %ymm13
 vpmullw %ymm5, %ymm9, %ymm14
@@ -3602,78 +3613,78 @@
 vpmullw %ymm1, %ymm6, %ymm5
 vpaddw %ymm5, %ymm8, %ymm8
 vpmullw %ymm0, %ymm6, %ymm7
-vmovdqa 8896(%rsp), %ymm0
-vpsubw 9088(%rsp), %ymm0, %ymm0
+vmovdqa 8896(%r8), %ymm0
+vpsubw 9088(%r8), %ymm0, %ymm0
 vpsubw %ymm0, %ymm12, %ymm6
-vpsubw 9280(%rsp), %ymm6, %ymm6
-vmovdqa %ymm6, 9088(%rsp)
+vpsubw 9280(%r8), %ymm6, %ymm6
+vmovdqa %ymm6, 9088(%r8)
 vpaddw %ymm7, %ymm0, %ymm0
-vpsubw 8704(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8896(%rsp)
-vmovdqa 8928(%rsp), %ymm1
-vpsubw 9120(%rsp), %ymm1, %ymm1
+vpsubw 8704(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8896(%r8)
+vmovdqa 8928(%r8), %ymm1
+vpsubw 9120(%r8), %ymm1, %ymm1
 vpsubw %ymm1, %ymm13, %ymm7
-vpsubw 9312(%rsp), %ymm7, %ymm7
-vmovdqa %ymm7, 9120(%rsp)
+vpsubw 9312(%r8), %ymm7, %ymm7
+vmovdqa %ymm7, 9120(%r8)
 vpaddw %ymm8, %ymm1, %ymm1
-vpsubw 8736(%rsp), %ymm1, %ymm1
-vmovdqa %ymm1, 8928(%rsp)
-vmovdqa 8960(%rsp), %ymm2
-vpsubw 9152(%rsp), %ymm2, %ymm2
+vpsubw 8736(%r8), %ymm1, %ymm1
+vmovdqa %ymm1, 8928(%r8)
+vmovdqa 8960(%r8), %ymm2
+vpsubw 9152(%r8), %ymm2, %ymm2
 vpsubw %ymm2, %ymm14, %ymm8
-vpsubw 9344(%rsp), %ymm8, %ymm8
-vmovdqa %ymm8, 9152(%rsp)
+vpsubw 9344(%r8), %ymm8, %ymm8
+vmovdqa %ymm8, 9152(%r8)
 vpaddw %ymm9, %ymm2, %ymm2
-vpsubw 8768(%rsp), %ymm2, %ymm2
-vmovdqa %ymm2, 8960(%rsp)
-vmovdqa 8992(%rsp), %ymm3
-vpsubw 9184(%rsp), %ymm3, %ymm3
+vpsubw 8768(%r8), %ymm2, %ymm2
+vmovdqa %ymm2, 8960(%r8)
+vmovdqa 8992(%r8), %ymm3
+vpsubw 9184(%r8), %ymm3, %ymm3
 vpsubw %ymm3, %ymm15, %ymm9
-vmovdqa %ymm9, 9184(%rsp)
+vmovdqa %ymm9, 9184(%r8)
 vpaddw %ymm10, %ymm3, %ymm3
-vpsubw 8800(%rsp), %ymm3, %ymm3
-vmovdqa %ymm3, 8992(%rsp)
-vmovdqa 9024(%rsp), %ymm4
-vpsubw 9216(%rsp), %ymm4, %ymm4
+vpsubw 8800(%r8), %ymm3, %ymm3
+vmovdqa %ymm3, 8992(%r8)
+vmovdqa 9024(%r8), %ymm4
+vpsubw 9216(%r8), %ymm4, %ymm4
 vpaddw %ymm11, %ymm4, %ymm4
-vpsubw 8832(%rsp), %ymm4, %ymm4
-vmovdqa %ymm4, 9024(%rsp)
-vmovdqa 6592(%rsp), %ymm0
-vmovdqa 7296(%rsp), %ymm6
-vpaddw 6944(%rsp), %ymm0, %ymm0
-vpaddw 7648(%rsp), %ymm6, %ymm6
-vmovdqa 6624(%rsp), %ymm1
-vmovdqa 7328(%rsp), %ymm7
-vpaddw 6976(%rsp), %ymm1, %ymm1
-vpaddw 7680(%rsp), %ymm7, %ymm7
-vmovdqa 6656(%rsp), %ymm2
-vmovdqa 7360(%rsp), %ymm8
-vpaddw 7008(%rsp), %ymm2, %ymm2
-vpaddw 7712(%rsp), %ymm8, %ymm8
-vmovdqa 6688(%rsp), %ymm3
-vmovdqa 7392(%rsp), %ymm9
-vpaddw 7040(%rsp), %ymm3, %ymm3
-vpaddw 7744(%rsp), %ymm9, %ymm9
-vmovdqa 6720(%rsp), %ymm4
-vmovdqa 7424(%rsp), %ymm10
-vpaddw 7072(%rsp), %ymm4, %ymm4
-vpaddw 7776(%rsp), %ymm10, %ymm10
-vmovdqa 6752(%rsp), %ymm5
-vmovdqa 7456(%rsp), %ymm11
-vpaddw 7104(%rsp), %ymm5, %ymm5
-vpaddw 7808(%rsp), %ymm11, %ymm11
+vpsubw 8832(%r8), %ymm4, %ymm4
+vmovdqa %ymm4, 9024(%r8)
+vmovdqa 6592(%r8), %ymm0
+vmovdqa 7296(%r8), %ymm6
+vpaddw 6944(%r8), %ymm0, %ymm0
+vpaddw 7648(%r8), %ymm6, %ymm6
+vmovdqa 6624(%r8), %ymm1
+vmovdqa 7328(%r8), %ymm7
+vpaddw 6976(%r8), %ymm1, %ymm1
+vpaddw 7680(%r8), %ymm7, %ymm7
+vmovdqa 6656(%r8), %ymm2
+vmovdqa 7360(%r8), %ymm8
+vpaddw 7008(%r8), %ymm2, %ymm2
+vpaddw 7712(%r8), %ymm8, %ymm8
+vmovdqa 6688(%r8), %ymm3
+vmovdqa 7392(%r8), %ymm9
+vpaddw 7040(%r8), %ymm3, %ymm3
+vpaddw 7744(%r8), %ymm9, %ymm9
+vmovdqa 6720(%r8), %ymm4
+vmovdqa 7424(%r8), %ymm10
+vpaddw 7072(%r8), %ymm4, %ymm4
+vpaddw 7776(%r8), %ymm10, %ymm10
+vmovdqa 6752(%r8), %ymm5
+vmovdqa 7456(%r8), %ymm11
+vpaddw 7104(%r8), %ymm5, %ymm5
+vpaddw 7808(%r8), %ymm11, %ymm11
 vpmullw %ymm0, %ymm6, %ymm12
-vmovdqa %ymm12, 5888(%rsp)
+vmovdqa %ymm12, 5888(%r8)
 vpmullw %ymm0, %ymm7, %ymm13
 vpmullw %ymm1, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 5920(%rsp)
+vmovdqa %ymm13, 5920(%r8)
 vpmullw %ymm0, %ymm8, %ymm12
 vpmullw %ymm1, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm2, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 5952(%rsp)
+vmovdqa %ymm12, 5952(%r8)
 vpmullw %ymm0, %ymm9, %ymm13
 vpmullw %ymm1, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3681,7 +3692,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm3, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 5984(%rsp)
+vmovdqa %ymm13, 5984(%r8)
 vpmullw %ymm0, %ymm10, %ymm12
 vpmullw %ymm1, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -3691,7 +3702,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6016(%rsp)
+vmovdqa %ymm12, 6016(%r8)
 vpmullw %ymm0, %ymm11, %ymm13
 vpmullw %ymm1, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3703,7 +3714,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm5, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6048(%rsp)
+vmovdqa %ymm13, 6048(%r8)
 vpmullw %ymm1, %ymm11, %ymm12
 vpmullw %ymm2, %ymm10, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -3713,7 +3724,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm5, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6080(%rsp)
+vmovdqa %ymm12, 6080(%r8)
 vpmullw %ymm2, %ymm11, %ymm13
 vpmullw %ymm3, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3721,51 +3732,51 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm5, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6112(%rsp)
+vmovdqa %ymm13, 6112(%r8)
 vpmullw %ymm3, %ymm11, %ymm12
 vpmullw %ymm4, %ymm10, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm5, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6144(%rsp)
+vmovdqa %ymm12, 6144(%r8)
 vpmullw %ymm4, %ymm11, %ymm13
 vpmullw %ymm5, %ymm10, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6176(%rsp)
+vmovdqa %ymm13, 6176(%r8)
 vpmullw %ymm5, %ymm11, %ymm12
-vmovdqa %ymm12, 6208(%rsp)
-vmovdqa 6784(%rsp), %ymm0
-vmovdqa 7488(%rsp), %ymm6
-vpaddw 7136(%rsp), %ymm0, %ymm0
-vpaddw 7840(%rsp), %ymm6, %ymm6
-vmovdqa 6816(%rsp), %ymm1
-vmovdqa 7520(%rsp), %ymm7
-vpaddw 7168(%rsp), %ymm1, %ymm1
-vpaddw 7872(%rsp), %ymm7, %ymm7
-vmovdqa 6848(%rsp), %ymm2
-vmovdqa 7552(%rsp), %ymm8
-vpaddw 7200(%rsp), %ymm2, %ymm2
-vpaddw 7904(%rsp), %ymm8, %ymm8
-vmovdqa 6880(%rsp), %ymm3
-vmovdqa 7584(%rsp), %ymm9
-vpaddw 7232(%rsp), %ymm3, %ymm3
-vpaddw 7936(%rsp), %ymm9, %ymm9
-vmovdqa 6912(%rsp), %ymm4
-vmovdqa 7616(%rsp), %ymm10
-vpaddw 7264(%rsp), %ymm4, %ymm4
-vpaddw 7968(%rsp), %ymm10, %ymm10
+vmovdqa %ymm12, 6208(%r8)
+vmovdqa 6784(%r8), %ymm0
+vmovdqa 7488(%r8), %ymm6
+vpaddw 7136(%r8), %ymm0, %ymm0
+vpaddw 7840(%r8), %ymm6, %ymm6
+vmovdqa 6816(%r8), %ymm1
+vmovdqa 7520(%r8), %ymm7
+vpaddw 7168(%r8), %ymm1, %ymm1
+vpaddw 7872(%r8), %ymm7, %ymm7
+vmovdqa 6848(%r8), %ymm2
+vmovdqa 7552(%r8), %ymm8
+vpaddw 7200(%r8), %ymm2, %ymm2
+vpaddw 7904(%r8), %ymm8, %ymm8
+vmovdqa 6880(%r8), %ymm3
+vmovdqa 7584(%r8), %ymm9
+vpaddw 7232(%r8), %ymm3, %ymm3
+vpaddw 7936(%r8), %ymm9, %ymm9
+vmovdqa 6912(%r8), %ymm4
+vmovdqa 7616(%r8), %ymm10
+vpaddw 7264(%r8), %ymm4, %ymm4
+vpaddw 7968(%r8), %ymm10, %ymm10
 vpmullw %ymm0, %ymm6, %ymm12
-vmovdqa %ymm12, 6272(%rsp)
+vmovdqa %ymm12, 6272(%r8)
 vpmullw %ymm0, %ymm7, %ymm13
 vpmullw %ymm1, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6304(%rsp)
+vmovdqa %ymm13, 6304(%r8)
 vpmullw %ymm0, %ymm8, %ymm12
 vpmullw %ymm1, %ymm7, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm2, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6336(%rsp)
+vmovdqa %ymm12, 6336(%r8)
 vpmullw %ymm0, %ymm9, %ymm13
 vpmullw %ymm1, %ymm8, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3773,7 +3784,7 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm3, %ymm6, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6368(%rsp)
+vmovdqa %ymm13, 6368(%r8)
 vpmullw %ymm0, %ymm10, %ymm12
 vpmullw %ymm1, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
@@ -3783,7 +3794,7 @@
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm6, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6400(%rsp)
+vmovdqa %ymm12, 6400(%r8)
 vpmullw %ymm1, %ymm10, %ymm13
 vpmullw %ymm2, %ymm9, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
@@ -3791,39 +3802,39 @@
 vpaddw %ymm13, %ymm15, %ymm13
 vpmullw %ymm4, %ymm7, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6432(%rsp)
+vmovdqa %ymm13, 6432(%r8)
 vpmullw %ymm2, %ymm10, %ymm12
 vpmullw %ymm3, %ymm9, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
 vpmullw %ymm4, %ymm8, %ymm15
 vpaddw %ymm12, %ymm15, %ymm12
-vmovdqa %ymm12, 6464(%rsp)
+vmovdqa %ymm12, 6464(%r8)
 vpmullw %ymm3, %ymm10, %ymm13
 vpmullw %ymm4, %ymm9, %ymm15
 vpaddw %ymm13, %ymm15, %ymm13
-vmovdqa %ymm13, 6496(%rsp)
+vmovdqa %ymm13, 6496(%r8)
 vpmullw %ymm4, %ymm10, %ymm12
-vmovdqa %ymm12, 6528(%rsp)
-vpaddw 6592(%rsp), %ymm0, %ymm0
-vpaddw 7296(%rsp), %ymm6, %ymm6
-vpaddw 6944(%rsp), %ymm0, %ymm0
-vpaddw 7648(%rsp), %ymm6, %ymm6
-vpaddw 6624(%rsp), %ymm1, %ymm1
-vpaddw 7328(%rsp), %ymm7, %ymm7
-vpaddw 6976(%rsp), %ymm1, %ymm1
-vpaddw 7680(%rsp), %ymm7, %ymm7
-vpaddw 6656(%rsp), %ymm2, %ymm2
-vpaddw 7360(%rsp), %ymm8, %ymm8
-vpaddw 7008(%rsp), %ymm2, %ymm2
-vpaddw 7712(%rsp), %ymm8, %ymm8
-vpaddw 6688(%rsp), %ymm3, %ymm3
-vpaddw 7392(%rsp), %ymm9, %ymm9
-vpaddw 7040(%rsp), %ymm3, %ymm3
-vpaddw 7744(%rsp), %ymm9, %ymm9
-vpaddw 6720(%rsp), %ymm4, %ymm4
-vpaddw 7424(%rsp), %ymm10, %ymm10
-vpaddw 7072(%rsp), %ymm4, %ymm4
-vpaddw 7776(%rsp), %ymm10, %ymm10
+vmovdqa %ymm12, 6528(%r8)
+vpaddw 6592(%r8), %ymm0, %ymm0
+vpaddw 7296(%r8), %ymm6, %ymm6
+vpaddw 6944(%r8), %ymm0, %ymm0
+vpaddw 7648(%r8), %ymm6, %ymm6
+vpaddw 6624(%r8), %ymm1, %ymm1
+vpaddw 7328(%r8), %ymm7, %ymm7
+vpaddw 6976(%r8), %ymm1, %ymm1
+vpaddw 7680(%r8), %ymm7, %ymm7
+vpaddw 6656(%r8), %ymm2, %ymm2
+vpaddw 7360(%r8), %ymm8, %ymm8
+vpaddw 7008(%r8), %ymm2, %ymm2
+vpaddw 7712(%r8), %ymm8, %ymm8
+vpaddw 6688(%r8), %ymm3, %ymm3
+vpaddw 7392(%r8), %ymm9, %ymm9
+vpaddw 7040(%r8), %ymm3, %ymm3
+vpaddw 7744(%r8), %ymm9, %ymm9
+vpaddw 6720(%r8), %ymm4, %ymm4
+vpaddw 7424(%r8), %ymm10, %ymm10
+vpaddw 7072(%r8), %ymm4, %ymm4
+vpaddw 7776(%r8), %ymm10, %ymm10
 vpmullw %ymm0, %ymm11, %ymm12
 vpmullw %ymm1, %ymm10, %ymm15
 vpaddw %ymm15, %ymm12, %ymm12
@@ -3835,9 +3846,9 @@
 vpaddw %ymm15, %ymm12, %ymm12
 vpmullw %ymm5, %ymm6, %ymm15
 vpaddw %ymm15, %ymm12, %ymm12
-vpsubw 6048(%rsp), %ymm12, %ymm12
-vpsubw 6432(%rsp), %ymm12, %ymm12
-vmovdqa %ymm12, 6240(%rsp)
+vpsubw 6048(%r8), %ymm12, %ymm12
+vpsubw 6432(%r8), %ymm12, %ymm12
+vmovdqa %ymm12, 6240(%r8)
 vpmullw %ymm5, %ymm7, %ymm12
 vpmullw %ymm5, %ymm8, %ymm13
 vpmullw %ymm5, %ymm9, %ymm14
@@ -3887,125 +3898,125 @@
 vpmullw %ymm1, %ymm6, %ymm5
 vpaddw %ymm5, %ymm8, %ymm8
 vpmullw %ymm0, %ymm6, %ymm7
-vmovdqa 6080(%rsp), %ymm0
-vpsubw 6272(%rsp), %ymm0, %ymm0
+vmovdqa 6080(%r8), %ymm0
+vpsubw 6272(%r8), %ymm0, %ymm0
 vpsubw %ymm0, %ymm12, %ymm6
-vpsubw 6464(%rsp), %ymm6, %ymm6
-vmovdqa %ymm6, 6272(%rsp)
+vpsubw 6464(%r8), %ymm6, %ymm6
+vmovdqa %ymm6, 6272(%r8)
 vpaddw %ymm7, %ymm0, %ymm0
-vpsubw 5888(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 6080(%rsp)
-vmovdqa 6112(%rsp), %ymm1
-vpsubw 6304(%rsp), %ymm1, %ymm1
+vpsubw 5888(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 6080(%r8)
+vmovdqa 6112(%r8), %ymm1
+vpsubw 6304(%r8), %ymm1, %ymm1
 vpsubw %ymm1, %ymm13, %ymm7
-vpsubw 6496(%rsp), %ymm7, %ymm7
-vmovdqa %ymm7, 6304(%rsp)
+vpsubw 6496(%r8), %ymm7, %ymm7
+vmovdqa %ymm7, 6304(%r8)
 vpaddw %ymm8, %ymm1, %ymm1
-vpsubw 5920(%rsp), %ymm1, %ymm1
-vmovdqa %ymm1, 6112(%rsp)
-vmovdqa 6144(%rsp), %ymm2
-vpsubw 6336(%rsp), %ymm2, %ymm2
+vpsubw 5920(%r8), %ymm1, %ymm1
+vmovdqa %ymm1, 6112(%r8)
+vmovdqa 6144(%r8), %ymm2
+vpsubw 6336(%r8), %ymm2, %ymm2
 vpsubw %ymm2, %ymm14, %ymm8
-vpsubw 6528(%rsp), %ymm8, %ymm8
-vmovdqa %ymm8, 6336(%rsp)
+vpsubw 6528(%r8), %ymm8, %ymm8
+vmovdqa %ymm8, 6336(%r8)
 vpaddw %ymm9, %ymm2, %ymm2
-vpsubw 5952(%rsp), %ymm2, %ymm2
-vmovdqa %ymm2, 6144(%rsp)
-vmovdqa 6176(%rsp), %ymm3
-vpsubw 6368(%rsp), %ymm3, %ymm3
+vpsubw 5952(%r8), %ymm2, %ymm2
+vmovdqa %ymm2, 6144(%r8)
+vmovdqa 6176(%r8), %ymm3
+vpsubw 6368(%r8), %ymm3, %ymm3
 vpsubw %ymm3, %ymm15, %ymm9
-vmovdqa %ymm9, 6368(%rsp)
+vmovdqa %ymm9, 6368(%r8)
 vpaddw %ymm10, %ymm3, %ymm3
-vpsubw 5984(%rsp), %ymm3, %ymm3
-vmovdqa %ymm3, 6176(%rsp)
-vmovdqa 6208(%rsp), %ymm4
-vpsubw 6400(%rsp), %ymm4, %ymm4
+vpsubw 5984(%r8), %ymm3, %ymm3
+vmovdqa %ymm3, 6176(%r8)
+vmovdqa 6208(%r8), %ymm4
+vpsubw 6400(%r8), %ymm4, %ymm4
 vpaddw %ymm11, %ymm4, %ymm4
-vpsubw 6016(%rsp), %ymm4, %ymm4
-vmovdqa %ymm4, 6208(%rsp)
-vmovdqa 8352(%rsp), %ymm0
-vpsubw 8704(%rsp), %ymm0, %ymm0
-vmovdqa 6240(%rsp), %ymm1
+vpsubw 6016(%r8), %ymm4, %ymm4
+vmovdqa %ymm4, 6208(%r8)
+vmovdqa 8352(%r8), %ymm0
+vpsubw 8704(%r8), %ymm0, %ymm0
+vmovdqa 6240(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9056(%rsp), %ymm1, %ymm6
-vpsubw 8000(%rsp), %ymm0, %ymm0
-vpaddw 5888(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8352(%rsp)
-vmovdqa 8384(%rsp), %ymm0
-vpsubw 8736(%rsp), %ymm0, %ymm0
-vmovdqa 6272(%rsp), %ymm1
+vpsubw 9056(%r8), %ymm1, %ymm6
+vpsubw 8000(%r8), %ymm0, %ymm0
+vpaddw 5888(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8352(%r8)
+vmovdqa 8384(%r8), %ymm0
+vpsubw 8736(%r8), %ymm0, %ymm0
+vmovdqa 6272(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9088(%rsp), %ymm1, %ymm7
-vpsubw 8032(%rsp), %ymm0, %ymm0
-vpaddw 5920(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8384(%rsp)
-vmovdqa 8416(%rsp), %ymm0
-vpsubw 8768(%rsp), %ymm0, %ymm0
-vmovdqa 6304(%rsp), %ymm1
+vpsubw 9088(%r8), %ymm1, %ymm7
+vpsubw 8032(%r8), %ymm0, %ymm0
+vpaddw 5920(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8384(%r8)
+vmovdqa 8416(%r8), %ymm0
+vpsubw 8768(%r8), %ymm0, %ymm0
+vmovdqa 6304(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9120(%rsp), %ymm1, %ymm8
-vpsubw 8064(%rsp), %ymm0, %ymm0
-vpaddw 5952(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8416(%rsp)
-vmovdqa 8448(%rsp), %ymm0
-vpsubw 8800(%rsp), %ymm0, %ymm0
-vmovdqa 6336(%rsp), %ymm1
+vpsubw 9120(%r8), %ymm1, %ymm8
+vpsubw 8064(%r8), %ymm0, %ymm0
+vpaddw 5952(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8416(%r8)
+vmovdqa 8448(%r8), %ymm0
+vpsubw 8800(%r8), %ymm0, %ymm0
+vmovdqa 6336(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9152(%rsp), %ymm1, %ymm9
-vpsubw 8096(%rsp), %ymm0, %ymm0
-vpaddw 5984(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8448(%rsp)
-vmovdqa 8480(%rsp), %ymm0
-vpsubw 8832(%rsp), %ymm0, %ymm0
-vmovdqa 6368(%rsp), %ymm1
+vpsubw 9152(%r8), %ymm1, %ymm9
+vpsubw 8096(%r8), %ymm0, %ymm0
+vpaddw 5984(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8448(%r8)
+vmovdqa 8480(%r8), %ymm0
+vpsubw 8832(%r8), %ymm0, %ymm0
+vmovdqa 6368(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9184(%rsp), %ymm1, %ymm10
-vpsubw 8128(%rsp), %ymm0, %ymm0
-vpaddw 6016(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8480(%rsp)
-vmovdqa 8512(%rsp), %ymm0
-vpsubw 8864(%rsp), %ymm0, %ymm0
-vmovdqa 6400(%rsp), %ymm1
+vpsubw 9184(%r8), %ymm1, %ymm10
+vpsubw 8128(%r8), %ymm0, %ymm0
+vpaddw 6016(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8480(%r8)
+vmovdqa 8512(%r8), %ymm0
+vpsubw 8864(%r8), %ymm0, %ymm0
+vmovdqa 6400(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9216(%rsp), %ymm1, %ymm11
-vpsubw 8160(%rsp), %ymm0, %ymm0
-vpaddw 6048(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8512(%rsp)
-vmovdqa 8544(%rsp), %ymm0
-vpsubw 8896(%rsp), %ymm0, %ymm0
-vmovdqa 6432(%rsp), %ymm1
+vpsubw 9216(%r8), %ymm1, %ymm11
+vpsubw 8160(%r8), %ymm0, %ymm0
+vpaddw 6048(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8512(%r8)
+vmovdqa 8544(%r8), %ymm0
+vpsubw 8896(%r8), %ymm0, %ymm0
+vmovdqa 6432(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9248(%rsp), %ymm1, %ymm12
-vpsubw 8192(%rsp), %ymm0, %ymm0
-vpaddw 6080(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8544(%rsp)
-vmovdqa 8576(%rsp), %ymm0
-vpsubw 8928(%rsp), %ymm0, %ymm0
-vmovdqa 6464(%rsp), %ymm1
+vpsubw 9248(%r8), %ymm1, %ymm12
+vpsubw 8192(%r8), %ymm0, %ymm0
+vpaddw 6080(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8544(%r8)
+vmovdqa 8576(%r8), %ymm0
+vpsubw 8928(%r8), %ymm0, %ymm0
+vmovdqa 6464(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9280(%rsp), %ymm1, %ymm13
-vpsubw 8224(%rsp), %ymm0, %ymm0
-vpaddw 6112(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8576(%rsp)
-vmovdqa 8608(%rsp), %ymm0
-vpsubw 8960(%rsp), %ymm0, %ymm0
-vmovdqa 6496(%rsp), %ymm1
+vpsubw 9280(%r8), %ymm1, %ymm13
+vpsubw 8224(%r8), %ymm0, %ymm0
+vpaddw 6112(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8576(%r8)
+vmovdqa 8608(%r8), %ymm0
+vpsubw 8960(%r8), %ymm0, %ymm0
+vmovdqa 6496(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9312(%rsp), %ymm1, %ymm14
-vpsubw 8256(%rsp), %ymm0, %ymm0
-vpaddw 6144(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8608(%rsp)
-vmovdqa 8640(%rsp), %ymm0
-vpsubw 8992(%rsp), %ymm0, %ymm0
-vmovdqa 6528(%rsp), %ymm1
+vpsubw 9312(%r8), %ymm1, %ymm14
+vpsubw 8256(%r8), %ymm0, %ymm0
+vpaddw 6144(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8608(%r8)
+vmovdqa 8640(%r8), %ymm0
+vpsubw 8992(%r8), %ymm0, %ymm0
+vmovdqa 6528(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
-vpsubw 9344(%rsp), %ymm1, %ymm15
-vpsubw 8288(%rsp), %ymm0, %ymm0
-vpaddw 6176(%rsp), %ymm0, %ymm0
-vmovdqa %ymm0, 8640(%rsp)
-vmovdqa 6208(%rsp), %ymm0
-vpsubw 8320(%rsp), %ymm0, %ymm0
-vpsubw 9024(%rsp), %ymm0, %ymm0
+vpsubw 9344(%r8), %ymm1, %ymm15
+vpsubw 8288(%r8), %ymm0, %ymm0
+vpaddw 6176(%r8), %ymm0, %ymm0
+vmovdqa %ymm0, 8640(%r8)
+vmovdqa 6208(%r8), %ymm0
+vpsubw 8320(%r8), %ymm0, %ymm0
+vpsubw 9024(%r8), %ymm0, %ymm0
 vpsubw 3488(%r10), %ymm0, %ymm0
 vpsubw 4896(%r10), %ymm0, %ymm0
 vmovdqa %ymm0, 4192(%r10)
@@ -4014,7 +4025,7 @@
 vpsubw %ymm0, %ymm6, %ymm6
 vpsubw 4928(%r10), %ymm6, %ymm6
 vpsubw 2816(%r10), %ymm0, %ymm0
-vpaddw 8000(%rsp), %ymm0, %ymm0
+vpaddw 8000(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3520(%r10)
 vmovdqa %ymm6, 4224(%r10)
 vmovdqa 3552(%r10), %ymm0
@@ -4022,7 +4033,7 @@
 vpsubw %ymm0, %ymm7, %ymm7
 vpsubw 4960(%r10), %ymm7, %ymm7
 vpsubw 2848(%r10), %ymm0, %ymm0
-vpaddw 8032(%rsp), %ymm0, %ymm0
+vpaddw 8032(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3552(%r10)
 vmovdqa %ymm7, 4256(%r10)
 vmovdqa 3584(%r10), %ymm0
@@ -4030,7 +4041,7 @@
 vpsubw %ymm0, %ymm8, %ymm8
 vpsubw 4992(%r10), %ymm8, %ymm8
 vpsubw 2880(%r10), %ymm0, %ymm0
-vpaddw 8064(%rsp), %ymm0, %ymm0
+vpaddw 8064(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3584(%r10)
 vmovdqa %ymm8, 4288(%r10)
 vmovdqa 3616(%r10), %ymm0
@@ -4038,7 +4049,7 @@
 vpsubw %ymm0, %ymm9, %ymm9
 vpsubw 5024(%r10), %ymm9, %ymm9
 vpsubw 2912(%r10), %ymm0, %ymm0
-vpaddw 8096(%rsp), %ymm0, %ymm0
+vpaddw 8096(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3616(%r10)
 vmovdqa %ymm9, 4320(%r10)
 vmovdqa 3648(%r10), %ymm0
@@ -4046,7 +4057,7 @@
 vpsubw %ymm0, %ymm10, %ymm10
 vpsubw 5056(%r10), %ymm10, %ymm10
 vpsubw 2944(%r10), %ymm0, %ymm0
-vpaddw 8128(%rsp), %ymm0, %ymm0
+vpaddw 8128(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3648(%r10)
 vmovdqa %ymm10, 4352(%r10)
 vmovdqa 3680(%r10), %ymm0
@@ -4054,7 +4065,7 @@
 vpsubw %ymm0, %ymm11, %ymm11
 vpsubw 5088(%r10), %ymm11, %ymm11
 vpsubw 2976(%r10), %ymm0, %ymm0
-vpaddw 8160(%rsp), %ymm0, %ymm0
+vpaddw 8160(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3680(%r10)
 vmovdqa %ymm11, 4384(%r10)
 vmovdqa 3712(%r10), %ymm0
@@ -4062,7 +4073,7 @@
 vpsubw %ymm0, %ymm12, %ymm12
 vpsubw 5120(%r10), %ymm12, %ymm12
 vpsubw 3008(%r10), %ymm0, %ymm0
-vpaddw 8192(%rsp), %ymm0, %ymm0
+vpaddw 8192(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3712(%r10)
 vmovdqa %ymm12, 4416(%r10)
 vmovdqa 3744(%r10), %ymm0
@@ -4070,7 +4081,7 @@
 vpsubw %ymm0, %ymm13, %ymm13
 vpsubw 5152(%r10), %ymm13, %ymm13
 vpsubw 3040(%r10), %ymm0, %ymm0
-vpaddw 8224(%rsp), %ymm0, %ymm0
+vpaddw 8224(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3744(%r10)
 vmovdqa %ymm13, 4448(%r10)
 vmovdqa 3776(%r10), %ymm0
@@ -4078,7 +4089,7 @@
 vpsubw %ymm0, %ymm14, %ymm14
 vpsubw 5184(%r10), %ymm14, %ymm14
 vpsubw 3072(%r10), %ymm0, %ymm0
-vpaddw 8256(%rsp), %ymm0, %ymm0
+vpaddw 8256(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3776(%r10)
 vmovdqa %ymm14, 4480(%r10)
 vmovdqa 3808(%r10), %ymm0
@@ -4086,111 +4097,111 @@
 vpsubw %ymm0, %ymm15, %ymm15
 vpsubw 5216(%r10), %ymm15, %ymm15
 vpsubw 3104(%r10), %ymm0, %ymm0
-vpaddw 8288(%rsp), %ymm0, %ymm0
+vpaddw 8288(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3808(%r10)
 vmovdqa %ymm15, 4512(%r10)
 vmovdqa 3840(%r10), %ymm0
 vpsubw 4544(%r10), %ymm0, %ymm0
-vmovdqa 9024(%rsp), %ymm1
+vmovdqa 9024(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5248(%r10), %ymm1, %ymm1
 vpsubw 3136(%r10), %ymm0, %ymm0
-vpaddw 8320(%rsp), %ymm0, %ymm0
+vpaddw 8320(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3840(%r10)
 vmovdqa %ymm1, 4544(%r10)
 vmovdqa 3872(%r10), %ymm0
 vpsubw 4576(%r10), %ymm0, %ymm0
-vmovdqa 9056(%rsp), %ymm1
+vmovdqa 9056(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5280(%r10), %ymm1, %ymm1
 vpsubw 3168(%r10), %ymm0, %ymm0
-vpaddw 8352(%rsp), %ymm0, %ymm0
+vpaddw 8352(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3872(%r10)
 vmovdqa %ymm1, 4576(%r10)
 vmovdqa 3904(%r10), %ymm0
 vpsubw 4608(%r10), %ymm0, %ymm0
-vmovdqa 9088(%rsp), %ymm1
+vmovdqa 9088(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5312(%r10), %ymm1, %ymm1
 vpsubw 3200(%r10), %ymm0, %ymm0
-vpaddw 8384(%rsp), %ymm0, %ymm0
+vpaddw 8384(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3904(%r10)
 vmovdqa %ymm1, 4608(%r10)
 vmovdqa 3936(%r10), %ymm0
 vpsubw 4640(%r10), %ymm0, %ymm0
-vmovdqa 9120(%rsp), %ymm1
+vmovdqa 9120(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5344(%r10), %ymm1, %ymm1
 vpsubw 3232(%r10), %ymm0, %ymm0
-vpaddw 8416(%rsp), %ymm0, %ymm0
+vpaddw 8416(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3936(%r10)
 vmovdqa %ymm1, 4640(%r10)
 vmovdqa 3968(%r10), %ymm0
 vpsubw 4672(%r10), %ymm0, %ymm0
-vmovdqa 9152(%rsp), %ymm1
+vmovdqa 9152(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5376(%r10), %ymm1, %ymm1
 vpsubw 3264(%r10), %ymm0, %ymm0
-vpaddw 8448(%rsp), %ymm0, %ymm0
+vpaddw 8448(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 3968(%r10)
 vmovdqa %ymm1, 4672(%r10)
 vmovdqa 4000(%r10), %ymm0
 vpsubw 4704(%r10), %ymm0, %ymm0
-vmovdqa 9184(%rsp), %ymm1
+vmovdqa 9184(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5408(%r10), %ymm1, %ymm1
 vpsubw 3296(%r10), %ymm0, %ymm0
-vpaddw 8480(%rsp), %ymm0, %ymm0
+vpaddw 8480(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 4000(%r10)
 vmovdqa %ymm1, 4704(%r10)
 vmovdqa 4032(%r10), %ymm0
 vpsubw 4736(%r10), %ymm0, %ymm0
-vmovdqa 9216(%rsp), %ymm1
+vmovdqa 9216(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5440(%r10), %ymm1, %ymm1
 vpsubw 3328(%r10), %ymm0, %ymm0
-vpaddw 8512(%rsp), %ymm0, %ymm0
+vpaddw 8512(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 4032(%r10)
 vmovdqa %ymm1, 4736(%r10)
 vmovdqa 4064(%r10), %ymm0
 vpsubw 4768(%r10), %ymm0, %ymm0
-vmovdqa 9248(%rsp), %ymm1
+vmovdqa 9248(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5472(%r10), %ymm1, %ymm1
 vpsubw 3360(%r10), %ymm0, %ymm0
-vpaddw 8544(%rsp), %ymm0, %ymm0
+vpaddw 8544(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 4064(%r10)
 vmovdqa %ymm1, 4768(%r10)
 vmovdqa 4096(%r10), %ymm0
 vpsubw 4800(%r10), %ymm0, %ymm0
-vmovdqa 9280(%rsp), %ymm1
+vmovdqa 9280(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5504(%r10), %ymm1, %ymm1
 vpsubw 3392(%r10), %ymm0, %ymm0
-vpaddw 8576(%rsp), %ymm0, %ymm0
+vpaddw 8576(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 4096(%r10)
 vmovdqa %ymm1, 4800(%r10)
 vmovdqa 4128(%r10), %ymm0
 vpsubw 4832(%r10), %ymm0, %ymm0
-vmovdqa 9312(%rsp), %ymm1
+vmovdqa 9312(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5536(%r10), %ymm1, %ymm1
 vpsubw 3424(%r10), %ymm0, %ymm0
-vpaddw 8608(%rsp), %ymm0, %ymm0
+vpaddw 8608(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 4128(%r10)
 vmovdqa %ymm1, 4832(%r10)
 vmovdqa 4160(%r10), %ymm0
 vpsubw 4864(%r10), %ymm0, %ymm0
-vmovdqa 9344(%rsp), %ymm1
+vmovdqa 9344(%r8), %ymm1
 vpsubw %ymm0, %ymm1, %ymm1
 vpsubw 5568(%r10), %ymm1, %ymm1
 vpsubw 3456(%r10), %ymm0, %ymm0
-vpaddw 8640(%rsp), %ymm0, %ymm0
+vpaddw 8640(%r8), %ymm0, %ymm0
 vmovdqa %ymm0, 4160(%r10)
 vmovdqa %ymm1, 4864(%r10)
 vpxor %ymm1, %ymm1, %ymm1
 vmovdqa %ymm1, 5600(%r10)
-subq $32, %rsp
+subq $32, %r8
 vmovdqa 2816(%r10), %ymm0
 vmovdqa 2880(%r10), %ymm1
 vmovdqa 2944(%r10), %ymm2
@@ -4231,7 +4242,7 @@
 vpunpckhwd 3232(%r10), %ymm2, %ymm1
 vpunpcklwd 3296(%r10), %ymm3, %ymm2
 vpunpckhwd 3296(%r10), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -4283,7 +4294,7 @@
 vmovdqa %ymm15, 2496(%r12)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 2688(%r12)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 1344(%r12)
 vpermq $78, %ymm11, %ymm11
@@ -4329,7 +4340,7 @@
 vpunpckhwd 3744(%r10), %ymm2, %ymm1
 vpunpcklwd 3808(%r10), %ymm3, %ymm2
 vpunpckhwd 3808(%r10), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -4381,7 +4392,7 @@
 vmovdqa %ymm15, 2528(%r12)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 2720(%r12)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 1376(%r12)
 vpermq $78, %ymm11, %ymm11
@@ -4427,7 +4438,7 @@
 vpunpckhwd 4256(%r10), %ymm2, %ymm1
 vpunpcklwd 4320(%r10), %ymm3, %ymm2
 vpunpckhwd 4320(%r10), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -4479,7 +4490,7 @@
 vmovdqa %ymm15, 2560(%r12)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 2752(%r12)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 1408(%r12)
 vpermq $78, %ymm11, %ymm11
@@ -4525,7 +4536,7 @@
 vpunpckhwd 4640(%r10), %ymm2, %ymm1
 vpunpcklwd 4704(%r10), %ymm3, %ymm2
 vpunpckhwd 4704(%r10), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -4577,7 +4588,7 @@
 vmovdqa %ymm15, 2592(%r12)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 2784(%r12)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 1440(%r12)
 vpermq $78, %ymm11, %ymm11
@@ -4623,7 +4634,7 @@
 vpunpckhwd 5152(%r10), %ymm2, %ymm1
 vpunpcklwd 5216(%r10), %ymm3, %ymm2
 vpunpckhwd 5216(%r10), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -4675,7 +4686,7 @@
 vmovdqa %ymm15, 2624(%r12)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 2816(%r12)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 1472(%r12)
 vpermq $78, %ymm11, %ymm11
@@ -4721,7 +4732,7 @@
 vpunpckhwd 5664(%r10), %ymm2, %ymm1
 vpunpcklwd 5728(%r10), %ymm3, %ymm2
 vpunpckhwd 5728(%r10), %ymm3, %ymm3
-vmovdqa %ymm11, 0(%rsp)
+vmovdqa %ymm11, 0(%r8)
 vpunpckldq %ymm14, %ymm12, %ymm11
 vpunpckhdq %ymm14, %ymm12, %ymm12
 vpunpckldq %ymm15, %ymm13, %ymm14
@@ -4773,54 +4784,53 @@
 vmovdqa %ymm15, 2656(%r12)
 vinserti128 $0, %xmm10, %ymm14, %ymm15
 vmovdqa %ymm15, 2848(%r12)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa 0(%r8), %ymm11
 vinserti128 $1, %xmm1, %ymm11, %ymm14
 vmovdqa %ymm14, 1504(%r12)
 vpermq $78, %ymm11, %ymm11
 vinserti128 $0, %xmm11, %ymm1, %ymm1
 vmovdqa %ymm1, 3040(%r12)
-addq $32, %rsp
+addq $32, %r8
 add $1536, %rax
 add $1536, %r11
 add $3072, %r12
 dec %ecx
 jnz karatsuba_loop_4eced63f144beffcb0247f9c6f67d165
 sub $12288, %r12
-add $9408, %rsp
-subq $2400, %rsp
+add $9408-2400, %r8
 vpxor %ymm0, %ymm0, %ymm0
-vmovdqa %ymm0, 1792(%rsp)
-vmovdqa %ymm0, 1824(%rsp)
-vmovdqa %ymm0, 1856(%rsp)
-vmovdqa %ymm0, 1888(%rsp)
-vmovdqa %ymm0, 1920(%rsp)
-vmovdqa %ymm0, 1952(%rsp)
-vmovdqa %ymm0, 1984(%rsp)
-vmovdqa %ymm0, 2016(%rsp)
-vmovdqa %ymm0, 2048(%rsp)
-vmovdqa %ymm0, 2080(%rsp)
-vmovdqa %ymm0, 2112(%rsp)
-vmovdqa %ymm0, 2144(%rsp)
-vmovdqa %ymm0, 2176(%rsp)
-vmovdqa %ymm0, 2208(%rsp)
-vmovdqa %ymm0, 2240(%rsp)
-vmovdqa %ymm0, 2272(%rsp)
-vmovdqa %ymm0, 2304(%rsp)
-vmovdqa %ymm0, 2336(%rsp)
-vmovdqa %ymm0, 2368(%rsp)
-vmovdqa %ymm0, 2400(%rsp)
-vmovdqa %ymm0, 2432(%rsp)
-vmovdqa %ymm0, 2464(%rsp)
-vmovdqa %ymm0, 2496(%rsp)
-vmovdqa %ymm0, 2528(%rsp)
-vmovdqa %ymm0, 2560(%rsp)
-vmovdqa %ymm0, 2592(%rsp)
-vmovdqa %ymm0, 2624(%rsp)
-vmovdqa %ymm0, 2656(%rsp)
-vmovdqa %ymm0, 2688(%rsp)
-vmovdqa %ymm0, 2720(%rsp)
-vmovdqa %ymm0, 2752(%rsp)
-vmovdqa %ymm0, 2784(%rsp)
+vmovdqa %ymm0, 1792(%r8)
+vmovdqa %ymm0, 1824(%r8)
+vmovdqa %ymm0, 1856(%r8)
+vmovdqa %ymm0, 1888(%r8)
+vmovdqa %ymm0, 1920(%r8)
+vmovdqa %ymm0, 1952(%r8)
+vmovdqa %ymm0, 1984(%r8)
+vmovdqa %ymm0, 2016(%r8)
+vmovdqa %ymm0, 2048(%r8)
+vmovdqa %ymm0, 2080(%r8)
+vmovdqa %ymm0, 2112(%r8)
+vmovdqa %ymm0, 2144(%r8)
+vmovdqa %ymm0, 2176(%r8)
+vmovdqa %ymm0, 2208(%r8)
+vmovdqa %ymm0, 2240(%r8)
+vmovdqa %ymm0, 2272(%r8)
+vmovdqa %ymm0, 2304(%r8)
+vmovdqa %ymm0, 2336(%r8)
+vmovdqa %ymm0, 2368(%r8)
+vmovdqa %ymm0, 2400(%r8)
+vmovdqa %ymm0, 2432(%r8)
+vmovdqa %ymm0, 2464(%r8)
+vmovdqa %ymm0, 2496(%r8)
+vmovdqa %ymm0, 2528(%r8)
+vmovdqa %ymm0, 2560(%r8)
+vmovdqa %ymm0, 2592(%r8)
+vmovdqa %ymm0, 2624(%r8)
+vmovdqa %ymm0, 2656(%r8)
+vmovdqa %ymm0, 2688(%r8)
+vmovdqa %ymm0, 2720(%r8)
+vmovdqa %ymm0, 2752(%r8)
+vmovdqa %ymm0, 2784(%r8)
 vmovdqa const729(%rip), %ymm15
 vmovdqa const3_inv(%rip), %ymm14
 vmovdqa const5_inv(%rip), %ymm13
@@ -4860,14 +4870,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 0(%r12), %ymm8
 vmovdqa 864(%r12), %ymm9
-vmovdqa %ymm8, 0(%rsp)
-vmovdqa %ymm0, 32(%rsp)
-vmovdqa %ymm1, 64(%rsp)
-vmovdqa %ymm7, 96(%rsp)
-vmovdqa %ymm5, 128(%rsp)
-vmovdqa %ymm2, 160(%rsp)
-vmovdqa %ymm3, 192(%rsp)
-vmovdqa %ymm9, 224(%rsp)
+vmovdqa %ymm8, 0(%r8)
+vmovdqa %ymm0, 32(%r8)
+vmovdqa %ymm1, 64(%r8)
+vmovdqa %ymm7, 96(%r8)
+vmovdqa %ymm5, 128(%r8)
+vmovdqa %ymm2, 160(%r8)
+vmovdqa %ymm3, 192(%r8)
+vmovdqa %ymm9, 224(%r8)
 vmovdqa 1824(%r12), %ymm0
 vpsubw 1920(%r12), %ymm0, %ymm0
 vmovdqa 2208(%r12), %ymm1
@@ -4903,14 +4913,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 1728(%r12), %ymm8
 vmovdqa 2592(%r12), %ymm9
-vmovdqa %ymm8, 256(%rsp)
-vmovdqa %ymm0, 288(%rsp)
-vmovdqa %ymm1, 320(%rsp)
-vmovdqa %ymm7, 352(%rsp)
-vmovdqa %ymm5, 384(%rsp)
-vmovdqa %ymm2, 416(%rsp)
-vmovdqa %ymm3, 448(%rsp)
-vmovdqa %ymm9, 480(%rsp)
+vmovdqa %ymm8, 256(%r8)
+vmovdqa %ymm0, 288(%r8)
+vmovdqa %ymm1, 320(%r8)
+vmovdqa %ymm7, 352(%r8)
+vmovdqa %ymm5, 384(%r8)
+vmovdqa %ymm2, 416(%r8)
+vmovdqa %ymm3, 448(%r8)
+vmovdqa %ymm9, 480(%r8)
 vmovdqa 3552(%r12), %ymm0
 vpsubw 3648(%r12), %ymm0, %ymm0
 vmovdqa 3936(%r12), %ymm1
@@ -4946,14 +4956,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 3456(%r12), %ymm8
 vmovdqa 4320(%r12), %ymm9
-vmovdqa %ymm8, 512(%rsp)
-vmovdqa %ymm0, 544(%rsp)
-vmovdqa %ymm1, 576(%rsp)
-vmovdqa %ymm7, 608(%rsp)
-vmovdqa %ymm5, 640(%rsp)
-vmovdqa %ymm2, 672(%rsp)
-vmovdqa %ymm3, 704(%rsp)
-vmovdqa %ymm9, 736(%rsp)
+vmovdqa %ymm8, 512(%r8)
+vmovdqa %ymm0, 544(%r8)
+vmovdqa %ymm1, 576(%r8)
+vmovdqa %ymm7, 608(%r8)
+vmovdqa %ymm5, 640(%r8)
+vmovdqa %ymm2, 672(%r8)
+vmovdqa %ymm3, 704(%r8)
+vmovdqa %ymm9, 736(%r8)
 vmovdqa 5280(%r12), %ymm0
 vpsubw 5376(%r12), %ymm0, %ymm0
 vmovdqa 5664(%r12), %ymm1
@@ -4989,14 +4999,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 5184(%r12), %ymm8
 vmovdqa 6048(%r12), %ymm9
-vmovdqa %ymm8, 768(%rsp)
-vmovdqa %ymm0, 800(%rsp)
-vmovdqa %ymm1, 832(%rsp)
-vmovdqa %ymm7, 864(%rsp)
-vmovdqa %ymm5, 896(%rsp)
-vmovdqa %ymm2, 928(%rsp)
-vmovdqa %ymm3, 960(%rsp)
-vmovdqa %ymm9, 992(%rsp)
+vmovdqa %ymm8, 768(%r8)
+vmovdqa %ymm0, 800(%r8)
+vmovdqa %ymm1, 832(%r8)
+vmovdqa %ymm7, 864(%r8)
+vmovdqa %ymm5, 896(%r8)
+vmovdqa %ymm2, 928(%r8)
+vmovdqa %ymm3, 960(%r8)
+vmovdqa %ymm9, 992(%r8)
 vmovdqa 7008(%r12), %ymm0
 vpsubw 7104(%r12), %ymm0, %ymm0
 vmovdqa 7392(%r12), %ymm1
@@ -5032,14 +5042,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 6912(%r12), %ymm8
 vmovdqa 7776(%r12), %ymm9
-vmovdqa %ymm8, 1024(%rsp)
-vmovdqa %ymm0, 1056(%rsp)
-vmovdqa %ymm1, 1088(%rsp)
-vmovdqa %ymm7, 1120(%rsp)
-vmovdqa %ymm5, 1152(%rsp)
-vmovdqa %ymm2, 1184(%rsp)
-vmovdqa %ymm3, 1216(%rsp)
-vmovdqa %ymm9, 1248(%rsp)
+vmovdqa %ymm8, 1024(%r8)
+vmovdqa %ymm0, 1056(%r8)
+vmovdqa %ymm1, 1088(%r8)
+vmovdqa %ymm7, 1120(%r8)
+vmovdqa %ymm5, 1152(%r8)
+vmovdqa %ymm2, 1184(%r8)
+vmovdqa %ymm3, 1216(%r8)
+vmovdqa %ymm9, 1248(%r8)
 vmovdqa 8736(%r12), %ymm0
 vpsubw 8832(%r12), %ymm0, %ymm0
 vmovdqa 9120(%r12), %ymm1
@@ -5075,14 +5085,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 8640(%r12), %ymm8
 vmovdqa 9504(%r12), %ymm9
-vmovdqa %ymm8, 1280(%rsp)
-vmovdqa %ymm0, 1312(%rsp)
-vmovdqa %ymm1, 1344(%rsp)
-vmovdqa %ymm7, 1376(%rsp)
-vmovdqa %ymm5, 1408(%rsp)
-vmovdqa %ymm2, 1440(%rsp)
-vmovdqa %ymm3, 1472(%rsp)
-vmovdqa %ymm9, 1504(%rsp)
+vmovdqa %ymm8, 1280(%r8)
+vmovdqa %ymm0, 1312(%r8)
+vmovdqa %ymm1, 1344(%r8)
+vmovdqa %ymm7, 1376(%r8)
+vmovdqa %ymm5, 1408(%r8)
+vmovdqa %ymm2, 1440(%r8)
+vmovdqa %ymm3, 1472(%r8)
+vmovdqa %ymm9, 1504(%r8)
 vmovdqa 10464(%r12), %ymm0
 vpsubw 10560(%r12), %ymm0, %ymm0
 vmovdqa 10848(%r12), %ymm1
@@ -5118,23 +5128,23 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 10368(%r12), %ymm8
 vmovdqa 11232(%r12), %ymm9
-vmovdqa %ymm8, 1536(%rsp)
-vmovdqa %ymm0, 1568(%rsp)
-vmovdqa %ymm1, 1600(%rsp)
-vmovdqa %ymm7, 1632(%rsp)
-vmovdqa %ymm5, 1664(%rsp)
-vmovdqa %ymm2, 1696(%rsp)
-vmovdqa %ymm3, 1728(%rsp)
-vmovdqa %ymm9, 1760(%rsp)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa %ymm8, 1536(%r8)
+vmovdqa %ymm0, 1568(%r8)
+vmovdqa %ymm1, 1600(%r8)
+vmovdqa %ymm7, 1632(%r8)
+vmovdqa %ymm5, 1664(%r8)
+vmovdqa %ymm2, 1696(%r8)
+vmovdqa %ymm3, 1728(%r8)
+vmovdqa %ymm9, 1760(%r8)
+vmovdqa 0(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm10
 vpunpckhwd const0(%rip), %ymm11, %ymm9
 vpslld $1, %ymm10, %ymm10
 vpslld $1, %ymm9, %ymm9
-vmovdqa 256(%rsp), %ymm8
+vmovdqa 256(%r8), %ymm8
 vpunpcklwd const0(%rip), %ymm8, %ymm7
 vpunpckhwd const0(%rip), %ymm8, %ymm8
-vmovdqa 512(%rsp), %ymm6
+vmovdqa 512(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm7, %ymm4
@@ -5148,7 +5158,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1536(%rsp), %ymm5
+vmovdqa 1536(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm8
 vpunpckhwd const0(%rip), %ymm5, %ymm7
 vpslld $1, %ymm8, %ymm8
@@ -5160,9 +5170,9 @@
 vpand mask32_to_16(%rip), %ymm4, %ymm4
 vpand mask32_to_16(%rip), %ymm3, %ymm3
 vpackusdw %ymm3, %ymm4, %ymm3
-vmovdqa 768(%rsp), %ymm4
-vpaddw 1024(%rsp), %ymm4, %ymm7
-vpsubw 1024(%rsp), %ymm4, %ymm4
+vmovdqa 768(%r8), %ymm4
+vpaddw 1024(%r8), %ymm4, %ymm7
+vpsubw 1024(%r8), %ymm4, %ymm4
 vpsrlw $2, %ymm4, %ymm4
 vpsubw %ymm6, %ymm4, %ymm4
 vpmullw %ymm14, %ymm4, %ymm4
@@ -5172,7 +5182,7 @@
 vpsubw %ymm7, %ymm8, %ymm7
 vpsrlw $3, %ymm7, %ymm7
 vpsubw %ymm3, %ymm7, %ymm7
-vmovdqa 1280(%rsp), %ymm8
+vmovdqa 1280(%r8), %ymm8
 vpsubw %ymm11, %ymm8, %ymm8
 vpmullw %ymm15, %ymm5, %ymm9
 vpsubw %ymm9, %ymm8, %ymm9
@@ -5197,7 +5207,7 @@
 vpand mask_keephigh(%rip), %ymm9, %ymm10
 vpor %ymm10, %ymm7, %ymm7
 vpaddw %ymm7, %ymm11, %ymm11
-vmovdqa %xmm9, 2048(%rsp)
+vmovdqa %xmm9, 2048(%r8)
 vpshufb shuf48_16(%rip), %ymm8, %ymm8
 vpand mask3_5_3_5(%rip), %ymm8, %ymm9
 vpand mask5_3_5_3(%rip), %ymm8, %ymm8
@@ -5205,7 +5215,7 @@
 vpand mask_keephigh(%rip), %ymm9, %ymm10
 vpor %ymm10, %ymm8, %ymm8
 vpaddw %ymm8, %ymm6, %ymm6
-vmovdqa %xmm9, 2304(%rsp)
+vmovdqa %xmm9, 2304(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_3_5(%rip), %ymm5, %ymm9
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
@@ -5213,7 +5223,7 @@
 vpand mask_keephigh(%rip), %ymm9, %ymm10
 vpor %ymm10, %ymm5, %ymm5
 vpaddw %ymm5, %ymm3, %ymm3
-vmovdqa %xmm9, 2560(%rsp)
+vmovdqa %xmm9, 2560(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 0(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -5222,15 +5232,15 @@
 vmovdqu %ymm3, 704(%rdi)
 vpand mask_mod8192(%rip), %ymm4, %ymm4
 vmovdqu %ymm4, 1056(%rdi)
-vmovdqa 32(%rsp), %ymm5
+vmovdqa 32(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm8
 vpunpckhwd const0(%rip), %ymm5, %ymm7
 vpslld $1, %ymm8, %ymm8
 vpslld $1, %ymm7, %ymm7
-vmovdqa 288(%rsp), %ymm4
+vmovdqa 288(%r8), %ymm4
 vpunpcklwd const0(%rip), %ymm4, %ymm3
 vpunpckhwd const0(%rip), %ymm4, %ymm4
-vmovdqa 544(%rsp), %ymm6
+vmovdqa 544(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm3, %ymm9
@@ -5244,7 +5254,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1568(%rsp), %ymm11
+vmovdqa 1568(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm4
 vpunpckhwd const0(%rip), %ymm11, %ymm3
 vpslld $1, %ymm4, %ymm4
@@ -5256,9 +5266,9 @@
 vpand mask32_to_16(%rip), %ymm9, %ymm9
 vpand mask32_to_16(%rip), %ymm10, %ymm10
 vpackusdw %ymm10, %ymm9, %ymm10
-vmovdqa 800(%rsp), %ymm9
-vpaddw 1056(%rsp), %ymm9, %ymm3
-vpsubw 1056(%rsp), %ymm9, %ymm9
+vmovdqa 800(%r8), %ymm9
+vpaddw 1056(%r8), %ymm9, %ymm3
+vpsubw 1056(%r8), %ymm9, %ymm9
 vpsrlw $2, %ymm9, %ymm9
 vpsubw %ymm6, %ymm9, %ymm9
 vpmullw %ymm14, %ymm9, %ymm9
@@ -5268,7 +5278,7 @@
 vpsubw %ymm3, %ymm4, %ymm3
 vpsrlw $3, %ymm3, %ymm3
 vpsubw %ymm10, %ymm3, %ymm3
-vmovdqa 1312(%rsp), %ymm4
+vmovdqa 1312(%r8), %ymm4
 vpsubw %ymm5, %ymm4, %ymm4
 vpmullw %ymm15, %ymm11, %ymm7
 vpsubw %ymm7, %ymm4, %ymm7
@@ -5293,7 +5303,7 @@
 vpand mask_keephigh(%rip), %ymm7, %ymm8
 vpor %ymm8, %ymm3, %ymm3
 vpaddw %ymm3, %ymm5, %ymm5
-vmovdqa %xmm7, 2080(%rsp)
+vmovdqa %xmm7, 2080(%r8)
 vpshufb shuf48_16(%rip), %ymm4, %ymm4
 vpand mask3_5_3_5(%rip), %ymm4, %ymm7
 vpand mask5_3_5_3(%rip), %ymm4, %ymm4
@@ -5301,7 +5311,7 @@
 vpand mask_keephigh(%rip), %ymm7, %ymm8
 vpor %ymm8, %ymm4, %ymm4
 vpaddw %ymm4, %ymm6, %ymm6
-vmovdqa %xmm7, 2336(%rsp)
+vmovdqa %xmm7, 2336(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_3_5(%rip), %ymm11, %ymm7
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
@@ -5309,7 +5319,7 @@
 vpand mask_keephigh(%rip), %ymm7, %ymm8
 vpor %ymm8, %ymm11, %ymm11
 vpaddw %ymm11, %ymm10, %ymm10
-vmovdqa %xmm7, 2592(%rsp)
+vmovdqa %xmm7, 2592(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %ymm5, 88(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -5318,15 +5328,15 @@
 vmovdqu %ymm10, 792(%rdi)
 vpand mask_mod8192(%rip), %ymm9, %ymm9
 vmovdqu %ymm9, 1144(%rdi)
-vmovdqa 64(%rsp), %ymm11
+vmovdqa 64(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm4
 vpunpckhwd const0(%rip), %ymm11, %ymm3
 vpslld $1, %ymm4, %ymm4
 vpslld $1, %ymm3, %ymm3
-vmovdqa 320(%rsp), %ymm9
+vmovdqa 320(%r8), %ymm9
 vpunpcklwd const0(%rip), %ymm9, %ymm10
 vpunpckhwd const0(%rip), %ymm9, %ymm9
-vmovdqa 576(%rsp), %ymm6
+vmovdqa 576(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm10, %ymm7
@@ -5340,7 +5350,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1600(%rsp), %ymm5
+vmovdqa 1600(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm9
 vpunpckhwd const0(%rip), %ymm5, %ymm10
 vpslld $1, %ymm9, %ymm9
@@ -5352,9 +5362,9 @@
 vpand mask32_to_16(%rip), %ymm7, %ymm7
 vpand mask32_to_16(%rip), %ymm8, %ymm8
 vpackusdw %ymm8, %ymm7, %ymm8
-vmovdqa 832(%rsp), %ymm7
-vpaddw 1088(%rsp), %ymm7, %ymm10
-vpsubw 1088(%rsp), %ymm7, %ymm7
+vmovdqa 832(%r8), %ymm7
+vpaddw 1088(%r8), %ymm7, %ymm10
+vpsubw 1088(%r8), %ymm7, %ymm7
 vpsrlw $2, %ymm7, %ymm7
 vpsubw %ymm6, %ymm7, %ymm7
 vpmullw %ymm14, %ymm7, %ymm7
@@ -5364,7 +5374,7 @@
 vpsubw %ymm10, %ymm9, %ymm10
 vpsrlw $3, %ymm10, %ymm10
 vpsubw %ymm8, %ymm10, %ymm10
-vmovdqa 1344(%rsp), %ymm9
+vmovdqa 1344(%r8), %ymm9
 vpsubw %ymm11, %ymm9, %ymm9
 vpmullw %ymm15, %ymm5, %ymm3
 vpsubw %ymm3, %ymm9, %ymm3
@@ -5389,7 +5399,7 @@
 vpand mask_keephigh(%rip), %ymm3, %ymm4
 vpor %ymm4, %ymm10, %ymm10
 vpaddw %ymm10, %ymm11, %ymm11
-vmovdqa %xmm3, 2112(%rsp)
+vmovdqa %xmm3, 2112(%r8)
 vpshufb shuf48_16(%rip), %ymm9, %ymm9
 vpand mask3_5_3_5(%rip), %ymm9, %ymm3
 vpand mask5_3_5_3(%rip), %ymm9, %ymm9
@@ -5397,7 +5407,7 @@
 vpand mask_keephigh(%rip), %ymm3, %ymm4
 vpor %ymm4, %ymm9, %ymm9
 vpaddw %ymm9, %ymm6, %ymm6
-vmovdqa %xmm3, 2368(%rsp)
+vmovdqa %xmm3, 2368(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_3_5(%rip), %ymm5, %ymm3
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
@@ -5405,7 +5415,7 @@
 vpand mask_keephigh(%rip), %ymm3, %ymm4
 vpor %ymm4, %ymm5, %ymm5
 vpaddw %ymm5, %ymm8, %ymm8
-vmovdqa %xmm3, 2624(%rsp)
+vmovdqa %xmm3, 2624(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 176(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -5414,15 +5424,15 @@
 vmovdqu %ymm8, 880(%rdi)
 vpand mask_mod8192(%rip), %ymm7, %ymm7
 vmovdqu %ymm7, 1232(%rdi)
-vmovdqa 96(%rsp), %ymm5
+vmovdqa 96(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm9
 vpunpckhwd const0(%rip), %ymm5, %ymm10
 vpslld $1, %ymm9, %ymm9
 vpslld $1, %ymm10, %ymm10
-vmovdqa 352(%rsp), %ymm7
+vmovdqa 352(%r8), %ymm7
 vpunpcklwd const0(%rip), %ymm7, %ymm8
 vpunpckhwd const0(%rip), %ymm7, %ymm7
-vmovdqa 608(%rsp), %ymm6
+vmovdqa 608(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm8, %ymm3
@@ -5436,7 +5446,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1632(%rsp), %ymm11
+vmovdqa 1632(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm7
 vpunpckhwd const0(%rip), %ymm11, %ymm8
 vpslld $1, %ymm7, %ymm7
@@ -5448,9 +5458,9 @@
 vpand mask32_to_16(%rip), %ymm3, %ymm3
 vpand mask32_to_16(%rip), %ymm4, %ymm4
 vpackusdw %ymm4, %ymm3, %ymm4
-vmovdqa 864(%rsp), %ymm3
-vpaddw 1120(%rsp), %ymm3, %ymm8
-vpsubw 1120(%rsp), %ymm3, %ymm3
+vmovdqa 864(%r8), %ymm3
+vpaddw 1120(%r8), %ymm3, %ymm8
+vpsubw 1120(%r8), %ymm3, %ymm3
 vpsrlw $2, %ymm3, %ymm3
 vpsubw %ymm6, %ymm3, %ymm3
 vpmullw %ymm14, %ymm3, %ymm3
@@ -5460,7 +5470,7 @@
 vpsubw %ymm8, %ymm7, %ymm8
 vpsrlw $3, %ymm8, %ymm8
 vpsubw %ymm4, %ymm8, %ymm8
-vmovdqa 1376(%rsp), %ymm7
+vmovdqa 1376(%r8), %ymm7
 vpsubw %ymm5, %ymm7, %ymm7
 vpmullw %ymm15, %ymm11, %ymm10
 vpsubw %ymm10, %ymm7, %ymm10
@@ -5485,7 +5495,7 @@
 vpand mask_keephigh(%rip), %ymm10, %ymm9
 vpor %ymm9, %ymm8, %ymm8
 vpaddw %ymm8, %ymm5, %ymm5
-vmovdqa %xmm10, 2144(%rsp)
+vmovdqa %xmm10, 2144(%r8)
 vpshufb shuf48_16(%rip), %ymm7, %ymm7
 vpand mask3_5_3_5(%rip), %ymm7, %ymm10
 vpand mask5_3_5_3(%rip), %ymm7, %ymm7
@@ -5493,7 +5503,7 @@
 vpand mask_keephigh(%rip), %ymm10, %ymm9
 vpor %ymm9, %ymm7, %ymm7
 vpaddw %ymm7, %ymm6, %ymm6
-vmovdqa %xmm10, 2400(%rsp)
+vmovdqa %xmm10, 2400(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_3_5(%rip), %ymm11, %ymm10
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
@@ -5501,7 +5511,7 @@
 vpand mask_keephigh(%rip), %ymm10, %ymm9
 vpor %ymm9, %ymm11, %ymm11
 vpaddw %ymm11, %ymm4, %ymm4
-vmovdqa %xmm10, 2656(%rsp)
+vmovdqa %xmm10, 2656(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %ymm5, 264(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -5510,15 +5520,15 @@
 vmovdqu %ymm4, 968(%rdi)
 vpand mask_mod8192(%rip), %ymm3, %ymm3
 vmovdqu %ymm3, 1320(%rdi)
-vmovdqa 128(%rsp), %ymm11
+vmovdqa 128(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm7
 vpunpckhwd const0(%rip), %ymm11, %ymm8
 vpslld $1, %ymm7, %ymm7
 vpslld $1, %ymm8, %ymm8
-vmovdqa 384(%rsp), %ymm3
+vmovdqa 384(%r8), %ymm3
 vpunpcklwd const0(%rip), %ymm3, %ymm4
 vpunpckhwd const0(%rip), %ymm3, %ymm3
-vmovdqa 640(%rsp), %ymm6
+vmovdqa 640(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm4, %ymm10
@@ -5532,7 +5542,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1664(%rsp), %ymm5
+vmovdqa 1664(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm3
 vpunpckhwd const0(%rip), %ymm5, %ymm4
 vpslld $1, %ymm3, %ymm3
@@ -5544,9 +5554,9 @@
 vpand mask32_to_16(%rip), %ymm10, %ymm10
 vpand mask32_to_16(%rip), %ymm9, %ymm9
 vpackusdw %ymm9, %ymm10, %ymm9
-vmovdqa 896(%rsp), %ymm10
-vpaddw 1152(%rsp), %ymm10, %ymm4
-vpsubw 1152(%rsp), %ymm10, %ymm10
+vmovdqa 896(%r8), %ymm10
+vpaddw 1152(%r8), %ymm10, %ymm4
+vpsubw 1152(%r8), %ymm10, %ymm10
 vpsrlw $2, %ymm10, %ymm10
 vpsubw %ymm6, %ymm10, %ymm10
 vpmullw %ymm14, %ymm10, %ymm10
@@ -5556,7 +5566,7 @@
 vpsubw %ymm4, %ymm3, %ymm4
 vpsrlw $3, %ymm4, %ymm4
 vpsubw %ymm9, %ymm4, %ymm4
-vmovdqa 1408(%rsp), %ymm3
+vmovdqa 1408(%r8), %ymm3
 vpsubw %ymm11, %ymm3, %ymm3
 vpmullw %ymm15, %ymm5, %ymm8
 vpsubw %ymm8, %ymm3, %ymm8
@@ -5590,7 +5600,7 @@
 vpaddw %ymm10, %ymm7, %ymm7
 vpand mask_mod8192(%rip), %ymm7, %ymm7
 vmovdqu %ymm7, 0(%rdi)
-vmovdqa %xmm2, 1920(%rsp)
+vmovdqa %xmm2, 1920(%r8)
 vpshufb shuf48_16(%rip), %ymm4, %ymm4
 vpand mask3_5_3_5(%rip), %ymm4, %ymm2
 vpand mask5_3_5_3(%rip), %ymm4, %ymm4
@@ -5598,7 +5608,7 @@
 vpand mask_keephigh(%rip), %ymm2, %ymm7
 vpor %ymm7, %ymm4, %ymm4
 vpaddw %ymm4, %ymm11, %ymm11
-vmovdqa %xmm2, 2176(%rsp)
+vmovdqa %xmm2, 2176(%r8)
 vpshufb shuf48_16(%rip), %ymm3, %ymm3
 vpand mask3_5_3_5(%rip), %ymm3, %ymm2
 vpand mask5_3_5_3(%rip), %ymm3, %ymm3
@@ -5606,7 +5616,7 @@
 vpand mask_keephigh(%rip), %ymm2, %ymm7
 vpor %ymm7, %ymm3, %ymm3
 vpaddw %ymm3, %ymm6, %ymm6
-vmovdqa %xmm2, 2432(%rsp)
+vmovdqa %xmm2, 2432(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_3_5(%rip), %ymm5, %ymm2
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
@@ -5614,22 +5624,22 @@
 vpand mask_keephigh(%rip), %ymm2, %ymm7
 vpor %ymm7, %ymm5, %ymm5
 vpaddw %ymm5, %ymm9, %ymm9
-vmovdqa %xmm2, 2688(%rsp)
+vmovdqa %xmm2, 2688(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 352(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
 vmovdqu %ymm6, 704(%rdi)
 vpand mask_mod8192(%rip), %ymm9, %ymm9
 vmovdqu %ymm9, 1056(%rdi)
-vmovdqa 160(%rsp), %ymm5
+vmovdqa 160(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm3
 vpunpckhwd const0(%rip), %ymm5, %ymm4
 vpslld $1, %ymm3, %ymm3
 vpslld $1, %ymm4, %ymm4
-vmovdqa 416(%rsp), %ymm10
+vmovdqa 416(%r8), %ymm10
 vpunpcklwd const0(%rip), %ymm10, %ymm9
 vpunpckhwd const0(%rip), %ymm10, %ymm10
-vmovdqa 672(%rsp), %ymm6
+vmovdqa 672(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm9, %ymm2
@@ -5643,7 +5653,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1696(%rsp), %ymm11
+vmovdqa 1696(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm10
 vpunpckhwd const0(%rip), %ymm11, %ymm9
 vpslld $1, %ymm10, %ymm10
@@ -5655,9 +5665,9 @@
 vpand mask32_to_16(%rip), %ymm2, %ymm2
 vpand mask32_to_16(%rip), %ymm7, %ymm7
 vpackusdw %ymm7, %ymm2, %ymm7
-vmovdqa 928(%rsp), %ymm2
-vpaddw 1184(%rsp), %ymm2, %ymm9
-vpsubw 1184(%rsp), %ymm2, %ymm2
+vmovdqa 928(%r8), %ymm2
+vpaddw 1184(%r8), %ymm2, %ymm9
+vpsubw 1184(%r8), %ymm2, %ymm2
 vpsrlw $2, %ymm2, %ymm2
 vpsubw %ymm6, %ymm2, %ymm2
 vpmullw %ymm14, %ymm2, %ymm2
@@ -5667,7 +5677,7 @@
 vpsubw %ymm9, %ymm10, %ymm9
 vpsrlw $3, %ymm9, %ymm9
 vpsubw %ymm7, %ymm9, %ymm9
-vmovdqa 1440(%rsp), %ymm10
+vmovdqa 1440(%r8), %ymm10
 vpsubw %ymm5, %ymm10, %ymm10
 vpmullw %ymm15, %ymm11, %ymm4
 vpsubw %ymm4, %ymm10, %ymm4
@@ -5701,7 +5711,7 @@
 vpaddw %ymm2, %ymm3, %ymm3
 vpand mask_mod8192(%rip), %ymm3, %ymm3
 vmovdqu %ymm3, 88(%rdi)
-vmovdqa %xmm8, 1952(%rsp)
+vmovdqa %xmm8, 1952(%r8)
 vpshufb shuf48_16(%rip), %ymm9, %ymm9
 vpand mask3_5_3_5(%rip), %ymm9, %ymm8
 vpand mask5_3_5_3(%rip), %ymm9, %ymm9
@@ -5709,7 +5719,7 @@
 vpand mask_keephigh(%rip), %ymm8, %ymm3
 vpor %ymm3, %ymm9, %ymm9
 vpaddw %ymm9, %ymm5, %ymm5
-vmovdqa %xmm8, 2208(%rsp)
+vmovdqa %xmm8, 2208(%r8)
 vpshufb shuf48_16(%rip), %ymm10, %ymm10
 vpand mask3_5_3_5(%rip), %ymm10, %ymm8
 vpand mask5_3_5_3(%rip), %ymm10, %ymm10
@@ -5717,7 +5727,7 @@
 vpand mask_keephigh(%rip), %ymm8, %ymm3
 vpor %ymm3, %ymm10, %ymm10
 vpaddw %ymm10, %ymm6, %ymm6
-vmovdqa %xmm8, 2464(%rsp)
+vmovdqa %xmm8, 2464(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_3_5(%rip), %ymm11, %ymm8
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
@@ -5725,22 +5735,22 @@
 vpand mask_keephigh(%rip), %ymm8, %ymm3
 vpor %ymm3, %ymm11, %ymm11
 vpaddw %ymm11, %ymm7, %ymm7
-vmovdqa %xmm8, 2720(%rsp)
+vmovdqa %xmm8, 2720(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %ymm5, 440(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
 vmovdqu %ymm6, 792(%rdi)
 vpand mask_mod8192(%rip), %ymm7, %ymm7
 vmovdqu %ymm7, 1144(%rdi)
-vmovdqa 192(%rsp), %ymm11
+vmovdqa 192(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm10
 vpunpckhwd const0(%rip), %ymm11, %ymm9
 vpslld $1, %ymm10, %ymm10
 vpslld $1, %ymm9, %ymm9
-vmovdqa 448(%rsp), %ymm2
+vmovdqa 448(%r8), %ymm2
 vpunpcklwd const0(%rip), %ymm2, %ymm7
 vpunpckhwd const0(%rip), %ymm2, %ymm2
-vmovdqa 704(%rsp), %ymm6
+vmovdqa 704(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm7, %ymm8
@@ -5754,7 +5764,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1728(%rsp), %ymm5
+vmovdqa 1728(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm2
 vpunpckhwd const0(%rip), %ymm5, %ymm7
 vpslld $1, %ymm2, %ymm2
@@ -5766,9 +5776,9 @@
 vpand mask32_to_16(%rip), %ymm8, %ymm8
 vpand mask32_to_16(%rip), %ymm3, %ymm3
 vpackusdw %ymm3, %ymm8, %ymm3
-vmovdqa 960(%rsp), %ymm8
-vpaddw 1216(%rsp), %ymm8, %ymm7
-vpsubw 1216(%rsp), %ymm8, %ymm8
+vmovdqa 960(%r8), %ymm8
+vpaddw 1216(%r8), %ymm8, %ymm7
+vpsubw 1216(%r8), %ymm8, %ymm8
 vpsrlw $2, %ymm8, %ymm8
 vpsubw %ymm6, %ymm8, %ymm8
 vpmullw %ymm14, %ymm8, %ymm8
@@ -5778,7 +5788,7 @@
 vpsubw %ymm7, %ymm2, %ymm7
 vpsrlw $3, %ymm7, %ymm7
 vpsubw %ymm3, %ymm7, %ymm7
-vmovdqa 1472(%rsp), %ymm2
+vmovdqa 1472(%r8), %ymm2
 vpsubw %ymm11, %ymm2, %ymm2
 vpmullw %ymm15, %ymm5, %ymm9
 vpsubw %ymm9, %ymm2, %ymm9
@@ -5812,7 +5822,7 @@
 vpaddw %ymm8, %ymm10, %ymm10
 vpand mask_mod8192(%rip), %ymm10, %ymm10
 vmovdqu %ymm10, 176(%rdi)
-vmovdqa %xmm4, 1984(%rsp)
+vmovdqa %xmm4, 1984(%r8)
 vpshufb shuf48_16(%rip), %ymm7, %ymm7
 vpand mask3_5_3_5(%rip), %ymm7, %ymm4
 vpand mask5_3_5_3(%rip), %ymm7, %ymm7
@@ -5820,7 +5830,7 @@
 vpand mask_keephigh(%rip), %ymm4, %ymm10
 vpor %ymm10, %ymm7, %ymm7
 vpaddw %ymm7, %ymm11, %ymm11
-vmovdqa %xmm4, 2240(%rsp)
+vmovdqa %xmm4, 2240(%r8)
 vpshufb shuf48_16(%rip), %ymm2, %ymm2
 vpand mask3_5_3_5(%rip), %ymm2, %ymm4
 vpand mask5_3_5_3(%rip), %ymm2, %ymm2
@@ -5828,7 +5838,7 @@
 vpand mask_keephigh(%rip), %ymm4, %ymm10
 vpor %ymm10, %ymm2, %ymm2
 vpaddw %ymm2, %ymm6, %ymm6
-vmovdqa %xmm4, 2496(%rsp)
+vmovdqa %xmm4, 2496(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_3_5(%rip), %ymm5, %ymm4
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
@@ -5836,22 +5846,22 @@
 vpand mask_keephigh(%rip), %ymm4, %ymm10
 vpor %ymm10, %ymm5, %ymm5
 vpaddw %ymm5, %ymm3, %ymm3
-vmovdqa %xmm4, 2752(%rsp)
+vmovdqa %xmm4, 2752(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 528(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
 vmovdqu %ymm6, 880(%rdi)
 vpand mask_mod8192(%rip), %ymm3, %ymm3
 vmovdqu %ymm3, 1232(%rdi)
-vmovdqa 224(%rsp), %ymm5
+vmovdqa 224(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm2
 vpunpckhwd const0(%rip), %ymm5, %ymm7
 vpslld $1, %ymm2, %ymm2
 vpslld $1, %ymm7, %ymm7
-vmovdqa 480(%rsp), %ymm8
+vmovdqa 480(%r8), %ymm8
 vpunpcklwd const0(%rip), %ymm8, %ymm3
 vpunpckhwd const0(%rip), %ymm8, %ymm8
-vmovdqa 736(%rsp), %ymm6
+vmovdqa 736(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm3, %ymm4
@@ -5865,7 +5875,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1760(%rsp), %ymm11
+vmovdqa 1760(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm8
 vpunpckhwd const0(%rip), %ymm11, %ymm3
 vpslld $1, %ymm8, %ymm8
@@ -5877,9 +5887,9 @@
 vpand mask32_to_16(%rip), %ymm4, %ymm4
 vpand mask32_to_16(%rip), %ymm10, %ymm10
 vpackusdw %ymm10, %ymm4, %ymm10
-vmovdqa 992(%rsp), %ymm4
-vpaddw 1248(%rsp), %ymm4, %ymm3
-vpsubw 1248(%rsp), %ymm4, %ymm4
+vmovdqa 992(%r8), %ymm4
+vpaddw 1248(%r8), %ymm4, %ymm3
+vpsubw 1248(%r8), %ymm4, %ymm4
 vpsrlw $2, %ymm4, %ymm4
 vpsubw %ymm6, %ymm4, %ymm4
 vpmullw %ymm14, %ymm4, %ymm4
@@ -5889,7 +5899,7 @@
 vpsubw %ymm3, %ymm8, %ymm3
 vpsrlw $3, %ymm3, %ymm3
 vpsubw %ymm10, %ymm3, %ymm3
-vmovdqa 1504(%rsp), %ymm8
+vmovdqa 1504(%r8), %ymm8
 vpsubw %ymm5, %ymm8, %ymm8
 vpmullw %ymm15, %ymm11, %ymm7
 vpsubw %ymm7, %ymm8, %ymm7
@@ -5923,7 +5933,7 @@
 vpaddw %ymm4, %ymm2, %ymm2
 vpand mask_mod8192(%rip), %ymm2, %ymm2
 vmovdqu %ymm2, 264(%rdi)
-vmovdqa %xmm9, 2016(%rsp)
+vmovdqa %xmm9, 2016(%r8)
 vpshufb shuf48_16(%rip), %ymm3, %ymm3
 vpand mask3_5_3_5(%rip), %ymm3, %ymm9
 vpand mask5_3_5_3(%rip), %ymm3, %ymm3
@@ -5931,7 +5941,7 @@
 vpand mask_keephigh(%rip), %ymm9, %ymm2
 vpor %ymm2, %ymm3, %ymm3
 vpaddw %ymm3, %ymm5, %ymm5
-vmovdqa %xmm9, 2272(%rsp)
+vmovdqa %xmm9, 2272(%r8)
 vpshufb shuf48_16(%rip), %ymm8, %ymm8
 vpand mask3_5_3_5(%rip), %ymm8, %ymm9
 vpand mask5_3_5_3(%rip), %ymm8, %ymm8
@@ -5939,7 +5949,7 @@
 vpand mask_keephigh(%rip), %ymm9, %ymm2
 vpor %ymm2, %ymm8, %ymm8
 vpaddw %ymm8, %ymm6, %ymm6
-vmovdqa %xmm9, 2528(%rsp)
+vmovdqa %xmm9, 2528(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_3_5(%rip), %ymm11, %ymm9
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
@@ -5947,7 +5957,7 @@
 vpand mask_keephigh(%rip), %ymm9, %ymm2
 vpor %ymm2, %ymm11, %ymm11
 vpaddw %ymm11, %ymm10, %ymm10
-vmovdqa %xmm9, 2784(%rsp)
+vmovdqa %xmm9, 2784(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %ymm5, 616(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -5989,14 +5999,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 32(%r12), %ymm8
 vmovdqa 896(%r12), %ymm9
-vmovdqa %ymm8, 0(%rsp)
-vmovdqa %ymm0, 32(%rsp)
-vmovdqa %ymm1, 64(%rsp)
-vmovdqa %ymm7, 96(%rsp)
-vmovdqa %ymm5, 128(%rsp)
-vmovdqa %ymm2, 160(%rsp)
-vmovdqa %ymm3, 192(%rsp)
-vmovdqa %ymm9, 224(%rsp)
+vmovdqa %ymm8, 0(%r8)
+vmovdqa %ymm0, 32(%r8)
+vmovdqa %ymm1, 64(%r8)
+vmovdqa %ymm7, 96(%r8)
+vmovdqa %ymm5, 128(%r8)
+vmovdqa %ymm2, 160(%r8)
+vmovdqa %ymm3, 192(%r8)
+vmovdqa %ymm9, 224(%r8)
 vmovdqa 1856(%r12), %ymm0
 vpsubw 1952(%r12), %ymm0, %ymm0
 vmovdqa 2240(%r12), %ymm1
@@ -6032,14 +6042,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 1760(%r12), %ymm8
 vmovdqa 2624(%r12), %ymm9
-vmovdqa %ymm8, 256(%rsp)
-vmovdqa %ymm0, 288(%rsp)
-vmovdqa %ymm1, 320(%rsp)
-vmovdqa %ymm7, 352(%rsp)
-vmovdqa %ymm5, 384(%rsp)
-vmovdqa %ymm2, 416(%rsp)
-vmovdqa %ymm3, 448(%rsp)
-vmovdqa %ymm9, 480(%rsp)
+vmovdqa %ymm8, 256(%r8)
+vmovdqa %ymm0, 288(%r8)
+vmovdqa %ymm1, 320(%r8)
+vmovdqa %ymm7, 352(%r8)
+vmovdqa %ymm5, 384(%r8)
+vmovdqa %ymm2, 416(%r8)
+vmovdqa %ymm3, 448(%r8)
+vmovdqa %ymm9, 480(%r8)
 vmovdqa 3584(%r12), %ymm0
 vpsubw 3680(%r12), %ymm0, %ymm0
 vmovdqa 3968(%r12), %ymm1
@@ -6075,14 +6085,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 3488(%r12), %ymm8
 vmovdqa 4352(%r12), %ymm9
-vmovdqa %ymm8, 512(%rsp)
-vmovdqa %ymm0, 544(%rsp)
-vmovdqa %ymm1, 576(%rsp)
-vmovdqa %ymm7, 608(%rsp)
-vmovdqa %ymm5, 640(%rsp)
-vmovdqa %ymm2, 672(%rsp)
-vmovdqa %ymm3, 704(%rsp)
-vmovdqa %ymm9, 736(%rsp)
+vmovdqa %ymm8, 512(%r8)
+vmovdqa %ymm0, 544(%r8)
+vmovdqa %ymm1, 576(%r8)
+vmovdqa %ymm7, 608(%r8)
+vmovdqa %ymm5, 640(%r8)
+vmovdqa %ymm2, 672(%r8)
+vmovdqa %ymm3, 704(%r8)
+vmovdqa %ymm9, 736(%r8)
 vmovdqa 5312(%r12), %ymm0
 vpsubw 5408(%r12), %ymm0, %ymm0
 vmovdqa 5696(%r12), %ymm1
@@ -6118,14 +6128,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 5216(%r12), %ymm8
 vmovdqa 6080(%r12), %ymm9
-vmovdqa %ymm8, 768(%rsp)
-vmovdqa %ymm0, 800(%rsp)
-vmovdqa %ymm1, 832(%rsp)
-vmovdqa %ymm7, 864(%rsp)
-vmovdqa %ymm5, 896(%rsp)
-vmovdqa %ymm2, 928(%rsp)
-vmovdqa %ymm3, 960(%rsp)
-vmovdqa %ymm9, 992(%rsp)
+vmovdqa %ymm8, 768(%r8)
+vmovdqa %ymm0, 800(%r8)
+vmovdqa %ymm1, 832(%r8)
+vmovdqa %ymm7, 864(%r8)
+vmovdqa %ymm5, 896(%r8)
+vmovdqa %ymm2, 928(%r8)
+vmovdqa %ymm3, 960(%r8)
+vmovdqa %ymm9, 992(%r8)
 vmovdqa 7040(%r12), %ymm0
 vpsubw 7136(%r12), %ymm0, %ymm0
 vmovdqa 7424(%r12), %ymm1
@@ -6161,14 +6171,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 6944(%r12), %ymm8
 vmovdqa 7808(%r12), %ymm9
-vmovdqa %ymm8, 1024(%rsp)
-vmovdqa %ymm0, 1056(%rsp)
-vmovdqa %ymm1, 1088(%rsp)
-vmovdqa %ymm7, 1120(%rsp)
-vmovdqa %ymm5, 1152(%rsp)
-vmovdqa %ymm2, 1184(%rsp)
-vmovdqa %ymm3, 1216(%rsp)
-vmovdqa %ymm9, 1248(%rsp)
+vmovdqa %ymm8, 1024(%r8)
+vmovdqa %ymm0, 1056(%r8)
+vmovdqa %ymm1, 1088(%r8)
+vmovdqa %ymm7, 1120(%r8)
+vmovdqa %ymm5, 1152(%r8)
+vmovdqa %ymm2, 1184(%r8)
+vmovdqa %ymm3, 1216(%r8)
+vmovdqa %ymm9, 1248(%r8)
 vmovdqa 8768(%r12), %ymm0
 vpsubw 8864(%r12), %ymm0, %ymm0
 vmovdqa 9152(%r12), %ymm1
@@ -6204,14 +6214,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 8672(%r12), %ymm8
 vmovdqa 9536(%r12), %ymm9
-vmovdqa %ymm8, 1280(%rsp)
-vmovdqa %ymm0, 1312(%rsp)
-vmovdqa %ymm1, 1344(%rsp)
-vmovdqa %ymm7, 1376(%rsp)
-vmovdqa %ymm5, 1408(%rsp)
-vmovdqa %ymm2, 1440(%rsp)
-vmovdqa %ymm3, 1472(%rsp)
-vmovdqa %ymm9, 1504(%rsp)
+vmovdqa %ymm8, 1280(%r8)
+vmovdqa %ymm0, 1312(%r8)
+vmovdqa %ymm1, 1344(%r8)
+vmovdqa %ymm7, 1376(%r8)
+vmovdqa %ymm5, 1408(%r8)
+vmovdqa %ymm2, 1440(%r8)
+vmovdqa %ymm3, 1472(%r8)
+vmovdqa %ymm9, 1504(%r8)
 vmovdqa 10496(%r12), %ymm0
 vpsubw 10592(%r12), %ymm0, %ymm0
 vmovdqa 10880(%r12), %ymm1
@@ -6247,23 +6257,23 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 10400(%r12), %ymm8
 vmovdqa 11264(%r12), %ymm9
-vmovdqa %ymm8, 1536(%rsp)
-vmovdqa %ymm0, 1568(%rsp)
-vmovdqa %ymm1, 1600(%rsp)
-vmovdqa %ymm7, 1632(%rsp)
-vmovdqa %ymm5, 1664(%rsp)
-vmovdqa %ymm2, 1696(%rsp)
-vmovdqa %ymm3, 1728(%rsp)
-vmovdqa %ymm9, 1760(%rsp)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa %ymm8, 1536(%r8)
+vmovdqa %ymm0, 1568(%r8)
+vmovdqa %ymm1, 1600(%r8)
+vmovdqa %ymm7, 1632(%r8)
+vmovdqa %ymm5, 1664(%r8)
+vmovdqa %ymm2, 1696(%r8)
+vmovdqa %ymm3, 1728(%r8)
+vmovdqa %ymm9, 1760(%r8)
+vmovdqa 0(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm8
 vpunpckhwd const0(%rip), %ymm11, %ymm3
 vpslld $1, %ymm8, %ymm8
 vpslld $1, %ymm3, %ymm3
-vmovdqa 256(%rsp), %ymm4
+vmovdqa 256(%r8), %ymm4
 vpunpcklwd const0(%rip), %ymm4, %ymm10
 vpunpckhwd const0(%rip), %ymm4, %ymm4
-vmovdqa 512(%rsp), %ymm6
+vmovdqa 512(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm10, %ymm9
@@ -6277,7 +6287,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1536(%rsp), %ymm5
+vmovdqa 1536(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm4
 vpunpckhwd const0(%rip), %ymm5, %ymm10
 vpslld $1, %ymm4, %ymm4
@@ -6289,9 +6299,9 @@
 vpand mask32_to_16(%rip), %ymm9, %ymm9
 vpand mask32_to_16(%rip), %ymm2, %ymm2
 vpackusdw %ymm2, %ymm9, %ymm2
-vmovdqa 768(%rsp), %ymm9
-vpaddw 1024(%rsp), %ymm9, %ymm10
-vpsubw 1024(%rsp), %ymm9, %ymm9
+vmovdqa 768(%r8), %ymm9
+vpaddw 1024(%r8), %ymm9, %ymm10
+vpsubw 1024(%r8), %ymm9, %ymm9
 vpsrlw $2, %ymm9, %ymm9
 vpsubw %ymm6, %ymm9, %ymm9
 vpmullw %ymm14, %ymm9, %ymm9
@@ -6301,7 +6311,7 @@
 vpsubw %ymm10, %ymm4, %ymm10
 vpsrlw $3, %ymm10, %ymm10
 vpsubw %ymm2, %ymm10, %ymm10
-vmovdqa 1280(%rsp), %ymm4
+vmovdqa 1280(%r8), %ymm4
 vpsubw %ymm11, %ymm4, %ymm4
 vpmullw %ymm15, %ymm5, %ymm3
 vpsubw %ymm3, %ymm4, %ymm3
@@ -6325,27 +6335,27 @@
 vpermq $206, %ymm3, %ymm3
 vpand mask_keephigh(%rip), %ymm3, %ymm8
 vpor %ymm8, %ymm10, %ymm10
-vpaddw 2048(%rsp), %ymm11, %ymm11
+vpaddw 2048(%r8), %ymm11, %ymm11
 vpaddw %ymm10, %ymm11, %ymm11
-vmovdqa %xmm3, 2048(%rsp)
+vmovdqa %xmm3, 2048(%r8)
 vpshufb shuf48_16(%rip), %ymm4, %ymm4
 vpand mask3_5_3_5(%rip), %ymm4, %ymm3
 vpand mask5_3_5_3(%rip), %ymm4, %ymm4
 vpermq $206, %ymm3, %ymm3
 vpand mask_keephigh(%rip), %ymm3, %ymm8
 vpor %ymm8, %ymm4, %ymm4
-vpaddw 2304(%rsp), %ymm6, %ymm6
+vpaddw 2304(%r8), %ymm6, %ymm6
 vpaddw %ymm4, %ymm6, %ymm6
-vmovdqa %xmm3, 2304(%rsp)
+vmovdqa %xmm3, 2304(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_3_5(%rip), %ymm5, %ymm3
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
 vpermq $206, %ymm3, %ymm3
 vpand mask_keephigh(%rip), %ymm3, %ymm8
 vpor %ymm8, %ymm5, %ymm5
-vpaddw 2560(%rsp), %ymm2, %ymm2
+vpaddw 2560(%r8), %ymm2, %ymm2
 vpaddw %ymm5, %ymm2, %ymm2
-vmovdqa %xmm3, 2560(%rsp)
+vmovdqa %xmm3, 2560(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 32(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -6354,15 +6364,15 @@
 vmovdqu %ymm2, 736(%rdi)
 vpand mask_mod8192(%rip), %ymm9, %ymm9
 vmovdqu %ymm9, 1088(%rdi)
-vmovdqa 32(%rsp), %ymm5
+vmovdqa 32(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm4
 vpunpckhwd const0(%rip), %ymm5, %ymm10
 vpslld $1, %ymm4, %ymm4
 vpslld $1, %ymm10, %ymm10
-vmovdqa 288(%rsp), %ymm9
+vmovdqa 288(%r8), %ymm9
 vpunpcklwd const0(%rip), %ymm9, %ymm2
 vpunpckhwd const0(%rip), %ymm9, %ymm9
-vmovdqa 544(%rsp), %ymm6
+vmovdqa 544(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm2, %ymm3
@@ -6376,7 +6386,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1568(%rsp), %ymm11
+vmovdqa 1568(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm9
 vpunpckhwd const0(%rip), %ymm11, %ymm2
 vpslld $1, %ymm9, %ymm9
@@ -6388,9 +6398,9 @@
 vpand mask32_to_16(%rip), %ymm3, %ymm3
 vpand mask32_to_16(%rip), %ymm8, %ymm8
 vpackusdw %ymm8, %ymm3, %ymm8
-vmovdqa 800(%rsp), %ymm3
-vpaddw 1056(%rsp), %ymm3, %ymm2
-vpsubw 1056(%rsp), %ymm3, %ymm3
+vmovdqa 800(%r8), %ymm3
+vpaddw 1056(%r8), %ymm3, %ymm2
+vpsubw 1056(%r8), %ymm3, %ymm3
 vpsrlw $2, %ymm3, %ymm3
 vpsubw %ymm6, %ymm3, %ymm3
 vpmullw %ymm14, %ymm3, %ymm3
@@ -6400,7 +6410,7 @@
 vpsubw %ymm2, %ymm9, %ymm2
 vpsrlw $3, %ymm2, %ymm2
 vpsubw %ymm8, %ymm2, %ymm2
-vmovdqa 1312(%rsp), %ymm9
+vmovdqa 1312(%r8), %ymm9
 vpsubw %ymm5, %ymm9, %ymm9
 vpmullw %ymm15, %ymm11, %ymm10
 vpsubw %ymm10, %ymm9, %ymm10
@@ -6424,27 +6434,27 @@
 vpermq $206, %ymm10, %ymm10
 vpand mask_keephigh(%rip), %ymm10, %ymm4
 vpor %ymm4, %ymm2, %ymm2
-vpaddw 2080(%rsp), %ymm5, %ymm5
+vpaddw 2080(%r8), %ymm5, %ymm5
 vpaddw %ymm2, %ymm5, %ymm5
-vmovdqa %xmm10, 2080(%rsp)
+vmovdqa %xmm10, 2080(%r8)
 vpshufb shuf48_16(%rip), %ymm9, %ymm9
 vpand mask3_5_3_5(%rip), %ymm9, %ymm10
 vpand mask5_3_5_3(%rip), %ymm9, %ymm9
 vpermq $206, %ymm10, %ymm10
 vpand mask_keephigh(%rip), %ymm10, %ymm4
 vpor %ymm4, %ymm9, %ymm9
-vpaddw 2336(%rsp), %ymm6, %ymm6
+vpaddw 2336(%r8), %ymm6, %ymm6
 vpaddw %ymm9, %ymm6, %ymm6
-vmovdqa %xmm10, 2336(%rsp)
+vmovdqa %xmm10, 2336(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_3_5(%rip), %ymm11, %ymm10
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
 vpermq $206, %ymm10, %ymm10
 vpand mask_keephigh(%rip), %ymm10, %ymm4
 vpor %ymm4, %ymm11, %ymm11
-vpaddw 2592(%rsp), %ymm8, %ymm8
+vpaddw 2592(%r8), %ymm8, %ymm8
 vpaddw %ymm11, %ymm8, %ymm8
-vmovdqa %xmm10, 2592(%rsp)
+vmovdqa %xmm10, 2592(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %ymm5, 120(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -6453,15 +6463,15 @@
 vmovdqu %ymm8, 824(%rdi)
 vpand mask_mod8192(%rip), %ymm3, %ymm3
 vmovdqu %ymm3, 1176(%rdi)
-vmovdqa 64(%rsp), %ymm11
+vmovdqa 64(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm9
 vpunpckhwd const0(%rip), %ymm11, %ymm2
 vpslld $1, %ymm9, %ymm9
 vpslld $1, %ymm2, %ymm2
-vmovdqa 320(%rsp), %ymm3
+vmovdqa 320(%r8), %ymm3
 vpunpcklwd const0(%rip), %ymm3, %ymm8
 vpunpckhwd const0(%rip), %ymm3, %ymm3
-vmovdqa 576(%rsp), %ymm6
+vmovdqa 576(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm8, %ymm10
@@ -6475,7 +6485,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1600(%rsp), %ymm5
+vmovdqa 1600(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm3
 vpunpckhwd const0(%rip), %ymm5, %ymm8
 vpslld $1, %ymm3, %ymm3
@@ -6487,9 +6497,9 @@
 vpand mask32_to_16(%rip), %ymm10, %ymm10
 vpand mask32_to_16(%rip), %ymm4, %ymm4
 vpackusdw %ymm4, %ymm10, %ymm4
-vmovdqa 832(%rsp), %ymm10
-vpaddw 1088(%rsp), %ymm10, %ymm8
-vpsubw 1088(%rsp), %ymm10, %ymm10
+vmovdqa 832(%r8), %ymm10
+vpaddw 1088(%r8), %ymm10, %ymm8
+vpsubw 1088(%r8), %ymm10, %ymm10
 vpsrlw $2, %ymm10, %ymm10
 vpsubw %ymm6, %ymm10, %ymm10
 vpmullw %ymm14, %ymm10, %ymm10
@@ -6499,7 +6509,7 @@
 vpsubw %ymm8, %ymm3, %ymm8
 vpsrlw $3, %ymm8, %ymm8
 vpsubw %ymm4, %ymm8, %ymm8
-vmovdqa 1344(%rsp), %ymm3
+vmovdqa 1344(%r8), %ymm3
 vpsubw %ymm11, %ymm3, %ymm3
 vpmullw %ymm15, %ymm5, %ymm2
 vpsubw %ymm2, %ymm3, %ymm2
@@ -6523,27 +6533,27 @@
 vpermq $206, %ymm2, %ymm2
 vpand mask_keephigh(%rip), %ymm2, %ymm9
 vpor %ymm9, %ymm8, %ymm8
-vpaddw 2112(%rsp), %ymm11, %ymm11
+vpaddw 2112(%r8), %ymm11, %ymm11
 vpaddw %ymm8, %ymm11, %ymm11
-vmovdqa %xmm2, 2112(%rsp)
+vmovdqa %xmm2, 2112(%r8)
 vpshufb shuf48_16(%rip), %ymm3, %ymm3
 vpand mask3_5_3_5(%rip), %ymm3, %ymm2
 vpand mask5_3_5_3(%rip), %ymm3, %ymm3
 vpermq $206, %ymm2, %ymm2
 vpand mask_keephigh(%rip), %ymm2, %ymm9
 vpor %ymm9, %ymm3, %ymm3
-vpaddw 2368(%rsp), %ymm6, %ymm6
+vpaddw 2368(%r8), %ymm6, %ymm6
 vpaddw %ymm3, %ymm6, %ymm6
-vmovdqa %xmm2, 2368(%rsp)
+vmovdqa %xmm2, 2368(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_3_5(%rip), %ymm5, %ymm2
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
 vpermq $206, %ymm2, %ymm2
 vpand mask_keephigh(%rip), %ymm2, %ymm9
 vpor %ymm9, %ymm5, %ymm5
-vpaddw 2624(%rsp), %ymm4, %ymm4
+vpaddw 2624(%r8), %ymm4, %ymm4
 vpaddw %ymm5, %ymm4, %ymm4
-vmovdqa %xmm2, 2624(%rsp)
+vmovdqa %xmm2, 2624(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 208(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -6552,15 +6562,15 @@
 vmovdqu %ymm4, 912(%rdi)
 vpand mask_mod8192(%rip), %ymm10, %ymm10
 vmovdqu %ymm10, 1264(%rdi)
-vmovdqa 96(%rsp), %ymm5
+vmovdqa 96(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm3
 vpunpckhwd const0(%rip), %ymm5, %ymm8
 vpslld $1, %ymm3, %ymm3
 vpslld $1, %ymm8, %ymm8
-vmovdqa 352(%rsp), %ymm10
+vmovdqa 352(%r8), %ymm10
 vpunpcklwd const0(%rip), %ymm10, %ymm4
 vpunpckhwd const0(%rip), %ymm10, %ymm10
-vmovdqa 608(%rsp), %ymm6
+vmovdqa 608(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm4, %ymm2
@@ -6574,7 +6584,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1632(%rsp), %ymm11
+vmovdqa 1632(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm10
 vpunpckhwd const0(%rip), %ymm11, %ymm4
 vpslld $1, %ymm10, %ymm10
@@ -6586,9 +6596,9 @@
 vpand mask32_to_16(%rip), %ymm2, %ymm2
 vpand mask32_to_16(%rip), %ymm9, %ymm9
 vpackusdw %ymm9, %ymm2, %ymm9
-vmovdqa 864(%rsp), %ymm2
-vpaddw 1120(%rsp), %ymm2, %ymm4
-vpsubw 1120(%rsp), %ymm2, %ymm2
+vmovdqa 864(%r8), %ymm2
+vpaddw 1120(%r8), %ymm2, %ymm4
+vpsubw 1120(%r8), %ymm2, %ymm2
 vpsrlw $2, %ymm2, %ymm2
 vpsubw %ymm6, %ymm2, %ymm2
 vpmullw %ymm14, %ymm2, %ymm2
@@ -6598,7 +6608,7 @@
 vpsubw %ymm4, %ymm10, %ymm4
 vpsrlw $3, %ymm4, %ymm4
 vpsubw %ymm9, %ymm4, %ymm4
-vmovdqa 1376(%rsp), %ymm10
+vmovdqa 1376(%r8), %ymm10
 vpsubw %ymm5, %ymm10, %ymm10
 vpmullw %ymm15, %ymm11, %ymm8
 vpsubw %ymm8, %ymm10, %ymm8
@@ -6622,27 +6632,27 @@
 vpermq $206, %ymm8, %ymm8
 vpand mask_keephigh(%rip), %ymm8, %ymm3
 vpor %ymm3, %ymm4, %ymm4
-vpaddw 2144(%rsp), %ymm5, %ymm5
+vpaddw 2144(%r8), %ymm5, %ymm5
 vpaddw %ymm4, %ymm5, %ymm5
-vmovdqa %xmm8, 2144(%rsp)
+vmovdqa %xmm8, 2144(%r8)
 vpshufb shuf48_16(%rip), %ymm10, %ymm10
 vpand mask3_5_3_5(%rip), %ymm10, %ymm8
 vpand mask5_3_5_3(%rip), %ymm10, %ymm10
 vpermq $206, %ymm8, %ymm8
 vpand mask_keephigh(%rip), %ymm8, %ymm3
 vpor %ymm3, %ymm10, %ymm10
-vpaddw 2400(%rsp), %ymm6, %ymm6
+vpaddw 2400(%r8), %ymm6, %ymm6
 vpaddw %ymm10, %ymm6, %ymm6
-vmovdqa %xmm8, 2400(%rsp)
+vmovdqa %xmm8, 2400(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_3_5(%rip), %ymm11, %ymm8
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
 vpermq $206, %ymm8, %ymm8
 vpand mask_keephigh(%rip), %ymm8, %ymm3
 vpor %ymm3, %ymm11, %ymm11
-vpaddw 2656(%rsp), %ymm9, %ymm9
+vpaddw 2656(%r8), %ymm9, %ymm9
 vpaddw %ymm11, %ymm9, %ymm9
-vmovdqa %xmm8, 2656(%rsp)
+vmovdqa %xmm8, 2656(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %ymm5, 296(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -6651,15 +6661,15 @@
 vmovdqu %ymm9, 1000(%rdi)
 vpand mask_mod8192(%rip), %ymm2, %ymm2
 vmovdqu %ymm2, 1352(%rdi)
-vmovdqa 128(%rsp), %ymm11
+vmovdqa 128(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm10
 vpunpckhwd const0(%rip), %ymm11, %ymm4
 vpslld $1, %ymm10, %ymm10
 vpslld $1, %ymm4, %ymm4
-vmovdqa 384(%rsp), %ymm2
+vmovdqa 384(%r8), %ymm2
 vpunpcklwd const0(%rip), %ymm2, %ymm9
 vpunpckhwd const0(%rip), %ymm2, %ymm2
-vmovdqa 640(%rsp), %ymm6
+vmovdqa 640(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm9, %ymm8
@@ -6673,7 +6683,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1664(%rsp), %ymm5
+vmovdqa 1664(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm2
 vpunpckhwd const0(%rip), %ymm5, %ymm9
 vpslld $1, %ymm2, %ymm2
@@ -6685,9 +6695,9 @@
 vpand mask32_to_16(%rip), %ymm8, %ymm8
 vpand mask32_to_16(%rip), %ymm3, %ymm3
 vpackusdw %ymm3, %ymm8, %ymm3
-vmovdqa 896(%rsp), %ymm8
-vpaddw 1152(%rsp), %ymm8, %ymm9
-vpsubw 1152(%rsp), %ymm8, %ymm8
+vmovdqa 896(%r8), %ymm8
+vpaddw 1152(%r8), %ymm8, %ymm9
+vpsubw 1152(%r8), %ymm8, %ymm8
 vpsrlw $2, %ymm8, %ymm8
 vpsubw %ymm6, %ymm8, %ymm8
 vpmullw %ymm14, %ymm8, %ymm8
@@ -6697,7 +6707,7 @@
 vpsubw %ymm9, %ymm2, %ymm9
 vpsrlw $3, %ymm9, %ymm9
 vpsubw %ymm3, %ymm9, %ymm9
-vmovdqa 1408(%rsp), %ymm2
+vmovdqa 1408(%r8), %ymm2
 vpsubw %ymm11, %ymm2, %ymm2
 vpmullw %ymm15, %ymm5, %ymm4
 vpsubw %ymm4, %ymm2, %ymm4
@@ -6728,53 +6738,53 @@
 vpand mask_keephigh(%rip), %ymm7, %ymm10
 vpor %ymm10, %ymm8, %ymm8
 vmovdqu 32(%rdi), %ymm10
-vpaddw 1920(%rsp), %ymm10, %ymm10
+vpaddw 1920(%r8), %ymm10, %ymm10
 vpaddw %ymm8, %ymm10, %ymm10
 vpand mask_mod8192(%rip), %ymm10, %ymm10
 vmovdqu %ymm10, 32(%rdi)
-vmovdqa %xmm7, 1920(%rsp)
+vmovdqa %xmm7, 1920(%r8)
 vpshufb shuf48_16(%rip), %ymm9, %ymm9
 vpand mask3_5_3_5(%rip), %ymm9, %ymm7
 vpand mask5_3_5_3(%rip), %ymm9, %ymm9
 vpermq $206, %ymm7, %ymm7
 vpand mask_keephigh(%rip), %ymm7, %ymm10
 vpor %ymm10, %ymm9, %ymm9
-vpaddw 2176(%rsp), %ymm11, %ymm11
+vpaddw 2176(%r8), %ymm11, %ymm11
 vpaddw %ymm9, %ymm11, %ymm11
-vmovdqa %xmm7, 2176(%rsp)
+vmovdqa %xmm7, 2176(%r8)
 vpshufb shuf48_16(%rip), %ymm2, %ymm2
 vpand mask3_5_3_5(%rip), %ymm2, %ymm7
 vpand mask5_3_5_3(%rip), %ymm2, %ymm2
 vpermq $206, %ymm7, %ymm7
 vpand mask_keephigh(%rip), %ymm7, %ymm10
 vpor %ymm10, %ymm2, %ymm2
-vpaddw 2432(%rsp), %ymm6, %ymm6
+vpaddw 2432(%r8), %ymm6, %ymm6
 vpaddw %ymm2, %ymm6, %ymm6
-vmovdqa %xmm7, 2432(%rsp)
+vmovdqa %xmm7, 2432(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_3_5(%rip), %ymm5, %ymm7
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
 vpermq $206, %ymm7, %ymm7
 vpand mask_keephigh(%rip), %ymm7, %ymm10
 vpor %ymm10, %ymm5, %ymm5
-vpaddw 2688(%rsp), %ymm3, %ymm3
+vpaddw 2688(%r8), %ymm3, %ymm3
 vpaddw %ymm5, %ymm3, %ymm3
-vmovdqa %xmm7, 2688(%rsp)
+vmovdqa %xmm7, 2688(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 384(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
 vmovdqu %ymm6, 736(%rdi)
 vpand mask_mod8192(%rip), %ymm3, %ymm3
 vmovdqu %ymm3, 1088(%rdi)
-vmovdqa 160(%rsp), %ymm5
+vmovdqa 160(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm2
 vpunpckhwd const0(%rip), %ymm5, %ymm9
 vpslld $1, %ymm2, %ymm2
 vpslld $1, %ymm9, %ymm9
-vmovdqa 416(%rsp), %ymm8
+vmovdqa 416(%r8), %ymm8
 vpunpcklwd const0(%rip), %ymm8, %ymm3
 vpunpckhwd const0(%rip), %ymm8, %ymm8
-vmovdqa 672(%rsp), %ymm6
+vmovdqa 672(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm3, %ymm7
@@ -6788,7 +6798,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1696(%rsp), %ymm11
+vmovdqa 1696(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm8
 vpunpckhwd const0(%rip), %ymm11, %ymm3
 vpslld $1, %ymm8, %ymm8
@@ -6800,9 +6810,9 @@
 vpand mask32_to_16(%rip), %ymm7, %ymm7
 vpand mask32_to_16(%rip), %ymm10, %ymm10
 vpackusdw %ymm10, %ymm7, %ymm10
-vmovdqa 928(%rsp), %ymm7
-vpaddw 1184(%rsp), %ymm7, %ymm3
-vpsubw 1184(%rsp), %ymm7, %ymm7
+vmovdqa 928(%r8), %ymm7
+vpaddw 1184(%r8), %ymm7, %ymm3
+vpsubw 1184(%r8), %ymm7, %ymm7
 vpsrlw $2, %ymm7, %ymm7
 vpsubw %ymm6, %ymm7, %ymm7
 vpmullw %ymm14, %ymm7, %ymm7
@@ -6812,7 +6822,7 @@
 vpsubw %ymm3, %ymm8, %ymm3
 vpsrlw $3, %ymm3, %ymm3
 vpsubw %ymm10, %ymm3, %ymm3
-vmovdqa 1440(%rsp), %ymm8
+vmovdqa 1440(%r8), %ymm8
 vpsubw %ymm5, %ymm8, %ymm8
 vpmullw %ymm15, %ymm11, %ymm9
 vpsubw %ymm9, %ymm8, %ymm9
@@ -6843,53 +6853,53 @@
 vpand mask_keephigh(%rip), %ymm4, %ymm2
 vpor %ymm2, %ymm7, %ymm7
 vmovdqu 120(%rdi), %ymm2
-vpaddw 1952(%rsp), %ymm2, %ymm2
+vpaddw 1952(%r8), %ymm2, %ymm2
 vpaddw %ymm7, %ymm2, %ymm2
 vpand mask_mod8192(%rip), %ymm2, %ymm2
 vmovdqu %ymm2, 120(%rdi)
-vmovdqa %xmm4, 1952(%rsp)
+vmovdqa %xmm4, 1952(%r8)
 vpshufb shuf48_16(%rip), %ymm3, %ymm3
 vpand mask3_5_3_5(%rip), %ymm3, %ymm4
 vpand mask5_3_5_3(%rip), %ymm3, %ymm3
 vpermq $206, %ymm4, %ymm4
 vpand mask_keephigh(%rip), %ymm4, %ymm2
 vpor %ymm2, %ymm3, %ymm3
-vpaddw 2208(%rsp), %ymm5, %ymm5
+vpaddw 2208(%r8), %ymm5, %ymm5
 vpaddw %ymm3, %ymm5, %ymm5
-vmovdqa %xmm4, 2208(%rsp)
+vmovdqa %xmm4, 2208(%r8)
 vpshufb shuf48_16(%rip), %ymm8, %ymm8
 vpand mask3_5_3_5(%rip), %ymm8, %ymm4
 vpand mask5_3_5_3(%rip), %ymm8, %ymm8
 vpermq $206, %ymm4, %ymm4
 vpand mask_keephigh(%rip), %ymm4, %ymm2
 vpor %ymm2, %ymm8, %ymm8
-vpaddw 2464(%rsp), %ymm6, %ymm6
+vpaddw 2464(%r8), %ymm6, %ymm6
 vpaddw %ymm8, %ymm6, %ymm6
-vmovdqa %xmm4, 2464(%rsp)
+vmovdqa %xmm4, 2464(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_3_5(%rip), %ymm11, %ymm4
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
 vpermq $206, %ymm4, %ymm4
 vpand mask_keephigh(%rip), %ymm4, %ymm2
 vpor %ymm2, %ymm11, %ymm11
-vpaddw 2720(%rsp), %ymm10, %ymm10
+vpaddw 2720(%r8), %ymm10, %ymm10
 vpaddw %ymm11, %ymm10, %ymm10
-vmovdqa %xmm4, 2720(%rsp)
+vmovdqa %xmm4, 2720(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %ymm5, 472(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
 vmovdqu %ymm6, 824(%rdi)
 vpand mask_mod8192(%rip), %ymm10, %ymm10
 vmovdqu %ymm10, 1176(%rdi)
-vmovdqa 192(%rsp), %ymm11
+vmovdqa 192(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm8
 vpunpckhwd const0(%rip), %ymm11, %ymm3
 vpslld $1, %ymm8, %ymm8
 vpslld $1, %ymm3, %ymm3
-vmovdqa 448(%rsp), %ymm7
+vmovdqa 448(%r8), %ymm7
 vpunpcklwd const0(%rip), %ymm7, %ymm10
 vpunpckhwd const0(%rip), %ymm7, %ymm7
-vmovdqa 704(%rsp), %ymm6
+vmovdqa 704(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm10, %ymm4
@@ -6903,7 +6913,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1728(%rsp), %ymm5
+vmovdqa 1728(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm7
 vpunpckhwd const0(%rip), %ymm5, %ymm10
 vpslld $1, %ymm7, %ymm7
@@ -6915,9 +6925,9 @@
 vpand mask32_to_16(%rip), %ymm4, %ymm4
 vpand mask32_to_16(%rip), %ymm2, %ymm2
 vpackusdw %ymm2, %ymm4, %ymm2
-vmovdqa 960(%rsp), %ymm4
-vpaddw 1216(%rsp), %ymm4, %ymm10
-vpsubw 1216(%rsp), %ymm4, %ymm4
+vmovdqa 960(%r8), %ymm4
+vpaddw 1216(%r8), %ymm4, %ymm10
+vpsubw 1216(%r8), %ymm4, %ymm4
 vpsrlw $2, %ymm4, %ymm4
 vpsubw %ymm6, %ymm4, %ymm4
 vpmullw %ymm14, %ymm4, %ymm4
@@ -6927,7 +6937,7 @@
 vpsubw %ymm10, %ymm7, %ymm10
 vpsrlw $3, %ymm10, %ymm10
 vpsubw %ymm2, %ymm10, %ymm10
-vmovdqa 1472(%rsp), %ymm7
+vmovdqa 1472(%r8), %ymm7
 vpsubw %ymm11, %ymm7, %ymm7
 vpmullw %ymm15, %ymm5, %ymm3
 vpsubw %ymm3, %ymm7, %ymm3
@@ -6958,53 +6968,53 @@
 vpand mask_keephigh(%rip), %ymm9, %ymm8
 vpor %ymm8, %ymm4, %ymm4
 vmovdqu 208(%rdi), %ymm8
-vpaddw 1984(%rsp), %ymm8, %ymm8
+vpaddw 1984(%r8), %ymm8, %ymm8
 vpaddw %ymm4, %ymm8, %ymm8
 vpand mask_mod8192(%rip), %ymm8, %ymm8
 vmovdqu %ymm8, 208(%rdi)
-vmovdqa %xmm9, 1984(%rsp)
+vmovdqa %xmm9, 1984(%r8)
 vpshufb shuf48_16(%rip), %ymm10, %ymm10
 vpand mask3_5_3_5(%rip), %ymm10, %ymm9
 vpand mask5_3_5_3(%rip), %ymm10, %ymm10
 vpermq $206, %ymm9, %ymm9
 vpand mask_keephigh(%rip), %ymm9, %ymm8
 vpor %ymm8, %ymm10, %ymm10
-vpaddw 2240(%rsp), %ymm11, %ymm11
+vpaddw 2240(%r8), %ymm11, %ymm11
 vpaddw %ymm10, %ymm11, %ymm11
-vmovdqa %xmm9, 2240(%rsp)
+vmovdqa %xmm9, 2240(%r8)
 vpshufb shuf48_16(%rip), %ymm7, %ymm7
 vpand mask3_5_3_5(%rip), %ymm7, %ymm9
 vpand mask5_3_5_3(%rip), %ymm7, %ymm7
 vpermq $206, %ymm9, %ymm9
 vpand mask_keephigh(%rip), %ymm9, %ymm8
 vpor %ymm8, %ymm7, %ymm7
-vpaddw 2496(%rsp), %ymm6, %ymm6
+vpaddw 2496(%r8), %ymm6, %ymm6
 vpaddw %ymm7, %ymm6, %ymm6
-vmovdqa %xmm9, 2496(%rsp)
+vmovdqa %xmm9, 2496(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_3_5(%rip), %ymm5, %ymm9
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
 vpermq $206, %ymm9, %ymm9
 vpand mask_keephigh(%rip), %ymm9, %ymm8
 vpor %ymm8, %ymm5, %ymm5
-vpaddw 2752(%rsp), %ymm2, %ymm2
+vpaddw 2752(%r8), %ymm2, %ymm2
 vpaddw %ymm5, %ymm2, %ymm2
-vmovdqa %xmm9, 2752(%rsp)
+vmovdqa %xmm9, 2752(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 560(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
 vmovdqu %ymm6, 912(%rdi)
 vpand mask_mod8192(%rip), %ymm2, %ymm2
 vmovdqu %ymm2, 1264(%rdi)
-vmovdqa 224(%rsp), %ymm5
+vmovdqa 224(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm7
 vpunpckhwd const0(%rip), %ymm5, %ymm10
 vpslld $1, %ymm7, %ymm7
 vpslld $1, %ymm10, %ymm10
-vmovdqa 480(%rsp), %ymm4
+vmovdqa 480(%r8), %ymm4
 vpunpcklwd const0(%rip), %ymm4, %ymm2
 vpunpckhwd const0(%rip), %ymm4, %ymm4
-vmovdqa 736(%rsp), %ymm6
+vmovdqa 736(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm2, %ymm9
@@ -7018,7 +7028,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1760(%rsp), %ymm11
+vmovdqa 1760(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm4
 vpunpckhwd const0(%rip), %ymm11, %ymm2
 vpslld $1, %ymm4, %ymm4
@@ -7030,9 +7040,9 @@
 vpand mask32_to_16(%rip), %ymm9, %ymm9
 vpand mask32_to_16(%rip), %ymm8, %ymm8
 vpackusdw %ymm8, %ymm9, %ymm8
-vmovdqa 992(%rsp), %ymm9
-vpaddw 1248(%rsp), %ymm9, %ymm2
-vpsubw 1248(%rsp), %ymm9, %ymm9
+vmovdqa 992(%r8), %ymm9
+vpaddw 1248(%r8), %ymm9, %ymm2
+vpsubw 1248(%r8), %ymm9, %ymm9
 vpsrlw $2, %ymm9, %ymm9
 vpsubw %ymm6, %ymm9, %ymm9
 vpmullw %ymm14, %ymm9, %ymm9
@@ -7042,7 +7052,7 @@
 vpsubw %ymm2, %ymm4, %ymm2
 vpsrlw $3, %ymm2, %ymm2
 vpsubw %ymm8, %ymm2, %ymm2
-vmovdqa 1504(%rsp), %ymm4
+vmovdqa 1504(%r8), %ymm4
 vpsubw %ymm5, %ymm4, %ymm4
 vpmullw %ymm15, %ymm11, %ymm10
 vpsubw %ymm10, %ymm4, %ymm10
@@ -7073,38 +7083,38 @@
 vpand mask_keephigh(%rip), %ymm3, %ymm7
 vpor %ymm7, %ymm9, %ymm9
 vmovdqu 296(%rdi), %ymm7
-vpaddw 2016(%rsp), %ymm7, %ymm7
+vpaddw 2016(%r8), %ymm7, %ymm7
 vpaddw %ymm9, %ymm7, %ymm7
 vpand mask_mod8192(%rip), %ymm7, %ymm7
 vmovdqu %ymm7, 296(%rdi)
-vmovdqa %xmm3, 2016(%rsp)
+vmovdqa %xmm3, 2016(%r8)
 vpshufb shuf48_16(%rip), %ymm2, %ymm2
 vpand mask3_5_3_5(%rip), %ymm2, %ymm3
 vpand mask5_3_5_3(%rip), %ymm2, %ymm2
 vpermq $206, %ymm3, %ymm3
 vpand mask_keephigh(%rip), %ymm3, %ymm7
 vpor %ymm7, %ymm2, %ymm2
-vpaddw 2272(%rsp), %ymm5, %ymm5
+vpaddw 2272(%r8), %ymm5, %ymm5
 vpaddw %ymm2, %ymm5, %ymm5
-vmovdqa %xmm3, 2272(%rsp)
+vmovdqa %xmm3, 2272(%r8)
 vpshufb shuf48_16(%rip), %ymm4, %ymm4
 vpand mask3_5_3_5(%rip), %ymm4, %ymm3
 vpand mask5_3_5_3(%rip), %ymm4, %ymm4
 vpermq $206, %ymm3, %ymm3
 vpand mask_keephigh(%rip), %ymm3, %ymm7
 vpor %ymm7, %ymm4, %ymm4
-vpaddw 2528(%rsp), %ymm6, %ymm6
+vpaddw 2528(%r8), %ymm6, %ymm6
 vpaddw %ymm4, %ymm6, %ymm6
-vmovdqa %xmm3, 2528(%rsp)
+vmovdqa %xmm3, 2528(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_3_5(%rip), %ymm11, %ymm3
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
 vpermq $206, %ymm3, %ymm3
 vpand mask_keephigh(%rip), %ymm3, %ymm7
 vpor %ymm7, %ymm11, %ymm11
-vpaddw 2784(%rsp), %ymm8, %ymm8
+vpaddw 2784(%r8), %ymm8, %ymm8
 vpaddw %ymm11, %ymm8, %ymm8
-vmovdqa %xmm3, 2784(%rsp)
+vmovdqa %xmm3, 2784(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %ymm5, 648(%rdi)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
@@ -7146,14 +7156,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 64(%r12), %ymm8
 vmovdqa 928(%r12), %ymm9
-vmovdqa %ymm8, 0(%rsp)
-vmovdqa %ymm0, 32(%rsp)
-vmovdqa %ymm1, 64(%rsp)
-vmovdqa %ymm7, 96(%rsp)
-vmovdqa %ymm5, 128(%rsp)
-vmovdqa %ymm2, 160(%rsp)
-vmovdqa %ymm3, 192(%rsp)
-vmovdqa %ymm9, 224(%rsp)
+vmovdqa %ymm8, 0(%r8)
+vmovdqa %ymm0, 32(%r8)
+vmovdqa %ymm1, 64(%r8)
+vmovdqa %ymm7, 96(%r8)
+vmovdqa %ymm5, 128(%r8)
+vmovdqa %ymm2, 160(%r8)
+vmovdqa %ymm3, 192(%r8)
+vmovdqa %ymm9, 224(%r8)
 vmovdqa 1888(%r12), %ymm0
 vpsubw 1984(%r12), %ymm0, %ymm0
 vmovdqa 2272(%r12), %ymm1
@@ -7189,14 +7199,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 1792(%r12), %ymm8
 vmovdqa 2656(%r12), %ymm9
-vmovdqa %ymm8, 256(%rsp)
-vmovdqa %ymm0, 288(%rsp)
-vmovdqa %ymm1, 320(%rsp)
-vmovdqa %ymm7, 352(%rsp)
-vmovdqa %ymm5, 384(%rsp)
-vmovdqa %ymm2, 416(%rsp)
-vmovdqa %ymm3, 448(%rsp)
-vmovdqa %ymm9, 480(%rsp)
+vmovdqa %ymm8, 256(%r8)
+vmovdqa %ymm0, 288(%r8)
+vmovdqa %ymm1, 320(%r8)
+vmovdqa %ymm7, 352(%r8)
+vmovdqa %ymm5, 384(%r8)
+vmovdqa %ymm2, 416(%r8)
+vmovdqa %ymm3, 448(%r8)
+vmovdqa %ymm9, 480(%r8)
 vmovdqa 3616(%r12), %ymm0
 vpsubw 3712(%r12), %ymm0, %ymm0
 vmovdqa 4000(%r12), %ymm1
@@ -7232,14 +7242,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 3520(%r12), %ymm8
 vmovdqa 4384(%r12), %ymm9
-vmovdqa %ymm8, 512(%rsp)
-vmovdqa %ymm0, 544(%rsp)
-vmovdqa %ymm1, 576(%rsp)
-vmovdqa %ymm7, 608(%rsp)
-vmovdqa %ymm5, 640(%rsp)
-vmovdqa %ymm2, 672(%rsp)
-vmovdqa %ymm3, 704(%rsp)
-vmovdqa %ymm9, 736(%rsp)
+vmovdqa %ymm8, 512(%r8)
+vmovdqa %ymm0, 544(%r8)
+vmovdqa %ymm1, 576(%r8)
+vmovdqa %ymm7, 608(%r8)
+vmovdqa %ymm5, 640(%r8)
+vmovdqa %ymm2, 672(%r8)
+vmovdqa %ymm3, 704(%r8)
+vmovdqa %ymm9, 736(%r8)
 vmovdqa 5344(%r12), %ymm0
 vpsubw 5440(%r12), %ymm0, %ymm0
 vmovdqa 5728(%r12), %ymm1
@@ -7275,14 +7285,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 5248(%r12), %ymm8
 vmovdqa 6112(%r12), %ymm9
-vmovdqa %ymm8, 768(%rsp)
-vmovdqa %ymm0, 800(%rsp)
-vmovdqa %ymm1, 832(%rsp)
-vmovdqa %ymm7, 864(%rsp)
-vmovdqa %ymm5, 896(%rsp)
-vmovdqa %ymm2, 928(%rsp)
-vmovdqa %ymm3, 960(%rsp)
-vmovdqa %ymm9, 992(%rsp)
+vmovdqa %ymm8, 768(%r8)
+vmovdqa %ymm0, 800(%r8)
+vmovdqa %ymm1, 832(%r8)
+vmovdqa %ymm7, 864(%r8)
+vmovdqa %ymm5, 896(%r8)
+vmovdqa %ymm2, 928(%r8)
+vmovdqa %ymm3, 960(%r8)
+vmovdqa %ymm9, 992(%r8)
 vmovdqa 7072(%r12), %ymm0
 vpsubw 7168(%r12), %ymm0, %ymm0
 vmovdqa 7456(%r12), %ymm1
@@ -7318,14 +7328,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 6976(%r12), %ymm8
 vmovdqa 7840(%r12), %ymm9
-vmovdqa %ymm8, 1024(%rsp)
-vmovdqa %ymm0, 1056(%rsp)
-vmovdqa %ymm1, 1088(%rsp)
-vmovdqa %ymm7, 1120(%rsp)
-vmovdqa %ymm5, 1152(%rsp)
-vmovdqa %ymm2, 1184(%rsp)
-vmovdqa %ymm3, 1216(%rsp)
-vmovdqa %ymm9, 1248(%rsp)
+vmovdqa %ymm8, 1024(%r8)
+vmovdqa %ymm0, 1056(%r8)
+vmovdqa %ymm1, 1088(%r8)
+vmovdqa %ymm7, 1120(%r8)
+vmovdqa %ymm5, 1152(%r8)
+vmovdqa %ymm2, 1184(%r8)
+vmovdqa %ymm3, 1216(%r8)
+vmovdqa %ymm9, 1248(%r8)
 vmovdqa 8800(%r12), %ymm0
 vpsubw 8896(%r12), %ymm0, %ymm0
 vmovdqa 9184(%r12), %ymm1
@@ -7361,14 +7371,14 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 8704(%r12), %ymm8
 vmovdqa 9568(%r12), %ymm9
-vmovdqa %ymm8, 1280(%rsp)
-vmovdqa %ymm0, 1312(%rsp)
-vmovdqa %ymm1, 1344(%rsp)
-vmovdqa %ymm7, 1376(%rsp)
-vmovdqa %ymm5, 1408(%rsp)
-vmovdqa %ymm2, 1440(%rsp)
-vmovdqa %ymm3, 1472(%rsp)
-vmovdqa %ymm9, 1504(%rsp)
+vmovdqa %ymm8, 1280(%r8)
+vmovdqa %ymm0, 1312(%r8)
+vmovdqa %ymm1, 1344(%r8)
+vmovdqa %ymm7, 1376(%r8)
+vmovdqa %ymm5, 1408(%r8)
+vmovdqa %ymm2, 1440(%r8)
+vmovdqa %ymm3, 1472(%r8)
+vmovdqa %ymm9, 1504(%r8)
 vmovdqa 10528(%r12), %ymm0
 vpsubw 10624(%r12), %ymm0, %ymm0
 vmovdqa 10912(%r12), %ymm1
@@ -7404,23 +7414,23 @@
 vpaddw %ymm4, %ymm7, %ymm7
 vmovdqa 10432(%r12), %ymm8
 vmovdqa 11296(%r12), %ymm9
-vmovdqa %ymm8, 1536(%rsp)
-vmovdqa %ymm0, 1568(%rsp)
-vmovdqa %ymm1, 1600(%rsp)
-vmovdqa %ymm7, 1632(%rsp)
-vmovdqa %ymm5, 1664(%rsp)
-vmovdqa %ymm2, 1696(%rsp)
-vmovdqa %ymm3, 1728(%rsp)
-vmovdqa %ymm9, 1760(%rsp)
-vmovdqa 0(%rsp), %ymm11
+vmovdqa %ymm8, 1536(%r8)
+vmovdqa %ymm0, 1568(%r8)
+vmovdqa %ymm1, 1600(%r8)
+vmovdqa %ymm7, 1632(%r8)
+vmovdqa %ymm5, 1664(%r8)
+vmovdqa %ymm2, 1696(%r8)
+vmovdqa %ymm3, 1728(%r8)
+vmovdqa %ymm9, 1760(%r8)
+vmovdqa 0(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm4
 vpunpckhwd const0(%rip), %ymm11, %ymm2
 vpslld $1, %ymm4, %ymm4
 vpslld $1, %ymm2, %ymm2
-vmovdqa 256(%rsp), %ymm9
+vmovdqa 256(%r8), %ymm9
 vpunpcklwd const0(%rip), %ymm9, %ymm8
 vpunpckhwd const0(%rip), %ymm9, %ymm9
-vmovdqa 512(%rsp), %ymm6
+vmovdqa 512(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm8, %ymm3
@@ -7434,7 +7444,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1536(%rsp), %ymm5
+vmovdqa 1536(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm9
 vpunpckhwd const0(%rip), %ymm5, %ymm8
 vpslld $1, %ymm9, %ymm9
@@ -7446,9 +7456,9 @@
 vpand mask32_to_16(%rip), %ymm3, %ymm3
 vpand mask32_to_16(%rip), %ymm7, %ymm7
 vpackusdw %ymm7, %ymm3, %ymm7
-vmovdqa 768(%rsp), %ymm3
-vpaddw 1024(%rsp), %ymm3, %ymm8
-vpsubw 1024(%rsp), %ymm3, %ymm3
+vmovdqa 768(%r8), %ymm3
+vpaddw 1024(%r8), %ymm3, %ymm8
+vpsubw 1024(%r8), %ymm3, %ymm3
 vpsrlw $2, %ymm3, %ymm3
 vpsubw %ymm6, %ymm3, %ymm3
 vpmullw %ymm14, %ymm3, %ymm3
@@ -7458,7 +7468,7 @@
 vpsubw %ymm8, %ymm9, %ymm8
 vpsrlw $3, %ymm8, %ymm8
 vpsubw %ymm7, %ymm8, %ymm8
-vmovdqa 1280(%rsp), %ymm9
+vmovdqa 1280(%r8), %ymm9
 vpsubw %ymm11, %ymm9, %ymm9
 vpmullw %ymm15, %ymm5, %ymm2
 vpsubw %ymm2, %ymm9, %ymm2
@@ -7482,27 +7492,27 @@
 vpermq $139, %ymm2, %ymm2
 vpand mask_keephigh(%rip), %ymm2, %ymm4
 vpor %ymm4, %ymm8, %ymm8
-vpaddw 2048(%rsp), %ymm11, %ymm11
+vpaddw 2048(%r8), %ymm11, %ymm11
 vpaddw %ymm8, %ymm11, %ymm11
-vmovdqa %xmm2, 2048(%rsp)
+vmovdqa %xmm2, 2048(%r8)
 vpshufb shuf48_16(%rip), %ymm9, %ymm9
 vpand mask3_5_4_3_1(%rip), %ymm9, %ymm2
 vpand mask5_3_5_3(%rip), %ymm9, %ymm9
 vpermq $139, %ymm2, %ymm2
 vpand mask_keephigh(%rip), %ymm2, %ymm4
 vpor %ymm4, %ymm9, %ymm9
-vpaddw 2304(%rsp), %ymm6, %ymm6
+vpaddw 2304(%r8), %ymm6, %ymm6
 vpaddw %ymm9, %ymm6, %ymm6
-vmovdqa %xmm2, 2304(%rsp)
+vmovdqa %xmm2, 2304(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_4_3_1(%rip), %ymm5, %ymm2
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
 vpermq $139, %ymm2, %ymm2
 vpand mask_keephigh(%rip), %ymm2, %ymm4
 vpor %ymm4, %ymm5, %ymm5
-vpaddw 2560(%rsp), %ymm7, %ymm7
+vpaddw 2560(%r8), %ymm7, %ymm7
 vpaddw %ymm5, %ymm7, %ymm7
-vmovdqa %xmm2, 2560(%rsp)
+vmovdqa %xmm2, 2560(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %xmm11, 64(%rdi)
 vextracti128 $1, %ymm11, %xmm11
@@ -7519,15 +7529,15 @@
 vmovdqu %xmm3, 1120(%rdi)
 vextracti128 $1, %ymm3, %xmm3
 vmovq %xmm3, 1136(%rdi)
-vmovdqa 32(%rsp), %ymm5
+vmovdqa 32(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm9
 vpunpckhwd const0(%rip), %ymm5, %ymm8
 vpslld $1, %ymm9, %ymm9
 vpslld $1, %ymm8, %ymm8
-vmovdqa 288(%rsp), %ymm3
+vmovdqa 288(%r8), %ymm3
 vpunpcklwd const0(%rip), %ymm3, %ymm7
 vpunpckhwd const0(%rip), %ymm3, %ymm3
-vmovdqa 544(%rsp), %ymm6
+vmovdqa 544(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm7, %ymm2
@@ -7541,7 +7551,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1568(%rsp), %ymm11
+vmovdqa 1568(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm3
 vpunpckhwd const0(%rip), %ymm11, %ymm7
 vpslld $1, %ymm3, %ymm3
@@ -7553,9 +7563,9 @@
 vpand mask32_to_16(%rip), %ymm2, %ymm2
 vpand mask32_to_16(%rip), %ymm4, %ymm4
 vpackusdw %ymm4, %ymm2, %ymm4
-vmovdqa 800(%rsp), %ymm2
-vpaddw 1056(%rsp), %ymm2, %ymm7
-vpsubw 1056(%rsp), %ymm2, %ymm2
+vmovdqa 800(%r8), %ymm2
+vpaddw 1056(%r8), %ymm2, %ymm7
+vpsubw 1056(%r8), %ymm2, %ymm2
 vpsrlw $2, %ymm2, %ymm2
 vpsubw %ymm6, %ymm2, %ymm2
 vpmullw %ymm14, %ymm2, %ymm2
@@ -7565,7 +7575,7 @@
 vpsubw %ymm7, %ymm3, %ymm7
 vpsrlw $3, %ymm7, %ymm7
 vpsubw %ymm4, %ymm7, %ymm7
-vmovdqa 1312(%rsp), %ymm3
+vmovdqa 1312(%r8), %ymm3
 vpsubw %ymm5, %ymm3, %ymm3
 vpmullw %ymm15, %ymm11, %ymm8
 vpsubw %ymm8, %ymm3, %ymm8
@@ -7589,27 +7599,27 @@
 vpermq $139, %ymm8, %ymm8
 vpand mask_keephigh(%rip), %ymm8, %ymm9
 vpor %ymm9, %ymm7, %ymm7
-vpaddw 2080(%rsp), %ymm5, %ymm5
+vpaddw 2080(%r8), %ymm5, %ymm5
 vpaddw %ymm7, %ymm5, %ymm5
-vmovdqa %xmm8, 2080(%rsp)
+vmovdqa %xmm8, 2080(%r8)
 vpshufb shuf48_16(%rip), %ymm3, %ymm3
 vpand mask3_5_4_3_1(%rip), %ymm3, %ymm8
 vpand mask5_3_5_3(%rip), %ymm3, %ymm3
 vpermq $139, %ymm8, %ymm8
 vpand mask_keephigh(%rip), %ymm8, %ymm9
 vpor %ymm9, %ymm3, %ymm3
-vpaddw 2336(%rsp), %ymm6, %ymm6
+vpaddw 2336(%r8), %ymm6, %ymm6
 vpaddw %ymm3, %ymm6, %ymm6
-vmovdqa %xmm8, 2336(%rsp)
+vmovdqa %xmm8, 2336(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_4_3_1(%rip), %ymm11, %ymm8
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
 vpermq $139, %ymm8, %ymm8
 vpand mask_keephigh(%rip), %ymm8, %ymm9
 vpor %ymm9, %ymm11, %ymm11
-vpaddw 2592(%rsp), %ymm4, %ymm4
+vpaddw 2592(%r8), %ymm4, %ymm4
 vpaddw %ymm11, %ymm4, %ymm4
-vmovdqa %xmm8, 2592(%rsp)
+vmovdqa %xmm8, 2592(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %xmm5, 152(%rdi)
 vextracti128 $1, %ymm5, %xmm5
@@ -7626,15 +7636,15 @@
 vmovdqu %xmm2, 1208(%rdi)
 vextracti128 $1, %ymm2, %xmm2
 vmovq %xmm2, 1224(%rdi)
-vmovdqa 64(%rsp), %ymm11
+vmovdqa 64(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm3
 vpunpckhwd const0(%rip), %ymm11, %ymm7
 vpslld $1, %ymm3, %ymm3
 vpslld $1, %ymm7, %ymm7
-vmovdqa 320(%rsp), %ymm2
+vmovdqa 320(%r8), %ymm2
 vpunpcklwd const0(%rip), %ymm2, %ymm4
 vpunpckhwd const0(%rip), %ymm2, %ymm2
-vmovdqa 576(%rsp), %ymm6
+vmovdqa 576(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm4, %ymm8
@@ -7648,7 +7658,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1600(%rsp), %ymm5
+vmovdqa 1600(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm2
 vpunpckhwd const0(%rip), %ymm5, %ymm4
 vpslld $1, %ymm2, %ymm2
@@ -7660,9 +7670,9 @@
 vpand mask32_to_16(%rip), %ymm8, %ymm8
 vpand mask32_to_16(%rip), %ymm9, %ymm9
 vpackusdw %ymm9, %ymm8, %ymm9
-vmovdqa 832(%rsp), %ymm8
-vpaddw 1088(%rsp), %ymm8, %ymm4
-vpsubw 1088(%rsp), %ymm8, %ymm8
+vmovdqa 832(%r8), %ymm8
+vpaddw 1088(%r8), %ymm8, %ymm4
+vpsubw 1088(%r8), %ymm8, %ymm8
 vpsrlw $2, %ymm8, %ymm8
 vpsubw %ymm6, %ymm8, %ymm8
 vpmullw %ymm14, %ymm8, %ymm8
@@ -7672,7 +7682,7 @@
 vpsubw %ymm4, %ymm2, %ymm4
 vpsrlw $3, %ymm4, %ymm4
 vpsubw %ymm9, %ymm4, %ymm4
-vmovdqa 1344(%rsp), %ymm2
+vmovdqa 1344(%r8), %ymm2
 vpsubw %ymm11, %ymm2, %ymm2
 vpmullw %ymm15, %ymm5, %ymm7
 vpsubw %ymm7, %ymm2, %ymm7
@@ -7696,27 +7706,27 @@
 vpermq $139, %ymm7, %ymm7
 vpand mask_keephigh(%rip), %ymm7, %ymm3
 vpor %ymm3, %ymm4, %ymm4
-vpaddw 2112(%rsp), %ymm11, %ymm11
+vpaddw 2112(%r8), %ymm11, %ymm11
 vpaddw %ymm4, %ymm11, %ymm11
-vmovdqa %xmm7, 2112(%rsp)
+vmovdqa %xmm7, 2112(%r8)
 vpshufb shuf48_16(%rip), %ymm2, %ymm2
 vpand mask3_5_4_3_1(%rip), %ymm2, %ymm7
 vpand mask5_3_5_3(%rip), %ymm2, %ymm2
 vpermq $139, %ymm7, %ymm7
 vpand mask_keephigh(%rip), %ymm7, %ymm3
 vpor %ymm3, %ymm2, %ymm2
-vpaddw 2368(%rsp), %ymm6, %ymm6
+vpaddw 2368(%r8), %ymm6, %ymm6
 vpaddw %ymm2, %ymm6, %ymm6
-vmovdqa %xmm7, 2368(%rsp)
+vmovdqa %xmm7, 2368(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_4_3_1(%rip), %ymm5, %ymm7
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
 vpermq $139, %ymm7, %ymm7
 vpand mask_keephigh(%rip), %ymm7, %ymm3
 vpor %ymm3, %ymm5, %ymm5
-vpaddw 2624(%rsp), %ymm9, %ymm9
+vpaddw 2624(%r8), %ymm9, %ymm9
 vpaddw %ymm5, %ymm9, %ymm9
-vmovdqa %xmm7, 2624(%rsp)
+vmovdqa %xmm7, 2624(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %xmm11, 240(%rdi)
 vextracti128 $1, %ymm11, %xmm11
@@ -7733,15 +7743,15 @@
 vmovdqu %xmm8, 1296(%rdi)
 vextracti128 $1, %ymm8, %xmm8
 vmovq %xmm8, 1312(%rdi)
-vmovdqa 96(%rsp), %ymm5
+vmovdqa 96(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm2
 vpunpckhwd const0(%rip), %ymm5, %ymm4
 vpslld $1, %ymm2, %ymm2
 vpslld $1, %ymm4, %ymm4
-vmovdqa 352(%rsp), %ymm8
+vmovdqa 352(%r8), %ymm8
 vpunpcklwd const0(%rip), %ymm8, %ymm9
 vpunpckhwd const0(%rip), %ymm8, %ymm8
-vmovdqa 608(%rsp), %ymm6
+vmovdqa 608(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm9, %ymm7
@@ -7755,7 +7765,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1632(%rsp), %ymm11
+vmovdqa 1632(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm8
 vpunpckhwd const0(%rip), %ymm11, %ymm9
 vpslld $1, %ymm8, %ymm8
@@ -7767,9 +7777,9 @@
 vpand mask32_to_16(%rip), %ymm7, %ymm7
 vpand mask32_to_16(%rip), %ymm3, %ymm3
 vpackusdw %ymm3, %ymm7, %ymm3
-vmovdqa 864(%rsp), %ymm7
-vpaddw 1120(%rsp), %ymm7, %ymm9
-vpsubw 1120(%rsp), %ymm7, %ymm7
+vmovdqa 864(%r8), %ymm7
+vpaddw 1120(%r8), %ymm7, %ymm9
+vpsubw 1120(%r8), %ymm7, %ymm7
 vpsrlw $2, %ymm7, %ymm7
 vpsubw %ymm6, %ymm7, %ymm7
 vpmullw %ymm14, %ymm7, %ymm7
@@ -7779,7 +7789,7 @@
 vpsubw %ymm9, %ymm8, %ymm9
 vpsrlw $3, %ymm9, %ymm9
 vpsubw %ymm3, %ymm9, %ymm9
-vmovdqa 1376(%rsp), %ymm8
+vmovdqa 1376(%r8), %ymm8
 vpsubw %ymm5, %ymm8, %ymm8
 vpmullw %ymm15, %ymm11, %ymm4
 vpsubw %ymm4, %ymm8, %ymm4
@@ -7803,60 +7813,60 @@
 vpermq $139, %ymm4, %ymm4
 vpand mask_keephigh(%rip), %ymm4, %ymm2
 vpor %ymm2, %ymm9, %ymm9
-vpaddw 2144(%rsp), %ymm5, %ymm5
+vpaddw 2144(%r8), %ymm5, %ymm5
 vpaddw %ymm9, %ymm5, %ymm5
-vmovdqa %xmm4, 2144(%rsp)
+vmovdqa %xmm4, 2144(%r8)
 vpshufb shuf48_16(%rip), %ymm8, %ymm8
 vpand mask3_5_4_3_1(%rip), %ymm8, %ymm4
 vpand mask5_3_5_3(%rip), %ymm8, %ymm8
 vpermq $139, %ymm4, %ymm4
 vpand mask_keephigh(%rip), %ymm4, %ymm2
 vpor %ymm2, %ymm8, %ymm8
-vpaddw 2400(%rsp), %ymm6, %ymm6
+vpaddw 2400(%r8), %ymm6, %ymm6
 vpaddw %ymm8, %ymm6, %ymm6
-vmovdqa %xmm4, 2400(%rsp)
+vmovdqa %xmm4, 2400(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_4_3_1(%rip), %ymm11, %ymm4
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
 vpermq $139, %ymm4, %ymm4
 vpand mask_keephigh(%rip), %ymm4, %ymm2
 vpor %ymm2, %ymm11, %ymm11
-vpaddw 2656(%rsp), %ymm3, %ymm3
+vpaddw 2656(%r8), %ymm3, %ymm3
 vpaddw %ymm11, %ymm3, %ymm3
-vmovdqa %xmm4, 2656(%rsp)
+vmovdqa %xmm4, 2656(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %xmm5, 328(%rdi)
 vextracti128 $1, %ymm5, %xmm5
 vmovq %xmm5, 344(%rdi)
 vpshufb shufmin1_mask3(%rip), %ymm5, %ymm5
-vmovdqa %xmm5, 1792(%rsp)
+vmovdqa %xmm5, 1792(%r8)
 vpand mask_mod8192(%rip), %ymm6, %ymm6
 vmovdqu %xmm6, 680(%rdi)
 vextracti128 $1, %ymm6, %xmm6
 vmovq %xmm6, 696(%rdi)
 vpshufb shufmin1_mask3(%rip), %ymm6, %ymm6
-vmovdqa %xmm6, 1824(%rsp)
+vmovdqa %xmm6, 1824(%r8)
 vpand mask_mod8192(%rip), %ymm3, %ymm3
 vmovdqu %xmm3, 1032(%rdi)
 vextracti128 $1, %ymm3, %xmm3
 vmovq %xmm3, 1048(%rdi)
 vpshufb shufmin1_mask3(%rip), %ymm3, %ymm3
-vmovdqa %xmm3, 1856(%rsp)
+vmovdqa %xmm3, 1856(%r8)
 vpand mask_mod8192(%rip), %ymm7, %ymm7
 vmovdqu %xmm7, 1384(%rdi)
 vextracti128 $1, %ymm7, %xmm7
 vpextrw $0, %xmm7, 1400(%rdi)
 vpshufb shufmin1_mask3(%rip), %ymm7, %ymm7
-vmovdqa %xmm7, 1888(%rsp)
-vmovdqa 128(%rsp), %ymm11
+vmovdqa %xmm7, 1888(%r8)
+vmovdqa 128(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm8
 vpunpckhwd const0(%rip), %ymm11, %ymm9
 vpslld $1, %ymm8, %ymm8
 vpslld $1, %ymm9, %ymm9
-vmovdqa 384(%rsp), %ymm7
+vmovdqa 384(%r8), %ymm7
 vpunpcklwd const0(%rip), %ymm7, %ymm3
 vpunpckhwd const0(%rip), %ymm7, %ymm7
-vmovdqa 640(%rsp), %ymm6
+vmovdqa 640(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm3, %ymm4
@@ -7870,7 +7880,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1664(%rsp), %ymm5
+vmovdqa 1664(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm7
 vpunpckhwd const0(%rip), %ymm5, %ymm3
 vpslld $1, %ymm7, %ymm7
@@ -7882,9 +7892,9 @@
 vpand mask32_to_16(%rip), %ymm4, %ymm4
 vpand mask32_to_16(%rip), %ymm2, %ymm2
 vpackusdw %ymm2, %ymm4, %ymm2
-vmovdqa 896(%rsp), %ymm4
-vpaddw 1152(%rsp), %ymm4, %ymm3
-vpsubw 1152(%rsp), %ymm4, %ymm4
+vmovdqa 896(%r8), %ymm4
+vpaddw 1152(%r8), %ymm4, %ymm3
+vpsubw 1152(%r8), %ymm4, %ymm4
 vpsrlw $2, %ymm4, %ymm4
 vpsubw %ymm6, %ymm4, %ymm4
 vpmullw %ymm14, %ymm4, %ymm4
@@ -7894,7 +7904,7 @@
 vpsubw %ymm3, %ymm7, %ymm3
 vpsrlw $3, %ymm3, %ymm3
 vpsubw %ymm2, %ymm3, %ymm3
-vmovdqa 1408(%rsp), %ymm7
+vmovdqa 1408(%r8), %ymm7
 vpsubw %ymm11, %ymm7, %ymm7
 vpmullw %ymm15, %ymm5, %ymm9
 vpsubw %ymm9, %ymm7, %ymm9
@@ -7925,40 +7935,40 @@
 vpand mask_keephigh(%rip), %ymm10, %ymm8
 vpor %ymm8, %ymm4, %ymm4
 vmovdqu 64(%rdi), %ymm8
-vpaddw 1920(%rsp), %ymm8, %ymm8
+vpaddw 1920(%r8), %ymm8, %ymm8
 vpaddw %ymm4, %ymm8, %ymm8
 vpand mask_mod8192(%rip), %ymm8, %ymm8
 vmovdqu %xmm8, 64(%rdi)
 vextracti128 $1, %ymm8, %xmm8
 vmovq %xmm8, 80(%rdi)
-vmovdqa %xmm10, 1920(%rsp)
+vmovdqa %xmm10, 1920(%r8)
 vpshufb shuf48_16(%rip), %ymm3, %ymm3
 vpand mask3_5_4_3_1(%rip), %ymm3, %ymm10
 vpand mask5_3_5_3(%rip), %ymm3, %ymm3
 vpermq $139, %ymm10, %ymm10
 vpand mask_keephigh(%rip), %ymm10, %ymm8
 vpor %ymm8, %ymm3, %ymm3
-vpaddw 2176(%rsp), %ymm11, %ymm11
+vpaddw 2176(%r8), %ymm11, %ymm11
 vpaddw %ymm3, %ymm11, %ymm11
-vmovdqa %xmm10, 2176(%rsp)
+vmovdqa %xmm10, 2176(%r8)
 vpshufb shuf48_16(%rip), %ymm7, %ymm7
 vpand mask3_5_4_3_1(%rip), %ymm7, %ymm10
 vpand mask5_3_5_3(%rip), %ymm7, %ymm7
 vpermq $139, %ymm10, %ymm10
 vpand mask_keephigh(%rip), %ymm10, %ymm8
 vpor %ymm8, %ymm7, %ymm7
-vpaddw 2432(%rsp), %ymm6, %ymm6
+vpaddw 2432(%r8), %ymm6, %ymm6
 vpaddw %ymm7, %ymm6, %ymm6
-vmovdqa %xmm10, 2432(%rsp)
+vmovdqa %xmm10, 2432(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_4_3_1(%rip), %ymm5, %ymm10
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
 vpermq $139, %ymm10, %ymm10
 vpand mask_keephigh(%rip), %ymm10, %ymm8
 vpor %ymm8, %ymm5, %ymm5
-vpaddw 2688(%rsp), %ymm2, %ymm2
+vpaddw 2688(%r8), %ymm2, %ymm2
 vpaddw %ymm5, %ymm2, %ymm2
-vmovdqa %xmm10, 2688(%rsp)
+vmovdqa %xmm10, 2688(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %xmm11, 416(%rdi)
 vextracti128 $1, %ymm11, %xmm11
@@ -7971,15 +7981,15 @@
 vmovdqu %xmm2, 1120(%rdi)
 vextracti128 $1, %ymm2, %xmm2
 vmovq %xmm2, 1136(%rdi)
-vmovdqa 160(%rsp), %ymm5
+vmovdqa 160(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm7
 vpunpckhwd const0(%rip), %ymm5, %ymm3
 vpslld $1, %ymm7, %ymm7
 vpslld $1, %ymm3, %ymm3
-vmovdqa 416(%rsp), %ymm4
+vmovdqa 416(%r8), %ymm4
 vpunpcklwd const0(%rip), %ymm4, %ymm2
 vpunpckhwd const0(%rip), %ymm4, %ymm4
-vmovdqa 672(%rsp), %ymm6
+vmovdqa 672(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm2, %ymm10
@@ -7993,7 +8003,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1696(%rsp), %ymm11
+vmovdqa 1696(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm4
 vpunpckhwd const0(%rip), %ymm11, %ymm2
 vpslld $1, %ymm4, %ymm4
@@ -8005,9 +8015,9 @@
 vpand mask32_to_16(%rip), %ymm10, %ymm10
 vpand mask32_to_16(%rip), %ymm8, %ymm8
 vpackusdw %ymm8, %ymm10, %ymm8
-vmovdqa 928(%rsp), %ymm10
-vpaddw 1184(%rsp), %ymm10, %ymm2
-vpsubw 1184(%rsp), %ymm10, %ymm10
+vmovdqa 928(%r8), %ymm10
+vpaddw 1184(%r8), %ymm10, %ymm2
+vpsubw 1184(%r8), %ymm10, %ymm10
 vpsrlw $2, %ymm10, %ymm10
 vpsubw %ymm6, %ymm10, %ymm10
 vpmullw %ymm14, %ymm10, %ymm10
@@ -8017,7 +8027,7 @@
 vpsubw %ymm2, %ymm4, %ymm2
 vpsrlw $3, %ymm2, %ymm2
 vpsubw %ymm8, %ymm2, %ymm2
-vmovdqa 1440(%rsp), %ymm4
+vmovdqa 1440(%r8), %ymm4
 vpsubw %ymm5, %ymm4, %ymm4
 vpmullw %ymm15, %ymm11, %ymm3
 vpsubw %ymm3, %ymm4, %ymm3
@@ -8048,40 +8058,40 @@
 vpand mask_keephigh(%rip), %ymm9, %ymm7
 vpor %ymm7, %ymm10, %ymm10
 vmovdqu 152(%rdi), %ymm7
-vpaddw 1952(%rsp), %ymm7, %ymm7
+vpaddw 1952(%r8), %ymm7, %ymm7
 vpaddw %ymm10, %ymm7, %ymm7
 vpand mask_mod8192(%rip), %ymm7, %ymm7
 vmovdqu %xmm7, 152(%rdi)
 vextracti128 $1, %ymm7, %xmm7
 vmovq %xmm7, 168(%rdi)
-vmovdqa %xmm9, 1952(%rsp)
+vmovdqa %xmm9, 1952(%r8)
 vpshufb shuf48_16(%rip), %ymm2, %ymm2
 vpand mask3_5_4_3_1(%rip), %ymm2, %ymm9
 vpand mask5_3_5_3(%rip), %ymm2, %ymm2
 vpermq $139, %ymm9, %ymm9
 vpand mask_keephigh(%rip), %ymm9, %ymm7
 vpor %ymm7, %ymm2, %ymm2
-vpaddw 2208(%rsp), %ymm5, %ymm5
+vpaddw 2208(%r8), %ymm5, %ymm5
 vpaddw %ymm2, %ymm5, %ymm5
-vmovdqa %xmm9, 2208(%rsp)
+vmovdqa %xmm9, 2208(%r8)
 vpshufb shuf48_16(%rip), %ymm4, %ymm4
 vpand mask3_5_4_3_1(%rip), %ymm4, %ymm9
 vpand mask5_3_5_3(%rip), %ymm4, %ymm4
 vpermq $139, %ymm9, %ymm9
 vpand mask_keephigh(%rip), %ymm9, %ymm7
 vpor %ymm7, %ymm4, %ymm4
-vpaddw 2464(%rsp), %ymm6, %ymm6
+vpaddw 2464(%r8), %ymm6, %ymm6
 vpaddw %ymm4, %ymm6, %ymm6
-vmovdqa %xmm9, 2464(%rsp)
+vmovdqa %xmm9, 2464(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_4_3_1(%rip), %ymm11, %ymm9
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
 vpermq $139, %ymm9, %ymm9
 vpand mask_keephigh(%rip), %ymm9, %ymm7
 vpor %ymm7, %ymm11, %ymm11
-vpaddw 2720(%rsp), %ymm8, %ymm8
+vpaddw 2720(%r8), %ymm8, %ymm8
 vpaddw %ymm11, %ymm8, %ymm8
-vmovdqa %xmm9, 2720(%rsp)
+vmovdqa %xmm9, 2720(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %xmm5, 504(%rdi)
 vextracti128 $1, %ymm5, %xmm5
@@ -8094,15 +8104,15 @@
 vmovdqu %xmm8, 1208(%rdi)
 vextracti128 $1, %ymm8, %xmm8
 vmovq %xmm8, 1224(%rdi)
-vmovdqa 192(%rsp), %ymm11
+vmovdqa 192(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm4
 vpunpckhwd const0(%rip), %ymm11, %ymm2
 vpslld $1, %ymm4, %ymm4
 vpslld $1, %ymm2, %ymm2
-vmovdqa 448(%rsp), %ymm10
+vmovdqa 448(%r8), %ymm10
 vpunpcklwd const0(%rip), %ymm10, %ymm8
 vpunpckhwd const0(%rip), %ymm10, %ymm10
-vmovdqa 704(%rsp), %ymm6
+vmovdqa 704(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm5
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm5, %ymm8, %ymm9
@@ -8116,7 +8126,7 @@
 vpand mask32_to_16(%rip), %ymm5, %ymm5
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm5, %ymm6
-vmovdqa 1728(%rsp), %ymm5
+vmovdqa 1728(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm10
 vpunpckhwd const0(%rip), %ymm5, %ymm8
 vpslld $1, %ymm10, %ymm10
@@ -8128,9 +8138,9 @@
 vpand mask32_to_16(%rip), %ymm9, %ymm9
 vpand mask32_to_16(%rip), %ymm7, %ymm7
 vpackusdw %ymm7, %ymm9, %ymm7
-vmovdqa 960(%rsp), %ymm9
-vpaddw 1216(%rsp), %ymm9, %ymm8
-vpsubw 1216(%rsp), %ymm9, %ymm9
+vmovdqa 960(%r8), %ymm9
+vpaddw 1216(%r8), %ymm9, %ymm8
+vpsubw 1216(%r8), %ymm9, %ymm9
 vpsrlw $2, %ymm9, %ymm9
 vpsubw %ymm6, %ymm9, %ymm9
 vpmullw %ymm14, %ymm9, %ymm9
@@ -8140,7 +8150,7 @@
 vpsubw %ymm8, %ymm10, %ymm8
 vpsrlw $3, %ymm8, %ymm8
 vpsubw %ymm7, %ymm8, %ymm8
-vmovdqa 1472(%rsp), %ymm10
+vmovdqa 1472(%r8), %ymm10
 vpsubw %ymm11, %ymm10, %ymm10
 vpmullw %ymm15, %ymm5, %ymm2
 vpsubw %ymm2, %ymm10, %ymm2
@@ -8171,40 +8181,40 @@
 vpand mask_keephigh(%rip), %ymm3, %ymm4
 vpor %ymm4, %ymm9, %ymm9
 vmovdqu 240(%rdi), %ymm4
-vpaddw 1984(%rsp), %ymm4, %ymm4
+vpaddw 1984(%r8), %ymm4, %ymm4
 vpaddw %ymm9, %ymm4, %ymm4
 vpand mask_mod8192(%rip), %ymm4, %ymm4
 vmovdqu %xmm4, 240(%rdi)
 vextracti128 $1, %ymm4, %xmm4
 vmovq %xmm4, 256(%rdi)
-vmovdqa %xmm3, 1984(%rsp)
+vmovdqa %xmm3, 1984(%r8)
 vpshufb shuf48_16(%rip), %ymm8, %ymm8
 vpand mask3_5_4_3_1(%rip), %ymm8, %ymm3
 vpand mask5_3_5_3(%rip), %ymm8, %ymm8
 vpermq $139, %ymm3, %ymm3
 vpand mask_keephigh(%rip), %ymm3, %ymm4
 vpor %ymm4, %ymm8, %ymm8
-vpaddw 2240(%rsp), %ymm11, %ymm11
+vpaddw 2240(%r8), %ymm11, %ymm11
 vpaddw %ymm8, %ymm11, %ymm11
-vmovdqa %xmm3, 2240(%rsp)
+vmovdqa %xmm3, 2240(%r8)
 vpshufb shuf48_16(%rip), %ymm10, %ymm10
 vpand mask3_5_4_3_1(%rip), %ymm10, %ymm3
 vpand mask5_3_5_3(%rip), %ymm10, %ymm10
 vpermq $139, %ymm3, %ymm3
 vpand mask_keephigh(%rip), %ymm3, %ymm4
 vpor %ymm4, %ymm10, %ymm10
-vpaddw 2496(%rsp), %ymm6, %ymm6
+vpaddw 2496(%r8), %ymm6, %ymm6
 vpaddw %ymm10, %ymm6, %ymm6
-vmovdqa %xmm3, 2496(%rsp)
+vmovdqa %xmm3, 2496(%r8)
 vpshufb shuf48_16(%rip), %ymm5, %ymm5
 vpand mask3_5_4_3_1(%rip), %ymm5, %ymm3
 vpand mask5_3_5_3(%rip), %ymm5, %ymm5
 vpermq $139, %ymm3, %ymm3
 vpand mask_keephigh(%rip), %ymm3, %ymm4
 vpor %ymm4, %ymm5, %ymm5
-vpaddw 2752(%rsp), %ymm7, %ymm7
+vpaddw 2752(%r8), %ymm7, %ymm7
 vpaddw %ymm5, %ymm7, %ymm7
-vmovdqa %xmm3, 2752(%rsp)
+vmovdqa %xmm3, 2752(%r8)
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %xmm11, 592(%rdi)
 vextracti128 $1, %ymm11, %xmm11
@@ -8217,15 +8227,15 @@
 vmovdqu %xmm7, 1296(%rdi)
 vextracti128 $1, %ymm7, %xmm7
 vmovq %xmm7, 1312(%rdi)
-vmovdqa 224(%rsp), %ymm5
+vmovdqa 224(%r8), %ymm5
 vpunpcklwd const0(%rip), %ymm5, %ymm10
 vpunpckhwd const0(%rip), %ymm5, %ymm8
 vpslld $1, %ymm10, %ymm10
 vpslld $1, %ymm8, %ymm8
-vmovdqa 480(%rsp), %ymm9
+vmovdqa 480(%r8), %ymm9
 vpunpcklwd const0(%rip), %ymm9, %ymm7
 vpunpckhwd const0(%rip), %ymm9, %ymm9
-vmovdqa 736(%rsp), %ymm6
+vmovdqa 736(%r8), %ymm6
 vpunpcklwd const0(%rip), %ymm6, %ymm11
 vpunpckhwd const0(%rip), %ymm6, %ymm6
 vpaddd %ymm11, %ymm7, %ymm3
@@ -8239,7 +8249,7 @@
 vpand mask32_to_16(%rip), %ymm11, %ymm11
 vpand mask32_to_16(%rip), %ymm6, %ymm6
 vpackusdw %ymm6, %ymm11, %ymm6
-vmovdqa 1760(%rsp), %ymm11
+vmovdqa 1760(%r8), %ymm11
 vpunpcklwd const0(%rip), %ymm11, %ymm9
 vpunpckhwd const0(%rip), %ymm11, %ymm7
 vpslld $1, %ymm9, %ymm9
@@ -8251,9 +8261,9 @@
 vpand mask32_to_16(%rip), %ymm3, %ymm3
 vpand mask32_to_16(%rip), %ymm4, %ymm4
 vpackusdw %ymm4, %ymm3, %ymm4
-vmovdqa 992(%rsp), %ymm3
-vpaddw 1248(%rsp), %ymm3, %ymm7
-vpsubw 1248(%rsp), %ymm3, %ymm3
+vmovdqa 992(%r8), %ymm3
+vpaddw 1248(%r8), %ymm3, %ymm7
+vpsubw 1248(%r8), %ymm3, %ymm3
 vpsrlw $2, %ymm3, %ymm3
 vpsubw %ymm6, %ymm3, %ymm3
 vpmullw %ymm14, %ymm3, %ymm3
@@ -8263,7 +8273,7 @@
 vpsubw %ymm7, %ymm9, %ymm7
 vpsrlw $3, %ymm7, %ymm7
 vpsubw %ymm4, %ymm7, %ymm7
-vmovdqa 1504(%rsp), %ymm9
+vmovdqa 1504(%r8), %ymm9
 vpsubw %ymm5, %ymm9, %ymm9
 vpmullw %ymm15, %ymm11, %ymm8
 vpsubw %ymm8, %ymm9, %ymm8
@@ -8283,16 +8293,29 @@
 vpsubw %ymm9, %ymm6, %ymm6
 vextracti128 $1, %ymm4, %xmm8
 vpshufb shufmin1_mask3(%rip), %ymm8, %ymm8
-vmovdqa %ymm8, 2816(%rsp)
+vmovdqa %ymm8, 2816(%r8)
 vextracti128 $1, %ymm3, %xmm8
 vpshufb shufmin1_mask3(%rip), %ymm8, %ymm8
-vmovdqa %ymm8, 2848(%rsp)
+vmovdqa %ymm8, 2848(%r8)
 vextracti128 $1, %ymm7, %xmm8
 vpshufb shufmin1_mask3(%rip), %ymm8, %ymm8
-vmovdqa %ymm8, 2880(%rsp)
+vmovdqa %ymm8, 2880(%r8)
 vmovdqu 680(%rdi), %ymm8
 vmovdqu 1032(%rdi), %ymm10
-vmovdqu 1384(%rdi), %ymm2
+
+# Only 18 bytes can be read at 1384, but vmovdqu reads 32.
+# Copy 18 bytes to the red zone and zero pad to 32 bytes.
+xor %r9, %r9
+movq %r9, -16(%rsp)
+movq %r9, -8(%rsp)
+movq 1384(%rdi), %r9
+movq %r9, -32(%rsp)
+movq 1384+8(%rdi), %r9
+movq %r9, -24(%rsp)
+movw 1384+16(%rdi), %r9w
+movw %r9w, -16(%rsp)
+vmovdqu -32(%rsp), %ymm2
+
 vpaddw %ymm5, %ymm8, %ymm5
 vpaddw %ymm6, %ymm10, %ymm6
 vpaddw %ymm4, %ymm2, %ymm4
@@ -8303,42 +8326,42 @@
 vpand mask_keephigh(%rip), %ymm2, %ymm10
 vpor %ymm10, %ymm3, %ymm3
 vmovdqu 328(%rdi), %ymm10
-vpaddw 2016(%rsp), %ymm10, %ymm10
+vpaddw 2016(%r8), %ymm10, %ymm10
 vpaddw %ymm3, %ymm10, %ymm10
 vpand mask_mod8192(%rip), %ymm10, %ymm10
 vmovdqu %xmm10, 328(%rdi)
 vextracti128 $1, %ymm10, %xmm10
 vmovq %xmm10, 344(%rdi)
 vpshufb shufmin1_mask3(%rip), %ymm10, %ymm10
-vmovdqa %xmm10, 1792(%rsp)
-vmovdqa %xmm2, 2016(%rsp)
+vmovdqa %xmm10, 1792(%r8)
+vmovdqa %xmm2, 2016(%r8)
 vpshufb shuf48_16(%rip), %ymm7, %ymm7
 vpand mask3_5_4_3_1(%rip), %ymm7, %ymm2
 vpand mask5_3_5_3(%rip), %ymm7, %ymm7
 vpermq $139, %ymm2, %ymm2
 vpand mask_keephigh(%rip), %ymm2, %ymm10
 vpor %ymm10, %ymm7, %ymm7
-vpaddw 2272(%rsp), %ymm5, %ymm5
+vpaddw 2272(%r8), %ymm5, %ymm5
 vpaddw %ymm7, %ymm5, %ymm5
-vmovdqa %xmm2, 2272(%rsp)
+vmovdqa %xmm2, 2272(%r8)
 vpshufb shuf48_16(%rip), %ymm9, %ymm9
 vpand mask3_5_4_3_1(%rip), %ymm9, %ymm2
 vpand mask5_3_5_3(%rip), %ymm9, %ymm9
 vpermq $139, %ymm2, %ymm2
 vpand mask_keephigh(%rip), %ymm2, %ymm10
 vpor %ymm10, %ymm9, %ymm9
-vpaddw 2528(%rsp), %ymm6, %ymm6
+vpaddw 2528(%r8), %ymm6, %ymm6
 vpaddw %ymm9, %ymm6, %ymm6
-vmovdqa %xmm2, 2528(%rsp)
+vmovdqa %xmm2, 2528(%r8)
 vpshufb shuf48_16(%rip), %ymm11, %ymm11
 vpand mask3_5_4_3_1(%rip), %ymm11, %ymm2
 vpand mask5_3_5_3(%rip), %ymm11, %ymm11
 vpermq $139, %ymm2, %ymm2
 vpand mask_keephigh(%rip), %ymm2, %ymm10
 vpor %ymm10, %ymm11, %ymm11
-vpaddw 2784(%rsp), %ymm4, %ymm4
+vpaddw 2784(%r8), %ymm4, %ymm4
 vpaddw %ymm11, %ymm4, %ymm4
-vmovdqa %xmm2, 2784(%rsp)
+vmovdqa %xmm2, 2784(%r8)
 vpand mask_mod8192(%rip), %ymm5, %ymm5
 vmovdqu %xmm5, 680(%rdi)
 vextracti128 $1, %ymm5, %xmm5
@@ -8352,108 +8375,107 @@
 vextracti128 $1, %ymm4, %xmm4
 vpextrw $0, %xmm4, 1400(%rdi)
 vmovdqu 0(%rdi), %ymm11
-vpaddw 1888(%rsp), %ymm11, %ymm11
-vpaddw 2816(%rsp), %ymm11, %ymm11
+vpaddw 1888(%r8), %ymm11, %ymm11
+vpaddw 2816(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 0(%rdi)
 vmovdqu 352(%rdi), %ymm11
-vpaddw 2528(%rsp), %ymm11, %ymm11
-vpaddw 2848(%rsp), %ymm11, %ymm11
+vpaddw 2528(%r8), %ymm11, %ymm11
+vpaddw 2848(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 352(%rdi)
 vmovdqu 704(%rdi), %ymm11
-vpaddw 2784(%rsp), %ymm11, %ymm11
-vpaddw 2880(%rsp), %ymm11, %ymm11
+vpaddw 2784(%r8), %ymm11, %ymm11
+vpaddw 2880(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 704(%rdi)
 vmovdqu 88(%rdi), %ymm11
-vpaddw 2048(%rsp), %ymm11, %ymm11
-vpaddw 1920(%rsp), %ymm11, %ymm11
+vpaddw 2048(%r8), %ymm11, %ymm11
+vpaddw 1920(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 88(%rdi)
 vmovdqu 440(%rdi), %ymm11
-vpaddw 2304(%rsp), %ymm11, %ymm11
+vpaddw 2304(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 440(%rdi)
 vmovdqu 792(%rdi), %ymm11
-vpaddw 2560(%rsp), %ymm11, %ymm11
+vpaddw 2560(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 792(%rdi)
 vmovdqu 176(%rdi), %ymm11
-vpaddw 2080(%rsp), %ymm11, %ymm11
-vpaddw 1952(%rsp), %ymm11, %ymm11
+vpaddw 2080(%r8), %ymm11, %ymm11
+vpaddw 1952(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 176(%rdi)
 vmovdqu 528(%rdi), %ymm11
-vpaddw 2336(%rsp), %ymm11, %ymm11
+vpaddw 2336(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 528(%rdi)
 vmovdqu 880(%rdi), %ymm11
-vpaddw 2592(%rsp), %ymm11, %ymm11
+vpaddw 2592(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 880(%rdi)
 vmovdqu 264(%rdi), %ymm11
-vpaddw 2112(%rsp), %ymm11, %ymm11
-vpaddw 1984(%rsp), %ymm11, %ymm11
+vpaddw 2112(%r8), %ymm11, %ymm11
+vpaddw 1984(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 264(%rdi)
 vmovdqu 616(%rdi), %ymm11
-vpaddw 2368(%rsp), %ymm11, %ymm11
+vpaddw 2368(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 616(%rdi)
 vmovdqu 968(%rdi), %ymm11
-vpaddw 2624(%rsp), %ymm11, %ymm11
+vpaddw 2624(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 968(%rdi)
 vmovdqu 352(%rdi), %ymm11
-vpaddw 2144(%rsp), %ymm11, %ymm11
+vpaddw 2144(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 352(%rdi)
 vmovdqu 704(%rdi), %ymm11
-vpaddw 2400(%rsp), %ymm11, %ymm11
+vpaddw 2400(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 704(%rdi)
 vmovdqu 1056(%rdi), %ymm11
-vpaddw 2656(%rsp), %ymm11, %ymm11
+vpaddw 2656(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 1056(%rdi)
 vmovdqu 440(%rdi), %ymm11
-vpaddw 2176(%rsp), %ymm11, %ymm11
+vpaddw 2176(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 440(%rdi)
 vmovdqu 792(%rdi), %ymm11
-vpaddw 2432(%rsp), %ymm11, %ymm11
+vpaddw 2432(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 792(%rdi)
 vmovdqu 1144(%rdi), %ymm11
-vpaddw 2688(%rsp), %ymm11, %ymm11
+vpaddw 2688(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 1144(%rdi)
 vmovdqu 528(%rdi), %ymm11
-vpaddw 2208(%rsp), %ymm11, %ymm11
+vpaddw 2208(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 528(%rdi)
 vmovdqu 880(%rdi), %ymm11
-vpaddw 2464(%rsp), %ymm11, %ymm11
+vpaddw 2464(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 880(%rdi)
 vmovdqu 1232(%rdi), %ymm11
-vpaddw 2720(%rsp), %ymm11, %ymm11
+vpaddw 2720(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 1232(%rdi)
 vmovdqu 616(%rdi), %ymm11
-vpaddw 2240(%rsp), %ymm11, %ymm11
+vpaddw 2240(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 616(%rdi)
 vmovdqu 968(%rdi), %ymm11
-vpaddw 2496(%rsp), %ymm11, %ymm11
+vpaddw 2496(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 968(%rdi)
 vmovdqu 1320(%rdi), %ymm11
-vpaddw 2752(%rsp), %ymm11, %ymm11
+vpaddw 2752(%r8), %ymm11, %ymm11
 vpand mask_mod8192(%rip), %ymm11, %ymm11
 vmovdqu %ymm11, 1320(%rdi)
-mov %r8, %rsp
 pop %r12
 .cfi_restore r12
 pop %rbp
diff --git a/deps/boringssl/src/crypto/hrss/hrss.c b/deps/boringssl/src/crypto/hrss/hrss.c
index 67ff4c1..0247001 100644
--- a/deps/boringssl/src/crypto/hrss/hrss.c
+++ b/deps/boringssl/src/crypto/hrss/hrss.c
@@ -22,6 +22,7 @@
 #include <openssl/cpu.h>
 #include <openssl/hmac.h>
 #include <openssl/mem.h>
+#include <openssl/rand.h>
 #include <openssl/sha.h>
 
 #if defined(_MSC_VER)
@@ -939,6 +940,34 @@
   printf("]\n");
 }
 
+// POLY_MUL_SCRATCH contains space for the working variables needed by
+// |poly_mul|. The contents afterwards may be discarded, but the object may also
+// be reused with future |poly_mul| calls to save heap allocations.
+//
+// This object must have 32-byte alignment.
+struct POLY_MUL_SCRATCH {
+  union {
+    // This is used by |poly_mul_novec|.
+    struct {
+      uint16_t prod[2 * N];
+      uint16_t scratch[1318];
+    } novec;
+
+#if defined(HRSS_HAVE_VECTOR_UNIT)
+    // This is used by |poly_mul_vec|.
+    struct {
+      vec_t prod[VECS_PER_POLY * 2];
+      vec_t scratch[172];
+    } vec;
+#endif
+
+#if defined(POLY_RQ_MUL_ASM)
+    // This is the space used by |poly_Rq_mul|.
+    uint8_t rq[POLY_MUL_RQ_SCRATCH_SPACE];
+#endif
+  } u;
+};
+
 #if defined(HRSS_HAVE_VECTOR_UNIT)
 
 // poly_mul_vec_aux is a recursive function that multiplies |n| words from |a|
@@ -1184,8 +1213,8 @@
 }
 
 // poly_mul_vec sets |*out| to |x|×|y| mod (𝑥^n - 1).
-static void poly_mul_vec(struct poly *out, const struct poly *x,
-                         const struct poly *y) {
+static void poly_mul_vec(struct POLY_MUL_SCRATCH *scratch, struct poly *out,
+                         const struct poly *x, const struct poly *y) {
   OPENSSL_memset((uint16_t *)&x->v[N], 0, 3 * sizeof(uint16_t));
   OPENSSL_memset((uint16_t *)&y->v[N], 0, 3 * sizeof(uint16_t));
 
@@ -1194,9 +1223,9 @@
   OPENSSL_STATIC_ASSERT(alignof(struct poly) == alignof(vec_t),
                         "struct poly has incorrect alignment");
 
-  vec_t prod[VECS_PER_POLY * 2];
-  vec_t scratch[172];
-  poly_mul_vec_aux(prod, scratch, x->vectors, y->vectors, VECS_PER_POLY);
+  vec_t *const prod = scratch->u.vec.prod;
+  vec_t *const aux_scratch = scratch->u.vec.scratch;
+  poly_mul_vec_aux(prod, aux_scratch, x->vectors, y->vectors, VECS_PER_POLY);
 
   // |prod| needs to be reduced mod (𝑥^n - 1), which just involves adding the
   // upper-half to the lower-half. However, N is 701, which isn't a multiple of
@@ -1273,11 +1302,11 @@
 }
 
 // poly_mul_novec sets |*out| to |x|×|y| mod (𝑥^n - 1).
-static void poly_mul_novec(struct poly *out, const struct poly *x,
-                           const struct poly *y) {
-  uint16_t prod[2 * N];
-  uint16_t scratch[1318];
-  poly_mul_novec_aux(prod, scratch, x->v, y->v, N);
+static void poly_mul_novec(struct POLY_MUL_SCRATCH *scratch, struct poly *out,
+                           const struct poly *x, const struct poly *y) {
+  uint16_t *const prod = scratch->u.novec.prod;
+  uint16_t *const aux_scratch = scratch->u.novec.scratch;
+  poly_mul_novec_aux(prod, aux_scratch, x->v, y->v, N);
 
   for (size_t i = 0; i < N; i++) {
     out->v[i] = prod[i] + prod[i + N];
@@ -1285,25 +1314,25 @@
   OPENSSL_memset(&out->v[N], 0, 3 * sizeof(uint16_t));
 }
 
-static void poly_mul(struct poly *r, const struct poly *a,
-                     const struct poly *b) {
+static void poly_mul(struct POLY_MUL_SCRATCH *scratch, struct poly *r,
+                     const struct poly *a, const struct poly *b) {
 #if defined(POLY_RQ_MUL_ASM)
   const int has_avx2 = (OPENSSL_ia32cap_P[2] & (1 << 5)) != 0;
   if (has_avx2) {
-    poly_Rq_mul(r->v, a->v, b->v);
+    poly_Rq_mul(r->v, a->v, b->v, scratch->u.rq);
     return;
   }
 #endif
 
 #if defined(HRSS_HAVE_VECTOR_UNIT)
   if (vec_capable()) {
-    poly_mul_vec(r, a, b);
+    poly_mul_vec(scratch, r, a, b);
     return;
   }
 #endif
 
   // Fallback, non-vector case.
-  poly_mul_novec(r, a, b);
+  poly_mul_novec(scratch, r, a, b);
 }
 
 // poly_mul_x_minus_1 sets |p| to |p|×(𝑥 - 1) mod (𝑥^n - 1).
@@ -1548,7 +1577,8 @@
 }
 
 // poly_invert sets |*out| to |in^-1| (i.e. such that |*out|×|in| = 1 mod Φ(N)).
-static void poly_invert(struct poly *out, const struct poly *in) {
+static void poly_invert(struct POLY_MUL_SCRATCH *scratch, struct poly *out,
+                        const struct poly *in) {
   // Inversion mod Q, which is done based on the result of inverting mod
   // 2. See [NTRUTN14] paper, bottom of page two.
   struct poly a, *b, tmp;
@@ -1565,9 +1595,9 @@
   // We are working mod Q=2**13 and we need to iterate ceil(log_2(13))
   // times, which is four.
   for (unsigned i = 0; i < 4; i++) {
-    poly_mul(&tmp, &a, b);
+    poly_mul(scratch, &tmp, &a, b);
     tmp.v[0] += 2;
-    poly_mul(b, b, &tmp);
+    poly_mul(scratch, b, b, &tmp);
   }
 }
 
@@ -1871,9 +1901,7 @@
       sizeof(struct HRSS_public_key) >= sizeof(struct public_key) + 15,
       "HRSS public key too small");
 
-  uintptr_t p = (uintptr_t)ext;
-  p = (p + 15) & ~15;
-  return (struct public_key *)p;
+  return align_pointer(ext->opaque, 16);
 }
 
 // private_key_from_external does the same thing as |public_key_from_external|,
@@ -1885,151 +1913,219 @@
       sizeof(struct HRSS_private_key) >= sizeof(struct private_key) + 15,
       "HRSS private key too small");
 
-  uintptr_t p = (uintptr_t)ext;
-  p = (p + 15) & ~15;
-  return (struct private_key *)p;
+  return align_pointer(ext->opaque, 16);
 }
 
-void HRSS_generate_key(
+// malloc_align32 returns a pointer to |size| bytes of 32-byte-aligned heap and
+// sets |*out_ptr| to a value that can be passed to |OPENSSL_free| to release
+// it. It returns NULL if out of memory.
+static void *malloc_align32(void **out_ptr, size_t size) {
+  void *ptr = OPENSSL_malloc(size + 31);
+  if (!ptr) {
+    *out_ptr = NULL;
+    return NULL;
+  }
+
+  *out_ptr = ptr;
+  return align_pointer(ptr, 32);
+}
+
+int HRSS_generate_key(
     struct HRSS_public_key *out_pub, struct HRSS_private_key *out_priv,
     const uint8_t in[HRSS_SAMPLE_BYTES + HRSS_SAMPLE_BYTES + 32]) {
   struct public_key *pub = public_key_from_external(out_pub);
   struct private_key *priv = private_key_from_external(out_priv);
 
+  struct vars {
+    struct POLY_MUL_SCRATCH scratch;
+    struct poly f;
+    struct poly pg_phi1;
+    struct poly pfg_phi1;
+    struct poly pfg_phi1_inverse;
+  };
+
+  void *malloc_ptr;
+  struct vars *const vars = malloc_align32(&malloc_ptr, sizeof(struct vars));
+  if (!vars) {
+    // If the caller ignores the return value the output will still be safe.
+    // The private key output is randomised in case it's later passed to
+    // |HRSS_encap|.
+    memset(out_pub, 0, sizeof(struct HRSS_public_key));
+    RAND_bytes((uint8_t*) out_priv, sizeof(struct HRSS_private_key));
+    return 0;
+  }
+
   OPENSSL_memcpy(priv->hmac_key, in + 2 * HRSS_SAMPLE_BYTES,
                  sizeof(priv->hmac_key));
 
-  struct poly f;
-  poly_short_sample_plus(&f, in);
-  poly3_from_poly(&priv->f, &f);
+  poly_short_sample_plus(&vars->f, in);
+  poly3_from_poly(&priv->f, &vars->f);
   HRSS_poly3_invert(&priv->f_inverse, &priv->f);
 
   // pg_phi1 is p (i.e. 3) × g × Φ(1) (i.e. 𝑥-1).
-  struct poly pg_phi1;
-  poly_short_sample_plus(&pg_phi1, in + HRSS_SAMPLE_BYTES);
+  poly_short_sample_plus(&vars->pg_phi1, in + HRSS_SAMPLE_BYTES);
   for (unsigned i = 0; i < N; i++) {
-    pg_phi1.v[i] *= 3;
+    vars->pg_phi1.v[i] *= 3;
   }
-  poly_mul_x_minus_1(&pg_phi1);
+  poly_mul_x_minus_1(&vars->pg_phi1);
 
-  struct poly pfg_phi1;
-  poly_mul(&pfg_phi1, &f, &pg_phi1);
+  poly_mul(&vars->scratch, &vars->pfg_phi1, &vars->f, &vars->pg_phi1);
 
-  struct poly pfg_phi1_inverse;
-  poly_invert(&pfg_phi1_inverse, &pfg_phi1);
+  poly_invert(&vars->scratch, &vars->pfg_phi1_inverse, &vars->pfg_phi1);
 
-  poly_mul(&pub->ph, &pfg_phi1_inverse, &pg_phi1);
-  poly_mul(&pub->ph, &pub->ph, &pg_phi1);
+  poly_mul(&vars->scratch, &pub->ph, &vars->pfg_phi1_inverse, &vars->pg_phi1);
+  poly_mul(&vars->scratch, &pub->ph, &pub->ph, &vars->pg_phi1);
   poly_clamp(&pub->ph);
 
-  poly_mul(&priv->ph_inverse, &pfg_phi1_inverse, &f);
-  poly_mul(&priv->ph_inverse, &priv->ph_inverse, &f);
+  poly_mul(&vars->scratch, &priv->ph_inverse, &vars->pfg_phi1_inverse,
+           &vars->f);
+  poly_mul(&vars->scratch, &priv->ph_inverse, &priv->ph_inverse, &vars->f);
   poly_clamp(&priv->ph_inverse);
+
+  OPENSSL_free(malloc_ptr);
+  return 1;
 }
 
 static const char kSharedKey[] = "shared key";
 
-void HRSS_encap(uint8_t out_ciphertext[POLY_BYTES],
-                uint8_t out_shared_key[32],
-                const struct HRSS_public_key *in_pub,
-                const uint8_t in[HRSS_SAMPLE_BYTES + HRSS_SAMPLE_BYTES]) {
+int HRSS_encap(uint8_t out_ciphertext[POLY_BYTES], uint8_t out_shared_key[32],
+               const struct HRSS_public_key *in_pub,
+               const uint8_t in[HRSS_SAMPLE_BYTES + HRSS_SAMPLE_BYTES]) {
   const struct public_key *pub =
       public_key_from_external((struct HRSS_public_key *)in_pub);
-  struct poly m, r, m_lifted;
-  poly_short_sample(&m, in);
-  poly_short_sample(&r, in + HRSS_SAMPLE_BYTES);
-  poly_lift(&m_lifted, &m);
 
-  struct poly prh_plus_m;
-  poly_mul(&prh_plus_m, &r, &pub->ph);
-  for (unsigned i = 0; i < N; i++) {
-    prh_plus_m.v[i] += m_lifted.v[i];
+  struct vars {
+    struct POLY_MUL_SCRATCH scratch;
+    struct poly m, r, m_lifted;
+    struct poly prh_plus_m;
+    SHA256_CTX hash_ctx;
+    uint8_t m_bytes[HRSS_POLY3_BYTES];
+    uint8_t r_bytes[HRSS_POLY3_BYTES];
+  };
+
+  void *malloc_ptr;
+  struct vars *const vars = malloc_align32(&malloc_ptr, sizeof(struct vars));
+  if (!vars) {
+    // If the caller ignores the return value the output will still be safe.
+    // The private key output is randomised in case it's used to encrypt and
+    // transmit something.
+    memset(out_ciphertext, 0, POLY_BYTES);
+    RAND_bytes(out_shared_key, 32);
+    return 0;
   }
 
-  poly_marshal(out_ciphertext, &prh_plus_m);
+  poly_short_sample(&vars->m, in);
+  poly_short_sample(&vars->r, in + HRSS_SAMPLE_BYTES);
+  poly_lift(&vars->m_lifted, &vars->m);
 
-  uint8_t m_bytes[HRSS_POLY3_BYTES], r_bytes[HRSS_POLY3_BYTES];
-  poly_marshal_mod3(m_bytes, &m);
-  poly_marshal_mod3(r_bytes, &r);
+  poly_mul(&vars->scratch, &vars->prh_plus_m, &vars->r, &pub->ph);
+  for (unsigned i = 0; i < N; i++) {
+    vars->prh_plus_m.v[i] += vars->m_lifted.v[i];
+  }
 
-  SHA256_CTX hash_ctx;
-  SHA256_Init(&hash_ctx);
-  SHA256_Update(&hash_ctx, kSharedKey, sizeof(kSharedKey));
-  SHA256_Update(&hash_ctx, m_bytes, sizeof(m_bytes));
-  SHA256_Update(&hash_ctx, r_bytes, sizeof(r_bytes));
-  SHA256_Update(&hash_ctx, out_ciphertext, POLY_BYTES);
-  SHA256_Final(out_shared_key, &hash_ctx);
+  poly_marshal(out_ciphertext, &vars->prh_plus_m);
+
+  poly_marshal_mod3(vars->m_bytes, &vars->m);
+  poly_marshal_mod3(vars->r_bytes, &vars->r);
+
+  SHA256_Init(&vars->hash_ctx);
+  SHA256_Update(&vars->hash_ctx, kSharedKey, sizeof(kSharedKey));
+  SHA256_Update(&vars->hash_ctx, vars->m_bytes, sizeof(vars->m_bytes));
+  SHA256_Update(&vars->hash_ctx, vars->r_bytes, sizeof(vars->r_bytes));
+  SHA256_Update(&vars->hash_ctx, out_ciphertext, POLY_BYTES);
+  SHA256_Final(out_shared_key, &vars->hash_ctx);
+
+  OPENSSL_free(malloc_ptr);
+  return 1;
 }
 
-void HRSS_decap(uint8_t out_shared_key[HRSS_KEY_BYTES],
+int HRSS_decap(uint8_t out_shared_key[HRSS_KEY_BYTES],
                 const struct HRSS_private_key *in_priv,
                 const uint8_t *ciphertext, size_t ciphertext_len) {
   const struct private_key *priv =
       private_key_from_external((struct HRSS_private_key *)in_priv);
 
+  struct vars {
+    struct POLY_MUL_SCRATCH scratch;
+    uint8_t masked_key[SHA256_CBLOCK];
+    SHA256_CTX hash_ctx;
+    struct poly c;
+    struct poly f, cf;
+    struct poly3 cf3, m3;
+    struct poly m, m_lifted;
+    struct poly r;
+    struct poly3 r3;
+    uint8_t expected_ciphertext[HRSS_CIPHERTEXT_BYTES];
+    uint8_t m_bytes[HRSS_POLY3_BYTES];
+    uint8_t r_bytes[HRSS_POLY3_BYTES];
+    uint8_t shared_key[32];
+  };
+
+  void *malloc_ptr;
+  struct vars *const vars = malloc_align32(&malloc_ptr, sizeof(struct vars));
+  if (!vars) {
+    // If the caller ignores the return value the output will still be safe.
+    // The private key output is randomised in case it's used to encrypt and
+    // transmit something.
+    RAND_bytes(out_shared_key, HRSS_KEY_BYTES);
+    return 0;
+  }
+
   // This is HMAC, expanded inline rather than using the |HMAC| function so that
   // we can avoid dealing with possible allocation failures and so keep this
   // function infallible.
-  uint8_t masked_key[SHA256_CBLOCK];
-  OPENSSL_STATIC_ASSERT(sizeof(priv->hmac_key) <= sizeof(masked_key),
+  OPENSSL_STATIC_ASSERT(sizeof(priv->hmac_key) <= sizeof(vars->masked_key),
                         "HRSS HMAC key larger than SHA-256 block size");
   for (size_t i = 0; i < sizeof(priv->hmac_key); i++) {
-    masked_key[i] = priv->hmac_key[i] ^ 0x36;
+    vars->masked_key[i] = priv->hmac_key[i] ^ 0x36;
   }
-  OPENSSL_memset(masked_key + sizeof(priv->hmac_key), 0x36,
-                 sizeof(masked_key) - sizeof(priv->hmac_key));
+  OPENSSL_memset(vars->masked_key + sizeof(priv->hmac_key), 0x36,
+                 sizeof(vars->masked_key) - sizeof(priv->hmac_key));
 
-  SHA256_CTX hash_ctx;
-  SHA256_Init(&hash_ctx);
-  SHA256_Update(&hash_ctx, masked_key, sizeof(masked_key));
-  SHA256_Update(&hash_ctx, ciphertext, ciphertext_len);
+  SHA256_Init(&vars->hash_ctx);
+  SHA256_Update(&vars->hash_ctx, vars->masked_key, sizeof(vars->masked_key));
+  SHA256_Update(&vars->hash_ctx, ciphertext, ciphertext_len);
   uint8_t inner_digest[SHA256_DIGEST_LENGTH];
-  SHA256_Final(inner_digest, &hash_ctx);
+  SHA256_Final(inner_digest, &vars->hash_ctx);
 
   for (size_t i = 0; i < sizeof(priv->hmac_key); i++) {
-    masked_key[i] ^= (0x5c ^ 0x36);
+    vars->masked_key[i] ^= (0x5c ^ 0x36);
   }
-  OPENSSL_memset(masked_key + sizeof(priv->hmac_key), 0x5c,
-                 sizeof(masked_key) - sizeof(priv->hmac_key));
+  OPENSSL_memset(vars->masked_key + sizeof(priv->hmac_key), 0x5c,
+                 sizeof(vars->masked_key) - sizeof(priv->hmac_key));
 
-  SHA256_Init(&hash_ctx);
-  SHA256_Update(&hash_ctx, masked_key, sizeof(masked_key));
-  SHA256_Update(&hash_ctx, inner_digest, sizeof(inner_digest));
+  SHA256_Init(&vars->hash_ctx);
+  SHA256_Update(&vars->hash_ctx, vars->masked_key, sizeof(vars->masked_key));
+  SHA256_Update(&vars->hash_ctx, inner_digest, sizeof(inner_digest));
   OPENSSL_STATIC_ASSERT(HRSS_KEY_BYTES == SHA256_DIGEST_LENGTH,
                         "HRSS shared key length incorrect");
-  SHA256_Final(out_shared_key, &hash_ctx);
+  SHA256_Final(out_shared_key, &vars->hash_ctx);
 
-  struct poly c;
   // If the ciphertext is publicly invalid then a random shared key is still
   // returned to simply the logic of the caller, but this path is not constant
   // time.
   if (ciphertext_len != HRSS_CIPHERTEXT_BYTES ||
-      !poly_unmarshal(&c, ciphertext)) {
-    return;
+      !poly_unmarshal(&vars->c, ciphertext)) {
+    goto out;
   }
 
-  struct poly f, cf;
-  struct poly3 cf3, m3;
-  poly_from_poly3(&f, &priv->f);
-  poly_mul(&cf, &c, &f);
-  poly3_from_poly(&cf3, &cf);
+  poly_from_poly3(&vars->f, &priv->f);
+  poly_mul(&vars->scratch, &vars->cf, &vars->c, &vars->f);
+  poly3_from_poly(&vars->cf3, &vars->cf);
   // Note that cf3 is not reduced mod Φ(N). That reduction is deferred.
-  HRSS_poly3_mul(&m3, &cf3, &priv->f_inverse);
+  HRSS_poly3_mul(&vars->m3, &vars->cf3, &priv->f_inverse);
 
-  struct poly m, m_lifted;
-  poly_from_poly3(&m, &m3);
-  poly_lift(&m_lifted, &m);
+  poly_from_poly3(&vars->m, &vars->m3);
+  poly_lift(&vars->m_lifted, &vars->m);
 
-  struct poly r;
   for (unsigned i = 0; i < N; i++) {
-    r.v[i] = c.v[i] - m_lifted.v[i];
+    vars->r.v[i] = vars->c.v[i] - vars->m_lifted.v[i];
   }
-  poly_mul(&r, &r, &priv->ph_inverse);
-  poly_mod_phiN(&r);
-  poly_clamp(&r);
+  poly_mul(&vars->scratch, &vars->r, &vars->r, &priv->ph_inverse);
+  poly_mod_phiN(&vars->r);
+  poly_clamp(&vars->r);
 
-  struct poly3 r3;
-  crypto_word_t ok = poly3_from_poly_checked(&r3, &r);
+  crypto_word_t ok = poly3_from_poly_checked(&vars->r3, &vars->r);
 
   // [NTRUCOMP] section 5.1 includes ReEnc2 and a proof that it's valid. Rather
   // than do an expensive |poly_mul|, it rebuilds |c'| from |c - lift(m)|
@@ -2054,32 +2150,34 @@
   // The |poly_marshal| here then is just confirming that |poly_unmarshal| is
   // strict and could be omitted.
 
-  uint8_t expected_ciphertext[HRSS_CIPHERTEXT_BYTES];
   OPENSSL_STATIC_ASSERT(HRSS_CIPHERTEXT_BYTES == POLY_BYTES,
                         "ciphertext is the wrong size");
-  assert(ciphertext_len == sizeof(expected_ciphertext));
-  poly_marshal(expected_ciphertext, &c);
+  assert(ciphertext_len == sizeof(vars->expected_ciphertext));
+  poly_marshal(vars->expected_ciphertext, &vars->c);
 
-  uint8_t m_bytes[HRSS_POLY3_BYTES];
-  uint8_t r_bytes[HRSS_POLY3_BYTES];
-  poly_marshal_mod3(m_bytes, &m);
-  poly_marshal_mod3(r_bytes, &r);
+  poly_marshal_mod3(vars->m_bytes, &vars->m);
+  poly_marshal_mod3(vars->r_bytes, &vars->r);
 
-  ok &= constant_time_is_zero_w(CRYPTO_memcmp(ciphertext, expected_ciphertext,
-                                              sizeof(expected_ciphertext)));
+  ok &= constant_time_is_zero_w(
+      CRYPTO_memcmp(ciphertext, vars->expected_ciphertext,
+                    sizeof(vars->expected_ciphertext)));
 
-  uint8_t shared_key[32];
-  SHA256_Init(&hash_ctx);
-  SHA256_Update(&hash_ctx, kSharedKey, sizeof(kSharedKey));
-  SHA256_Update(&hash_ctx, m_bytes, sizeof(m_bytes));
-  SHA256_Update(&hash_ctx, r_bytes, sizeof(r_bytes));
-  SHA256_Update(&hash_ctx, expected_ciphertext, sizeof(expected_ciphertext));
-  SHA256_Final(shared_key, &hash_ctx);
+  SHA256_Init(&vars->hash_ctx);
+  SHA256_Update(&vars->hash_ctx, kSharedKey, sizeof(kSharedKey));
+  SHA256_Update(&vars->hash_ctx, vars->m_bytes, sizeof(vars->m_bytes));
+  SHA256_Update(&vars->hash_ctx, vars->r_bytes, sizeof(vars->r_bytes));
+  SHA256_Update(&vars->hash_ctx, vars->expected_ciphertext,
+                sizeof(vars->expected_ciphertext));
+  SHA256_Final(vars->shared_key, &vars->hash_ctx);
 
-  for (unsigned i = 0; i < sizeof(shared_key); i++) {
+  for (unsigned i = 0; i < sizeof(vars->shared_key); i++) {
     out_shared_key[i] =
-        constant_time_select_8(ok, shared_key[i], out_shared_key[i]);
+        constant_time_select_8(ok, vars->shared_key[i], out_shared_key[i]);
   }
+
+out:
+  OPENSSL_free(malloc_ptr);
+  return 1;
 }
 
 void HRSS_marshal_public_key(uint8_t out[HRSS_PUBLIC_KEY_BYTES],
diff --git a/deps/boringssl/src/crypto/hrss/hrss_test.cc b/deps/boringssl/src/crypto/hrss/hrss_test.cc
index 66b9047..7adbe9e 100644
--- a/deps/boringssl/src/crypto/hrss/hrss_test.cc
+++ b/deps/boringssl/src/crypto/hrss/hrss_test.cc
@@ -143,7 +143,7 @@
 
   HRSS_public_key pub;
   HRSS_private_key priv;
-  HRSS_generate_key(&pub, &priv, generate_key_entropy);
+  ASSERT_TRUE(HRSS_generate_key(&pub, &priv, generate_key_entropy));
 
   uint8_t encap_entropy[HRSS_ENCAP_BYTES];
   for (unsigned i = 0; i < sizeof(encap_entropy); i++) {
@@ -157,10 +157,10 @@
 
   uint8_t ciphertext[HRSS_CIPHERTEXT_BYTES];
   uint8_t shared_key[HRSS_KEY_BYTES];
-  HRSS_encap(ciphertext, shared_key, &pub2, encap_entropy);
+  ASSERT_TRUE(HRSS_encap(ciphertext, shared_key, &pub2, encap_entropy));
 
   uint8_t shared_key2[HRSS_KEY_BYTES];
-  HRSS_decap(shared_key2, &priv, ciphertext, sizeof(ciphertext));
+  ASSERT_TRUE(HRSS_decap(shared_key2, &priv, ciphertext, sizeof(ciphertext)));
 
   EXPECT_EQ(Bytes(shared_key), Bytes(shared_key2));
 }
@@ -173,7 +173,7 @@
 
     HRSS_public_key pub;
     HRSS_private_key priv;
-    HRSS_generate_key(&pub, &priv, generate_key_entropy);
+    ASSERT_TRUE(HRSS_generate_key(&pub, &priv, generate_key_entropy));
 
     for (unsigned j = 0; j < 10; j++) {
       uint8_t encap_entropy[HRSS_ENCAP_BYTES];
@@ -182,10 +182,11 @@
 
       uint8_t ciphertext[HRSS_CIPHERTEXT_BYTES];
       uint8_t shared_key[HRSS_KEY_BYTES];
-      HRSS_encap(ciphertext, shared_key, &pub, encap_entropy);
+      ASSERT_TRUE(HRSS_encap(ciphertext, shared_key, &pub, encap_entropy));
 
       uint8_t shared_key2[HRSS_KEY_BYTES];
-      HRSS_decap(shared_key2, &priv, ciphertext, sizeof(ciphertext));
+      ASSERT_TRUE(
+          HRSS_decap(shared_key2, &priv, ciphertext, sizeof(ciphertext)));
       EXPECT_EQ(Bytes(shared_key), Bytes(shared_key2));
 
       uint32_t offset;
@@ -193,7 +194,8 @@
       uint8_t bit;
       RAND_bytes(&bit, sizeof(bit));
       ciphertext[offset % sizeof(ciphertext)] ^= (1 << (bit & 7));
-      HRSS_decap(shared_key2, &priv, ciphertext, sizeof(ciphertext));
+      ASSERT_TRUE(
+          HRSS_decap(shared_key2, &priv, ciphertext, sizeof(ciphertext)));
       EXPECT_NE(Bytes(shared_key), Bytes(shared_key2));
     }
   }
@@ -216,7 +218,7 @@
   HRSS_private_key priv;
   OPENSSL_memset(&pub, 0, sizeof(pub));
   OPENSSL_memset(&priv, 0, sizeof(priv));
-  HRSS_generate_key(&pub, &priv, generate_key_entropy);
+  ASSERT_TRUE(HRSS_generate_key(&pub, &priv, generate_key_entropy));
 
   static const uint8_t kExpectedPub[HRSS_PUBLIC_KEY_BYTES] = {
       0x4a, 0x21, 0x39, 0x7c, 0xb4, 0xa6, 0x58, 0x15, 0x35, 0x77, 0xe4, 0x2a,
@@ -325,7 +327,7 @@
   }
   uint8_t ciphertext[HRSS_CIPHERTEXT_BYTES];
   uint8_t shared_key[HRSS_KEY_BYTES];
-  HRSS_encap(ciphertext, shared_key, &pub, encap_entropy);
+  ASSERT_TRUE(HRSS_encap(ciphertext, shared_key, &pub, encap_entropy));
 
   static const uint8_t kExpectedCiphertext[HRSS_CIPHERTEXT_BYTES] = {
       0xe0, 0xc0, 0x77, 0xeb, 0x7a, 0x48, 0x7d, 0x74, 0x4e, 0x4f, 0x6d, 0xb9,
@@ -433,13 +435,13 @@
   };
   EXPECT_EQ(Bytes(shared_key), Bytes(kExpectedSharedKey));
 
-  HRSS_decap(shared_key, &priv, ciphertext, sizeof(ciphertext));
+  ASSERT_TRUE(HRSS_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)));
   EXPECT_EQ(Bytes(shared_key, sizeof(shared_key)),
             Bytes(kExpectedSharedKey, sizeof(kExpectedSharedKey)));
 
   // Corrupt the ciphertext and ensure that the failure key is constant.
   ciphertext[50] ^= 4;
-  HRSS_decap(shared_key, &priv, ciphertext, sizeof(ciphertext));
+  ASSERT_TRUE(HRSS_decap(shared_key, &priv, ciphertext, sizeof(ciphertext)));
 
   static const uint8_t kExpectedFailureKey[HRSS_KEY_BYTES] = {
       0x13, 0xf7, 0xed, 0x51, 0x00, 0xbc, 0xca, 0x29, 0xdf, 0xb0, 0xd0,
@@ -460,6 +462,23 @@
   alignas(16) uint16_t r[N + 3];
   alignas(16) uint16_t a[N + 3] = {0};
   alignas(16) uint16_t b[N + 3] = {0};
-  CHECK_ABI(poly_Rq_mul, r, a, b);
+
+  uint8_t kCanary[256];
+  OPENSSL_STATIC_ASSERT(sizeof(kCanary) % 32 == 0, "needed for alignment");
+  memset(kCanary, 42, sizeof(kCanary));
+  alignas(32) uint8_t
+      scratch[sizeof(kCanary) + POLY_MUL_RQ_SCRATCH_SPACE + sizeof(kCanary)];
+  OPENSSL_memcpy(scratch, kCanary, sizeof(kCanary));
+  OPENSSL_memcpy(scratch + sizeof(kCanary) + POLY_MUL_RQ_SCRATCH_SPACE, kCanary,
+                 sizeof(kCanary));
+
+  // The function should not touch more than |POLY_MUL_RQ_SCRATCH_SPACE| bytes
+  // of |scratch|.
+  CHECK_ABI(poly_Rq_mul, r, a, b, &scratch[sizeof(kCanary)]);
+
+  EXPECT_EQ(Bytes(scratch, sizeof(kCanary)), Bytes(kCanary));
+  EXPECT_EQ(Bytes(scratch + sizeof(kCanary) + POLY_MUL_RQ_SCRATCH_SPACE,
+                  sizeof(kCanary)),
+            Bytes(kCanary));
 }
 #endif  // POLY_RQ_MUL_ASM && SUPPORTS_ABI_TEST
diff --git a/deps/boringssl/src/crypto/hrss/internal.h b/deps/boringssl/src/crypto/hrss/internal.h
index c0d9bd2..340b2e0 100644
--- a/deps/boringssl/src/crypto/hrss/internal.h
+++ b/deps/boringssl/src/crypto/hrss/internal.h
@@ -47,10 +47,17 @@
 #if !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_SMALL) && \
     defined(OPENSSL_X86_64) && defined(OPENSSL_LINUX)
 #define POLY_RQ_MUL_ASM
+// POLY_MUL_RQ_SCRATCH_SPACE is the number of bytes of scratch space needed
+// by the assembly function poly_Rq_mul.
+#define POLY_MUL_RQ_SCRATCH_SPACE (6144 + 6144 + 12288 + 512 + 9408 + 32)
+
 // poly_Rq_mul is defined in assembly. Inputs and outputs must be 16-byte-
 // aligned.
-extern void poly_Rq_mul(uint16_t r[N + 3], const uint16_t a[N + 3],
-                        const uint16_t b[N + 3]);
+extern void poly_Rq_mul(
+    uint16_t r[N + 3], const uint16_t a[N + 3], const uint16_t b[N + 3],
+    // The following should be `scratch[POLY_MUL_RQ_SCRATCH_SPACE]` but
+    // GCC 11.1 has a bug with unions that breaks that.
+    uint8_t scratch[]);
 #endif
 
 
diff --git a/deps/boringssl/src/crypto/internal.h b/deps/boringssl/src/crypto/internal.h
index edba9f9..03bb779 100644
--- a/deps/boringssl/src/crypto/internal.h
+++ b/deps/boringssl/src/crypto/internal.h
@@ -109,6 +109,7 @@
 #ifndef OPENSSL_HEADER_CRYPTO_INTERNAL_H
 #define OPENSSL_HEADER_CRYPTO_INTERNAL_H
 
+#include <openssl/crypto.h>
 #include <openssl/ex_data.h>
 #include <openssl/stack.h>
 #include <openssl/thread.h>
@@ -208,6 +209,9 @@
 #define OPENSSL_SSE2
 #endif
 
+
+// Pointer utility functions.
+
 // buffers_alias returns one if |a| and |b| alias and zero otherwise.
 static inline int buffers_alias(const uint8_t *a, size_t a_len,
                                 const uint8_t *b, size_t b_len) {
@@ -220,6 +224,23 @@
   return a_u + a_len > b_u && b_u + b_len > a_u;
 }
 
+// align_pointer returns |ptr|, advanced to |alignment|. |alignment| must be a
+// power of two, and |ptr| must have at least |alignment - 1| bytes of scratch
+// space.
+static inline void *align_pointer(void *ptr, size_t alignment) {
+  // |alignment| must be a power of two.
+  assert(alignment != 0 && (alignment & (alignment - 1)) == 0);
+  // Instead of aligning |ptr| as a |uintptr_t| and casting back, compute the
+  // offset and advance in pointer space. C guarantees that casting from pointer
+  // to |uintptr_t| and back gives the same pointer, but general
+  // integer-to-pointer conversions are implementation-defined. GCC does define
+  // it in the useful way, but this makes fewer assumptions.
+  uintptr_t offset = (0u - (uintptr_t)ptr) & (alignment - 1);
+  ptr = (char *)ptr + offset;
+  assert(((uintptr_t)ptr & (alignment - 1)) == 0);
+  return ptr;
+}
+
 
 // Constant-time utility functions.
 //
@@ -470,6 +491,13 @@
 
 // Reference counting.
 
+// Automatically enable C11 atomics if implemented.
+#if !defined(OPENSSL_C11_ATOMIC) && defined(OPENSSL_THREADS) &&   \
+    !defined(__STDC_NO_ATOMICS__) && defined(__STDC_VERSION__) && \
+    __STDC_VERSION__ >= 201112L
+#define OPENSSL_C11_ATOMIC
+#endif
+
 // CRYPTO_REFCOUNT_MAX is the value at which the reference count saturates.
 #define CRYPTO_REFCOUNT_MAX 0xffffffff
 
@@ -607,6 +635,7 @@
 typedef enum {
   OPENSSL_THREAD_LOCAL_ERR = 0,
   OPENSSL_THREAD_LOCAL_RAND,
+  OPENSSL_THREAD_LOCAL_FIPS_COUNTERS,
   OPENSSL_THREAD_LOCAL_TEST,
   NUM_OPENSSL_THREAD_LOCALS,
 } thread_local_data_t;
@@ -811,6 +840,58 @@
   return memset(dst, c, n);
 }
 
+
+// Loads and stores.
+//
+// The following functions load and store sized integers with the specified
+// endianness. They use |memcpy|, and so avoid alignment or strict aliasing
+// requirements on the input and output pointers.
+
+static inline uint32_t CRYPTO_load_u32_le(const void *in) {
+  uint32_t v;
+  OPENSSL_memcpy(&v, in, sizeof(v));
+  return v;
+}
+
+static inline void CRYPTO_store_u32_le(void *out, uint32_t v) {
+  OPENSSL_memcpy(out, &v, sizeof(v));
+}
+
+static inline uint32_t CRYPTO_load_u32_be(const void *in) {
+  uint32_t v;
+  OPENSSL_memcpy(&v, in, sizeof(v));
+  return CRYPTO_bswap4(v);
+}
+
+static inline void CRYPTO_store_u32_be(void *out, uint32_t v) {
+  v = CRYPTO_bswap4(v);
+  OPENSSL_memcpy(out, &v, sizeof(v));
+}
+
+static inline uint64_t CRYPTO_load_u64_be(const void *ptr) {
+  uint64_t ret;
+  OPENSSL_memcpy(&ret, ptr, sizeof(ret));
+  return CRYPTO_bswap8(ret);
+}
+
+static inline void CRYPTO_store_u64_be(void *out, uint64_t v) {
+  v = CRYPTO_bswap8(v);
+  OPENSSL_memcpy(out, &v, sizeof(v));
+}
+
+static inline crypto_word_t CRYPTO_load_word_le(const void *in) {
+  crypto_word_t v;
+  OPENSSL_memcpy(&v, in, sizeof(v));
+  return v;
+}
+
+static inline void CRYPTO_store_word_le(void *out, crypto_word_t v) {
+  OPENSSL_memcpy(out, &v, sizeof(v));
+}
+
+
+// FIPS functions.
+
 #if defined(BORINGSSL_FIPS)
 // BORINGSSL_FIPS_abort is called when a FIPS power-on or continuous test
 // fails. It prevents any further cryptographic operations by the current
@@ -826,6 +907,11 @@
 int boringssl_fips_self_test(const uint8_t *module_hash,
                              size_t module_hash_len);
 
+#if defined(BORINGSSL_FIPS_COUNTERS)
+void boringssl_fips_inc_counter(enum fips_counter_t counter);
+#else
+OPENSSL_INLINE void boringssl_fips_inc_counter(enum fips_counter_t counter) {}
+#endif
 
 #if defined(__cplusplus)
 }  // extern C
diff --git a/deps/boringssl/src/crypto/lhash/internal.h b/deps/boringssl/src/crypto/lhash/internal.h
new file mode 100644
index 0000000..64dca1d
--- /dev/null
+++ b/deps/boringssl/src/crypto/lhash/internal.h
@@ -0,0 +1,253 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#ifndef OPENSSL_HEADER_LHASH_INTERNAL_H
+#define OPENSSL_HEADER_LHASH_INTERNAL_H
+
+#include <openssl/lhash.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// lhash is a traditional, chaining hash table that automatically expands and
+// contracts as needed. One should not use the lh_* functions directly, rather
+// use the type-safe macro wrappers:
+//
+// A hash table of a specific type of object has type |LHASH_OF(type)|. This
+// can be defined (once) with |DEFINE_LHASH_OF(type)| and declared where needed
+// with |DECLARE_LHASH_OF(type)|. For example:
+//
+//   struct foo {
+//     int bar;
+//   };
+//
+//   DEFINE_LHASH_OF(struct foo)
+//
+// Although note that the hash table will contain /pointers/ to |foo|.
+//
+// A macro will be defined for each of the |OPENSSL_lh_*| functions below. For
+// |LHASH_OF(foo)|, the macros would be |lh_foo_new|, |lh_foo_num_items| etc.
+
+
+// lhash_cmp_func is a comparison function that returns a value equal, or not
+// equal, to zero depending on whether |*a| is equal, or not equal to |*b|,
+// respectively. Note the difference between this and |stack_cmp_func| in that
+// this takes pointers to the objects directly.
+//
+// This function's actual type signature is int (*)(const T*, const T*). The
+// low-level |lh_*| functions will be passed a type-specific wrapper to call it
+// correctly.
+typedef int (*lhash_cmp_func)(const void *a, const void *b);
+typedef int (*lhash_cmp_func_helper)(lhash_cmp_func func, const void *a,
+                                     const void *b);
+
+// lhash_hash_func is a function that maps an object to a uniformly distributed
+// uint32_t.
+//
+// This function's actual type signature is uint32_t (*)(const T*). The
+// low-level |lh_*| functions will be passed a type-specific wrapper to call it
+// correctly.
+typedef uint32_t (*lhash_hash_func)(const void *a);
+typedef uint32_t (*lhash_hash_func_helper)(lhash_hash_func func, const void *a);
+
+typedef struct lhash_st _LHASH;
+
+// OPENSSL_lh_new returns a new, empty hash table or NULL on error.
+OPENSSL_EXPORT _LHASH *OPENSSL_lh_new(lhash_hash_func hash,
+                                      lhash_cmp_func comp);
+
+// OPENSSL_lh_free frees the hash table itself but none of the elements. See
+// |OPENSSL_lh_doall|.
+OPENSSL_EXPORT void OPENSSL_lh_free(_LHASH *lh);
+
+// OPENSSL_lh_num_items returns the number of items in |lh|.
+OPENSSL_EXPORT size_t OPENSSL_lh_num_items(const _LHASH *lh);
+
+// OPENSSL_lh_retrieve finds an element equal to |data| in the hash table and
+// returns it. If no such element exists, it returns NULL.
+OPENSSL_EXPORT void *OPENSSL_lh_retrieve(const _LHASH *lh, const void *data,
+                                         lhash_hash_func_helper call_hash_func,
+                                         lhash_cmp_func_helper call_cmp_func);
+
+// OPENSSL_lh_retrieve_key finds an element matching |key|, given the specified
+// hash and comparison function. This differs from |OPENSSL_lh_retrieve| in that
+// the key may be a different type than the values stored in |lh|. |key_hash|
+// and |cmp_key| must be compatible with the functions passed into
+// |OPENSSL_lh_new|.
+OPENSSL_EXPORT void *OPENSSL_lh_retrieve_key(const _LHASH *lh, const void *key,
+                                             uint32_t key_hash,
+                                             int (*cmp_key)(const void *key,
+                                                            const void *value));
+
+// OPENSSL_lh_insert inserts |data| into the hash table. If an existing element
+// is equal to |data| (with respect to the comparison function) then |*old_data|
+// will be set to that value and it will be replaced. Otherwise, or in the
+// event of an error, |*old_data| will be set to NULL. It returns one on
+// success or zero in the case of an allocation error.
+OPENSSL_EXPORT int OPENSSL_lh_insert(_LHASH *lh, void **old_data, void *data,
+                                     lhash_hash_func_helper call_hash_func,
+                                     lhash_cmp_func_helper call_cmp_func);
+
+// OPENSSL_lh_delete removes an element equal to |data| from the hash table and
+// returns it. If no such element is found, it returns NULL.
+OPENSSL_EXPORT void *OPENSSL_lh_delete(_LHASH *lh, const void *data,
+                                       lhash_hash_func_helper call_hash_func,
+                                       lhash_cmp_func_helper call_cmp_func);
+
+// OPENSSL_lh_doall_arg calls |func| on each element of the hash table and also
+// passes |arg| as the second argument.
+// TODO(fork): rename this
+OPENSSL_EXPORT void OPENSSL_lh_doall_arg(_LHASH *lh,
+                                         void (*func)(void *, void *),
+                                         void *arg);
+
+#define DEFINE_LHASH_OF(type)                                                  \
+  DECLARE_LHASH_OF(type)                                                       \
+                                                                               \
+  typedef int (*lhash_##type##_cmp_func)(const type *, const type *);          \
+  typedef uint32_t (*lhash_##type##_hash_func)(const type *);                  \
+                                                                               \
+  OPENSSL_INLINE int lh_##type##_call_cmp_func(lhash_cmp_func func,            \
+                                               const void *a, const void *b) { \
+    return ((lhash_##type##_cmp_func)func)((const type *)a, (const type *)b);  \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE uint32_t lh_##type##_call_hash_func(lhash_hash_func func,     \
+                                                     const void *a) {          \
+    return ((lhash_##type##_hash_func)func)((const type *)a);                  \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE LHASH_OF(type) *lh_##type##_new(                              \
+      lhash_##type##_hash_func hash, lhash_##type##_cmp_func comp) {           \
+    return (LHASH_OF(type) *)OPENSSL_lh_new((lhash_hash_func)hash,             \
+                                            (lhash_cmp_func)comp);             \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE void lh_##type##_free(LHASH_OF(type) *lh) {                   \
+    OPENSSL_lh_free((_LHASH *)lh);                                             \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE size_t lh_##type##_num_items(const LHASH_OF(type) *lh) {      \
+    return OPENSSL_lh_num_items((const _LHASH *)lh);                           \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE type *lh_##type##_retrieve(const LHASH_OF(type) *lh,          \
+                                            const type *data) {                \
+    return (type *)OPENSSL_lh_retrieve((const _LHASH *)lh, data,               \
+                                       lh_##type##_call_hash_func,             \
+                                       lh_##type##_call_cmp_func);             \
+  }                                                                            \
+                                                                               \
+  typedef struct {                                                             \
+    int (*cmp_key)(const void *key, const type *value);                        \
+    const void *key;                                                           \
+  } LHASH_CMP_KEY_##type;                                                      \
+                                                                               \
+  OPENSSL_INLINE int lh_##type##_call_cmp_key(const void *key,                 \
+                                              const void *value) {             \
+    const LHASH_CMP_KEY_##type *cb = (const LHASH_CMP_KEY_##type *)key;        \
+    return cb->cmp_key(cb->key, (const type *)value);                          \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE type *lh_##type##_retrieve_key(                               \
+      const LHASH_OF(type) *lh, const void *key, uint32_t key_hash,            \
+      int (*cmp_key)(const void *key, const type *value)) {                    \
+    LHASH_CMP_KEY_##type cb = {cmp_key, key};                                  \
+    return (type *)OPENSSL_lh_retrieve_key((const _LHASH *)lh, &cb, key_hash,  \
+                                           lh_##type##_call_cmp_key);          \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE int lh_##type##_insert(LHASH_OF(type) *lh, type **old_data,   \
+                                        type *data) {                          \
+    void *old_data_void = NULL;                                                \
+    int ret = OPENSSL_lh_insert((_LHASH *)lh, &old_data_void, data,            \
+                                lh_##type##_call_hash_func,                    \
+                                lh_##type##_call_cmp_func);                    \
+    *old_data = (type *)old_data_void;                                         \
+    return ret;                                                                \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE type *lh_##type##_delete(LHASH_OF(type) *lh,                  \
+                                          const type *data) {                  \
+    return (type *)OPENSSL_lh_delete((_LHASH *)lh, data,                       \
+                                     lh_##type##_call_hash_func,               \
+                                     lh_##type##_call_cmp_func);               \
+  }                                                                            \
+                                                                               \
+  typedef struct {                                                             \
+    void (*doall_arg)(type *, void *);                                         \
+    void *arg;                                                                 \
+  } LHASH_DOALL_##type;                                                        \
+                                                                               \
+  OPENSSL_INLINE void lh_##type##_call_doall_arg(void *value, void *arg) {     \
+    const LHASH_DOALL_##type *cb = (const LHASH_DOALL_##type *)arg;            \
+    cb->doall_arg((type *)value, cb->arg);                                     \
+  }                                                                            \
+                                                                               \
+  OPENSSL_INLINE void lh_##type##_doall_arg(                                   \
+      LHASH_OF(type) *lh, void (*func)(type *, void *), void *arg) {           \
+    LHASH_DOALL_##type cb = {func, arg};                                       \
+    OPENSSL_lh_doall_arg((_LHASH *)lh, lh_##type##_call_doall_arg, &cb);       \
+  }
+
+
+#if defined(__cplusplus)
+}  // extern C
+#endif
+
+#endif  // OPENSSL_HEADER_LHASH_INTERNAL_H
diff --git a/deps/boringssl/src/crypto/lhash/lhash.c b/deps/boringssl/src/crypto/lhash/lhash.c
index 98ee60a..4a95a2e 100644
--- a/deps/boringssl/src/crypto/lhash/lhash.c
+++ b/deps/boringssl/src/crypto/lhash/lhash.c
@@ -62,6 +62,7 @@
 
 #include <openssl/mem.h>
 
+#include "internal.h"
 #include "../internal.h"
 
 
@@ -73,6 +74,16 @@
 static const size_t kMaxAverageChainLength = 2;
 static const size_t kMinAverageChainLength = 1;
 
+// lhash_item_st is an element of a hash chain. It points to the opaque data
+// for this element and to the next item in the chain. The linked-list is NULL
+// terminated.
+typedef struct lhash_item_st {
+  void *data;
+  struct lhash_item_st *next;
+  // hash contains the cached, hash value of |data|.
+  uint32_t hash;
+} LHASH_ITEM;
+
 struct lhash_st {
   // num_items contains the total number of items in the hash table.
   size_t num_items;
@@ -92,7 +103,7 @@
   lhash_hash_func hash;
 };
 
-_LHASH *lh_new(lhash_hash_func hash, lhash_cmp_func comp) {
+_LHASH *OPENSSL_lh_new(lhash_hash_func hash, lhash_cmp_func comp) {
   _LHASH *ret = OPENSSL_malloc(sizeof(_LHASH));
   if (ret == NULL) {
     return NULL;
@@ -112,7 +123,7 @@
   return ret;
 }
 
-void lh_free(_LHASH *lh) {
+void OPENSSL_lh_free(_LHASH *lh) {
   if (lh == NULL) {
     return;
   }
@@ -129,7 +140,7 @@
   OPENSSL_free(lh);
 }
 
-size_t lh_num_items(const _LHASH *lh) { return lh->num_items; }
+size_t OPENSSL_lh_num_items(const _LHASH *lh) { return lh->num_items; }
 
 // get_next_ptr_and_hash returns a pointer to the pointer that points to the
 // item equal to |data|. In other words, it searches for an item equal to |data|
@@ -175,16 +186,18 @@
   return ret;
 }
 
-void *lh_retrieve(const _LHASH *lh, const void *data,
-                  lhash_hash_func_helper call_hash_func,
-                  lhash_cmp_func_helper call_cmp_func) {
+void *OPENSSL_lh_retrieve(const _LHASH *lh, const void *data,
+                          lhash_hash_func_helper call_hash_func,
+                          lhash_cmp_func_helper call_cmp_func) {
   LHASH_ITEM **next_ptr =
       get_next_ptr_and_hash(lh, NULL, data, call_hash_func, call_cmp_func);
   return *next_ptr == NULL ? NULL : (*next_ptr)->data;
 }
 
-void *lh_retrieve_key(const _LHASH *lh, const void *key, uint32_t key_hash,
-                      int (*cmp_key)(const void *key, const void *value)) {
+void *OPENSSL_lh_retrieve_key(const _LHASH *lh, const void *key,
+                              uint32_t key_hash,
+                              int (*cmp_key)(const void *key,
+                                             const void *value)) {
   LHASH_ITEM **next_ptr = get_next_ptr_by_key(lh, key, key_hash, cmp_key);
   return *next_ptr == NULL ? NULL : (*next_ptr)->data;
 }
@@ -252,9 +265,9 @@
   }
 }
 
-int lh_insert(_LHASH *lh, void **old_data, void *data,
-              lhash_hash_func_helper call_hash_func,
-              lhash_cmp_func_helper call_cmp_func) {
+int OPENSSL_lh_insert(_LHASH *lh, void **old_data, void *data,
+                      lhash_hash_func_helper call_hash_func,
+                      lhash_cmp_func_helper call_cmp_func) {
   uint32_t hash;
   LHASH_ITEM **next_ptr, *item;
 
@@ -287,9 +300,9 @@
   return 1;
 }
 
-void *lh_delete(_LHASH *lh, const void *data,
-                lhash_hash_func_helper call_hash_func,
-                lhash_cmp_func_helper call_cmp_func) {
+void *OPENSSL_lh_delete(_LHASH *lh, const void *data,
+                        lhash_hash_func_helper call_hash_func,
+                        lhash_cmp_func_helper call_cmp_func) {
   LHASH_ITEM **next_ptr, *item, *ret;
 
   next_ptr =
@@ -311,7 +324,7 @@
   return ret;
 }
 
-void lh_doall_arg(_LHASH *lh, void (*func)(void *, void *), void *arg) {
+void OPENSSL_lh_doall_arg(_LHASH *lh, void (*func)(void *, void *), void *arg) {
   if (lh == NULL) {
     return;
   }
@@ -338,11 +351,3 @@
   // resizing is done here.
   lh_maybe_resize(lh);
 }
-
-uint32_t lh_strhash(const char *c) {
-  if (c == NULL) {
-    return 0;
-  }
-
-  return OPENSSL_hash32(c, strlen(c));
-}
diff --git a/deps/boringssl/src/crypto/lhash/lhash_test.cc b/deps/boringssl/src/crypto/lhash/lhash_test.cc
index 885d3c7..6b4bfad 100644
--- a/deps/boringssl/src/crypto/lhash/lhash_test.cc
+++ b/deps/boringssl/src/crypto/lhash/lhash_test.cc
@@ -25,8 +25,12 @@
 #include <utility>
 #include <vector>
 
+#include <openssl/mem.h>
+
 #include <gtest/gtest.h>
 
+#include "internal.h"
+
 
 DEFINE_LHASH_OF(char)
 
@@ -58,7 +62,7 @@
 
 TEST(LHashTest, Basic) {
   std::unique_ptr<LHASH_OF(char), FreeLHASH_OF_char> lh(
-      lh_char_new(lh_strhash, strcmp));
+      lh_char_new(OPENSSL_strhash, strcmp));
   ASSERT_TRUE(lh);
 
   // lh is expected to store a canonical instance of each string. dummy_lh
@@ -90,15 +94,6 @@
                         &actual);
       std::sort(actual.begin(), actual.end());
       EXPECT_EQ(expected, actual);
-
-      // Also test |lh_*_doall|.
-      actual.clear();
-      static ValueList *global_actual_list;
-      global_actual_list = &actual;
-      lh_char_doall(lh.get(),
-                    [](char *ptr) { global_actual_list->push_back(ptr); });
-      std::sort(actual.begin(), actual.end());
-      EXPECT_EQ(expected, actual);
     }
 
     enum Action {
@@ -116,7 +111,7 @@
 
         // Do the same lookup with |lh_char_retrieve_key|.
         value = lh_char_retrieve_key(
-            lh.get(), &key, lh_strhash(key.get()),
+            lh.get(), &key, OPENSSL_strhash(key.get()),
             [](const void *key_ptr, const char *data) -> int {
               const char *key_data =
                   reinterpret_cast<const std::unique_ptr<char[]> *>(key_ptr)
diff --git a/deps/boringssl/src/crypto/mem.c b/deps/boringssl/src/crypto/mem.c
index 0491f15..4ccc263 100644
--- a/deps/boringssl/src/crypto/mem.c
+++ b/deps/boringssl/src/crypto/mem.c
@@ -93,20 +93,38 @@
 #define WEAK_SYMBOL_FUNC(rettype, name, args) static rettype(*name) args = NULL;
 #endif
 
+#if defined(BORINGSSL_SDALLOCX)
 // sdallocx is a sized |free| function. By passing the size (which we happen to
-// always know in BoringSSL), the malloc implementation can save work. We cannot
-// depend on |sdallocx| being available, however, so it's a weak symbol.
+// always know in BoringSSL), the malloc implementation can save work.
 //
-// This will always be safe, but will only be overridden if the malloc
-// implementation is statically linked with BoringSSL. So, if |sdallocx| is
-// provided in, say, libc.so, we still won't use it because that's dynamically
-// linked. This isn't an ideal result, but its helps in some cases.
-WEAK_SYMBOL_FUNC(void, sdallocx, (void *ptr, size_t size, int flags));
+// This is guarded by BORINGSSL_SDALLOCX, rather than being a weak symbol,
+// because it can work poorly if there are two malloc implementations in the
+// address space. (Which probably isn't valid, ODR etc, but
+// https://github.com/grpc/grpc/issues/25450). In that situation, |malloc| can
+// come from one allocator but |sdallocx| from another and crashes quickly
+// result. We can't match |sdallocx| with |mallocx| because tcmalloc only
+// provides the former, so a mismatch can still happen.
+void sdallocx(void *ptr, size_t size, int flags);
+#endif
 
 // The following three functions can be defined to override default heap
 // allocation and freeing. If defined, it is the responsibility of
 // |OPENSSL_memory_free| to zero out the memory before returning it to the
 // system. |OPENSSL_memory_free| will not be passed NULL pointers.
+//
+// WARNING: These functions are called on every allocation and free in
+// BoringSSL across the entire process. They may be called by any code in the
+// process which calls BoringSSL, including in process initializers and thread
+// destructors. When called, BoringSSL may hold pthreads locks. Any other code
+// in the process which, directly or indirectly, calls BoringSSL may be on the
+// call stack and may itself be using arbitrary synchronization primitives.
+//
+// As a result, these functions may not have the usual programming environment
+// available to most C or C++ code. In particular, they may not call into
+// BoringSSL, or any library which depends on BoringSSL. Any synchronization
+// primitives used must tolerate every other synchronization primitive linked
+// into the process, including pthreads locks. Failing to meet these constraints
+// may result in deadlocks, crashes, or memory corruption.
 WEAK_SYMBOL_FUNC(void*, OPENSSL_memory_alloc, (size_t size));
 WEAK_SYMBOL_FUNC(void, OPENSSL_memory_free, (void *ptr));
 WEAK_SYMBOL_FUNC(size_t, OPENSSL_memory_get_size, (void *ptr));
@@ -148,11 +166,11 @@
 
   size_t size = *(size_t *)ptr;
   OPENSSL_cleanse(ptr, size + OPENSSL_MALLOC_PREFIX);
-  if (sdallocx) {
-    sdallocx(ptr, size + OPENSSL_MALLOC_PREFIX, 0 /* flags */);
-  } else {
-    free(ptr);
-  }
+#if defined(BORINGSSL_SDALLOCX)
+  sdallocx(ptr, size + OPENSSL_MALLOC_PREFIX, 0 /* flags */);
+#else
+  free(ptr);
+#endif
 }
 
 void *OPENSSL_realloc(void *orig_ptr, size_t new_size) {
@@ -233,6 +251,8 @@
   return h;
 }
 
+uint32_t OPENSSL_strhash(const char *s) { return OPENSSL_hash32(s, strlen(s)); }
+
 size_t OPENSSL_strnlen(const char *s, size_t len) {
   for (size_t i = 0; i < len; i++) {
     if (s[i] == 0) {
@@ -308,22 +328,15 @@
 }
 
 char *OPENSSL_strndup(const char *str, size_t size) {
-  char *ret;
-  size_t alloc_size;
-
-  if (str == NULL) {
-    return NULL;
-  }
-
   size = OPENSSL_strnlen(str, size);
 
-  alloc_size = size + 1;
+  size_t alloc_size = size + 1;
   if (alloc_size < size) {
     // overflow
     OPENSSL_PUT_ERROR(CRYPTO, ERR_R_MALLOC_FAILURE);
     return NULL;
   }
-  ret = OPENSSL_malloc(alloc_size);
+  char *ret = OPENSSL_malloc(alloc_size);
   if (ret == NULL) {
     OPENSSL_PUT_ERROR(CRYPTO, ERR_R_MALLOC_FAILURE);
     return NULL;
diff --git a/deps/boringssl/src/crypto/obj/obj.c b/deps/boringssl/src/crypto/obj/obj.c
index 3bf1abf..958625d 100644
--- a/deps/boringssl/src/crypto/obj/obj.c
+++ b/deps/boringssl/src/crypto/obj/obj.c
@@ -67,8 +67,12 @@
 #include <openssl/mem.h>
 #include <openssl/thread.h>
 
-#include "obj_dat.h"
+#include "../asn1/internal.h"
 #include "../internal.h"
+#include "../lhash/internal.h"
+
+// obj_data.h must be included after the definition of |ASN1_OBJECT|.
+#include "obj_dat.h"
 
 
 DEFINE_LHASH_OF(ASN1_OBJECT)
@@ -338,12 +342,12 @@
   return 1;
 }
 
-const ASN1_OBJECT *OBJ_nid2obj(int nid) {
+ASN1_OBJECT *OBJ_nid2obj(int nid) {
   if (nid >= 0 && nid < NUM_NID) {
     if (nid != NID_undef && kObjects[nid].nid == NID_undef) {
       goto err;
     }
-    return &kObjects[nid];
+    return (ASN1_OBJECT *)&kObjects[nid];
   }
 
   CRYPTO_STATIC_MUTEX_lock_read(&global_added_lock);
@@ -411,7 +415,7 @@
     }
 
     if (nid != NID_undef) {
-      return (ASN1_OBJECT*) OBJ_nid2obj(nid);
+      return OBJ_nid2obj(nid);
     }
   }
 
@@ -484,7 +488,7 @@
 }
 
 static uint32_t hash_short_name(const ASN1_OBJECT *obj) {
-  return lh_strhash(obj->sn);
+  return OPENSSL_strhash(obj->sn);
 }
 
 static int cmp_short_name(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
@@ -492,7 +496,7 @@
 }
 
 static uint32_t hash_long_name(const ASN1_OBJECT *obj) {
-  return lh_strhash(obj->ln);
+  return OPENSSL_strhash(obj->ln);
 }
 
 static int cmp_long_name(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
diff --git a/deps/boringssl/src/crypto/obj/obj_test.cc b/deps/boringssl/src/crypto/obj/obj_test.cc
index e623843..08796e2 100644
--- a/deps/boringssl/src/crypto/obj/obj_test.cc
+++ b/deps/boringssl/src/crypto/obj/obj_test.cc
@@ -75,14 +75,16 @@
 
 static bool ExpectObj2Txt(const uint8_t *der, size_t der_len,
                           bool always_return_oid, const char *expected) {
-  ASN1_OBJECT obj;
-  OPENSSL_memset(&obj, 0, sizeof(obj));
-  obj.data = der;
-  obj.length = static_cast<int>(der_len);
+  bssl::UniquePtr<ASN1_OBJECT> obj(
+      ASN1_OBJECT_create(NID_undef, der, static_cast<int>(der_len),
+                         /*sn=*/nullptr, /*ln=*/nullptr));
+  if (!obj) {
+    return false;
+  }
 
   int expected_len = static_cast<int>(strlen(expected));
 
-  int len = OBJ_obj2txt(nullptr, 0, &obj, always_return_oid);
+  int len = OBJ_obj2txt(nullptr, 0, obj.get(), always_return_oid);
   if (len != expected_len) {
     fprintf(stderr,
             "OBJ_obj2txt of %s with out_len = 0 returned %d, wanted %d.\n",
@@ -92,7 +94,7 @@
 
   char short_buf[1];
   OPENSSL_memset(short_buf, 0xff, sizeof(short_buf));
-  len = OBJ_obj2txt(short_buf, sizeof(short_buf), &obj, always_return_oid);
+  len = OBJ_obj2txt(short_buf, sizeof(short_buf), obj.get(), always_return_oid);
   if (len != expected_len) {
     fprintf(stderr,
             "OBJ_obj2txt of %s with out_len = 1 returned %d, wanted %d.\n",
@@ -109,7 +111,7 @@
   }
 
   char buf[256];
-  len = OBJ_obj2txt(buf, sizeof(buf), &obj, always_return_oid);
+  len = OBJ_obj2txt(buf, sizeof(buf), obj.get(), always_return_oid);
   if (len != expected_len) {
     fprintf(stderr,
             "OBJ_obj2txt of %s with out_len = 256 returned %d, wanted %d.\n",
@@ -166,17 +168,14 @@
   ASSERT_TRUE(ExpectObj2Txt(nullptr, 0, false /* return name */, ""));
   ASSERT_TRUE(ExpectObj2Txt(nullptr, 0, true /* don't return name */, ""));
 
-  ASN1_OBJECT obj;
-  OPENSSL_memset(&obj, 0, sizeof(obj));
-
   // kNonMinimalOID is kBasicConstraints with the final component non-minimally
   // encoded.
-  static const uint8_t kNonMinimalOID[] = {
-      0x55, 0x1d, 0x80, 0x13,
-  };
-  obj.data = kNonMinimalOID;
-  obj.length = sizeof(kNonMinimalOID);
-  ASSERT_EQ(-1, OBJ_obj2txt(NULL, 0, &obj, 0));
+  static const uint8_t kNonMinimalOID[] = {0x55, 0x1d, 0x80, 0x13};
+  bssl::UniquePtr<ASN1_OBJECT> obj(
+      ASN1_OBJECT_create(NID_undef, kNonMinimalOID, sizeof(kNonMinimalOID),
+                         /*sn=*/nullptr, /*ln=*/nullptr));
+  ASSERT_TRUE(obj);
+  ASSERT_EQ(-1, OBJ_obj2txt(NULL, 0, obj.get(), 0));
 
   // kOverflowOID is the DER representation of
   // 1.2.840.113554.4.1.72585.18446744073709551616. (The final value is 2^64.)
@@ -184,16 +183,16 @@
       0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09,
       0x82, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00,
   };
-  obj.data = kOverflowOID;
-  obj.length = sizeof(kOverflowOID);
-  ASSERT_EQ(-1, OBJ_obj2txt(NULL, 0, &obj, 0));
+  obj.reset(ASN1_OBJECT_create(NID_undef, kOverflowOID, sizeof(kOverflowOID),
+                               /*sn=*/nullptr, /*ln=*/nullptr));
+  ASSERT_TRUE(obj);
+  ASSERT_EQ(-1, OBJ_obj2txt(NULL, 0, obj.get(), 0));
 
   // kInvalidOID is a mis-encoded version of kBasicConstraints with the final
   // octet having the high bit set.
-  static const uint8_t kInvalidOID[] = {
-      0x55, 0x1d, 0x93,
-  };
-  obj.data = kInvalidOID;
-  obj.length = sizeof(kInvalidOID);
-  ASSERT_EQ(-1, OBJ_obj2txt(NULL, 0, &obj, 0));
+  static const uint8_t kInvalidOID[] = {0x55, 0x1d, 0x93};
+  obj.reset(ASN1_OBJECT_create(NID_undef, kInvalidOID, sizeof(kInvalidOID),
+                               /*sn=*/nullptr, /*ln=*/nullptr));
+  ASSERT_TRUE(obj);
+  ASSERT_EQ(-1, OBJ_obj2txt(NULL, 0, obj.get(), 0));
 }
diff --git a/deps/boringssl/src/crypto/pem/pem_all.c b/deps/boringssl/src/crypto/pem/pem_all.c
index 6b40883..e419774 100644
--- a/deps/boringssl/src/crypto/pem/pem_all.c
+++ b/deps/boringssl/src/crypto/pem/pem_all.c
@@ -157,8 +157,6 @@
     return pkey_get_rsa(pktmp, rsa);
 }
 
-#ifndef OPENSSL_NO_FP_API
-
 RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **rsa, pem_password_cb *cb, void *u)
 {
     EVP_PKEY *pktmp;
@@ -166,8 +164,6 @@
     return pkey_get_rsa(pktmp, rsa);
 }
 
-#endif
-
 IMPLEMENT_PEM_write_cb_const(RSAPrivateKey, RSA, PEM_STRING_RSA,
                              RSAPrivateKey)
 
@@ -205,7 +201,6 @@
                              DSAPrivateKey)
 
     IMPLEMENT_PEM_rw(DSA_PUBKEY, DSA, PEM_STRING_PUBLIC, DSA_PUBKEY)
-# ifndef OPENSSL_NO_FP_API
 DSA *PEM_read_DSAPrivateKey(FILE *fp, DSA **dsa, pem_password_cb *cb, void *u)
 {
     EVP_PKEY *pktmp;
@@ -213,8 +208,6 @@
     return pkey_get_dsa(pktmp, dsa); /* will free pktmp */
 }
 
-# endif
-
 IMPLEMENT_PEM_rw_const(DSAparams, DSA, PEM_STRING_DSAPARAMS, DSAparams)
 #endif
 static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey)
@@ -245,7 +238,6 @@
                        ECPrivateKey)
 
     IMPLEMENT_PEM_rw(EC_PUBKEY, EC_KEY, PEM_STRING_PUBLIC, EC_PUBKEY)
-#ifndef OPENSSL_NO_FP_API
 EC_KEY *PEM_read_ECPrivateKey(FILE *fp, EC_KEY **eckey, pem_password_cb *cb,
                               void *u)
 {
@@ -254,7 +246,6 @@
     return pkey_get_eckey(pktmp, eckey); /* will free pktmp */
 }
 
-#endif
 
 IMPLEMENT_PEM_write_const(DHparams, DH, PEM_STRING_DHPARAMS, DHparams)
 
diff --git a/deps/boringssl/src/crypto/pem/pem_info.c b/deps/boringssl/src/crypto/pem/pem_info.c
index 1cda35b..3a1d0cc 100644
--- a/deps/boringssl/src/crypto/pem/pem_info.c
+++ b/deps/boringssl/src/crypto/pem/pem_info.c
@@ -70,7 +70,6 @@
 #include <openssl/rsa.h>
 #include <openssl/x509.h>
 
-#ifndef OPENSSL_NO_FP_API
 STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk,
                                         pem_password_cb *cb, void *u)
 {
@@ -83,7 +82,6 @@
     BIO_free(b);
     return ret;
 }
-#endif
 
 enum parse_result_t {
     parse_ok,
diff --git a/deps/boringssl/src/crypto/pem/pem_lib.c b/deps/boringssl/src/crypto/pem/pem_lib.c
index 00c0e0a..747d694 100644
--- a/deps/boringssl/src/crypto/pem/pem_lib.c
+++ b/deps/boringssl/src/crypto/pem/pem_lib.c
@@ -117,7 +117,6 @@
     buf[j + i * 2 + 1] = '\0';
 }
 
-#ifndef OPENSSL_NO_FP_API
 void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
                     pem_password_cb *cb, void *u)
 {
@@ -130,7 +129,6 @@
     BIO_free(b);
     return ret;
 }
-#endif
 
 static int check_pem(const char *nm, const char *name)
 {
@@ -252,7 +250,6 @@
     return ret;
 }
 
-#ifndef OPENSSL_NO_FP_API
 int PEM_ASN1_write(i2d_of_void *i2d, const char *name, FILE *fp,
                    void *x, const EVP_CIPHER *enc, unsigned char *kstr,
                    int klen, pem_password_cb *callback, void *u)
@@ -266,7 +263,6 @@
     BIO_free(b);
     return ret;
 }
-#endif
 
 int PEM_ASN1_write_bio(i2d_of_void *i2d, const char *name, BIO *bp,
                        void *x, const EVP_CIPHER *enc, unsigned char *kstr,
@@ -507,7 +503,6 @@
     return (1);
 }
 
-#ifndef OPENSSL_NO_FP_API
 int PEM_write(FILE *fp, const char *name, const char *header,
               const unsigned char *data, long len)
 {
@@ -520,7 +515,6 @@
     BIO_free(b);
     return (ret);
 }
-#endif
 
 int PEM_write_bio(BIO *bp, const char *name, const char *header,
                   const unsigned char *data, long len)
@@ -578,7 +572,6 @@
     return (0);
 }
 
-#ifndef OPENSSL_NO_FP_API
 int PEM_read(FILE *fp, char **name, char **header, unsigned char **data,
              long *len)
 {
@@ -591,7 +584,6 @@
     BIO_free(b);
     return (ret);
 }
-#endif
 
 int PEM_read_bio(BIO *bp, char **name, char **header, unsigned char **data,
                  long *len)
diff --git a/deps/boringssl/src/crypto/pem/pem_pk8.c b/deps/boringssl/src/crypto/pem/pem_pk8.c
index 819a329..8a1f040 100644
--- a/deps/boringssl/src/crypto/pem/pem_pk8.c
+++ b/deps/boringssl/src/crypto/pem/pem_pk8.c
@@ -190,7 +190,6 @@
     return ret;
 }
 
-#ifndef OPENSSL_NO_FP_API
 
 int i2d_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
                            char *kstr, int klen, pem_password_cb *cb, void *u)
@@ -248,7 +247,6 @@
     return ret;
 }
 
-#endif
 
 IMPLEMENT_PEM_rw(PKCS8, X509_SIG, PEM_STRING_PKCS8, X509_SIG)
 
diff --git a/deps/boringssl/src/crypto/pem/pem_pkey.c b/deps/boringssl/src/crypto/pem/pem_pkey.c
index 5776535..48d8c96 100644
--- a/deps/boringssl/src/crypto/pem/pem_pkey.c
+++ b/deps/boringssl/src/crypto/pem/pem_pkey.c
@@ -150,7 +150,6 @@
     return PEM_write_bio_PKCS8PrivateKey(bp, x, enc, (char *)kstr, klen, cb, u);
 }
 
-#ifndef OPENSSL_NO_FP_API
 EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
                               void *u)
 {
@@ -178,7 +177,6 @@
     return ret;
 }
 
-#endif
 
 /* Transparently read in PKCS#3 or X9.42 DH parameters */
 
@@ -203,7 +201,6 @@
     return ret;
 }
 
-#ifndef OPENSSL_NO_FP_API
 DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u)
 {
     BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
@@ -215,4 +212,3 @@
     BIO_free(b);
     return ret;
 }
-#endif
diff --git a/deps/boringssl/src/crypto/pkcs7/pkcs7_test.cc b/deps/boringssl/src/crypto/pkcs7/pkcs7_test.cc
index 948b44f..8e14603 100644
--- a/deps/boringssl/src/crypto/pkcs7/pkcs7_test.cc
+++ b/deps/boringssl/src/crypto/pkcs7/pkcs7_test.cc
@@ -17,6 +17,7 @@
 #include <openssl/bytestring.h>
 #include <openssl/crypto.h>
 #include <openssl/mem.h>
+#include <openssl/pem.h>
 #include <openssl/pkcs7.h>
 #include <openssl/stack.h>
 #include <openssl/x509.h>
@@ -492,6 +493,9 @@
   ASSERT_TRUE(PKCS7_get_certificates(certs2.get(), &pkcs7));
   EXPECT_EQ(0u, CBS_len(&pkcs7));
 
+  // PKCS#7 stores certificates in a SET OF, so |PKCS7_bundle_certificates| may
+  // not preserve the original order. All of our test inputs are already sorted,
+  // but this check should be relaxed if we add others.
   ASSERT_EQ(sk_X509_num(certs.get()), sk_X509_num(certs2.get()));
   for (size_t i = 0; i < sk_X509_num(certs.get()); i++) {
     X509 *a = sk_X509_value(certs.get(), i);
@@ -574,6 +578,9 @@
   ASSERT_TRUE(PKCS7_get_CRLs(crls2.get(), &pkcs7));
   EXPECT_EQ(0u, CBS_len(&pkcs7));
 
+  // PKCS#7 stores CRLs in a SET OF, so |PKCS7_bundle_CRLs| may not preserve the
+  // original order. All of our test inputs are already sorted, but this check
+  // should be relaxed if we add others.
   ASSERT_EQ(sk_X509_CRL_num(crls.get()), sk_X509_CRL_num(crls.get()));
   for (size_t i = 0; i < sk_X509_CRL_num(crls.get()); i++) {
     X509_CRL *a = sk_X509_CRL_value(crls.get(), i);
@@ -656,3 +663,115 @@
 TEST(PKCS7Test, PEMCRLs) {
   TestPEMCRLs(kPEMCRL);
 }
+
+// Test that we output certificates in the canonical DER order.
+TEST(PKCS7Test, SortCerts) {
+  // kPKCS7NSS contains three certificates in the canonical DER order.
+  CBS pkcs7;
+  CBS_init(&pkcs7, kPKCS7NSS, sizeof(kPKCS7NSS));
+  bssl::UniquePtr<STACK_OF(X509)> certs(sk_X509_new_null());
+  ASSERT_TRUE(certs);
+  ASSERT_TRUE(PKCS7_get_certificates(certs.get(), &pkcs7));
+  ASSERT_EQ(3u, sk_X509_num(certs.get()));
+
+  X509 *cert1 = sk_X509_value(certs.get(), 0);
+  X509 *cert2 = sk_X509_value(certs.get(), 1);
+  X509 *cert3 = sk_X509_value(certs.get(), 2);
+
+  auto check_order = [&](X509 *new_cert1, X509 *new_cert2, X509 *new_cert3) {
+    // Bundle the certificates in the new order.
+    bssl::UniquePtr<STACK_OF(X509)> new_certs(sk_X509_new_null());
+    ASSERT_TRUE(new_certs);
+    ASSERT_TRUE(bssl::PushToStack(new_certs.get(), bssl::UpRef(new_cert1)));
+    ASSERT_TRUE(bssl::PushToStack(new_certs.get(), bssl::UpRef(new_cert2)));
+    ASSERT_TRUE(bssl::PushToStack(new_certs.get(), bssl::UpRef(new_cert3)));
+    bssl::ScopedCBB cbb;
+    ASSERT_TRUE(CBB_init(cbb.get(), sizeof(kPKCS7NSS)));
+    ASSERT_TRUE(PKCS7_bundle_certificates(cbb.get(), new_certs.get()));
+
+    // The bundle should be sorted back to the original order.
+    CBS cbs;
+    CBS_init(&cbs, CBB_data(cbb.get()), CBB_len(cbb.get()));
+    bssl::UniquePtr<STACK_OF(X509)> result(sk_X509_new_null());
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(PKCS7_get_certificates(result.get(), &cbs));
+    ASSERT_EQ(sk_X509_num(certs.get()), sk_X509_num(result.get()));
+    for (size_t i = 0; i < sk_X509_num(certs.get()); i++) {
+      X509 *a = sk_X509_value(certs.get(), i);
+      X509 *b = sk_X509_value(result.get(), i);
+      EXPECT_EQ(0, X509_cmp(a, b));
+    }
+  };
+
+  check_order(cert1, cert2, cert3);
+  check_order(cert3, cert2, cert1);
+  check_order(cert2, cert3, cert1);
+}
+
+// Test that we output CRLs in the canonical DER order.
+TEST(PKCS7Test, SortCRLs) {
+  static const char kCRL1[] = R"(
+-----BEGIN X509 CRL-----
+MIIBpzCBkAIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ
+Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNV
+HRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAnrBKKgvd9x9zwK9rtUvVeFeJ7+LN
+ZEAc+a5oxpPNEsJx6hXoApYEbzXMxuWBQoCs5iEBycSGudct21L+MVf27M38KrWo
+eOkq0a2siqViQZO2Fb/SUFR0k9zb8xl86Zf65lgPplALun0bV/HT7MJcl04Tc4os
+dsAReBs5nqTGNEd5AlC1iKHvQZkM//MD51DspKnDpsDiUVi54h9C1SpfZmX8H2Vv
+diyu0fZ/bPAM3VAGawatf/SyWfBMyKpoPXEG39oAzmjjOj8en82psn7m474IGaho
+/vBbhl1ms5qQiLYPjm4YELtnXQoFyC72tBjbdFd/ZE9k4CNKDbxFUXFbkw==
+-----END X509 CRL-----
+)";
+  static const char kCRL2[] = R"(
+-----BEGIN X509 CRL-----
+MIIBvjCBpwIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ
+Qm9yaW5nU1NMFw0xNjA5MjYxNTEyNDRaFw0xNjEwMjYxNTEyNDRaMBUwEwICEAAX
+DTE2MDkyNjE1MTIyNlqgDjAMMAoGA1UdFAQDAgECMA0GCSqGSIb3DQEBCwUAA4IB
+AQCUGaM4DcWzlQKrcZvI8TMeR8BpsvQeo5BoI/XZu2a8h//PyRyMwYeaOM+3zl0d
+sjgCT8b3C1FPgT+P2Lkowv7rJ+FHJRNQkogr+RuqCSPTq65ha4WKlRGWkMFybzVH
+NloxC+aU3lgp/NlX9yUtfqYmJek1CDrOOGPrAEAwj1l/BUeYKNGqfBWYJQtPJu+5
+OaSvIYGpETCZJscUWODmLEb/O3DM438vLvxonwGqXqS0KX37+CHpUlyhnSovxXxp
+Pz4aF+L7OtczxL0GYtD2fR9B7TDMqsNmHXgQrixvvOY7MUdLGbd4RfJL3yA53hyO
+xzfKY2TzxLiOmctG0hXFkH5J
+-----END X509 CRL-----
+)";
+
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCRL1, strlen(kCRL1)));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<X509_CRL> crl1(
+      PEM_read_bio_X509_CRL(bio.get(), nullptr, nullptr, nullptr));
+  ASSERT_TRUE(crl1);
+  bio.reset(BIO_new_mem_buf(kCRL2, strlen(kCRL2)));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<X509_CRL> crl2(
+      PEM_read_bio_X509_CRL(bio.get(), nullptr, nullptr, nullptr));
+  ASSERT_TRUE(crl2);
+
+  // DER's SET OF ordering sorts by tag, then length, so |crl1| comes before
+  // |crl2|.
+  auto check_order = [&](X509_CRL *new_crl1, X509_CRL *new_crl2) {
+    // Bundle the CRLs in the new order.
+    bssl::UniquePtr<STACK_OF(X509_CRL)> new_crls(sk_X509_CRL_new_null());
+    ASSERT_TRUE(new_crls);
+    ASSERT_TRUE(bssl::PushToStack(new_crls.get(), bssl::UpRef(new_crl1)));
+    ASSERT_TRUE(bssl::PushToStack(new_crls.get(), bssl::UpRef(new_crl2)));
+    bssl::ScopedCBB cbb;
+    ASSERT_TRUE(CBB_init(cbb.get(), 64));
+    ASSERT_TRUE(PKCS7_bundle_CRLs(cbb.get(), new_crls.get()));
+
+    // The bundle should be sorted back to the original order.
+    CBS cbs;
+    CBS_init(&cbs, CBB_data(cbb.get()), CBB_len(cbb.get()));
+    bssl::UniquePtr<STACK_OF(X509_CRL)> result(sk_X509_CRL_new_null());
+    ASSERT_TRUE(result);
+    ASSERT_TRUE(PKCS7_get_CRLs(result.get(), &cbs));
+    ASSERT_EQ(2u, sk_X509_CRL_num(result.get()));
+    EXPECT_EQ(0, X509_CRL_cmp(crl1.get(), sk_X509_CRL_value(result.get(), 0)));
+    EXPECT_EQ(0, X509_CRL_cmp(crl2.get(), sk_X509_CRL_value(result.get(), 1)));
+  };
+
+  check_order(crl1.get(), crl2.get());
+  check_order(crl2.get(), crl1.get());
+}
diff --git a/deps/boringssl/src/crypto/pkcs7/pkcs7_x509.c b/deps/boringssl/src/crypto/pkcs7/pkcs7_x509.c
index 107bdea..3f1526c 100644
--- a/deps/boringssl/src/crypto/pkcs7/pkcs7_x509.c
+++ b/deps/boringssl/src/crypto/pkcs7/pkcs7_x509.c
@@ -192,7 +192,8 @@
     }
   }
 
-  return CBB_flush(out);
+  // |certificates| is a implicitly-tagged SET OF.
+  return CBB_flush_asn1_set_of(&certificates) && CBB_flush(out);
 }
 
 int PKCS7_bundle_certificates(CBB *out, const STACK_OF(X509) *certs) {
@@ -222,7 +223,8 @@
     }
   }
 
-  return CBB_flush(out);
+  // |crl_data| is a implicitly-tagged SET OF.
+  return CBB_flush_asn1_set_of(&crl_data) && CBB_flush(out);
 }
 
 int PKCS7_bundle_CRLs(CBB *out, const STACK_OF(X509_CRL) *crls) {
@@ -235,7 +237,7 @@
     return NULL;
   }
   OPENSSL_memset(ret, 0, sizeof(PKCS7));
-  ret->type = (ASN1_OBJECT *)OBJ_nid2obj(NID_pkcs7_signed);
+  ret->type = OBJ_nid2obj(NID_pkcs7_signed);
   ret->d.sign = OPENSSL_malloc(sizeof(PKCS7_SIGNED));
   if (ret->d.sign == NULL) {
     goto err;
diff --git a/deps/boringssl/src/crypto/pkcs8/pkcs12_test.cc b/deps/boringssl/src/crypto/pkcs8/pkcs12_test.cc
index d345006..e67630d 100644
--- a/deps/boringssl/src/crypto/pkcs8/pkcs12_test.cc
+++ b/deps/boringssl/src/crypto/pkcs8/pkcs12_test.cc
@@ -28,1457 +28,19 @@
 #include "../test/test_util.h"
 
 
-// kPKCS12DER contains sample PKCS#12 data generated by OpenSSL with:
-// openssl pkcs12 -export -inkey key.pem -in cacert.pem
-static const uint8_t kOpenSSL[] = {
-    0x30, 0x82, 0x09, 0xa1, 0x02, 0x01, 0x03, 0x30, 0x82, 0x09, 0x67, 0x06,
-    0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
-    0x09, 0x58, 0x04, 0x82, 0x09, 0x54, 0x30, 0x82, 0x09, 0x50, 0x30, 0x82,
-    0x04, 0x07, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-    0x06, 0xa0, 0x82, 0x03, 0xf8, 0x30, 0x82, 0x03, 0xf4, 0x02, 0x01, 0x00,
-    0x30, 0x82, 0x03, 0xed, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x07, 0x01, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x0c, 0x01, 0x06, 0x30, 0x0e, 0x04, 0x08, 0x31, 0x24, 0xca,
-    0x7d, 0xc3, 0x25, 0x3e, 0xdc, 0x02, 0x02, 0x08, 0x00, 0x80, 0x82, 0x03,
-    0xc0, 0x55, 0xe7, 0x7f, 0x9c, 0xd6, 0x0c, 0xd2, 0x69, 0x1d, 0x6e, 0x8b,
-    0xb8, 0x07, 0xec, 0x4a, 0xe7, 0x06, 0x67, 0xd1, 0x24, 0x1b, 0xd5, 0x68,
-    0x13, 0x3d, 0xd7, 0x56, 0x5e, 0x15, 0x40, 0xdb, 0xda, 0x88, 0x36, 0xc9,
-    0x02, 0x96, 0xb5, 0xb5, 0xf7, 0x81, 0xef, 0x88, 0x1d, 0x66, 0x62, 0xa8,
-    0x83, 0xf7, 0x91, 0xb1, 0x26, 0x1f, 0x9b, 0x25, 0x78, 0x0a, 0x04, 0xb1,
-    0xc0, 0x93, 0x48, 0xa2, 0xf0, 0x51, 0x4f, 0x2b, 0xf8, 0x03, 0x67, 0x61,
-    0x1b, 0xed, 0x29, 0xfe, 0x3f, 0xdd, 0x83, 0xa3, 0x93, 0x75, 0xa7, 0xd9,
-    0x37, 0x5b, 0xa7, 0xc9, 0xf4, 0x52, 0x86, 0xd2, 0x3f, 0xca, 0x61, 0x5c,
-    0x1e, 0xf9, 0x07, 0x7d, 0xbd, 0xda, 0x76, 0x8a, 0x03, 0x8e, 0x12, 0x4e,
-    0x8f, 0x68, 0x6e, 0x72, 0x6e, 0xf0, 0xbe, 0x22, 0xc7, 0x9d, 0x97, 0x7c,
-    0x45, 0xc0, 0xaa, 0x31, 0xe1, 0x55, 0x81, 0xb3, 0xec, 0x98, 0x94, 0xac,
-    0xf7, 0x15, 0x9b, 0x42, 0x49, 0x8c, 0x2a, 0x29, 0x7a, 0x25, 0x92, 0x64,
-    0x92, 0xbd, 0x4e, 0x5c, 0xec, 0xff, 0x61, 0xbb, 0x8e, 0x5c, 0xc8, 0xdb,
-    0xba, 0x97, 0x30, 0xf4, 0x55, 0x9e, 0x1b, 0xfa, 0xbe, 0x2a, 0x90, 0xcf,
-    0xe8, 0xc0, 0x9d, 0xb0, 0x0e, 0x24, 0x61, 0xe7, 0x3a, 0xb7, 0x7f, 0xda,
-    0x63, 0xaa, 0x2a, 0x4a, 0xa6, 0x91, 0x52, 0xa6, 0x76, 0xc9, 0xbe, 0x9f,
-    0x1b, 0x1d, 0xa4, 0x09, 0x5b, 0x0f, 0xd1, 0x64, 0x4e, 0xdf, 0x0c, 0x44,
-    0x59, 0x3a, 0xef, 0x9a, 0xd8, 0x22, 0xa2, 0x5f, 0x80, 0xb5, 0x4f, 0xbe,
-    0x84, 0x23, 0xe3, 0x74, 0x77, 0x3c, 0x9e, 0x27, 0x64, 0xac, 0x65, 0xf4,
-    0xbb, 0x34, 0xb7, 0xa4, 0xfe, 0x02, 0x1a, 0x88, 0x05, 0x3b, 0x4b, 0xb8,
-    0xd8, 0xb9, 0x26, 0x69, 0x22, 0x97, 0x3d, 0x93, 0x9b, 0xe8, 0x72, 0xaa,
-    0x4d, 0x8f, 0x76, 0x51, 0x12, 0x59, 0x58, 0xf1, 0x1a, 0xa3, 0xdb, 0x5d,
-    0xbc, 0xea, 0x84, 0x19, 0x55, 0x4f, 0x00, 0xfb, 0xe2, 0x57, 0x47, 0xca,
-    0xea, 0xbe, 0x8f, 0x85, 0x8b, 0x1c, 0x27, 0x8d, 0x81, 0x70, 0x7f, 0xf1,
-    0x56, 0x58, 0xe1, 0x26, 0x94, 0xd8, 0x2f, 0xde, 0xac, 0xc8, 0xac, 0xbf,
-    0xc3, 0xc6, 0x67, 0xa6, 0xf4, 0x6c, 0xec, 0x20, 0x3c, 0xbc, 0x9d, 0xd9,
-    0xd0, 0xa1, 0x4e, 0x8c, 0x11, 0x19, 0x2b, 0xb3, 0xa1, 0xdf, 0x6a, 0x8f,
-    0xa2, 0xc3, 0xcc, 0xf6, 0xbd, 0x09, 0x7a, 0x96, 0x61, 0x20, 0xd4, 0x06,
-    0x99, 0x4c, 0x6f, 0x23, 0x9b, 0x4c, 0xcc, 0x73, 0x8b, 0x42, 0x48, 0x99,
-    0x45, 0x8f, 0xcb, 0xc8, 0x46, 0x1a, 0xfb, 0x51, 0x03, 0x6a, 0xf2, 0x22,
-    0x85, 0x88, 0x9d, 0x61, 0x8b, 0x16, 0x33, 0xf4, 0xf7, 0x9b, 0xc8, 0x21,
-    0x4f, 0xb1, 0xcd, 0x30, 0xfc, 0x29, 0x88, 0x12, 0xdc, 0xd4, 0x30, 0x4c,
-    0xb9, 0xad, 0x34, 0xde, 0x01, 0xf8, 0xc1, 0x12, 0xa7, 0x4d, 0xc7, 0x31,
-    0x99, 0x2b, 0x45, 0x88, 0x06, 0x34, 0x69, 0x6e, 0x6d, 0x34, 0xd8, 0xdd,
-    0x0a, 0x3d, 0x59, 0x74, 0x36, 0x31, 0x6a, 0xed, 0x91, 0x3b, 0x5b, 0x88,
-    0x43, 0x46, 0x3f, 0x67, 0x66, 0xe4, 0xde, 0x52, 0xb4, 0xbf, 0x7b, 0x3d,
-    0x54, 0x79, 0xaf, 0x8d, 0xf5, 0x0a, 0x80, 0xfd, 0xeb, 0x31, 0x24, 0xbc,
-    0x24, 0xd7, 0x21, 0x9f, 0x87, 0xab, 0xbd, 0x75, 0x2c, 0x13, 0x13, 0x96,
-    0xab, 0x76, 0xfb, 0xb2, 0x44, 0xd0, 0xd2, 0x19, 0xf1, 0x95, 0x9a, 0x91,
-    0xbf, 0x7a, 0x7b, 0x76, 0x95, 0x72, 0xa9, 0x16, 0xfc, 0x3e, 0xa9, 0x4e,
-    0x01, 0x15, 0x3d, 0x43, 0x73, 0xa3, 0x8b, 0xef, 0x48, 0xad, 0x11, 0xbd,
-    0x53, 0xd3, 0x0c, 0x15, 0x15, 0x1a, 0xb4, 0x3a, 0xe0, 0x7f, 0x9a, 0xa1,
-    0x36, 0x47, 0x72, 0x92, 0xf0, 0xdf, 0xb0, 0xe2, 0xbc, 0x35, 0xd4, 0x32,
-    0x6b, 0x37, 0x69, 0x4f, 0x47, 0x9a, 0xe2, 0x35, 0x8a, 0x31, 0x60, 0xed,
-    0x80, 0x57, 0xe2, 0x9d, 0x58, 0x9c, 0x7f, 0x46, 0xd2, 0x54, 0x0e, 0x28,
-    0x53, 0x8b, 0x1f, 0x46, 0x34, 0x22, 0xac, 0x71, 0xc7, 0xca, 0x0f, 0xb4,
-    0xb7, 0x7a, 0xfc, 0x34, 0x57, 0xa5, 0x86, 0x8d, 0x66, 0x5c, 0xc7, 0x3a,
-    0xdb, 0xf8, 0x79, 0x3a, 0x8a, 0xf6, 0xa2, 0x1e, 0x09, 0xc9, 0x10, 0xe9,
-    0x93, 0x3a, 0xc5, 0xed, 0xb2, 0xca, 0xbb, 0x66, 0xf1, 0x9d, 0xc9, 0x9c,
-    0x42, 0x75, 0x64, 0x3e, 0xe4, 0x12, 0x2b, 0x67, 0xf8, 0xbf, 0x2b, 0x98,
-    0x5d, 0xb6, 0xa0, 0xba, 0x79, 0x98, 0xe0, 0x47, 0x5c, 0x77, 0x85, 0x4e,
-    0x26, 0x71, 0xfe, 0xab, 0x5c, 0xa8, 0x32, 0x93, 0xec, 0xd0, 0x26, 0x90,
-    0xe4, 0xda, 0x2f, 0x34, 0x8a, 0x50, 0xb8, 0x3b, 0x7b, 0x4c, 0x5f, 0xa9,
-    0x3e, 0x8a, 0xa8, 0xf3, 0xc0, 0xb7, 0x50, 0x0b, 0x77, 0x4e, 0x8c, 0xa0,
-    0xaf, 0xdb, 0x59, 0xe7, 0xac, 0xd1, 0x34, 0x4e, 0x62, 0x47, 0x2e, 0x1e,
-    0x5e, 0xb4, 0xc9, 0x64, 0xf8, 0x0f, 0xf4, 0xf8, 0xb6, 0x9a, 0xe3, 0x7e,
-    0xcf, 0xb7, 0xee, 0x11, 0x14, 0x52, 0x89, 0x3b, 0x27, 0x98, 0xfc, 0x95,
-    0xa7, 0xad, 0xbf, 0x61, 0x34, 0xad, 0x1a, 0x24, 0x2a, 0x48, 0x66, 0x65,
-    0x75, 0x9c, 0x59, 0xc0, 0x4f, 0x5f, 0x3d, 0x5a, 0x8c, 0xee, 0xd0, 0xb1,
-    0x17, 0x6d, 0x34, 0x46, 0x37, 0xa0, 0xba, 0x71, 0xac, 0x77, 0x73, 0x29,
-    0xa3, 0x37, 0x4f, 0x02, 0xd3, 0x7f, 0x0e, 0xe8, 0xce, 0xff, 0x80, 0x11,
-    0x45, 0x42, 0x03, 0x5a, 0x87, 0xaa, 0xff, 0x25, 0x12, 0x1f, 0x43, 0x19,
-    0x3e, 0xa9, 0x62, 0x96, 0x0c, 0x6f, 0x33, 0x88, 0x5c, 0xaa, 0xf9, 0xe2,
-    0xb4, 0xb9, 0xf7, 0x55, 0xae, 0xb5, 0x76, 0x57, 0x47, 0x83, 0xe3, 0xfa,
-    0x05, 0xda, 0x86, 0x02, 0x97, 0xb4, 0x60, 0xae, 0x59, 0xd5, 0x6c, 0xc1,
-    0x33, 0xe1, 0x36, 0x36, 0x94, 0x79, 0x9e, 0xad, 0xa3, 0x2d, 0xbc, 0xb5,
-    0xa2, 0xeb, 0xdd, 0xcd, 0xcb, 0x48, 0x42, 0x15, 0xb8, 0xe6, 0x0e, 0x76,
-    0x5b, 0x57, 0x74, 0x24, 0xe6, 0x89, 0xc4, 0xe8, 0x08, 0xa9, 0xfe, 0xb3,
-    0x23, 0xa6, 0xca, 0x72, 0xe2, 0xe4, 0xcb, 0xc1, 0x4a, 0xd1, 0x1d, 0xb9,
-    0x5e, 0x36, 0x97, 0x19, 0x7c, 0x15, 0x48, 0xf1, 0x2d, 0xeb, 0xec, 0xad,
-    0x52, 0x6f, 0x2f, 0xe1, 0x19, 0xcf, 0xcf, 0x98, 0x13, 0x0d, 0xcc, 0xb2,
-    0xa6, 0x8a, 0xda, 0x93, 0x24, 0x3d, 0x5d, 0x83, 0xfe, 0x8d, 0x9e, 0x47,
-    0xd8, 0x6e, 0x8d, 0x06, 0x52, 0x7d, 0x46, 0x84, 0x04, 0x69, 0x34, 0x61,
-    0x04, 0x50, 0x1f, 0x86, 0x92, 0x94, 0xe9, 0x0b, 0x13, 0x5b, 0xf6, 0x16,
-    0x81, 0xeb, 0xfa, 0xf1, 0xbb, 0x04, 0x68, 0x17, 0xca, 0x35, 0x6f, 0xba,
-    0x4e, 0x4c, 0x33, 0xce, 0xf4, 0x26, 0xb7, 0x74, 0xab, 0xa5, 0xd0, 0xaa,
-    0x0d, 0x85, 0x11, 0x30, 0x58, 0x62, 0xdf, 0x48, 0xc7, 0xdf, 0xc9, 0x38,
-    0x9e, 0x6f, 0x96, 0x23, 0x2f, 0xc1, 0xd4, 0x8d, 0x65, 0x9b, 0x46, 0x5f,
-    0x9c, 0xea, 0x26, 0x60, 0xb5, 0x95, 0x85, 0x71, 0x18, 0xc3, 0xf4, 0x54,
-    0x61, 0xca, 0xfe, 0x55, 0x3b, 0xbe, 0x81, 0xaf, 0xd9, 0x3a, 0x27, 0xe9,
-    0x1c, 0x30, 0x82, 0x05, 0x41, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82, 0x05, 0x32, 0x04, 0x82, 0x05, 0x2e,
-    0x30, 0x82, 0x05, 0x2a, 0x30, 0x82, 0x05, 0x26, 0x06, 0x0b, 0x2a, 0x86,
-    0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02, 0xa0, 0x82, 0x04,
-    0xee, 0x30, 0x82, 0x04, 0xea, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, 0x48,
-    0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0e, 0x04, 0x08, 0xd9,
-    0x68, 0xcb, 0x08, 0x16, 0xc8, 0x93, 0x57, 0x02, 0x02, 0x08, 0x00, 0x04,
-    0x82, 0x04, 0xc8, 0x7c, 0xdb, 0xa6, 0x1e, 0x33, 0xa4, 0xc6, 0x4e, 0x13,
-    0x22, 0x7a, 0x1f, 0xc6, 0x82, 0xab, 0x93, 0x5f, 0xf0, 0xa4, 0xe4, 0x40,
-    0xac, 0xdf, 0x16, 0xec, 0x8d, 0x1f, 0xd9, 0xe4, 0x03, 0xd6, 0xc9, 0xc4,
-    0x1d, 0xfd, 0xa3, 0xe3, 0xba, 0xfc, 0xcb, 0xd0, 0x47, 0x65, 0x0c, 0x6e,
-    0x5d, 0xfc, 0xd2, 0xd4, 0x63, 0xa7, 0x93, 0xf6, 0x8a, 0x44, 0x8c, 0xfe,
-    0x84, 0xd8, 0x0d, 0xa6, 0x16, 0x22, 0xe1, 0x65, 0x10, 0x5e, 0x18, 0x44,
-    0x58, 0x2f, 0xc7, 0x64, 0x74, 0x5f, 0xcf, 0x73, 0x34, 0xe1, 0x4b, 0xe4,
-    0xb3, 0x5b, 0xdb, 0x81, 0x4b, 0x1c, 0x38, 0x72, 0xa6, 0xc5, 0xeb, 0x56,
-    0x9b, 0xc7, 0xe3, 0x3d, 0x54, 0x6e, 0x05, 0x2c, 0xd3, 0x57, 0xc9, 0x4f,
-    0x80, 0x1e, 0xd7, 0xd8, 0x26, 0x6a, 0xcb, 0x79, 0x46, 0x70, 0xfc, 0x45,
-    0xa7, 0x79, 0xab, 0x01, 0x03, 0xb6, 0xb1, 0x44, 0x41, 0xd9, 0x73, 0x37,
-    0xaa, 0xd7, 0xf9, 0x44, 0x93, 0xaf, 0xbb, 0xb5, 0x77, 0xeb, 0x2b, 0x20,
-    0x2e, 0xbd, 0xea, 0x2f, 0xde, 0xa6, 0x2f, 0xd6, 0xac, 0x74, 0xa5, 0x34,
-    0xfb, 0xdf, 0xf7, 0x02, 0xa2, 0x20, 0x15, 0xc8, 0x61, 0x72, 0xbb, 0x7f,
-    0x04, 0xf6, 0x0f, 0xf8, 0x7e, 0xc3, 0xe6, 0xab, 0x2a, 0xe6, 0xd8, 0xe1,
-    0x0d, 0x5a, 0x3c, 0xc0, 0x58, 0xae, 0xf8, 0x1b, 0x15, 0x3c, 0x7b, 0x7f,
-    0xf5, 0x9f, 0xec, 0xf7, 0x3f, 0x30, 0x4f, 0x3d, 0x6c, 0x44, 0xdd, 0x0e,
-    0x4c, 0x2c, 0x93, 0x68, 0x43, 0x31, 0xa8, 0x97, 0x4b, 0xf6, 0x66, 0x71,
-    0x2a, 0x52, 0x3e, 0x3a, 0xe6, 0x72, 0x8a, 0xe6, 0xe3, 0xc8, 0xff, 0x65,
-    0x68, 0x1a, 0x46, 0x21, 0xb3, 0xf0, 0x46, 0x7c, 0x0c, 0x65, 0xd1, 0x8e,
-    0xa4, 0x91, 0x11, 0x5c, 0x93, 0xeb, 0xeb, 0xae, 0x46, 0xf4, 0xbb, 0xf8,
-    0xf3, 0x7e, 0x20, 0x30, 0xf8, 0xcd, 0x19, 0xcd, 0x54, 0x0a, 0x7f, 0x4f,
-    0xe8, 0xac, 0xa9, 0xac, 0x72, 0x96, 0x80, 0x45, 0x2a, 0x4a, 0x63, 0x90,
-    0x01, 0x19, 0xd0, 0x7e, 0x26, 0x53, 0x2d, 0xc4, 0x20, 0xa5, 0x1f, 0x89,
-    0x67, 0x0f, 0xd9, 0x75, 0x51, 0x0a, 0xf1, 0xd4, 0xfd, 0x2e, 0xbe, 0xe6,
-    0x94, 0x3b, 0x6c, 0x8c, 0xe3, 0x0f, 0x5f, 0xce, 0x58, 0x48, 0xde, 0x8d,
-    0xeb, 0xd3, 0xe1, 0x0a, 0xcd, 0xdf, 0x34, 0x4d, 0xd1, 0x5b, 0xab, 0x41,
-    0x41, 0x6b, 0xeb, 0xa1, 0x2f, 0x01, 0x4a, 0x72, 0x2e, 0xf4, 0x5e, 0x44,
-    0x76, 0xc7, 0xe6, 0x16, 0xb9, 0xfb, 0x10, 0x37, 0x00, 0x2d, 0xc6, 0x3b,
-    0x17, 0x72, 0x21, 0xdb, 0xac, 0x86, 0x7b, 0xf5, 0x70, 0x3f, 0x73, 0xa3,
-    0xce, 0x0e, 0x20, 0xbb, 0x59, 0x4c, 0x23, 0xc2, 0xe8, 0x22, 0x22, 0xe0,
-    0x02, 0x0d, 0xe4, 0xa2, 0x3f, 0x55, 0x9d, 0xc0, 0xeb, 0x9a, 0xc4, 0xf3,
-    0xaa, 0xb8, 0xf1, 0x73, 0xec, 0x47, 0xe8, 0x2d, 0x6b, 0xa1, 0x40, 0x94,
-    0xf6, 0x07, 0xb9, 0x6f, 0x03, 0x5a, 0x78, 0xe5, 0x59, 0x41, 0x1a, 0xc7,
-    0xcd, 0x43, 0x10, 0x20, 0x28, 0x95, 0xe0, 0x2a, 0x6f, 0xf2, 0xf8, 0x12,
-    0xd6, 0x13, 0x7f, 0x37, 0x3d, 0x38, 0xa7, 0x22, 0x91, 0xc6, 0xe3, 0x52,
-    0xde, 0xd8, 0xbf, 0x78, 0x9a, 0xa4, 0xf7, 0xc0, 0x8c, 0xbf, 0x81, 0x28,
-    0x20, 0xb8, 0x01, 0xde, 0xb5, 0x6b, 0x0a, 0x56, 0x12, 0x5c, 0x62, 0x1d,
-    0xaf, 0xb7, 0xf2, 0x74, 0x66, 0x0a, 0x7a, 0xc4, 0x9f, 0x1e, 0xc2, 0xa8,
-    0x4c, 0xd6, 0x76, 0x6d, 0x74, 0x35, 0x37, 0x12, 0x5c, 0x95, 0xee, 0x98,
-    0x1d, 0xe2, 0x91, 0xde, 0x13, 0x08, 0xd0, 0x59, 0x4d, 0x62, 0x92, 0x69,
-    0x1b, 0xf7, 0x21, 0x45, 0xaf, 0x83, 0xf8, 0x64, 0xf0, 0xfb, 0x92, 0x9d,
-    0xa1, 0xd9, 0x61, 0x5e, 0x00, 0xc8, 0x1a, 0x6e, 0x6a, 0x2d, 0xad, 0xa8,
-    0x1b, 0x0e, 0xaf, 0xea, 0xb2, 0xae, 0x1c, 0x89, 0xc7, 0x4d, 0x2c, 0x0f,
-    0x4d, 0x8d, 0x78, 0x8d, 0x15, 0x9d, 0x4c, 0x90, 0x52, 0xa1, 0xa9, 0xd8,
-    0xb2, 0x66, 0xb9, 0xb1, 0x46, 0x0a, 0x69, 0x86, 0x2b, 0x0f, 0xb2, 0x41,
-    0xce, 0xe8, 0x8e, 0x49, 0x97, 0x08, 0x0b, 0x70, 0x97, 0xcb, 0xa4, 0x33,
-    0x3f, 0x83, 0x6b, 0x6c, 0x17, 0xce, 0xd8, 0xd5, 0x9b, 0xd4, 0x55, 0x9b,
-    0x99, 0xe1, 0xba, 0x61, 0x31, 0x36, 0x79, 0x31, 0x5f, 0xa1, 0x8c, 0xa9,
-    0x77, 0x42, 0xaa, 0x8c, 0x45, 0x6e, 0xb6, 0x90, 0x08, 0xe8, 0x2e, 0xc4,
-    0x72, 0x69, 0x42, 0xca, 0xa2, 0xd4, 0x8a, 0x2c, 0x37, 0xe1, 0xde, 0xb8,
-    0x98, 0x36, 0xeb, 0xcc, 0x58, 0x0c, 0x24, 0xad, 0xab, 0x62, 0x44, 0x6d,
-    0x80, 0xd5, 0xce, 0x2e, 0x4a, 0x3e, 0xa5, 0xc5, 0x34, 0xf8, 0x32, 0x26,
-    0x2a, 0x56, 0xa4, 0xdd, 0xe9, 0x92, 0x06, 0xad, 0xe8, 0x85, 0x77, 0x6b,
-    0xf1, 0x1b, 0xeb, 0xac, 0x77, 0x19, 0x1c, 0x6a, 0xb7, 0xef, 0x28, 0x70,
-    0x87, 0x92, 0x33, 0xdd, 0xaa, 0x30, 0xc1, 0xa0, 0x93, 0x64, 0x18, 0xa2,
-    0x91, 0x7f, 0xf7, 0xc4, 0xa5, 0x16, 0x93, 0xb3, 0x5b, 0xd8, 0x53, 0x28,
-    0xc5, 0x5e, 0xb1, 0xce, 0x97, 0xbc, 0xb6, 0x65, 0xa8, 0x53, 0xcd, 0xf4,
-    0x4d, 0x6b, 0xea, 0x6f, 0x6f, 0xa5, 0x1c, 0xf1, 0x0f, 0xcb, 0x04, 0x25,
-    0x4a, 0xfe, 0x7d, 0xfc, 0xa3, 0xbd, 0x41, 0xd3, 0x96, 0x6a, 0x8b, 0xad,
-    0xd4, 0xaa, 0x0a, 0x76, 0xea, 0x3b, 0xab, 0x39, 0x55, 0xa3, 0x89, 0x9f,
-    0xf6, 0xf5, 0x9b, 0x9c, 0x83, 0xf8, 0x28, 0x50, 0xdf, 0x31, 0x74, 0x83,
-    0xdb, 0xf1, 0x0f, 0x4c, 0x35, 0x6a, 0xe5, 0x64, 0x2e, 0xb9, 0x77, 0x3d,
-    0xdd, 0xff, 0xa3, 0xa7, 0x90, 0x79, 0xc6, 0x5b, 0x01, 0x16, 0x38, 0xa8,
-    0x22, 0xa3, 0x14, 0x13, 0xed, 0xd0, 0x89, 0x0d, 0x1f, 0x3a, 0x41, 0x4c,
-    0x57, 0x79, 0xfc, 0x1d, 0xdf, 0xad, 0x1a, 0x11, 0x15, 0x31, 0x7e, 0xdb,
-    0x99, 0x3a, 0x6c, 0xde, 0x94, 0x9a, 0x45, 0x4c, 0xfb, 0xa5, 0xa5, 0x31,
-    0xee, 0xe3, 0x09, 0x13, 0x6d, 0xfd, 0x19, 0x37, 0x3f, 0xf6, 0xed, 0x8f,
-    0x0c, 0xce, 0x4b, 0xd1, 0xe1, 0x3d, 0xfb, 0x85, 0x00, 0x84, 0x19, 0xeb,
-    0xa2, 0x63, 0x1d, 0x2b, 0x2d, 0x21, 0xee, 0x08, 0x5a, 0x6d, 0xb0, 0xb1,
-    0xd6, 0x81, 0x00, 0xb6, 0xd0, 0x09, 0x90, 0xb4, 0x84, 0x17, 0xd9, 0x2a,
-    0x3c, 0x1d, 0x53, 0xc6, 0xc1, 0x8b, 0xda, 0xae, 0x0c, 0x0a, 0x3e, 0x1c,
-    0x8a, 0xc4, 0xd6, 0x97, 0x5d, 0x48, 0xe7, 0x79, 0x80, 0x78, 0xaa, 0xde,
-    0x17, 0x60, 0x5d, 0x28, 0x15, 0x3a, 0x42, 0xb7, 0x85, 0xc8, 0x60, 0x93,
-    0x28, 0xb0, 0x4e, 0xc9, 0xf7, 0x46, 0xe7, 0xfc, 0x4e, 0x9f, 0x9f, 0x12,
-    0xdf, 0xcb, 0x6e, 0x0c, 0xaf, 0x71, 0xda, 0xb7, 0xec, 0x3d, 0x46, 0xf3,
-    0x35, 0x41, 0x42, 0xd8, 0x27, 0x92, 0x99, 0x1c, 0x4d, 0xc9, 0x3c, 0xe9,
-    0x0e, 0xcb, 0x3f, 0x57, 0x65, 0x77, 0x0d, 0xdd, 0xff, 0xea, 0x70, 0x35,
-    0xcc, 0xf5, 0x38, 0x1b, 0x57, 0xdf, 0x6d, 0xcb, 0xfd, 0x13, 0x39, 0xd6,
-    0x04, 0xe2, 0xf1, 0xc2, 0xd9, 0xea, 0x8c, 0x9f, 0xfb, 0xb5, 0xfc, 0xe6,
-    0xa9, 0xaa, 0x0f, 0x43, 0xc9, 0x9c, 0x91, 0xe4, 0x21, 0xaf, 0x37, 0x14,
-    0x78, 0x46, 0xe1, 0x29, 0x41, 0x0c, 0x4e, 0xf5, 0x93, 0x1d, 0xf8, 0x33,
-    0x47, 0x6f, 0x9d, 0x8b, 0xf3, 0x27, 0xd4, 0xbb, 0xf6, 0xae, 0xfa, 0xa5,
-    0x8b, 0x41, 0x8f, 0xb4, 0xd7, 0x2f, 0xc1, 0x27, 0xea, 0x70, 0x55, 0x1d,
-    0xe2, 0xd8, 0x0c, 0x4a, 0x5e, 0x7c, 0x87, 0xa4, 0x0e, 0x84, 0x07, 0xd3,
-    0x38, 0x67, 0x2c, 0x55, 0x11, 0xfd, 0x1e, 0xda, 0x4d, 0x66, 0x01, 0x12,
-    0x0c, 0x1b, 0x7c, 0x7c, 0x5c, 0x82, 0x21, 0x35, 0x65, 0x5c, 0x7a, 0xd2,
-    0x66, 0xc2, 0x2b, 0x5e, 0xb8, 0xb1, 0xcb, 0xdf, 0x59, 0xc9, 0x31, 0xb7,
-    0x17, 0x26, 0x96, 0x5e, 0x6f, 0x1c, 0x62, 0x3d, 0x8d, 0x88, 0xf1, 0xd1,
-    0x01, 0x3e, 0xf9, 0x6f, 0xb9, 0x77, 0xdc, 0xee, 0xee, 0x78, 0x59, 0xef,
-    0xcf, 0x3a, 0x87, 0x88, 0xa2, 0xea, 0xfd, 0x0a, 0xa9, 0xa9, 0x3e, 0x0c,
-    0xf8, 0x7f, 0x97, 0x32, 0x17, 0xc2, 0x97, 0xcb, 0xa4, 0x9b, 0xae, 0x5d,
-    0xe7, 0x39, 0x2b, 0x2b, 0xa8, 0xe6, 0x7b, 0x51, 0x75, 0x1f, 0x53, 0x54,
-    0x37, 0xf4, 0x00, 0xa4, 0xb0, 0xa0, 0x93, 0xb4, 0x33, 0xe7, 0xae, 0x28,
-    0xc0, 0x2d, 0x3a, 0xb3, 0xaa, 0xd7, 0x3c, 0x76, 0x44, 0x4b, 0xbb, 0x6a,
-    0x67, 0x98, 0xce, 0xf8, 0x15, 0x13, 0x67, 0x79, 0x3c, 0x15, 0x09, 0xb7,
-    0x22, 0xc0, 0xec, 0x07, 0x8a, 0xfd, 0x44, 0xcb, 0x99, 0xbd, 0xdc, 0xd5,
-    0x53, 0x4c, 0x97, 0x1b, 0x46, 0xaf, 0xc0, 0x6c, 0x06, 0x01, 0x93, 0x8a,
-    0x50, 0x51, 0x6a, 0xe4, 0x5c, 0x0a, 0x52, 0x81, 0x3b, 0x75, 0xed, 0xa2,
-    0x97, 0xa6, 0x5c, 0x55, 0x63, 0xee, 0xfb, 0x33, 0x82, 0x10, 0xa8, 0x21,
-    0x1a, 0x8d, 0xc8, 0xe1, 0x52, 0x68, 0x38, 0x88, 0x2f, 0xae, 0x2b, 0x22,
-    0x7a, 0x9b, 0x0c, 0x19, 0x73, 0x6f, 0x91, 0xc7, 0xfa, 0x95, 0x61, 0x28,
-    0x74, 0x73, 0x70, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48,
-    0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14, 0x14, 0x74,
-    0x2d, 0x52, 0x8e, 0x0d, 0x0c, 0x06, 0x6c, 0x32, 0x64, 0xd3, 0x7e, 0x33,
-    0x31, 0x68, 0x8b, 0x28, 0x1a, 0x75, 0x30, 0x31, 0x30, 0x21, 0x30, 0x09,
-    0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x22,
-    0x8e, 0xff, 0x5a, 0x78, 0xec, 0x2c, 0x21, 0xa2, 0x48, 0xb7, 0x63, 0x88,
-    0x10, 0x47, 0x1c, 0xc0, 0xd3, 0xec, 0x5a, 0x04, 0x08, 0xb3, 0x2e, 0x21,
-    0xfd, 0x82, 0x14, 0xd8, 0x5c, 0x02, 0x02, 0x08, 0x00,
-};
+std::string GetTestData(const char *path);
 
-// kNSS is the result of importing the OpenSSL example PKCS#12 into Chrome and
-// then exporting it again.
-static const uint8_t kNSS[] = {
-    0x30, 0x80, 0x02, 0x01, 0x03, 0x30, 0x80, 0x06, 0x09, 0x2a, 0x86, 0x48,
-    0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x80, 0x24, 0x80, 0x04, 0x82,
-    0x09, 0xef, 0x30, 0x80, 0x30, 0x80, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
-    0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x80, 0x24, 0x80, 0x04, 0x82, 0x05,
-    0x77, 0x30, 0x82, 0x05, 0x73, 0x30, 0x82, 0x05, 0x6f, 0x06, 0x0b, 0x2a,
-    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02, 0xa0, 0x82,
-    0x04, 0xf6, 0x30, 0x82, 0x04, 0xf2, 0x30, 0x24, 0x06, 0x0a, 0x2a, 0x86,
-    0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x16, 0x04, 0x10,
-    0xac, 0x71, 0x8a, 0x7c, 0x89, 0xcf, 0xa8, 0xb0, 0xd6, 0xd1, 0x07, 0xf0,
-    0x83, 0x4f, 0x7a, 0xd0, 0x02, 0x02, 0x07, 0xd0, 0x04, 0x82, 0x04, 0xc8,
-    0xea, 0x51, 0x2c, 0x61, 0xaa, 0x9d, 0xf3, 0x90, 0xe1, 0x38, 0x45, 0xb0,
-    0x5f, 0xfd, 0xe2, 0x04, 0x65, 0xe6, 0xff, 0x87, 0xb6, 0x78, 0x69, 0xb0,
-    0xcb, 0x14, 0xe9, 0x99, 0x39, 0xe3, 0xe5, 0x70, 0x84, 0x57, 0x68, 0xf7,
-    0x28, 0xb9, 0x75, 0xa6, 0xfb, 0x16, 0x72, 0xe1, 0x34, 0xb8, 0x3b, 0x61,
-    0x51, 0x89, 0x18, 0x94, 0x40, 0xef, 0x73, 0xda, 0xdb, 0xd7, 0xb7, 0x44,
-    0x73, 0x8f, 0x16, 0x84, 0xa2, 0x99, 0xa6, 0x05, 0x5e, 0x74, 0xae, 0xe2,
-    0xcf, 0x3e, 0x99, 0xca, 0xcd, 0x76, 0x36, 0x77, 0x59, 0xec, 0x25, 0x59,
-    0x3d, 0x4b, 0x45, 0xa5, 0x4e, 0x7b, 0x7a, 0xc9, 0x8b, 0xde, 0x4f, 0x70,
-    0x6d, 0xb1, 0xa8, 0xf3, 0xb6, 0xb5, 0xe7, 0x67, 0x3f, 0xe9, 0x64, 0xb8,
-    0x49, 0xf4, 0x11, 0x94, 0x9d, 0x1c, 0xb0, 0xa5, 0xfb, 0xb3, 0x61, 0xd4,
-    0xf3, 0xa7, 0x68, 0x66, 0xd7, 0xa4, 0xf0, 0xcd, 0xc8, 0x40, 0x4f, 0x3e,
-    0xa7, 0x26, 0x40, 0x76, 0x64, 0xa1, 0x4e, 0xf1, 0x91, 0xc2, 0xa3, 0xef,
-    0xbc, 0xcd, 0x42, 0xe5, 0xd2, 0x6f, 0xff, 0xfe, 0x4d, 0x33, 0x01, 0xb4,
-    0x99, 0x63, 0x1b, 0xd3, 0x01, 0x55, 0x00, 0xa6, 0x23, 0x9b, 0xa9, 0x17,
-    0x09, 0x38, 0x32, 0x18, 0x36, 0xbc, 0x20, 0x02, 0xfe, 0x7b, 0xec, 0xd3,
-    0x4c, 0x7d, 0xc9, 0xc9, 0xce, 0x66, 0x3b, 0x34, 0x6e, 0xea, 0xf9, 0xb1,
-    0x1a, 0x83, 0xa3, 0x3c, 0x8d, 0xc7, 0x79, 0xc9, 0xff, 0x6b, 0x1d, 0x35,
-    0xf6, 0x2a, 0x3d, 0x3b, 0x83, 0x16, 0x64, 0xcf, 0x9f, 0x7c, 0x31, 0x02,
-    0xda, 0x37, 0x1a, 0x16, 0x49, 0xdc, 0xd9, 0x70, 0xae, 0x99, 0x2c, 0xc7,
-    0x01, 0xba, 0x42, 0xab, 0xe9, 0x4d, 0xa4, 0x78, 0x2c, 0xbd, 0xa0, 0xf1,
-    0xb7, 0xcf, 0xdd, 0xc1, 0xdb, 0x8f, 0x04, 0x87, 0x0b, 0x47, 0x4f, 0xd5,
-    0xd5, 0xe7, 0xfc, 0x6e, 0x42, 0xd5, 0x91, 0x4d, 0x7b, 0x1b, 0x5c, 0x3c,
-    0x02, 0x70, 0xdb, 0x05, 0x91, 0xaf, 0x35, 0x43, 0x05, 0xc2, 0x6d, 0xcf,
-    0x59, 0x23, 0xfc, 0xc4, 0xf6, 0x67, 0xf1, 0x84, 0x61, 0x4a, 0xb6, 0x4c,
-    0x15, 0x15, 0xa3, 0xea, 0x8f, 0x13, 0x15, 0xe3, 0xd2, 0xb5, 0x50, 0xc8,
-    0xae, 0xc8, 0x5c, 0x03, 0xb5, 0x63, 0x93, 0xaa, 0x10, 0xd7, 0x56, 0x0d,
-    0x6e, 0x13, 0x45, 0x8f, 0xec, 0x17, 0x5c, 0x5c, 0x73, 0x91, 0x5f, 0x6c,
-    0xaf, 0x11, 0x13, 0x32, 0x5e, 0x14, 0xf9, 0xaf, 0xaf, 0x43, 0x04, 0x60,
-    0x93, 0x42, 0x30, 0xa6, 0x75, 0xc0, 0x83, 0xd2, 0x4c, 0xa5, 0x0a, 0x16,
-    0x39, 0xef, 0x3f, 0xf7, 0x9d, 0x23, 0x19, 0xb9, 0xcd, 0xd8, 0x7c, 0x6e,
-    0xee, 0x6d, 0x2e, 0xff, 0x5a, 0xf3, 0xb9, 0xab, 0xe5, 0x64, 0xdc, 0xc2,
-    0x67, 0x30, 0x73, 0x19, 0x2d, 0xea, 0xd2, 0x19, 0x1f, 0x1f, 0xe0, 0xd9,
-    0xac, 0xc9, 0xdb, 0x38, 0x74, 0x5e, 0x31, 0x47, 0x2e, 0x9e, 0x2b, 0xcc,
-    0xb9, 0xe4, 0x29, 0xf8, 0xb2, 0xbf, 0x1b, 0xbc, 0x68, 0x96, 0x79, 0xcf,
-    0xaf, 0xf2, 0x1f, 0x57, 0x3f, 0x74, 0xc4, 0x71, 0x63, 0xb4, 0xe8, 0xbe,
-    0x58, 0xdb, 0x28, 0x62, 0xb5, 0x79, 0x8b, 0xe4, 0xd0, 0x96, 0xd0, 0xda,
-    0x0f, 0xd2, 0x70, 0x93, 0x2f, 0x71, 0xe0, 0x9f, 0x28, 0xb7, 0x52, 0x38,
-    0x9c, 0xcb, 0x8b, 0x2a, 0x8e, 0xbf, 0x0e, 0x3d, 0x60, 0x05, 0x0a, 0x91,
-    0x5b, 0xb5, 0x78, 0x10, 0x31, 0x00, 0x80, 0x31, 0x2d, 0xd7, 0xb0, 0x88,
-    0xc7, 0xd9, 0x58, 0xc6, 0xfc, 0x3b, 0xf4, 0xee, 0xec, 0xba, 0x05, 0xae,
-    0xae, 0xff, 0xcf, 0xd0, 0x71, 0xc6, 0xe7, 0xf3, 0x8b, 0x64, 0x50, 0x7a,
-    0x09, 0x93, 0x0f, 0x34, 0x59, 0x2d, 0xde, 0x4b, 0x1d, 0x86, 0x49, 0xff,
-    0x63, 0x76, 0x28, 0x6b, 0x52, 0x1b, 0x46, 0x06, 0x18, 0x90, 0x1c, 0x2d,
-    0xc5, 0x03, 0xcc, 0x00, 0x4d, 0xb7, 0xb2, 0x12, 0xc5, 0xf9, 0xb4, 0xa4,
-    0x6a, 0x36, 0x62, 0x46, 0x34, 0x2a, 0xf0, 0x11, 0xa3, 0xd6, 0x80, 0x21,
-    0xbf, 0x3b, 0xfd, 0xc5, 0x25, 0xa0, 0x4d, 0xc0, 0x2e, 0xc0, 0xf1, 0x7b,
-    0x96, 0x11, 0x64, 0x8e, 0xb9, 0xdb, 0x89, 0x4e, 0x33, 0x89, 0xf5, 0xc6,
-    0xfc, 0x2b, 0x99, 0xf5, 0xc2, 0x04, 0x83, 0x15, 0x47, 0xa8, 0xa5, 0xc1,
-    0x4a, 0xe4, 0x76, 0xab, 0x3e, 0xf0, 0x9b, 0xb7, 0x8d, 0x46, 0xd3, 0x52,
-    0x9b, 0xbd, 0xfd, 0x2b, 0xba, 0x73, 0x5d, 0x23, 0x67, 0x68, 0xe1, 0x76,
-    0x6f, 0x56, 0x2b, 0x17, 0xe4, 0x7e, 0x9a, 0xfd, 0x05, 0x48, 0x39, 0xc9,
-    0xcf, 0xa5, 0x83, 0xf7, 0x90, 0x9c, 0xa4, 0x28, 0x57, 0x40, 0xe9, 0xd4,
-    0x4b, 0x1a, 0x4b, 0x6f, 0x65, 0x14, 0xca, 0x43, 0xc1, 0x3f, 0x7c, 0xec,
-    0x82, 0x47, 0x0e, 0x64, 0x8b, 0x6f, 0x8c, 0xb2, 0xf0, 0x6d, 0xeb, 0x6f,
-    0x71, 0x8f, 0xcc, 0x2d, 0x60, 0x2b, 0xc3, 0x9f, 0x13, 0x94, 0xc7, 0x23,
-    0x02, 0xf5, 0xe6, 0xdf, 0x2d, 0xa9, 0xdb, 0xa9, 0xf3, 0xee, 0xe9, 0x3f,
-    0x2a, 0x69, 0x24, 0x6b, 0x78, 0xff, 0x6a, 0xd7, 0xe4, 0x69, 0x8c, 0x17,
-    0xd5, 0xc1, 0x36, 0x1a, 0xca, 0x77, 0xb0, 0xb5, 0x6b, 0x96, 0x4a, 0xb5,
-    0x0e, 0x4d, 0x0b, 0xd6, 0xd9, 0x78, 0xc5, 0xbf, 0xe3, 0x59, 0xfe, 0x63,
-    0xe3, 0xd3, 0x3c, 0x9a, 0xfa, 0xd7, 0x69, 0x5b, 0xef, 0xd3, 0xa4, 0xa3,
-    0xb9, 0x1f, 0x5c, 0x40, 0x20, 0x95, 0x38, 0x2d, 0xf5, 0x04, 0x0c, 0x2c,
-    0x79, 0x77, 0xc1, 0xb6, 0xcc, 0x74, 0x3c, 0x66, 0xf1, 0xc6, 0x65, 0xab,
-    0x4d, 0x68, 0x41, 0x16, 0x71, 0x51, 0xb9, 0x1b, 0xcb, 0xa7, 0x6d, 0xe0,
-    0x70, 0xa9, 0xfa, 0x65, 0x6b, 0x7b, 0x1e, 0xc5, 0xdf, 0xe2, 0x4c, 0x96,
-    0x44, 0x6b, 0x24, 0xa1, 0x15, 0x8e, 0xe7, 0x9b, 0x1f, 0x51, 0xef, 0xd7,
-    0x65, 0x5f, 0xcd, 0x74, 0x7f, 0x2d, 0x5c, 0xba, 0xba, 0x20, 0x32, 0x8d,
-    0x1c, 0xf1, 0x5a, 0xed, 0x21, 0xad, 0x78, 0x7b, 0x59, 0x58, 0xe4, 0xf6,
-    0xa7, 0x10, 0x35, 0xca, 0x5d, 0x86, 0x1a, 0x68, 0xba, 0x1c, 0x3c, 0x1c,
-    0x23, 0x79, 0x8b, 0x9f, 0xda, 0x5c, 0xd1, 0x5a, 0xa9, 0xc8, 0xf6, 0xc9,
-    0xdf, 0x21, 0x5a, 0x98, 0xdc, 0xf4, 0xb9, 0x02, 0x97, 0x2c, 0x10, 0x60,
-    0xc9, 0xb5, 0xea, 0x75, 0x0b, 0xd9, 0x8a, 0xa4, 0x86, 0x92, 0xbe, 0xf5,
-    0xd8, 0xc7, 0x6b, 0x13, 0x8b, 0xbb, 0xca, 0x5f, 0xe4, 0x8b, 0xce, 0xb5,
-    0x27, 0xae, 0x53, 0xed, 0xef, 0x37, 0xa6, 0x81, 0x8f, 0x70, 0x25, 0x18,
-    0x93, 0x06, 0x8c, 0x18, 0xcd, 0x7a, 0x1a, 0x8d, 0xfc, 0xde, 0x6f, 0x30,
-    0xdb, 0x41, 0xb6, 0x42, 0x14, 0x54, 0xf8, 0xcd, 0xc6, 0xf8, 0x0f, 0x82,
-    0x17, 0xfa, 0x8d, 0xba, 0x80, 0x81, 0x6a, 0xf7, 0x02, 0x97, 0x00, 0x78,
-    0xd6, 0x5b, 0xc9, 0xba, 0xd1, 0x99, 0xef, 0x8e, 0x48, 0x6c, 0x35, 0x10,
-    0x5b, 0xf1, 0x9b, 0x93, 0x4f, 0xbd, 0x7d, 0x27, 0x9e, 0xc7, 0x86, 0xb2,
-    0x8f, 0x6a, 0x91, 0x59, 0x2d, 0x14, 0xab, 0x1b, 0x34, 0x6e, 0xfa, 0x25,
-    0x5e, 0x14, 0xc7, 0xef, 0x3d, 0x0f, 0x13, 0xf9, 0x45, 0x4b, 0x90, 0xbc,
-    0xd8, 0x51, 0x42, 0x95, 0x25, 0x9b, 0x1b, 0x7c, 0xaf, 0x3b, 0x60, 0x21,
-    0x4c, 0x5f, 0x7c, 0x63, 0x4b, 0x45, 0xa6, 0xdc, 0xfd, 0x32, 0xf3, 0x06,
-    0x61, 0x11, 0x2d, 0x27, 0xde, 0x19, 0x38, 0x63, 0xf9, 0x70, 0xd1, 0x82,
-    0x8e, 0xc7, 0x99, 0xe1, 0x96, 0x9b, 0x54, 0x93, 0x64, 0x5f, 0xd1, 0x62,
-    0x9c, 0x37, 0x10, 0x1a, 0x8a, 0x82, 0x8d, 0x2a, 0x93, 0x95, 0x22, 0xc9,
-    0x21, 0xf5, 0xce, 0x21, 0xbb, 0x7c, 0x17, 0xee, 0x20, 0xa0, 0x73, 0xaa,
-    0x69, 0x78, 0x4e, 0x0d, 0x2c, 0x2c, 0x96, 0x23, 0xdc, 0x07, 0x16, 0xbd,
-    0xe7, 0xd5, 0x49, 0xcc, 0x44, 0xd1, 0x9d, 0xd7, 0xa3, 0x01, 0x60, 0xa0,
-    0xe0, 0x41, 0x63, 0x28, 0x8a, 0x43, 0xdb, 0x4f, 0x25, 0x5b, 0x27, 0x52,
-    0x4a, 0xee, 0x42, 0x43, 0x9a, 0xef, 0x33, 0x43, 0x70, 0xda, 0x64, 0x57,
-    0x49, 0x0c, 0x7f, 0xfd, 0xc7, 0x88, 0x26, 0x94, 0x10, 0xcc, 0x05, 0x1d,
-    0x54, 0x95, 0xea, 0x4e, 0x65, 0x28, 0x03, 0xbc, 0xa2, 0x62, 0xd2, 0xce,
-    0x60, 0x34, 0xf9, 0xdb, 0x26, 0xb5, 0xe6, 0x9b, 0x55, 0x2c, 0x8f, 0x30,
-    0x3a, 0x94, 0x9a, 0x15, 0x79, 0x22, 0x75, 0x4d, 0x1b, 0x91, 0xe0, 0x5b,
-    0xdb, 0xd1, 0x15, 0x7f, 0xcc, 0xc6, 0x88, 0xb5, 0x00, 0x3f, 0x5d, 0x84,
-    0x2e, 0x68, 0xde, 0x6f, 0x41, 0x5b, 0x4e, 0xe7, 0xdf, 0xe6, 0x3b, 0x7e,
-    0xf2, 0xdd, 0xfc, 0x01, 0xf2, 0x1b, 0x52, 0xba, 0xc4, 0x51, 0xae, 0x8f,
-    0xa0, 0x55, 0x12, 0x81, 0x57, 0xe0, 0x58, 0x5e, 0xea, 0xd7, 0x85, 0xfb,
-    0x19, 0x8b, 0xb7, 0x24, 0x29, 0x94, 0xa7, 0xfc, 0xed, 0x17, 0xaa, 0x32,
-    0x50, 0x11, 0xb3, 0x7a, 0x43, 0x3a, 0xc0, 0x2b, 0x82, 0x9c, 0x85, 0xd9,
-    0xd0, 0xdb, 0x21, 0x71, 0x83, 0xb4, 0x30, 0x14, 0xec, 0xfc, 0x8d, 0x32,
-    0xd6, 0xa2, 0x36, 0x5e, 0x3b, 0xe9, 0x12, 0x0c, 0x95, 0xd6, 0x0c, 0x0c,
-    0x31, 0x66, 0x30, 0x3f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x09, 0x14, 0x31, 0x32, 0x1e, 0x30, 0x00, 0x49, 0x00, 0x6e, 0x00,
-    0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x74, 0x00,
-    0x20, 0x00, 0x57, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x69, 0x00,
-    0x74, 0x00, 0x73, 0x00, 0x20, 0x00, 0x50, 0x00, 0x74, 0x00, 0x79, 0x00,
-    0x20, 0x00, 0x4c, 0x00, 0x74, 0x00, 0x64, 0x30, 0x23, 0x06, 0x09, 0x2a,
-    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14,
-    0x14, 0x74, 0x2d, 0x52, 0x8e, 0x0d, 0x0c, 0x06, 0x6c, 0x32, 0x64, 0xd3,
-    0x7e, 0x33, 0x31, 0x68, 0x8b, 0x28, 0x1a, 0x75, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x30, 0x80, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x07, 0x06, 0xa0, 0x80, 0x30, 0x80, 0x02, 0x01, 0x00, 0x30, 0x80,
-    0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0x30,
-    0x24, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01,
-    0x06, 0x30, 0x16, 0x04, 0x10, 0x9d, 0x1b, 0x68, 0x8e, 0x11, 0xc2, 0xb2,
-    0xd6, 0xd0, 0xe9, 0x5a, 0x9e, 0x96, 0xc1, 0x8c, 0xa6, 0x02, 0x02, 0x07,
-    0xd0, 0xa0, 0x80, 0x04, 0x82, 0x03, 0xf8, 0x1d, 0xce, 0x13, 0x70, 0x7a,
-    0x6b, 0x0a, 0x12, 0x2d, 0x01, 0x84, 0x63, 0x5c, 0x07, 0x82, 0x23, 0xf8,
-    0x8a, 0x5e, 0x53, 0x8f, 0xc8, 0xb4, 0x87, 0x1a, 0xa2, 0x98, 0xdb, 0xc6,
-    0x26, 0xca, 0xbb, 0x20, 0x24, 0xad, 0xac, 0xdf, 0xbe, 0x73, 0x6d, 0x97,
-    0x4b, 0x6e, 0x5b, 0x45, 0xd2, 0x84, 0xd4, 0xa4, 0x82, 0xd0, 0xce, 0x40,
-    0x13, 0x4c, 0x6d, 0x4d, 0x2e, 0xc1, 0x96, 0x95, 0x01, 0x64, 0xf3, 0xf0,
-    0x5f, 0x06, 0x06, 0xea, 0xf7, 0x84, 0x8f, 0xb3, 0xb0, 0x6e, 0x7c, 0x9b,
-    0x71, 0x73, 0xb9, 0xcd, 0xac, 0x72, 0xf6, 0xa0, 0x23, 0xda, 0x9b, 0x9f,
-    0xec, 0x16, 0xef, 0x33, 0xd4, 0xd0, 0x4d, 0x20, 0xf0, 0x75, 0xa9, 0x73,
-    0xf4, 0x31, 0xc7, 0x57, 0xb8, 0x0d, 0x9d, 0x85, 0x7c, 0xee, 0x3a, 0x24,
-    0x7b, 0x74, 0xa0, 0x5c, 0xad, 0xde, 0x5e, 0x05, 0x1e, 0xeb, 0x02, 0x78,
-    0x12, 0xb4, 0xb9, 0xc6, 0xe5, 0xc5, 0x99, 0xbc, 0x05, 0x62, 0x5b, 0x10,
-    0x52, 0x08, 0x00, 0x9e, 0x73, 0xac, 0xe4, 0x1d, 0xdb, 0xb8, 0xbf, 0x48,
-    0x03, 0x28, 0x05, 0x3c, 0x61, 0x1a, 0x8b, 0x4c, 0xd7, 0x5f, 0x8c, 0xb4,
-    0xcd, 0x91, 0x1c, 0x0b, 0xf4, 0x55, 0xd4, 0x1c, 0x42, 0x4a, 0xd4, 0xf5,
-    0x15, 0x38, 0xd9, 0x06, 0xfc, 0x49, 0xf6, 0xe5, 0xa7, 0x09, 0x5d, 0x01,
-    0xbd, 0xc3, 0xd1, 0x09, 0x9f, 0x5d, 0x0c, 0x19, 0x43, 0xd0, 0xfa, 0x25,
-    0x17, 0xad, 0x2a, 0xbf, 0x89, 0x63, 0x06, 0xa8, 0x02, 0x03, 0xe4, 0xfe,
-    0x19, 0x08, 0x70, 0xa1, 0x74, 0x74, 0xb6, 0xb6, 0x0f, 0x19, 0x4d, 0x54,
-    0xa5, 0xb2, 0xd7, 0x37, 0x3b, 0x17, 0xc0, 0x5d, 0xc2, 0x8a, 0xf1, 0xcc,
-    0xed, 0xef, 0x65, 0xc8, 0xca, 0xbe, 0x02, 0xd4, 0x9b, 0x1e, 0xef, 0xc9,
-    0xe0, 0x91, 0x82, 0xb0, 0xe0, 0x50, 0xc7, 0xa0, 0xcc, 0x01, 0x6d, 0x55,
-    0xe5, 0x67, 0x99, 0x65, 0x13, 0xe4, 0xd2, 0x90, 0x91, 0xf3, 0x76, 0x0b,
-    0x6a, 0x2d, 0x19, 0xaf, 0x61, 0xb3, 0x7f, 0x4c, 0x04, 0xfe, 0x68, 0xf6,
-    0xb3, 0x56, 0xd8, 0xf3, 0x34, 0xd7, 0x04, 0x0a, 0x31, 0xc8, 0x37, 0xdf,
-    0xac, 0xd8, 0x91, 0x80, 0x8a, 0x30, 0x12, 0x22, 0x80, 0xd7, 0x24, 0xcf,
-    0x70, 0xaf, 0x56, 0xaf, 0x81, 0xfe, 0x63, 0xf1, 0xea, 0x57, 0x4c, 0xf2,
-    0xdb, 0x30, 0x50, 0x92, 0xc1, 0xeb, 0x04, 0x9a, 0xdf, 0xf5, 0x74, 0x57,
-    0x5b, 0x58, 0xc2, 0x4e, 0x6b, 0x11, 0xf3, 0xe1, 0xb3, 0x0f, 0x56, 0x35,
-    0x04, 0xf8, 0x50, 0x1d, 0x7e, 0xe6, 0x99, 0xa2, 0x48, 0xdb, 0xea, 0x62,
-    0x4f, 0x98, 0xc2, 0xef, 0xbf, 0x7f, 0x94, 0xc0, 0x36, 0xc0, 0xf3, 0x27,
-    0xfe, 0xe2, 0x17, 0x1e, 0x91, 0x7d, 0x96, 0xa9, 0x2b, 0x71, 0x51, 0xc3,
-    0x59, 0x2d, 0x11, 0x50, 0x1e, 0xcb, 0xce, 0xff, 0x04, 0x4d, 0x16, 0xf5,
-    0xc2, 0xd4, 0x1f, 0xdd, 0x7f, 0x5a, 0xfd, 0x1d, 0xe9, 0x63, 0x52, 0x44,
-    0x76, 0x5f, 0x91, 0xfd, 0xe8, 0xdf, 0x0a, 0x69, 0x0d, 0xd3, 0x64, 0x91,
-    0xea, 0xdd, 0x03, 0x4f, 0x42, 0xa5, 0xe9, 0xa1, 0x70, 0x05, 0xf3, 0x22,
-    0x8e, 0xad, 0x70, 0x1a, 0x3e, 0x94, 0x42, 0x06, 0xe7, 0x47, 0x37, 0x3d,
-    0xf5, 0xda, 0x3e, 0x2a, 0x3a, 0xc0, 0x23, 0xd9, 0x4a, 0x26, 0x69, 0x13,
-    0xa6, 0x93, 0x7c, 0xf2, 0xaf, 0x04, 0x5e, 0x9b, 0x88, 0xc7, 0x77, 0xd0,
-    0x93, 0xab, 0x1b, 0xbd, 0x3d, 0x69, 0x90, 0xab, 0x41, 0xa9, 0xbc, 0x84,
-    0x18, 0x4d, 0x29, 0x02, 0xc1, 0xf8, 0xff, 0x63, 0x18, 0x24, 0x74, 0x8f,
-    0x7e, 0x44, 0x33, 0xaf, 0x88, 0x8b, 0x93, 0x5b, 0x9a, 0xae, 0x6b, 0x08,
-    0xa2, 0x82, 0x5d, 0xf3, 0xbe, 0x61, 0xc3, 0xf0, 0x2d, 0x31, 0x4c, 0xb5,
-    0xb5, 0x91, 0x0f, 0xfa, 0x81, 0x61, 0xad, 0xfc, 0xba, 0x91, 0xeb, 0x3b,
-    0x9d, 0x22, 0x41, 0x45, 0x0e, 0x8e, 0x24, 0xc7, 0x1c, 0x81, 0x95, 0xa8,
-    0x7b, 0x64, 0xed, 0xa5, 0xec, 0x5a, 0x68, 0x3c, 0x85, 0x8d, 0x92, 0xb7,
-    0x24, 0x0f, 0xed, 0xf5, 0xc6, 0x31, 0x61, 0xdc, 0xef, 0xa7, 0xcb, 0x8f,
-    0xda, 0x43, 0x05, 0x42, 0xf6, 0x9e, 0xbc, 0x1b, 0x9a, 0xa1, 0xe8, 0x1d,
-    0x8d, 0x42, 0xdb, 0x80, 0x83, 0x55, 0x52, 0x2b, 0x95, 0x00, 0x05, 0x82,
-    0x84, 0xc3, 0x54, 0x23, 0x8e, 0x1d, 0x00, 0xa2, 0x16, 0x3e, 0xce, 0x3d,
-    0xcc, 0x9e, 0xb8, 0x4c, 0x59, 0xb2, 0x12, 0xa2, 0x23, 0xc1, 0x46, 0x50,
-    0x86, 0xae, 0x75, 0x7e, 0x49, 0x38, 0x77, 0x94, 0xf0, 0x27, 0xd8, 0x17,
-    0x38, 0x8c, 0xe0, 0x73, 0x00, 0xfb, 0xaf, 0xbf, 0xe8, 0xed, 0x85, 0x58,
-    0x3e, 0xb4, 0x88, 0x04, 0xc8, 0x22, 0x1b, 0xb4, 0x75, 0xa2, 0xc4, 0xdd,
-    0x06, 0xd2, 0x83, 0x42, 0x21, 0x57, 0xfc, 0xd8, 0xae, 0x9c, 0x0e, 0xd8,
-    0x6a, 0x70, 0xd1, 0xeb, 0x44, 0x9c, 0xb7, 0x37, 0x04, 0x05, 0xf5, 0x17,
-    0xbe, 0xf3, 0x56, 0x1b, 0x06, 0x36, 0x1c, 0x59, 0x7b, 0x65, 0x8d, 0xbb,
-    0xbe, 0x22, 0x9a, 0x70, 0xa3, 0xe9, 0x60, 0x1a, 0xc9, 0xdd, 0x81, 0x3c,
-    0x2d, 0x4e, 0xc0, 0x8a, 0xe5, 0x91, 0xa7, 0xc1, 0x80, 0x07, 0x47, 0x7a,
-    0x74, 0x4f, 0x3e, 0x4a, 0xdc, 0xb2, 0xcc, 0xff, 0x37, 0x66, 0x05, 0xcb,
-    0xd6, 0xe9, 0x90, 0xf5, 0xef, 0x2b, 0x7e, 0xa7, 0x66, 0x51, 0xcb, 0x48,
-    0xb3, 0x8a, 0x6f, 0x06, 0xba, 0x8b, 0x3d, 0x35, 0x36, 0xdf, 0x0e, 0x40,
-    0xe5, 0xa1, 0xe3, 0xdd, 0x89, 0xab, 0x64, 0x9c, 0x01, 0x15, 0x9e, 0x93,
-    0xea, 0xf9, 0x4f, 0x9e, 0xf5, 0x8b, 0xf2, 0xc2, 0xbb, 0xe5, 0xc3, 0xa3,
-    0xe3, 0x13, 0x63, 0x4f, 0x7d, 0x20, 0xe4, 0x66, 0x96, 0x84, 0x8d, 0xd4,
-    0xca, 0x72, 0x52, 0xdc, 0xb8, 0x93, 0xd4, 0xa5, 0x3e, 0x6e, 0x42, 0x56,
-    0x80, 0x46, 0x77, 0x86, 0x49, 0xfe, 0xf3, 0xb4, 0x5b, 0x37, 0xfc, 0xb8,
-    0x0c, 0xd7, 0x63, 0xac, 0x3c, 0x6f, 0xf0, 0xbe, 0xbe, 0xb4, 0x13, 0xe7,
-    0x34, 0xe5, 0x06, 0xbf, 0x17, 0x48, 0x6e, 0xc0, 0x26, 0x94, 0xdd, 0xed,
-    0xf4, 0xda, 0x97, 0x25, 0xab, 0xd6, 0x9b, 0xc3, 0x8c, 0xeb, 0x17, 0x09,
-    0xfc, 0x03, 0x5a, 0x2f, 0x19, 0x85, 0x50, 0xc4, 0xe6, 0x35, 0x71, 0x94,
-    0xad, 0xc5, 0xcf, 0x08, 0xcf, 0x69, 0x3b, 0xc3, 0x31, 0xec, 0xf1, 0xfa,
-    0x80, 0x66, 0x8f, 0x14, 0xde, 0x56, 0x21, 0x12, 0x9b, 0x0c, 0xdf, 0x92,
-    0x48, 0x06, 0xce, 0xdb, 0xeb, 0x28, 0x54, 0x27, 0x8b, 0xa9, 0xef, 0x0c,
-    0xf4, 0xa0, 0xcc, 0x84, 0x59, 0x60, 0xed, 0x18, 0x65, 0xca, 0x67, 0x0c,
-    0xd1, 0x1f, 0xcf, 0x59, 0x4b, 0xce, 0x07, 0x27, 0x08, 0x6a, 0xea, 0x53,
-    0xdc, 0x47, 0xb3, 0x4e, 0xe4, 0x0b, 0xff, 0x9a, 0x7d, 0x6b, 0x0d, 0x2f,
-    0x2d, 0x60, 0xd7, 0x8b, 0x22, 0xf5, 0x30, 0x43, 0x09, 0xe6, 0xdf, 0x01,
-    0x03, 0x27, 0x2d, 0xb5, 0x74, 0x52, 0x5d, 0x08, 0xc7, 0x5a, 0x44, 0x25,
-    0x0f, 0x2c, 0x14, 0x8f, 0x48, 0xea, 0x18, 0x99, 0xd1, 0xcc, 0xc5, 0xdc,
-    0x65, 0xa5, 0x3d, 0x25, 0x94, 0xa9, 0xc7, 0xad, 0x3e, 0xa4, 0xf6, 0xe6,
-    0xbd, 0xa7, 0x70, 0xd4, 0xdc, 0x9b, 0x26, 0xcb, 0x31, 0x70, 0xaf, 0x3e,
-    0xa4, 0xb6, 0x8d, 0x21, 0x31, 0x67, 0x35, 0x35, 0x86, 0x67, 0xd1, 0x02,
-    0x6c, 0x36, 0x76, 0xc9, 0x20, 0xf6, 0x0f, 0x30, 0x41, 0x83, 0x19, 0xf5,
-    0xe1, 0x33, 0x90, 0xbc, 0x7b, 0x8c, 0x9b, 0x8a, 0x68, 0x30, 0x9e, 0xed,
-    0xf4, 0x88, 0xc9, 0x04, 0x08, 0x2b, 0xb0, 0x0f, 0xae, 0xc7, 0xe0, 0x6e,
-    0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39, 0x30, 0x21, 0x30,
-    0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
-    0xe0, 0xf7, 0xa1, 0x1b, 0xf6, 0x3f, 0x05, 0xad, 0x55, 0x6a, 0x20, 0x4c,
-    0x71, 0xca, 0x62, 0x47, 0x13, 0x28, 0xd5, 0x05, 0x04, 0x10, 0x3e, 0x87,
-    0x2d, 0x96, 0xea, 0x80, 0x4b, 0xab, 0x3a, 0xb9, 0xee, 0x09, 0x65, 0x28,
-    0xbc, 0x8d, 0x02, 0x02, 0x07, 0xd0, 0x00, 0x00,
-};
-
-// kWindows is a dummy key and certificate exported from the certificate
-// manager on Windows 7.
-static const uint8_t kWindows[] = {
-    0x30, 0x82, 0x0a, 0x02, 0x02, 0x01, 0x03, 0x30, 0x82, 0x09, 0xbe, 0x06,
-    0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
-    0x09, 0xaf, 0x04, 0x82, 0x09, 0xab, 0x30, 0x82, 0x09, 0xa7, 0x30, 0x82,
-    0x06, 0x08, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-    0x01, 0xa0, 0x82, 0x05, 0xf9, 0x04, 0x82, 0x05, 0xf5, 0x30, 0x82, 0x05,
-    0xf1, 0x30, 0x82, 0x05, 0xed, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02, 0xa0, 0x82, 0x04, 0xfe, 0x30, 0x82,
-    0x04, 0xfa, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x0c, 0x01, 0x03, 0x30, 0x0e, 0x04, 0x08, 0xb7, 0x20, 0x55, 0x5a,
-    0x4d, 0x3f, 0x0e, 0x89, 0x02, 0x02, 0x07, 0xd0, 0x04, 0x82, 0x04, 0xd8,
-    0x3a, 0xcc, 0xd6, 0xcb, 0x4d, 0x54, 0xc0, 0x04, 0x56, 0x10, 0xcc, 0x49,
-    0xe4, 0xe0, 0x10, 0x73, 0xfb, 0x1a, 0xdd, 0x1d, 0x4f, 0x6e, 0x55, 0xe3,
-    0xa4, 0xab, 0xf9, 0x26, 0xaa, 0x42, 0x54, 0xa0, 0xd1, 0xf0, 0x8d, 0xbf,
-    0x71, 0x7d, 0x18, 0x00, 0x17, 0xb3, 0xb7, 0x63, 0x50, 0x8d, 0x2c, 0xeb,
-    0x2f, 0xe3, 0xc3, 0xbf, 0x93, 0xc8, 0x46, 0x48, 0x99, 0x47, 0xe2, 0x3b,
-    0x8d, 0x71, 0x01, 0x5f, 0x59, 0x5b, 0x61, 0x7e, 0x1f, 0x0c, 0x6e, 0x3e,
-    0xc4, 0x74, 0x99, 0x98, 0x30, 0xff, 0x37, 0x7b, 0x30, 0x19, 0xb5, 0xfc,
-    0x69, 0x94, 0x5f, 0x79, 0x69, 0x34, 0xda, 0xb5, 0x21, 0xcf, 0xfe, 0x72,
-    0x87, 0xe8, 0x7d, 0x29, 0x7e, 0x27, 0x25, 0x90, 0x80, 0x98, 0xdd, 0x8d,
-    0xbf, 0x42, 0xb0, 0x10, 0xd8, 0x7d, 0x6d, 0xfe, 0x6f, 0x0d, 0x61, 0x09,
-    0xfd, 0xb2, 0x9b, 0xeb, 0xbf, 0x1c, 0xca, 0x33, 0xbc, 0x4e, 0x19, 0x52,
-    0x55, 0x53, 0xb4, 0xa5, 0x98, 0x6c, 0xa3, 0x3b, 0xf8, 0xa4, 0x8d, 0x79,
-    0xcf, 0x40, 0xf2, 0x89, 0x09, 0x3c, 0x38, 0xab, 0xae, 0xf4, 0x09, 0x3b,
-    0xb6, 0xcb, 0xdd, 0xd7, 0xad, 0xe0, 0x5a, 0x71, 0x64, 0xc9, 0x0f, 0x18,
-    0xac, 0x3c, 0x12, 0xd4, 0x22, 0x54, 0x24, 0x1a, 0xa5, 0x35, 0x78, 0x99,
-    0x09, 0x4a, 0x18, 0x95, 0x23, 0xb9, 0xf7, 0x89, 0x3f, 0x13, 0x43, 0x1f,
-    0x8d, 0x76, 0x6b, 0x04, 0xdb, 0x64, 0xf4, 0x8e, 0xf5, 0x50, 0xa0, 0xae,
-    0x1c, 0x8c, 0xc8, 0xf3, 0xde, 0xf3, 0x11, 0x2d, 0xfe, 0x76, 0xf0, 0xac,
-    0x46, 0x54, 0x23, 0x03, 0x49, 0xfa, 0x73, 0xcd, 0xe0, 0xa1, 0x6c, 0x66,
-    0x4d, 0x1b, 0x99, 0x57, 0x3d, 0x61, 0x61, 0xeb, 0x61, 0x40, 0xc7, 0xd6,
-    0x41, 0xbe, 0x63, 0x21, 0x1e, 0x7e, 0xb5, 0x0e, 0x94, 0x93, 0x37, 0x41,
-    0xe8, 0x91, 0x06, 0xd7, 0xa3, 0x33, 0x78, 0x17, 0x17, 0x59, 0x78, 0x8f,
-    0xaf, 0xed, 0xf9, 0x90, 0xfb, 0xb6, 0xc8, 0xa9, 0x0b, 0x10, 0x1a, 0xf1,
-    0xab, 0x10, 0x11, 0xbc, 0x7f, 0xa5, 0x2d, 0x34, 0x7d, 0x7b, 0xaf, 0xc8,
-    0xb2, 0x00, 0x6b, 0xd4, 0xbb, 0x25, 0x9b, 0xc7, 0x14, 0x8b, 0x50, 0x0a,
-    0xd5, 0x2c, 0x1f, 0xa0, 0x5f, 0x07, 0x1d, 0x5e, 0x1a, 0xa4, 0x4b, 0x85,
-    0xb2, 0xa6, 0xe2, 0xdd, 0xb7, 0xda, 0x11, 0x25, 0x51, 0xbf, 0x72, 0x50,
-    0x53, 0xa1, 0x3d, 0xfa, 0x1d, 0x34, 0x75, 0xdd, 0x7a, 0xe0, 0x90, 0x56,
-    0x14, 0xc3, 0xe8, 0x0b, 0xea, 0x32, 0x5f, 0x92, 0xfc, 0x2e, 0x4d, 0x0e,
-    0xfe, 0xba, 0x1a, 0x00, 0x6d, 0x8f, 0x75, 0xac, 0x49, 0x4c, 0x79, 0x03,
-    0x2e, 0xf2, 0xcc, 0x8e, 0x96, 0x27, 0x3c, 0x59, 0x28, 0x7f, 0x52, 0x8d,
-    0xc3, 0x3b, 0x24, 0x68, 0xff, 0xbb, 0xd0, 0x4e, 0xdf, 0xc4, 0x91, 0x32,
-    0x14, 0x5e, 0x43, 0x73, 0xd8, 0x56, 0x65, 0xe1, 0x48, 0x89, 0xe4, 0x33,
-    0xef, 0x4b, 0x51, 0x50, 0xf2, 0x53, 0xe7, 0xae, 0x7d, 0xb6, 0x8c, 0x80,
-    0xee, 0x8d, 0x9e, 0x24, 0x1a, 0xdd, 0x95, 0x7d, 0x22, 0x58, 0x76, 0xf8,
-    0xbb, 0x63, 0x36, 0x17, 0xdc, 0xc6, 0x3e, 0xb8, 0xe9, 0x1f, 0xd8, 0xe0,
-    0x06, 0x18, 0x1b, 0x3c, 0x45, 0xcb, 0xe1, 0x5a, 0x41, 0xe5, 0x32, 0xa3,
-    0x85, 0x1b, 0xff, 0xe0, 0x5e, 0x28, 0xee, 0xe9, 0x05, 0xc7, 0xc8, 0x47,
-    0x85, 0xe8, 0x13, 0x7f, 0x1b, 0xda, 0xd7, 0x3e, 0x8e, 0xb8, 0xa3, 0x96,
-    0x34, 0x19, 0x3b, 0x0c, 0x88, 0x26, 0x38, 0xe7, 0x65, 0xf6, 0x03, 0x4f,
-    0xc8, 0x37, 0x6e, 0x2f, 0x5e, 0x5d, 0xcd, 0xa3, 0x29, 0x37, 0xe8, 0x86,
-    0x84, 0x66, 0x37, 0x84, 0xa0, 0x49, 0x4e, 0x8f, 0x3b, 0x1a, 0x42, 0x9f,
-    0x62, 0x1f, 0x2b, 0x97, 0xc9, 0x18, 0x21, 0xd2, 0xa5, 0xcd, 0x8f, 0xa4,
-    0x03, 0xf8, 0x82, 0x1e, 0xb8, 0x3e, 0x6b, 0x54, 0x29, 0x75, 0x5f, 0x80,
-    0xe6, 0x8f, 0x2f, 0x65, 0xb0, 0x6b, 0xbb, 0x18, 0x6e, 0x0d, 0x32, 0x62,
-    0x8c, 0x97, 0x48, 0xd3, 0xaa, 0xf2, 0x5e, 0xb8, 0x25, 0xbc, 0xb5, 0x22,
-    0x4a, 0xac, 0xcf, 0xdc, 0x8b, 0x48, 0xfc, 0x95, 0xf2, 0x17, 0x21, 0x1e,
-    0xda, 0x13, 0xd3, 0x1b, 0xe2, 0x37, 0xd5, 0xbf, 0x92, 0xe4, 0x81, 0xf5,
-    0x98, 0x57, 0x51, 0x14, 0xda, 0x80, 0x7d, 0x4a, 0x6a, 0xce, 0x17, 0xaf,
-    0xdb, 0xc3, 0x2e, 0x84, 0x3b, 0x1e, 0x02, 0x51, 0x4a, 0xc1, 0x25, 0x8c,
-    0x5a, 0x20, 0x56, 0xee, 0xec, 0x59, 0xcf, 0xd7, 0x3e, 0x5f, 0x39, 0x9f,
-    0xbf, 0x4d, 0x4e, 0x94, 0xb1, 0x1d, 0x83, 0x70, 0xc0, 0xab, 0xff, 0xfa,
-    0x7c, 0x2e, 0x5b, 0xfb, 0x57, 0x3f, 0x60, 0xb8, 0xf3, 0x36, 0x5f, 0xbf,
-    0x6a, 0x8c, 0x6f, 0xe0, 0x34, 0xe8, 0x75, 0x26, 0xc2, 0x1e, 0x22, 0x64,
-    0x0e, 0x43, 0xc1, 0x93, 0xe6, 0x8a, 0x2e, 0xe9, 0xd9, 0xe0, 0x9f, 0x56,
-    0x50, 0x8a, 0xbd, 0x68, 0xf6, 0x57, 0x63, 0x55, 0xbb, 0xe7, 0xfe, 0x22,
-    0xca, 0xdc, 0x85, 0x38, 0x39, 0xc8, 0x66, 0x02, 0x28, 0x0f, 0xe0, 0x1c,
-    0xd6, 0x0f, 0x5d, 0x6a, 0x0b, 0xd8, 0xe5, 0x6a, 0xeb, 0x54, 0xb2, 0xe0,
-    0x02, 0x6f, 0xe2, 0x42, 0x89, 0x66, 0xc2, 0xd5, 0xc6, 0xe2, 0xb2, 0x04,
-    0x6d, 0x8a, 0x2b, 0x48, 0xc2, 0x51, 0x07, 0x8e, 0xf3, 0x91, 0x0b, 0xb7,
-    0x55, 0x6e, 0xbb, 0xbf, 0x11, 0x5a, 0xcb, 0x2c, 0xb3, 0x1e, 0x61, 0xd3,
-    0xdb, 0x90, 0xad, 0xba, 0x10, 0x96, 0xe2, 0x16, 0xf4, 0x0c, 0x47, 0xbd,
-    0x64, 0x66, 0x7a, 0x17, 0x63, 0xb9, 0x02, 0xcb, 0x53, 0x7a, 0x35, 0x92,
-    0x74, 0xc3, 0x2a, 0x7d, 0xc5, 0x11, 0x18, 0x2f, 0xa3, 0x62, 0x2c, 0xc0,
-    0x87, 0xd3, 0xd3, 0xba, 0xcb, 0xe0, 0x86, 0x9b, 0x4b, 0xc5, 0x59, 0x98,
-    0x7e, 0x32, 0x96, 0x55, 0xc1, 0x3d, 0x5a, 0xcd, 0x90, 0x2d, 0xf8, 0xb7,
-    0xa8, 0xba, 0xce, 0x89, 0x64, 0xa6, 0xf3, 0x1b, 0x11, 0x2e, 0x12, 0x99,
-    0x4d, 0x34, 0x45, 0x13, 0x66, 0xb7, 0x69, 0x7b, 0xc5, 0x79, 0xf5, 0x6b,
-    0xc2, 0x1d, 0xc8, 0x3f, 0x09, 0x18, 0x0a, 0xfc, 0xf7, 0xaf, 0x98, 0xc2,
-    0xc7, 0xcc, 0x85, 0x29, 0xc6, 0x22, 0x7a, 0x77, 0xab, 0xb5, 0xac, 0xf7,
-    0x9e, 0x70, 0x8e, 0x7f, 0x3c, 0xf1, 0xbd, 0xd9, 0x7a, 0x92, 0x84, 0xc5,
-    0xb8, 0x56, 0xc3, 0xcb, 0xf7, 0x25, 0xad, 0xda, 0x0e, 0x1c, 0xe4, 0x68,
-    0x66, 0x83, 0x91, 0x78, 0xf1, 0xe7, 0x8c, 0xaa, 0x45, 0xb6, 0x85, 0x74,
-    0x9b, 0x08, 0xff, 0xac, 0x38, 0x55, 0xa5, 0x6a, 0xea, 0x2e, 0x75, 0x71,
-    0xd3, 0xa2, 0xdc, 0x1c, 0xc0, 0xc7, 0x0b, 0xa9, 0xd5, 0x7e, 0xf9, 0x63,
-    0x82, 0x87, 0xb7, 0x81, 0x01, 0xb9, 0x31, 0xdf, 0x41, 0x35, 0x0e, 0xe2,
-    0x1f, 0x48, 0xbf, 0x60, 0xce, 0xb0, 0xb4, 0x38, 0xa5, 0xb4, 0x76, 0xa3,
-    0x80, 0x1f, 0x93, 0x57, 0xf2, 0x05, 0x81, 0x42, 0xd1, 0xae, 0x56, 0x6d,
-    0xc5, 0x4c, 0xab, 0xa6, 0x24, 0x2a, 0x02, 0x3b, 0xb1, 0xc4, 0x75, 0xcf,
-    0x15, 0x90, 0xb5, 0xf2, 0xe7, 0x10, 0x69, 0xa0, 0xe3, 0xc4, 0xe6, 0x52,
-    0x63, 0x14, 0xb4, 0x15, 0x91, 0x8e, 0xba, 0x7a, 0xad, 0x2d, 0x9b, 0x24,
-    0x74, 0x36, 0x31, 0xca, 0xcb, 0x4b, 0x5a, 0xbf, 0xd3, 0x4e, 0xb4, 0xc1,
-    0x48, 0x44, 0x74, 0x2f, 0x83, 0xe4, 0x39, 0x3d, 0x90, 0x2d, 0x32, 0x12,
-    0xf7, 0xfa, 0xd3, 0xe3, 0xdb, 0x4f, 0xe6, 0xe7, 0x20, 0x2c, 0x57, 0xc0,
-    0xf9, 0x80, 0xe1, 0xdc, 0x1c, 0xf2, 0x05, 0x54, 0x35, 0xf6, 0xbd, 0xfb,
-    0xbd, 0xc5, 0xb2, 0x82, 0x32, 0x63, 0x32, 0xca, 0xf4, 0xf7, 0x14, 0x92,
-    0x87, 0x8a, 0x45, 0x37, 0x56, 0x93, 0xda, 0x4f, 0x04, 0x59, 0x03, 0x24,
-    0x93, 0x1a, 0x0b, 0x4e, 0xdb, 0x58, 0xbf, 0xda, 0x2a, 0x0e, 0x7e, 0x98,
-    0x6c, 0x0c, 0xeb, 0x21, 0xf9, 0xbf, 0x9b, 0x1f, 0xc0, 0xef, 0xd3, 0xea,
-    0xcb, 0x99, 0x5e, 0x14, 0x3e, 0x10, 0xfa, 0xad, 0x38, 0xf7, 0x68, 0x9f,
-    0xa3, 0xcc, 0xdf, 0xe5, 0x31, 0x91, 0x98, 0xde, 0x74, 0x5f, 0x7b, 0xce,
-    0xe4, 0x54, 0xd9, 0x51, 0xec, 0xf5, 0x4b, 0x17, 0x5f, 0x99, 0x4c, 0xf8,
-    0x00, 0xe0, 0x10, 0x09, 0x07, 0x64, 0xae, 0x61, 0x3b, 0x60, 0xa3, 0x89,
-    0x38, 0xc4, 0x80, 0xf2, 0x1e, 0x11, 0x26, 0x78, 0x72, 0x05, 0x97, 0x27,
-    0xba, 0x83, 0x33, 0x1b, 0x14, 0x4b, 0xc0, 0xc8, 0xb0, 0xcc, 0x0a, 0x9b,
-    0x3e, 0x4c, 0xde, 0x12, 0x07, 0x11, 0xd5, 0xf0, 0xc0, 0xdd, 0x70, 0x3d,
-    0xd8, 0x7a, 0xf7, 0xa2, 0xf2, 0x70, 0xad, 0x54, 0xce, 0x67, 0x41, 0x12,
-    0x29, 0x1f, 0xe1, 0x49, 0x5f, 0x4c, 0x77, 0x41, 0x7c, 0x74, 0x25, 0x9c,
-    0x91, 0xd1, 0x0d, 0xa5, 0x9a, 0xb8, 0x56, 0x4c, 0x01, 0xc0, 0x77, 0x51,
-    0x14, 0xc8, 0x92, 0x40, 0x9a, 0xbd, 0x7f, 0x3b, 0x9b, 0x17, 0xbb, 0x80,
-    0x6e, 0x50, 0x64, 0x31, 0xed, 0xe2, 0x22, 0x9f, 0x96, 0x8e, 0xe2, 0x4e,
-    0x54, 0x6e, 0x36, 0x35, 0xfc, 0xf2, 0xed, 0xfc, 0x56, 0x63, 0xdb, 0x89,
-    0x19, 0x99, 0xf8, 0x47, 0xff, 0xce, 0x35, 0xd2, 0x86, 0x63, 0xbc, 0xe4,
-    0x8c, 0x5d, 0x12, 0x94, 0x31, 0x81, 0xdb, 0x30, 0x13, 0x06, 0x09, 0x2a,
-    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x06, 0x04, 0x04,
-    0x01, 0x00, 0x00, 0x00, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
-    0xf7, 0x0d, 0x01, 0x09, 0x14, 0x31, 0x4a, 0x1e, 0x48, 0x00, 0x65, 0x00,
-    0x65, 0x00, 0x36, 0x00, 0x64, 0x00, 0x38, 0x00, 0x38, 0x00, 0x30, 0x00,
-    0x35, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x36, 0x00, 0x64, 0x00, 0x39, 0x00,
-    0x2d, 0x00, 0x34, 0x00, 0x32, 0x00, 0x65, 0x00, 0x32, 0x00, 0x2d, 0x00,
-    0x38, 0x00, 0x62, 0x00, 0x36, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x66, 0x00,
-    0x65, 0x00, 0x61, 0x00, 0x62, 0x00, 0x35, 0x00, 0x65, 0x00, 0x66, 0x00,
-    0x32, 0x00, 0x38, 0x00, 0x32, 0x00, 0x37, 0x00, 0x30, 0x30, 0x6b, 0x06,
-    0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x11, 0x01, 0x31, 0x5e,
-    0x1e, 0x5c, 0x00, 0x4d, 0x00, 0x69, 0x00, 0x63, 0x00, 0x72, 0x00, 0x6f,
-    0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00, 0x74, 0x00, 0x20, 0x00, 0x45,
-    0x00, 0x6e, 0x00, 0x68, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x65,
-    0x00, 0x64, 0x00, 0x20, 0x00, 0x43, 0x00, 0x72, 0x00, 0x79, 0x00, 0x70,
-    0x00, 0x74, 0x00, 0x6f, 0x00, 0x67, 0x00, 0x72, 0x00, 0x61, 0x00, 0x70,
-    0x00, 0x68, 0x00, 0x69, 0x00, 0x63, 0x00, 0x20, 0x00, 0x50, 0x00, 0x72,
-    0x00, 0x6f, 0x00, 0x76, 0x00, 0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x72,
-    0x00, 0x20, 0x00, 0x76, 0x00, 0x31, 0x00, 0x2e, 0x00, 0x30, 0x30, 0x82,
-    0x03, 0x97, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-    0x06, 0xa0, 0x82, 0x03, 0x88, 0x30, 0x82, 0x03, 0x84, 0x02, 0x01, 0x00,
-    0x30, 0x82, 0x03, 0x7d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x07, 0x01, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x0c, 0x01, 0x06, 0x30, 0x0e, 0x04, 0x08, 0x92, 0x16, 0x6d,
-    0x6d, 0x68, 0xd3, 0xb0, 0xc1, 0x02, 0x02, 0x07, 0xd0, 0x80, 0x82, 0x03,
-    0x50, 0xee, 0x76, 0xe8, 0x60, 0xbf, 0xca, 0x3c, 0x2d, 0xe5, 0x29, 0x22,
-    0xf6, 0x33, 0xc3, 0x50, 0x6a, 0xdb, 0xf3, 0x58, 0x3c, 0xd9, 0x7c, 0xd8,
-    0xf9, 0x83, 0x89, 0x17, 0xa8, 0x1b, 0x6b, 0x09, 0xc1, 0x99, 0x49, 0xb0,
-    0x43, 0x06, 0xc6, 0x42, 0x4b, 0x7c, 0x85, 0x4b, 0xe6, 0x69, 0x38, 0x91,
-    0xce, 0x3d, 0x3c, 0x97, 0xd5, 0x14, 0x4f, 0x15, 0x5a, 0x81, 0x4d, 0x77,
-    0x40, 0xe0, 0xe1, 0x1c, 0x69, 0x3f, 0x1d, 0x65, 0x68, 0xb3, 0x98, 0x95,
-    0x30, 0x6c, 0xb0, 0x70, 0x93, 0x0c, 0xce, 0xec, 0xaf, 0x57, 0xc6, 0x9c,
-    0x34, 0xb4, 0x2b, 0xaf, 0xc3, 0x5e, 0x70, 0x87, 0x17, 0xe8, 0xc9, 0x54,
-    0x06, 0xb5, 0xb7, 0x83, 0xff, 0x46, 0x2b, 0xb6, 0x6a, 0x66, 0x2f, 0x6d,
-    0x0f, 0x96, 0x53, 0x66, 0x65, 0xb8, 0x7b, 0x48, 0x55, 0x83, 0xd3, 0xc4,
-    0x16, 0x93, 0xde, 0x72, 0x59, 0xf1, 0x9a, 0xab, 0xd5, 0xd5, 0xcb, 0x24,
-    0xa6, 0x4a, 0x4e, 0x57, 0xf3, 0x6e, 0xca, 0xb1, 0xeb, 0x7d, 0xdb, 0x02,
-    0xd2, 0x79, 0x89, 0xef, 0xa2, 0x8b, 0xee, 0x6f, 0xdc, 0x5e, 0x65, 0xa5,
-    0x09, 0x33, 0x51, 0xb5, 0x21, 0xc8, 0xc6, 0xab, 0xed, 0xd5, 0x50, 0x93,
-    0x39, 0x71, 0x97, 0xd3, 0x2c, 0xdd, 0xaf, 0xb1, 0xc6, 0x9b, 0x4b, 0x69,
-    0x98, 0xae, 0xaf, 0x21, 0xa0, 0x8a, 0x90, 0x25, 0xe0, 0xf4, 0x8c, 0xf2,
-    0xc3, 0x4f, 0x64, 0xb6, 0xc6, 0x64, 0x90, 0xff, 0x95, 0x0a, 0xcc, 0x8c,
-    0xf4, 0x86, 0x80, 0x53, 0x8d, 0x51, 0x0b, 0xcd, 0x45, 0x4f, 0xcf, 0x7c,
-    0xc6, 0xdf, 0x08, 0x5e, 0xa7, 0xdf, 0x4f, 0xcf, 0x84, 0xde, 0xb8, 0x4d,
-    0x73, 0x40, 0x06, 0xbe, 0x33, 0x82, 0xe8, 0x41, 0x1b, 0x9a, 0xc3, 0x5b,
-    0xb6, 0xf3, 0xfc, 0x32, 0x98, 0xcc, 0xcc, 0x5e, 0xd5, 0xb7, 0x86, 0x0f,
-    0xc8, 0x59, 0x72, 0xcb, 0x9a, 0xc5, 0x3c, 0x50, 0xb8, 0x25, 0xb8, 0x87,
-    0x3e, 0x49, 0xd4, 0x2d, 0x2f, 0x50, 0x35, 0xeb, 0xb8, 0x10, 0xa7, 0xea,
-    0xb1, 0xe2, 0x0c, 0x6a, 0x84, 0x2c, 0xe2, 0x7a, 0x26, 0xef, 0x7e, 0x6b,
-    0x1e, 0x47, 0x6e, 0x98, 0xc0, 0x3f, 0x92, 0x24, 0xe7, 0x88, 0xf9, 0x18,
-    0x78, 0x37, 0x8a, 0x54, 0xa6, 0x2b, 0x5b, 0xf0, 0xc7, 0xe2, 0x98, 0xa4,
-    0xa6, 0x2e, 0xc3, 0x6a, 0x75, 0x66, 0x51, 0xe8, 0x0d, 0x90, 0xfd, 0xa7,
-    0xec, 0x22, 0xb3, 0x7d, 0x9d, 0x0c, 0xfe, 0x72, 0x7f, 0x98, 0xf6, 0x86,
-    0x30, 0xd3, 0x7c, 0xee, 0xa5, 0xc5, 0x20, 0x89, 0x79, 0x04, 0x8e, 0xa8,
-    0xb6, 0x94, 0x70, 0x4e, 0x75, 0xe5, 0xa0, 0xae, 0x8c, 0x7f, 0x72, 0x4c,
-    0xd5, 0x9f, 0xd2, 0x56, 0x0d, 0xb2, 0x28, 0x45, 0x99, 0xf8, 0x40, 0xd4,
-    0x3f, 0x42, 0x4a, 0x0c, 0x92, 0x23, 0xe1, 0x17, 0xaf, 0x68, 0xa6, 0x0f,
-    0x1d, 0x32, 0x0d, 0xf8, 0x08, 0x8e, 0xdc, 0x79, 0x68, 0xf0, 0xfe, 0x0b,
-    0xda, 0x94, 0x2d, 0xa6, 0xa7, 0x76, 0x7e, 0xd6, 0xca, 0xec, 0x7c, 0x37,
-    0x52, 0x4f, 0x77, 0xcf, 0xa3, 0xcf, 0x8a, 0xfe, 0x89, 0xd9, 0x3e, 0xbc,
-    0xb5, 0x06, 0xa0, 0x21, 0x91, 0x89, 0x77, 0x84, 0x85, 0x43, 0x2a, 0x65,
-    0xec, 0x75, 0x4d, 0x0d, 0x1c, 0x79, 0x0f, 0x61, 0xca, 0x3e, 0x62, 0xbb,
-    0x41, 0xf9, 0x4c, 0x5c, 0x3b, 0xde, 0x33, 0x8e, 0xdf, 0x51, 0x72, 0x93,
-    0xca, 0xa6, 0xc7, 0x16, 0xe5, 0xb3, 0x22, 0xb6, 0x2e, 0xbf, 0xae, 0x1d,
-    0x91, 0x1d, 0x49, 0x96, 0xa3, 0x25, 0xd4, 0xce, 0x6f, 0xf0, 0xfb, 0xb7,
-    0xf5, 0x4a, 0x24, 0x03, 0x54, 0x4b, 0x7f, 0x0b, 0xb4, 0x31, 0xb4, 0x33,
-    0xb7, 0x40, 0xf0, 0xd5, 0x4c, 0xee, 0xe3, 0x4b, 0x12, 0x8c, 0xc9, 0xa7,
-    0x06, 0xb1, 0x02, 0x5a, 0x14, 0x6f, 0xe2, 0x3b, 0x68, 0x9b, 0x3d, 0xfc,
-    0x83, 0x4a, 0xcc, 0xb5, 0x77, 0xe7, 0xf0, 0x1b, 0x52, 0xce, 0x60, 0x89,
-    0xe2, 0x45, 0x76, 0xaa, 0x76, 0x70, 0xc2, 0xfd, 0x21, 0x8f, 0x1d, 0x67,
-    0x1a, 0x4c, 0xe8, 0x81, 0x2b, 0x2e, 0xa9, 0x56, 0x0a, 0x27, 0x0f, 0x81,
-    0xba, 0x5c, 0x4f, 0xfa, 0x6e, 0x7e, 0x33, 0x7d, 0x78, 0xed, 0xd2, 0xe3,
-    0x24, 0xae, 0x24, 0xb2, 0x1b, 0x62, 0x71, 0x0e, 0x73, 0xfe, 0x8a, 0x3b,
-    0x98, 0x0d, 0x82, 0x8e, 0x8d, 0x0f, 0xb3, 0xe2, 0x65, 0x87, 0xeb, 0x36,
-    0x91, 0x4d, 0x8a, 0xfb, 0x22, 0x7a, 0x23, 0x2c, 0xe1, 0xb6, 0x94, 0xb6,
-    0x90, 0x94, 0xcc, 0x0c, 0x7d, 0x02, 0x36, 0x56, 0xda, 0x45, 0x20, 0x90,
-    0x48, 0xdb, 0xa4, 0xf5, 0x27, 0xac, 0x22, 0x49, 0x25, 0xaa, 0xd8, 0xa7,
-    0x79, 0x38, 0x80, 0xc0, 0x95, 0xc7, 0xd1, 0x5c, 0x17, 0x7c, 0xa7, 0xec,
-    0xd2, 0x63, 0xc6, 0xc6, 0x55, 0xfe, 0x78, 0x99, 0x06, 0x2c, 0x6e, 0x4f,
-    0xfe, 0xd1, 0x5b, 0x8c, 0x2f, 0xa1, 0x42, 0x03, 0x26, 0x5a, 0x5e, 0xda,
-    0xef, 0x43, 0xd2, 0x0e, 0xf9, 0x5f, 0xdb, 0x1d, 0x9c, 0xd1, 0xcb, 0x65,
-    0x84, 0x26, 0xed, 0x91, 0x8f, 0x16, 0xb4, 0x1c, 0xc0, 0xb3, 0x8d, 0x79,
-    0xae, 0x9b, 0xcb, 0x36, 0x6d, 0xcd, 0x67, 0x1f, 0x87, 0x11, 0x2a, 0x7c,
-    0xb1, 0x8c, 0xfb, 0x06, 0xab, 0xd2, 0xd6, 0x2a, 0xe3, 0x45, 0x6c, 0xa5,
-    0xc0, 0x19, 0x6b, 0xfc, 0xc3, 0xb7, 0x54, 0x35, 0xda, 0xdf, 0x12, 0x97,
-    0x5c, 0xac, 0x59, 0xb4, 0x42, 0x25, 0xef, 0x04, 0xf7, 0x4c, 0xdb, 0x74,
-    0xb9, 0x68, 0x8f, 0xee, 0x37, 0x0a, 0xc6, 0x21, 0x86, 0x0f, 0x6f, 0x8e,
-    0xab, 0xd5, 0x7b, 0x38, 0x5e, 0x5f, 0x7d, 0xb9, 0x5a, 0xcb, 0xce, 0xa0,
-    0x56, 0x37, 0x13, 0x71, 0x4b, 0xba, 0x43, 0x7c, 0xc0, 0xb7, 0x7f, 0x32,
-    0xd7, 0x46, 0x27, 0x58, 0xfc, 0xdb, 0xb5, 0x64, 0x20, 0x3b, 0x20, 0x85,
-    0x79, 0xa8, 0x9a, 0x22, 0xaf, 0x29, 0x86, 0xc5, 0x9d, 0x23, 0x96, 0x52,
-    0xca, 0xc7, 0x9d, 0x92, 0x26, 0xe5, 0x3a, 0x60, 0xd6, 0xad, 0x8d, 0x5a,
-    0xd9, 0x29, 0xbe, 0xd5, 0x5c, 0x3a, 0x77, 0xda, 0x34, 0xe2, 0x76, 0xcb,
-    0x98, 0xa4, 0xf3, 0x33, 0xf1, 0x68, 0x20, 0x83, 0x95, 0x0b, 0x8d, 0x93,
-    0x59, 0x02, 0x0c, 0x8f, 0xe4, 0xc4, 0xb0, 0xe7, 0x61, 0x0d, 0xf9, 0x80,
-    0x20, 0x58, 0x40, 0xea, 0xb7, 0x0b, 0x1b, 0xad, 0xe3, 0x30, 0x3b, 0x30,
-    0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14,
-    0x2d, 0x77, 0x79, 0x79, 0x90, 0x41, 0x75, 0xf4, 0x4a, 0x7f, 0xf7, 0x15,
-    0x94, 0x28, 0x62, 0xf7, 0x69, 0xd4, 0x44, 0x27, 0x04, 0x14, 0x2b, 0x2f,
-    0xd9, 0x24, 0xc3, 0x8a, 0x34, 0xbb, 0x52, 0x52, 0x7b, 0xf6, 0x0e, 0x7b,
-    0xfe, 0x3a, 0x66, 0x47, 0x40, 0x49, 0x02, 0x02, 0x07, 0xd0,
-};
-
-// kPBES2WithSHA1 is a PKCS#12 file using PBES2 and HMAC-SHA-1 created with:
-// openssl pkcs12 -export -inkey key.pem -in cert.pem -keypbe AES-128-CBC -certpbe AES-128-CBC
-//
-// This was generated with an older OpenSSL, which used hmacWithSHA1 as the PRF.
-// (There is currently no way to specify the PRF in the pkcs12 command.)
-static const uint8_t kPBES2WithSHA1[] = {
-  0x30, 0x82, 0x0a, 0x03, 0x02, 0x01, 0x03, 0x30, 0x82, 0x09, 0xc9, 0x06,
-  0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
-  0x09, 0xba, 0x04, 0x82, 0x09, 0xb6, 0x30, 0x82, 0x09, 0xb2, 0x30, 0x82,
-  0x04, 0x34, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-  0x06, 0xa0, 0x82, 0x04, 0x25, 0x30, 0x82, 0x04, 0x21, 0x02, 0x01, 0x00,
-  0x30, 0x82, 0x04, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-  0x01, 0x07, 0x01, 0x30, 0x49, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-  0x0d, 0x01, 0x05, 0x0d, 0x30, 0x3c, 0x30, 0x1b, 0x06, 0x09, 0x2a, 0x86,
-  0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x0e, 0x04, 0x08, 0xdb,
-  0x48, 0xe6, 0x98, 0x09, 0x8f, 0x6e, 0x2d, 0x02, 0x02, 0x08, 0x00, 0x30,
-  0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02,
-  0x04, 0x10, 0xee, 0xb3, 0x10, 0xe5, 0x21, 0x85, 0x03, 0x3e, 0x69, 0xad,
-  0xdf, 0x78, 0xa7, 0xd8, 0xac, 0xf1, 0x80, 0x82, 0x03, 0xc0, 0xcb, 0x58,
-  0x11, 0x28, 0x1d, 0xbc, 0x3c, 0x8c, 0xe7, 0x7b, 0x15, 0x67, 0x30, 0xf3,
-  0x2b, 0x94, 0x10, 0x8c, 0xbe, 0xfd, 0xaa, 0x11, 0xd7, 0x99, 0xee, 0x21,
-  0xb6, 0x1b, 0x4f, 0x53, 0xcb, 0x44, 0xff, 0x4f, 0xbf, 0xf6, 0x43, 0x3d,
-  0x12, 0xe6, 0x09, 0xe8, 0x05, 0xdd, 0x2f, 0xc5, 0x39, 0xde, 0x0c, 0x88,
-  0xe8, 0x4e, 0x89, 0x8f, 0x5f, 0xdf, 0x23, 0x50, 0xe6, 0xb7, 0xba, 0x1a,
-  0xdd, 0x1c, 0x63, 0x51, 0x0e, 0x71, 0xb7, 0xf7, 0x39, 0x3c, 0xd4, 0xe7,
-  0x52, 0x50, 0xc5, 0xd7, 0xbf, 0x65, 0x94, 0x72, 0x97, 0x2a, 0xb9, 0x68,
-  0xc2, 0xbd, 0x0c, 0x97, 0x02, 0x74, 0x23, 0x7f, 0x11, 0x6b, 0xea, 0xb4,
-  0xe4, 0x2f, 0xf0, 0x8b, 0x91, 0x5c, 0xdb, 0xae, 0x10, 0xbf, 0x89, 0xbc,
-  0x62, 0xef, 0x99, 0xbf, 0x07, 0x59, 0x58, 0x12, 0xef, 0xaf, 0xe6, 0xcd,
-  0x30, 0x27, 0xe4, 0xab, 0x44, 0xf7, 0xf9, 0x14, 0xb2, 0x5d, 0xfa, 0x97,
-  0xe6, 0x9a, 0xed, 0x85, 0x60, 0x86, 0xd9, 0xb0, 0xd7, 0xa4, 0xe4, 0x00,
-  0xa8, 0xee, 0xbb, 0xfc, 0x0d, 0xe8, 0x58, 0x7a, 0xca, 0x02, 0x1d, 0x02,
-  0xab, 0xbd, 0x16, 0x50, 0x4f, 0xfc, 0x60, 0xde, 0x48, 0xb1, 0x7f, 0xea,
-  0xba, 0x45, 0x7b, 0x29, 0xfe, 0x8e, 0xed, 0x48, 0xd2, 0x31, 0x64, 0xda,
-  0x89, 0x84, 0x6f, 0xd1, 0xd2, 0xb1, 0x7b, 0x97, 0x19, 0x38, 0x16, 0xd9,
-  0x3f, 0xd6, 0xdb, 0x6f, 0xab, 0x56, 0x34, 0xca, 0x34, 0x9c, 0x57, 0x41,
-  0x6e, 0x87, 0x85, 0x2a, 0xa8, 0xfb, 0xe9, 0xf6, 0x3d, 0xb6, 0x83, 0x7b,
-  0x02, 0xc9, 0xbe, 0xf1, 0xbb, 0x8e, 0xe5, 0x68, 0xae, 0xaa, 0xe1, 0x25,
-  0x8d, 0x1f, 0x1f, 0x52, 0x45, 0x3e, 0xef, 0x33, 0xd8, 0x58, 0xd9, 0x48,
-  0xd4, 0xb5, 0xe1, 0x53, 0x21, 0xb5, 0xbd, 0xd4, 0x63, 0x1f, 0xbf, 0xe4,
-  0x30, 0x5e, 0xc3, 0x63, 0xce, 0xdc, 0x12, 0x8c, 0xc7, 0x0c, 0xea, 0x3b,
-  0xf3, 0x0b, 0x38, 0x8d, 0xcc, 0x9b, 0xe7, 0xa0, 0x14, 0x5e, 0x48, 0x9c,
-  0x74, 0x86, 0x8e, 0x2b, 0x77, 0x80, 0xbb, 0x85, 0xa6, 0xd4, 0x25, 0x6e,
-  0x75, 0x07, 0x59, 0xd6, 0x88, 0x00, 0x35, 0x03, 0x5a, 0xb0, 0x86, 0x7e,
-  0x01, 0xa7, 0x77, 0x74, 0x13, 0xfa, 0x9f, 0x2d, 0xe3, 0x90, 0xda, 0x68,
-  0x23, 0x36, 0x0b, 0x62, 0x21, 0x76, 0xda, 0x6c, 0x05, 0x35, 0x80, 0xfc,
-  0xee, 0x5f, 0x3c, 0xac, 0x60, 0x2a, 0x9c, 0x6e, 0x4c, 0xaa, 0xa3, 0xd1,
-  0xdf, 0x2c, 0x7e, 0x0e, 0xc0, 0xa0, 0x84, 0xe4, 0xb2, 0x33, 0x1f, 0x8c,
-  0xcb, 0x74, 0x31, 0x18, 0x5b, 0x0b, 0x18, 0x41, 0xc6, 0x87, 0x13, 0xa2,
-  0xad, 0x1d, 0x43, 0x5e, 0x67, 0xd0, 0x31, 0xf5, 0x61, 0x7c, 0x3d, 0x16,
-  0x55, 0x01, 0x94, 0x45, 0xa4, 0x50, 0x0f, 0xb1, 0x1b, 0x81, 0x51, 0xa7,
-  0x92, 0xae, 0xa3, 0x6d, 0x4e, 0x55, 0x46, 0x37, 0x98, 0xe1, 0xe4, 0x5c,
-  0x29, 0x79, 0xc9, 0x76, 0x0a, 0xb5, 0x9d, 0x1b, 0x8a, 0xf6, 0xab, 0xeb,
-  0x69, 0x6e, 0x17, 0x88, 0xeb, 0x82, 0xfa, 0x78, 0x2f, 0x8c, 0x30, 0xfd,
-  0xf1, 0x74, 0xcd, 0x53, 0x78, 0x27, 0x43, 0x82, 0x05, 0x37, 0x07, 0xb3,
-  0x4c, 0x89, 0x9d, 0x00, 0x1d, 0x73, 0xad, 0x0f, 0xcd, 0x63, 0xbe, 0x9b,
-  0xa9, 0x50, 0xa5, 0x43, 0x74, 0x86, 0x87, 0xbc, 0xd9, 0x97, 0x66, 0x84,
-  0x35, 0x3e, 0x67, 0xce, 0x92, 0x2c, 0x78, 0xc7, 0x88, 0x19, 0x6a, 0x1c,
-  0xa8, 0x93, 0x0b, 0x79, 0x21, 0xe5, 0x39, 0x1b, 0x00, 0x68, 0x2a, 0x0b,
-  0xac, 0x6a, 0x2f, 0xc1, 0x9c, 0x90, 0x18, 0x86, 0x63, 0x53, 0x72, 0x34,
-  0xd9, 0xa8, 0x92, 0xce, 0x64, 0x3a, 0xeb, 0xba, 0xd8, 0x31, 0xf3, 0xfb,
-  0x2a, 0xac, 0xc6, 0xe7, 0xd1, 0x0b, 0x7c, 0xfc, 0xbb, 0x69, 0x57, 0xc8,
-  0x97, 0x3d, 0xdb, 0x81, 0x77, 0x2a, 0x9f, 0x07, 0x2c, 0x79, 0x69, 0xbc,
-  0x51, 0x0e, 0x68, 0x11, 0x00, 0x10, 0xed, 0x9f, 0xb8, 0x8d, 0xa0, 0x25,
-  0x20, 0xd3, 0x3d, 0x08, 0x20, 0x46, 0xfa, 0x89, 0xef, 0x69, 0x4c, 0x60,
-  0x33, 0x80, 0xb9, 0x53, 0xb4, 0x7b, 0xab, 0x38, 0xf1, 0xcd, 0xb8, 0x75,
-  0xc4, 0x85, 0x0a, 0xda, 0xab, 0x19, 0x40, 0xd3, 0x88, 0xd5, 0xf7, 0x5f,
-  0x8e, 0xcd, 0x8e, 0xa4, 0x1c, 0x9c, 0x22, 0x6d, 0xce, 0x66, 0x29, 0xfa,
-  0x62, 0x6f, 0x01, 0xdc, 0x46, 0x45, 0x38, 0x64, 0xf7, 0xc4, 0x94, 0xfd,
-  0x48, 0x44, 0x70, 0x4d, 0xef, 0xf0, 0x4b, 0x95, 0xf8, 0x68, 0x8d, 0xb7,
-  0x35, 0x7d, 0xc6, 0xf5, 0x97, 0xce, 0x5d, 0xad, 0xe8, 0x5c, 0xeb, 0x4f,
-  0x9b, 0x5b, 0x03, 0xce, 0x33, 0x60, 0xf5, 0xce, 0xcc, 0xfe, 0xfb, 0x77,
-  0x40, 0xc4, 0xf4, 0x9d, 0xf3, 0x2c, 0xdb, 0x83, 0xc2, 0x1a, 0xf2, 0xb6,
-  0xbe, 0xfc, 0x2c, 0x7f, 0x29, 0x20, 0x35, 0x50, 0x00, 0x60, 0x03, 0xd2,
-  0xb3, 0x03, 0x18, 0x64, 0xb9, 0x64, 0x98, 0x33, 0xdb, 0x47, 0x43, 0xe2,
-  0xa1, 0x85, 0x79, 0x9b, 0xb1, 0x0b, 0x0e, 0xbb, 0x14, 0x5f, 0xb9, 0x16,
-  0xb6, 0xc3, 0xf6, 0x5c, 0x01, 0xe3, 0xaa, 0x3f, 0x03, 0xad, 0x18, 0xeb,
-  0x0e, 0x3d, 0xa3, 0x1f, 0xcc, 0x4d, 0x48, 0x44, 0x7e, 0xda, 0xb9, 0x9d,
-  0x17, 0xe8, 0x92, 0x46, 0xea, 0xf5, 0x3e, 0x05, 0x4e, 0xa7, 0xb5, 0x94,
-  0x6d, 0x95, 0x42, 0xa7, 0x71, 0xfb, 0xc2, 0x45, 0xd6, 0xd2, 0x86, 0xd0,
-  0x79, 0x99, 0x1f, 0x96, 0x78, 0x22, 0xeb, 0x05, 0x26, 0xf2, 0xa1, 0x67,
-  0x67, 0x2b, 0xae, 0x1d, 0x28, 0x42, 0xd6, 0xbe, 0x08, 0xf6, 0xb7, 0x54,
-  0xc8, 0x82, 0xbf, 0x92, 0x0f, 0x2c, 0xba, 0x47, 0xe2, 0x01, 0x73, 0x2c,
-  0xd7, 0x34, 0x84, 0x2f, 0xb6, 0x41, 0x84, 0xeb, 0x7a, 0xb2, 0xf9, 0xdd,
-  0x31, 0xbe, 0x07, 0xb4, 0x88, 0x05, 0xd8, 0xe1, 0x79, 0x55, 0xe6, 0x4b,
-  0x8c, 0xdc, 0xd1, 0x76, 0x58, 0x72, 0x42, 0x28, 0xb3, 0x9f, 0xd0, 0x05,
-  0x37, 0x6b, 0x65, 0x74, 0xce, 0x0d, 0x01, 0xa9, 0x49, 0xc5, 0x90, 0xab,
-  0x90, 0x16, 0x2c, 0x9c, 0xba, 0xcb, 0x94, 0xc7, 0xfa, 0xe0, 0x39, 0x82,
-  0xa2, 0x88, 0xd6, 0x0c, 0xc4, 0x4d, 0xfe, 0xb4, 0xbc, 0x87, 0xe5, 0x63,
-  0x3b, 0x6b, 0xf0, 0xd1, 0x09, 0x39, 0x8f, 0x51, 0x4f, 0x32, 0xae, 0xed,
-  0x0c, 0xff, 0x79, 0x52, 0x19, 0xa9, 0x4e, 0x45, 0x11, 0xc3, 0x5f, 0xd6,
-  0x2b, 0x66, 0xe3, 0x9c, 0xbe, 0xbc, 0xda, 0x65, 0x25, 0xcd, 0xf5, 0x73,
-  0x45, 0x09, 0xf5, 0x5d, 0x6b, 0x83, 0x45, 0x28, 0x98, 0x2c, 0x58, 0x44,
-  0xca, 0x37, 0xeb, 0xc3, 0xc2, 0x10, 0x77, 0x14, 0x79, 0x9b, 0xd8, 0xb2,
-  0xbf, 0x45, 0xd5, 0x63, 0xe4, 0x37, 0x42, 0x7b, 0x2d, 0xe2, 0x49, 0xb3,
-  0x18, 0x8e, 0x86, 0x73, 0xf1, 0x59, 0x8a, 0xf2, 0x3c, 0x49, 0x12, 0x7b,
-  0xb1, 0x40, 0x8c, 0x8c, 0xac, 0x05, 0x50, 0xbd, 0x9b, 0x3b, 0x84, 0x81,
-  0x68, 0x26, 0x88, 0x1b, 0xbf, 0xa0, 0x28, 0xc2, 0x06, 0xa9, 0xe4, 0xd9,
-  0x1f, 0x5d, 0xca, 0x96, 0x4f, 0xfe, 0xd8, 0x64, 0xee, 0x73, 0x30, 0x82,
-  0x05, 0x76, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-  0x01, 0xa0, 0x82, 0x05, 0x67, 0x04, 0x82, 0x05, 0x63, 0x30, 0x82, 0x05,
-  0x5f, 0x30, 0x82, 0x05, 0x5b, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-  0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02, 0xa0, 0x82, 0x05, 0x23, 0x30, 0x82,
-  0x05, 0x1f, 0x30, 0x49, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-  0x01, 0x05, 0x0d, 0x30, 0x3c, 0x30, 0x1b, 0x06, 0x09, 0x2a, 0x86, 0x48,
-  0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x0e, 0x04, 0x08, 0xe3, 0x3e,
-  0xd3, 0x8d, 0xd6, 0xb5, 0x8a, 0x05, 0x02, 0x02, 0x08, 0x00, 0x30, 0x1d,
-  0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02, 0x04,
-  0x10, 0x61, 0xa0, 0x2f, 0x8d, 0x0c, 0xa1, 0x03, 0xc9, 0xdf, 0x2e, 0x81,
-  0x65, 0xe0, 0x63, 0x70, 0x55, 0x04, 0x82, 0x04, 0xd0, 0x24, 0x1e, 0xf9,
-  0x1d, 0xc4, 0xe9, 0xbf, 0x49, 0x3c, 0x1e, 0x55, 0x4a, 0xd4, 0xb0, 0x0c,
-  0xdd, 0x5b, 0x92, 0xb2, 0xed, 0x18, 0xac, 0x66, 0x90, 0x1b, 0x29, 0x3d,
-  0x10, 0xad, 0x02, 0xe7, 0x17, 0x83, 0x44, 0x67, 0xba, 0x11, 0x6f, 0x05,
-  0xf5, 0xf7, 0x37, 0xcb, 0x5a, 0xe9, 0x0e, 0xc3, 0x4b, 0x1b, 0x62, 0xee,
-  0xb2, 0xb7, 0x14, 0x85, 0x07, 0x2d, 0x95, 0x83, 0xa9, 0xdc, 0x3d, 0x4b,
-  0x33, 0xad, 0x68, 0xbf, 0x54, 0xf8, 0xef, 0x25, 0x05, 0x40, 0xcd, 0x61,
-  0xbe, 0x12, 0xeb, 0x78, 0x75, 0x36, 0x08, 0x8c, 0x5a, 0x57, 0xa1, 0x98,
-  0xd5, 0x42, 0x01, 0x1b, 0x4c, 0x25, 0xc2, 0x18, 0x9f, 0x91, 0xfe, 0x78,
-  0x88, 0x99, 0x47, 0x5a, 0x20, 0x2c, 0x37, 0x31, 0x05, 0x98, 0xef, 0x91,
-  0x6e, 0xeb, 0x2e, 0x86, 0x90, 0x61, 0xb1, 0x57, 0x1a, 0x05, 0x82, 0x14,
-  0x0c, 0xa8, 0x94, 0xae, 0x56, 0x7b, 0xd6, 0x2f, 0x8b, 0x2e, 0x91, 0xa6,
-  0x12, 0x68, 0x1f, 0x06, 0x09, 0x2f, 0xa6, 0xed, 0x33, 0x99, 0x72, 0x56,
-  0xe5, 0xf7, 0xea, 0xcc, 0xcf, 0x27, 0xa5, 0xad, 0x49, 0x5a, 0xbc, 0x7b,
-  0xe3, 0x62, 0x63, 0x8f, 0x00, 0x2b, 0x96, 0xc5, 0x3f, 0xaf, 0x24, 0xba,
-  0xf6, 0x8d, 0xe2, 0xef, 0x18, 0x50, 0xd6, 0xd8, 0x4f, 0xb2, 0x5d, 0xb7,
-  0x96, 0x6f, 0x02, 0xf7, 0x7d, 0xf2, 0xa2, 0x7b, 0x9b, 0x13, 0x98, 0xde,
-  0xdd, 0x6e, 0xb5, 0x48, 0x52, 0x8e, 0x44, 0xad, 0xe0, 0xcf, 0x40, 0x9f,
-  0xfd, 0x88, 0x33, 0x66, 0xce, 0x6a, 0x49, 0x5f, 0xe7, 0x4b, 0x36, 0x93,
-  0x7f, 0x49, 0x62, 0xc9, 0x5a, 0xae, 0xa1, 0xca, 0xf7, 0x5a, 0xbe, 0x85,
-  0x77, 0x9a, 0x8f, 0xce, 0x4d, 0x84, 0x81, 0xd0, 0xa2, 0xee, 0x60, 0x92,
-  0x86, 0x16, 0x2a, 0xd5, 0x08, 0xb6, 0x58, 0x63, 0x07, 0x7c, 0x41, 0xac,
-  0x97, 0x4f, 0xf0, 0xcf, 0xd8, 0xd2, 0xb1, 0xd7, 0x1d, 0xe5, 0xb8, 0x7c,
-  0x04, 0x2b, 0xd9, 0xee, 0xf7, 0x22, 0x88, 0xa1, 0x53, 0xdb, 0x5e, 0x5b,
-  0x47, 0x49, 0xeb, 0xcf, 0x04, 0x78, 0x69, 0xd1, 0xfc, 0x8a, 0xa9, 0x61,
-  0x92, 0xbf, 0x5c, 0x7f, 0xde, 0x49, 0x42, 0xfc, 0x0d, 0xc2, 0xa2, 0x8f,
-  0xba, 0xdf, 0x12, 0xa4, 0x62, 0xfb, 0x8d, 0xd3, 0xc5, 0xf9, 0x85, 0x4c,
-  0x17, 0x70, 0xb7, 0xf7, 0x99, 0x29, 0x52, 0x92, 0x36, 0xc5, 0x4b, 0x31,
-  0x23, 0x5c, 0x09, 0x27, 0x3c, 0xa0, 0x76, 0x5d, 0x92, 0x99, 0x63, 0x88,
-  0xca, 0xad, 0xed, 0xd7, 0x85, 0x98, 0x2f, 0xbe, 0xaa, 0xa5, 0xf3, 0x0a,
-  0x76, 0x13, 0x01, 0x90, 0x8a, 0xe7, 0x5a, 0x2d, 0x2b, 0x1a, 0x80, 0x33,
-  0x86, 0xab, 0xd8, 0xa7, 0xae, 0x0b, 0x7d, 0xcd, 0x64, 0x8d, 0xa6, 0xb6,
-  0xfb, 0x83, 0x9f, 0x91, 0x23, 0xcb, 0xda, 0x63, 0xd0, 0xde, 0xf4, 0xdd,
-  0xaa, 0x23, 0x49, 0x6c, 0x44, 0xfa, 0x6f, 0x12, 0x13, 0x90, 0x37, 0xde,
-  0xa3, 0x72, 0x45, 0x1a, 0xa7, 0xab, 0x01, 0x6d, 0xd6, 0x34, 0xe7, 0x51,
-  0x0e, 0x33, 0xbc, 0x09, 0xbf, 0xb6, 0x16, 0xf8, 0xd3, 0x11, 0x11, 0xd1,
-  0x5f, 0xaa, 0x32, 0xb6, 0x5b, 0xe7, 0xbc, 0xdd, 0xaa, 0xe4, 0xed, 0x42,
-  0x3d, 0x2e, 0xf7, 0xa1, 0x06, 0x39, 0xd4, 0x00, 0xc6, 0xc8, 0xed, 0xb5,
-  0x96, 0xc1, 0xbf, 0x4c, 0xf1, 0xf6, 0xc6, 0x59, 0xf4, 0x99, 0x9c, 0x10,
-  0x22, 0xa1, 0x3a, 0xcd, 0x94, 0xac, 0x0b, 0xc8, 0x7e, 0x29, 0xbc, 0xf0,
-  0xae, 0x27, 0x7a, 0xb8, 0x5c, 0xa0, 0x13, 0x36, 0xb5, 0x19, 0x4b, 0x2c,
-  0xc1, 0xce, 0x49, 0x57, 0x1d, 0x36, 0xf0, 0xc2, 0x4c, 0xdf, 0x6d, 0xc9,
-  0x64, 0x68, 0xcb, 0xea, 0x22, 0x32, 0xd7, 0x11, 0x2c, 0x77, 0xbe, 0x01,
-  0xa3, 0x82, 0x2d, 0xa1, 0x4b, 0x13, 0x93, 0x87, 0x3d, 0x01, 0x74, 0xc6,
-  0xc6, 0xf9, 0xae, 0x2e, 0xa1, 0x44, 0x5d, 0x47, 0x6c, 0x6f, 0xc6, 0xce,
-  0xef, 0x32, 0xf8, 0x8d, 0x53, 0x4d, 0xa5, 0xf0, 0xa0, 0x51, 0x7e, 0xd8,
-  0x35, 0x55, 0x2a, 0x04, 0xb9, 0x42, 0xa7, 0x51, 0xba, 0xad, 0xce, 0x88,
-  0x7b, 0x93, 0x25, 0x9d, 0x03, 0x08, 0xfa, 0x75, 0x38, 0x63, 0x78, 0x13,
-  0x11, 0x9d, 0xf6, 0xcc, 0x18, 0xe3, 0x99, 0xa9, 0x5d, 0x90, 0x6b, 0xbf,
-  0x9c, 0x69, 0x99, 0x63, 0x27, 0x35, 0x8a, 0x26, 0x07, 0x67, 0xd1, 0xae,
-  0x57, 0xec, 0xc0, 0x45, 0x6e, 0x2a, 0x42, 0x46, 0x8f, 0xe4, 0x84, 0xc7,
-  0x67, 0x06, 0x0c, 0xa7, 0x7e, 0x5c, 0x20, 0x80, 0xdc, 0xc1, 0xe4, 0x7a,
-  0x74, 0x76, 0x8f, 0x41, 0x78, 0xce, 0x6a, 0xf9, 0xcb, 0x7f, 0xe9, 0x17,
-  0x70, 0x45, 0x01, 0x9a, 0xc3, 0x9c, 0xa2, 0x68, 0xa0, 0x79, 0xfd, 0x44,
-  0x4c, 0xc8, 0xa0, 0xaf, 0xa5, 0xba, 0x0f, 0x03, 0x30, 0x43, 0x4a, 0x1d,
-  0x3e, 0xd4, 0x8e, 0x1f, 0x6d, 0x09, 0xf9, 0x63, 0xde, 0xd2, 0x9e, 0x77,
-  0xe7, 0xde, 0x61, 0x52, 0x76, 0x0f, 0x6d, 0x37, 0xf7, 0xc2, 0x69, 0x96,
-  0x9d, 0xc5, 0xd9, 0x15, 0x10, 0xf2, 0x22, 0x1f, 0x3b, 0x83, 0xb3, 0xb4,
-  0x2c, 0x25, 0x36, 0xc3, 0x3a, 0x24, 0x17, 0xed, 0xad, 0x11, 0x1f, 0x46,
-  0x31, 0x0c, 0x6a, 0x3c, 0xd2, 0x1a, 0xe7, 0x41, 0xb3, 0x75, 0xd8, 0x80,
-  0xb3, 0xf8, 0x2b, 0xab, 0xb5, 0x81, 0xc6, 0x5e, 0x40, 0x9a, 0x77, 0xaa,
-  0x79, 0x31, 0x1f, 0x79, 0xfe, 0x0f, 0x0f, 0xb0, 0x36, 0xb7, 0xdc, 0xca,
-  0xf6, 0xbf, 0x80, 0xeb, 0x78, 0xc6, 0x73, 0x6a, 0xb3, 0x71, 0x69, 0x9c,
-  0x1d, 0xdd, 0x90, 0xd9, 0x73, 0x07, 0x43, 0x37, 0x19, 0x7f, 0x22, 0xa4,
-  0x9a, 0x4d, 0x98, 0x66, 0x10, 0x5b, 0x08, 0x62, 0xb3, 0xd8, 0x2f, 0x56,
-  0x68, 0x22, 0xdf, 0xd1, 0xa2, 0x5a, 0x45, 0xf9, 0xb4, 0xb9, 0xf2, 0x48,
-  0x4e, 0x38, 0x1a, 0x23, 0x36, 0x6d, 0x42, 0x56, 0xbb, 0x32, 0xe3, 0x00,
-  0x84, 0xa9, 0xe2, 0xba, 0xb6, 0x86, 0xc9, 0xa6, 0x64, 0x8a, 0xd6, 0xa6,
-  0xc4, 0xd7, 0x3e, 0x8b, 0x34, 0x1b, 0x6b, 0x65, 0xfe, 0xb1, 0xc9, 0x93,
-  0xe1, 0xeb, 0x8a, 0x3b, 0xf1, 0x0f, 0xdb, 0x84, 0xe2, 0x2d, 0xf8, 0x69,
-  0x04, 0xee, 0xaf, 0x58, 0x2f, 0xc7, 0x96, 0x70, 0x4d, 0xd9, 0x4c, 0x1d,
-  0x52, 0x38, 0xc6, 0x26, 0x27, 0x41, 0x38, 0x0b, 0xa5, 0x1c, 0x16, 0xd0,
-  0x1d, 0x32, 0x99, 0xb9, 0x1f, 0x35, 0xaf, 0x02, 0xb0, 0x13, 0x0f, 0x95,
-  0xd3, 0x9b, 0xd6, 0x09, 0xcc, 0x29, 0x46, 0xe8, 0xf1, 0x54, 0x4d, 0xb8,
-  0x96, 0xa6, 0x0d, 0x59, 0x61, 0x1f, 0xee, 0xaf, 0xbc, 0x23, 0x58, 0xff,
-  0xcf, 0x96, 0x91, 0x1f, 0x00, 0x80, 0x4e, 0x9a, 0xa2, 0xe0, 0x00, 0xf7,
-  0x3e, 0xb1, 0x91, 0x6c, 0x29, 0x58, 0x5e, 0xe7, 0xc7, 0x23, 0xfa, 0x88,
-  0xf7, 0xfb, 0x0b, 0x0e, 0x4a, 0x04, 0x46, 0xe0, 0x67, 0x10, 0x09, 0xea,
-  0xc0, 0xa9, 0xbe, 0x83, 0x11, 0x33, 0x8e, 0xfb, 0xd6, 0xd5, 0x67, 0xef,
-  0xb4, 0x13, 0x4d, 0x17, 0xa1, 0x44, 0xb7, 0x98, 0x77, 0xd0, 0x63, 0xe7,
-  0x9c, 0xa7, 0x96, 0x29, 0xe5, 0xfe, 0x72, 0x4c, 0xa9, 0x85, 0x9b, 0xc9,
-  0xf3, 0xf6, 0x05, 0x0a, 0x28, 0x68, 0x99, 0x31, 0xe8, 0x64, 0x30, 0x9c,
-  0x2a, 0x90, 0x48, 0x84, 0x00, 0x1a, 0x66, 0x0e, 0x3e, 0xf7, 0xaa, 0xc9,
-  0x6c, 0x5b, 0x57, 0x7b, 0xa9, 0x17, 0x91, 0x1e, 0x6b, 0xe8, 0x12, 0xa1,
-  0xd4, 0xde, 0x1e, 0x38, 0x14, 0x7b, 0xe0, 0x9a, 0x15, 0xae, 0x5a, 0x26,
-  0x93, 0x7a, 0xd6, 0x8d, 0x26, 0x61, 0x28, 0xf2, 0x40, 0x71, 0xc7, 0x8a,
-  0x2d, 0x69, 0x72, 0x04, 0x5b, 0xb9, 0xc1, 0x7b, 0x17, 0xde, 0x2c, 0xfc,
-  0xa9, 0xf2, 0xf8, 0x34, 0x33, 0x09, 0x87, 0x91, 0xdf, 0xeb, 0xf7, 0x57,
-  0x5b, 0x32, 0xe2, 0xd4, 0xe4, 0x47, 0x78, 0xe8, 0x9b, 0x1a, 0xab, 0x44,
-  0x55, 0x28, 0x98, 0x20, 0xa7, 0x16, 0x8b, 0x4e, 0x42, 0xf1, 0x91, 0xbe,
-  0x00, 0x87, 0x3a, 0x91, 0x63, 0x9a, 0xc2, 0x8d, 0x13, 0x34, 0x8b, 0x33,
-  0x02, 0x88, 0x1e, 0xb1, 0xa8, 0x07, 0x6d, 0xb1, 0xf5, 0xb3, 0x7a, 0x3d,
-  0x17, 0x3f, 0xbd, 0xa1, 0xdb, 0x04, 0x0f, 0x29, 0x7b, 0x0e, 0x98, 0x18,
-  0x63, 0x0b, 0x60, 0xcd, 0xa5, 0x0d, 0x5f, 0x1e, 0x53, 0xcd, 0xfa, 0xc0,
-  0xc7, 0x99, 0x53, 0x5f, 0xb7, 0xe5, 0x4a, 0x30, 0xde, 0x14, 0xc9, 0x49,
-  0x46, 0x31, 0xb6, 0x92, 0xf3, 0x4b, 0xc1, 0xb0, 0xdd, 0xec, 0x48, 0xff,
-  0x2d, 0x52, 0x53, 0x64, 0x27, 0x4c, 0x78, 0x96, 0x80, 0x90, 0xa3, 0xd7,
-  0xfd, 0x7a, 0x23, 0x36, 0xa0, 0x76, 0x9e, 0x96, 0xfc, 0xcd, 0xec, 0x58,
-  0xf8, 0x76, 0x4b, 0x2f, 0x8d, 0xb9, 0xd6, 0x89, 0xa1, 0x57, 0xe1, 0xc6,
-  0xed, 0x9a, 0x1e, 0xde, 0xc7, 0x68, 0x93, 0x2b, 0x2e, 0x84, 0x1a, 0xf9,
-  0x8c, 0x58, 0xb8, 0xf0, 0x29, 0xfe, 0x7b, 0x03, 0x84, 0xe8, 0x52, 0x1c,
-  0x01, 0xbb, 0xcc, 0x5d, 0x88, 0xcd, 0x37, 0x8b, 0xe2, 0x2d, 0x30, 0xd1,
-  0xbe, 0xf7, 0xc1, 0x95, 0xb7, 0x01, 0x43, 0xab, 0x30, 0x3f, 0x96, 0x47,
-  0x6d, 0x52, 0x29, 0x87, 0x10, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, 0x2a,
-  0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14,
-  0x14, 0x74, 0x2d, 0x52, 0x8e, 0x0d, 0x0c, 0x06, 0x6c, 0x32, 0x64, 0xd3,
-  0x7e, 0x33, 0x31, 0x68, 0x8b, 0x28, 0x1a, 0x75, 0x30, 0x31, 0x30, 0x21,
-  0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04,
-  0x14, 0x2f, 0x5c, 0xc6, 0xaf, 0xa7, 0xcc, 0xb5, 0x77, 0x40, 0xca, 0x71,
-  0xc3, 0x8c, 0xc6, 0x69, 0xdc, 0xc6, 0x7f, 0x54, 0xef, 0x04, 0x08, 0xf8,
-  0x9c, 0x8b, 0x12, 0x27, 0xe8, 0xec, 0x65, 0x02, 0x02, 0x08, 0x00};
-
-// kPBES2WithSHA256 is a PKCS#12 file using PBES2 and HMAC-SHA-256. It was
-// generated in the same way as |kPBES2WithSHA1|, but using OpenSSL 1.1.1b,
-// which uses hmacWithSHA256 as the PRF.
-static const uint8_t kPBES2WithSHA256[] = {
-    0x30, 0x82, 0x0a, 0x7f, 0x02, 0x01, 0x03, 0x30, 0x82, 0x0a, 0x45, 0x06,
-    0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
-    0x0a, 0x36, 0x04, 0x82, 0x0a, 0x32, 0x30, 0x82, 0x0a, 0x2e, 0x30, 0x82,
-    0x04, 0xa2, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-    0x06, 0xa0, 0x82, 0x04, 0x93, 0x30, 0x82, 0x04, 0x8f, 0x02, 0x01, 0x00,
-    0x30, 0x82, 0x04, 0x88, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x07, 0x01, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a, 0x86,
-    0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08, 0xb2,
-    0x5e, 0x0d, 0x6d, 0xda, 0xaa, 0x2f, 0xbe, 0x02, 0x02, 0x08, 0x00, 0x30,
-    0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09, 0x05,
-    0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
-    0x01, 0x02, 0x04, 0x10, 0x3c, 0x04, 0x78, 0x37, 0xb3, 0xb2, 0x24, 0xd3,
-    0xb5, 0x46, 0x20, 0xb7, 0xd2, 0xdd, 0x5d, 0x2e, 0x80, 0x82, 0x04, 0x20,
-    0x3a, 0x01, 0xe4, 0xf4, 0x57, 0xd3, 0xed, 0x14, 0xd0, 0x42, 0x3f, 0xd3,
-    0x61, 0xee, 0x84, 0xcd, 0x2b, 0x08, 0x60, 0x30, 0xbd, 0x72, 0xa7, 0xd5,
-    0xa4, 0xf2, 0x13, 0xe9, 0xf0, 0x44, 0x66, 0x26, 0x34, 0xe7, 0x2c, 0x5d,
-    0xc9, 0xb0, 0x4b, 0xab, 0x47, 0x16, 0xab, 0xe6, 0x06, 0xa6, 0x3b, 0x79,
-    0x41, 0x0c, 0x79, 0xd5, 0x9b, 0x02, 0x67, 0xd8, 0x7f, 0xc8, 0x36, 0x37,
-    0x27, 0xb4, 0x44, 0xa2, 0x5e, 0x0d, 0x38, 0xb8, 0x41, 0x8e, 0x3a, 0xf1,
-    0xe9, 0xab, 0xe0, 0x19, 0xd0, 0xe1, 0xc7, 0x92, 0xd4, 0x5b, 0x35, 0xf3,
-    0x79, 0x48, 0x3b, 0xfc, 0x25, 0xfc, 0xc6, 0x9f, 0xed, 0x35, 0x28, 0x5b,
-    0xfa, 0xee, 0x50, 0x42, 0xa3, 0xc3, 0x96, 0xee, 0xe0, 0x87, 0x33, 0x5e,
-    0xa7, 0xc7, 0x0a, 0xfe, 0xda, 0xe5, 0xd5, 0x29, 0x6a, 0x57, 0x08, 0x7f,
-    0x56, 0x37, 0x2a, 0x1a, 0xa0, 0x6d, 0xe9, 0x84, 0xac, 0xed, 0x0e, 0xd8,
-    0xc0, 0xd8, 0xc6, 0x77, 0xb1, 0xdd, 0x1b, 0xa1, 0xed, 0xa7, 0x79, 0x13,
-    0x2e, 0x5b, 0x9b, 0x80, 0x44, 0x9e, 0xff, 0x0a, 0x6e, 0x99, 0x33, 0xcf,
-    0xf1, 0x47, 0x24, 0xaa, 0x48, 0xe7, 0x2c, 0xb3, 0xe6, 0xdc, 0xd4, 0x1e,
-    0xe4, 0xb8, 0x5e, 0x72, 0xaf, 0x3f, 0xd3, 0x25, 0x4a, 0xac, 0x7b, 0x35,
-    0xb1, 0x82, 0xa5, 0xd9, 0xf8, 0x01, 0x12, 0x92, 0x49, 0x4c, 0x17, 0x07,
-    0xb2, 0xb1, 0x3e, 0xcb, 0xfd, 0xd1, 0x17, 0xb5, 0x65, 0x3d, 0x0c, 0x2b,
-    0x2b, 0xc0, 0x37, 0x9c, 0xe7, 0x04, 0x9b, 0x71, 0x5a, 0x10, 0xc0, 0xba,
-    0x3b, 0x31, 0xde, 0x0d, 0x66, 0x6c, 0x0d, 0x4c, 0x99, 0x22, 0x76, 0x2a,
-    0x75, 0x7f, 0x84, 0xd1, 0x07, 0x1f, 0x57, 0xf0, 0x0b, 0x71, 0x41, 0xea,
-    0x38, 0xe2, 0xe7, 0xbe, 0x11, 0x3c, 0x92, 0x8c, 0x7b, 0x0e, 0xb4, 0x7e,
-    0x76, 0xc4, 0x80, 0x41, 0xae, 0x4c, 0xe2, 0x38, 0x36, 0xcb, 0x82, 0x39,
-    0x38, 0x3a, 0x55, 0xb4, 0xe2, 0x35, 0x94, 0xc3, 0xae, 0x3d, 0xd1, 0x03,
-    0xf3, 0xdb, 0x00, 0xd9, 0xfa, 0x96, 0x62, 0x25, 0x97, 0x51, 0xc5, 0xcf,
-    0x84, 0xe8, 0xf7, 0x8b, 0x2f, 0x31, 0xeb, 0xa7, 0x0a, 0x22, 0x6f, 0xad,
-    0xf5, 0x28, 0x25, 0xaa, 0x99, 0x0e, 0xb1, 0x83, 0x9f, 0x70, 0x79, 0xaf,
-    0x10, 0x7c, 0x2c, 0x55, 0xfe, 0x24, 0x7d, 0xea, 0x85, 0x48, 0x8e, 0x7a,
-    0xf7, 0x47, 0xd8, 0x0c, 0x64, 0x97, 0xe0, 0x8f, 0x62, 0x5e, 0xd0, 0x4f,
-    0x21, 0xa4, 0x46, 0x8e, 0x28, 0xb0, 0xb1, 0x90, 0xec, 0x01, 0x7d, 0xc4,
-    0xc8, 0x6f, 0xf2, 0xe2, 0xb7, 0xc4, 0x35, 0x6c, 0xa9, 0xf6, 0xaf, 0xc2,
-    0xb6, 0xa9, 0x02, 0x6d, 0xb2, 0x8b, 0x43, 0x6b, 0x41, 0x80, 0x9d, 0x5e,
-    0x51, 0xa7, 0x31, 0x00, 0x1b, 0xb5, 0x24, 0xed, 0x40, 0x99, 0x33, 0xde,
-    0x87, 0xd1, 0x4b, 0x76, 0x78, 0x57, 0x4c, 0x33, 0x79, 0x89, 0xd3, 0xfa,
-    0x70, 0x0f, 0x2f, 0x31, 0x42, 0x8c, 0xce, 0xe9, 0xc0, 0x58, 0xe1, 0x30,
-    0x30, 0xf1, 0xe9, 0xab, 0xc8, 0x60, 0x7c, 0xe0, 0x6a, 0x99, 0xe7, 0xd3,
-    0x21, 0x1a, 0xcc, 0x98, 0x60, 0x44, 0xaa, 0xff, 0xee, 0xec, 0x34, 0x20,
-    0x19, 0xba, 0x03, 0x3b, 0x67, 0x6f, 0xee, 0xd5, 0xb3, 0xa7, 0x21, 0x57,
-    0xd6, 0x49, 0xaf, 0x91, 0x8f, 0xec, 0x70, 0xd0, 0x59, 0x1a, 0x79, 0xe2,
-    0xd2, 0x94, 0x82, 0x53, 0xfb, 0xea, 0xd6, 0x83, 0x49, 0x4a, 0x6f, 0xd6,
-    0xed, 0x15, 0xc3, 0x71, 0x08, 0x3a, 0xbf, 0xde, 0xa8, 0x2d, 0x54, 0xaf,
-    0x4a, 0x40, 0xbc, 0xe5, 0x53, 0xae, 0x4b, 0x3d, 0x70, 0xfe, 0x1c, 0x03,
-    0x1e, 0xb2, 0x9d, 0x1c, 0x35, 0xbd, 0x9a, 0xf8, 0xc5, 0xd1, 0xa5, 0x4a,
-    0x63, 0x18, 0x02, 0xd4, 0xff, 0xdd, 0xcd, 0xb3, 0x6c, 0x38, 0xd1, 0x9a,
-    0xad, 0x16, 0x71, 0xf1, 0xc6, 0x1d, 0x8f, 0x6c, 0x30, 0xfa, 0x2e, 0x13,
-    0x9d, 0x0b, 0x4e, 0xe6, 0xd3, 0x37, 0x80, 0x58, 0x26, 0x0d, 0x04, 0x97,
-    0xe6, 0x8d, 0xcc, 0x63, 0x3c, 0x39, 0x38, 0x2f, 0x7a, 0x73, 0x01, 0x0f,
-    0x22, 0x69, 0x47, 0x54, 0x9e, 0x42, 0xc8, 0x59, 0xb5, 0x35, 0x43, 0xb4,
-    0x37, 0x45, 0x59, 0x85, 0xf2, 0x47, 0xc3, 0xfb, 0x23, 0x13, 0x18, 0xef,
-    0xd8, 0x11, 0x70, 0x74, 0xce, 0x97, 0xcf, 0xbf, 0xd5, 0x2d, 0x99, 0x00,
-    0x86, 0x56, 0x9b, 0xdf, 0x05, 0x67, 0xf4, 0x49, 0x1e, 0xb5, 0x12, 0x23,
-    0x46, 0x04, 0x83, 0xf3, 0xc1, 0x59, 0xc7, 0x7b, 0xc3, 0x22, 0x0c, 0x2c,
-    0x1b, 0x7d, 0x18, 0xb6, 0xd2, 0xfa, 0x28, 0x36, 0x8b, 0x51, 0x6d, 0x58,
-    0xf4, 0xd6, 0xdf, 0x38, 0x94, 0xcf, 0x6c, 0x50, 0x4f, 0x0a, 0xf3, 0xc3,
-    0x91, 0x39, 0xa5, 0xc9, 0xbc, 0xa8, 0xeb, 0x24, 0x1a, 0xdd, 0x58, 0x9e,
-    0xdc, 0xb2, 0xee, 0xe1, 0xa5, 0x16, 0x68, 0xc2, 0x63, 0x8c, 0xc9, 0xa7,
-    0xbe, 0x1e, 0x30, 0x84, 0xa6, 0x28, 0xeb, 0x50, 0xd9, 0xdd, 0x15, 0xea,
-    0x64, 0x34, 0xf0, 0x7a, 0x56, 0x6a, 0xdd, 0xb2, 0x70, 0x2e, 0xea, 0x72,
-    0x66, 0x39, 0x54, 0xaa, 0x36, 0xfa, 0x68, 0xaa, 0x06, 0x5d, 0x48, 0xca,
-    0xad, 0x4e, 0xfe, 0x4b, 0x40, 0xdf, 0x43, 0x46, 0xd6, 0xdf, 0x3f, 0xa1,
-    0x9e, 0x4c, 0xdc, 0xfe, 0x4c, 0x01, 0x09, 0x7f, 0xd8, 0x00, 0x84, 0x94,
-    0x29, 0x17, 0x67, 0x00, 0xd3, 0x46, 0xd2, 0xba, 0xb9, 0x62, 0x66, 0x50,
-    0xcd, 0x7c, 0x7a, 0x70, 0x46, 0x4a, 0x32, 0x62, 0xc2, 0x6e, 0xe7, 0x5e,
-    0x04, 0x24, 0xc5, 0xfd, 0x9d, 0xf4, 0x9b, 0xc8, 0xe9, 0xeb, 0x73, 0xf9,
-    0xaa, 0xa4, 0xcc, 0x63, 0xa3, 0xdc, 0x63, 0xe0, 0x30, 0xec, 0x70, 0x40,
-    0x9e, 0x7c, 0x63, 0x79, 0xae, 0xba, 0xfd, 0x95, 0x4c, 0x46, 0xf1, 0xc4,
-    0xae, 0xb9, 0x03, 0xe8, 0xd4, 0xe4, 0x90, 0x29, 0x3a, 0xbb, 0xdb, 0xd8,
-    0x8f, 0x40, 0xc3, 0x39, 0x9a, 0x4c, 0x70, 0x54, 0x9f, 0xc9, 0x0a, 0x04,
-    0x23, 0x98, 0x6b, 0x9c, 0xc2, 0xe0, 0xad, 0xae, 0x30, 0xef, 0xff, 0x44,
-    0x5b, 0x73, 0x2e, 0x8f, 0xd7, 0x2b, 0x12, 0xf0, 0x31, 0x08, 0xfb, 0xb9,
-    0x55, 0xf0, 0xc3, 0x62, 0xbb, 0x5f, 0x6d, 0xa7, 0x1d, 0x61, 0xc2, 0x26,
-    0xce, 0xab, 0xb6, 0x88, 0x25, 0xce, 0x8b, 0x02, 0xb6, 0xc5, 0xa2, 0xcc,
-    0xd4, 0xa3, 0x74, 0x5b, 0x76, 0xf7, 0xb4, 0xd9, 0x9c, 0x93, 0x86, 0x7e,
-    0xac, 0x82, 0xe0, 0x0d, 0x83, 0xe1, 0xc9, 0x7f, 0x2a, 0x86, 0xbb, 0xaa,
-    0xfe, 0xdc, 0x17, 0x9c, 0x28, 0x77, 0xe1, 0x58, 0x18, 0x15, 0x09, 0xe3,
-    0xda, 0xdb, 0x8d, 0xee, 0x55, 0xf6, 0xda, 0xad, 0xe5, 0x52, 0x84, 0xb4,
-    0xf0, 0x24, 0xce, 0xa1, 0x54, 0x4b, 0x9f, 0xea, 0x5d, 0x4d, 0x7f, 0x53,
-    0x0b, 0x79, 0x1d, 0x87, 0xcb, 0x0b, 0xa8, 0xef, 0x03, 0xfa, 0x58, 0x57,
-    0xf6, 0x02, 0x70, 0xdb, 0x7a, 0x64, 0x89, 0x1f, 0xc7, 0xca, 0x87, 0x02,
-    0x27, 0x33, 0xc5, 0x5b, 0x2a, 0x50, 0xc5, 0xb5, 0x7b, 0x2d, 0x3d, 0xa9,
-    0xbc, 0x21, 0x7b, 0xf2, 0xbe, 0x9c, 0x56, 0x35, 0x83, 0xba, 0xce, 0x34,
-    0x8d, 0xec, 0x7b, 0xaa, 0xe4, 0xcb, 0xd1, 0x4f, 0x4a, 0x31, 0x00, 0xd1,
-    0xb8, 0x30, 0x38, 0xaf, 0xe8, 0xe3, 0xd7, 0xc2, 0x8c, 0xe3, 0xb4, 0x23,
-    0xb3, 0x27, 0x07, 0xc6, 0x88, 0xec, 0x58, 0xe9, 0x59, 0xfb, 0xa9, 0x11,
-    0xa2, 0xc8, 0x77, 0x22, 0x6a, 0x5b, 0x86, 0xde, 0xdc, 0xed, 0x76, 0x6e,
-    0x73, 0x79, 0x5c, 0xb4, 0xcf, 0x19, 0x76, 0x5c, 0x6b, 0x1c, 0x4b, 0x03,
-    0xcb, 0x35, 0x08, 0x94, 0x37, 0x01, 0x98, 0x52, 0xd8, 0x31, 0x42, 0x3d,
-    0x7f, 0xa1, 0x11, 0x06, 0x07, 0x88, 0xb8, 0x31, 0x35, 0xb2, 0x49, 0x28,
-    0xc6, 0x2c, 0x44, 0x43, 0xb6, 0xbc, 0x58, 0x76, 0x6c, 0x4f, 0xc8, 0xb6,
-    0x30, 0x82, 0x05, 0x84, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x07, 0x01, 0xa0, 0x82, 0x05, 0x75, 0x04, 0x82, 0x05, 0x71, 0x30,
-    0x82, 0x05, 0x6d, 0x30, 0x82, 0x05, 0x69, 0x06, 0x0b, 0x2a, 0x86, 0x48,
-    0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x02, 0xa0, 0x82, 0x05, 0x31,
-    0x30, 0x82, 0x05, 0x2d, 0x30, 0x57, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
-    0xf7, 0x0d, 0x01, 0x05, 0x0d, 0x30, 0x4a, 0x30, 0x29, 0x06, 0x09, 0x2a,
-    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x05, 0x0c, 0x30, 0x1c, 0x04, 0x08,
-    0x79, 0x31, 0xf9, 0xe2, 0x42, 0x33, 0xf1, 0xaa, 0x02, 0x02, 0x08, 0x00,
-    0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x09,
-    0x05, 0x00, 0x30, 0x1d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
-    0x04, 0x01, 0x02, 0x04, 0x10, 0xc9, 0xda, 0x5f, 0x96, 0xc8, 0x2c, 0x85,
-    0x5d, 0xa0, 0x30, 0x82, 0x16, 0x6b, 0xf4, 0xfd, 0x91, 0x04, 0x82, 0x04,
-    0xd0, 0xc3, 0x89, 0x6a, 0x56, 0x6a, 0x84, 0x58, 0x76, 0xd7, 0x23, 0xd5,
-    0xa8, 0xc1, 0x2f, 0x43, 0x38, 0x99, 0xf8, 0x64, 0x97, 0xe7, 0xe8, 0xd2,
-    0xcf, 0x36, 0x9b, 0x7e, 0x04, 0xe5, 0x87, 0x80, 0xff, 0xd5, 0x58, 0x50,
-    0x5a, 0xb3, 0xc0, 0x15, 0xc9, 0xd5, 0x61, 0xd6, 0x3b, 0x7f, 0x2f, 0x3b,
-    0x98, 0x50, 0x55, 0x09, 0xcf, 0xc3, 0xdd, 0xbd, 0x8b, 0xcd, 0xdf, 0x20,
-    0x90, 0xe1, 0xd2, 0xcd, 0x22, 0x9f, 0xa7, 0x3e, 0x10, 0xd3, 0xb7, 0x26,
-    0x54, 0x65, 0xfb, 0x18, 0x12, 0x58, 0x81, 0xd8, 0xe6, 0x97, 0xdf, 0x32,
-    0xd1, 0x04, 0x4a, 0xdb, 0x05, 0xb4, 0x13, 0xa9, 0x86, 0x62, 0x20, 0x94,
-    0xdc, 0xaf, 0x98, 0x53, 0x16, 0xc7, 0xb2, 0x9c, 0x44, 0x30, 0xc5, 0xaa,
-    0x14, 0x7a, 0x2d, 0x93, 0x20, 0xff, 0x6d, 0x8d, 0x47, 0x69, 0x6f, 0x39,
-    0xd4, 0x15, 0x81, 0x6b, 0x85, 0x36, 0xf9, 0x59, 0xa5, 0x8e, 0x5c, 0x40,
-    0x62, 0xf8, 0xfe, 0xf7, 0xe6, 0x75, 0xf7, 0x37, 0xfe, 0x5d, 0x53, 0xa6,
-    0x66, 0xe5, 0x0e, 0x4a, 0x23, 0xa9, 0x80, 0x4b, 0x04, 0x11, 0x0e, 0x50,
-    0xef, 0x9e, 0x88, 0xed, 0x39, 0xd1, 0x5f, 0xfa, 0x90, 0x22, 0xa3, 0x70,
-    0x0c, 0x8b, 0x20, 0x9c, 0x80, 0x2c, 0x90, 0x2e, 0x2c, 0xe0, 0xe6, 0x26,
-    0x84, 0xd8, 0x6a, 0xe4, 0x20, 0x1e, 0xbc, 0x96, 0xba, 0x07, 0x9d, 0x1d,
-    0x3d, 0x6c, 0xd1, 0x04, 0xc8, 0xd1, 0x79, 0x2c, 0x96, 0x0f, 0xe8, 0xa5,
-    0x6b, 0x03, 0x06, 0x51, 0xfd, 0x7b, 0x44, 0xab, 0x66, 0x4a, 0x41, 0x04,
-    0x02, 0x64, 0x5a, 0x40, 0x7d, 0x6b, 0x1a, 0xbc, 0x6e, 0xee, 0x68, 0x70,
-    0x3c, 0x10, 0x32, 0x73, 0x76, 0x28, 0x48, 0xd9, 0xa4, 0xe1, 0x21, 0xf6,
-    0xe4, 0x03, 0x94, 0x10, 0xef, 0x82, 0xe0, 0x76, 0x7c, 0x99, 0x30, 0x26,
-    0x9a, 0x95, 0xa2, 0xc5, 0xb9, 0xa7, 0xae, 0x9f, 0x85, 0xcb, 0xf1, 0x82,
-    0xcd, 0x3d, 0x06, 0xec, 0xaf, 0x72, 0xc1, 0x33, 0x09, 0xf9, 0x51, 0x94,
-    0x42, 0xf0, 0x69, 0xb9, 0xc6, 0x04, 0xe6, 0x7a, 0xfb, 0x1c, 0xee, 0xac,
-    0x95, 0x9b, 0x88, 0x67, 0x19, 0xa8, 0x79, 0x67, 0xc7, 0x1b, 0xcc, 0x72,
-    0xe9, 0x18, 0xd2, 0x96, 0xcf, 0x3d, 0xf8, 0x98, 0x20, 0x53, 0xc9, 0x37,
-    0x0f, 0x92, 0xb1, 0xbc, 0xaf, 0xc6, 0xec, 0x4f, 0x25, 0xda, 0x95, 0x14,
-    0xed, 0xb8, 0x3e, 0xaf, 0xd1, 0x52, 0x4c, 0x28, 0x3b, 0x84, 0x8c, 0x49,
-    0x34, 0x63, 0x2b, 0xd4, 0xf4, 0x78, 0xb1, 0x8f, 0xb0, 0x35, 0x7b, 0xd5,
-    0x44, 0xc3, 0x98, 0x9e, 0x85, 0x86, 0xae, 0xee, 0x05, 0xdd, 0xa1, 0x6f,
-    0x53, 0xe4, 0xdc, 0x6f, 0xf5, 0x7c, 0x7e, 0xd8, 0x7a, 0x9b, 0x18, 0x43,
-    0x3f, 0x7b, 0x2a, 0xf3, 0xb5, 0x39, 0x5a, 0x1c, 0x72, 0x3b, 0xdd, 0x01,
-    0x79, 0x97, 0xff, 0xdb, 0x58, 0xe5, 0x4d, 0x61, 0xde, 0xcf, 0x2f, 0x13,
-    0x7b, 0xaf, 0x6b, 0xa4, 0xf2, 0x59, 0x0a, 0x13, 0x56, 0x1c, 0x05, 0x00,
-    0x0f, 0x18, 0x66, 0x33, 0x72, 0xbd, 0x62, 0x8d, 0x11, 0xf7, 0x20, 0x52,
-    0x29, 0x42, 0x83, 0x33, 0xc1, 0x0f, 0x07, 0x80, 0xd4, 0x58, 0xe2, 0x22,
-    0x94, 0xad, 0xec, 0xbf, 0x01, 0xb6, 0x71, 0x7d, 0x92, 0xb1, 0x75, 0x14,
-    0xf2, 0xfb, 0x77, 0x39, 0x0d, 0x82, 0xb5, 0x51, 0xba, 0x1f, 0x65, 0x57,
-    0xaa, 0x68, 0x6a, 0x17, 0x41, 0x13, 0x38, 0xc0, 0xe5, 0xeb, 0xcc, 0x8c,
-    0xdd, 0xb7, 0x00, 0x4e, 0x01, 0x06, 0x25, 0xab, 0x87, 0x1c, 0x30, 0x69,
-    0xc4, 0x15, 0x0e, 0xf8, 0xf0, 0x72, 0xb6, 0x1d, 0x92, 0x7e, 0xe2, 0xe6,
-    0x77, 0xed, 0xb8, 0x3f, 0xcf, 0x57, 0x8d, 0x90, 0xe4, 0xa3, 0x79, 0x49,
-    0x9a, 0xe0, 0x1f, 0x4a, 0xde, 0xe9, 0x44, 0x8d, 0xd5, 0x23, 0x3b, 0x07,
-    0x63, 0x92, 0x9f, 0xde, 0xba, 0x7e, 0x67, 0xb0, 0x82, 0x41, 0x2a, 0xcd,
-    0xe1, 0xbb, 0x40, 0xf1, 0x8a, 0x66, 0x70, 0x74, 0xf1, 0x99, 0x7d, 0xb0,
-    0x0b, 0x6a, 0xa2, 0x5e, 0x7e, 0xc0, 0x8c, 0xb2, 0x71, 0xda, 0xcf, 0xbc,
-    0xfb, 0x9c, 0x03, 0x0e, 0x33, 0x5e, 0x13, 0xb2, 0x34, 0x38, 0xc1, 0x83,
-    0x95, 0xdf, 0x46, 0xfc, 0xe0, 0xe0, 0xaf, 0x93, 0xe0, 0x70, 0xd5, 0x15,
-    0x8c, 0x2f, 0xae, 0x4b, 0xa6, 0xeb, 0x13, 0x8f, 0xaf, 0x1b, 0xf5, 0x71,
-    0xc4, 0x62, 0x71, 0x08, 0x97, 0x10, 0x52, 0xfe, 0xbd, 0x60, 0xd7, 0x9f,
-    0xdf, 0x3d, 0xc5, 0xdd, 0xcd, 0xe7, 0x8e, 0x85, 0x60, 0xdf, 0x61, 0x79,
-    0x5b, 0x90, 0xd9, 0xaa, 0x56, 0x30, 0x6d, 0x0f, 0xfb, 0x27, 0x84, 0xdd,
-    0x3d, 0x04, 0x6a, 0xe0, 0x70, 0x7e, 0xbb, 0x59, 0xf4, 0xeb, 0xe8, 0xc0,
-    0x62, 0xaa, 0xf6, 0xed, 0xca, 0xae, 0xb2, 0x2b, 0x0f, 0xc1, 0x56, 0x45,
-    0xe7, 0x24, 0x6b, 0xaf, 0xeb, 0x15, 0x26, 0xb2, 0xcd, 0xae, 0x1f, 0xe7,
-    0x11, 0xc0, 0x1c, 0x19, 0x4a, 0xc7, 0x51, 0x2a, 0x29, 0xdf, 0x14, 0x82,
-    0x43, 0xfe, 0x52, 0x39, 0xba, 0xe6, 0x6c, 0xa5, 0x76, 0x8b, 0xb1, 0x21,
-    0x9c, 0x20, 0xb0, 0x10, 0x0c, 0x44, 0xf2, 0xd4, 0x6e, 0x41, 0x1b, 0x8f,
-    0x90, 0x23, 0xe3, 0x87, 0xfc, 0xf1, 0x46, 0xc6, 0x5b, 0xae, 0xd0, 0x2a,
-    0x2b, 0x78, 0xf5, 0x2b, 0xb9, 0x9f, 0x46, 0x4b, 0x30, 0xf8, 0x49, 0x57,
-    0x7e, 0xb4, 0xff, 0xca, 0xad, 0x4d, 0xf3, 0xc1, 0x7b, 0x42, 0xe0, 0xa4,
-    0x37, 0x2f, 0xe2, 0xb2, 0x60, 0xe8, 0xaf, 0xd7, 0x39, 0x23, 0x4c, 0x67,
-    0x44, 0xe5, 0x6d, 0xb3, 0x25, 0x11, 0x9f, 0x2b, 0xea, 0x23, 0xfb, 0x1e,
-    0xce, 0xbf, 0xa4, 0x2f, 0x88, 0xec, 0x18, 0x40, 0x16, 0x43, 0x9f, 0x71,
-    0x9c, 0x8d, 0xbd, 0x5d, 0x55, 0x3b, 0x92, 0x4e, 0x23, 0x3c, 0x87, 0xed,
-    0x5f, 0x2e, 0x8f, 0xde, 0x83, 0xad, 0x30, 0x42, 0x7e, 0x1a, 0x5e, 0xf5,
-    0xc5, 0x75, 0xbb, 0x99, 0x6e, 0xf1, 0x87, 0xe0, 0xf3, 0x51, 0x1e, 0x7d,
-    0xe8, 0xfc, 0xc6, 0x88, 0xf2, 0x39, 0x6d, 0xae, 0x73, 0x9f, 0xad, 0x9b,
-    0x7b, 0x67, 0x99, 0xdb, 0x90, 0x0e, 0xa0, 0xfc, 0xaf, 0xcc, 0xdb, 0x8b,
-    0xaa, 0xc2, 0x54, 0xd5, 0x2d, 0xb3, 0x5f, 0xa3, 0x0a, 0x3e, 0xd6, 0x8d,
-    0x40, 0x4d, 0x3b, 0xe5, 0x2d, 0x31, 0xd8, 0xb2, 0x12, 0x07, 0xca, 0x36,
-    0x56, 0xd9, 0x2f, 0x55, 0x82, 0xdc, 0x8e, 0x92, 0xa9, 0x6c, 0x91, 0x9e,
-    0x22, 0xe4, 0xc6, 0x27, 0x8b, 0x1a, 0xa2, 0x78, 0x56, 0x2c, 0x5a, 0x19,
-    0xdf, 0x40, 0xf9, 0xfb, 0x44, 0x21, 0x5b, 0xdf, 0x2f, 0x99, 0x84, 0x49,
-    0xcf, 0x1a, 0x15, 0xa5, 0x59, 0x3a, 0x66, 0x09, 0x4d, 0xc1, 0xf2, 0xb1,
-    0x24, 0x33, 0xbd, 0x86, 0x41, 0xdc, 0x33, 0x9b, 0x03, 0xc0, 0xa8, 0xf8,
-    0x94, 0x78, 0x2e, 0x16, 0x97, 0xef, 0x23, 0xee, 0xa4, 0xac, 0x3a, 0x90,
-    0xb6, 0xd9, 0xc0, 0xda, 0x5e, 0x26, 0x34, 0x26, 0xce, 0xc9, 0xf8, 0x45,
-    0x37, 0x83, 0x7c, 0xbd, 0x9c, 0x60, 0x40, 0x61, 0x28, 0xcd, 0x9c, 0xb4,
-    0xe4, 0xe6, 0x5c, 0x4f, 0xd1, 0x79, 0x42, 0x13, 0xa9, 0x6f, 0x26, 0x23,
-    0xc2, 0x6c, 0x8e, 0x8d, 0x7e, 0x3f, 0xee, 0x2b, 0x4d, 0xd2, 0x5b, 0x80,
-    0xdc, 0x74, 0xda, 0x1f, 0xbc, 0x26, 0x54, 0xc5, 0xfe, 0xee, 0xa9, 0x4f,
-    0xce, 0x46, 0xaf, 0x90, 0xb0, 0x12, 0x9a, 0x18, 0x0e, 0x06, 0x05, 0xc7,
-    0x98, 0xef, 0xcc, 0x6d, 0xa3, 0x46, 0x91, 0xa5, 0x0e, 0xe7, 0x35, 0x1a,
-    0x7f, 0x9d, 0xae, 0xa0, 0xb4, 0x0a, 0x32, 0x3b, 0xe4, 0xcd, 0x4b, 0x3e,
-    0x89, 0x73, 0xc9, 0x97, 0x38, 0xe5, 0x86, 0x4f, 0x24, 0xed, 0x4a, 0x43,
-    0x04, 0x02, 0xc1, 0x29, 0x8d, 0x85, 0xa2, 0xdd, 0xb2, 0x61, 0x3c, 0xce,
-    0x8b, 0x47, 0x2e, 0xed, 0x4b, 0x24, 0x94, 0xb7, 0xbf, 0x9d, 0x55, 0x42,
-    0x95, 0xc2, 0x27, 0xe5, 0x09, 0xd4, 0x20, 0x03, 0x20, 0x21, 0x3a, 0xd8,
-    0xd2, 0xa2, 0xb3, 0x47, 0x93, 0x4f, 0x5a, 0x39, 0xca, 0xd8, 0x74, 0xa9,
-    0x19, 0xa6, 0x9a, 0x23, 0xb1, 0x21, 0xa3, 0xb3, 0x14, 0xcc, 0xe2, 0x12,
-    0x91, 0x30, 0xdb, 0x50, 0xf8, 0x44, 0x74, 0xd6, 0x70, 0xdd, 0x7d, 0x26,
-    0x7f, 0xbf, 0x32, 0x93, 0x1f, 0x3d, 0x40, 0xbf, 0x2e, 0xec, 0x28, 0xf5,
-    0xb1, 0xaf, 0x11, 0xc7, 0x4e, 0x64, 0x13, 0x3c, 0xbf, 0x2e, 0x19, 0x81,
-    0xfe, 0x35, 0xba, 0xec, 0x6e, 0xb6, 0xa9, 0xfe, 0xc6, 0x85, 0x33, 0x41,
-    0x58, 0xab, 0x06, 0xae, 0x2b, 0x96, 0x62, 0x1f, 0x2c, 0x6c, 0xad, 0xec,
-    0x1a, 0x59, 0x55, 0x5a, 0x6f, 0xe0, 0xeb, 0x71, 0x8d, 0xb5, 0x0c, 0x81,
-    0x2a, 0x39, 0xbd, 0x67, 0x39, 0x48, 0xfb, 0x91, 0x64, 0xad, 0x01, 0x4c,
-    0x4a, 0x0f, 0x30, 0x29, 0xa0, 0xcf, 0x30, 0x96, 0x43, 0xe9, 0xfc, 0x22,
-    0x4b, 0xf3, 0x4f, 0xab, 0xec, 0xbc, 0x5a, 0xfb, 0x7f, 0x20, 0xd9, 0xd5,
-    0xc7, 0xce, 0x93, 0xa3, 0x2e, 0x82, 0xd1, 0xa0, 0xc6, 0x16, 0xd5, 0x64,
-    0x2d, 0x3f, 0x69, 0x15, 0xfd, 0xf3, 0x28, 0x3d, 0x4e, 0x61, 0x01, 0x2c,
-    0xd4, 0x2b, 0x40, 0x51, 0x6e, 0x95, 0x00, 0xa4, 0x34, 0x31, 0x25, 0x30,
-    0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15,
-    0x31, 0x16, 0x04, 0x14, 0x47, 0xf4, 0x18, 0xa5, 0x4b, 0x85, 0xb7, 0x02,
-    0xc1, 0x97, 0xff, 0x57, 0xb6, 0x6f, 0x21, 0x45, 0x34, 0x3d, 0x92, 0x22,
-    0x30, 0x31, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02,
-    0x1a, 0x05, 0x00, 0x04, 0x14, 0x17, 0x45, 0x0c, 0xdf, 0x53, 0x76, 0x9b,
-    0xce, 0x3b, 0x12, 0xdd, 0x47, 0x05, 0x6d, 0x16, 0x90, 0x9d, 0x29, 0x9b,
-    0xe1, 0x04, 0x08, 0xa1, 0xf2, 0x82, 0x1c, 0xd1, 0xd1, 0x7b, 0x5c, 0x02,
-    0x02, 0x08, 0x00,
-};
-
-// kNoEncryption is a PKCS#12 file with neither the key or certificate is
-// encrypted. It was generated with:
-//
-//   openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -keypbe NONE -certpbe NONE -password pass:foo
-static const uint8_t kNoEncryption[] = {
-    0x30, 0x82, 0x03, 0x6e, 0x02, 0x01, 0x03, 0x30, 0x82, 0x03, 0x34, 0x06,
-    0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
-    0x03, 0x25, 0x04, 0x82, 0x03, 0x21, 0x30, 0x82, 0x03, 0x1d, 0x30, 0x82,
-    0x02, 0x3e, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-    0x01, 0xa0, 0x82, 0x02, 0x2f, 0x04, 0x82, 0x02, 0x2b, 0x30, 0x82, 0x02,
-    0x27, 0x30, 0x82, 0x02, 0x23, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x0c, 0x0a, 0x01, 0x03, 0xa0, 0x82, 0x01, 0xeb, 0x30, 0x82,
-    0x01, 0xe7, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09,
-    0x16, 0x01, 0xa0, 0x82, 0x01, 0xd7, 0x04, 0x82, 0x01, 0xd3, 0x30, 0x82,
-    0x01, 0xcf, 0x30, 0x82, 0x01, 0x76, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02,
-    0x09, 0x00, 0xd9, 0x4c, 0x04, 0xda, 0x49, 0x7d, 0xbf, 0xeb, 0x30, 0x09,
-    0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x30, 0x45, 0x31,
-    0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55,
-    0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53,
-    0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30,
-    0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65,
-    0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73,
-    0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e, 0x17, 0x0d,
-    0x31, 0x34, 0x30, 0x34, 0x32, 0x33, 0x32, 0x33, 0x32, 0x31, 0x35, 0x37,
-    0x5a, 0x17, 0x0d, 0x31, 0x34, 0x30, 0x35, 0x32, 0x33, 0x32, 0x33, 0x32,
-    0x31, 0x35, 0x37, 0x5a, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
-    0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06,
-    0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53,
-    0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04,
-    0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20,
-    0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20,
-    0x4c, 0x74, 0x64, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48,
-    0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03,
-    0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xe6, 0x2b, 0x69, 0xe2, 0xbf, 0x65,
-    0x9f, 0x97, 0xbe, 0x2f, 0x1e, 0x0d, 0x94, 0x8a, 0x4c, 0xd5, 0x97, 0x6b,
-    0xb7, 0xa9, 0x1e, 0x0d, 0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e, 0x9d, 0xdc,
-    0xba, 0x5a, 0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9, 0xc3, 0xc4,
-    0xa3, 0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a, 0x1c, 0xf5,
-    0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1, 0xa3, 0x50,
-    0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04,
-    0x14, 0xab, 0x84, 0xd2, 0xac, 0xab, 0x95, 0xf0, 0x82, 0x4e, 0x16, 0x78,
-    0x07, 0x55, 0x57, 0x5f, 0xe4, 0x26, 0x8d, 0x82, 0xd1, 0x30, 0x1f, 0x06,
-    0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xab, 0x84,
-    0xd2, 0xac, 0xab, 0x95, 0xf0, 0x82, 0x4e, 0x16, 0x78, 0x07, 0x55, 0x57,
-    0x5f, 0xe4, 0x26, 0x8d, 0x82, 0xd1, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d,
-    0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x09, 0x06, 0x07,
-    0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01, 0x03, 0x48, 0x00, 0x30, 0x45,
-    0x02, 0x21, 0x00, 0xf2, 0xa0, 0x35, 0x5e, 0x51, 0x3a, 0x36, 0xc3, 0x82,
-    0x79, 0x9b, 0xee, 0x27, 0x50, 0x85, 0x8e, 0x70, 0x06, 0x74, 0x95, 0x57,
-    0xd2, 0x29, 0x74, 0x00, 0xf4, 0xbe, 0x15, 0x87, 0x5d, 0xc4, 0x07, 0x02,
-    0x20, 0x7c, 0x1e, 0x79, 0x14, 0x6a, 0x21, 0x83, 0xf0, 0x7a, 0x74, 0x68,
-    0x79, 0x5f, 0x14, 0x99, 0x9a, 0x68, 0xb4, 0xf1, 0xcb, 0x9e, 0x15, 0x5e,
-    0xe6, 0x1f, 0x32, 0x52, 0x61, 0x5e, 0x75, 0xc9, 0x14, 0x31, 0x25, 0x30,
-    0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15,
-    0x31, 0x16, 0x04, 0x14, 0x3f, 0x31, 0x38, 0xec, 0xb9, 0xf1, 0x45, 0xe1,
-    0x3e, 0x90, 0x71, 0x0d, 0xc1, 0x28, 0xba, 0x4e, 0x6f, 0xa0, 0x9c, 0xed,
-    0x30, 0x81, 0xd8, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
-    0x07, 0x01, 0xa0, 0x81, 0xca, 0x04, 0x81, 0xc7, 0x30, 0x81, 0xc4, 0x30,
-    0x81, 0xc1, 0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c,
-    0x0a, 0x01, 0x01, 0xa0, 0x81, 0x8a, 0x30, 0x81, 0x87, 0x02, 0x01, 0x00,
-    0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06,
-    0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x04, 0x6d, 0x30,
-    0x6b, 0x02, 0x01, 0x01, 0x04, 0x20, 0x07, 0x0f, 0x08, 0x72, 0x7a, 0xd4,
-    0xa0, 0x4a, 0x9c, 0xdd, 0x59, 0xc9, 0x4d, 0x89, 0x68, 0x77, 0x08, 0xb5,
-    0x6f, 0xc9, 0x5d, 0x30, 0x77, 0x0e, 0xe8, 0xd1, 0xc9, 0xce, 0x0a, 0x8b,
-    0xb4, 0x6a, 0xa1, 0x44, 0x03, 0x42, 0x00, 0x04, 0xe6, 0x2b, 0x69, 0xe2,
-    0xbf, 0x65, 0x9f, 0x97, 0xbe, 0x2f, 0x1e, 0x0d, 0x94, 0x8a, 0x4c, 0xd5,
-    0x97, 0x6b, 0xb7, 0xa9, 0x1e, 0x0d, 0x46, 0xfb, 0xdd, 0xa9, 0xa9, 0x1e,
-    0x9d, 0xdc, 0xba, 0x5a, 0x01, 0xe7, 0xd6, 0x97, 0xa8, 0x0a, 0x18, 0xf9,
-    0xc3, 0xc4, 0xa3, 0x1e, 0x56, 0xe2, 0x7c, 0x83, 0x48, 0xdb, 0x16, 0x1a,
-    0x1c, 0xf5, 0x1d, 0x7e, 0xf1, 0x94, 0x2d, 0x4b, 0xcf, 0x72, 0x22, 0xc1,
-    0x31, 0x25, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14, 0x3f, 0x31, 0x38, 0xec, 0xb9,
-    0xf1, 0x45, 0xe1, 0x3e, 0x90, 0x71, 0x0d, 0xc1, 0x28, 0xba, 0x4e, 0x6f,
-    0xa0, 0x9c, 0xed, 0x30, 0x31, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
-    0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0xd0, 0xb4, 0x17, 0x1a,
-    0xdb, 0xa3, 0x27, 0xd8, 0x9e, 0xd3, 0xf2, 0xb3, 0x3e, 0x96, 0x07, 0x3a,
-    0xf2, 0x6a, 0xc2, 0x1c, 0x04, 0x08, 0xb5, 0xa8, 0xb9, 0xdb, 0x2f, 0xf1,
-    0xa4, 0xcd, 0x02, 0x02, 0x08, 0x00,
-};
-
+// kPassword is the password shared by most of the sample PKCS#12 files.
 static const char kPassword[] = "foo";
 
-// Generated with
-//   openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -password pass:
-static const uint8_t kEmptyPassword[] = {
-    0x30, 0x82, 0x03, 0xd2, 0x02, 0x01, 0x03, 0x30, 0x82, 0x03, 0x98, 0x06,
-    0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
-    0x03, 0x89, 0x04, 0x82, 0x03, 0x85, 0x30, 0x82, 0x03, 0x81, 0x30, 0x82,
-    0x02, 0x77, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-    0x06, 0xa0, 0x82, 0x02, 0x68, 0x30, 0x82, 0x02, 0x64, 0x02, 0x01, 0x00,
-    0x30, 0x82, 0x02, 0x5d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x07, 0x01, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x0c, 0x01, 0x06, 0x30, 0x0e, 0x04, 0x08, 0xe2, 0xb3, 0x39,
-    0xd2, 0xbd, 0x5a, 0x8c, 0x0e, 0x02, 0x02, 0x08, 0x00, 0x80, 0x82, 0x02,
-    0x30, 0x78, 0x22, 0x01, 0x8c, 0x16, 0xcd, 0x11, 0x86, 0xa1, 0xc7, 0x6e,
-    0xc4, 0x77, 0xa1, 0x8d, 0xb4, 0x85, 0xc3, 0xb2, 0x02, 0x63, 0x70, 0x8b,
-    0xfb, 0xb0, 0x5c, 0x8f, 0x1c, 0xec, 0x0f, 0xc7, 0x7d, 0xb6, 0x0a, 0x03,
-    0x5f, 0x20, 0x00, 0x32, 0x2d, 0x2e, 0x12, 0x4e, 0x5a, 0x60, 0x48, 0x6f,
-    0xd0, 0xe9, 0x8c, 0x15, 0x59, 0x5c, 0x62, 0xe6, 0x24, 0x4c, 0xfd, 0x1f,
-    0x30, 0xa1, 0x22, 0x8b, 0x0f, 0xe5, 0x37, 0x82, 0x6b, 0x19, 0x0d, 0xcc,
-    0x85, 0x4d, 0xce, 0x64, 0x9b, 0x82, 0x29, 0xfe, 0x4a, 0xe4, 0x11, 0xd2,
-    0xe6, 0x01, 0xce, 0xdb, 0x54, 0x64, 0x6b, 0x07, 0x69, 0xb5, 0x19, 0xfb,
-    0xf1, 0x72, 0x84, 0x3c, 0x4e, 0x8d, 0x64, 0xfd, 0xa9, 0x33, 0xaa, 0x1d,
-    0x59, 0x1a, 0x3c, 0xcd, 0x1e, 0xee, 0x3a, 0x3e, 0x8a, 0x9c, 0xf1, 0x21,
-    0x24, 0xeb, 0x52, 0xd1, 0x7f, 0x32, 0x57, 0x68, 0xa0, 0xac, 0x2d, 0x94,
-    0xe8, 0x4c, 0x59, 0xa3, 0x43, 0xfb, 0x18, 0x79, 0x4c, 0xbe, 0xc2, 0x84,
-    0x3d, 0x6e, 0xb3, 0x2f, 0xc8, 0x72, 0xbc, 0x29, 0xec, 0x06, 0x87, 0xc3,
-    0x9a, 0x48, 0x40, 0x0e, 0xe6, 0x34, 0xc1, 0x4a, 0xf7, 0x2a, 0x6e, 0xe0,
-    0x0c, 0x9c, 0xa2, 0x32, 0x55, 0xd6, 0x43, 0x2c, 0x9d, 0x74, 0x4b, 0xf0,
-    0x5c, 0xaa, 0x2f, 0x6b, 0xb4, 0xa3, 0xb6, 0x10, 0xe1, 0x20, 0xad, 0xa2,
-    0xb7, 0x31, 0x54, 0x1c, 0x92, 0x55, 0xb1, 0x47, 0x9b, 0x56, 0xe7, 0x89,
-    0x90, 0x40, 0xa4, 0x87, 0x71, 0x38, 0x95, 0xec, 0x43, 0x26, 0x4b, 0x59,
-    0xad, 0x6d, 0xf0, 0xc2, 0xf7, 0x6f, 0xa0, 0x9a, 0xbb, 0x23, 0x50, 0x44,
-    0xbf, 0x8f, 0x49, 0x37, 0xc9, 0x4f, 0xd5, 0x23, 0x7e, 0xf6, 0x5d, 0xfb,
-    0xd8, 0x07, 0x64, 0xe0, 0xa8, 0xa3, 0x3a, 0x3e, 0xc7, 0x8f, 0x57, 0x8a,
-    0xb2, 0x5b, 0xc9, 0xfc, 0x27, 0x25, 0x2d, 0xcd, 0xcc, 0x9b, 0x5c, 0x44,
-    0x07, 0x7d, 0xf4, 0xad, 0x42, 0x12, 0x25, 0x48, 0x14, 0x56, 0x22, 0x66,
-    0xe5, 0xec, 0xe8, 0x76, 0x32, 0xe3, 0x18, 0xb1, 0xac, 0x2b, 0x0f, 0xd2,
-    0x92, 0x82, 0xe2, 0xd4, 0x42, 0x0d, 0x0d, 0x31, 0xb3, 0x8e, 0x53, 0x17,
-    0xc4, 0x8a, 0x0a, 0xf9, 0x6f, 0x39, 0xd1, 0x09, 0x55, 0x04, 0xe5, 0x09,
-    0x15, 0xe7, 0x3f, 0x2a, 0xf0, 0x89, 0xff, 0xb1, 0xa8, 0xe3, 0x8a, 0xf8,
-    0x9b, 0xa4, 0x34, 0x93, 0xea, 0x46, 0x26, 0xcf, 0x23, 0x73, 0x82, 0x87,
-    0x7c, 0xe3, 0xd2, 0x9b, 0x49, 0x53, 0x5b, 0x99, 0xa9, 0xd4, 0x87, 0xa4,
-    0xf0, 0xd0, 0x82, 0x40, 0xb0, 0x0b, 0x8c, 0xb2, 0x72, 0xca, 0x2c, 0xb1,
-    0x57, 0x54, 0x65, 0xf6, 0x88, 0xbb, 0x0d, 0x93, 0xac, 0xcb, 0x73, 0x90,
-    0xa8, 0x7b, 0x16, 0x55, 0x73, 0x7e, 0x7e, 0xe3, 0xe1, 0xc5, 0xc4, 0x0c,
-    0x36, 0x5e, 0x33, 0x91, 0x49, 0x9c, 0x71, 0x11, 0xf5, 0xd3, 0x5b, 0x38,
-    0xbd, 0xe6, 0xb5, 0x0f, 0x72, 0x8c, 0x34, 0xc6, 0x18, 0x6c, 0xc9, 0xe5,
-    0x40, 0x9c, 0xbe, 0xd8, 0x3e, 0x4d, 0x42, 0xd3, 0x96, 0x98, 0x14, 0x51,
-    0x29, 0xba, 0xed, 0x4c, 0x4f, 0x09, 0x50, 0x47, 0xf1, 0x84, 0x14, 0x65,
-    0x07, 0x85, 0x82, 0xad, 0x72, 0x34, 0x54, 0x5b, 0x0e, 0x44, 0x5d, 0xb8,
-    0x2c, 0x71, 0x67, 0x55, 0x20, 0x73, 0x20, 0xb9, 0x56, 0x7a, 0x69, 0x46,
-    0xca, 0x24, 0x47, 0x43, 0xd9, 0x47, 0xe7, 0x78, 0x7e, 0xc6, 0xfc, 0x59,
-    0xe5, 0xd9, 0x75, 0xe7, 0x65, 0x2e, 0xd8, 0xa3, 0x6e, 0x58, 0xdd, 0x96,
-    0x6b, 0xf4, 0x30, 0xd2, 0x3c, 0x42, 0xce, 0x2a, 0x9c, 0x7f, 0xa7, 0x69,
-    0xdf, 0xf5, 0x78, 0xc1, 0x83, 0x1e, 0x21, 0x5a, 0xad, 0x2a, 0x6a, 0x0a,
-    0x8b, 0x93, 0xa0, 0x0b, 0x96, 0x6d, 0xd9, 0xaa, 0x57, 0xa9, 0xd2, 0x31,
-    0x21, 0x52, 0x11, 0x51, 0x4b, 0x85, 0xe5, 0x07, 0x2f, 0x9b, 0x50, 0xff,
-    0xa7, 0x93, 0x93, 0xa7, 0xf6, 0x9e, 0x39, 0xc8, 0xfd, 0x53, 0x4d, 0x6d,
-    0x64, 0x2b, 0xdd, 0xc6, 0x7e, 0xe8, 0xfe, 0x04, 0xa8, 0x20, 0xf8, 0xb3,
-    0xa2, 0x5b, 0xa0, 0x0b, 0x27, 0x8a, 0x25, 0x70, 0x95, 0x30, 0x82, 0x01,
-    0x02, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01,
-    0xa0, 0x81, 0xf4, 0x04, 0x81, 0xf1, 0x30, 0x81, 0xee, 0x30, 0x81, 0xeb,
-    0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01,
-    0x02, 0xa0, 0x81, 0xb4, 0x30, 0x81, 0xb1, 0x30, 0x1c, 0x06, 0x0a, 0x2a,
-    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0e, 0x04,
-    0x08, 0x3b, 0xa5, 0xb3, 0x6e, 0x74, 0x4b, 0xef, 0x0c, 0x02, 0x02, 0x08,
-    0x00, 0x04, 0x81, 0x90, 0x36, 0x54, 0x47, 0x07, 0xf6, 0xa4, 0x5f, 0xb1,
-    0x28, 0xd7, 0xcb, 0x92, 0xe1, 0xbd, 0x43, 0xa9, 0xf0, 0xa2, 0x07, 0x97,
-    0x29, 0x0d, 0xec, 0x4a, 0xea, 0xd1, 0x14, 0xe9, 0xa5, 0x10, 0xcb, 0x52,
-    0x26, 0x2e, 0x18, 0xf8, 0xc8, 0x73, 0xf4, 0xee, 0x91, 0x8e, 0x78, 0xc1,
-    0x38, 0xd7, 0x7c, 0x9b, 0x5d, 0xb9, 0x64, 0x49, 0x5f, 0x23, 0x6b, 0xe4,
-    0xf1, 0xe1, 0x72, 0x71, 0xb9, 0xad, 0xdf, 0x31, 0x9a, 0xab, 0xd0, 0xad,
-    0x17, 0xb9, 0x2d, 0x4b, 0x8e, 0xbe, 0xe0, 0xb1, 0x94, 0xd6, 0xc0, 0x37,
-    0xee, 0x38, 0x65, 0xc0, 0xc5, 0x4e, 0x09, 0x7a, 0x02, 0x17, 0xd8, 0x4f,
-    0x83, 0x4f, 0xa2, 0x2e, 0x62, 0xb6, 0x97, 0x3e, 0x36, 0xbd, 0x15, 0x08,
-    0x40, 0x50, 0xc1, 0x8b, 0x7e, 0x9b, 0xc6, 0x79, 0xe1, 0x1e, 0xaf, 0xd9,
-    0x53, 0x82, 0x61, 0xb2, 0x52, 0x8a, 0xf2, 0x56, 0x70, 0xc3, 0x72, 0xcd,
-    0xa9, 0xb5, 0xf0, 0x6a, 0xc0, 0x4b, 0x89, 0xe5, 0x7c, 0x93, 0xb9, 0x1e,
-    0x68, 0xb4, 0x3a, 0xc3, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86,
-    0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14, 0x3f,
-    0x31, 0x38, 0xec, 0xb9, 0xf1, 0x45, 0xe1, 0x3e, 0x90, 0x71, 0x0d, 0xc1,
-    0x28, 0xba, 0x4e, 0x6f, 0xa0, 0x9c, 0xed, 0x30, 0x31, 0x30, 0x21, 0x30,
-    0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
-    0x7f, 0xd4, 0x5b, 0x84, 0x34, 0xb7, 0xf9, 0x87, 0x88, 0x7c, 0x52, 0x7a,
-    0x79, 0x02, 0x96, 0x58, 0xcc, 0xdb, 0x9d, 0xf2, 0x04, 0x08, 0x62, 0xf5,
-    0x7d, 0x8f, 0x84, 0xe5, 0x64, 0x25, 0x02, 0x02, 0x08, 0x00};
-
-// Generated with
-//   openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -password pass:
-// But with OpenSSL patched to pass NULL into PKCS12_create and PKCS12_set_mac.
-static const uint8_t kNullPassword[] = {
-    0x30, 0x82, 0x03, 0xd2, 0x02, 0x01, 0x03, 0x30, 0x82, 0x03, 0x98, 0x06,
-    0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
-    0x03, 0x89, 0x04, 0x82, 0x03, 0x85, 0x30, 0x82, 0x03, 0x81, 0x30, 0x82,
-    0x02, 0x77, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-    0x06, 0xa0, 0x82, 0x02, 0x68, 0x30, 0x82, 0x02, 0x64, 0x02, 0x01, 0x00,
-    0x30, 0x82, 0x02, 0x5d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x07, 0x01, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x0c, 0x01, 0x06, 0x30, 0x0e, 0x04, 0x08, 0x72, 0xdc, 0x9c,
-    0xcd, 0xe8, 0x69, 0xd5, 0xcc, 0x02, 0x02, 0x08, 0x00, 0x80, 0x82, 0x02,
-    0x30, 0x35, 0xfd, 0xee, 0x78, 0x47, 0x71, 0x12, 0x87, 0xc2, 0xcf, 0x1c,
-    0x12, 0xc4, 0x7a, 0x68, 0x6a, 0xb5, 0x21, 0xd6, 0xa4, 0x1a, 0x0d, 0xd3,
-    0x47, 0x6b, 0xad, 0xf0, 0xe0, 0xfc, 0x58, 0x6b, 0xd1, 0xf1, 0x1a, 0xce,
-    0xf5, 0x55, 0xca, 0x3b, 0x85, 0x18, 0x7e, 0x0d, 0x1e, 0x33, 0xcd, 0xf0,
-    0xd1, 0x0c, 0x26, 0x67, 0x67, 0x44, 0xba, 0x71, 0x93, 0xf8, 0xa4, 0xe0,
-    0x18, 0xe2, 0x1a, 0x23, 0x8e, 0xb5, 0xc7, 0xdc, 0xe1, 0x73, 0xa9, 0xa6,
-    0x03, 0xb1, 0x3a, 0x3a, 0xbd, 0x21, 0x51, 0x04, 0x30, 0xf0, 0x9e, 0xb5,
-    0xc9, 0xee, 0x5d, 0x7c, 0xf4, 0xae, 0x55, 0xd7, 0x15, 0x0c, 0xb3, 0x50,
-    0xa4, 0x52, 0x49, 0x74, 0x1a, 0xb3, 0xe9, 0xe8, 0x95, 0x4d, 0x57, 0x11,
-    0x5a, 0x8b, 0xf2, 0xdb, 0x2c, 0x2b, 0x79, 0xb0, 0xee, 0x1f, 0xd2, 0x02,
-    0xa4, 0x4c, 0x44, 0x1c, 0x7b, 0xea, 0x81, 0x8d, 0x5c, 0x1d, 0x52, 0xbe,
-    0x68, 0xf1, 0x56, 0x96, 0xf1, 0x14, 0x62, 0x2c, 0x34, 0x12, 0xbc, 0x7e,
-    0xa4, 0x59, 0x46, 0x6d, 0x9e, 0x97, 0xd5, 0x2a, 0x33, 0x43, 0x85, 0x93,
-    0x06, 0xf7, 0x8a, 0xc9, 0xd1, 0xb5, 0x91, 0x4a, 0x52, 0xba, 0xde, 0xca,
-    0x34, 0x65, 0x4b, 0x0a, 0xc8, 0x8a, 0xb1, 0xf1, 0x72, 0x21, 0x40, 0xc6,
-    0x6f, 0x23, 0xf7, 0x42, 0xb9, 0xec, 0xbb, 0xf1, 0x43, 0x1b, 0x98, 0x6e,
-    0xba, 0xe4, 0xee, 0x33, 0xc3, 0x51, 0xcb, 0x0c, 0x67, 0x7e, 0x19, 0xb3,
-    0x4e, 0x20, 0xab, 0x5a, 0x27, 0x81, 0xbb, 0x74, 0xd0, 0x2c, 0xa6, 0x16,
-    0x18, 0x57, 0xdd, 0xcf, 0xf9, 0xdc, 0x3d, 0x6d, 0x53, 0x2c, 0x91, 0xb6,
-    0xf1, 0xe6, 0xe2, 0xee, 0xc3, 0xc4, 0x06, 0x62, 0x98, 0x83, 0x2a, 0xe8,
-    0xc7, 0xdd, 0x22, 0xbc, 0xd1, 0xeb, 0x1f, 0xd5, 0x33, 0x49, 0x52, 0x72,
-    0x01, 0x84, 0x3a, 0x9e, 0xbd, 0x98, 0x9b, 0x44, 0xff, 0x58, 0x66, 0x6e,
-    0x03, 0x9a, 0x96, 0x52, 0x9e, 0x1d, 0xa2, 0x59, 0xc5, 0x5b, 0x32, 0xe1,
-    0x9e, 0xb0, 0xe0, 0x8c, 0xfb, 0x4c, 0x41, 0x04, 0x3a, 0x4e, 0x41, 0x3d,
-    0x7c, 0x01, 0x50, 0x8f, 0xe9, 0x21, 0xaa, 0xfc, 0x8b, 0x56, 0x64, 0xe2,
-    0x6b, 0x48, 0x74, 0x9f, 0x57, 0x21, 0x3e, 0x7f, 0x79, 0x12, 0x09, 0x84,
-    0x48, 0xa2, 0xcd, 0xdb, 0xb0, 0x27, 0x34, 0xf1, 0xef, 0x3c, 0xe5, 0xef,
-    0xe4, 0xe2, 0x1f, 0x04, 0x85, 0xc6, 0x00, 0x50, 0x19, 0x65, 0x1b, 0x7d,
-    0x0b, 0x60, 0x09, 0xe5, 0xe1, 0xd1, 0x71, 0xdc, 0x2f, 0x5e, 0xfa, 0x86,
-    0xf0, 0x8c, 0xf0, 0xf0, 0xf0, 0x46, 0xc5, 0xff, 0xc7, 0xcb, 0x6f, 0x37,
-    0x94, 0xc5, 0xb7, 0x62, 0xcb, 0xbc, 0x44, 0x2c, 0x0b, 0x96, 0xb7, 0x1d,
-    0x4f, 0xd6, 0xb0, 0x58, 0x50, 0x2f, 0xd6, 0xef, 0xe6, 0xfb, 0x75, 0x4c,
-    0xcf, 0xa6, 0x23, 0x79, 0xd1, 0x94, 0x7c, 0xaf, 0xff, 0x4e, 0x20, 0x61,
-    0x5f, 0x1d, 0x79, 0x59, 0x5c, 0x78, 0xd2, 0xad, 0xda, 0x87, 0xb9, 0x20,
-    0x5b, 0x67, 0x50, 0x82, 0x8b, 0x5f, 0xb0, 0x58, 0x99, 0x62, 0xa6, 0xd2,
-    0x03, 0x82, 0xbc, 0x8e, 0x89, 0xba, 0x9c, 0xe5, 0x20, 0x9a, 0x42, 0x37,
-    0x5f, 0x5b, 0x7b, 0xf0, 0x64, 0xf2, 0xc5, 0x54, 0x22, 0x9e, 0x15, 0xec,
-    0xca, 0xf7, 0x27, 0xad, 0x3a, 0xfb, 0x3c, 0xc0, 0x11, 0x9e, 0x4b, 0x5f,
-    0x41, 0xf1, 0xcd, 0x0e, 0xca, 0x9b, 0xb5, 0x0d, 0xab, 0x29, 0x76, 0x67,
-    0x04, 0x1b, 0xff, 0x52, 0xc7, 0x2c, 0x14, 0xd6, 0x04, 0x23, 0xc9, 0xcf,
-    0xf4, 0x3b, 0x71, 0x93, 0xb7, 0xe2, 0x2f, 0xe6, 0x1a, 0x32, 0x19, 0xba,
-    0x1c, 0x93, 0x87, 0x73, 0x7d, 0x51, 0x1d, 0x6b, 0x75, 0xbd, 0x17, 0xff,
-    0xef, 0xd0, 0x8f, 0x65, 0x37, 0xa0, 0x48, 0x67, 0x94, 0xfd, 0x6a, 0x71,
-    0xb3, 0x3f, 0x4e, 0x69, 0xa9, 0xc4, 0xae, 0xd1, 0x9b, 0x78, 0xdd, 0xeb,
-    0x06, 0x09, 0xca, 0x38, 0x13, 0x3b, 0x2a, 0xed, 0xea, 0x0c, 0xdf, 0xfe,
-    0x1f, 0x15, 0x86, 0x6b, 0xec, 0x20, 0x0d, 0x19, 0xd1, 0x32, 0xd6, 0x68,
-    0xc8, 0x26, 0x04, 0x91, 0x46, 0x6a, 0x67, 0x52, 0xba, 0x30, 0x82, 0x01,
-    0x02, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01,
-    0xa0, 0x81, 0xf4, 0x04, 0x81, 0xf1, 0x30, 0x81, 0xee, 0x30, 0x81, 0xeb,
-    0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01,
-    0x02, 0xa0, 0x81, 0xb4, 0x30, 0x81, 0xb1, 0x30, 0x1c, 0x06, 0x0a, 0x2a,
-    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0e, 0x04,
-    0x08, 0x32, 0xe5, 0x74, 0x9b, 0x0d, 0xcf, 0xa3, 0x05, 0x02, 0x02, 0x08,
-    0x00, 0x04, 0x81, 0x90, 0x7f, 0xa7, 0x6e, 0x5b, 0x73, 0x39, 0x15, 0x93,
-    0x42, 0x7c, 0xda, 0xc0, 0x16, 0xa0, 0x75, 0x96, 0x3d, 0x95, 0xc8, 0x52,
-    0x6b, 0x65, 0x32, 0xe5, 0xce, 0x62, 0x9b, 0xd5, 0xac, 0x38, 0xd7, 0xaa,
-    0x69, 0x22, 0xcc, 0xa9, 0x8d, 0x74, 0x15, 0x87, 0x06, 0xbd, 0x25, 0xd4,
-    0xd5, 0xa5, 0xda, 0x12, 0xd9, 0xd9, 0x47, 0x42, 0x05, 0xf3, 0xb7, 0x17,
-    0x4c, 0x54, 0xdb, 0x5e, 0x1c, 0xb9, 0x1d, 0x6b, 0xe2, 0xa8, 0x95, 0x08,
-    0x20, 0x09, 0x71, 0x35, 0x68, 0xb7, 0x1c, 0x6a, 0x6c, 0xfd, 0x99, 0xf9,
-    0x2b, 0x6f, 0xb3, 0x53, 0x55, 0xd9, 0xbe, 0x8c, 0xb1, 0x26, 0x12, 0xab,
-    0x8a, 0x58, 0x68, 0x84, 0x9f, 0xa1, 0xa6, 0xeb, 0x70, 0x33, 0x14, 0x0e,
-    0xf6, 0xb7, 0x31, 0x81, 0x79, 0x35, 0xb2, 0xab, 0x10, 0x4d, 0xe3, 0x16,
-    0xbd, 0x7f, 0x7e, 0x72, 0x12, 0xd5, 0x04, 0xd8, 0x23, 0x97, 0xca, 0x26,
-    0x38, 0x62, 0x2c, 0xb7, 0x09, 0x00, 0x3f, 0x01, 0xe0, 0xf7, 0xff, 0x12,
-    0x25, 0x26, 0x99, 0xdc, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86,
-    0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14, 0x3f,
-    0x31, 0x38, 0xec, 0xb9, 0xf1, 0x45, 0xe1, 0x3e, 0x90, 0x71, 0x0d, 0xc1,
-    0x28, 0xba, 0x4e, 0x6f, 0xa0, 0x9c, 0xed, 0x30, 0x31, 0x30, 0x21, 0x30,
-    0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
-    0xd1, 0x96, 0xa3, 0x29, 0xa9, 0x45, 0x1d, 0xad, 0xa1, 0x78, 0xa7, 0x1e,
-    0x30, 0xb8, 0x76, 0xd0, 0x87, 0x23, 0x4b, 0x02, 0x04, 0x08, 0x9c, 0xff,
-    0x9a, 0xa3, 0xf5, 0x70, 0xa8, 0xd9, 0x02, 0x02, 0x08, 0x00};
-
-// Generated with
-//   openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -password pass:"Hello, 世界"
-static const uint8_t kUnicode[] = {
-    0x30, 0x82, 0x03, 0xd2, 0x02, 0x01, 0x03, 0x30, 0x82, 0x03, 0x98, 0x06,
-    0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x82,
-    0x03, 0x89, 0x04, 0x82, 0x03, 0x85, 0x30, 0x82, 0x03, 0x81, 0x30, 0x82,
-    0x02, 0x77, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07,
-    0x06, 0xa0, 0x82, 0x02, 0x68, 0x30, 0x82, 0x02, 0x64, 0x02, 0x01, 0x00,
-    0x30, 0x82, 0x02, 0x5d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
-    0x01, 0x07, 0x01, 0x30, 0x1c, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
-    0x0d, 0x01, 0x0c, 0x01, 0x06, 0x30, 0x0e, 0x04, 0x08, 0x41, 0x46, 0x22,
-    0xac, 0xe7, 0xd6, 0x7e, 0x61, 0x02, 0x02, 0x08, 0x00, 0x80, 0x82, 0x02,
-    0x30, 0x9a, 0x05, 0x55, 0x5a, 0xa0, 0xf6, 0xd4, 0x8c, 0x5e, 0x1c, 0x27,
-    0x91, 0x11, 0xfd, 0x1d, 0xe8, 0xfd, 0xae, 0xf2, 0xe6, 0x9f, 0x28, 0xb8,
-    0x1e, 0xfa, 0xce, 0x88, 0xb4, 0x23, 0xd6, 0xfa, 0x6e, 0x07, 0xe9, 0x33,
-    0x81, 0x70, 0x1d, 0xd0, 0x5e, 0x94, 0x04, 0xf1, 0x60, 0x8e, 0xbf, 0xe1,
-    0xef, 0xf4, 0xd7, 0xb2, 0x2f, 0x0d, 0xe9, 0x70, 0x2b, 0xe8, 0x62, 0xfc,
-    0xd3, 0x2a, 0x49, 0xf3, 0xf1, 0x06, 0x6f, 0x2a, 0x94, 0x8c, 0x42, 0xff,
-    0xc6, 0x80, 0xa8, 0x6a, 0xbf, 0xa3, 0x0a, 0xd3, 0x8e, 0x59, 0x52, 0xea,
-    0x60, 0xe8, 0x5a, 0x64, 0x23, 0xac, 0x8d, 0x40, 0x2d, 0xc9, 0xfe, 0x0b,
-    0xf3, 0x93, 0x52, 0xc3, 0x3e, 0xea, 0x34, 0x9a, 0xea, 0x42, 0x6a, 0xe4,
-    0x09, 0x25, 0x44, 0x5d, 0x5e, 0xb4, 0x3b, 0xfb, 0xe0, 0xc2, 0xdf, 0xd8,
-    0xaf, 0xae, 0x20, 0x59, 0xb0, 0x8c, 0xdd, 0xb3, 0x4a, 0x5f, 0xca, 0x6c,
-    0x2f, 0xe3, 0xb4, 0x99, 0xc6, 0x8f, 0x75, 0xc5, 0x72, 0x31, 0x0e, 0x4c,
-    0x46, 0xe6, 0xe1, 0xbf, 0x3f, 0xdf, 0x02, 0x7d, 0xde, 0x35, 0xad, 0xd9,
-    0x9d, 0xcb, 0x74, 0xa7, 0x5c, 0x52, 0x3b, 0xc2, 0x9c, 0x76, 0xbd, 0xf7,
-    0x96, 0xfc, 0xc5, 0x9d, 0xc7, 0xa7, 0x79, 0x30, 0xa0, 0x89, 0xd6, 0xd3,
-    0xa8, 0xe8, 0x63, 0xd2, 0x3a, 0x3f, 0x88, 0xc1, 0x22, 0x8c, 0x20, 0x9c,
-    0xa0, 0x23, 0x07, 0xc3, 0xe4, 0x0c, 0x36, 0x19, 0xa8, 0xa3, 0xc4, 0xbc,
-    0xbc, 0xd6, 0x3d, 0x80, 0xcb, 0x54, 0x91, 0xc4, 0xab, 0x02, 0xd2, 0x43,
-    0x30, 0xe5, 0x01, 0xbd, 0x25, 0xcd, 0xe4, 0x29, 0x55, 0x0f, 0x6e, 0x83,
-    0xb8, 0xfb, 0x70, 0xf2, 0x34, 0x9a, 0x15, 0xc6, 0x16, 0xdf, 0x89, 0xe4,
-    0xd4, 0x83, 0x26, 0x08, 0x62, 0x05, 0xa6, 0xea, 0xf3, 0x63, 0xc3, 0xb5,
-    0x69, 0x62, 0xf8, 0x60, 0x5c, 0x28, 0x21, 0x51, 0xa4, 0x43, 0x76, 0xdd,
-    0x41, 0x6d, 0xbd, 0x6d, 0x8e, 0x3c, 0x63, 0x44, 0xb6, 0xea, 0x3a, 0x2a,
-    0x1c, 0x9d, 0x4b, 0x84, 0xed, 0xbd, 0x2e, 0x3b, 0x97, 0x89, 0xc3, 0x88,
-    0xca, 0x5f, 0x84, 0x0f, 0xbd, 0x50, 0xeb, 0x94, 0x67, 0x83, 0xa7, 0x83,
-    0xdf, 0xd7, 0x9d, 0x81, 0x40, 0x3a, 0x7b, 0xcf, 0x8b, 0x1d, 0x19, 0x9b,
-    0xe1, 0x4e, 0xb4, 0x4b, 0x1d, 0x5b, 0x98, 0xa8, 0xe3, 0x89, 0xed, 0xf3,
-    0x48, 0xfc, 0x0d, 0x38, 0xd5, 0x42, 0x31, 0xe3, 0x79, 0x3a, 0xea, 0xa3,
-    0x4b, 0x58, 0xa3, 0x75, 0x7c, 0xd4, 0xc4, 0x38, 0x27, 0x9e, 0x97, 0x4e,
-    0xc1, 0x70, 0xa8, 0xee, 0x85, 0xe2, 0xb8, 0x53, 0x57, 0x15, 0x05, 0xbb,
-    0xf1, 0xb4, 0xfd, 0xe8, 0x24, 0x99, 0x64, 0xa7, 0xf3, 0x6a, 0xcc, 0x4b,
-    0xe9, 0x8d, 0x66, 0x38, 0x9a, 0x45, 0xe2, 0x73, 0x5e, 0x66, 0x18, 0xd9,
-    0x64, 0x46, 0xd7, 0xd1, 0x23, 0x30, 0xbe, 0xa2, 0x4b, 0x5c, 0x0e, 0x4c,
-    0x8a, 0x47, 0x88, 0xb4, 0x7a, 0x2e, 0x0f, 0xb6, 0xab, 0x2d, 0x56, 0x20,
-    0x76, 0xf4, 0xa1, 0x37, 0xb6, 0x6b, 0x98, 0x2a, 0xb4, 0xda, 0x67, 0xcb,
-    0x67, 0x5c, 0xc7, 0x2d, 0x41, 0xf6, 0x14, 0x0d, 0x6b, 0x16, 0x05, 0xe6,
-    0x0a, 0xa2, 0xf7, 0x03, 0x5e, 0xf8, 0x9c, 0x85, 0x58, 0xa5, 0x82, 0xa4,
-    0xaf, 0xd1, 0xf0, 0x3a, 0x48, 0xa1, 0x68, 0x10, 0xa4, 0xa5, 0xc5, 0x87,
-    0xf5, 0xc3, 0xf8, 0x94, 0x9d, 0x13, 0xcb, 0x08, 0x42, 0x14, 0xb3, 0x68,
-    0x68, 0x18, 0xec, 0xa9, 0x57, 0x9c, 0xeb, 0xc9, 0xe9, 0xaf, 0x7d, 0xcc,
-    0xb9, 0x4d, 0x58, 0x8d, 0xbf, 0x04, 0xb7, 0x1c, 0x3f, 0xfa, 0xd3, 0xb9,
-    0xb9, 0xad, 0x0e, 0xd2, 0x5f, 0x8b, 0x41, 0xfa, 0xdc, 0x85, 0x3e, 0x0a,
-    0xba, 0x49, 0x1b, 0xe2, 0x0c, 0xb6, 0x85, 0x9b, 0x24, 0x3c, 0xdf, 0x26,
-    0x9d, 0x05, 0x50, 0x64, 0x12, 0x96, 0x24, 0xdb, 0x4d, 0x79, 0x07, 0xa7,
-    0xb2, 0x3c, 0xf9, 0x42, 0xca, 0xda, 0x67, 0xc0, 0x6d, 0xf2, 0x7e, 0xbc,
-    0x1e, 0x5c, 0x2b, 0x4b, 0xf6, 0xf4, 0x35, 0x82, 0x70, 0x6b, 0x81, 0x16,
-    0xfc, 0xf1, 0xa9, 0x5f, 0x07, 0x2c, 0xe9, 0x1e, 0x3f, 0x30, 0x82, 0x01,
-    0x02, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01,
-    0xa0, 0x81, 0xf4, 0x04, 0x81, 0xf1, 0x30, 0x81, 0xee, 0x30, 0x81, 0xeb,
-    0x06, 0x0b, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x0a, 0x01,
-    0x02, 0xa0, 0x81, 0xb4, 0x30, 0x81, 0xb1, 0x30, 0x1c, 0x06, 0x0a, 0x2a,
-    0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0e, 0x04,
-    0x08, 0x6f, 0xb9, 0x01, 0x93, 0x6c, 0x0d, 0x4d, 0xe1, 0x02, 0x02, 0x08,
-    0x00, 0x04, 0x81, 0x90, 0x07, 0x42, 0x6a, 0x5a, 0x4c, 0x41, 0x41, 0x38,
-    0xe4, 0x15, 0xc2, 0x85, 0x4e, 0x88, 0xc6, 0xd3, 0x6f, 0x9f, 0x25, 0xd8,
-    0x66, 0x86, 0xf3, 0x65, 0x5d, 0x51, 0x43, 0xd6, 0x03, 0x91, 0x4c, 0xeb,
-    0xbb, 0x75, 0xce, 0x8b, 0xf4, 0x47, 0x43, 0x4c, 0x1a, 0x4b, 0x48, 0x92,
-    0xf4, 0xaf, 0x0a, 0x5f, 0x49, 0x96, 0xea, 0xaf, 0x31, 0x29, 0x7b, 0xa3,
-    0xb5, 0xd3, 0xe4, 0x67, 0x0c, 0x20, 0x0e, 0x52, 0x9e, 0xcf, 0xcf, 0x6a,
-    0x2d, 0x45, 0x38, 0x52, 0x61, 0xbf, 0x10, 0x2b, 0xc1, 0xc5, 0xde, 0x04,
-    0x1d, 0x0a, 0x52, 0x88, 0x07, 0x39, 0xc2, 0xc1, 0xd0, 0x44, 0x39, 0x9f,
-    0x46, 0xf2, 0x69, 0xa4, 0x30, 0x5b, 0xe4, 0x60, 0x68, 0x69, 0xb0, 0x95,
-    0x78, 0x05, 0xef, 0xe1, 0x81, 0xc2, 0xd2, 0x4e, 0x29, 0x52, 0x39, 0x51,
-    0xfc, 0x3d, 0x28, 0xe1, 0x7b, 0x58, 0x76, 0xcf, 0x35, 0x33, 0x2f, 0xef,
-    0x95, 0x76, 0x0c, 0x52, 0x11, 0x69, 0x17, 0x3c, 0x56, 0x36, 0xc6, 0xe1,
-    0x9c, 0x1c, 0xd4, 0x23, 0x31, 0x25, 0x30, 0x23, 0x06, 0x09, 0x2a, 0x86,
-    0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x15, 0x31, 0x16, 0x04, 0x14, 0x3f,
-    0x31, 0x38, 0xec, 0xb9, 0xf1, 0x45, 0xe1, 0x3e, 0x90, 0x71, 0x0d, 0xc1,
-    0x28, 0xba, 0x4e, 0x6f, 0xa0, 0x9c, 0xed, 0x30, 0x31, 0x30, 0x21, 0x30,
-    0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14,
-    0x52, 0x8c, 0x3f, 0x72, 0x8c, 0xcf, 0x3a, 0xeb, 0xc8, 0xff, 0xc2, 0x8c,
-    0x48, 0x42, 0xa6, 0x1c, 0x42, 0x6e, 0x18, 0x43, 0x04, 0x08, 0xea, 0xec,
-    0xdc, 0xf6, 0xc4, 0xdf, 0xda, 0xd6, 0x02, 0x02, 0x08, 0x00};
-
+// kUnicodePassword is the password for unicode_password.p12
 static const char kUnicodePassword[] = u8"Hello, 世界";
 
+static bssl::Span<const uint8_t> StringToBytes(const std::string &str) {
+  return bssl::MakeConstSpan(reinterpret_cast<const uint8_t *>(str.data()),
+                             str.size());
+}
+
 static void TestImpl(const char *name, bssl::Span<const uint8_t> der,
                      const char *password,
                      const char *friendly_name) {
@@ -1531,50 +93,91 @@
 }
 
 TEST(PKCS12Test, TestOpenSSL) {
-  TestImpl("OpenSSL", kOpenSSL, kPassword, nullptr);
+  // openssl.p12 was generated by OpenSSL with:
+  //   openssl pkcs12 -export -inkey key.pem -in cacert.pem
+  std::string data = GetTestData("crypto/pkcs8/test/openssl.p12");
+  TestImpl("OpenSSL", StringToBytes(data), kPassword, nullptr);
 }
 
 TEST(PKCS12Test, TestNSS) {
-  TestImpl("NSS", kNSS, kPassword, "Internet Widgits Pty Ltd");
+  // nss.p12 is the result of importing the OpenSSL example PKCS#12 into Chrome
+  // on Linux and then exporting it again.
+  std::string data = GetTestData("crypto/pkcs8/test/nss.p12");
+  TestImpl("NSS", StringToBytes(data), kPassword, "Internet Widgits Pty Ltd");
 }
 
 TEST(PKCS12Test, TestWindows) {
-  // kWindows has a friendlyName, but only on the key, where we ignore it, and
-  // not the certificate.
-  TestImpl("Windows", kWindows, kPassword, nullptr);
+  // windows.p12 is a dummy key and certificate exported from the certificate
+  // manager on Windows 7. It has a friendlyName, but only on the key, where we
+  // ignore it, and not the certificate.
+  std::string data = GetTestData("crypto/pkcs8/test/windows.p12");
+  TestImpl("Windows", StringToBytes(data), kPassword, nullptr);
 }
 
 TEST(PKCS12Test, TestPBES2) {
-  TestImpl("kPBES2WithSHA1", kPBES2WithSHA1, kPassword, nullptr);
-  TestImpl("kPBES2WithSHA256", kPBES2WithSHA256, kPassword, nullptr);
+  // pbes2_sha1.p12 is a PKCS#12 file using PBES2 and HMAC-SHA-1 created with:
+  // openssl pkcs12 -export -inkey key.pem -in cert.pem -keypbe AES-128-CBC
+  // -certpbe AES-128-CBC
+  //
+  // This was generated with an older OpenSSL, which used hmacWithSHA1 as the
+  // PRF. (There is currently no way to specify the PRF in the pkcs12 command.)
+  std::string data = GetTestData("crypto/pkcs8/test/pbes2_sha1.p12");
+  TestImpl("kPBES2WithSHA1", StringToBytes(data), kPassword, nullptr);
+
+  // pbes2_sha256.p12 is a PKCS#12 file using PBES2 and HMAC-SHA-256. It was
+  // generated in the same way as pbes2_sha1.p12, but using OpenSSL 1.1.1b,
+  // which uses hmacWithSHA256 as the PRF.
+  data = GetTestData("crypto/pkcs8/test/pbes2_sha256.p12");
+  TestImpl("kPBES2WithSHA256", StringToBytes(data), kPassword, nullptr);
 }
 
 TEST(PKCS12Test, TestNoEncryption) {
-  TestImpl("kNoEncryption", kNoEncryption, kPassword, nullptr);
+  // no_encryption.p12 is a PKCS#12 file with neither the key or certificate is
+  // encrypted. It was generated with:
+  //
+  //   openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -keypbe NONE -certpbe NONE -password pass:foo
+  std::string data = GetTestData("crypto/pkcs8/test/no_encryption.p12");
+  TestImpl("kNoEncryption", StringToBytes(data), kPassword, nullptr);
 }
 
 TEST(PKCS12Test, TestEmptyPassword) {
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   return;  // The MAC check always passes in fuzzer mode.
 #endif
-  TestImpl("EmptyPassword (empty password)", kEmptyPassword, "", nullptr);
-  TestImpl("EmptyPassword (null password)", kEmptyPassword, nullptr, nullptr);
+
+  // Generated with
+  //   openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -password pass:  
+  std::string data = GetTestData("crypto/pkcs8/test/empty_password.p12");
+  TestImpl("EmptyPassword (empty password)", StringToBytes(data), "", nullptr);
+  TestImpl("EmptyPassword (null password)", StringToBytes(data), nullptr,
+           nullptr);
 }
 
 TEST(PKCS12Test, TestNullPassword) {
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   return;  // The MAC check always passes in fuzzer mode.
 #endif
-  TestImpl("NullPassword (empty password)", kNullPassword, "", nullptr);
-  TestImpl("NullPassword (null password)", kNullPassword, nullptr, nullptr);
+
+  // Generated with
+  //   openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -password pass:
+  // But with OpenSSL patched to pass NULL into PKCS12_create and
+  // PKCS12_set_mac.
+  std::string data = GetTestData("crypto/pkcs8/test/null_password.p12");
+  TestImpl("NullPassword (empty password)", StringToBytes(data), "", nullptr);
+  TestImpl("NullPassword (null password)", StringToBytes(data), nullptr,
+           nullptr);
 }
 
 TEST(PKCS12Test, TestUnicode) {
-  TestImpl("Unicode", kUnicode, kUnicodePassword, nullptr);
+  // Generated with
+  //   openssl pkcs12 -export -inkey ecdsa_p256_key.pem -in ecdsa_p256_cert.pem -password pass:"Hello, 世界"
+  std::string data = GetTestData("crypto/pkcs8/test/unicode_password.p12");
+  TestImpl("Unicode", StringToBytes(data), kUnicodePassword, nullptr);
 }
 
 TEST(PKCS12Test, TestWindowsCompat) {
-  TestCompat(kWindows);
+  std::string data = GetTestData("crypto/pkcs8/test/windows.p12");
+  TestCompat(StringToBytes(data));
 }
 
 // kTestKey is a test P-256 key.
@@ -1825,6 +428,27 @@
                 {bssl::Span<const uint8_t>(kTestCert2)},
                 NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
                 NID_pbe_WithSHA1And3_Key_TripleDES_CBC, 100, 100);
+
+  // Test unencrypted and partially unencrypted PKCS#12 files.
+  TestRoundTrip(kPassword, /*name=*/nullptr,
+                bssl::Span<const uint8_t>(kTestKey),
+                bssl::Span<const uint8_t>(kTestCert),
+                {bssl::Span<const uint8_t>(kTestCert2)},
+                /*key_nid=*/-1,
+                /*cert_nid=*/-1, /*iterations=*/100, /*mac_iterations=*/100);
+  TestRoundTrip(kPassword, /*name=*/nullptr,
+                bssl::Span<const uint8_t>(kTestKey),
+                bssl::Span<const uint8_t>(kTestCert),
+                {bssl::Span<const uint8_t>(kTestCert2)},
+                /*key_nid=*/NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
+                /*cert_nid=*/-1, /*iterations=*/100, /*mac_iterations=*/100);
+  TestRoundTrip(kPassword, /*name=*/nullptr,
+                bssl::Span<const uint8_t>(kTestKey),
+                bssl::Span<const uint8_t>(kTestCert),
+                {bssl::Span<const uint8_t>(kTestCert2)},
+                /*key_nid=*/-1,
+                /*cert_nid=*/NID_pbe_WithSHA1And3_Key_TripleDES_CBC,
+                /*iterations=*/100, /*mac_iterations=*/100);
 }
 
 static bssl::UniquePtr<EVP_PKEY> MakeTestKey() {
diff --git a/deps/boringssl/src/crypto/pkcs8/pkcs8_x509.c b/deps/boringssl/src/crypto/pkcs8/pkcs8_x509.c
index a2f9075..e24fb42 100644
--- a/deps/boringssl/src/crypto/pkcs8/pkcs8_x509.c
+++ b/deps/boringssl/src/crypto/pkcs8/pkcs8_x509.c
@@ -943,11 +943,6 @@
 
   // OpenSSL selects the last certificate which matches the private key as
   // |out_cert|.
-  //
-  // TODO(davidben): OpenSSL additionally reverses the order of the
-  // certificates, which was likely originally a bug, but may be a feature by
-  // now. See https://crbug.com/boringssl/250 and
-  // https://github.com/openssl/openssl/issues/6698.
   *out_cert = NULL;
   size_t num_certs = sk_X509_num(ca_certs);
   if (*out_pkey != NULL && num_certs > 0) {
@@ -1074,31 +1069,24 @@
   return 1;
 }
 
-static int make_cert_safe_contents(uint8_t **out_data, size_t *out_len,
-                                   X509 *cert, const STACK_OF(X509) *chain,
-                                   const char *name, const uint8_t *key_id,
-                                   size_t key_id_len) {
-  int ret = 0;
-  CBB cbb, safe_contents;
-  if (!CBB_init(&cbb, 0) ||
-      !CBB_add_asn1(&cbb, &safe_contents, CBS_ASN1_SEQUENCE) ||
+static int add_cert_safe_contents(CBB *cbb, X509 *cert,
+                                  const STACK_OF(X509) *chain, const char *name,
+                                  const uint8_t *key_id, size_t key_id_len) {
+  CBB safe_contents;
+  if (!CBB_add_asn1(cbb, &safe_contents, CBS_ASN1_SEQUENCE) ||
       (cert != NULL &&
        !add_cert_bag(&safe_contents, cert, name, key_id, key_id_len))) {
-    goto err;
+    return 0;
   }
 
   for (size_t i = 0; i < sk_X509_num(chain); i++) {
     // Only the leaf certificate gets attributes.
     if (!add_cert_bag(&safe_contents, sk_X509_value(chain, i), NULL, NULL, 0)) {
-      goto err;
+      return 0;
     }
   }
 
-  ret = CBB_finish(&cbb, out_data, out_len);
-
-err:
-  CBB_cleanup(&cbb);
-  return ret;
+  return CBB_flush(cbb);
 }
 
 static int add_encrypted_data(CBB *out, int pbe_nid, const char *password,
@@ -1181,9 +1169,6 @@
   if (// In OpenSSL, this specifies a non-standard Microsoft key usage extension
       // which we do not currently support.
       key_type != 0 ||
-      // In OpenSSL, -1 here means to use no encryption, which we do not
-      // currently support.
-      key_nid < 0 || cert_nid < 0 ||
       // In OpenSSL, -1 here means to omit the MAC, which we do not
       // currently support. Omitting it is also invalid for a password-based
       // PKCS#12 file.
@@ -1194,6 +1179,36 @@
     return 0;
   }
 
+  // PKCS#12 is a very confusing recursive data format, built out of another
+  // recursive data format. Section 5.1 of RFC 7292 describes the encoding
+  // algorithm, but there is no clear overview. A quick summary:
+  //
+  // PKCS#7 defines a ContentInfo structure, which is a overgeneralized typed
+  // combinator structure for applying cryptography. We care about two types. A
+  // data ContentInfo contains an OCTET STRING and is a leaf node of the
+  // combinator tree. An encrypted-data ContentInfo contains encryption
+  // parameters (key derivation and encryption) and wraps another ContentInfo,
+  // usually data.
+  //
+  // A PKCS#12 file is a PFX structure (section 4), which contains a single data
+  // ContentInfo and a MAC over it. This root ContentInfo is the
+  // AuthenticatedSafe and its payload is a SEQUENCE of other ContentInfos, so
+  // that different parts of the PKCS#12 file can by differently protected.
+  //
+  // Each ContentInfo in the AuthenticatedSafe, after undoing all the PKCS#7
+  // combinators, has SafeContents payload. A SafeContents is a SEQUENCE of
+  // SafeBag. SafeBag is PKCS#12's typed structure, with subtypes such as KeyBag
+  // and CertBag. Confusingly, there is a SafeContents bag type which itself
+  // recursively contains more SafeBags, but we do not implement this. Bags also
+  // can have attributes.
+  //
+  // The grouping of SafeBags into intermediate ContentInfos does not appear to
+  // be significant, except that all SafeBags sharing a ContentInfo have the
+  // same level of protection. Additionally, while keys may be encrypted by
+  // placing a KeyBag in an encrypted-data ContentInfo, PKCS#12 also defines a
+  // key-specific encryption container, PKCS8ShroudedKeyBag, which is used
+  // instead.
+
   // Note that |password| may be NULL to specify no password, rather than the
   // empty string. They are encoded differently in PKCS#12. (One is the empty
   // byte array and the other is NUL-terminated UCS-2.)
@@ -1236,24 +1251,43 @@
   // If there are any certificates, place them in CertBags wrapped in a single
   // encrypted ContentInfo.
   if (cert != NULL || sk_X509_num(chain) > 0) {
-    uint8_t *data;
-    size_t len;
-    if (!make_cert_safe_contents(&data, &len, cert, chain, name, key_id,
-                                 key_id_len)) {
-      goto err;
-    }
-    int ok = add_encrypted_data(&content_infos, cert_nid, password,
-                                password_len, iterations, data, len);
-    OPENSSL_free(data);
-    if (!ok) {
-      goto err;
+    if (cert_nid < 0) {
+      // Place the certificates in an unencrypted ContentInfo. This could be
+      // more compactly-encoded by reusing the same ContentInfo as the key, but
+      // OpenSSL does not do this. We keep them separate for consistency. (Keys,
+      // even when encrypted, are always placed in unencrypted ContentInfos.
+      // PKCS#12 defines bag-level encryption for keys.)
+      CBB content_info, oid, wrapper, data;
+      if (!CBB_add_asn1(&content_infos, &content_info, CBS_ASN1_SEQUENCE) ||
+          !CBB_add_asn1(&content_info, &oid, CBS_ASN1_OBJECT) ||
+          !CBB_add_bytes(&oid, kPKCS7Data, sizeof(kPKCS7Data)) ||
+          !CBB_add_asn1(&content_info, &wrapper,
+                        CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
+          !CBB_add_asn1(&wrapper, &data, CBS_ASN1_OCTETSTRING) ||
+          !add_cert_safe_contents(&data, cert, chain, name, key_id,
+                                  key_id_len) ||
+          !CBB_flush(&content_infos)) {
+        goto err;
+      }
+    } else {
+      CBB plaintext_cbb;
+      int ok = CBB_init(&plaintext_cbb, 0) &&
+               add_cert_safe_contents(&plaintext_cbb, cert, chain, name, key_id,
+                                      key_id_len) &&
+               add_encrypted_data(
+                   &content_infos, cert_nid, password, password_len, iterations,
+                   CBB_data(&plaintext_cbb), CBB_len(&plaintext_cbb));
+      CBB_cleanup(&plaintext_cbb);
+      if (!ok) {
+        goto err;
+      }
     }
   }
 
-  // If there is a key, place it in a single PKCS8ShroudedKeyBag wrapped in an
-  // unencrypted ContentInfo. (One could also place it in a KeyBag inside an
-  // encrypted ContentInfo, but OpenSSL does not do this and some PKCS#12
-  // consumers do not support KeyBags.)
+  // If there is a key, place it in a single KeyBag or PKCS8ShroudedKeyBag
+  // wrapped in an unencrypted ContentInfo. (One could also place it in a KeyBag
+  // inside an encrypted ContentInfo, but OpenSSL does not do this and some
+  // PKCS#12 consumers do not support KeyBags.)
   if (pkey != NULL) {
     CBB content_info, oid, wrapper, data, safe_contents, bag, bag_oid,
         bag_contents;
@@ -1267,16 +1301,29 @@
         !CBB_add_asn1(&data, &safe_contents, CBS_ASN1_SEQUENCE) ||
         // Add a SafeBag containing a PKCS8ShroudedKeyBag.
         !CBB_add_asn1(&safe_contents, &bag, CBS_ASN1_SEQUENCE) ||
-        !CBB_add_asn1(&bag, &bag_oid, CBS_ASN1_OBJECT) ||
-        !CBB_add_bytes(&bag_oid, kPKCS8ShroudedKeyBag,
-                       sizeof(kPKCS8ShroudedKeyBag)) ||
-        !CBB_add_asn1(&bag, &bag_contents,
-                      CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
-        !PKCS8_marshal_encrypted_private_key(
-            &bag_contents, key_nid, NULL, password, password_len,
-            NULL /* generate a random salt */, 0 /* use default salt length */,
-            iterations, pkey) ||
-        !add_bag_attributes(&bag, name, key_id, key_id_len) ||
+        !CBB_add_asn1(&bag, &bag_oid, CBS_ASN1_OBJECT)) {
+      goto err;
+    }
+    if (key_nid < 0) {
+      if (!CBB_add_bytes(&bag_oid, kKeyBag, sizeof(kKeyBag)) ||
+          !CBB_add_asn1(&bag, &bag_contents,
+                        CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
+          !EVP_marshal_private_key(&bag_contents, pkey)) {
+        goto err;
+      }
+    } else {
+      if (!CBB_add_bytes(&bag_oid, kPKCS8ShroudedKeyBag,
+                         sizeof(kPKCS8ShroudedKeyBag)) ||
+          !CBB_add_asn1(&bag, &bag_contents,
+                        CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 0) ||
+          !PKCS8_marshal_encrypted_private_key(
+              &bag_contents, key_nid, NULL, password, password_len,
+              NULL /* generate a random salt */,
+              0 /* use default salt length */, iterations, pkey)) {
+        goto err;
+      }
+    }
+    if (!add_bag_attributes(&bag, name, key_id, key_id_len) ||
         !CBB_flush(&content_infos)) {
       goto err;
     }
diff --git a/deps/boringssl/src/crypto/poly1305/poly1305.c b/deps/boringssl/src/crypto/poly1305/poly1305.c
index 31a567d..c07b1e9 100644
--- a/deps/boringssl/src/crypto/poly1305/poly1305.c
+++ b/deps/boringssl/src/crypto/poly1305/poly1305.c
@@ -56,7 +56,7 @@
 
 static inline struct poly1305_state_st *poly1305_aligned_state(
     poly1305_state *state) {
-  return (struct poly1305_state_st *)(((uintptr_t)state + 63) & ~63);
+  return align_pointer(state, 64);
 }
 
 // poly1305_blocks updates |state| given some amount of input data. This
diff --git a/deps/boringssl/src/crypto/pool/pool.c b/deps/boringssl/src/crypto/pool/pool.c
index 917e43c..88bf8af 100644
--- a/deps/boringssl/src/crypto/pool/pool.c
+++ b/deps/boringssl/src/crypto/pool/pool.c
@@ -22,6 +22,7 @@
 #include <openssl/thread.h>
 
 #include "../internal.h"
+#include "../lhash/internal.h"
 #include "internal.h"
 
 
diff --git a/deps/boringssl/src/crypto/rand_extra/deterministic.c b/deps/boringssl/src/crypto/rand_extra/deterministic.c
index 38cfd11..435f063 100644
--- a/deps/boringssl/src/crypto/rand_extra/deterministic.c
+++ b/deps/boringssl/src/crypto/rand_extra/deterministic.c
@@ -49,4 +49,8 @@
   CRYPTO_chacha_20(out, out, requested, kZeroKey, nonce, 0);
 }
 
+void CRYPTO_sysrand_for_seed(uint8_t *out, size_t requested) {
+  CRYPTO_sysrand(out, requested);
+}
+
 #endif  // BORINGSSL_UNSAFE_DETERMINISTIC_MODE
diff --git a/deps/boringssl/src/crypto/rand_extra/fuchsia.c b/deps/boringssl/src/crypto/rand_extra/fuchsia.c
index 0514d80..ee6cfdb 100644
--- a/deps/boringssl/src/crypto/rand_extra/fuchsia.c
+++ b/deps/boringssl/src/crypto/rand_extra/fuchsia.c
@@ -27,4 +27,8 @@
   zx_cprng_draw(out, requested);
 }
 
+void CRYPTO_sysrand_for_seed(uint8_t *out, size_t requested) {
+  CRYPTO_sysrand(out, requested);
+}
+
 #endif  // OPENSSL_FUCHSIA && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE
diff --git a/deps/boringssl/src/crypto/rand_extra/passive.c b/deps/boringssl/src/crypto/rand_extra/passive.c
index a8c2487..a2b388f 100644
--- a/deps/boringssl/src/crypto/rand_extra/passive.c
+++ b/deps/boringssl/src/crypto/rand_extra/passive.c
@@ -15,7 +15,7 @@
 #include <openssl/base.h>
 #include "../fipsmodule/rand/internal.h"
 
-#if defined(BORINGSSL_FIPS_PASSIVE_ENTROPY)
+#if defined(BORINGSSL_FIPS)
 
 // RAND_need_entropy is called by the FIPS module when it has blocked because of
 // a lack of entropy. This signal is used as an indication to feed it more.
@@ -31,4 +31,4 @@
   RAND_load_entropy(buf, todo, used_cpu);
 }
 
-#endif  // BORINGSSL_FIPS_PASSIVE_ENTROPY
+#endif  // FIPS
diff --git a/deps/boringssl/src/crypto/rand_extra/rand_extra.c b/deps/boringssl/src/crypto/rand_extra/rand_extra.c
index 596605a..e73b99e 100644
--- a/deps/boringssl/src/crypto/rand_extra/rand_extra.c
+++ b/deps/boringssl/src/crypto/rand_extra/rand_extra.c
@@ -69,6 +69,6 @@
 
 const RAND_METHOD *RAND_get_rand_method(void) { return RAND_SSLeay(); }
 
-void RAND_set_rand_method(const RAND_METHOD *method) {}
+int RAND_set_rand_method(const RAND_METHOD *method) { return 1; }
 
 void RAND_cleanup(void) {}
diff --git a/deps/boringssl/src/crypto/rand_extra/windows.c b/deps/boringssl/src/crypto/rand_extra/windows.c
index 82d5542..8ade689 100644
--- a/deps/boringssl/src/crypto/rand_extra/windows.c
+++ b/deps/boringssl/src/crypto/rand_extra/windows.c
@@ -66,4 +66,8 @@
   return;
 }
 
+void CRYPTO_sysrand_for_seed(uint8_t *out, size_t requested) {
+  CRYPTO_sysrand(out, requested);
+}
+
 #endif  // OPENSSL_WINDOWS && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE
diff --git a/deps/boringssl/src/crypto/rsa_extra/rsa_asn1.c b/deps/boringssl/src/crypto/rsa_extra/rsa_asn1.c
index 3cc6a9c..58fd69a 100644
--- a/deps/boringssl/src/crypto/rsa_extra/rsa_asn1.c
+++ b/deps/boringssl/src/crypto/rsa_extra/rsa_asn1.c
@@ -102,8 +102,7 @@
     return NULL;
   }
 
-  if (!BN_is_odd(ret->e) ||
-      BN_num_bits(ret->e) < 2) {
+  if (!RSA_check_key(ret)) {
     OPENSSL_PUT_ERROR(RSA, RSA_R_BAD_RSA_PARAMETERS);
     RSA_free(ret);
     return NULL;
diff --git a/deps/boringssl/src/crypto/thread_pthread.c b/deps/boringssl/src/crypto/thread_pthread.c
index 2cb1000..e873d04 100644
--- a/deps/boringssl/src/crypto/thread_pthread.c
+++ b/deps/boringssl/src/crypto/thread_pthread.c
@@ -127,34 +127,6 @@
 static pthread_key_t g_thread_local_key;
 static int g_thread_local_key_created = 0;
 
-// OPENSSL_DANGEROUS_RELEASE_PTHREAD_KEY can be defined to cause
-// |pthread_key_delete| to be called in a destructor function. This can be
-// useful for programs that dlclose BoringSSL.
-//
-// Note that dlclose()ing BoringSSL is not supported and will leak memory:
-// thread-local values will be leaked as well as anything initialised via a
-// once. The |pthread_key_t| is destroyed because they run out very quickly,
-// while the other leaks are slow, and this allows code that happens to use
-// dlclose() despite all the problems to continue functioning.
-//
-// This is marked "dangerous" because it can cause multi-threaded processes to
-// crash (even if they don't use dlclose): if the destructor runs while other
-// threads are still executing then they may end up using an invalid key to
-// access thread-local variables.
-//
-// This may be removed after February 2020.
-#if defined(OPENSSL_DANGEROUS_RELEASE_PTHREAD_KEY) && \
-    (defined(__GNUC__) || defined(__clang__))
-// thread_key_destructor is called when the library is unloaded with dlclose.
-static void thread_key_destructor(void) __attribute__((destructor, unused));
-static void thread_key_destructor(void) {
-  if (g_thread_local_key_created) {
-    g_thread_local_key_created = 0;
-    pthread_key_delete(g_thread_local_key);
-  }
-}
-#endif
-
 static void thread_local_init(void) {
   g_thread_local_key_created =
       pthread_key_create(&g_thread_local_key, thread_local_destructor) == 0;
diff --git a/deps/boringssl/src/crypto/x509/a_strex.c b/deps/boringssl/src/crypto/x509/a_strex.c
deleted file mode 100644
index 2c4824e..0000000
--- a/deps/boringssl/src/crypto/x509/a_strex.c
+++ /dev/null
@@ -1,653 +0,0 @@
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.] */
-
-#include <openssl/x509.h>
-
-#include <inttypes.h>
-#include <string.h>
-
-#include <openssl/asn1.h>
-#include <openssl/mem.h>
-#include <openssl/obj.h>
-
-#include "charmap.h"
-#include "../asn1/asn1_locl.h"
-
-/*
- * ASN1_STRING_print_ex() and X509_NAME_print_ex(). Enhanced string and name
- * printing routines handling multibyte characters, RFC2253 and a host of
- * other options.
- */
-
-#define CHARTYPE_BS_ESC         (ASN1_STRFLGS_ESC_2253 | CHARTYPE_FIRST_ESC_2253 | CHARTYPE_LAST_ESC_2253)
-
-#define ESC_FLAGS (ASN1_STRFLGS_ESC_2253 | \
-                  ASN1_STRFLGS_ESC_QUOTE | \
-                  ASN1_STRFLGS_ESC_CTRL | \
-                  ASN1_STRFLGS_ESC_MSB)
-
-static int send_bio_chars(void *arg, const void *buf, int len)
-{
-    if (!arg)
-        return 1;
-    if (BIO_write(arg, buf, len) != len)
-        return 0;
-    return 1;
-}
-
-static int send_fp_chars(void *arg, const void *buf, int len)
-{
-    if (!arg)
-        return 1;
-    if (fwrite(buf, 1, len, arg) != (unsigned int)len)
-        return 0;
-    return 1;
-}
-
-typedef int char_io (void *arg, const void *buf, int len);
-
-/*
- * This function handles display of strings, one character at a time. It is
- * passed an unsigned long for each character because it could come from 2 or
- * even 4 byte forms.
- */
-
-#define HEX_SIZE(type) (sizeof(type)*2)
-
-static int do_esc_char(uint32_t c, unsigned char flags, char *do_quotes,
-                       char_io *io_ch, void *arg)
-{
-    unsigned char chflgs, chtmp;
-    char tmphex[HEX_SIZE(uint32_t) + 3];
-
-    if (c > 0xffff) {
-        BIO_snprintf(tmphex, sizeof tmphex, "\\W%08" PRIX32, c);
-        if (!io_ch(arg, tmphex, 10))
-            return -1;
-        return 10;
-    }
-    if (c > 0xff) {
-        BIO_snprintf(tmphex, sizeof tmphex, "\\U%04" PRIX32, c);
-        if (!io_ch(arg, tmphex, 6))
-            return -1;
-        return 6;
-    }
-    chtmp = (unsigned char)c;
-    if (chtmp > 0x7f)
-        chflgs = flags & ASN1_STRFLGS_ESC_MSB;
-    else
-        chflgs = char_type[chtmp] & flags;
-    if (chflgs & CHARTYPE_BS_ESC) {
-        /* If we don't escape with quotes, signal we need quotes */
-        if (chflgs & ASN1_STRFLGS_ESC_QUOTE) {
-            if (do_quotes)
-                *do_quotes = 1;
-            if (!io_ch(arg, &chtmp, 1))
-                return -1;
-            return 1;
-        }
-        if (!io_ch(arg, "\\", 1))
-            return -1;
-        if (!io_ch(arg, &chtmp, 1))
-            return -1;
-        return 2;
-    }
-    if (chflgs & (ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB)) {
-        BIO_snprintf(tmphex, 11, "\\%02X", chtmp);
-        if (!io_ch(arg, tmphex, 3))
-            return -1;
-        return 3;
-    }
-    /*
-     * If we get this far and do any escaping at all must escape the escape
-     * character itself: backslash.
-     */
-    if (chtmp == '\\' && flags & ESC_FLAGS) {
-        if (!io_ch(arg, "\\\\", 2))
-            return -1;
-        return 2;
-    }
-    if (!io_ch(arg, &chtmp, 1))
-        return -1;
-    return 1;
-}
-
-#define BUF_TYPE_WIDTH_MASK     0x7
-#define BUF_TYPE_CONVUTF8       0x8
-
-/*
- * This function sends each character in a buffer to do_esc_char(). It
- * interprets the content formats and converts to or from UTF8 as
- * appropriate.
- */
-
-static int do_buf(unsigned char *buf, int buflen,
-                  int type, unsigned char flags, char *quotes, char_io *io_ch,
-                  void *arg)
-{
-    int i, outlen, len, charwidth;
-    unsigned char orflags, *p, *q;
-    uint32_t c;
-    p = buf;
-    q = buf + buflen;
-    outlen = 0;
-    charwidth = type & BUF_TYPE_WIDTH_MASK;
-
-    switch (charwidth) {
-    case 4:
-        if (buflen & 3) {
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_UNIVERSALSTRING);
-            return -1;
-        }
-        break;
-    case 2:
-        if (buflen & 1) {
-            OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_BMPSTRING);
-            return -1;
-        }
-        break;
-    default:
-        break;
-    }
-
-    while (p != q) {
-        if (p == buf && flags & ASN1_STRFLGS_ESC_2253)
-            orflags = CHARTYPE_FIRST_ESC_2253;
-        else
-            orflags = 0;
-        switch (charwidth) {
-        case 4:
-            c = ((uint32_t)*p++) << 24;
-            c |= ((uint32_t)*p++) << 16;
-            c |= ((uint32_t)*p++) << 8;
-            c |= *p++;
-            break;
-
-        case 2:
-            c = ((uint32_t)*p++) << 8;
-            c |= *p++;
-            break;
-
-        case 1:
-            c = *p++;
-            break;
-
-        case 0:
-            i = UTF8_getc(p, buflen, &c);
-            if (i < 0)
-                return -1;      /* Invalid UTF8String */
-            buflen -= i;
-            p += i;
-            break;
-        default:
-            return -1;          /* invalid width */
-        }
-        if (p == q && flags & ASN1_STRFLGS_ESC_2253)
-            orflags = CHARTYPE_LAST_ESC_2253;
-        if (type & BUF_TYPE_CONVUTF8) {
-            unsigned char utfbuf[6];
-            int utflen;
-            utflen = UTF8_putc(utfbuf, sizeof utfbuf, c);
-            for (i = 0; i < utflen; i++) {
-                /*
-                 * We don't need to worry about setting orflags correctly
-                 * because if utflen==1 its value will be correct anyway
-                 * otherwise each character will be > 0x7f and so the
-                 * character will never be escaped on first and last.
-                 */
-                len =
-                    do_esc_char(utfbuf[i], (unsigned char)(flags | orflags),
-                                quotes, io_ch, arg);
-                if (len < 0)
-                    return -1;
-                outlen += len;
-            }
-        } else {
-            len =
-                do_esc_char(c, (unsigned char)(flags | orflags), quotes,
-                            io_ch, arg);
-            if (len < 0)
-                return -1;
-            outlen += len;
-        }
-    }
-    return outlen;
-}
-
-/* This function hex dumps a buffer of characters */
-
-static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf,
-                       int buflen)
-{
-    static const char hexdig[] = "0123456789ABCDEF";
-    unsigned char *p, *q;
-    char hextmp[2];
-    if (arg) {
-        p = buf;
-        q = buf + buflen;
-        while (p != q) {
-            hextmp[0] = hexdig[*p >> 4];
-            hextmp[1] = hexdig[*p & 0xf];
-            if (!io_ch(arg, hextmp, 2))
-                return -1;
-            p++;
-        }
-    }
-    return buflen << 1;
-}
-
-/*
- * "dump" a string. This is done when the type is unknown, or the flags
- * request it. We can either dump the content octets or the entire DER
- * encoding. This uses the RFC2253 #01234 format.
- */
-
-static int do_dump(unsigned long lflags, char_io *io_ch, void *arg,
-                   const ASN1_STRING *str)
-{
-    /*
-     * Placing the ASN1_STRING in a temp ASN1_TYPE allows the DER encoding to
-     * readily obtained
-     */
-    ASN1_TYPE t;
-    unsigned char *der_buf, *p;
-    int outlen, der_len;
-
-    if (!io_ch(arg, "#", 1))
-        return -1;
-    /* If we don't dump DER encoding just dump content octets */
-    if (!(lflags & ASN1_STRFLGS_DUMP_DER)) {
-        outlen = do_hex_dump(io_ch, arg, str->data, str->length);
-        if (outlen < 0)
-            return -1;
-        return outlen + 1;
-    }
-    t.type = str->type;
-    t.value.ptr = (char *)str;
-    der_len = i2d_ASN1_TYPE(&t, NULL);
-    der_buf = OPENSSL_malloc(der_len);
-    if (!der_buf)
-        return -1;
-    p = der_buf;
-    i2d_ASN1_TYPE(&t, &p);
-    outlen = do_hex_dump(io_ch, arg, der_buf, der_len);
-    OPENSSL_free(der_buf);
-    if (outlen < 0)
-        return -1;
-    return outlen + 1;
-}
-
-/*
- * Lookup table to convert tags to character widths, 0 = UTF8 encoded, -1 is
- * used for non string types otherwise it is the number of bytes per
- * character
- */
-
-static const signed char tag2nbyte[] = {
-    -1, -1, -1, -1, -1,         /* 0-4 */
-    -1, -1, -1, -1, -1,         /* 5-9 */
-    -1, -1, 0, -1,              /* 10-13 */
-    -1, -1, -1, -1,             /* 15-17 */
-    1, 1, 1,                    /* 18-20 */
-    -1, 1, 1, 1,                /* 21-24 */
-    -1, 1, -1,                  /* 25-27 */
-    4, -1, 2                    /* 28-30 */
-};
-
-/*
- * This is the main function, print out an ASN1_STRING taking note of various
- * escape and display options. Returns number of characters written or -1 if
- * an error occurred.
- */
-
-static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags,
-                       const ASN1_STRING *str)
-{
-    int outlen, len;
-    int type;
-    char quotes;
-    unsigned char flags;
-    quotes = 0;
-    /* Keep a copy of escape flags */
-    flags = (unsigned char)(lflags & ESC_FLAGS);
-
-    type = str->type;
-
-    outlen = 0;
-
-    if (lflags & ASN1_STRFLGS_SHOW_TYPE) {
-        const char *tagname;
-        tagname = ASN1_tag2str(type);
-        outlen += strlen(tagname);
-        if (!io_ch(arg, tagname, outlen) || !io_ch(arg, ":", 1))
-            return -1;
-        outlen++;
-    }
-
-    /* Decide what to do with type, either dump content or display it */
-
-    /* Dump everything */
-    if (lflags & ASN1_STRFLGS_DUMP_ALL)
-        type = -1;
-    /* Ignore the string type */
-    else if (lflags & ASN1_STRFLGS_IGNORE_TYPE)
-        type = 1;
-    else {
-        /* Else determine width based on type */
-        if ((type > 0) && (type < 31))
-            type = tag2nbyte[type];
-        else
-            type = -1;
-        if ((type == -1) && !(lflags & ASN1_STRFLGS_DUMP_UNKNOWN))
-            type = 1;
-    }
-
-    if (type == -1) {
-        len = do_dump(lflags, io_ch, arg, str);
-        if (len < 0)
-            return -1;
-        outlen += len;
-        return outlen;
-    }
-
-    if (lflags & ASN1_STRFLGS_UTF8_CONVERT) {
-        /*
-         * Note: if string is UTF8 and we want to convert to UTF8 then we
-         * just interpret it as 1 byte per character to avoid converting
-         * twice.
-         */
-        if (!type)
-            type = 1;
-        else
-            type |= BUF_TYPE_CONVUTF8;
-    }
-
-    len = do_buf(str->data, str->length, type, flags, &quotes, io_ch, NULL);
-    if (len < 0)
-        return -1;
-    outlen += len;
-    if (quotes)
-        outlen += 2;
-    if (!arg)
-        return outlen;
-    if (quotes && !io_ch(arg, "\"", 1))
-        return -1;
-    if (do_buf(str->data, str->length, type, flags, NULL, io_ch, arg) < 0)
-        return -1;
-    if (quotes && !io_ch(arg, "\"", 1))
-        return -1;
-    return outlen;
-}
-
-/* Used for line indenting: print 'indent' spaces */
-
-static int do_indent(char_io *io_ch, void *arg, int indent)
-{
-    int i;
-    for (i = 0; i < indent; i++)
-        if (!io_ch(arg, " ", 1))
-            return 0;
-    return 1;
-}
-
-#define FN_WIDTH_LN     25
-#define FN_WIDTH_SN     10
-
-static int do_name_ex(char_io *io_ch, void *arg, const X509_NAME *n,
-                      int indent, unsigned long flags)
-{
-    int i, prev = -1, orflags, cnt;
-    int fn_opt, fn_nid;
-    ASN1_OBJECT *fn;
-    ASN1_STRING *val;
-    X509_NAME_ENTRY *ent;
-    char objtmp[80];
-    const char *objbuf;
-    int outlen, len;
-    const char *sep_dn, *sep_mv, *sep_eq;
-    int sep_dn_len, sep_mv_len, sep_eq_len;
-    if (indent < 0)
-        indent = 0;
-    outlen = indent;
-    if (!do_indent(io_ch, arg, indent))
-        return -1;
-    switch (flags & XN_FLAG_SEP_MASK) {
-    case XN_FLAG_SEP_MULTILINE:
-        sep_dn = "\n";
-        sep_dn_len = 1;
-        sep_mv = " + ";
-        sep_mv_len = 3;
-        break;
-
-    case XN_FLAG_SEP_COMMA_PLUS:
-        sep_dn = ",";
-        sep_dn_len = 1;
-        sep_mv = "+";
-        sep_mv_len = 1;
-        indent = 0;
-        break;
-
-    case XN_FLAG_SEP_CPLUS_SPC:
-        sep_dn = ", ";
-        sep_dn_len = 2;
-        sep_mv = " + ";
-        sep_mv_len = 3;
-        indent = 0;
-        break;
-
-    case XN_FLAG_SEP_SPLUS_SPC:
-        sep_dn = "; ";
-        sep_dn_len = 2;
-        sep_mv = " + ";
-        sep_mv_len = 3;
-        indent = 0;
-        break;
-
-    default:
-        return -1;
-    }
-
-    if (flags & XN_FLAG_SPC_EQ) {
-        sep_eq = " = ";
-        sep_eq_len = 3;
-    } else {
-        sep_eq = "=";
-        sep_eq_len = 1;
-    }
-
-    fn_opt = flags & XN_FLAG_FN_MASK;
-
-    cnt = X509_NAME_entry_count(n);
-    for (i = 0; i < cnt; i++) {
-        if (flags & XN_FLAG_DN_REV)
-            ent = X509_NAME_get_entry(n, cnt - i - 1);
-        else
-            ent = X509_NAME_get_entry(n, i);
-        if (prev != -1) {
-            if (prev == ent->set) {
-                if (!io_ch(arg, sep_mv, sep_mv_len))
-                    return -1;
-                outlen += sep_mv_len;
-            } else {
-                if (!io_ch(arg, sep_dn, sep_dn_len))
-                    return -1;
-                outlen += sep_dn_len;
-                if (!do_indent(io_ch, arg, indent))
-                    return -1;
-                outlen += indent;
-            }
-        }
-        prev = ent->set;
-        fn = X509_NAME_ENTRY_get_object(ent);
-        val = X509_NAME_ENTRY_get_data(ent);
-        fn_nid = OBJ_obj2nid(fn);
-        if (fn_opt != XN_FLAG_FN_NONE) {
-            int objlen, fld_len;
-            if ((fn_opt == XN_FLAG_FN_OID) || (fn_nid == NID_undef)) {
-                OBJ_obj2txt(objtmp, sizeof objtmp, fn, 1);
-                fld_len = 0;    /* XXX: what should this be? */
-                objbuf = objtmp;
-            } else {
-                if (fn_opt == XN_FLAG_FN_SN) {
-                    fld_len = FN_WIDTH_SN;
-                    objbuf = OBJ_nid2sn(fn_nid);
-                } else if (fn_opt == XN_FLAG_FN_LN) {
-                    fld_len = FN_WIDTH_LN;
-                    objbuf = OBJ_nid2ln(fn_nid);
-                } else {
-                    fld_len = 0; /* XXX: what should this be? */
-                    objbuf = "";
-                }
-            }
-            objlen = strlen(objbuf);
-            if (!io_ch(arg, objbuf, objlen))
-                return -1;
-            if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) {
-                if (!do_indent(io_ch, arg, fld_len - objlen))
-                    return -1;
-                outlen += fld_len - objlen;
-            }
-            if (!io_ch(arg, sep_eq, sep_eq_len))
-                return -1;
-            outlen += objlen + sep_eq_len;
-        }
-        /*
-         * If the field name is unknown then fix up the DER dump flag. We
-         * might want to limit this further so it will DER dump on anything
-         * other than a few 'standard' fields.
-         */
-        if ((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS))
-            orflags = ASN1_STRFLGS_DUMP_ALL;
-        else
-            orflags = 0;
-
-        len = do_print_ex(io_ch, arg, flags | orflags, val);
-        if (len < 0)
-            return -1;
-        outlen += len;
-    }
-    return outlen;
-}
-
-/* Wrappers round the main functions */
-
-int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent,
-                       unsigned long flags)
-{
-    if (flags == XN_FLAG_COMPAT)
-        return X509_NAME_print(out, nm, indent);
-    return do_name_ex(send_bio_chars, out, nm, indent, flags);
-}
-
-#ifndef OPENSSL_NO_FP_API
-int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, int indent,
-                          unsigned long flags)
-{
-    if (flags == XN_FLAG_COMPAT) {
-        BIO *btmp;
-        int ret;
-        btmp = BIO_new_fp(fp, BIO_NOCLOSE);
-        if (!btmp)
-            return -1;
-        ret = X509_NAME_print(btmp, nm, indent);
-        BIO_free(btmp);
-        return ret;
-    }
-    return do_name_ex(send_fp_chars, fp, nm, indent, flags);
-}
-#endif
-
-int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str, unsigned long flags)
-{
-    return do_print_ex(send_bio_chars, out, flags, str);
-}
-
-#ifndef OPENSSL_NO_FP_API
-int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str, unsigned long flags)
-{
-    return do_print_ex(send_fp_chars, fp, flags, str);
-}
-#endif
-
-/*
- * Utility function: convert any string type to UTF8, returns number of bytes
- * in output string or a negative error code
- */
-
-int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in)
-{
-    ASN1_STRING stmp, *str = &stmp;
-    int mbflag, type, ret;
-    if (!in)
-        return -1;
-    type = in->type;
-    if ((type < 0) || (type > 30))
-        return -1;
-    mbflag = tag2nbyte[type];
-    if (mbflag == -1)
-        return -1;
-    mbflag |= MBSTRING_FLAG;
-    stmp.data = NULL;
-    stmp.length = 0;
-    stmp.flags = 0;
-    ret =
-        ASN1_mbstring_copy(&str, in->data, in->length, mbflag,
-                           B_ASN1_UTF8STRING);
-    if (ret < 0)
-        return ret;
-    *out = stmp.data;
-    return stmp.length;
-}
diff --git a/deps/boringssl/src/crypto/x509/a_verify.c b/deps/boringssl/src/crypto/x509/a_verify.c
index 8587b59..ec671c0 100644
--- a/deps/boringssl/src/crypto/x509/a_verify.c
+++ b/deps/boringssl/src/crypto/x509/a_verify.c
@@ -69,23 +69,27 @@
 
 #include "internal.h"
 
-int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *a,
-                     ASN1_BIT_STRING *signature, void *asn, EVP_PKEY *pkey)
-{
-    EVP_MD_CTX ctx;
-    uint8_t *buf_in = NULL;
-    int ret = 0, inl = 0;
-
+int ASN1_item_verify(const ASN1_ITEM *it, const X509_ALGOR *a,
+                     const ASN1_BIT_STRING *signature, void *asn,
+                     EVP_PKEY *pkey) {
     if (!pkey) {
         OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }
 
-    if (signature->type == V_ASN1_BIT_STRING && signature->flags & 0x7) {
-        OPENSSL_PUT_ERROR(X509, X509_R_INVALID_BIT_STRING_BITS_LEFT);
-        return 0;
+    size_t sig_len;
+    if (signature->type == V_ASN1_BIT_STRING) {
+        if (!ASN1_BIT_STRING_num_bytes(signature, &sig_len)) {
+            OPENSSL_PUT_ERROR(X509, X509_R_INVALID_BIT_STRING_BITS_LEFT);
+            return 0;
+        }
+    } else {
+        sig_len = (size_t)ASN1_STRING_length(signature);
     }
 
+    EVP_MD_CTX ctx;
+    uint8_t *buf_in = NULL;
+    int ret = 0, inl = 0;
     EVP_MD_CTX_init(&ctx);
 
     if (!x509_digest_verify_init(&ctx, a, pkey)) {
@@ -99,7 +103,7 @@
         goto err;
     }
 
-    if (!EVP_DigestVerify(&ctx, signature->data, (size_t)signature->length,
+    if (!EVP_DigestVerify(&ctx, ASN1_STRING_get0_data(signature), sig_len,
                           buf_in, inl)) {
         OPENSSL_PUT_ERROR(X509, ERR_R_EVP_LIB);
         goto err;
diff --git a/deps/boringssl/src/crypto/x509/algorithm.c b/deps/boringssl/src/crypto/x509/algorithm.c
index c021dc4..7f90480 100644
--- a/deps/boringssl/src/crypto/x509/algorithm.c
+++ b/deps/boringssl/src/crypto/x509/algorithm.c
@@ -110,7 +110,7 @@
   return 1;
 }
 
-int x509_digest_verify_init(EVP_MD_CTX *ctx, X509_ALGOR *sigalg,
+int x509_digest_verify_init(EVP_MD_CTX *ctx, const X509_ALGOR *sigalg,
                             EVP_PKEY *pkey) {
   /* Convert the signature OID into digest and public key OIDs. */
   int sigalg_nid = OBJ_obj2nid(sigalg->algorithm);
diff --git a/deps/boringssl/src/crypto/x509/by_dir.c b/deps/boringssl/src/crypto/x509/by_dir.c
index 7b91cbd..a630cdf 100644
--- a/deps/boringssl/src/crypto/x509/by_dir.c
+++ b/deps/boringssl/src/crypto/x509/by_dir.c
@@ -68,6 +68,7 @@
 #if !defined(OPENSSL_TRUSTY)
 
 #include "../internal.h"
+#include "internal.h"
 
 typedef struct lookup_dir_hashes_st {
     unsigned long hash;
diff --git a/deps/boringssl/src/crypto/x509/by_file.c b/deps/boringssl/src/crypto/x509/by_file.c
index 994beb9..1614c8c 100644
--- a/deps/boringssl/src/crypto/x509/by_file.c
+++ b/deps/boringssl/src/crypto/x509/by_file.c
@@ -61,6 +61,8 @@
 #include <openssl/pem.h>
 #include <openssl/thread.h>
 
+#include "internal.h"
+
 #ifndef OPENSSL_NO_STDIO
 
 static int by_file_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
diff --git a/deps/boringssl/src/crypto/x509/internal.h b/deps/boringssl/src/crypto/x509/internal.h
index 4957c1e..ac68755 100644
--- a/deps/boringssl/src/crypto/x509/internal.h
+++ b/deps/boringssl/src/crypto/x509/internal.h
@@ -1,16 +1,60 @@
-/* Copyright (c) 2016, Google Inc.
+/*
+ * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project
+ * 2013.
+ */
+/* ====================================================================
+ * Copyright (c) 2013 The OpenSSL Project.  All rights reserved.
  *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
  *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
 
 #ifndef OPENSSL_HEADER_X509_INTERNAL_H
 #define OPENSSL_HEADER_X509_INTERNAL_H
@@ -24,13 +68,302 @@
 #endif
 
 
+/* Internal structures. */
+
+struct X509_val_st {
+  ASN1_TIME *notBefore;
+  ASN1_TIME *notAfter;
+} /* X509_VAL */;
+
+struct X509_pubkey_st {
+  X509_ALGOR *algor;
+  ASN1_BIT_STRING *public_key;
+  EVP_PKEY *pkey;
+} /* X509_PUBKEY */;
+
+struct X509_name_entry_st {
+  ASN1_OBJECT *object;
+  ASN1_STRING *value;
+  int set;
+} /* X509_NAME_ENTRY */;
+
+// we always keep X509_NAMEs in 2 forms.
+struct X509_name_st {
+  STACK_OF(X509_NAME_ENTRY) *entries;
+  int modified;  // true if 'bytes' needs to be built
+  BUF_MEM *bytes;
+  // unsigned long hash; Keep the hash around for lookups
+  unsigned char *canon_enc;
+  int canon_enclen;
+} /* X509_NAME */;
+
+struct x509_attributes_st {
+  ASN1_OBJECT *object;
+  STACK_OF(ASN1_TYPE) *set;
+} /* X509_ATTRIBUTE */;
+
+struct x509_cert_aux_st {
+  STACK_OF(ASN1_OBJECT) *trust;   // trusted uses
+  STACK_OF(ASN1_OBJECT) *reject;  // rejected uses
+  ASN1_UTF8STRING *alias;         // "friendly name"
+  ASN1_OCTET_STRING *keyid;       // key id of private key
+  STACK_OF(X509_ALGOR) *other;    // other unspecified info
+} /* X509_CERT_AUX */;
+
+struct X509_extension_st {
+  ASN1_OBJECT *object;
+  ASN1_BOOLEAN critical;
+  ASN1_OCTET_STRING *value;
+} /* X509_EXTENSION */;
+
+typedef struct {
+  ASN1_INTEGER *version;  // [ 0 ] default of v1
+  ASN1_INTEGER *serialNumber;
+  X509_ALGOR *signature;
+  X509_NAME *issuer;
+  X509_VAL *validity;
+  X509_NAME *subject;
+  X509_PUBKEY *key;
+  ASN1_BIT_STRING *issuerUID;            // [ 1 ] optional in v2
+  ASN1_BIT_STRING *subjectUID;           // [ 2 ] optional in v2
+  STACK_OF(X509_EXTENSION) *extensions;  // [ 3 ] optional in v3
+  ASN1_ENCODING enc;
+} X509_CINF;
+
+DECLARE_ASN1_FUNCTIONS(X509_CINF)
+
+struct x509_st {
+  X509_CINF *cert_info;
+  X509_ALGOR *sig_alg;
+  ASN1_BIT_STRING *signature;
+  CRYPTO_refcount_t references;
+  CRYPTO_EX_DATA ex_data;
+  // These contain copies of various extension values
+  long ex_pathlen;
+  long ex_pcpathlen;
+  unsigned long ex_flags;
+  unsigned long ex_kusage;
+  unsigned long ex_xkusage;
+  unsigned long ex_nscert;
+  ASN1_OCTET_STRING *skid;
+  AUTHORITY_KEYID *akid;
+  X509_POLICY_CACHE *policy_cache;
+  STACK_OF(DIST_POINT) *crldp;
+  STACK_OF(GENERAL_NAME) *altname;
+  NAME_CONSTRAINTS *nc;
+  unsigned char sha1_hash[SHA_DIGEST_LENGTH];
+  X509_CERT_AUX *aux;
+  CRYPTO_BUFFER *buf;
+  CRYPTO_MUTEX lock;
+} /* X509 */;
+
+typedef struct {
+  ASN1_ENCODING enc;
+  ASN1_INTEGER *version;
+  X509_NAME *subject;
+  X509_PUBKEY *pubkey;
+  //  d=2 hl=2 l=  0 cons: cont: 00
+  STACK_OF(X509_ATTRIBUTE) *attributes;  // [ 0 ]
+} X509_REQ_INFO;
+
+DECLARE_ASN1_FUNCTIONS(X509_REQ_INFO)
+
+struct X509_req_st {
+  X509_REQ_INFO *req_info;
+  X509_ALGOR *sig_alg;
+  ASN1_BIT_STRING *signature;
+  CRYPTO_refcount_t references;
+} /* X509_REQ */;
+
+typedef struct {
+  ASN1_INTEGER *version;
+  X509_ALGOR *sig_alg;
+  X509_NAME *issuer;
+  ASN1_TIME *lastUpdate;
+  ASN1_TIME *nextUpdate;
+  STACK_OF(X509_REVOKED) *revoked;
+  STACK_OF(X509_EXTENSION) /* [0] */ *extensions;
+  ASN1_ENCODING enc;
+} X509_CRL_INFO;
+
+DECLARE_ASN1_FUNCTIONS(X509_CRL_INFO)
+
+struct X509_crl_st {
+  // actual signature
+  X509_CRL_INFO *crl;
+  X509_ALGOR *sig_alg;
+  ASN1_BIT_STRING *signature;
+  CRYPTO_refcount_t references;
+  int flags;
+  // Copies of various extensions
+  AUTHORITY_KEYID *akid;
+  ISSUING_DIST_POINT *idp;
+  // Convenient breakdown of IDP
+  int idp_flags;
+  int idp_reasons;
+  // CRL and base CRL numbers for delta processing
+  ASN1_INTEGER *crl_number;
+  ASN1_INTEGER *base_crl_number;
+  unsigned char sha1_hash[SHA_DIGEST_LENGTH];
+  STACK_OF(GENERAL_NAMES) *issuers;
+  const X509_CRL_METHOD *meth;
+  void *meth_data;
+} /* X509_CRL */;
+
+struct X509_VERIFY_PARAM_st {
+  char *name;
+  time_t check_time;                // Time to use
+  unsigned long inh_flags;          // Inheritance flags
+  unsigned long flags;              // Various verify flags
+  int purpose;                      // purpose to check untrusted certificates
+  int trust;                        // trust setting to check
+  int depth;                        // Verify depth
+  STACK_OF(ASN1_OBJECT) *policies;  // Permissible policies
+  // The following fields specify acceptable peer identities.
+  STACK_OF(OPENSSL_STRING) *hosts;  // Set of acceptable names
+  unsigned int hostflags;           // Flags to control matching features
+  char *peername;                   // Matching hostname in peer certificate
+  char *email;                      // If not NULL email address to match
+  size_t emaillen;
+  unsigned char *ip;     // If not NULL IP address to match
+  size_t iplen;          // Length of IP address
+  unsigned char poison;  // Fail all verifications at name checking
+} /* X509_VERIFY_PARAM */;
+
+struct x509_object_st {
+  // one of the above types
+  int type;
+  union {
+    char *ptr;
+    X509 *x509;
+    X509_CRL *crl;
+    EVP_PKEY *pkey;
+  } data;
+} /* X509_OBJECT */;
+
+// This is a static that defines the function interface
+struct x509_lookup_method_st {
+  const char *name;
+  int (*new_item)(X509_LOOKUP *ctx);
+  void (*free)(X509_LOOKUP *ctx);
+  int (*init)(X509_LOOKUP *ctx);
+  int (*shutdown)(X509_LOOKUP *ctx);
+  int (*ctrl)(X509_LOOKUP *ctx, int cmd, const char *argc, long argl,
+              char **ret);
+  int (*get_by_subject)(X509_LOOKUP *ctx, int type, X509_NAME *name,
+                        X509_OBJECT *ret);
+  int (*get_by_issuer_serial)(X509_LOOKUP *ctx, int type, X509_NAME *name,
+                              ASN1_INTEGER *serial, X509_OBJECT *ret);
+  int (*get_by_fingerprint)(X509_LOOKUP *ctx, int type, unsigned char *bytes,
+                            int len, X509_OBJECT *ret);
+  int (*get_by_alias)(X509_LOOKUP *ctx, int type, char *str, int len,
+                      X509_OBJECT *ret);
+} /* X509_LOOKUP_METHOD */;
+
+// This is used to hold everything.  It is used for all certificate
+// validation.  Once we have a certificate chain, the 'verify'
+// function is then called to actually check the cert chain.
+struct x509_store_st {
+  // The following is a cache of trusted certs
+  int cache;                    // if true, stash any hits
+  STACK_OF(X509_OBJECT) *objs;  // Cache of all objects
+  CRYPTO_MUTEX objs_lock;
+  STACK_OF(X509) *additional_untrusted;
+
+  // These are external lookup methods
+  STACK_OF(X509_LOOKUP) *get_cert_methods;
+
+  X509_VERIFY_PARAM *param;
+
+  // Callbacks for various operations
+  X509_STORE_CTX_verify_fn verify;          // called to verify a certificate
+  X509_STORE_CTX_verify_cb verify_cb;       // error callback
+  X509_STORE_CTX_get_issuer_fn get_issuer;  // get issuers cert from ctx
+  X509_STORE_CTX_check_issued_fn check_issued;  // check issued
+  X509_STORE_CTX_check_revocation_fn
+      check_revocation;                   // Check revocation status of chain
+  X509_STORE_CTX_get_crl_fn get_crl;      // retrieve CRL
+  X509_STORE_CTX_check_crl_fn check_crl;  // Check CRL validity
+  X509_STORE_CTX_cert_crl_fn cert_crl;    // Check certificate against CRL
+  X509_STORE_CTX_lookup_certs_fn lookup_certs;
+  X509_STORE_CTX_lookup_crls_fn lookup_crls;
+  X509_STORE_CTX_cleanup_fn cleanup;
+
+  CRYPTO_refcount_t references;
+} /* X509_STORE */;
+
+
+// This is the functions plus an instance of the local variables.
+struct x509_lookup_st {
+  int init;                    // have we been started
+  int skip;                    // don't use us.
+  X509_LOOKUP_METHOD *method;  // the functions
+  char *method_data;           // method data
+
+  X509_STORE *store_ctx;  // who owns us
+} /* X509_LOOKUP */;
+
+// This is a used when verifying cert chains.  Since the
+// gathering of the cert chain can take some time (and have to be
+// 'retried', this needs to be kept and passed around.
+struct x509_store_ctx_st {
+  X509_STORE *ctx;
+
+  // The following are set by the caller
+  X509 *cert;                 // The cert to check
+  STACK_OF(X509) *untrusted;  // chain of X509s - untrusted - passed in
+  STACK_OF(X509_CRL) *crls;   // set of CRLs passed in
+
+  X509_VERIFY_PARAM *param;
+  void *other_ctx;  // Other info for use with get_issuer()
+
+  // Callbacks for various operations
+  X509_STORE_CTX_verify_fn verify;          // called to verify a certificate
+  X509_STORE_CTX_verify_cb verify_cb;       // error callback
+  X509_STORE_CTX_get_issuer_fn get_issuer;  // get issuers cert from ctx
+  X509_STORE_CTX_check_issued_fn check_issued;  // check issued
+  X509_STORE_CTX_check_revocation_fn
+      check_revocation;                   // Check revocation status of chain
+  X509_STORE_CTX_get_crl_fn get_crl;      // retrieve CRL
+  X509_STORE_CTX_check_crl_fn check_crl;  // Check CRL validity
+  X509_STORE_CTX_cert_crl_fn cert_crl;    // Check certificate against CRL
+  X509_STORE_CTX_check_policy_fn check_policy;
+  X509_STORE_CTX_lookup_certs_fn lookup_certs;
+  X509_STORE_CTX_lookup_crls_fn lookup_crls;
+  X509_STORE_CTX_cleanup_fn cleanup;
+
+  // The following is built up
+  int valid;               // if 0, rebuild chain
+  int last_untrusted;      // index of last untrusted cert
+  STACK_OF(X509) *chain;   // chain of X509s - built up and trusted
+  X509_POLICY_TREE *tree;  // Valid policy tree
+
+  int explicit_policy;  // Require explicit policy value
+
+  // When something goes wrong, this is why
+  int error_depth;
+  int error;
+  X509 *current_cert;
+  X509 *current_issuer;   // cert currently being tested as valid issuer
+  X509_CRL *current_crl;  // current CRL
+
+  int current_crl_score;         // score of current CRL
+  unsigned int current_reasons;  // Reason mask
+
+  X509_STORE_CTX *parent;  // For CRL path validation: parent context
+
+  CRYPTO_EX_DATA ex_data;
+} /* X509_STORE_CTX */;
+
+
 /* RSA-PSS functions. */
 
 /* x509_rsa_pss_to_ctx configures |ctx| for an RSA-PSS operation based on
  * signature algorithm parameters in |sigalg| (which must have type
  * |NID_rsassaPss|) and key |pkey|. It returns one on success and zero on
  * error. */
-int x509_rsa_pss_to_ctx(EVP_MD_CTX *ctx, X509_ALGOR *sigalg, EVP_PKEY *pkey);
+int x509_rsa_pss_to_ctx(EVP_MD_CTX *ctx, const X509_ALGOR *sigalg,
+                        EVP_PKEY *pkey);
 
 /* x509_rsa_pss_to_ctx sets |algor| to the signature algorithm parameters for
  * |ctx|, which must have been configured for an RSA-PSS signing operation. It
@@ -55,7 +388,7 @@
  * with public key |pkey| and parameters from |algor|. The |ctx| argument must
  * have been initialised with |EVP_MD_CTX_init|. It returns one on success, or
  * zero on error. */
-int x509_digest_verify_init(EVP_MD_CTX *ctx, X509_ALGOR *sigalg,
+int x509_digest_verify_init(EVP_MD_CTX *ctx, const X509_ALGOR *sigalg,
                             EVP_PKEY *pkey);
 
 
diff --git a/deps/boringssl/src/crypto/x509/name_print.c b/deps/boringssl/src/crypto/x509/name_print.c
new file mode 100644
index 0000000..b5523c0
--- /dev/null
+++ b/deps/boringssl/src/crypto/x509/name_print.c
@@ -0,0 +1,246 @@
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.] */
+
+#include <openssl/x509.h>
+
+#include <inttypes.h>
+#include <string.h>
+
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/obj.h>
+
+
+static int maybe_write(BIO *out, const void *buf, int len)
+{
+    /* If |out| is NULL, ignore the output but report the length. */
+    return out == NULL || BIO_write(out, buf, len) == len;
+}
+
+/* do_indent prints |indent| spaces to |out|. */
+static int do_indent(BIO *out, int indent)
+{
+    for (int i = 0; i < indent; i++) {
+        if (!maybe_write(out, " ", 1)) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
+#define FN_WIDTH_LN     25
+#define FN_WIDTH_SN     10
+
+static int do_name_ex(BIO *out, const X509_NAME *n, int indent,
+                      unsigned long flags)
+{
+    int i, prev = -1, orflags, cnt;
+    int fn_opt, fn_nid;
+    ASN1_OBJECT *fn;
+    ASN1_STRING *val;
+    X509_NAME_ENTRY *ent;
+    char objtmp[80];
+    const char *objbuf;
+    int outlen, len;
+    const char *sep_dn, *sep_mv, *sep_eq;
+    int sep_dn_len, sep_mv_len, sep_eq_len;
+    if (indent < 0)
+        indent = 0;
+    outlen = indent;
+    if (!do_indent(out, indent))
+        return -1;
+    switch (flags & XN_FLAG_SEP_MASK) {
+    case XN_FLAG_SEP_MULTILINE:
+        sep_dn = "\n";
+        sep_dn_len = 1;
+        sep_mv = " + ";
+        sep_mv_len = 3;
+        break;
+
+    case XN_FLAG_SEP_COMMA_PLUS:
+        sep_dn = ",";
+        sep_dn_len = 1;
+        sep_mv = "+";
+        sep_mv_len = 1;
+        indent = 0;
+        break;
+
+    case XN_FLAG_SEP_CPLUS_SPC:
+        sep_dn = ", ";
+        sep_dn_len = 2;
+        sep_mv = " + ";
+        sep_mv_len = 3;
+        indent = 0;
+        break;
+
+    case XN_FLAG_SEP_SPLUS_SPC:
+        sep_dn = "; ";
+        sep_dn_len = 2;
+        sep_mv = " + ";
+        sep_mv_len = 3;
+        indent = 0;
+        break;
+
+    default:
+        return -1;
+    }
+
+    if (flags & XN_FLAG_SPC_EQ) {
+        sep_eq = " = ";
+        sep_eq_len = 3;
+    } else {
+        sep_eq = "=";
+        sep_eq_len = 1;
+    }
+
+    fn_opt = flags & XN_FLAG_FN_MASK;
+
+    cnt = X509_NAME_entry_count(n);
+    for (i = 0; i < cnt; i++) {
+        if (flags & XN_FLAG_DN_REV)
+            ent = X509_NAME_get_entry(n, cnt - i - 1);
+        else
+            ent = X509_NAME_get_entry(n, i);
+        if (prev != -1) {
+            if (prev == X509_NAME_ENTRY_set(ent)) {
+                if (!maybe_write(out, sep_mv, sep_mv_len))
+                    return -1;
+                outlen += sep_mv_len;
+            } else {
+                if (!maybe_write(out, sep_dn, sep_dn_len))
+                    return -1;
+                outlen += sep_dn_len;
+                if (!do_indent(out, indent))
+                    return -1;
+                outlen += indent;
+            }
+        }
+        prev = X509_NAME_ENTRY_set(ent);
+        fn = X509_NAME_ENTRY_get_object(ent);
+        val = X509_NAME_ENTRY_get_data(ent);
+        fn_nid = OBJ_obj2nid(fn);
+        if (fn_opt != XN_FLAG_FN_NONE) {
+            int objlen, fld_len;
+            if ((fn_opt == XN_FLAG_FN_OID) || (fn_nid == NID_undef)) {
+                OBJ_obj2txt(objtmp, sizeof objtmp, fn, 1);
+                fld_len = 0;    /* XXX: what should this be? */
+                objbuf = objtmp;
+            } else {
+                if (fn_opt == XN_FLAG_FN_SN) {
+                    fld_len = FN_WIDTH_SN;
+                    objbuf = OBJ_nid2sn(fn_nid);
+                } else if (fn_opt == XN_FLAG_FN_LN) {
+                    fld_len = FN_WIDTH_LN;
+                    objbuf = OBJ_nid2ln(fn_nid);
+                } else {
+                    fld_len = 0; /* XXX: what should this be? */
+                    objbuf = "";
+                }
+            }
+            objlen = strlen(objbuf);
+            if (!maybe_write(out, objbuf, objlen))
+                return -1;
+            if ((objlen < fld_len) && (flags & XN_FLAG_FN_ALIGN)) {
+                if (!do_indent(out, fld_len - objlen))
+                    return -1;
+                outlen += fld_len - objlen;
+            }
+            if (!maybe_write(out, sep_eq, sep_eq_len))
+                return -1;
+            outlen += objlen + sep_eq_len;
+        }
+        /*
+         * If the field name is unknown then fix up the DER dump flag. We
+         * might want to limit this further so it will DER dump on anything
+         * other than a few 'standard' fields.
+         */
+        if ((fn_nid == NID_undef) && (flags & XN_FLAG_DUMP_UNKNOWN_FIELDS))
+            orflags = ASN1_STRFLGS_DUMP_ALL;
+        else
+            orflags = 0;
+
+        len = ASN1_STRING_print_ex(out, val, flags | orflags);
+        if (len < 0)
+            return -1;
+        outlen += len;
+    }
+    return outlen;
+}
+
+int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent,
+                       unsigned long flags)
+{
+    if (flags == XN_FLAG_COMPAT)
+        return X509_NAME_print(out, nm, indent);
+    return do_name_ex(out, nm, indent, flags);
+}
+
+int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm, int indent,
+                          unsigned long flags)
+{
+    BIO *bio = NULL;
+    if (fp != NULL) {
+        /* If |fp| is NULL, this function returns the number of bytes without
+         * writing. */
+        bio = BIO_new_fp(fp, BIO_NOCLOSE);
+        if (bio == NULL) {
+            return -1;
+        }
+    }
+    int ret = X509_NAME_print_ex(bio, nm, indent, flags);
+    BIO_free(bio);
+    return ret;
+}
diff --git a/deps/boringssl/src/crypto/x509/rsa_pss.c b/deps/boringssl/src/crypto/x509/rsa_pss.c
index 39637b9..1520c08 100644
--- a/deps/boringssl/src/crypto/x509/rsa_pss.c
+++ b/deps/boringssl/src/crypto/x509/rsa_pss.c
@@ -167,7 +167,8 @@
 }
 
 /* convert MGF1 algorithm ID to EVP_MD, default SHA1 */
-static const EVP_MD *rsa_mgf1_to_md(X509_ALGOR *alg, X509_ALGOR *maskHash) {
+static const EVP_MD *rsa_mgf1_to_md(const X509_ALGOR *alg,
+                                    X509_ALGOR *maskHash) {
   const EVP_MD *md;
   if (!alg) {
     return EVP_sha1();
@@ -246,7 +247,8 @@
   return ret;
 }
 
-int x509_rsa_pss_to_ctx(EVP_MD_CTX *ctx, X509_ALGOR *sigalg, EVP_PKEY *pkey) {
+int x509_rsa_pss_to_ctx(EVP_MD_CTX *ctx, const X509_ALGOR *sigalg,
+                        EVP_PKEY *pkey) {
   assert(OBJ_obj2nid(sigalg->algorithm) == NID_rsassaPss);
 
   /* Decode PSS parameters */
diff --git a/deps/boringssl/src/crypto/x509/t_crl.c b/deps/boringssl/src/crypto/x509/t_crl.c
index 14f98c5..d924f85 100644
--- a/deps/boringssl/src/crypto/x509/t_crl.c
+++ b/deps/boringssl/src/crypto/x509/t_crl.c
@@ -61,7 +61,6 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
-#ifndef OPENSSL_NO_FP_API
 int X509_CRL_print_fp(FILE *fp, X509_CRL *x)
 {
     BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
@@ -73,7 +72,6 @@
     BIO_free(b);
     return ret;
 }
-#endif
 
 int X509_CRL_print(BIO *out, X509_CRL *x)
 {
@@ -86,7 +84,13 @@
     BIO_printf(out, "Certificate Revocation List (CRL):\n");
     l = X509_CRL_get_version(x);
     BIO_printf(out, "%8sVersion %lu (0x%lx)\n", "", l + 1, l);
-    X509_signature_print(out, x->sig_alg, NULL);
+    const X509_ALGOR *sig_alg;
+    const ASN1_BIT_STRING *signature;
+    X509_CRL_get0_signature(x, &signature, &sig_alg);
+    // Note this and the other |X509_signature_print| call print the outer
+    // signature algorithm twice, rather than both the inner and outer ones.
+    // This matches OpenSSL, though it was probably a bug.
+    X509_signature_print(out, sig_alg, NULL);
     p = X509_NAME_oneline(X509_CRL_get_issuer(x), NULL, 0);
     BIO_printf(out, "%8sIssuer: %s\n", "", p);
     OPENSSL_free(p);
@@ -99,7 +103,8 @@
         BIO_printf(out, "NONE");
     BIO_printf(out, "\n");
 
-    X509V3_extensions_print(out, "CRL extensions", x->crl->extensions, 0, 8);
+    X509V3_extensions_print(out, "CRL extensions", X509_CRL_get0_extensions(x),
+                            0, 8);
 
     rev = X509_CRL_get_REVOKED(x);
 
@@ -118,7 +123,7 @@
         X509V3_extensions_print(out, "CRL entry extensions",
                                 r->extensions, 0, 8);
     }
-    X509_signature_print(out, x->sig_alg, x->signature);
+    X509_signature_print(out, sig_alg, signature);
 
     return 1;
 
diff --git a/deps/boringssl/src/crypto/x509/t_req.c b/deps/boringssl/src/crypto/x509/t_req.c
index 2fd36f8..8202664 100644
--- a/deps/boringssl/src/crypto/x509/t_req.c
+++ b/deps/boringssl/src/crypto/x509/t_req.c
@@ -63,6 +63,8 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
+#include "internal.h"
+
 
 int X509_REQ_print_fp(FILE *fp, X509_REQ *x) {
   BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);
diff --git a/deps/boringssl/src/crypto/x509/t_x509.c b/deps/boringssl/src/crypto/x509/t_x509.c
index 5db8746..7c32a87 100644
--- a/deps/boringssl/src/crypto/x509/t_x509.c
+++ b/deps/boringssl/src/crypto/x509/t_x509.c
@@ -54,7 +54,6 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
-#include <ctype.h>
 #include <openssl/asn1.h>
 #include <openssl/bio.h>
 #include <openssl/digest.h>
@@ -68,7 +67,6 @@
 #include "internal.h"
 
 
-#ifndef OPENSSL_NO_FP_API
 int X509_print_ex_fp(FILE *fp, X509 *x, unsigned long nmflag,
                      unsigned long cflag)
 {
@@ -86,7 +84,6 @@
 {
     return X509_print_ex_fp(fp, x, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
 }
-#endif
 
 int X509_print(BIO *bp, X509 *x)
 {
@@ -318,182 +315,6 @@
     return 1;
 }
 
-int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v)
-{
-    int i, n;
-    char buf[80];
-    const char *p;
-
-    if (v == NULL)
-        return (0);
-    n = 0;
-    p = (const char *)v->data;
-    for (i = 0; i < v->length; i++) {
-        if ((p[i] > '~') || ((p[i] < ' ') &&
-                             (p[i] != '\n') && (p[i] != '\r')))
-            buf[n] = '.';
-        else
-            buf[n] = p[i];
-        n++;
-        if (n >= 80) {
-            if (BIO_write(bp, buf, n) <= 0)
-                return (0);
-            n = 0;
-        }
-    }
-    if (n > 0)
-        if (BIO_write(bp, buf, n) <= 0)
-            return (0);
-    return (1);
-}
-
-int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
-{
-    if (tm->type == V_ASN1_UTCTIME)
-        return ASN1_UTCTIME_print(bp, tm);
-    if (tm->type == V_ASN1_GENERALIZEDTIME)
-        return ASN1_GENERALIZEDTIME_print(bp, tm);
-    BIO_write(bp, "Bad time value", 14);
-    return (0);
-}
-
-static const char *const mon[12] = {
-    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
-};
-
-int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
-{
-    char *v;
-    int gmt = 0;
-    int i;
-    int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
-    char *f = NULL;
-    int f_len = 0;
-
-    i = tm->length;
-    v = (char *)tm->data;
-
-    if (i < 12)
-        goto err;
-    if (v[i - 1] == 'Z')
-        gmt = 1;
-    for (i = 0; i < 12; i++)
-        if ((v[i] > '9') || (v[i] < '0'))
-            goto err;
-    y = (v[0] - '0') * 1000 + (v[1] - '0') * 100 + (v[2] - '0') * 10 + (v[3] -
-                                                                        '0');
-    M = (v[4] - '0') * 10 + (v[5] - '0');
-    if ((M > 12) || (M < 1))
-        goto err;
-    d = (v[6] - '0') * 10 + (v[7] - '0');
-    h = (v[8] - '0') * 10 + (v[9] - '0');
-    m = (v[10] - '0') * 10 + (v[11] - '0');
-    if (tm->length >= 14 &&
-        (v[12] >= '0') && (v[12] <= '9') &&
-        (v[13] >= '0') && (v[13] <= '9')) {
-        s = (v[12] - '0') * 10 + (v[13] - '0');
-        /* Check for fractions of seconds. */
-        if (tm->length >= 15 && v[14] == '.') {
-            int l = tm->length;
-            f = &v[14];         /* The decimal point. */
-            f_len = 1;
-            while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
-                ++f_len;
-        }
-    }
-
-    if (BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
-                   mon[M - 1], d, h, m, s, f_len, f, y,
-                   (gmt) ? " GMT" : "") <= 0)
-        return (0);
-    else
-        return (1);
- err:
-    BIO_write(bp, "Bad time value", 14);
-    return (0);
-}
-
-// consume_two_digits is a helper function for ASN1_UTCTIME_print. If |*v|,
-// assumed to be |*len| bytes long, has two leading digits, updates |*out| with
-// their value, updates |v| and |len|, and returns one. Otherwise, returns
-// zero.
-static int consume_two_digits(int* out, const char **v, int *len) {
-  if (*len < 2|| !isdigit((*v)[0]) || !isdigit((*v)[1])) {
-    return 0;
-  }
-  *out = ((*v)[0] - '0') * 10 + ((*v)[1] - '0');
-  *len -= 2;
-  *v += 2;
-  return 1;
-}
-
-// consume_zulu_timezone is a helper function for ASN1_UTCTIME_print. If |*v|,
-// assumed to be |*len| bytes long, starts with "Z" then it updates |*v| and
-// |*len| and returns one. Otherwise returns zero.
-static int consume_zulu_timezone(const char **v, int *len) {
-  if (*len == 0 || (*v)[0] != 'Z') {
-    return 0;
-  }
-
-  *len -= 1;
-  *v += 1;
-  return 1;
-}
-
-int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm) {
-  const char *v = (const char *)tm->data;
-  int len = tm->length;
-  int Y = 0, M = 0, D = 0, h = 0, m = 0, s = 0;
-
-  // YYMMDDhhmm are required to be present.
-  if (!consume_two_digits(&Y, &v, &len) ||
-      !consume_two_digits(&M, &v, &len) ||
-      !consume_two_digits(&D, &v, &len) ||
-      !consume_two_digits(&h, &v, &len) ||
-      !consume_two_digits(&m, &v, &len)) {
-    goto err;
-  }
-  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires seconds
-  // to be present, but historically this code has forgiven its absence.
-  consume_two_digits(&s, &v, &len);
-
-  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, specifies this
-  // interpretation of the year.
-  if (Y < 50) {
-    Y += 2000;
-  } else {
-    Y += 1900;
-  }
-  if (M > 12 || M == 0) {
-    goto err;
-  }
-  if (D > 31 || D == 0) {
-    goto err;
-  }
-  if (h > 23 || m > 59 || s > 60) {
-    goto err;
-  }
-
-  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires the "Z"
-  // to be present, but historically this code has forgiven its absence.
-  const int is_gmt = consume_zulu_timezone(&v, &len);
-
-  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, does not permit
-  // the specification of timezones using the +hhmm / -hhmm syntax, which is
-  // the only other thing that might legitimately be found at the end.
-  if (len) {
-    goto err;
-  }
-
-  return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s", mon[M - 1], D, h, m, s, Y,
-                    is_gmt ? " GMT" : "") > 0;
-
-err:
-  BIO_write(bp, "Bad time value", 14);
-  return 0;
-}
-
 int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase)
 {
     char *s, *c, *b;
diff --git a/deps/boringssl/src/crypto/x509/t_x509a.c b/deps/boringssl/src/crypto/x509/t_x509a.c
index 5436828..4c7b212 100644
--- a/deps/boringssl/src/crypto/x509/t_x509a.c
+++ b/deps/boringssl/src/crypto/x509/t_x509a.c
@@ -60,6 +60,9 @@
 #include <openssl/obj.h>
 #include <openssl/x509.h>
 
+#include "internal.h"
+
+
 /* X509_CERT_AUX and string set routines */
 
 int X509_CERT_AUX_print(BIO *out, X509_CERT_AUX *aux, int indent)
@@ -99,8 +102,10 @@
         BIO_puts(out, "\n");
     } else
         BIO_printf(out, "%*sNo Rejected Uses.\n", indent, "");
-    if (aux->alias)
-        BIO_printf(out, "%*sAlias: %s\n", indent, "", aux->alias->data);
+    if (aux->alias) {
+        BIO_printf(out, "%*sAlias: %.*s\n", indent, "", aux->alias->length,
+                   aux->alias->data);
+    }
     if (aux->keyid) {
         BIO_printf(out, "%*sKey Id: ", indent, "");
         for (j = 0; j < aux->keyid->length; j++)
diff --git a/deps/boringssl/src/crypto/x509/vpm_int.h b/deps/boringssl/src/crypto/x509/vpm_int.h
deleted file mode 100644
index 53b4a0d..0000000
--- a/deps/boringssl/src/crypto/x509/vpm_int.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/* vpm_int.h */
-/*
- * Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project
- * 2013.
- */
-/* ====================================================================
- * Copyright (c) 2013 The OpenSSL Project.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- *    software must display the following acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- *    endorse or promote products derived from this software without
- *    prior written permission. For written permission, please contact
- *    licensing@OpenSSL.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- *    nor may "OpenSSL" appear in their names without prior written
- *    permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- *    acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
- *
- * This product includes cryptographic software written by Eric Young
- * (eay@cryptsoft.com).  This product includes software written by Tim
- * Hudson (tjh@cryptsoft.com).
- *
- */
-
-/* internal only structure to hold additional X509_VERIFY_PARAM data */
-
-struct X509_VERIFY_PARAM_ID_st {
-    STACK_OF(OPENSSL_STRING) *hosts; /* Set of acceptable names */
-    unsigned int hostflags;     /* Flags to control matching features */
-    char *peername;             /* Matching hostname in peer certificate */
-    char *email;                /* If not NULL email address to match */
-    size_t emaillen;
-    unsigned char *ip;          /* If not NULL IP address to match */
-    size_t iplen;               /* Length of IP address */
-    unsigned char poison;       /* Fail all verifications */
-};
diff --git a/deps/boringssl/src/crypto/x509/x509_att.c b/deps/boringssl/src/crypto/x509/x509_att.c
index 85d65e7..e2a5121 100644
--- a/deps/boringssl/src/crypto/x509/x509_att.c
+++ b/deps/boringssl/src/crypto/x509/x509_att.c
@@ -62,6 +62,10 @@
 #include <openssl/stack.h>
 #include <openssl/x509.h>
 
+#include "../asn1/internal.h"
+#include "internal.h"
+
+
 int X509at_get_attr_count(const STACK_OF(X509_ATTRIBUTE) *x)
 {
     return sk_X509_ATTRIBUTE_num(x);
@@ -70,12 +74,11 @@
 int X509at_get_attr_by_NID(const STACK_OF(X509_ATTRIBUTE) *x, int nid,
                            int lastpos)
 {
-    const ASN1_OBJECT *obj;
-
-    obj = OBJ_nid2obj(nid);
-    if (obj == NULL)
-        return (-2);
-    return (X509at_get_attr_by_OBJ(x, obj, lastpos));
+    const ASN1_OBJECT *obj = OBJ_nid2obj(nid);
+    if (obj == NULL) {
+        return -1;
+    }
+    return X509at_get_attr_by_OBJ(x, obj, lastpos);
 }
 
 int X509at_get_attr_by_OBJ(const STACK_OF(X509_ATTRIBUTE) *sk,
@@ -197,24 +200,8 @@
     return ret;
 }
 
-void *X509at_get0_data_by_OBJ(STACK_OF(X509_ATTRIBUTE) *x,
-                              ASN1_OBJECT *obj, int lastpos, int type)
-{
-    int i;
-    X509_ATTRIBUTE *at;
-    i = X509at_get_attr_by_OBJ(x, obj, lastpos);
-    if (i == -1)
-        return NULL;
-    if ((lastpos <= -2) && (X509at_get_attr_by_OBJ(x, obj, i) != -1))
-        return NULL;
-    at = X509at_get_attr(x, i);
-    if (lastpos <= -3 && (X509_ATTRIBUTE_count(at) != 1))
-        return NULL;
-    return X509_ATTRIBUTE_get0_data(at, 0, type, NULL);
-}
-
 X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid,
-                                             int atrtype, const void *data,
+                                             int attrtype, const void *data,
                                              int len)
 {
     const ASN1_OBJECT *obj;
@@ -224,12 +211,12 @@
         OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_NID);
         return (NULL);
     }
-    return X509_ATTRIBUTE_create_by_OBJ(attr, obj, atrtype, data, len);
+    return X509_ATTRIBUTE_create_by_OBJ(attr, obj, attrtype, data, len);
 }
 
 X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(X509_ATTRIBUTE **attr,
                                              const ASN1_OBJECT *obj,
-                                             int atrtype, const void *data,
+                                             int attrtype, const void *data,
                                              int len)
 {
     X509_ATTRIBUTE *ret;
@@ -244,7 +231,7 @@
 
     if (!X509_ATTRIBUTE_set1_object(ret, obj))
         goto err;
-    if (!X509_ATTRIBUTE_set1_data(ret, atrtype, data, len))
+    if (!X509_ATTRIBUTE_set1_data(ret, attrtype, data, len))
         goto err;
 
     if ((attr != NULL) && (*attr == NULL))
@@ -257,17 +244,17 @@
 }
 
 X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_txt(X509_ATTRIBUTE **attr,
-                                             const char *atrname, int type,
+                                             const char *attrname, int type,
                                              const unsigned char *bytes,
                                              int len)
 {
     ASN1_OBJECT *obj;
     X509_ATTRIBUTE *nattr;
 
-    obj = OBJ_txt2obj(atrname, 0);
+    obj = OBJ_txt2obj(attrname, 0);
     if (obj == NULL) {
         OPENSSL_PUT_ERROR(X509, X509_R_INVALID_FIELD_NAME);
-        ERR_add_error_data(2, "name=", atrname);
+        ERR_add_error_data(2, "name=", attrname);
         return (NULL);
     }
     nattr = X509_ATTRIBUTE_create_by_OBJ(attr, obj, type, bytes, len);
@@ -307,9 +294,6 @@
             goto err;
         atype = attrtype;
     }
-    if (!(attr->value.set = sk_ASN1_TYPE_new_null()))
-        goto err;
-    attr->single = 0;
     /*
      * This is a bit naughty because the attribute should really have at
      * least one value but some types use and zero length SET and require
@@ -328,7 +312,7 @@
         ASN1_TYPE_set(ttmp, atype, stmp);
         stmp = NULL;
     }
-    if (!sk_ASN1_TYPE_push(attr->value.set, ttmp))
+    if (!sk_ASN1_TYPE_push(attr->set, ttmp))
         goto err;
     return 1;
  err:
@@ -338,13 +322,9 @@
     return 0;
 }
 
-int X509_ATTRIBUTE_count(X509_ATTRIBUTE *attr)
+int X509_ATTRIBUTE_count(const X509_ATTRIBUTE *attr)
 {
-    if (!attr->single)
-        return sk_ASN1_TYPE_num(attr->value.set);
-    if (attr->value.single)
-        return 1;
-    return 0;
+    return sk_ASN1_TYPE_num(attr->set);
 }
 
 ASN1_OBJECT *X509_ATTRIBUTE_get0_object(X509_ATTRIBUTE *attr)
@@ -355,27 +335,24 @@
 }
 
 void *X509_ATTRIBUTE_get0_data(X509_ATTRIBUTE *attr, int idx,
-                               int atrtype, void *data)
+                               int attrtype, void *unused)
 {
     ASN1_TYPE *ttmp;
     ttmp = X509_ATTRIBUTE_get0_type(attr, idx);
     if (!ttmp)
         return NULL;
-    if (atrtype != ASN1_TYPE_get(ttmp)) {
+    if (attrtype != ASN1_TYPE_get(ttmp)) {
         OPENSSL_PUT_ERROR(X509, X509_R_WRONG_TYPE);
         return NULL;
     }
-    return ttmp->value.ptr;
+    return (void *)asn1_type_value_as_pointer(ttmp);
 }
 
 ASN1_TYPE *X509_ATTRIBUTE_get0_type(X509_ATTRIBUTE *attr, int idx)
 {
     if (attr == NULL)
-        return (NULL);
+        return NULL;
     if (idx >= X509_ATTRIBUTE_count(attr))
         return NULL;
-    if (!attr->single)
-        return sk_ASN1_TYPE_value(attr->value.set, idx);
-    else
-        return attr->value.single;
+    return sk_ASN1_TYPE_value(attr->set, idx);
 }
diff --git a/deps/boringssl/src/crypto/x509/x509_cmp.c b/deps/boringssl/src/crypto/x509/x509_cmp.c
index cf0a941..5811f44 100644
--- a/deps/boringssl/src/crypto/x509/x509_cmp.c
+++ b/deps/boringssl/src/crypto/x509/x509_cmp.c
@@ -68,6 +68,7 @@
 
 #include "../internal.h"
 #include "../x509v3/internal.h"
+#include "internal.h"
 
 
 int X509_issuer_and_serial_cmp(const X509 *a, const X509 *b)
@@ -83,34 +84,6 @@
     return (X509_NAME_cmp(ai->issuer, bi->issuer));
 }
 
-unsigned long X509_issuer_and_serial_hash(X509 *a)
-{
-    unsigned long ret = 0;
-    EVP_MD_CTX ctx;
-    unsigned char md[16];
-    char *f;
-
-    EVP_MD_CTX_init(&ctx);
-    f = X509_NAME_oneline(a->cert_info->issuer, NULL, 0);
-    if (!EVP_DigestInit_ex(&ctx, EVP_md5(), NULL))
-        goto err;
-    if (!EVP_DigestUpdate(&ctx, (unsigned char *)f, strlen(f)))
-        goto err;
-    OPENSSL_free(f);
-    if (!EVP_DigestUpdate
-        (&ctx, (unsigned char *)a->cert_info->serialNumber->data,
-         (unsigned long)a->cert_info->serialNumber->length))
-        goto err;
-    if (!EVP_DigestFinal_ex(&ctx, &(md[0]), NULL))
-        goto err;
-    ret = (((unsigned long)md[0]) | ((unsigned long)md[1] << 8L) |
-           ((unsigned long)md[2] << 16L) | ((unsigned long)md[3] << 24L)
-        ) & 0xffffffffL;
- err:
-    EVP_MD_CTX_cleanup(&ctx);
-    return (ret);
-}
-
 int X509_issuer_name_cmp(const X509 *a, const X509 *b)
 {
     return (X509_NAME_cmp(a->cert_info->issuer, b->cert_info->issuer));
@@ -411,7 +384,7 @@
     } else
         i = 0;
 
-    if (X509_get_version(x) != 2) {
+    if (X509_get_version(x) != X509_VERSION_3) {
         rv = X509_V_ERR_SUITE_B_INVALID_VERSION;
         /* Correct error depth */
         i = 0;
@@ -429,7 +402,7 @@
     for (; i < sk_X509_num(chain); i++) {
         sign_nid = X509_get_signature_nid(x);
         x = sk_X509_value(chain, i);
-        if (X509_get_version(x) != 2) {
+        if (X509_get_version(x) != X509_VERSION_3) {
             rv = X509_V_ERR_SUITE_B_INVALID_VERSION;
             goto end;
         }
diff --git a/deps/boringssl/src/crypto/x509/x509_ext.c b/deps/boringssl/src/crypto/x509/x509_ext.c
index f6da54a..a08e2a8 100644
--- a/deps/boringssl/src/crypto/x509/x509_ext.c
+++ b/deps/boringssl/src/crypto/x509/x509_ext.c
@@ -62,6 +62,8 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
+#include "internal.h"
+
 int X509_CRL_get_ext_count(const X509_CRL *x)
 {
     return (X509v3_get_ext_count(x->crl->extensions));
@@ -208,5 +210,3 @@
 {
     return X509V3_add1_i2d(&x->extensions, nid, value, crit, flags);
 }
-
-IMPLEMENT_ASN1_SET_OF(X509_EXTENSION)
diff --git a/deps/boringssl/src/crypto/x509/x509_lu.c b/deps/boringssl/src/crypto/x509/x509_lu.c
index 4046c3e..6d51ffd 100644
--- a/deps/boringssl/src/crypto/x509/x509_lu.c
+++ b/deps/boringssl/src/crypto/x509/x509_lu.c
@@ -64,6 +64,7 @@
 #include <openssl/x509v3.h>
 
 #include "../internal.h"
+#include "internal.h"
 
 X509_LOOKUP *X509_LOOKUP_new(X509_LOOKUP_METHOD *method)
 {
diff --git a/deps/boringssl/src/crypto/x509/x509_obj.c b/deps/boringssl/src/crypto/x509/x509_obj.c
index 80d16c1..df54f77 100644
--- a/deps/boringssl/src/crypto/x509/x509_obj.c
+++ b/deps/boringssl/src/crypto/x509/x509_obj.c
@@ -64,6 +64,7 @@
 #include <openssl/x509.h>
 
 #include "../internal.h"
+#include "internal.h"
 
 
 /*
diff --git a/deps/boringssl/src/crypto/x509/x509_req.c b/deps/boringssl/src/crypto/x509/x509_req.c
index 9ab6e9d..99eabfe 100644
--- a/deps/boringssl/src/crypto/x509/x509_req.c
+++ b/deps/boringssl/src/crypto/x509/x509_req.c
@@ -65,6 +65,9 @@
 #include <openssl/pem.h>
 #include <openssl/x509.h>
 
+#include "internal.h"
+
+
 X509_REQ *X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey, const EVP_MD *md)
 {
     X509_REQ *ret;
@@ -157,62 +160,31 @@
     return (ok);
 }
 
-/*
- * It seems several organisations had the same idea of including a list of
- * extensions in a certificate request. There are at least two OIDs that are
- * used and there may be more: so the list is configurable.
- */
-
-static const int ext_nid_list[] = { NID_ext_req, NID_ms_ext_req, NID_undef };
-
-static const int *ext_nids = ext_nid_list;
-
 int X509_REQ_extension_nid(int req_nid)
 {
-    int i, nid;
-    for (i = 0;; i++) {
-        nid = ext_nids[i];
-        if (nid == NID_undef)
-            return 0;
-        else if (req_nid == nid)
-            return 1;
-    }
-}
-
-const int *X509_REQ_get_extension_nids(void)
-{
-    return ext_nids;
-}
-
-void X509_REQ_set_extension_nids(const int *nids)
-{
-    ext_nids = nids;
+    return req_nid == NID_ext_req || req_nid == NID_ms_ext_req;
 }
 
 STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req)
 {
-    X509_ATTRIBUTE *attr;
-    ASN1_TYPE *ext = NULL;
-    int idx;
-    const int *pnid;
-    const unsigned char *p;
-
-    if ((req == NULL) || (req->req_info == NULL) || !ext_nids)
-        return (NULL);
-    for (pnid = ext_nids; *pnid != NID_undef; pnid++) {
-        idx = X509_REQ_get_attr_by_NID(req, *pnid, -1);
-        if (idx == -1)
-            continue;
-        attr = X509_REQ_get_attr(req, idx);
-        if (attr->single)
-            ext = attr->value.single;
-        else if (sk_ASN1_TYPE_num(attr->value.set))
-            ext = sk_ASN1_TYPE_value(attr->value.set, 0);
-        break;
-    }
-    if (!ext || (ext->type != V_ASN1_SEQUENCE))
+    if (req == NULL || req->req_info == NULL) {
         return NULL;
-    p = ext->value.sequence->data;
+    }
+
+    int idx = X509_REQ_get_attr_by_NID(req, NID_ext_req, -1);
+    if (idx == -1) {
+        idx = X509_REQ_get_attr_by_NID(req, NID_ms_ext_req, -1);
+    }
+    if (idx == -1) {
+        return NULL;
+    }
+
+    X509_ATTRIBUTE *attr = X509_REQ_get_attr(req, idx);
+    ASN1_TYPE *ext = X509_ATTRIBUTE_get0_type(attr, 0);
+    if (!ext || ext->type != V_ASN1_SEQUENCE) {
+        return NULL;
+    }
+    const unsigned char *p = ext->value.sequence->data;
     return (STACK_OF(X509_EXTENSION) *)
         ASN1_item_d2i(NULL, &p, ext->value.sequence->length,
                       ASN1_ITEM_rptr(X509_EXTENSIONS));
@@ -223,44 +195,25 @@
  * in case we want to create a non standard one.
  */
 
-int X509_REQ_add_extensions_nid(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts,
-                                int nid)
+int X509_REQ_add_extensions_nid(X509_REQ *req,
+                                const STACK_OF(X509_EXTENSION) *exts, int nid)
 {
-    ASN1_TYPE *at = NULL;
-    X509_ATTRIBUTE *attr = NULL;
-    if (!(at = ASN1_TYPE_new()) || !(at->value.sequence = ASN1_STRING_new()))
-        goto err;
-
-    at->type = V_ASN1_SEQUENCE;
     /* Generate encoding of extensions */
-    at->value.sequence->length =
-        ASN1_item_i2d((ASN1_VALUE *)exts,
-                      &at->value.sequence->data,
-                      ASN1_ITEM_rptr(X509_EXTENSIONS));
-    if (!(attr = X509_ATTRIBUTE_new()))
-        goto err;
-    if (!(attr->value.set = sk_ASN1_TYPE_new_null()))
-        goto err;
-    if (!sk_ASN1_TYPE_push(attr->value.set, at))
-        goto err;
-    at = NULL;
-    attr->single = 0;
-    attr->object = (ASN1_OBJECT *)OBJ_nid2obj(nid);
-    if (!req->req_info->attributes) {
-        if (!(req->req_info->attributes = sk_X509_ATTRIBUTE_new_null()))
-            goto err;
+    unsigned char *ext = NULL;
+    int ext_len = ASN1_item_i2d((ASN1_VALUE *)exts, &ext,
+                                ASN1_ITEM_rptr(X509_EXTENSIONS));
+    if (ext_len <= 0) {
+        return 0;
     }
-    if (!sk_X509_ATTRIBUTE_push(req->req_info->attributes, attr))
-        goto err;
-    return 1;
- err:
-    X509_ATTRIBUTE_free(attr);
-    ASN1_TYPE_free(at);
-    return 0;
+    int ret = X509_REQ_add1_attr_by_NID(req, nid, V_ASN1_SEQUENCE, ext,
+                                        ext_len);
+    OPENSSL_free(ext);
+    return ret;
 }
 
 /* This is the normal usage: use the "official" OID */
-int X509_REQ_add_extensions(X509_REQ *req, STACK_OF(X509_EXTENSION) *exts)
+int X509_REQ_add_extensions(X509_REQ *req,
+                            const STACK_OF(X509_EXTENSION) *exts)
 {
     return X509_REQ_add_extensions_nid(req, exts, NID_ext_req);
 }
@@ -277,7 +230,7 @@
     return X509at_get_attr_by_NID(req->req_info->attributes, nid, lastpos);
 }
 
-int X509_REQ_get_attr_by_OBJ(const X509_REQ *req, ASN1_OBJECT *obj,
+int X509_REQ_get_attr_by_OBJ(const X509_REQ *req, const ASN1_OBJECT *obj,
                              int lastpos)
 {
     return X509at_get_attr_by_OBJ(req->req_info->attributes, obj, lastpos);
@@ -301,31 +254,31 @@
 }
 
 int X509_REQ_add1_attr_by_OBJ(X509_REQ *req,
-                              const ASN1_OBJECT *obj, int type,
-                              const unsigned char *bytes, int len)
+                              const ASN1_OBJECT *obj, int attrtype,
+                              const unsigned char *data, int len)
 {
     if (X509at_add1_attr_by_OBJ(&req->req_info->attributes, obj,
-                                type, bytes, len))
+                                attrtype, data, len))
         return 1;
     return 0;
 }
 
 int X509_REQ_add1_attr_by_NID(X509_REQ *req,
-                              int nid, int type,
-                              const unsigned char *bytes, int len)
+                              int nid, int attrtype,
+                              const unsigned char *data, int len)
 {
     if (X509at_add1_attr_by_NID(&req->req_info->attributes, nid,
-                                type, bytes, len))
+                                attrtype, data, len))
         return 1;
     return 0;
 }
 
 int X509_REQ_add1_attr_by_txt(X509_REQ *req,
-                              const char *attrname, int type,
-                              const unsigned char *bytes, int len)
+                              const char *attrname, int attrtype,
+                              const unsigned char *data, int len)
 {
     if (X509at_add1_attr_by_txt(&req->req_info->attributes, attrname,
-                                type, bytes, len))
+                                attrtype, data, len))
         return 1;
     return 0;
 }
diff --git a/deps/boringssl/src/crypto/x509/x509_set.c b/deps/boringssl/src/crypto/x509/x509_set.c
index 5f17851..93bb6a3 100644
--- a/deps/boringssl/src/crypto/x509/x509_set.c
+++ b/deps/boringssl/src/crypto/x509/x509_set.c
@@ -60,18 +60,21 @@
 #include <openssl/obj.h>
 #include <openssl/x509.h>
 
+#include "internal.h"
+
+
 long X509_get_version(const X509 *x509)
 {
+    // The default version is v1(0).
+    if (x509->cert_info->version == NULL) {
+        return X509_VERSION_1;
+    }
     return ASN1_INTEGER_get(x509->cert_info->version);
 }
 
-X509_CINF *X509_get_cert_info(const X509 *x509)
-{
-    return x509->cert_info;
-}
-
 int X509_set_version(X509 *x, long version)
 {
+    // TODO(davidben): Reject invalid version numbers.
     if (x == NULL)
         return (0);
     if (version == 0) {
@@ -86,7 +89,7 @@
     return (ASN1_INTEGER_set(x->cert_info->version, version));
 }
 
-int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial)
+int X509_set_serialNumber(X509 *x, const ASN1_INTEGER *serial)
 {
     ASN1_INTEGER *in;
 
@@ -231,16 +234,6 @@
     return x->cert_info->signature;
 }
 
-void X509_CINF_set_modified(X509_CINF *cinf)
-{
-    cinf->enc.modified = 1;
-}
-
-const X509_ALGOR *X509_CINF_get_signature(const X509_CINF *cinf)
-{
-    return cinf->signature;
-}
-
 X509_PUBKEY *X509_get_X509_PUBKEY(const X509 *x509)
 {
     return x509->cert_info->key;
diff --git a/deps/boringssl/src/crypto/x509/x509_test.cc b/deps/boringssl/src/crypto/x509/x509_test.cc
index 0debb8a..848bd07 100644
--- a/deps/boringssl/src/crypto/x509/x509_test.cc
+++ b/deps/boringssl/src/crypto/x509/x509_test.cc
@@ -19,17 +19,20 @@
 
 #include <gtest/gtest.h>
 
+#include <openssl/asn1.h>
 #include <openssl/bio.h>
 #include <openssl/bytestring.h>
 #include <openssl/crypto.h>
 #include <openssl/curve25519.h>
 #include <openssl/digest.h>
 #include <openssl/err.h>
+#include <openssl/nid.h>
 #include <openssl/pem.h>
 #include <openssl/pool.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
+#include "internal.h"
 #include "../internal.h"
 #include "../test/test_util.h"
 #include "../x509v3/internal.h"
@@ -441,6 +444,40 @@
 -----END X509 CRL-----
 )";
 
+// kAlgorithmMismatchCRL is kBasicCRL but with mismatched AlgorithmIdentifiers
+// in the outer structure and signed portion. The signature reflects the signed
+// portion.
+static const char kAlgorithmMismatchCRL[] = R"(
+-----BEGIN X509 CRL-----
+MIIBpzCBkAIBATANBgkqhkiG9w0BAQsFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ
+Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNV
+HRQEAwIBATANBgkqhkiG9w0BAQwFAAOCAQEAnrBKKgvd9x9zwK9rtUvVeFeJ7+LN
+ZEAc+a5oxpPNEsJx6hXoApYEbzXMxuWBQoCs5iEBycSGudct21L+MVf27M38KrWo
+eOkq0a2siqViQZO2Fb/SUFR0k9zb8xl86Zf65lgPplALun0bV/HT7MJcl04Tc4os
+dsAReBs5nqTGNEd5AlC1iKHvQZkM//MD51DspKnDpsDiUVi54h9C1SpfZmX8H2Vv
+diyu0fZ/bPAM3VAGawatf/SyWfBMyKpoPXEG39oAzmjjOj8en82psn7m474IGaho
+/vBbhl1ms5qQiLYPjm4YELtnXQoFyC72tBjbdFd/ZE9k4CNKDbxFUXFbkw==
+-----END X509 CRL-----
+)";
+
+// kAlgorithmMismatchCRL2 is kBasicCRL but with mismatched AlgorithmIdentifiers
+// in the outer structure and signed portion. The signature reflects the outer
+// structure.
+static const char kAlgorithmMismatchCRL2[] = R"(
+-----BEGIN X509 CRL-----
+MIIBpzCBkAIBATANBgkqhkiG9w0BAQwFADBOMQswCQYDVQQGEwJVUzETMBEGA1UE
+CAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzESMBAGA1UECgwJ
+Qm9yaW5nU1NMFw0xNjA5MjYxNTEwNTVaFw0xNjEwMjYxNTEwNTVaoA4wDDAKBgNV
+HRQEAwIBATANBgkqhkiG9w0BAQsFAAOCAQEAjCWtU7AK8nQ5TCFfzvbU04MWNuLp
+iZfqapRSRyMta4pyRomK773rEmJmYOc/ZNeIphVOlupMgGC2wyv5Z/SD1mxccJbv
+SlUWciwjskjgvyyU9KnJ5xPgf3e3Fl3G0u9yJEFd4mg6fRavs5pEDX56b0f+SkG+
+Vl1FZU94Uylm2kCqk9fRpTxualPGP6dksj3Aitt4x2Vdni4sUfg9vIEEOx2jnisq
+iLqpT94IdETCWAciE0dgbogdOOsNzMqSASfHM/XPigYLXpYgfaR8fca6OKDwFsVH
+SrkFz8Se3F6mCHnbDzYElbmA46iKU2J12LTrso3Ewq/qHq0mebfp2z0y6g==
+-----END X509 CRL-----
+)";
+
 // kEd25519Cert is a self-signed Ed25519 certificate.
 static const char kEd25519Cert[] = R"(
 -----BEGIN CERTIFICATE-----
@@ -1069,6 +1106,8 @@
   return stack;
 }
 
+static const time_t kReferenceTime = 1474934400 /* Sep 27th, 2016 */;
+
 static int Verify(X509 *leaf, const std::vector<X509 *> &roots,
                   const std::vector<X509 *> &intermediates,
                   const std::vector<X509_CRL *> &crls, unsigned long flags,
@@ -1111,7 +1150,7 @@
   if (param == nullptr) {
     return X509_V_ERR_UNSPECIFIED;
   }
-  X509_VERIFY_PARAM_set_time(param, 1474934400 /* Sep 27th, 2016 */);
+  X509_VERIFY_PARAM_set_time(param, kReferenceTime);
   X509_VERIFY_PARAM_set_depth(param, 16);
   if (configure_callback) {
     configure_callback(param);
@@ -1363,6 +1402,10 @@
       CRLFromPEM(kUnknownCriticalCRL));
   bssl::UniquePtr<X509_CRL> unknown_critical_crl2(
       CRLFromPEM(kUnknownCriticalCRL2));
+  bssl::UniquePtr<X509_CRL> algorithm_mismatch_crl(
+      CRLFromPEM(kAlgorithmMismatchCRL));
+  bssl::UniquePtr<X509_CRL> algorithm_mismatch_crl2(
+      CRLFromPEM(kAlgorithmMismatchCRL2));
 
   ASSERT_TRUE(root);
   ASSERT_TRUE(leaf);
@@ -1372,30 +1415,38 @@
   ASSERT_TRUE(known_critical_crl);
   ASSERT_TRUE(unknown_critical_crl);
   ASSERT_TRUE(unknown_critical_crl2);
+  ASSERT_TRUE(algorithm_mismatch_crl);
+  ASSERT_TRUE(algorithm_mismatch_crl2);
 
-  ASSERT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()},
+  EXPECT_EQ(X509_V_OK, Verify(leaf.get(), {root.get()}, {root.get()},
                               {basic_crl.get()}, X509_V_FLAG_CRL_CHECK));
-  ASSERT_EQ(
+  EXPECT_EQ(
       X509_V_ERR_CERT_REVOKED,
       Verify(leaf.get(), {root.get()}, {root.get()},
              {basic_crl.get(), revoked_crl.get()}, X509_V_FLAG_CRL_CHECK));
 
   std::vector<X509_CRL *> empty_crls;
-  ASSERT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL,
+  EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL,
             Verify(leaf.get(), {root.get()}, {root.get()}, empty_crls,
                    X509_V_FLAG_CRL_CHECK));
-  ASSERT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL,
+  EXPECT_EQ(X509_V_ERR_UNABLE_TO_GET_CRL,
             Verify(leaf.get(), {root.get()}, {root.get()},
                    {bad_issuer_crl.get()}, X509_V_FLAG_CRL_CHECK));
-  ASSERT_EQ(X509_V_OK,
+  EXPECT_EQ(X509_V_OK,
             Verify(leaf.get(), {root.get()}, {root.get()},
                    {known_critical_crl.get()}, X509_V_FLAG_CRL_CHECK));
-  ASSERT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION,
+  EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION,
             Verify(leaf.get(), {root.get()}, {root.get()},
                    {unknown_critical_crl.get()}, X509_V_FLAG_CRL_CHECK));
-  ASSERT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION,
+  EXPECT_EQ(X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION,
             Verify(leaf.get(), {root.get()}, {root.get()},
                    {unknown_critical_crl2.get()}, X509_V_FLAG_CRL_CHECK));
+  EXPECT_EQ(X509_V_ERR_CRL_SIGNATURE_FAILURE,
+            Verify(leaf.get(), {root.get()}, {root.get()},
+                   {algorithm_mismatch_crl.get()}, X509_V_FLAG_CRL_CHECK));
+  EXPECT_EQ(X509_V_ERR_CRL_SIGNATURE_FAILURE,
+            Verify(leaf.get(), {root.get()}, {root.get()},
+                   {algorithm_mismatch_crl2.get()}, X509_V_FLAG_CRL_CHECK));
 
   // Parsing kBadExtensionCRL should fail.
   EXPECT_FALSE(CRLFromPEM(kBadExtensionCRL));
@@ -1442,6 +1493,211 @@
                               {many_constraints.get()}, {}));
 }
 
+static bssl::UniquePtr<GENERAL_NAME> MakeGeneralName(int type,
+                                                     const std::string &value) {
+  if (type != GEN_EMAIL && type != GEN_DNS && type != GEN_URI) {
+    // This function only supports the IA5String types.
+    return nullptr;
+  }
+  bssl::UniquePtr<ASN1_IA5STRING> str(ASN1_IA5STRING_new());
+  bssl::UniquePtr<GENERAL_NAME> name(GENERAL_NAME_new());
+  if (!str || !name ||
+      !ASN1_STRING_set(str.get(), value.data(), value.size())) {
+    return nullptr;
+  }
+
+  name->type = type;
+  name->d.ia5 = str.release();
+  return name;
+}
+
+static bssl::UniquePtr<X509> MakeTestCert(const char *issuer,
+                                          const char *subject, EVP_PKEY *key) {
+  bssl::UniquePtr<X509> cert(X509_new());
+  if (!cert ||  //
+      !X509_set_version(cert.get(), X509_VERSION_3) ||
+      !X509_NAME_add_entry_by_txt(
+          X509_get_issuer_name(cert.get()), "CN", MBSTRING_UTF8,
+          reinterpret_cast<const uint8_t *>(issuer), -1, -1, 0) ||
+      !X509_NAME_add_entry_by_txt(
+          X509_get_subject_name(cert.get()), "CN", MBSTRING_UTF8,
+          reinterpret_cast<const uint8_t *>(subject), -1, -1, 0) ||
+      !X509_set_pubkey(cert.get(), key) ||
+      !ASN1_TIME_adj(X509_getm_notBefore(cert.get()), kReferenceTime, -1, 0) ||
+      !ASN1_TIME_adj(X509_getm_notAfter(cert.get()), kReferenceTime, 1, 0)) {
+    return nullptr;
+  }
+  return cert;
+}
+
+TEST(X509Test, NameConstraints) {
+  bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key);
+  ASSERT_TRUE(key);
+
+  const struct {
+    int type;
+    std::string name;
+    std::string constraint;
+    int result;
+  } kTests[] = {
+      // Empty string matches everything.
+      {GEN_DNS, "foo.example.com", "", X509_V_OK},
+      // Name constraints match the entire subtree.
+      {GEN_DNS, "foo.example.com", "example.com", X509_V_OK},
+      {GEN_DNS, "foo.example.com", "EXAMPLE.COM", X509_V_OK},
+      {GEN_DNS, "foo.example.com", "xample.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_DNS, "foo.example.com", "unrelated.much.longer.name.example",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      // A leading dot means at least one component must be added.
+      {GEN_DNS, "foo.example.com", ".example.com", X509_V_OK},
+      {GEN_DNS, "foo.example.com", "foo.example.com", X509_V_OK},
+      {GEN_DNS, "foo.example.com", ".foo.example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_DNS, "foo.example.com", ".xample.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_DNS, "foo.example.com", ".unrelated.much.longer.name.example",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      // NUL bytes, if not rejected, should not confuse the matching logic.
+      {GEN_DNS, std::string({'a', '\0', 'a'}), std::string({'a', '\0', 'b'}),
+       X509_V_ERR_PERMITTED_VIOLATION},
+
+      // Names must be emails.
+      {GEN_EMAIL, "not-an-email.example", "not-an-email.example",
+       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
+      // A leading dot matches all local names and all subdomains
+      {GEN_EMAIL, "foo@bar.example.com", ".example.com", X509_V_OK},
+      {GEN_EMAIL, "foo@bar.example.com", ".EXAMPLE.COM", X509_V_OK},
+      {GEN_EMAIL, "foo@bar.example.com", ".bar.example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      // Without a leading dot, the host must match exactly.
+      {GEN_EMAIL, "foo@example.com", "example.com", X509_V_OK},
+      {GEN_EMAIL, "foo@example.com", "EXAMPLE.COM", X509_V_OK},
+      {GEN_EMAIL, "foo@bar.example.com", "example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      // If the constraint specifies a mailbox, it specifies the whole thing.
+      // The halves are compared insensitively.
+      {GEN_EMAIL, "foo@example.com", "foo@example.com", X509_V_OK},
+      {GEN_EMAIL, "foo@example.com", "foo@EXAMPLE.COM", X509_V_OK},
+      {GEN_EMAIL, "foo@example.com", "FOO@example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_EMAIL, "foo@example.com", "bar@example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      // OpenSSL ignores a stray leading @.
+      {GEN_EMAIL, "foo@example.com", "@example.com", X509_V_OK},
+      {GEN_EMAIL, "foo@example.com", "@EXAMPLE.COM", X509_V_OK},
+      {GEN_EMAIL, "foo@bar.example.com", "@example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+
+      // Basic syntax check.
+      {GEN_URI, "not-a-url", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
+      {GEN_URI, "foo:not-a-url", "not-a-url",
+       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
+      {GEN_URI, "foo:/not-a-url", "not-a-url",
+       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
+      {GEN_URI, "foo:///not-a-url", "not-a-url",
+       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
+      {GEN_URI, "foo://:not-a-url", "not-a-url",
+       X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
+      {GEN_URI, "foo://", "not-a-url", X509_V_ERR_UNSUPPORTED_NAME_SYNTAX},
+      // Hosts are an exact match.
+      {GEN_URI, "foo://example.com", "example.com", X509_V_OK},
+      {GEN_URI, "foo://example.com:443", "example.com", X509_V_OK},
+      {GEN_URI, "foo://example.com/whatever", "example.com", X509_V_OK},
+      {GEN_URI, "foo://bar.example.com", "example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://bar.example.com:443", "example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://bar.example.com/whatever", "example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://bar.example.com", "xample.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://bar.example.com:443", "xample.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://bar.example.com/whatever", "xample.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com", "some-other-name.example",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com:443", "some-other-name.example",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com/whatever", "some-other-name.example",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      // A leading dot allows components to be added.
+      {GEN_URI, "foo://example.com", ".example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com:443", ".example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com/whatever", ".example.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://bar.example.com", ".example.com", X509_V_OK},
+      {GEN_URI, "foo://bar.example.com:443", ".example.com", X509_V_OK},
+      {GEN_URI, "foo://bar.example.com/whatever", ".example.com", X509_V_OK},
+      {GEN_URI, "foo://example.com", ".some-other-name.example",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com:443", ".some-other-name.example",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com/whatever", ".some-other-name.example",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com", ".xample.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com:443", ".xample.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+      {GEN_URI, "foo://example.com/whatever", ".xample.com",
+       X509_V_ERR_PERMITTED_VIOLATION},
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(t.type);
+    SCOPED_TRACE(t.name);
+    SCOPED_TRACE(t.constraint);
+
+    bssl::UniquePtr<GENERAL_NAME> name = MakeGeneralName(t.type, t.name);
+    ASSERT_TRUE(name);
+    bssl::UniquePtr<GENERAL_NAMES> names(GENERAL_NAMES_new());
+    ASSERT_TRUE(names);
+    ASSERT_TRUE(bssl::PushToStack(names.get(), std::move(name)));
+
+    bssl::UniquePtr<NAME_CONSTRAINTS> nc(NAME_CONSTRAINTS_new());
+    ASSERT_TRUE(nc);
+    nc->permittedSubtrees = sk_GENERAL_SUBTREE_new_null();
+    ASSERT_TRUE(nc->permittedSubtrees);
+    bssl::UniquePtr<GENERAL_SUBTREE> subtree(GENERAL_SUBTREE_new());
+    ASSERT_TRUE(subtree);
+    GENERAL_NAME_free(subtree->base);
+    subtree->base = MakeGeneralName(t.type, t.constraint).release();
+    ASSERT_TRUE(subtree->base);
+    ASSERT_TRUE(bssl::PushToStack(nc->permittedSubtrees, std::move(subtree)));
+
+    bssl::UniquePtr<X509> root = MakeTestCert("Root", "Root", key.get());
+    ASSERT_TRUE(root);
+    ASSERT_TRUE(X509_add1_ext_i2d(root.get(), NID_name_constraints, nc.get(),
+                                  /*crit=*/1, /*flags=*/0));
+    ASSERT_TRUE(X509_sign(root.get(), key.get(), EVP_sha256()));
+
+    bssl::UniquePtr<X509> leaf = MakeTestCert("Root", "Leaf", key.get());
+    ASSERT_TRUE(leaf);
+    ASSERT_TRUE(X509_add1_ext_i2d(leaf.get(), NID_subject_alt_name, names.get(),
+                                  /*crit=*/0, /*flags=*/0));
+    ASSERT_TRUE(X509_sign(leaf.get(), key.get(), EVP_sha256()));
+
+    int ret = Verify(leaf.get(), {root.get()}, {}, {}, 0);
+    EXPECT_EQ(t.result, ret) << X509_verify_cert_error_string(ret);
+  }
+}
+
+TEST(X509Test, PrintGeneralName) {
+  // TODO(https://crbug.com/boringssl/430): Add more tests. Also fix the
+  // external projects that use this to extract the SAN list and unexport.
+  bssl::UniquePtr<GENERAL_NAME> gen = MakeGeneralName(GEN_DNS, "example.com");
+  ASSERT_TRUE(gen);
+  bssl::UniquePtr<STACK_OF(CONF_VALUE)> values(
+      i2v_GENERAL_NAME(nullptr, gen.get(), nullptr));
+  ASSERT_TRUE(values);
+  ASSERT_EQ(1u, sk_CONF_VALUE_num(values.get()));
+  const CONF_VALUE *value = sk_CONF_VALUE_value(values.get(), 0);
+  EXPECT_STREQ(value->name, "DNS");
+  EXPECT_STREQ(value->value, "example.com");
+}
+
 TEST(X509Test, TestPSS) {
   bssl::UniquePtr<X509> cert(CertFromPEM(kExamplePSSCert));
   ASSERT_TRUE(cert);
@@ -1599,7 +1855,7 @@
     if (new_cert) {
       cert.reset(X509_new());
       // Fill in some fields for the certificate arbitrarily.
-      EXPECT_TRUE(X509_set_version(cert.get(), 2 /* X.509v3 */));
+      EXPECT_TRUE(X509_set_version(cert.get(), X509_VERSION_3));
       EXPECT_TRUE(ASN1_INTEGER_set(X509_get_serialNumber(cert.get()), 1));
       EXPECT_TRUE(X509_gmtime_adj(X509_getm_notBefore(cert.get()), 0));
       EXPECT_TRUE(
@@ -1750,7 +2006,8 @@
 
   ASSERT_EQ(static_cast<long>(data_len), i2d_X509(root.get(), nullptr));
 
-  X509_CINF_set_modified(root->cert_info);
+  // Re-encode the TBSCertificate.
+  i2d_re_X509_tbs(root.get(), nullptr);
 
   ASSERT_NE(static_cast<long>(data_len), i2d_X509(root.get(), nullptr));
 }
@@ -1937,65 +2194,6 @@
   EXPECT_EQ(X509_NAME_ENTRY_set(X509_NAME_get_entry(name.get(), 2)), 2);
 }
 
-TEST(X509Test, StringDecoding) {
-  static const struct {
-    std::vector<uint8_t> in;
-    int type;
-    const char *expected;
-  } kTests[] = {
-      // Non-minimal, two-byte UTF-8.
-      {{0xc0, 0x81}, V_ASN1_UTF8STRING, nullptr},
-      // Non-minimal, three-byte UTF-8.
-      {{0xe0, 0x80, 0x81}, V_ASN1_UTF8STRING, nullptr},
-      // Non-minimal, four-byte UTF-8.
-      {{0xf0, 0x80, 0x80, 0x81}, V_ASN1_UTF8STRING, nullptr},
-      // Truncated, four-byte UTF-8.
-      {{0xf0, 0x80, 0x80}, V_ASN1_UTF8STRING, nullptr},
-      // Low-surrogate value.
-      {{0xed, 0xa0, 0x80}, V_ASN1_UTF8STRING, nullptr},
-      // High-surrogate value.
-      {{0xed, 0xb0, 0x81}, V_ASN1_UTF8STRING, nullptr},
-      // Initial BOMs should be rejected from UCS-2 and UCS-4.
-      {{0xfe, 0xff, 0, 88}, V_ASN1_BMPSTRING, nullptr},
-      {{0, 0, 0xfe, 0xff, 0, 0, 0, 88}, V_ASN1_UNIVERSALSTRING, nullptr},
-      // Otherwise, BOMs should pass through.
-      {{0, 88, 0xfe, 0xff}, V_ASN1_BMPSTRING, "X\xef\xbb\xbf"},
-      {{0, 0, 0, 88, 0, 0, 0xfe, 0xff}, V_ASN1_UNIVERSALSTRING,
-       "X\xef\xbb\xbf"},
-      // The maximum code-point should pass though.
-      {{0, 16, 0xff, 0xfd}, V_ASN1_UNIVERSALSTRING, "\xf4\x8f\xbf\xbd"},
-      // Values outside the Unicode space should not.
-      {{0, 17, 0, 0}, V_ASN1_UNIVERSALSTRING, nullptr},
-      // Non-characters should be rejected.
-      {{0, 1, 0xff, 0xff}, V_ASN1_UNIVERSALSTRING, nullptr},
-      {{0, 1, 0xff, 0xfe}, V_ASN1_UNIVERSALSTRING, nullptr},
-      {{0, 0, 0xfd, 0xd5}, V_ASN1_UNIVERSALSTRING, nullptr},
-      // BMPString is UCS-2, not UTF-16, so surrogate pairs are invalid.
-      {{0xd8, 0, 0xdc, 1}, V_ASN1_BMPSTRING, nullptr},
-  };
-
-  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kTests); i++) {
-    SCOPED_TRACE(i);
-    const auto& test = kTests[i];
-    ASN1_STRING s;
-    s.type = test.type;
-    s.data = const_cast<uint8_t*>(test.in.data());
-    s.length = test.in.size();
-
-    uint8_t *utf8;
-    const int utf8_len = ASN1_STRING_to_UTF8(&utf8, &s);
-    EXPECT_EQ(utf8_len < 0, test.expected == nullptr);
-    if (utf8_len >= 0) {
-      if (test.expected != nullptr) {
-        EXPECT_EQ(Bytes(test.expected), Bytes(utf8, utf8_len));
-      }
-      OPENSSL_free(utf8);
-    } else {
-      ERR_clear_error();
-    }
-  }
-}
-
 TEST(X509Test, NoBasicConstraintsCertSign) {
   bssl::UniquePtr<X509> root(CertFromPEM(kSANTypesRoot));
   bssl::UniquePtr<X509> intermediate(
@@ -2986,3 +3184,165 @@
     }
   }
 }
+
+// Test that extracting fields of an |X509_ALGOR| works correctly.
+TEST(X509Test, X509AlgorExtract) {
+  static const char kTestOID[] = "1.2.840.113554.4.1.72585.2";
+  const struct {
+    int param_type;
+    std::vector<uint8_t> param_der;
+  } kTests[] = {
+      // No parameter.
+      {V_ASN1_UNDEF, {}},
+      // BOOLEAN { TRUE }
+      {V_ASN1_BOOLEAN, {0x01, 0x01, 0xff}},
+      // BOOLEAN { FALSE }
+      {V_ASN1_BOOLEAN, {0x01, 0x01, 0x00}},
+      // OCTET_STRING { "a" }
+      {V_ASN1_OCTET_STRING, {0x04, 0x01, 0x61}},
+      // BIT_STRING { `01` `00` }
+      {V_ASN1_BIT_STRING, {0x03, 0x02, 0x01, 0x00}},
+      // INTEGER { -1 }
+      {V_ASN1_INTEGER, {0x02, 0x01, 0xff}},
+      // OBJECT_IDENTIFIER { 1.2.840.113554.4.1.72585.2 }
+      {V_ASN1_OBJECT,
+       {0x06, 0x0c, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7,
+        0x09, 0x02}},
+      // NULL {}
+      {V_ASN1_NULL, {0x05, 0x00}},
+      // SEQUENCE {}
+      {V_ASN1_SEQUENCE, {0x30, 0x00}},
+      // SET {}
+      {V_ASN1_SET, {0x31, 0x00}},
+      // [0] { UTF8String { "a" } }
+      {V_ASN1_OTHER, {0xa0, 0x03, 0x0c, 0x01, 0x61}},
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(Bytes(t.param_der));
+
+    // Assemble an AlgorithmIdentifier with the parameter.
+    bssl::ScopedCBB cbb;
+    CBB seq, oid;
+    ASSERT_TRUE(CBB_init(cbb.get(), 64));
+    ASSERT_TRUE(CBB_add_asn1(cbb.get(), &seq, CBS_ASN1_SEQUENCE));
+    ASSERT_TRUE(CBB_add_asn1(&seq, &oid, CBS_ASN1_OBJECT));
+    ASSERT_TRUE(CBB_add_asn1_oid_from_text(&oid, kTestOID, strlen(kTestOID)));
+    ASSERT_TRUE(CBB_add_bytes(&seq, t.param_der.data(), t.param_der.size()));
+    ASSERT_TRUE(CBB_flush(cbb.get()));
+
+    const uint8_t *ptr = CBB_data(cbb.get());
+    bssl::UniquePtr<X509_ALGOR> alg(
+        d2i_X509_ALGOR(nullptr, &ptr, CBB_len(cbb.get())));
+    ASSERT_TRUE(alg);
+
+    const ASN1_OBJECT *obj;
+    int param_type;
+    const void *param_value;
+    X509_ALGOR_get0(&obj, &param_type, &param_value, alg.get());
+
+    EXPECT_EQ(param_type, t.param_type);
+    char oid_buf[sizeof(kTestOID)];
+    ASSERT_EQ(int(sizeof(oid_buf) - 1),
+              OBJ_obj2txt(oid_buf, sizeof(oid_buf), obj,
+                          /*always_return_oid=*/1));
+    EXPECT_STREQ(oid_buf, kTestOID);
+
+    // |param_type| and |param_value| must be consistent with |ASN1_TYPE|.
+    if (param_type == V_ASN1_UNDEF) {
+      EXPECT_EQ(nullptr, param_value);
+    } else {
+      bssl::UniquePtr<ASN1_TYPE> param(ASN1_TYPE_new());
+      ASSERT_TRUE(param);
+      ASSERT_TRUE(ASN1_TYPE_set1(param.get(), param_type, param_value));
+
+      uint8_t *param_der = nullptr;
+      int param_len = i2d_ASN1_TYPE(param.get(), &param_der);
+      ASSERT_GE(param_len, 0);
+      bssl::UniquePtr<uint8_t> free_param_der(param_der);
+
+      EXPECT_EQ(Bytes(param_der, param_len), Bytes(t.param_der));
+    }
+  }
+}
+
+// Test the various |X509_ATTRIBUTE| creation functions.
+TEST(X509Test, Attribute) {
+  // The friendlyName attribute has a BMPString value. See RFC 2985,
+  // section 5.5.1.
+  static const uint8_t kTest1[] = {0x26, 0x03};  // U+2603 SNOWMAN
+  static const uint8_t kTest1UTF8[] = {0xe2, 0x98, 0x83};
+  static const uint8_t kTest2[] = {0, 't', 0, 'e', 0, 's', 0, 't'};
+
+  auto check_attribute = [&](X509_ATTRIBUTE *attr, bool has_test2) {
+    EXPECT_EQ(NID_friendlyName, OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)));
+
+    EXPECT_EQ(has_test2 ? 2 : 1, X509_ATTRIBUTE_count(attr));
+
+    // The first attribute should contain |kTest1|.
+    const ASN1_TYPE *value = X509_ATTRIBUTE_get0_type(attr, 0);
+    ASSERT_TRUE(value);
+    EXPECT_EQ(V_ASN1_BMPSTRING, value->type);
+    EXPECT_EQ(Bytes(kTest1),
+              Bytes(ASN1_STRING_get0_data(value->value.bmpstring),
+                    ASN1_STRING_length(value->value.bmpstring)));
+
+    // |X509_ATTRIBUTE_get0_data| requires the type match.
+    EXPECT_FALSE(
+        X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_OCTET_STRING, nullptr));
+    const ASN1_BMPSTRING *bmpstring = static_cast<const ASN1_BMPSTRING *>(
+        X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_BMPSTRING, nullptr));
+    ASSERT_TRUE(bmpstring);
+    EXPECT_EQ(Bytes(kTest1), Bytes(ASN1_STRING_get0_data(bmpstring),
+                                   ASN1_STRING_length(bmpstring)));
+
+    if (has_test2) {
+      value = X509_ATTRIBUTE_get0_type(attr, 1);
+      ASSERT_TRUE(value);
+      EXPECT_EQ(V_ASN1_BMPSTRING, value->type);
+      EXPECT_EQ(Bytes(kTest2),
+                Bytes(ASN1_STRING_get0_data(value->value.bmpstring),
+                      ASN1_STRING_length(value->value.bmpstring)));
+    } else {
+      EXPECT_FALSE(X509_ATTRIBUTE_get0_type(attr, 1));
+    }
+
+    EXPECT_FALSE(X509_ATTRIBUTE_get0_type(attr, 2));
+  };
+
+  bssl::UniquePtr<ASN1_STRING> str(ASN1_STRING_type_new(V_ASN1_BMPSTRING));
+  ASSERT_TRUE(str);
+  ASSERT_TRUE(ASN1_STRING_set(str.get(), kTest1, sizeof(kTest1)));
+
+  // Test |X509_ATTRIBUTE_create|.
+  bssl::UniquePtr<X509_ATTRIBUTE> attr(
+      X509_ATTRIBUTE_create(NID_friendlyName, V_ASN1_BMPSTRING, str.get()));
+  ASSERT_TRUE(attr);
+  str.release();  // |X509_ATTRIBUTE_create| takes ownership on success.
+  check_attribute(attr.get(), /*has_test2=*/false);
+
+  // Test the |MBSTRING_*| form of |X509_ATTRIBUTE_set1_data|.
+  attr.reset(X509_ATTRIBUTE_new());
+  ASSERT_TRUE(attr);
+  ASSERT_TRUE(
+      X509_ATTRIBUTE_set1_object(attr.get(), OBJ_nid2obj(NID_friendlyName)));
+  ASSERT_TRUE(X509_ATTRIBUTE_set1_data(attr.get(), MBSTRING_UTF8, kTest1UTF8,
+                                       sizeof(kTest1UTF8)));
+  check_attribute(attr.get(), /*has_test2=*/false);
+
+  // Test the |ASN1_STRING| form of |X509_ATTRIBUTE_set1_data|.
+  ASSERT_TRUE(X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_BMPSTRING, kTest2,
+                                       sizeof(kTest2)));
+  check_attribute(attr.get(), /*has_test2=*/true);
+
+  // Test the |ASN1_TYPE| form of |X509_ATTRIBUTE_set1_data|.
+  attr.reset(X509_ATTRIBUTE_new());
+  ASSERT_TRUE(attr);
+  ASSERT_TRUE(
+      X509_ATTRIBUTE_set1_object(attr.get(), OBJ_nid2obj(NID_friendlyName)));
+  str.reset(ASN1_STRING_type_new(V_ASN1_BMPSTRING));
+  ASSERT_TRUE(str);
+  ASSERT_TRUE(ASN1_STRING_set(str.get(), kTest1, sizeof(kTest1)));
+  ASSERT_TRUE(
+      X509_ATTRIBUTE_set1_data(attr.get(), V_ASN1_BMPSTRING, str.get(), -1));
+  check_attribute(attr.get(), /*has_test2=*/false);
+}
diff --git a/deps/boringssl/src/crypto/x509/x509_trs.c b/deps/boringssl/src/crypto/x509/x509_trs.c
index d3002e8..c95d6fc 100644
--- a/deps/boringssl/src/crypto/x509/x509_trs.c
+++ b/deps/boringssl/src/crypto/x509/x509_trs.c
@@ -60,6 +60,8 @@
 #include <openssl/x509v3.h>
 
 #include "../x509v3/internal.h"
+#include "internal.h"
+
 
 static int tr_cmp(const X509_TRUST **a, const X509_TRUST **b);
 static void trtable_free(X509_TRUST *p);
diff --git a/deps/boringssl/src/crypto/x509/x509_v3.c b/deps/boringssl/src/crypto/x509/x509_v3.c
index 91bf024..985161d 100644
--- a/deps/boringssl/src/crypto/x509/x509_v3.c
+++ b/deps/boringssl/src/crypto/x509/x509_v3.c
@@ -62,6 +62,9 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
+#include "internal.h"
+
+
 int X509v3_get_ext_count(const STACK_OF(X509_EXTENSION) *x)
 {
     if (x == NULL)
@@ -72,12 +75,11 @@
 int X509v3_get_ext_by_NID(const STACK_OF(X509_EXTENSION) *x, int nid,
                           int lastpos)
 {
-    const ASN1_OBJECT *obj;
-
-    obj = OBJ_nid2obj(nid);
-    if (obj == NULL)
-        return (-2);
-    return (X509v3_get_ext_by_OBJ(x, obj, lastpos));
+    const ASN1_OBJECT *obj = OBJ_nid2obj(nid);
+    if (obj == NULL) {
+        return -1;
+    }
+    return X509v3_get_ext_by_OBJ(x, obj, lastpos);
 }
 
 int X509v3_get_ext_by_OBJ(const STACK_OF(X509_EXTENSION) *sk,
@@ -103,21 +105,24 @@
 int X509v3_get_ext_by_critical(const STACK_OF(X509_EXTENSION) *sk, int crit,
                                int lastpos)
 {
-    int n;
-    X509_EXTENSION *ex;
-
-    if (sk == NULL)
-        return (-1);
-    lastpos++;
-    if (lastpos < 0)
-        lastpos = 0;
-    n = sk_X509_EXTENSION_num(sk);
-    for (; lastpos < n; lastpos++) {
-        ex = sk_X509_EXTENSION_value(sk, lastpos);
-        if (((ex->critical > 0) && crit) || ((ex->critical <= 0) && !crit))
-            return (lastpos);
+    if (sk == NULL) {
+        return -1;
     }
-    return (-1);
+
+    lastpos++;
+    if (lastpos < 0) {
+        lastpos = 0;
+    }
+
+    crit = !!crit;
+    int n = sk_X509_EXTENSION_num(sk);
+    for (; lastpos < n; lastpos++) {
+        const X509_EXTENSION *ex = sk_X509_EXTENSION_value(sk, lastpos);
+        if (X509_EXTENSION_get_critical(ex) == crit) {
+            return lastpos;
+        }
+    }
+    return -1;
 }
 
 X509_EXTENSION *X509v3_get_ext(const STACK_OF(X509_EXTENSION) *x, int loc)
@@ -172,11 +177,9 @@
  err:
     OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
  err2:
-    if (new_ex != NULL)
-        X509_EXTENSION_free(new_ex);
-    if (sk != NULL)
-        sk_X509_EXTENSION_free(sk);
-    return (NULL);
+    X509_EXTENSION_free(new_ex);
+    sk_X509_EXTENSION_free(sk);
+    return NULL;
 }
 
 X509_EXTENSION *X509_EXTENSION_create_by_NID(X509_EXTENSION **ex, int nid,
@@ -268,7 +271,7 @@
     return (ex->value);
 }
 
-int X509_EXTENSION_get_critical(X509_EXTENSION *ex)
+int X509_EXTENSION_get_critical(const X509_EXTENSION *ex)
 {
     if (ex == NULL)
         return (0);
diff --git a/deps/boringssl/src/crypto/x509/x509_vfy.c b/deps/boringssl/src/crypto/x509/x509_vfy.c
index a997202..818459c 100644
--- a/deps/boringssl/src/crypto/x509/x509_vfy.c
+++ b/deps/boringssl/src/crypto/x509/x509_vfy.c
@@ -67,7 +67,7 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
-#include "vpm_int.h"
+#include "internal.h"
 #include "../internal.h"
 #include "../x509v3/internal.h"
 
@@ -835,20 +835,20 @@
     return ctx->verify_cb(0, ctx);
 }
 
-static int check_hosts(X509 *x, X509_VERIFY_PARAM_ID *id)
+static int check_hosts(X509 *x, X509_VERIFY_PARAM *param)
 {
     size_t i;
-    size_t n = sk_OPENSSL_STRING_num(id->hosts);
+    size_t n = sk_OPENSSL_STRING_num(param->hosts);
     char *name;
 
-    if (id->peername != NULL) {
-        OPENSSL_free(id->peername);
-        id->peername = NULL;
+    if (param->peername != NULL) {
+        OPENSSL_free(param->peername);
+        param->peername = NULL;
     }
     for (i = 0; i < n; ++i) {
-        name = sk_OPENSSL_STRING_value(id->hosts, i);
-        if (X509_check_host(x, name, strlen(name), id->hostflags,
-                            &id->peername) > 0)
+        name = sk_OPENSSL_STRING_value(param->hosts, i);
+        if (X509_check_host(x, name, strlen(name), param->hostflags,
+                            &param->peername) > 0)
             return 1;
     }
     return n == 0;
@@ -857,21 +857,20 @@
 static int check_id(X509_STORE_CTX *ctx)
 {
     X509_VERIFY_PARAM *vpm = ctx->param;
-    X509_VERIFY_PARAM_ID *id = vpm->id;
     X509 *x = ctx->cert;
-    if (id->poison) {
+    if (vpm->poison) {
         if (!check_id_error(ctx, X509_V_ERR_INVALID_CALL))
             return 0;
     }
-    if (id->hosts && check_hosts(x, id) <= 0) {
+    if (vpm->hosts && check_hosts(x, vpm) <= 0) {
         if (!check_id_error(ctx, X509_V_ERR_HOSTNAME_MISMATCH))
             return 0;
     }
-    if (id->email && X509_check_email(x, id->email, id->emaillen, 0) <= 0) {
+    if (vpm->email && X509_check_email(x, vpm->email, vpm->emaillen, 0) <= 0) {
         if (!check_id_error(ctx, X509_V_ERR_EMAIL_MISMATCH))
             return 0;
     }
-    if (id->ip && X509_check_ip(x, id->ip, id->iplen, 0) <= 0) {
+    if (vpm->ip && X509_check_ip(x, vpm->ip, vpm->iplen, 0) <= 0) {
         if (!check_id_error(ctx, X509_V_ERR_IP_ADDRESS_MISMATCH))
             return 0;
     }
@@ -1404,12 +1403,12 @@
 }
 
 /*
- * RFC3280 says nothing about the relationship between CRL path and
+ * RFC 3280 says nothing about the relationship between CRL path and
  * certificate path, which could lead to situations where a certificate could
- * be revoked or validated by a CA not authorised to do so. RFC5280 is more
+ * be revoked or validated by a CA not authorised to do so. RFC 5280 is more
  * strict and states that the two paths must end in the same trust anchor,
  * though some discussions remain... until this is resolved we use the
- * RFC5280 version
+ * RFC 5280 version
  */
 
 static int check_crl_chain(X509_STORE_CTX *ctx,
@@ -1920,8 +1919,8 @@
     int i, day, sec, ret = 0;
 
     /*
-     * Note that ASN.1 allows much more slack in the time format than RFC5280.
-     * In RFC5280, the representation is fixed:
+     * Note that ASN.1 allows much more slack in the time format than RFC 5280.
+     * In RFC 5280, the representation is fixed:
      * UTCTime: YYMMDDHHMMSSZ
      * GeneralizedTime: YYYYMMDDHHMMSSZ
      *
@@ -1977,9 +1976,9 @@
     return ret;
 }
 
-ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj)
+ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long offset_sec)
 {
-    return X509_time_adj(s, adj, NULL);
+    return X509_time_adj(s, offset_sec, NULL);
 }
 
 ASN1_TIME *X509_time_adj(ASN1_TIME *s, long offset_sec, time_t *in_tm)
@@ -1992,17 +1991,12 @@
 {
     time_t t = 0;
 
-    if (in_tm)
+    if (in_tm) {
         t = *in_tm;
-    else
+    } else {
         time(&t);
-
-    if (s && !(s->flags & ASN1_STRING_FLAG_MSTRING)) {
-        if (s->type == V_ASN1_UTCTIME)
-            return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec);
-        if (s->type == V_ASN1_GENERALIZEDTIME)
-            return ASN1_GENERALIZEDTIME_adj(s, t, offset_day, offset_sec);
     }
+
     return ASN1_TIME_adj(s, t, offset_day, offset_sec);
 }
 
@@ -2052,7 +2046,7 @@
     }
     /* Create new CRL */
     crl = X509_CRL_new();
-    if (!crl || !X509_CRL_set_version(crl, 1))
+    if (!crl || !X509_CRL_set_version(crl, X509_CRL_VERSION_2))
         goto memerr;
     /* Set issuer name */
     if (!X509_CRL_set_issuer_name(crl, X509_CRL_get_issuer(newer)))
@@ -2494,7 +2488,3 @@
         X509_VERIFY_PARAM_free(ctx->param);
     ctx->param = param;
 }
-
-IMPLEMENT_ASN1_SET_OF(X509)
-
-IMPLEMENT_ASN1_SET_OF(X509_ATTRIBUTE)
diff --git a/deps/boringssl/src/crypto/x509/x509_vpm.c b/deps/boringssl/src/crypto/x509/x509_vpm.c
index d8d1efe..5a881d6 100644
--- a/deps/boringssl/src/crypto/x509/x509_vpm.c
+++ b/deps/boringssl/src/crypto/x509/x509_vpm.c
@@ -62,8 +62,9 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
-#include "vpm_int.h"
+#include "internal.h"
 #include "../internal.h"
+#include "../x509v3/internal.h"
 
 
 /* X509_VERIFY_PARAM functions */
@@ -83,7 +84,7 @@
 
 #define string_stack_free(sk) sk_OPENSSL_STRING_pop_free(sk, str_free)
 
-static int int_x509_param_set_hosts(X509_VERIFY_PARAM_ID *id, int mode,
+static int int_x509_param_set_hosts(X509_VERIFY_PARAM *param, int mode,
                                     const char *name, size_t namelen)
 {
     char *copy;
@@ -100,26 +101,26 @@
     if (name && OPENSSL_memchr(name, '\0', namelen))
         return 0;
 
-    if (mode == SET_HOST && id->hosts) {
-        string_stack_free(id->hosts);
-        id->hosts = NULL;
+    if (mode == SET_HOST && param->hosts) {
+        string_stack_free(param->hosts);
+        param->hosts = NULL;
     }
 
     copy = OPENSSL_strndup(name, namelen);
     if (copy == NULL)
         return 0;
 
-    if (id->hosts == NULL &&
-        (id->hosts = sk_OPENSSL_STRING_new_null()) == NULL) {
+    if (param->hosts == NULL &&
+        (param->hosts = sk_OPENSSL_STRING_new_null()) == NULL) {
         OPENSSL_free(copy);
         return 0;
     }
 
-    if (!sk_OPENSSL_STRING_push(id->hosts, copy)) {
+    if (!sk_OPENSSL_STRING_push(param->hosts, copy)) {
         OPENSSL_free(copy);
-        if (sk_OPENSSL_STRING_num(id->hosts) == 0) {
-            sk_OPENSSL_STRING_free(id->hosts);
-            id->hosts = NULL;
+        if (sk_OPENSSL_STRING_num(param->hosts) == 0) {
+            sk_OPENSSL_STRING_free(param->hosts);
+            param->hosts = NULL;
         }
         return 0;
     }
@@ -129,7 +130,6 @@
 
 static void x509_verify_param_zero(X509_VERIFY_PARAM *param)
 {
-    X509_VERIFY_PARAM_ID *paramid;
     if (!param)
         return;
     param->name = NULL;
@@ -145,43 +145,34 @@
         sk_ASN1_OBJECT_pop_free(param->policies, ASN1_OBJECT_free);
         param->policies = NULL;
     }
-    paramid = param->id;
-    if (paramid->hosts) {
-        string_stack_free(paramid->hosts);
-        paramid->hosts = NULL;
+    if (param->hosts) {
+        string_stack_free(param->hosts);
+        param->hosts = NULL;
     }
-    if (paramid->peername) {
-        OPENSSL_free(paramid->peername);
-        paramid->peername = NULL;
+    if (param->peername) {
+        OPENSSL_free(param->peername);
+        param->peername = NULL;
     }
-    if (paramid->email) {
-        OPENSSL_free(paramid->email);
-        paramid->email = NULL;
-        paramid->emaillen = 0;
+    if (param->email) {
+        OPENSSL_free(param->email);
+        param->email = NULL;
+        param->emaillen = 0;
     }
-    if (paramid->ip) {
-        OPENSSL_free(paramid->ip);
-        paramid->ip = NULL;
-        paramid->iplen = 0;
+    if (param->ip) {
+        OPENSSL_free(param->ip);
+        param->ip = NULL;
+        param->iplen = 0;
     }
-    paramid->poison = 0;
+    param->poison = 0;
 }
 
 X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void)
 {
     X509_VERIFY_PARAM *param;
-    X509_VERIFY_PARAM_ID *paramid;
     param = OPENSSL_malloc(sizeof(X509_VERIFY_PARAM));
     if (!param)
         return NULL;
-    paramid = OPENSSL_malloc(sizeof(X509_VERIFY_PARAM_ID));
-    if (!paramid) {
-        OPENSSL_free(param);
-        return NULL;
-    }
     OPENSSL_memset(param, 0, sizeof(X509_VERIFY_PARAM));
-    OPENSSL_memset(paramid, 0, sizeof(X509_VERIFY_PARAM_ID));
-    param->id = paramid;
     x509_verify_param_zero(param);
     return param;
 }
@@ -191,7 +182,6 @@
     if (param == NULL)
         return;
     x509_verify_param_zero(param);
-    OPENSSL_free(param->id);
     OPENSSL_free(param);
 }
 
@@ -233,11 +223,6 @@
   (to_overwrite ||                              \
    ((src->field != (def)) && (to_default || (dest->field == (def)))))
 
-/* As above but for ID fields */
-
-#define test_x509_verify_param_copy_id(idf, def) \
-        test_x509_verify_param_copy(id->idf, def)
-
 /* Macro to test and copy a field if necessary */
 
 #define x509_verify_param_copy(field, def) \
@@ -249,10 +234,8 @@
 {
     unsigned long inh_flags;
     int to_default, to_overwrite;
-    X509_VERIFY_PARAM_ID *id;
     if (!src)
         return 1;
-    id = src->id;
     inh_flags = dest->inh_flags | src->inh_flags;
 
     if (inh_flags & X509_VP_FLAG_ONCE)
@@ -294,31 +277,31 @@
     }
 
     /* Copy the host flags if and only if we're copying the host list */
-    if (test_x509_verify_param_copy_id(hosts, NULL)) {
-        if (dest->id->hosts) {
-            string_stack_free(dest->id->hosts);
-            dest->id->hosts = NULL;
+    if (test_x509_verify_param_copy(hosts, NULL)) {
+        if (dest->hosts) {
+            string_stack_free(dest->hosts);
+            dest->hosts = NULL;
         }
-        if (id->hosts) {
-            dest->id->hosts =
-                sk_OPENSSL_STRING_deep_copy(id->hosts, str_copy, str_free);
-            if (dest->id->hosts == NULL)
+        if (src->hosts) {
+            dest->hosts =
+                sk_OPENSSL_STRING_deep_copy(src->hosts, str_copy, str_free);
+            if (dest->hosts == NULL)
                 return 0;
-            dest->id->hostflags = id->hostflags;
+            dest->hostflags = src->hostflags;
         }
     }
 
-    if (test_x509_verify_param_copy_id(email, NULL)) {
-        if (!X509_VERIFY_PARAM_set1_email(dest, id->email, id->emaillen))
+    if (test_x509_verify_param_copy(email, NULL)) {
+        if (!X509_VERIFY_PARAM_set1_email(dest, src->email, src->emaillen))
             return 0;
     }
 
-    if (test_x509_verify_param_copy_id(ip, NULL)) {
-        if (!X509_VERIFY_PARAM_set1_ip(dest, id->ip, id->iplen))
+    if (test_x509_verify_param_copy(ip, NULL)) {
+        if (!X509_VERIFY_PARAM_set1_ip(dest, src->ip, src->iplen))
             return 0;
     }
 
-    dest->id->poison = src->id->poison;
+    dest->poison = src->poison;
 
     return 1;
 }
@@ -457,8 +440,8 @@
 int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
                                 const char *name, size_t namelen)
 {
-    if (!int_x509_param_set_hosts(param->id, SET_HOST, name, namelen)) {
-        param->id->poison = 1;
+    if (!int_x509_param_set_hosts(param, SET_HOST, name, namelen)) {
+        param->poison = 1;
         return 0;
     }
     return 1;
@@ -467,8 +450,8 @@
 int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param,
                                 const char *name, size_t namelen)
 {
-    if (!int_x509_param_set_hosts(param->id, ADD_HOST, name, namelen)) {
-        param->id->poison = 1;
+    if (!int_x509_param_set_hosts(param, ADD_HOST, name, namelen)) {
+        param->poison = 1;
         return 0;
     }
     return 1;
@@ -477,21 +460,21 @@
 void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
                                      unsigned int flags)
 {
-    param->id->hostflags = flags;
+    param->hostflags = flags;
 }
 
 char *X509_VERIFY_PARAM_get0_peername(X509_VERIFY_PARAM *param)
 {
-    return param->id->peername;
+    return param->peername;
 }
 
 int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
                                  const char *email, size_t emaillen)
 {
     if (OPENSSL_memchr(email, '\0', emaillen) != NULL ||
-        !int_x509_param_set1(&param->id->email, &param->id->emaillen,
+        !int_x509_param_set1(&param->email, &param->emaillen,
                                email, emaillen)) {
-        param->id->poison = 1;
+        param->poison = 1;
         return 0;
     }
 
@@ -502,9 +485,9 @@
                               const unsigned char *ip, size_t iplen)
 {
     if ((iplen != 4 && iplen != 16) ||
-        !int_x509_param_set1((char **)&param->id->ip, &param->id->iplen,
+        !int_x509_param_set1((char **)&param->ip, &param->iplen,
                              (char *)ip, iplen)) {
-        param->id->poison = 1;
+        param->poison = 1;
         return 0;
     }
 
@@ -516,7 +499,7 @@
     unsigned char ipout[16];
     size_t iplen;
 
-    iplen = (size_t)a2i_ipadd(ipout, ipasc);
+    iplen = (size_t)x509v3_a2i_ipadd(ipout, ipasc);
     if (iplen == 0)
         return 0;
     return X509_VERIFY_PARAM_set1_ip(param, ipout, iplen);
@@ -532,10 +515,7 @@
     return param->name;
 }
 
-static const X509_VERIFY_PARAM_ID _empty_id =
-    { NULL, 0U, NULL, NULL, 0, NULL, 0, 0 };
-
-#define vpm_empty_id ((X509_VERIFY_PARAM_ID *)&_empty_id)
+#define vpm_empty_id NULL, 0U, NULL, NULL, 0, NULL, 0, 0
 
 /*
  * Default verify parameters: these are used for various applications and can
diff --git a/deps/boringssl/src/crypto/x509/x509cset.c b/deps/boringssl/src/crypto/x509/x509cset.c
index cc27acb..7816d73 100644
--- a/deps/boringssl/src/crypto/x509/x509cset.c
+++ b/deps/boringssl/src/crypto/x509/x509cset.c
@@ -60,6 +60,7 @@
 #include <openssl/x509.h>
 
 #include "../internal.h"
+#include "internal.h"
 
 int X509_CRL_set_version(X509_CRL *x, long version)
 {
@@ -250,3 +251,34 @@
 {
     return i2d_X509_CRL_INFO(crl->crl, outp);
 }
+
+int X509_CRL_set1_signature_algo(X509_CRL *crl, const X509_ALGOR *algo)
+{
+    /* TODO(davidben): Const-correct generated ASN.1 dup functions.
+     * Alternatively, when the types are hidden and we can embed required fields
+     * directly in structs, import |X509_ALGOR_copy| from upstream. */
+    X509_ALGOR *copy1 = X509_ALGOR_dup((X509_ALGOR *)algo);
+    X509_ALGOR *copy2 = X509_ALGOR_dup((X509_ALGOR *)algo);
+    if (copy1 == NULL || copy2 == NULL) {
+        X509_ALGOR_free(copy1);
+        X509_ALGOR_free(copy2);
+        return 0;
+    }
+
+    X509_ALGOR_free(crl->sig_alg);
+    crl->sig_alg = copy1;
+    X509_ALGOR_free(crl->crl->sig_alg);
+    crl->crl->sig_alg = copy2;
+    return 1;
+}
+
+int X509_CRL_set1_signature_value(X509_CRL *crl, const uint8_t *sig,
+                                  size_t sig_len)
+{
+    if (!ASN1_STRING_set(crl->signature, sig, sig_len)) {
+      return 0;
+    }
+    crl->signature->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+    crl->signature->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+    return 1;
+}
diff --git a/deps/boringssl/src/crypto/x509/x509name.c b/deps/boringssl/src/crypto/x509/x509name.c
index 0bf3459..6bc0952 100644
--- a/deps/boringssl/src/crypto/x509/x509name.c
+++ b/deps/boringssl/src/crypto/x509/x509name.c
@@ -64,6 +64,7 @@
 #include <openssl/x509.h>
 
 #include "../internal.h"
+#include "internal.h"
 
 
 int X509_NAME_get_text_by_NID(const X509_NAME *name, int nid, char *buf,
@@ -367,10 +368,7 @@
     if (!i)
         return (0);
     if (type != V_ASN1_UNDEF) {
-        if (type == V_ASN1_APP_CHOOSE)
-            ne->value->type = ASN1_PRINTABLE_type(bytes, len);
-        else
-            ne->value->type = type;
+        ne->value->type = type;
     }
     return (1);
 }
diff --git a/deps/boringssl/src/crypto/x509/x509rset.c b/deps/boringssl/src/crypto/x509/x509rset.c
index c4e6683..72b4148 100644
--- a/deps/boringssl/src/crypto/x509/x509rset.c
+++ b/deps/boringssl/src/crypto/x509/x509rset.c
@@ -59,6 +59,9 @@
 #include <openssl/obj.h>
 #include <openssl/x509.h>
 
+#include "internal.h"
+
+
 int X509_REQ_set_version(X509_REQ *x, long version)
 {
     if (x == NULL)
diff --git a/deps/boringssl/src/crypto/x509/x_algor.c b/deps/boringssl/src/crypto/x509/x_algor.c
index 13c9a8c..a454c0c 100644
--- a/deps/boringssl/src/crypto/x509/x_algor.c
+++ b/deps/boringssl/src/crypto/x509/x_algor.c
@@ -61,6 +61,8 @@
 #include <openssl/digest.h>
 #include <openssl/obj.h>
 
+#include "../asn1/internal.h"
+
 
 ASN1_SEQUENCE(X509_ALGOR) = {
         ASN1_SIMPLE(X509_ALGOR, algorithm, ASN1_OBJECT),
@@ -75,10 +77,7 @@
 IMPLEMENT_ASN1_ENCODE_FUNCTIONS_fname(X509_ALGORS, X509_ALGORS, X509_ALGORS)
 IMPLEMENT_ASN1_DUP_FUNCTION(X509_ALGOR)
 
-IMPLEMENT_ASN1_SET_OF(X509_ALGOR)
-
-int X509_ALGOR_set0(X509_ALGOR *alg, const ASN1_OBJECT *aobj, int ptype,
-                    void *pval)
+int X509_ALGOR_set0(X509_ALGOR *alg, ASN1_OBJECT *aobj, int ptype, void *pval)
 {
     if (!alg)
         return 0;
@@ -89,9 +88,8 @@
             return 0;
     }
     if (alg) {
-        if (alg->algorithm)
-            ASN1_OBJECT_free(alg->algorithm);
-        alg->algorithm = (ASN1_OBJECT *)aobj;
+        ASN1_OBJECT_free(alg->algorithm);
+        alg->algorithm = aobj;
     }
     if (ptype == 0)
         return 1;
@@ -105,19 +103,23 @@
     return 1;
 }
 
-void X509_ALGOR_get0(const ASN1_OBJECT **paobj, int *pptype, const void **ppval,
-                     const X509_ALGOR *algor)
+void X509_ALGOR_get0(const ASN1_OBJECT **out_obj, int *out_param_type,
+                     const void **out_param_value, const X509_ALGOR *alg)
 {
-    if (paobj)
-        *paobj = algor->algorithm;
-    if (pptype) {
-        if (algor->parameter == NULL) {
-            *pptype = V_ASN1_UNDEF;
-            return;
-        } else
-            *pptype = algor->parameter->type;
-        if (ppval)
-            *ppval = algor->parameter->value.ptr;
+    if (out_obj != NULL) {
+        *out_obj = alg->algorithm;
+    }
+    if (out_param_type != NULL) {
+        int type = V_ASN1_UNDEF;
+        const void *value = NULL;
+        if (alg->parameter != NULL) {
+            type = alg->parameter->type;
+            value = asn1_type_value_as_pointer(alg->parameter);
+        }
+        *out_param_type = type;
+        if (out_param_value != NULL) {
+            *out_param_value = value;
+        }
     }
 }
 
diff --git a/deps/boringssl/src/crypto/x509/x_all.c b/deps/boringssl/src/crypto/x509/x_all.c
index a29e038..7ceff50 100644
--- a/deps/boringssl/src/crypto/x509/x_all.c
+++ b/deps/boringssl/src/crypto/x509/x_all.c
@@ -66,6 +66,9 @@
 #include <openssl/rsa.h>
 #include <openssl/stack.h>
 
+#include "internal.h"
+
+
 int X509_verify(X509 *x509, EVP_PKEY *pkey)
 {
     if (X509_ALGOR_cmp(x509->sig_alg, x509->cert_info->signature)) {
@@ -137,7 +140,6 @@
                              spki->signature, spki->spkac, pkey));
 }
 
-#ifndef OPENSSL_NO_FP_API
 X509 *d2i_X509_fp(FILE *fp, X509 **x509)
 {
     return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509), fp, x509);
@@ -147,7 +149,6 @@
 {
     return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509), fp, x509);
 }
-#endif
 
 X509 *d2i_X509_bio(BIO *bp, X509 **x509)
 {
@@ -159,7 +160,6 @@
     return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509), bp, x509);
 }
 
-#ifndef OPENSSL_NO_FP_API
 X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl)
 {
     return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl);
@@ -169,7 +169,6 @@
 {
     return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_CRL), fp, crl);
 }
-#endif
 
 X509_CRL *d2i_X509_CRL_bio(BIO *bp, X509_CRL **crl)
 {
@@ -181,7 +180,6 @@
     return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_CRL), bp, crl);
 }
 
-#ifndef OPENSSL_NO_FP_API
 X509_REQ *d2i_X509_REQ_fp(FILE *fp, X509_REQ **req)
 {
     return ASN1_item_d2i_fp(ASN1_ITEM_rptr(X509_REQ), fp, req);
@@ -191,7 +189,6 @@
 {
     return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509_REQ), fp, req);
 }
-#endif
 
 X509_REQ *d2i_X509_REQ_bio(BIO *bp, X509_REQ **req)
 {
@@ -203,7 +200,6 @@
     return ASN1_item_i2d_bio(ASN1_ITEM_rptr(X509_REQ), bp, req);
 }
 
-#ifndef OPENSSL_NO_FP_API
 
 #define IMPLEMENT_D2I_FP(type, name, bio_func) \
   type *name(FILE *fp, type **obj) {           \
@@ -235,7 +231,6 @@
 
 IMPLEMENT_D2I_FP(RSA, d2i_RSA_PUBKEY_fp, d2i_RSA_PUBKEY_bio)
 IMPLEMENT_I2D_FP(RSA, i2d_RSA_PUBKEY_fp, i2d_RSA_PUBKEY_bio)
-#endif
 
 #define IMPLEMENT_D2I_BIO(type, name, d2i_func)         \
   type *name(BIO *bio, type **obj) {                    \
@@ -272,13 +267,11 @@
 IMPLEMENT_I2D_BIO(RSA, i2d_RSA_PUBKEY_bio, i2d_RSA_PUBKEY)
 
 #ifndef OPENSSL_NO_DSA
-# ifndef OPENSSL_NO_FP_API
 IMPLEMENT_D2I_FP(DSA, d2i_DSAPrivateKey_fp, d2i_DSAPrivateKey_bio)
 IMPLEMENT_I2D_FP(DSA, i2d_DSAPrivateKey_fp, i2d_DSAPrivateKey_bio)
 
 IMPLEMENT_D2I_FP(DSA, d2i_DSA_PUBKEY_fp, d2i_DSA_PUBKEY_bio)
 IMPLEMENT_I2D_FP(DSA, i2d_DSA_PUBKEY_fp, i2d_DSA_PUBKEY_bio)
-# endif
 
 IMPLEMENT_D2I_BIO(DSA, d2i_DSAPrivateKey_bio, d2i_DSAPrivateKey)
 IMPLEMENT_I2D_BIO(DSA, i2d_DSAPrivateKey_bio, i2d_DSAPrivateKey)
@@ -287,13 +280,11 @@
 IMPLEMENT_I2D_BIO(DSA, i2d_DSA_PUBKEY_bio, i2d_DSA_PUBKEY)
 #endif
 
-#ifndef OPENSSL_NO_FP_API
 IMPLEMENT_D2I_FP(EC_KEY, d2i_ECPrivateKey_fp, d2i_ECPrivateKey_bio)
 IMPLEMENT_I2D_FP(EC_KEY, i2d_ECPrivateKey_fp, i2d_ECPrivateKey_bio)
 
 IMPLEMENT_D2I_FP(EC_KEY, d2i_EC_PUBKEY_fp, d2i_EC_PUBKEY_bio)
 IMPLEMENT_I2D_FP(EC_KEY, i2d_EC_PUBKEY_fp, i2d_EC_PUBKEY_bio)
-#endif
 
 IMPLEMENT_D2I_BIO(EC_KEY, d2i_ECPrivateKey_bio, d2i_ECPrivateKey)
 IMPLEMENT_I2D_BIO(EC_KEY, i2d_ECPrivateKey_bio, i2d_ECPrivateKey)
@@ -339,15 +330,12 @@
             (ASN1_ITEM_rptr(X509_NAME), type, (char *)data, md, len));
 }
 
-#ifndef OPENSSL_NO_FP_API
 IMPLEMENT_D2I_FP(X509_SIG, d2i_PKCS8_fp, d2i_PKCS8_bio)
 IMPLEMENT_I2D_FP(X509_SIG, i2d_PKCS8_fp, i2d_PKCS8_bio)
-#endif
 
 IMPLEMENT_D2I_BIO(X509_SIG, d2i_PKCS8_bio, d2i_X509_SIG)
 IMPLEMENT_I2D_BIO(X509_SIG, i2d_PKCS8_bio, i2d_X509_SIG)
 
-#ifndef OPENSSL_NO_FP_API
 IMPLEMENT_D2I_FP(PKCS8_PRIV_KEY_INFO, d2i_PKCS8_PRIV_KEY_INFO_fp,
                  d2i_PKCS8_PRIV_KEY_INFO_bio)
 IMPLEMENT_I2D_FP(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO_fp,
@@ -387,7 +375,6 @@
     PKCS8_PRIV_KEY_INFO_free(p8inf);
     return ret;
 }
-#endif
 
 IMPLEMENT_D2I_BIO(EVP_PKEY, d2i_PrivateKey_bio, d2i_AutoPrivateKey)
 IMPLEMENT_I2D_BIO(EVP_PKEY, i2d_PrivateKey_bio, i2d_PrivateKey)
diff --git a/deps/boringssl/src/crypto/x509/x_attrib.c b/deps/boringssl/src/crypto/x509/x_attrib.c
index de8c95c..91b3ee8 100644
--- a/deps/boringssl/src/crypto/x509/x_attrib.c
+++ b/deps/boringssl/src/crypto/x509/x_attrib.c
@@ -59,53 +59,40 @@
 #include <openssl/x509.h>
 #include <openssl/obj.h>
 
-/*
- * X509_ATTRIBUTE: this has the following form: typedef struct
- * x509_attributes_st { ASN1_OBJECT *object; int single; union { char *ptr;
- * STACK_OF(ASN1_TYPE) *set; ASN1_TYPE *single; } value; } X509_ATTRIBUTE;
- * this needs some extra thought because the CHOICE type is merged with the
- * main structure and because the value can be anything at all we *must* try
- * the SET OF first because the ASN1_ANY type will swallow anything including
- * the whole SET OF structure.
- */
+#include "internal.h"
 
-ASN1_CHOICE(X509_ATTRIBUTE_SET) = {
-        ASN1_SET_OF(X509_ATTRIBUTE, value.set, ASN1_ANY),
-        ASN1_SIMPLE(X509_ATTRIBUTE, value.single, ASN1_ANY)
-} ASN1_CHOICE_END_selector(X509_ATTRIBUTE, X509_ATTRIBUTE_SET, single)
 
 ASN1_SEQUENCE(X509_ATTRIBUTE) = {
         ASN1_SIMPLE(X509_ATTRIBUTE, object, ASN1_OBJECT),
-        /* CHOICE type merged with parent */
-        ASN1_EX_COMBINE(0, 0, X509_ATTRIBUTE_SET)
+        ASN1_SET_OF(X509_ATTRIBUTE, set, ASN1_ANY),
 } ASN1_SEQUENCE_END(X509_ATTRIBUTE)
 
 IMPLEMENT_ASN1_FUNCTIONS(X509_ATTRIBUTE)
 IMPLEMENT_ASN1_DUP_FUNCTION(X509_ATTRIBUTE)
 
-X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int atrtype, void *value)
+X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int attrtype, void *value)
 {
-    X509_ATTRIBUTE *ret = NULL;
-    ASN1_TYPE *val = NULL;
+    ASN1_OBJECT *obj = OBJ_nid2obj(nid);
+    if (obj == NULL) {
+        return NULL;
+    }
 
-    if ((ret = X509_ATTRIBUTE_new()) == NULL)
-        return (NULL);
-    /* TODO(fork): const correctness. */
-    ret->object = (ASN1_OBJECT *)OBJ_nid2obj(nid);
-    ret->single = 0;
-    if ((ret->value.set = sk_ASN1_TYPE_new_null()) == NULL)
+    X509_ATTRIBUTE *ret = X509_ATTRIBUTE_new();
+    ASN1_TYPE *val = ASN1_TYPE_new();
+    if (ret == NULL || val == NULL) {
         goto err;
-    if ((val = ASN1_TYPE_new()) == NULL)
-        goto err;
-    if (!sk_ASN1_TYPE_push(ret->value.set, val))
-        goto err;
+    }
 
-    ASN1_TYPE_set(val, atrtype, value);
-    return (ret);
+    ret->object = obj;
+    if (!sk_ASN1_TYPE_push(ret->set, val)) {
+        goto err;
+    }
+
+    ASN1_TYPE_set(val, attrtype, value);
+    return ret;
+
  err:
-    if (ret != NULL)
-        X509_ATTRIBUTE_free(ret);
-    if (val != NULL)
-        ASN1_TYPE_free(val);
-    return (NULL);
+    X509_ATTRIBUTE_free(ret);
+    ASN1_TYPE_free(val);
+    return NULL;
 }
diff --git a/deps/boringssl/src/crypto/x509/x_crl.c b/deps/boringssl/src/crypto/x509/x_crl.c
index 3b9f137..0d419ac 100644
--- a/deps/boringssl/src/crypto/x509/x_crl.c
+++ b/deps/boringssl/src/crypto/x509/x_crl.c
@@ -66,6 +66,7 @@
 #include <openssl/x509v3.h>
 
 #include "../internal.h"
+#include "internal.h"
 
 /*
  * Method to handle CRL access. In general a CRL could be very large (several
@@ -204,11 +205,12 @@
 
         for (k = 0; k < sk_X509_EXTENSION_num(exts); k++) {
             ext = sk_X509_EXTENSION_value(exts, k);
-            if (ext->critical > 0) {
-                if (OBJ_obj2nid(ext->object) == NID_certificate_issuer)
-                    continue;
-                crl->flags |= EXFLAG_CRITICAL;
-                break;
+            if (X509_EXTENSION_get_critical(ext)) {
+              if (OBJ_obj2nid(X509_EXTENSION_get_object(ext)) ==
+                  NID_certificate_issuer)
+                continue;
+              crl->flags |= EXFLAG_CRITICAL;
+              break;
             }
         }
 
@@ -297,10 +299,10 @@
         for (idx = 0; idx < sk_X509_EXTENSION_num(exts); idx++) {
             int nid;
             ext = sk_X509_EXTENSION_value(exts, idx);
-            nid = OBJ_obj2nid(ext->object);
+            nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext));
             if (nid == NID_freshest_crl)
                 crl->flags |= EXFLAG_FRESHEST;
-            if (ext->critical > 0) {
+            if (X509_EXTENSION_get_critical(ext)) {
                 /* We handle IDP and deltas */
                 if ((nid == NID_issuing_distribution_point)
                     || (nid == NID_authority_key_identifier)
@@ -436,6 +438,11 @@
 
 static int def_crl_verify(X509_CRL *crl, EVP_PKEY *r)
 {
+    if (X509_ALGOR_cmp(crl->sig_alg, crl->crl->sig_alg) != 0) {
+        OPENSSL_PUT_ERROR(X509, X509_R_SIGNATURE_ALGORITHM_MISMATCH);
+        return 0;
+    }
+
     return (ASN1_item_verify(ASN1_ITEM_rptr(X509_CRL_INFO),
                              crl->sig_alg, crl->signature, crl->crl, r));
 }
@@ -556,7 +563,3 @@
 {
     return crl->meth_data;
 }
-
-IMPLEMENT_ASN1_SET_OF(X509_REVOKED)
-
-IMPLEMENT_ASN1_SET_OF(X509_CRL)
diff --git a/deps/boringssl/src/crypto/x509/x_exten.c b/deps/boringssl/src/crypto/x509/x_exten.c
index 36403e4..89998ca 100644
--- a/deps/boringssl/src/crypto/x509/x_exten.c
+++ b/deps/boringssl/src/crypto/x509/x_exten.c
@@ -59,6 +59,8 @@
 #include <openssl/cipher.h>
 #include <openssl/x509.h>
 
+#include "internal.h"
+
 
 ASN1_SEQUENCE(X509_EXTENSION) = {
         ASN1_SIMPLE(X509_EXTENSION, object, ASN1_OBJECT),
diff --git a/deps/boringssl/src/crypto/x509/x_name.c b/deps/boringssl/src/crypto/x509/x_name.c
index bef9ec4..e4b5835 100644
--- a/deps/boringssl/src/crypto/x509/x_name.c
+++ b/deps/boringssl/src/crypto/x509/x_name.c
@@ -66,8 +66,9 @@
 #include <openssl/stack.h>
 #include <openssl/x509.h>
 
-#include "../asn1/asn1_locl.h"
+#include "../asn1/internal.h"
 #include "../internal.h"
+#include "internal.h"
 
 
 typedef STACK_OF(X509_NAME_ENTRY) STACK_OF_X509_NAME_ENTRY;
@@ -521,8 +522,6 @@
     return 1;
 }
 
-IMPLEMENT_ASN1_SET_OF(X509_NAME_ENTRY)
-
 int X509_NAME_ENTRY_set(const X509_NAME_ENTRY *ne)
 {
     return ne->set;
diff --git a/deps/boringssl/src/crypto/x509/x_pubkey.c b/deps/boringssl/src/crypto/x509/x_pubkey.c
index 37dee49..c283e0d 100644
--- a/deps/boringssl/src/crypto/x509/x_pubkey.c
+++ b/deps/boringssl/src/crypto/x509/x_pubkey.c
@@ -68,6 +68,7 @@
 #include <openssl/thread.h>
 
 #include "../internal.h"
+#include "internal.h"
 
 /* Minor tweak to operation: free up EVP_PKEY */
 static int pubkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
@@ -180,35 +181,37 @@
     return NULL;
 }
 
-int X509_PUBKEY_set0_param(X509_PUBKEY *pub, const ASN1_OBJECT *aobj,
-                           int ptype, void *pval,
-                           unsigned char *penc, int penclen)
+int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *obj, int param_type,
+                           void *param_value, uint8_t *key, int key_len)
 {
-    if (!X509_ALGOR_set0(pub->algor, aobj, ptype, pval))
+    if (!X509_ALGOR_set0(pub->algor, obj, param_type, param_value)) {
         return 0;
-    if (penc) {
-        if (pub->public_key->data)
-            OPENSSL_free(pub->public_key->data);
-        pub->public_key->data = penc;
-        pub->public_key->length = penclen;
-        /* Set number of unused bits to zero */
-        pub->public_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
-        pub->public_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+    }
+
+    ASN1_STRING_set0(pub->public_key, key, key_len);
+    /* Set the number of unused bits to zero. */
+    pub->public_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+    pub->public_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+    return 1;
+}
+
+int X509_PUBKEY_get0_param(ASN1_OBJECT **out_obj, const uint8_t **out_key,
+                           int *out_key_len, X509_ALGOR **out_alg,
+                           X509_PUBKEY *pub)
+{
+    if (out_obj != NULL) {
+        *out_obj = pub->algor->algorithm;
+    }
+    if (out_key != NULL) {
+        *out_key = pub->public_key->data;
+        *out_key_len = pub->public_key->length;
+    }
+    if (out_alg != NULL) {
+        *out_alg = pub->algor;
     }
     return 1;
 }
 
-int X509_PUBKEY_get0_param(ASN1_OBJECT **ppkalg,
-                           const unsigned char **pk, int *ppklen,
-                           X509_ALGOR **pa, X509_PUBKEY *pub)
-{
-    if (ppkalg)
-        *ppkalg = pub->algor->algorithm;
-    if (pk) {
-        *pk = pub->public_key->data;
-        *ppklen = pub->public_key->length;
-    }
-    if (pa)
-        *pa = pub->algor;
-    return 1;
+const ASN1_BIT_STRING *X509_PUBKEY_get0_public_key(const X509_PUBKEY *pub) {
+    return pub->public_key;
 }
diff --git a/deps/boringssl/src/crypto/x509/x_req.c b/deps/boringssl/src/crypto/x509/x_req.c
index 5dfe19e..0e9dce1 100644
--- a/deps/boringssl/src/crypto/x509/x_req.c
+++ b/deps/boringssl/src/crypto/x509/x_req.c
@@ -60,17 +60,16 @@
 #include <openssl/thread.h>
 #include <openssl/x509.h>
 
+#include "internal.h"
+
+
 /*
  * X509_REQ_INFO is handled in an unusual way to get round invalid encodings.
  * Some broken certificate requests don't encode the attributes field if it
  * is empty. This is in violation of PKCS#10 but we need to tolerate it. We
  * do this by making the attributes field OPTIONAL then using the callback to
  * initialise it to an empty STACK. This means that the field will be
- * correctly encoded unless we NULL out the field. As a result we no longer
- * need the req_kludge field because the information is now contained in the
- * attributes field: 1. If it is NULL then it's the invalid omission. 2. If
- * it is empty it is the correct encoding. 3. If it is not empty then some
- * attributes are present.
+ * correctly encoded unless we NULL out the field.
  */
 
 static int rinf_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
@@ -90,9 +89,7 @@
         ASN1_SIMPLE(X509_REQ_INFO, version, ASN1_INTEGER),
         ASN1_SIMPLE(X509_REQ_INFO, subject, X509_NAME),
         ASN1_SIMPLE(X509_REQ_INFO, pubkey, X509_PUBKEY),
-        /* This isn't really OPTIONAL but it gets round invalid
-         * encodings
-         */
+        /* This isn't really OPTIONAL but it gets around invalid encodings. */
         ASN1_IMP_SET_OF_OPT(X509_REQ_INFO, attributes, X509_ATTRIBUTE, 0)
 } ASN1_SEQUENCE_END_enc(X509_REQ_INFO, X509_REQ_INFO)
 
diff --git a/deps/boringssl/src/crypto/x509/x_sig.c b/deps/boringssl/src/crypto/x509/x_sig.c
index ca08c64..8f9a5b7 100644
--- a/deps/boringssl/src/crypto/x509/x_sig.c
+++ b/deps/boringssl/src/crypto/x509/x_sig.c
@@ -61,6 +61,11 @@
 #include <openssl/x509.h>
 
 
+struct X509_sig_st {
+  X509_ALGOR *algor;
+  ASN1_OCTET_STRING *digest;
+} /* X509_SIG */;
+
 ASN1_SEQUENCE(X509_SIG) = {
         ASN1_SIMPLE(X509_SIG, algor, X509_ALGOR),
         ASN1_SIMPLE(X509_SIG, digest, ASN1_OCTET_STRING)
diff --git a/deps/boringssl/src/crypto/x509/x_val.c b/deps/boringssl/src/crypto/x509/x_val.c
index ad4f7e1..006c53b 100644
--- a/deps/boringssl/src/crypto/x509/x_val.c
+++ b/deps/boringssl/src/crypto/x509/x_val.c
@@ -60,6 +60,8 @@
 #include <openssl/asn1t.h>
 #include <openssl/x509.h>
 
+#include "internal.h"
+
 
 ASN1_SEQUENCE(X509_VAL) = {
         ASN1_SIMPLE(X509_VAL, notBefore, ASN1_TIME),
diff --git a/deps/boringssl/src/crypto/x509/x_x509.c b/deps/boringssl/src/crypto/x509/x_x509.c
index ff0bff8..38cceb1 100644
--- a/deps/boringssl/src/crypto/x509/x_x509.c
+++ b/deps/boringssl/src/crypto/x509/x_x509.c
@@ -69,6 +69,7 @@
 #include <openssl/x509v3.h>
 
 #include "../internal.h"
+#include "internal.h"
 
 static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
 
@@ -128,14 +129,14 @@
             }
         }
 
-        /* Per RFC5280, section 4.1.2.8, these fields require v2 or v3. */
+        /* Per RFC 5280, section 4.1.2.8, these fields require v2 or v3. */
         if (version == 0 && (ret->cert_info->issuerUID != NULL ||
                              ret->cert_info->subjectUID != NULL)) {
             OPENSSL_PUT_ERROR(X509, X509_R_INVALID_FIELD_FOR_VERSION);
             return 0;
         }
 
-        /* Per RFC5280, section 4.1.2.9, extensions require v3. */
+        /* Per RFC 5280, section 4.1.2.9, extensions require v3. */
         if (version != 2 && ret->cert_info->extensions != NULL) {
             OPENSSL_PUT_ERROR(X509, X509_R_INVALID_FIELD_FOR_VERSION);
             return 0;
diff --git a/deps/boringssl/src/crypto/x509/x_x509a.c b/deps/boringssl/src/crypto/x509/x_x509a.c
index 823fa5c..fca02a6 100644
--- a/deps/boringssl/src/crypto/x509/x_x509a.c
+++ b/deps/boringssl/src/crypto/x509/x_x509a.c
@@ -61,6 +61,9 @@
 #include <openssl/obj.h>
 #include <openssl/x509.h>
 
+#include "internal.h"
+
+
 /*
  * X509_CERT_AUX routines. These are used to encode additional user
  * modifiable data about a certificate. This data is appended to the X509
diff --git a/deps/boringssl/src/crypto/x509v3/internal.h b/deps/boringssl/src/crypto/x509v3/internal.h
index 245a5d1..df7d813 100644
--- a/deps/boringssl/src/crypto/x509v3/internal.h
+++ b/deps/boringssl/src/crypto/x509v3/internal.h
@@ -17,6 +17,8 @@
 
 #include <openssl/base.h>
 
+#include <openssl/conf.h>
+
 #if defined(__cplusplus)
 extern "C" {
 #endif
@@ -53,6 +55,27 @@
 // invalid.
 int x509v3_cache_extensions(X509 *x);
 
+// x509v3_a2i_ipadd decodes |ipasc| as an IPv4 or IPv6 address. IPv6 addresses
+// use colon-separated syntax while IPv4 addresses use dotted decimal syntax. If
+// it decodes an IPv4 address, it writes the result to the first four bytes of
+// |ipout| and returns four. If it decodes an IPv6 address, it writes the result
+// to all 16 bytes of |ipout| and returns 16. Otherwise, it returns zero.
+int x509v3_a2i_ipadd(unsigned char ipout[16], const char *ipasc);
+
+// A |BIT_STRING_BITNAME| is used to contain a list of bit names.
+typedef struct {
+  int bitnum;
+  const char *lname;
+  const char *sname;
+} BIT_STRING_BITNAME;
+
+// x509V3_add_value_asn1_string appends a |CONF_VALUE| with the specified name
+// and value to |*extlist|. if |*extlist| is NULL, it sets |*extlist| to a
+// newly-allocated |STACK_OF(CONF_VALUE)| first. It returns one on success and
+// zero on error.
+int x509V3_add_value_asn1_string(const char *name, const ASN1_STRING *value,
+                                 STACK_OF(CONF_VALUE) **extlist);
+
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/deps/boringssl/src/crypto/x509v3/pcy_cache.c b/deps/boringssl/src/crypto/x509v3/pcy_cache.c
index 755c079..1caea76 100644
--- a/deps/boringssl/src/crypto/x509v3/pcy_cache.c
+++ b/deps/boringssl/src/crypto/x509v3/pcy_cache.c
@@ -62,6 +62,7 @@
 
 #include "pcy_int.h"
 #include "../internal.h"
+#include "../x509/internal.h"
 
 static int policy_data_cmp(const X509_POLICY_DATA **a,
                            const X509_POLICY_DATA **b);
diff --git a/deps/boringssl/src/crypto/x509v3/pcy_data.c b/deps/boringssl/src/crypto/x509v3/pcy_data.c
index 58584c2..c4a56ca 100644
--- a/deps/boringssl/src/crypto/x509v3/pcy_data.c
+++ b/deps/boringssl/src/crypto/x509v3/pcy_data.c
@@ -79,7 +79,7 @@
 /*
  * Create a data based on an existing policy. If 'id' is NULL use the oid in
  * the policy, otherwise use 'id'. This behaviour covers the two types of
- * data in RFC3280: data with from a CertificatePolcies extension and
+ * data in RFC 3280: data with from a CertificatePolcies extension and
  * additional data with just the qualifiers of anyPolicy and ID from another
  * source.
  */
diff --git a/deps/boringssl/src/crypto/x509v3/pcy_int.h b/deps/boringssl/src/crypto/x509v3/pcy_int.h
index fc6e20a..aee71d6 100644
--- a/deps/boringssl/src/crypto/x509v3/pcy_int.h
+++ b/deps/boringssl/src/crypto/x509v3/pcy_int.h
@@ -65,7 +65,7 @@
 
 /*
  * This structure and the field names correspond to the Policy 'node' of
- * RFC3280. NB this structure contains no pointers to parent or child data:
+ * RFC 3280. NB this structure contains no pointers to parent or child data:
  * X509_POLICY_NODE contains that. This means that the main policy data can
  * be kept static and cached with the certificate.
  */
diff --git a/deps/boringssl/src/crypto/x509v3/pcy_map.c b/deps/boringssl/src/crypto/x509v3/pcy_map.c
index 7263c69..a4a3601 100644
--- a/deps/boringssl/src/crypto/x509v3/pcy_map.c
+++ b/deps/boringssl/src/crypto/x509v3/pcy_map.c
@@ -62,6 +62,7 @@
 #include <openssl/x509v3.h>
 
 #include "pcy_int.h"
+#include "../x509/internal.h"
 
 /*
  * Set policy mapping entries in cache. Note: this modifies the passed
diff --git a/deps/boringssl/src/crypto/x509v3/pcy_tree.c b/deps/boringssl/src/crypto/x509v3/pcy_tree.c
index 136b45f..596266b 100644
--- a/deps/boringssl/src/crypto/x509v3/pcy_tree.c
+++ b/deps/boringssl/src/crypto/x509v3/pcy_tree.c
@@ -67,6 +67,7 @@
 
 #include "pcy_int.h"
 #include "../internal.h"
+#include "../x509/internal.h"
 
 /*
  * Enable this to print out the complete policy tree at various point during
@@ -332,7 +333,7 @@
 }
 
 /*
- * This corresponds to RFC3280 6.1.3(d)(1): link any data from
+ * This corresponds to RFC 3280 6.1.3(d)(1): link any data from
  * CertificatePolicies onto matching parent or anyPolicy if no match.
  */
 
@@ -365,7 +366,7 @@
 }
 
 /*
- * This corresponds to RFC3280 6.1.3(d)(2): Create new data for any unmatched
+ * This corresponds to RFC 3280 6.1.3(d)(2): Create new data for any unmatched
  * policies in the parent and link to anyPolicy.
  */
 
@@ -500,7 +501,7 @@
     if (curr->flags & X509_V_FLAG_INHIBIT_MAP) {
         for (i = sk_X509_POLICY_NODE_num(nodes) - 1; i >= 0; i--) {
             node = sk_X509_POLICY_NODE_value(nodes, i);
-            /* Delete any mapped data: see RFC3280 XXXX */
+            /* Delete any mapped data: see RFC 3280 XXXX */
             if (node->data->flags & POLICY_DATA_FLAG_MAP_MASK) {
                 node->parent->nchild--;
                 OPENSSL_free(node);
diff --git a/deps/boringssl/src/crypto/x509v3/v3_akey.c b/deps/boringssl/src/crypto/x509v3/v3_akey.c
index 1037673..0aba20e 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_akey.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_akey.c
@@ -93,20 +93,39 @@
                                                  STACK_OF(CONF_VALUE)
                                                  *extlist)
 {
-    char *tmp;
+    char *tmp = NULL;
+    int extlist_was_null = extlist == NULL;
     if (akeyid->keyid) {
         tmp = x509v3_bytes_to_hex(akeyid->keyid->data, akeyid->keyid->length);
-        X509V3_add_value("keyid", tmp, &extlist);
+        int ok = tmp != NULL && X509V3_add_value("keyid", tmp, &extlist);
         OPENSSL_free(tmp);
+        if (!ok) {
+            goto err;
+        }
     }
-    if (akeyid->issuer)
-        extlist = i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist);
+    if (akeyid->issuer) {
+        STACK_OF(CONF_VALUE) *tmpextlist =
+            i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist);
+        if (tmpextlist == NULL) {
+            goto err;
+        }
+        extlist = tmpextlist;
+    }
     if (akeyid->serial) {
         tmp = x509v3_bytes_to_hex(akeyid->serial->data, akeyid->serial->length);
-        X509V3_add_value("serial", tmp, &extlist);
+        int ok = tmp != NULL && X509V3_add_value("serial", tmp, &extlist);
         OPENSSL_free(tmp);
+        if (!ok) {
+            goto err;
+        }
     }
     return extlist;
+
+err:
+    if (extlist_was_null) {
+        sk_CONF_VALUE_pop_free(extlist, X509V3_conf_free);
+    }
+    return NULL;
 }
 
 /*
diff --git a/deps/boringssl/src/crypto/x509v3/v3_alt.c b/deps/boringssl/src/crypto/x509v3/v3_alt.c
index 4d54075..0c55816 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_alt.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_alt.c
@@ -104,11 +104,17 @@
                                         GENERAL_NAMES *gens,
                                         STACK_OF(CONF_VALUE) *ret)
 {
-    size_t i;
-    GENERAL_NAME *gen;
-    for (i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
-        gen = sk_GENERAL_NAME_value(gens, i);
-        ret = i2v_GENERAL_NAME(method, gen, ret);
+    int ret_was_null = ret == NULL;
+    for (size_t i = 0; i < sk_GENERAL_NAME_num(gens); i++) {
+        GENERAL_NAME *gen = sk_GENERAL_NAME_value(gens, i);
+        STACK_OF(CONF_VALUE) *tmp = i2v_GENERAL_NAME(method, gen, ret);
+        if (tmp == NULL) {
+            if (ret_was_null) {
+                sk_CONF_VALUE_pop_free(ret, X509V3_conf_free);
+            }
+            return NULL;
+        }
+        ret = tmp;
     }
     if (!ret)
         return sk_CONF_VALUE_new_null();
@@ -119,6 +125,9 @@
                                        GENERAL_NAME *gen,
                                        STACK_OF(CONF_VALUE) *ret)
 {
+    /* Note the error-handling for this function relies on there being at most
+     * one |X509V3_add_value| call. If there were two and the second failed, we
+     * would need to sometimes free the first call's result. */
     unsigned char *p;
     char oline[256], htmp[5];
     int i;
@@ -139,17 +148,17 @@
         break;
 
     case GEN_EMAIL:
-        if (!X509V3_add_value_uchar("email", gen->d.ia5->data, &ret))
+        if (!x509V3_add_value_asn1_string("email", gen->d.ia5, &ret))
             return NULL;
         break;
 
     case GEN_DNS:
-        if (!X509V3_add_value_uchar("DNS", gen->d.ia5->data, &ret))
+        if (!x509V3_add_value_asn1_string("DNS", gen->d.ia5, &ret))
             return NULL;
         break;
 
     case GEN_URI:
-        if (!X509V3_add_value_uchar("URI", gen->d.ia5->data, &ret))
+        if (!x509V3_add_value_asn1_string("URI", gen->d.ia5, &ret))
             return NULL;
         break;
 
diff --git a/deps/boringssl/src/crypto/x509v3/v3_bitst.c b/deps/boringssl/src/crypto/x509v3/v3_bitst.c
index 402f830..871b776 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_bitst.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_bitst.c
@@ -63,6 +63,9 @@
 #include <openssl/obj.h>
 #include <openssl/x509v3.h>
 
+#include "internal.h"
+
+
 static const BIT_STRING_BITNAME ns_cert_type_table[] = {
     {0, "SSL Client", "client"},
     {1, "SSL Server", "server"},
diff --git a/deps/boringssl/src/crypto/x509v3/v3_conf.c b/deps/boringssl/src/crypto/x509v3/v3_conf.c
index 158f8df..3192752 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_conf.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_conf.c
@@ -69,6 +69,7 @@
 #include <openssl/x509v3.h>
 
 #include "../internal.h"
+#include "../x509/internal.h"
 #include "internal.h"
 
 static int v3_check_critical(const char **value);
diff --git a/deps/boringssl/src/crypto/x509v3/v3_cpols.c b/deps/boringssl/src/crypto/x509v3/v3_cpols.c
index 216e7ae..9f66f47 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_cpols.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_cpols.c
@@ -239,8 +239,7 @@
                 goto merr;
             if (!sk_POLICYQUALINFO_push(pol->qualifiers, qual))
                 goto merr;
-            /* TODO(fork): const correctness */
-            qual->pqualid = (ASN1_OBJECT *)OBJ_nid2obj(NID_id_qt_cps);
+            qual->pqualid = OBJ_nid2obj(NID_id_qt_cps);
             if (qual->pqualid == NULL) {
                 OPENSSL_PUT_ERROR(X509V3, ERR_R_INTERNAL_ERROR);
                 goto err;
@@ -307,8 +306,7 @@
     POLICYQUALINFO *qual;
     if (!(qual = POLICYQUALINFO_new()))
         goto merr;
-    /* TODO(fork): const correctness */
-    qual->pqualid = (ASN1_OBJECT *)OBJ_nid2obj(NID_id_qt_unotice);
+    qual->pqualid = OBJ_nid2obj(NID_id_qt_unotice);
     if (qual->pqualid == NULL) {
         OPENSSL_PUT_ERROR(X509V3, ERR_R_INTERNAL_ERROR);
         goto err;
@@ -434,8 +432,8 @@
         qualinfo = sk_POLICYQUALINFO_value(quals, i);
         switch (OBJ_obj2nid(qualinfo->pqualid)) {
         case NID_id_qt_cps:
-            BIO_printf(out, "%*sCPS: %s\n", indent, "",
-                       qualinfo->d.cpsuri->data);
+            BIO_printf(out, "%*sCPS: %.*s\n", indent, "",
+                       qualinfo->d.cpsuri->length, qualinfo->d.cpsuri->data);
             break;
 
         case NID_id_qt_unotice:
@@ -459,8 +457,8 @@
     if (notice->noticeref) {
         NOTICEREF *ref;
         ref = notice->noticeref;
-        BIO_printf(out, "%*sOrganization: %s\n", indent, "",
-                   ref->organization->data);
+        BIO_printf(out, "%*sOrganization: %.*s\n", indent, "",
+                   ref->organization->length, ref->organization->data);
         BIO_printf(out, "%*sNumber%s: ", indent, "",
                    sk_ASN1_INTEGER_num(ref->noticenos) > 1 ? "s" : "");
         for (i = 0; i < sk_ASN1_INTEGER_num(ref->noticenos); i++) {
@@ -482,8 +480,8 @@
         BIO_puts(out, "\n");
     }
     if (notice->exptext)
-        BIO_printf(out, "%*sExplicit Text: %s\n", indent, "",
-                   notice->exptext->data);
+        BIO_printf(out, "%*sExplicit Text: %.*s\n", indent, "",
+                   notice->exptext->length, notice->exptext->data);
 }
 
 void X509_POLICY_NODE_print(BIO *out, X509_POLICY_NODE *node, int indent)
diff --git a/deps/boringssl/src/crypto/x509v3/v3_crld.c b/deps/boringssl/src/crypto/x509v3/v3_crld.c
index c93c449..93e5b6d 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_crld.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_crld.c
@@ -66,6 +66,10 @@
 #include <openssl/obj.h>
 #include <openssl/x509v3.h>
 
+#include "internal.h"
+#include "../x509/internal.h"
+
+
 static void *v2i_crld(const X509V3_EXT_METHOD *method,
                       X509V3_CTX *ctx, STACK_OF(CONF_VALUE) *nval);
 static int i2r_crldp(const X509V3_EXT_METHOD *method, void *pcrldp, BIO *out,
@@ -341,8 +345,6 @@
     return NULL;
 }
 
-IMPLEMENT_ASN1_SET_OF(DIST_POINT)
-
 static int dpn_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
                   void *exarg)
 {
diff --git a/deps/boringssl/src/crypto/x509v3/v3_enum.c b/deps/boringssl/src/crypto/x509v3/v3_enum.c
index 3a9d4d6..9b222bb 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_enum.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_enum.c
@@ -61,6 +61,11 @@
 #include <openssl/mem.h>
 #include <openssl/x509v3.h>
 
+#include "internal.h"
+
+
+typedef BIT_STRING_BITNAME ENUMERATED_NAMES;
+
 static const ENUMERATED_NAMES crl_reasons[] = {
     {CRL_REASON_UNSPECIFIED, "Unspecified", "unspecified"},
     {CRL_REASON_KEY_COMPROMISE, "Key Compromise", "keyCompromise"},
diff --git a/deps/boringssl/src/crypto/x509v3/v3_lib.c b/deps/boringssl/src/crypto/x509v3/v3_lib.c
index d89733f..3fb0285 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_lib.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_lib.c
@@ -66,6 +66,8 @@
 #include <openssl/obj.h>
 #include <openssl/x509v3.h>
 
+#include "../x509/internal.h"
+
 #include "ext_dat.h"
 static STACK_OF(X509V3_EXT_METHOD) *ext_list = NULL;
 
diff --git a/deps/boringssl/src/crypto/x509v3/v3_ncons.c b/deps/boringssl/src/crypto/x509v3/v3_ncons.c
index 593a520..739a59e 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_ncons.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_ncons.c
@@ -66,6 +66,7 @@
 #include <openssl/x509v3.h>
 
 #include "../internal.h"
+#include "../x509/internal.h"
 
 
 static void *v2i_NAME_CONSTRAINTS(const X509V3_EXT_METHOD *method,
@@ -389,25 +390,73 @@
     return X509_V_OK;
 }
 
+static int starts_with(const CBS *cbs, uint8_t c)
+{
+    return CBS_len(cbs) > 0 && CBS_data(cbs)[0] == c;
+}
+
+static int equal_case(const CBS *a, const CBS *b)
+{
+    if (CBS_len(a) != CBS_len(b)) {
+        return 0;
+    }
+    /* Note we cannot use |OPENSSL_strncasecmp| because that would stop
+     * iterating at NUL. */
+    const uint8_t *a_data = CBS_data(a), *b_data = CBS_data(b);
+    for (size_t i = 0; i < CBS_len(a); i++) {
+        if (OPENSSL_tolower(a_data[i]) != OPENSSL_tolower(b_data[i])) {
+            return 0;
+        }
+    }
+    return 1;
+}
+
+static int has_suffix_case(const CBS *a, const CBS *b)
+{
+    if (CBS_len(a) < CBS_len(b)) {
+        return 0;
+    }
+    CBS copy = *a;
+    CBS_skip(&copy, CBS_len(a) - CBS_len(b));
+    return equal_case(&copy, b);
+}
+
 static int nc_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base)
 {
-    char *baseptr = (char *)base->data;
-    char *dnsptr = (char *)dns->data;
+    CBS dns_cbs, base_cbs;
+    CBS_init(&dns_cbs, dns->data, dns->length);
+    CBS_init(&base_cbs, base->data, base->length);
+
     /* Empty matches everything */
-    if (!*baseptr)
+    if (CBS_len(&base_cbs) == 0) {
         return X509_V_OK;
+    }
+
+    /* If |base_cbs| begins with a '.', do a simple suffix comparison. This is
+     * not part of RFC5280, but is part of OpenSSL's original behavior. */
+    if (starts_with(&base_cbs, '.')) {
+        if (has_suffix_case(&dns_cbs, &base_cbs)) {
+            return X509_V_OK;
+        }
+        return X509_V_ERR_PERMITTED_VIOLATION;
+    }
+
     /*
      * Otherwise can add zero or more components on the left so compare RHS
      * and if dns is longer and expect '.' as preceding character.
      */
-    if (dns->length > base->length) {
-        dnsptr += dns->length - base->length;
-        if (*baseptr != '.' && dnsptr[-1] != '.')
+    if (CBS_len(&dns_cbs) > CBS_len(&base_cbs)) {
+        uint8_t dot;
+        if (!CBS_skip(&dns_cbs, CBS_len(&dns_cbs) - CBS_len(&base_cbs) - 1) ||
+            !CBS_get_u8(&dns_cbs, &dot) ||
+            dot != '.') {
             return X509_V_ERR_PERMITTED_VIOLATION;
+        }
     }
 
-    if (OPENSSL_strcasecmp(baseptr, dnsptr))
+    if (!equal_case(&dns_cbs, &base_cbs)) {
         return X509_V_ERR_PERMITTED_VIOLATION;
+    }
 
     return X509_V_OK;
 
@@ -415,86 +464,94 @@
 
 static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base)
 {
-    const char *baseptr = (char *)base->data;
-    const char *emlptr = (char *)eml->data;
+    CBS eml_cbs, base_cbs;
+    CBS_init(&eml_cbs, eml->data, eml->length);
+    CBS_init(&base_cbs, base->data, base->length);
 
-    const char *baseat = strchr(baseptr, '@');
-    const char *emlat = strchr(emlptr, '@');
-    if (!emlat)
+    /* TODO(davidben): In OpenSSL 1.1.1, this switched from the first '@' to the
+     * last one. Match them here, or perhaps do an actual parse. Looks like
+     * multiple '@'s may be allowed in quoted strings. */
+    CBS eml_local, base_local;
+    if (!CBS_get_until_first(&eml_cbs, &eml_local, '@')) {
         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
+    }
+    int base_has_at = CBS_get_until_first(&base_cbs, &base_local, '@');
+
     /* Special case: inital '.' is RHS match */
-    if (!baseat && (*baseptr == '.')) {
-        if (eml->length > base->length) {
-            emlptr += eml->length - base->length;
-            if (!OPENSSL_strcasecmp(baseptr, emlptr))
-                return X509_V_OK;
+    if (!base_has_at && starts_with(&base_cbs, '.')) {
+        if (has_suffix_case(&eml_cbs, &base_cbs)) {
+            return X509_V_OK;
         }
         return X509_V_ERR_PERMITTED_VIOLATION;
     }
 
     /* If we have anything before '@' match local part */
-
-    if (baseat) {
-        if (baseat != baseptr) {
-            if ((baseat - baseptr) != (emlat - emlptr))
-                return X509_V_ERR_PERMITTED_VIOLATION;
+    if (base_has_at) {
+        /* TODO(davidben): This interprets a constraint of "@example.com" as
+         * "example.com", which is not part of RFC5280. */
+        if (CBS_len(&base_local) > 0) {
             /* Case sensitive match of local part */
-            if (strncmp(baseptr, emlptr, emlat - emlptr))
+            if (!CBS_mem_equal(&base_local, CBS_data(&eml_local),
+                               CBS_len(&eml_local))) {
                 return X509_V_ERR_PERMITTED_VIOLATION;
+            }
         }
         /* Position base after '@' */
-        baseptr = baseat + 1;
+        assert(starts_with(&base_cbs, '@'));
+        CBS_skip(&base_cbs, 1);
     }
-    emlptr = emlat + 1;
+
     /* Just have hostname left to match: case insensitive */
-    if (OPENSSL_strcasecmp(baseptr, emlptr))
+    assert(starts_with(&eml_cbs, '@'));
+    CBS_skip(&eml_cbs, 1);
+    if (!equal_case(&base_cbs, &eml_cbs)) {
         return X509_V_ERR_PERMITTED_VIOLATION;
+    }
 
     return X509_V_OK;
-
 }
 
 static int nc_uri(ASN1_IA5STRING *uri, ASN1_IA5STRING *base)
 {
-    const char *baseptr = (char *)base->data;
-    const char *hostptr = (char *)uri->data;
-    const char *p = strchr(hostptr, ':');
-    int hostlen;
+    CBS uri_cbs, base_cbs;
+    CBS_init(&uri_cbs, uri->data, uri->length);
+    CBS_init(&base_cbs, base->data, base->length);
+
     /* Check for foo:// and skip past it */
-    if (!p || (p[1] != '/') || (p[2] != '/'))
+    CBS scheme;
+    uint8_t byte;
+    if (!CBS_get_until_first(&uri_cbs, &scheme, ':') ||
+        !CBS_skip(&uri_cbs, 1) ||  // Skip the colon
+        !CBS_get_u8(&uri_cbs, &byte) || byte != '/' ||
+        !CBS_get_u8(&uri_cbs, &byte) || byte != '/') {
         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
-    hostptr = p + 3;
+    }
 
-    /* Determine length of hostname part of URI */
+    /* Look for a port indicator as end of hostname first. Otherwise look for
+     * trailing slash, or the end of the string.
+     * TODO(davidben): This is not a correct URI parser and mishandles IPv6
+     * literals. */
+    CBS host;
+    if (!CBS_get_until_first(&uri_cbs, &host, ':') &&
+        !CBS_get_until_first(&uri_cbs, &host, '/')) {
+        host = uri_cbs;
+    }
 
-    /* Look for a port indicator as end of hostname first */
-
-    p = strchr(hostptr, ':');
-    /* Otherwise look for trailing slash */
-    if (!p)
-        p = strchr(hostptr, '/');
-
-    if (!p)
-        hostlen = strlen(hostptr);
-    else
-        hostlen = p - hostptr;
-
-    if (hostlen == 0)
+    if (CBS_len(&host) == 0) {
         return X509_V_ERR_UNSUPPORTED_NAME_SYNTAX;
+    }
 
     /* Special case: inital '.' is RHS match */
-    if (*baseptr == '.') {
-        if (hostlen > base->length) {
-            p = hostptr + hostlen - base->length;
-            if (!OPENSSL_strncasecmp(p, baseptr, base->length))
-                return X509_V_OK;
+    if (starts_with(&base_cbs, '.')) {
+        if (has_suffix_case(&host, &base_cbs)) {
+            return X509_V_OK;
         }
         return X509_V_ERR_PERMITTED_VIOLATION;
     }
 
-    if ((base->length != (int)hostlen)
-        || OPENSSL_strncasecmp(hostptr, baseptr, hostlen))
+    if (!equal_case(&base_cbs, &host)) {
         return X509_V_ERR_PERMITTED_VIOLATION;
+    }
 
     return X509_V_OK;
 
diff --git a/deps/boringssl/src/crypto/x509v3/v3_pci.c b/deps/boringssl/src/crypto/x509v3/v3_pci.c
index f9031c0..57b64ef 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_pci.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_pci.c
@@ -75,7 +75,8 @@
     i2a_ASN1_OBJECT(out, pci->proxyPolicy->policyLanguage);
     BIO_puts(out, "\n");
     if (pci->proxyPolicy->policy && pci->proxyPolicy->policy->data)
-        BIO_printf(out, "%*sPolicy Text: %s\n", indent, "",
+        BIO_printf(out, "%*sPolicy Text: %.*s\n", indent, "",
+                   pci->proxyPolicy->policy->length,
                    pci->proxyPolicy->policy->data);
     return 1;
 }
diff --git a/deps/boringssl/src/crypto/x509v3/v3_prn.c b/deps/boringssl/src/crypto/x509v3/v3_prn.c
index f6f341a..ee4c482 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_prn.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_prn.c
@@ -107,20 +107,20 @@
 {
     void *ext_str = NULL;
     char *value = NULL;
-    const unsigned char *p;
     const X509V3_EXT_METHOD *method;
     STACK_OF(CONF_VALUE) *nval = NULL;
     int ok = 1;
 
     if (!(method = X509V3_EXT_get(ext)))
         return unknown_ext_print(out, ext, flag, indent, 0);
-    p = ext->value->data;
-    if (method->it)
-        ext_str =
-            ASN1_item_d2i(NULL, &p, ext->value->length,
-                          ASN1_ITEM_ptr(method->it));
-    else
-        ext_str = method->d2i(NULL, &p, ext->value->length);
+    const ASN1_STRING *ext_data = X509_EXTENSION_get_data(ext);
+    const unsigned char *p = ASN1_STRING_get0_data(ext_data);
+    if (method->it) {
+        ext_str = ASN1_item_d2i(NULL, &p, ASN1_STRING_length(ext_data),
+                                ASN1_ITEM_ptr(method->it));
+    } else {
+        ext_str = method->d2i(NULL, &p, ASN1_STRING_length(ext_data));
+    }
 
     if (!ext_str)
         return unknown_ext_print(out, ext, flag, indent, 1);
@@ -183,7 +183,7 @@
             return 0;
         if (!X509V3_EXT_print(bp, ex, flag, indent + 4)) {
             BIO_printf(bp, "%*s", indent + 4, "");
-            ASN1_STRING_print(bp, ex->value);
+            ASN1_STRING_print(bp, X509_EXTENSION_get_data(ex));
         }
         if (BIO_write(bp, "\n", 1) <= 0)
             return 0;
@@ -207,15 +207,17 @@
         return 1;
 
     case X509V3_EXT_PARSE_UNKNOWN:
-    case X509V3_EXT_DUMP_UNKNOWN:
-        return BIO_hexdump(out, ext->value->data, ext->value->length, indent);
+    case X509V3_EXT_DUMP_UNKNOWN: {
+        const ASN1_STRING *data = X509_EXTENSION_get_data(ext);
+        return BIO_hexdump(out, ASN1_STRING_get0_data(data),
+                           ASN1_STRING_length(data), indent);
+    }
 
     default:
         return 1;
     }
 }
 
-#ifndef OPENSSL_NO_FP_API
 int X509V3_EXT_print_fp(FILE *fp, X509_EXTENSION *ext, int flag, int indent)
 {
     BIO *bio_tmp;
@@ -226,4 +228,3 @@
     BIO_free(bio_tmp);
     return ret;
 }
-#endif
diff --git a/deps/boringssl/src/crypto/x509v3/v3_purp.c b/deps/boringssl/src/crypto/x509v3/v3_purp.c
index acb7602..d1f56f0 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_purp.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_purp.c
@@ -68,6 +68,7 @@
 #include <openssl/x509v3.h>
 
 #include "../internal.h"
+#include "../x509/internal.h"
 #include "internal.h"
 
 #define V1_ROOT (EXFLAG_V1|EXFLAG_SS)
@@ -440,7 +441,7 @@
     if (!X509_digest(x, EVP_sha1(), x->sha1_hash, NULL))
         x->ex_flags |= EXFLAG_INVALID;
     /* V1 should mean no extensions ... */
-    if (!X509_get_version(x))
+    if (X509_get_version(x) == X509_VERSION_1)
         x->ex_flags |= EXFLAG_V1;
     /* Handle basic constraints */
     if ((bs = X509_get_ext_d2i(x, NID_basic_constraints, &j, NULL))) {
diff --git a/deps/boringssl/src/crypto/x509v3/v3_skey.c b/deps/boringssl/src/crypto/x509v3/v3_skey.c
index 140356d..1cae7e1 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_skey.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_skey.c
@@ -63,6 +63,7 @@
 #include <openssl/obj.h>
 #include <openssl/x509v3.h>
 
+#include "../x509/internal.h"
 #include "internal.h"
 
 
diff --git a/deps/boringssl/src/crypto/x509v3/v3_utl.c b/deps/boringssl/src/crypto/x509v3/v3_utl.c
index c0952c0..5d91aed 100644
--- a/deps/boringssl/src/crypto/x509v3/v3_utl.c
+++ b/deps/boringssl/src/crypto/x509v3/v3_utl.c
@@ -81,49 +81,76 @@
 static void str_free(OPENSSL_STRING str);
 static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, ASN1_IA5STRING *email);
 
-static int ipv4_from_asc(unsigned char *v4, const char *in);
-static int ipv6_from_asc(unsigned char *v6, const char *in);
+static int ipv4_from_asc(unsigned char v4[4], const char *in);
+static int ipv6_from_asc(unsigned char v6[16], const char *in);
 static int ipv6_cb(const char *elem, int len, void *usr);
 static int ipv6_hex(unsigned char *out, const char *in, int inlen);
 
 /* Add a CONF_VALUE name value pair to stack */
 
-int X509V3_add_value(const char *name, const char *value,
-                     STACK_OF(CONF_VALUE) **extlist)
+static int x509V3_add_len_value(const char *name, const char *value,
+                                size_t value_len, int omit_value,
+                                STACK_OF(CONF_VALUE) **extlist)
 {
     CONF_VALUE *vtmp = NULL;
     char *tname = NULL, *tvalue = NULL;
+    int extlist_was_null = *extlist == NULL;
     if (name && !(tname = OPENSSL_strdup(name)))
-        goto err;
-    if (value && !(tvalue = OPENSSL_strdup(value)))
-        goto err;
+        goto malloc_err;
+    if (!omit_value) {
+        /* |CONF_VALUE| cannot represent strings with NULs. */
+        if (OPENSSL_memchr(value, 0, value_len)) {
+            OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_VALUE);
+            goto err;
+        }
+        tvalue = OPENSSL_strndup(value, value_len);
+        if (tvalue == NULL) {
+            goto malloc_err;
+        }
+    }
     if (!(vtmp = CONF_VALUE_new()))
-        goto err;
+        goto malloc_err;
     if (!*extlist && !(*extlist = sk_CONF_VALUE_new_null()))
-        goto err;
+        goto malloc_err;
     vtmp->section = NULL;
     vtmp->name = tname;
     vtmp->value = tvalue;
     if (!sk_CONF_VALUE_push(*extlist, vtmp))
-        goto err;
+        goto malloc_err;
     return 1;
- err:
+ malloc_err:
     OPENSSL_PUT_ERROR(X509V3, ERR_R_MALLOC_FAILURE);
-    if (vtmp)
-        OPENSSL_free(vtmp);
-    if (tname)
-        OPENSSL_free(tname);
-    if (tvalue)
-        OPENSSL_free(tvalue);
+ err:
+    if (extlist_was_null) {
+        sk_CONF_VALUE_free(*extlist);
+        *extlist = NULL;
+    }
+    OPENSSL_free(vtmp);
+    OPENSSL_free(tname);
+    OPENSSL_free(tvalue);
     return 0;
 }
 
+int X509V3_add_value(const char *name, const char *value,
+                     STACK_OF(CONF_VALUE) **extlist)
+{
+    return x509V3_add_len_value(name, value, value != NULL ? strlen(value) : 0,
+                                /*omit_value=*/value == NULL, extlist);
+}
+
 int X509V3_add_value_uchar(const char *name, const unsigned char *value,
                            STACK_OF(CONF_VALUE) **extlist)
 {
     return X509V3_add_value(name, (const char *)value, extlist);
 }
 
+int x509V3_add_value_asn1_string(const char *name, const ASN1_STRING *value,
+                                 STACK_OF(CONF_VALUE) **extlist)
+{
+    return x509V3_add_len_value(name, (const char *)value->data, value->length,
+                                /*omit_value=*/0, extlist);
+}
+
 /* Free function for STACK_OF(CONF_VALUE) */
 
 void X509V3_conf_free(CONF_VALUE *conf)
@@ -268,7 +295,7 @@
     return aint;
 }
 
-int X509V3_add_value_int(const char *name, ASN1_INTEGER *aint,
+int X509V3_add_value_int(const char *name, const ASN1_INTEGER *aint,
                          STACK_OF(CONF_VALUE) **extlist)
 {
     char *strtmp;
@@ -631,27 +658,45 @@
 
 static int append_ia5(STACK_OF(OPENSSL_STRING) **sk, ASN1_IA5STRING *email)
 {
-    char *emtmp;
     /* First some sanity checks */
     if (email->type != V_ASN1_IA5STRING)
         return 1;
-    if (!email->data || !email->length)
+    if (email->data == NULL || email->length == 0)
         return 1;
+    /* |OPENSSL_STRING| cannot represent strings with embedded NULs. Do not
+     * report them as outputs. */
+    if (OPENSSL_memchr(email->data, 0, email->length) != NULL)
+        return 1;
+
+    char *emtmp = NULL;
     if (!*sk)
         *sk = sk_OPENSSL_STRING_new(sk_strcmp);
     if (!*sk)
-        return 0;
+        goto err;
+
+    emtmp = OPENSSL_strndup((char *)email->data, email->length);
+    if (emtmp == NULL) {
+        goto err;
+    }
+
     /* Don't add duplicates */
     sk_OPENSSL_STRING_sort(*sk);
-    if (sk_OPENSSL_STRING_find(*sk, NULL, (char *)email->data))
+    if (sk_OPENSSL_STRING_find(*sk, NULL, emtmp)) {
+        OPENSSL_free(emtmp);
         return 1;
-    emtmp = OPENSSL_strdup((char *)email->data);
-    if (!emtmp || !sk_OPENSSL_STRING_push(*sk, emtmp)) {
-        X509_email_free(*sk);
-        *sk = NULL;
-        return 0;
+    }
+    if (!sk_OPENSSL_STRING_push(*sk, emtmp)) {
+        goto err;
     }
     return 1;
+
+err:
+    /* TODO(davidben): Fix the error-handling in this file. It currently relies
+     * on |append_ia5| leaving |*sk| at NULL on error. */
+    OPENSSL_free(emtmp);
+    X509_email_free(*sk);
+    *sk = NULL;
+    return 0;
 }
 
 void X509_email_free(STACK_OF(OPENSSL_STRING) *sk)
@@ -1112,7 +1157,7 @@
 
     if (ipasc == NULL)
         return -2;
-    iplen = (size_t)a2i_ipadd(ipout, ipasc);
+    iplen = (size_t)x509v3_a2i_ipadd(ipout, ipasc);
     if (iplen == 0)
         return -2;
     return do_x509_check(x, (char *)ipout, iplen, flags, GEN_IPADD, NULL);
@@ -1120,7 +1165,7 @@
 
 /*
  * Convert IP addresses both IPv4 and IPv6 into an OCTET STRING compatible
- * with RFC3280.
+ * with RFC 3280.
  */
 
 ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc)
@@ -1129,10 +1174,7 @@
     ASN1_OCTET_STRING *ret;
     int iplen;
 
-    /* If string contains a ':' assume IPv6 */
-
-    iplen = a2i_ipadd(ipout, ipasc);
-
+    iplen = x509v3_a2i_ipadd(ipout, ipasc);
     if (!iplen)
         return NULL;
 
@@ -1161,12 +1203,12 @@
     p = iptmp + (p - ipasc);
     *p++ = 0;
 
-    iplen1 = a2i_ipadd(ipout, iptmp);
+    iplen1 = x509v3_a2i_ipadd(ipout, iptmp);
 
     if (!iplen1)
         goto err;
 
-    iplen2 = a2i_ipadd(ipout + iplen1, p);
+    iplen2 = x509v3_a2i_ipadd(ipout + iplen1, p);
 
     OPENSSL_free(iptmp);
     iptmp = NULL;
@@ -1190,7 +1232,7 @@
     return NULL;
 }
 
-int a2i_ipadd(unsigned char *ipout, const char *ipasc)
+int x509v3_a2i_ipadd(unsigned char ipout[16], const char *ipasc)
 {
     /* If string contains a ':' assume IPv6 */
 
@@ -1205,7 +1247,7 @@
     }
 }
 
-static int ipv4_from_asc(unsigned char *v4, const char *in)
+static int ipv4_from_asc(unsigned char v4[4], const char *in)
 {
     int a0, a1, a2, a3;
     if (sscanf(in, "%d.%d.%d.%d", &a0, &a1, &a2, &a3) != 4)
@@ -1231,7 +1273,7 @@
     int zero_cnt;
 } IPV6_STAT;
 
-static int ipv6_from_asc(unsigned char *v6, const char *in)
+static int ipv6_from_asc(unsigned char v6[16], const char *in)
 {
     IPV6_STAT v6stat;
     v6stat.total = 0;
diff --git a/deps/boringssl/src/crypto/x509v3/v3name_test.cc b/deps/boringssl/src/crypto/x509v3/v3name_test.cc
index 2dcdd87..a501115 100644
--- a/deps/boringssl/src/crypto/x509v3/v3name_test.cc
+++ b/deps/boringssl/src/crypto/x509v3/v3name_test.cc
@@ -305,7 +305,7 @@
     crt = X509_new();
     if (crt == NULL)
         goto out;
-    if (!X509_set_version(crt, 3))
+    if (!X509_set_version(crt, X509_VERSION_3))
         goto out;
     ret = crt;
     crt = NULL;
diff --git a/deps/boringssl/src/decrepit/evp/evp_do_all.c b/deps/boringssl/src/decrepit/evp/evp_do_all.c
index d540144..a3fb077 100644
--- a/deps/boringssl/src/decrepit/evp/evp_do_all.c
+++ b/deps/boringssl/src/decrepit/evp/evp_do_all.c
@@ -78,6 +78,7 @@
   callback(EVP_sha256(), "SHA256", NULL, arg);
   callback(EVP_sha384(), "SHA384", NULL, arg);
   callback(EVP_sha512(), "SHA512", NULL, arg);
+  callback(EVP_sha512_256(), "SHA512-256", NULL, arg);
 
   callback(EVP_md4(), "md4", NULL, arg);
   callback(EVP_md5(), "md5", NULL, arg);
@@ -86,4 +87,11 @@
   callback(EVP_sha256(), "sha256", NULL, arg);
   callback(EVP_sha384(), "sha384", NULL, arg);
   callback(EVP_sha512(), "sha512", NULL, arg);
+  callback(EVP_sha512_256(), "sha512-256", NULL, arg);
+}
+
+void EVP_MD_do_all(void (*callback)(const EVP_MD *cipher, const char *name,
+                                    const char *unused, void *arg),
+                   void *arg) {
+  EVP_MD_do_all_sorted(callback, arg);
 }
diff --git a/deps/boringssl/src/decrepit/ripemd/internal.h b/deps/boringssl/src/decrepit/ripemd/internal.h
deleted file mode 100644
index 089be15..0000000
--- a/deps/boringssl/src/decrepit/ripemd/internal.h
+++ /dev/null
@@ -1,494 +0,0 @@
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.] */
-
-#ifndef OPENSSL_HEADER_RIPEMD_INTERNAL_H
-#define OPENSSL_HEADER_RIPEMD_INTERNAL_H
-
-#include <openssl/base.h>
-
-#if defined(__cplusplus)
-extern "C" {
-#endif
-
-
-static void ripemd160_block_data_order(uint32_t h[5], const uint8_t *data,
-                                       size_t num);
-
-#define DATA_ORDER_IS_LITTLE_ENDIAN
-
-#define HASH_LONG uint32_t
-#define HASH_CTX RIPEMD160_CTX
-#define HASH_CBLOCK RIPEMD160_CBLOCK
-#define HASH_DIGEST_LENGTH RIPEMD160_DIGEST_LENGTH
-#define HASH_UPDATE RIPEMD160_Update
-#define HASH_TRANSFORM RIPEMD160_Transform
-#define HASH_FINAL RIPEMD160_Final
-#define HASH_MAKE_STRING(c, s) \
-  do {                         \
-    unsigned long ll;          \
-    ll = (c)->h[0];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[1];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[2];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[3];            \
-    HOST_l2c(ll, (s));         \
-    ll = (c)->h[4];            \
-    HOST_l2c(ll, (s));         \
-  } while (0)
-#define HASH_BLOCK_DATA_ORDER ripemd160_block_data_order
-
-#include "../../crypto/fipsmodule/digest/md32_common.h"
-
-// Transformed F2 and F4 are courtesy of Wei Dai <weidai@eskimo.com>
-#define F1(x, y, z) ((x) ^ (y) ^ (z))
-#define F2(x, y, z) ((((y) ^ (z)) & (x)) ^ (z))
-#define F3(x, y, z) (((~(y)) | (x)) ^ (z))
-#define F4(x, y, z) ((((x) ^ (y)) & (z)) ^ (y))
-#define F5(x, y, z) (((~(z)) | (y)) ^ (x))
-
-#define RIPEMD160_A 0x67452301L
-#define RIPEMD160_B 0xEFCDAB89L
-#define RIPEMD160_C 0x98BADCFEL
-#define RIPEMD160_D 0x10325476L
-#define RIPEMD160_E 0xC3D2E1F0L
-
-#define ROTATE(a, n) (((a) << (n)) | (((a)&0xffffffff) >> (32 - (n))))
-
-#define RIP1(a, b, c, d, e, w, s) \
-  {                               \
-    a += F1(b, c, d) + X(w);      \
-    a = ROTATE(a, s) + e;         \
-    c = ROTATE(c, 10);            \
-  }
-
-#define RIP2(a, b, c, d, e, w, s, K) \
-  {                                  \
-    a += F2(b, c, d) + X(w) + K;     \
-    a = ROTATE(a, s) + e;            \
-    c = ROTATE(c, 10);               \
-  }
-
-#define RIP3(a, b, c, d, e, w, s, K) \
-  {                                  \
-    a += F3(b, c, d) + X(w) + K;     \
-    a = ROTATE(a, s) + e;            \
-    c = ROTATE(c, 10);               \
-  }
-
-#define RIP4(a, b, c, d, e, w, s, K) \
-  {                                  \
-    a += F4(b, c, d) + X(w) + K;     \
-    a = ROTATE(a, s) + e;            \
-    c = ROTATE(c, 10);               \
-  }
-
-#define RIP5(a, b, c, d, e, w, s, K) \
-  {                                  \
-    a += F5(b, c, d) + X(w) + K;     \
-    a = ROTATE(a, s) + e;            \
-    c = ROTATE(c, 10);               \
-  }
-
-#define KL0 0x00000000L
-#define KL1 0x5A827999L
-#define KL2 0x6ED9EBA1L
-#define KL3 0x8F1BBCDCL
-#define KL4 0xA953FD4EL
-
-#define KR0 0x50A28BE6L
-#define KR1 0x5C4DD124L
-#define KR2 0x6D703EF3L
-#define KR3 0x7A6D76E9L
-#define KR4 0x00000000L
-
-#define WL00  0
-#define SL00 11
-#define WL01  1
-#define SL01 14
-#define WL02  2
-#define SL02 15
-#define WL03  3
-#define SL03 12
-#define WL04  4
-#define SL04  5
-#define WL05  5
-#define SL05  8
-#define WL06  6
-#define SL06  7
-#define WL07  7
-#define SL07  9
-#define WL08  8
-#define SL08 11
-#define WL09  9
-#define SL09 13
-#define WL10 10
-#define SL10 14
-#define WL11 11
-#define SL11 15
-#define WL12 12
-#define SL12  6
-#define WL13 13
-#define SL13  7
-#define WL14 14
-#define SL14  9
-#define WL15 15
-#define SL15  8
-
-#define WL16  7
-#define SL16  7
-#define WL17  4
-#define SL17  6
-#define WL18 13
-#define SL18  8
-#define WL19  1
-#define SL19 13
-#define WL20 10
-#define SL20 11
-#define WL21  6
-#define SL21  9
-#define WL22 15
-#define SL22  7
-#define WL23  3
-#define SL23 15
-#define WL24 12
-#define SL24  7
-#define WL25  0
-#define SL25 12
-#define WL26  9
-#define SL26 15
-#define WL27  5
-#define SL27  9
-#define WL28  2
-#define SL28 11
-#define WL29 14
-#define SL29  7
-#define WL30 11
-#define SL30 13
-#define WL31  8
-#define SL31 12
-
-#define WL32  3
-#define SL32 11
-#define WL33 10
-#define SL33 13
-#define WL34 14
-#define SL34  6
-#define WL35  4
-#define SL35  7
-#define WL36  9
-#define SL36 14
-#define WL37 15
-#define SL37  9
-#define WL38  8
-#define SL38 13
-#define WL39  1
-#define SL39 15
-#define WL40  2
-#define SL40 14
-#define WL41  7
-#define SL41  8
-#define WL42  0
-#define SL42 13
-#define WL43  6
-#define SL43  6
-#define WL44 13
-#define SL44  5
-#define WL45 11
-#define SL45 12
-#define WL46  5
-#define SL46  7
-#define WL47 12
-#define SL47  5
-
-#define WL48  1
-#define SL48 11
-#define WL49  9
-#define SL49 12
-#define WL50 11
-#define SL50 14
-#define WL51 10
-#define SL51 15
-#define WL52  0
-#define SL52 14
-#define WL53  8
-#define SL53 15
-#define WL54 12
-#define SL54  9
-#define WL55  4
-#define SL55  8
-#define WL56 13
-#define SL56  9
-#define WL57  3
-#define SL57 14
-#define WL58  7
-#define SL58  5
-#define WL59 15
-#define SL59  6
-#define WL60 14
-#define SL60  8
-#define WL61  5
-#define SL61  6
-#define WL62  6
-#define SL62  5
-#define WL63  2
-#define SL63 12
-
-#define WL64  4
-#define SL64  9
-#define WL65  0
-#define SL65 15
-#define WL66  5
-#define SL66  5
-#define WL67  9
-#define SL67 11
-#define WL68  7
-#define SL68  6
-#define WL69 12
-#define SL69  8
-#define WL70  2
-#define SL70 13
-#define WL71 10
-#define SL71 12
-#define WL72 14
-#define SL72  5
-#define WL73  1
-#define SL73 12
-#define WL74  3
-#define SL74 13
-#define WL75  8
-#define SL75 14
-#define WL76 11
-#define SL76 11
-#define WL77  6
-#define SL77  8
-#define WL78 15
-#define SL78  5
-#define WL79 13
-#define SL79  6
-
-#define WR00  5
-#define SR00  8
-#define WR01 14
-#define SR01  9
-#define WR02  7
-#define SR02  9
-#define WR03  0
-#define SR03 11
-#define WR04  9
-#define SR04 13
-#define WR05  2
-#define SR05 15
-#define WR06 11
-#define SR06 15
-#define WR07  4
-#define SR07  5
-#define WR08 13
-#define SR08  7
-#define WR09  6
-#define SR09  7
-#define WR10 15
-#define SR10  8
-#define WR11  8
-#define SR11 11
-#define WR12  1
-#define SR12 14
-#define WR13 10
-#define SR13 14
-#define WR14  3
-#define SR14 12
-#define WR15 12
-#define SR15  6
-
-#define WR16  6
-#define SR16  9
-#define WR17 11
-#define SR17 13
-#define WR18  3
-#define SR18 15
-#define WR19  7
-#define SR19  7
-#define WR20  0
-#define SR20 12
-#define WR21 13
-#define SR21  8
-#define WR22  5
-#define SR22  9
-#define WR23 10
-#define SR23 11
-#define WR24 14
-#define SR24  7
-#define WR25 15
-#define SR25  7
-#define WR26  8
-#define SR26 12
-#define WR27 12
-#define SR27  7
-#define WR28  4
-#define SR28  6
-#define WR29  9
-#define SR29 15
-#define WR30  1
-#define SR30 13
-#define WR31  2
-#define SR31 11
-
-#define WR32 15
-#define SR32  9
-#define WR33  5
-#define SR33  7
-#define WR34  1
-#define SR34 15
-#define WR35  3
-#define SR35 11
-#define WR36  7
-#define SR36  8
-#define WR37 14
-#define SR37  6
-#define WR38  6
-#define SR38  6
-#define WR39  9
-#define SR39 14
-#define WR40 11
-#define SR40 12
-#define WR41  8
-#define SR41 13
-#define WR42 12
-#define SR42  5
-#define WR43  2
-#define SR43 14
-#define WR44 10
-#define SR44 13
-#define WR45  0
-#define SR45 13
-#define WR46  4
-#define SR46  7
-#define WR47 13
-#define SR47  5
-
-#define WR48  8
-#define SR48 15
-#define WR49  6
-#define SR49  5
-#define WR50  4
-#define SR50  8
-#define WR51  1
-#define SR51 11
-#define WR52  3
-#define SR52 14
-#define WR53 11
-#define SR53 14
-#define WR54 15
-#define SR54  6
-#define WR55  0
-#define SR55 14
-#define WR56  5
-#define SR56  6
-#define WR57 12
-#define SR57  9
-#define WR58  2
-#define SR58 12
-#define WR59 13
-#define SR59  9
-#define WR60  9
-#define SR60 12
-#define WR61  7
-#define SR61  5
-#define WR62 10
-#define SR62 15
-#define WR63 14
-#define SR63  8
-
-#define WR64 12
-#define SR64  8
-#define WR65 15
-#define SR65  5
-#define WR66 10
-#define SR66 12
-#define WR67  4
-#define SR67  9
-#define WR68  1
-#define SR68 12
-#define WR69  5
-#define SR69  5
-#define WR70  8
-#define SR70 14
-#define WR71  7
-#define SR71  6
-#define WR72  6
-#define SR72  8
-#define WR73  2
-#define SR73 13
-#define WR74 13
-#define SR74  6
-#define WR75 14
-#define SR75  5
-#define WR76  0
-#define SR76 15
-#define WR77  3
-#define SR77 13
-#define WR78  9
-#define SR78 11
-#define WR79 11
-#define SR79 11
-
-
-#if defined(__cplusplus)
-}  // extern C
-#endif
-
-#endif  // OPENSSL_HEADER_RIPEMD_INTERNAL_H
diff --git a/deps/boringssl/src/decrepit/ripemd/ripemd.c b/deps/boringssl/src/decrepit/ripemd/ripemd.c
index 17b3fdf..9120cdd 100644
--- a/deps/boringssl/src/decrepit/ripemd/ripemd.c
+++ b/deps/boringssl/src/decrepit/ripemd/ripemd.c
@@ -58,9 +58,16 @@
 
 #include <string.h>
 
-#include "internal.h"
+#include "../../crypto/internal.h"
+#include "../../crypto/fipsmodule/digest/md32_common.h"
 
 
+#define RIPEMD160_A 0x67452301L
+#define RIPEMD160_B 0xEFCDAB89L
+#define RIPEMD160_C 0x98BADCFEL
+#define RIPEMD160_D 0x10325476L
+#define RIPEMD160_E 0xC3D2E1F0L
+
 int RIPEMD160_Init(RIPEMD160_CTX *ctx) {
   OPENSSL_memset(ctx, 0, sizeof(*ctx));
   ctx->h[0] = RIPEMD160_A;
@@ -72,9 +79,422 @@
 }
 
 static void ripemd160_block_data_order(uint32_t h[5], const uint8_t *data,
+                                       size_t num);
+
+void RIPEMD160_Transform(RIPEMD160_CTX *c,
+                         const uint8_t data[RIPEMD160_CBLOCK]) {
+  ripemd160_block_data_order(c->h, data, 1);
+}
+
+int RIPEMD160_Update(RIPEMD160_CTX *c, const void *data, size_t len) {
+  crypto_md32_update(&ripemd160_block_data_order, c->h, c->data,
+                     RIPEMD160_CBLOCK, &c->num, &c->Nh, &c->Nl, data, len);
+  return 1;
+}
+
+int RIPEMD160_Final(uint8_t out[RIPEMD160_DIGEST_LENGTH], RIPEMD160_CTX *c) {
+  crypto_md32_final(&ripemd160_block_data_order, c->h, c->data,
+                    RIPEMD160_CBLOCK, &c->num, c->Nh, c->Nl,
+                    /*is_big_endian=*/0);
+
+  CRYPTO_store_u32_le(out, c->h[0]);
+  CRYPTO_store_u32_le(out + 4, c->h[1]);
+  CRYPTO_store_u32_le(out + 8, c->h[2]);
+  CRYPTO_store_u32_le(out + 12, c->h[3]);
+  CRYPTO_store_u32_le(out + 16, c->h[4]);
+  return 1;
+}
+
+// Transformed F2 and F4 are courtesy of Wei Dai <weidai@eskimo.com>
+#define F1(x, y, z) ((x) ^ (y) ^ (z))
+#define F2(x, y, z) ((((y) ^ (z)) & (x)) ^ (z))
+#define F3(x, y, z) (((~(y)) | (x)) ^ (z))
+#define F4(x, y, z) ((((x) ^ (y)) & (z)) ^ (y))
+#define F5(x, y, z) (((~(z)) | (y)) ^ (x))
+
+#define ROTATE(a, n) (((a) << (n)) | (((a)&0xffffffff) >> (32 - (n))))
+
+#define RIP1(a, b, c, d, e, w, s) \
+  {                               \
+    a += F1(b, c, d) + X(w);      \
+    a = ROTATE(a, s) + e;         \
+    c = ROTATE(c, 10);            \
+  }
+
+#define RIP2(a, b, c, d, e, w, s, K) \
+  {                                  \
+    a += F2(b, c, d) + X(w) + K;     \
+    a = ROTATE(a, s) + e;            \
+    c = ROTATE(c, 10);               \
+  }
+
+#define RIP3(a, b, c, d, e, w, s, K) \
+  {                                  \
+    a += F3(b, c, d) + X(w) + K;     \
+    a = ROTATE(a, s) + e;            \
+    c = ROTATE(c, 10);               \
+  }
+
+#define RIP4(a, b, c, d, e, w, s, K) \
+  {                                  \
+    a += F4(b, c, d) + X(w) + K;     \
+    a = ROTATE(a, s) + e;            \
+    c = ROTATE(c, 10);               \
+  }
+
+#define RIP5(a, b, c, d, e, w, s, K) \
+  {                                  \
+    a += F5(b, c, d) + X(w) + K;     \
+    a = ROTATE(a, s) + e;            \
+    c = ROTATE(c, 10);               \
+  }
+
+#define KL0 0x00000000L
+#define KL1 0x5A827999L
+#define KL2 0x6ED9EBA1L
+#define KL3 0x8F1BBCDCL
+#define KL4 0xA953FD4EL
+
+#define KR0 0x50A28BE6L
+#define KR1 0x5C4DD124L
+#define KR2 0x6D703EF3L
+#define KR3 0x7A6D76E9L
+#define KR4 0x00000000L
+
+#define WL00  0
+#define SL00 11
+#define WL01  1
+#define SL01 14
+#define WL02  2
+#define SL02 15
+#define WL03  3
+#define SL03 12
+#define WL04  4
+#define SL04  5
+#define WL05  5
+#define SL05  8
+#define WL06  6
+#define SL06  7
+#define WL07  7
+#define SL07  9
+#define WL08  8
+#define SL08 11
+#define WL09  9
+#define SL09 13
+#define WL10 10
+#define SL10 14
+#define WL11 11
+#define SL11 15
+#define WL12 12
+#define SL12  6
+#define WL13 13
+#define SL13  7
+#define WL14 14
+#define SL14  9
+#define WL15 15
+#define SL15  8
+
+#define WL16  7
+#define SL16  7
+#define WL17  4
+#define SL17  6
+#define WL18 13
+#define SL18  8
+#define WL19  1
+#define SL19 13
+#define WL20 10
+#define SL20 11
+#define WL21  6
+#define SL21  9
+#define WL22 15
+#define SL22  7
+#define WL23  3
+#define SL23 15
+#define WL24 12
+#define SL24  7
+#define WL25  0
+#define SL25 12
+#define WL26  9
+#define SL26 15
+#define WL27  5
+#define SL27  9
+#define WL28  2
+#define SL28 11
+#define WL29 14
+#define SL29  7
+#define WL30 11
+#define SL30 13
+#define WL31  8
+#define SL31 12
+
+#define WL32  3
+#define SL32 11
+#define WL33 10
+#define SL33 13
+#define WL34 14
+#define SL34  6
+#define WL35  4
+#define SL35  7
+#define WL36  9
+#define SL36 14
+#define WL37 15
+#define SL37  9
+#define WL38  8
+#define SL38 13
+#define WL39  1
+#define SL39 15
+#define WL40  2
+#define SL40 14
+#define WL41  7
+#define SL41  8
+#define WL42  0
+#define SL42 13
+#define WL43  6
+#define SL43  6
+#define WL44 13
+#define SL44  5
+#define WL45 11
+#define SL45 12
+#define WL46  5
+#define SL46  7
+#define WL47 12
+#define SL47  5
+
+#define WL48  1
+#define SL48 11
+#define WL49  9
+#define SL49 12
+#define WL50 11
+#define SL50 14
+#define WL51 10
+#define SL51 15
+#define WL52  0
+#define SL52 14
+#define WL53  8
+#define SL53 15
+#define WL54 12
+#define SL54  9
+#define WL55  4
+#define SL55  8
+#define WL56 13
+#define SL56  9
+#define WL57  3
+#define SL57 14
+#define WL58  7
+#define SL58  5
+#define WL59 15
+#define SL59  6
+#define WL60 14
+#define SL60  8
+#define WL61  5
+#define SL61  6
+#define WL62  6
+#define SL62  5
+#define WL63  2
+#define SL63 12
+
+#define WL64  4
+#define SL64  9
+#define WL65  0
+#define SL65 15
+#define WL66  5
+#define SL66  5
+#define WL67  9
+#define SL67 11
+#define WL68  7
+#define SL68  6
+#define WL69 12
+#define SL69  8
+#define WL70  2
+#define SL70 13
+#define WL71 10
+#define SL71 12
+#define WL72 14
+#define SL72  5
+#define WL73  1
+#define SL73 12
+#define WL74  3
+#define SL74 13
+#define WL75  8
+#define SL75 14
+#define WL76 11
+#define SL76 11
+#define WL77  6
+#define SL77  8
+#define WL78 15
+#define SL78  5
+#define WL79 13
+#define SL79  6
+
+#define WR00  5
+#define SR00  8
+#define WR01 14
+#define SR01  9
+#define WR02  7
+#define SR02  9
+#define WR03  0
+#define SR03 11
+#define WR04  9
+#define SR04 13
+#define WR05  2
+#define SR05 15
+#define WR06 11
+#define SR06 15
+#define WR07  4
+#define SR07  5
+#define WR08 13
+#define SR08  7
+#define WR09  6
+#define SR09  7
+#define WR10 15
+#define SR10  8
+#define WR11  8
+#define SR11 11
+#define WR12  1
+#define SR12 14
+#define WR13 10
+#define SR13 14
+#define WR14  3
+#define SR14 12
+#define WR15 12
+#define SR15  6
+
+#define WR16  6
+#define SR16  9
+#define WR17 11
+#define SR17 13
+#define WR18  3
+#define SR18 15
+#define WR19  7
+#define SR19  7
+#define WR20  0
+#define SR20 12
+#define WR21 13
+#define SR21  8
+#define WR22  5
+#define SR22  9
+#define WR23 10
+#define SR23 11
+#define WR24 14
+#define SR24  7
+#define WR25 15
+#define SR25  7
+#define WR26  8
+#define SR26 12
+#define WR27 12
+#define SR27  7
+#define WR28  4
+#define SR28  6
+#define WR29  9
+#define SR29 15
+#define WR30  1
+#define SR30 13
+#define WR31  2
+#define SR31 11
+
+#define WR32 15
+#define SR32  9
+#define WR33  5
+#define SR33  7
+#define WR34  1
+#define SR34 15
+#define WR35  3
+#define SR35 11
+#define WR36  7
+#define SR36  8
+#define WR37 14
+#define SR37  6
+#define WR38  6
+#define SR38  6
+#define WR39  9
+#define SR39 14
+#define WR40 11
+#define SR40 12
+#define WR41  8
+#define SR41 13
+#define WR42 12
+#define SR42  5
+#define WR43  2
+#define SR43 14
+#define WR44 10
+#define SR44 13
+#define WR45  0
+#define SR45 13
+#define WR46  4
+#define SR46  7
+#define WR47 13
+#define SR47  5
+
+#define WR48  8
+#define SR48 15
+#define WR49  6
+#define SR49  5
+#define WR50  4
+#define SR50  8
+#define WR51  1
+#define SR51 11
+#define WR52  3
+#define SR52 14
+#define WR53 11
+#define SR53 14
+#define WR54 15
+#define SR54  6
+#define WR55  0
+#define SR55 14
+#define WR56  5
+#define SR56  6
+#define WR57 12
+#define SR57  9
+#define WR58  2
+#define SR58 12
+#define WR59 13
+#define SR59  9
+#define WR60  9
+#define SR60 12
+#define WR61  7
+#define SR61  5
+#define WR62 10
+#define SR62 15
+#define WR63 14
+#define SR63  8
+
+#define WR64 12
+#define SR64  8
+#define WR65 15
+#define SR65  5
+#define WR66 10
+#define SR66 12
+#define WR67  4
+#define SR67  9
+#define WR68  1
+#define SR68 12
+#define WR69  5
+#define SR69  5
+#define WR70  8
+#define SR70 14
+#define WR71  7
+#define SR71  6
+#define WR72  6
+#define SR72  8
+#define WR73  2
+#define SR73 13
+#define WR74 13
+#define SR74  6
+#define WR75 14
+#define SR75  5
+#define WR76  0
+#define SR76 15
+#define WR77  3
+#define SR77 13
+#define WR78  9
+#define SR78 11
+#define WR79 11
+#define SR79 11
+
+static void ripemd160_block_data_order(uint32_t h[5], const uint8_t *data,
                                        size_t num) {
   uint32_t A, B, C, D, E;
-  uint32_t a, b, c, d, e, l;
+  uint32_t a, b, c, d, e;
   uint32_t XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12,
       XX13, XX14, XX15;
 #define X(i) XX##i
@@ -86,52 +506,52 @@
     D = h[3];
     E = h[4];
 
-    HOST_c2l(data, l);
-    X(0) = l;
-    HOST_c2l(data, l);
-    X(1) = l;
+    X(0) = CRYPTO_load_u32_le(data);
+    data += 4;
+    X(1) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(A, B, C, D, E, WL00, SL00);
-    HOST_c2l(data, l);
-    X(2) = l;
+    X(2) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(E, A, B, C, D, WL01, SL01);
-    HOST_c2l(data, l);
-    X(3) = l;
+    X(3) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(D, E, A, B, C, WL02, SL02);
-    HOST_c2l(data, l);
-    X(4) = l;
+    X(4) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(C, D, E, A, B, WL03, SL03);
-    HOST_c2l(data, l);
-    X(5) = l;
+    X(5) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(B, C, D, E, A, WL04, SL04);
-    HOST_c2l(data, l);
-    X(6) = l;
+    X(6) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(A, B, C, D, E, WL05, SL05);
-    HOST_c2l(data, l);
-    X(7) = l;
+    X(7) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(E, A, B, C, D, WL06, SL06);
-    HOST_c2l(data, l);
-    X(8) = l;
+    X(8) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(D, E, A, B, C, WL07, SL07);
-    HOST_c2l(data, l);
-    X(9) = l;
+    X(9) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(C, D, E, A, B, WL08, SL08);
-    HOST_c2l(data, l);
-    X(10) = l;
+    X(10) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(B, C, D, E, A, WL09, SL09);
-    HOST_c2l(data, l);
-    X(11) = l;
+    X(11) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(A, B, C, D, E, WL10, SL10);
-    HOST_c2l(data, l);
-    X(12) = l;
+    X(12) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(E, A, B, C, D, WL11, SL11);
-    HOST_c2l(data, l);
-    X(13) = l;
+    X(13) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(D, E, A, B, C, WL12, SL12);
-    HOST_c2l(data, l);
-    X(14) = l;
+    X(14) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(C, D, E, A, B, WL13, SL13);
-    HOST_c2l(data, l);
-    X(15) = l;
+    X(15) = CRYPTO_load_u32_le(data);
+    data += 4;
     RIP1(B, C, D, E, A, WL14, SL14);
     RIP1(A, B, C, D, E, WL15, SL15);
 
diff --git a/deps/boringssl/src/fuzz/CMakeLists.txt b/deps/boringssl/src/fuzz/CMakeLists.txt
index 98a959a..62652cb 100644
--- a/deps/boringssl/src/fuzz/CMakeLists.txt
+++ b/deps/boringssl/src/fuzz/CMakeLists.txt
@@ -29,3 +29,4 @@
 fuzzer(dtls_client ssl)
 fuzzer(ssl_ctx_api ssl)
 fuzzer(session ssl)
+fuzzer(decode_client_hello_inner ssl)
diff --git a/deps/boringssl/src/fuzz/cert.cc b/deps/boringssl/src/fuzz/cert.cc
index 0bfcac4..79e1456 100644
--- a/deps/boringssl/src/fuzz/cert.cc
+++ b/deps/boringssl/src/fuzz/cert.cc
@@ -26,6 +26,10 @@
     uint8_t *der = NULL;
     i2d_X509(x509, &der);
     OPENSSL_free(der);
+
+    BIO *bio = BIO_new(BIO_s_mem());
+    X509_print(bio, x509);
+    BIO_free(bio);
   }
   X509_free(x509);
   ERR_clear_error();
diff --git a/deps/boringssl/src/fuzz/decode_client_hello_inner.cc b/deps/boringssl/src/fuzz/decode_client_hello_inner.cc
new file mode 100644
index 0000000..db090c5
--- /dev/null
+++ b/deps/boringssl/src/fuzz/decode_client_hello_inner.cc
@@ -0,0 +1,52 @@
+/* Copyright (c) 2021, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/bytestring.h>
+#include <openssl/ssl.h>
+#include <openssl/span.h>
+
+#include "../ssl/internal.h"
+
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) {
+  static bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  static bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
+
+  CBS reader(bssl::MakeConstSpan(buf, len));
+  CBS encoded_client_hello_inner_cbs;
+
+  if (!CBS_get_u24_length_prefixed(&reader, &encoded_client_hello_inner_cbs)) {
+    return 0;
+  }
+
+  bssl::Array<uint8_t> encoded_client_hello_inner;
+  if (!encoded_client_hello_inner.CopyFrom(encoded_client_hello_inner_cbs)) {
+    return 0;
+  }
+
+  // Use the remaining bytes in |reader| as the ClientHelloOuter.
+  SSL_CLIENT_HELLO client_hello_outer;
+  if (!bssl::ssl_client_hello_init(ssl.get(), &client_hello_outer, reader)) {
+    return 0;
+  }
+
+  // Recover the ClientHelloInner from the EncodedClientHelloInner and
+  // ClientHelloOuter.
+  uint8_t alert_unused;
+  bssl::Array<uint8_t> client_hello_inner;
+  bssl::ssl_decode_client_hello_inner(
+      ssl.get(), &alert_unused, &client_hello_inner, encoded_client_hello_inner,
+      &client_hello_outer);
+  return 0;
+}
diff --git a/deps/boringssl/src/fuzz/ssl_ctx_api.cc b/deps/boringssl/src/fuzz/ssl_ctx_api.cc
index abf2f16..da0a2d3 100644
--- a/deps/boringssl/src/fuzz/ssl_ctx_api.cc
+++ b/deps/boringssl/src/fuzz/ssl_ctx_api.cc
@@ -22,6 +22,7 @@
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
+#include <openssl/hpke.h>
 #include <openssl/rsa.h>
 #include <openssl/ssl.h>
 #include <openssl/stack.h>
@@ -491,6 +492,28 @@
         }
         SSL_CTX_set1_sigalgs_list(ctx, sigalgs.c_str());
       },
+      [](SSL_CTX *ctx, CBS *cbs) {
+        bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+        if (keys == nullptr) {
+          return;
+        }
+        uint8_t is_retry_config;
+        CBS ech_config, private_key;
+        if (!CBS_get_u8(cbs, &is_retry_config) ||
+            !CBS_get_u16_length_prefixed(cbs, &ech_config) ||
+            !CBS_get_u16_length_prefixed(cbs, &private_key)) {
+          return;
+        }
+        bssl::ScopedEVP_HPKE_KEY key;
+        if (!EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(),
+                               CBS_data(&private_key), CBS_len(&private_key)) ||
+            !SSL_ECH_KEYS_add(keys.get(), is_retry_config,
+                              CBS_data(&ech_config), CBS_len(&ech_config),
+                              key.get()) ||
+            !SSL_CTX_set1_ech_keys(ctx, keys.get())) {
+          return;
+        }
+      },
   };
 
   bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
diff --git a/deps/boringssl/src/include/openssl/aead.h b/deps/boringssl/src/include/openssl/aead.h
index 3bc74e7..38eb076 100644
--- a/deps/boringssl/src/include/openssl/aead.h
+++ b/deps/boringssl/src/include/openssl/aead.h
@@ -122,7 +122,7 @@
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_gcm(void);
 
 // EVP_aead_chacha20_poly1305 is the AEAD built from ChaCha20 and
-// Poly1305 as described in RFC 7539.
+// Poly1305 as described in RFC 8439.
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_chacha20_poly1305(void);
 
 // EVP_aead_xchacha20_poly1305 is ChaCha20-Poly1305 with an extended nonce that
@@ -397,12 +397,9 @@
 
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_128_cbc_sha1_tls(void);
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_128_cbc_sha1_tls_implicit_iv(void);
-OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_128_cbc_sha256_tls(void);
 
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_cbc_sha1_tls(void);
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_cbc_sha1_tls_implicit_iv(void);
-OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_cbc_sha256_tls(void);
-OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_cbc_sha384_tls(void);
 
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_des_ede3_cbc_sha1_tls(void);
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv(void);
diff --git a/deps/boringssl/src/include/openssl/arm_arch.h b/deps/boringssl/src/include/openssl/arm_arch.h
index 31ff8a6..81dc796 100644
--- a/deps/boringssl/src/include/openssl/arm_arch.h
+++ b/deps/boringssl/src/include/openssl/arm_arch.h
@@ -124,7 +124,72 @@
 //   - Armv8.5-A Branch Target Identification
 // features which require emitting a .note.gnu.property section with the
 // appropriate architecture-dependent feature bits set.
-// Read more: "ELF for the Arm® 64-bit Architecture"
+//
+// |AARCH64_SIGN_LINK_REGISTER| and |AARCH64_VALIDATE_LINK_REGISTER| expand to
+// PACIxSP and AUTIxSP, respectively. |AARCH64_SIGN_LINK_REGISTER| should be
+// used immediately before saving the LR register (x30) to the stack.
+// |AARCH64_VALIDATE_LINK_REGISTER| should be used immediately after restoring
+// it. Note |AARCH64_SIGN_LINK_REGISTER|'s modifications to LR must be undone
+// with |AARCH64_VALIDATE_LINK_REGISTER| before RET. The SP register must also
+// have the same value at the two points. For example:
+//
+//   .global f
+//   f:
+//     AARCH64_SIGN_LINK_REGISTER
+//     stp x29, x30, [sp, #-96]!
+//     mov x29, sp
+//     ...
+//     ldp x29, x30, [sp], #96
+//     AARCH64_VALIDATE_LINK_REGISTER
+//     ret
+//
+// |AARCH64_VALID_CALL_TARGET| expands to BTI 'c'. Either it, or
+// |AARCH64_SIGN_LINK_REGISTER|, must be used at every point that may be an
+// indirect call target. In particular, all symbols exported from a file must
+// begin with one of these macros. For example, a leaf function that does not
+// save LR can instead use |AARCH64_VALID_CALL_TARGET|:
+//
+//   .globl return_zero
+//   return_zero:
+//     AARCH64_VALID_CALL_TARGET
+//     mov x0, #0
+//     ret
+//
+// A non-leaf function which does not immediately save LR may need both macros
+// because |AARCH64_SIGN_LINK_REGISTER| appears late. For example, the function
+// may jump to an alternate implementation before setting up the stack:
+//
+//   .globl with_early_jump
+//   with_early_jump:
+//     AARCH64_VALID_CALL_TARGET
+//     cmp x0, #128
+//     b.lt .Lwith_early_jump_128
+//     AARCH64_SIGN_LINK_REGISTER
+//     stp x29, x30, [sp, #-96]!
+//     mov x29, sp
+//     ...
+//     ldp x29, x30, [sp], #96
+//     AARCH64_VALIDATE_LINK_REGISTER
+//     ret
+//
+//  .Lwith_early_jump_128:
+//     ...
+//     ret
+//
+// These annotations are only required with indirect calls. Private symbols that
+// are only the target of direct calls do not require annotations. Also note
+// that |AARCH64_VALID_CALL_TARGET| is only valid for indirect calls (BLR), not
+// indirect jumps (BR). Indirect jumps in assembly are currently not supported
+// and would require a macro for BTI 'j'.
+//
+// Although not necessary, it is safe to use these macros in 32-bit ARM
+// assembly. This may be used to simplify dual 32-bit and 64-bit files.
+//
+// References:
+// - "ELF for the Arm® 64-bit Architecture"
+//   https://github.com/ARM-software/abi-aa/blob/master/aaelf64/aaelf64.rst
+// - "Providing protection for complex software"
+//   https://developer.arm.com/architectures/learn-the-architecture/providing-protection-for-complex-software
 
 #if defined(__ARM_FEATURE_BTI_DEFAULT) && __ARM_FEATURE_BTI_DEFAULT == 1
 #define GNU_PROPERTY_AARCH64_BTI (1 << 0)   // Has Branch Target Identification
diff --git a/deps/boringssl/src/include/openssl/asn1.h b/deps/boringssl/src/include/openssl/asn1.h
index 9269553..4f6fb3b 100644
--- a/deps/boringssl/src/include/openssl/asn1.h
+++ b/deps/boringssl/src/include/openssl/asn1.h
@@ -103,7 +103,7 @@
 #define V_ASN1_PRIMITIVE_TAG 0x1f
 
 // V_ASN1_MAX_UNIVERSAL is the highest supported universal tag number. It is
-// necessary to avoid ambiguity with |V_ASN1_NEG|.
+// necessary to avoid ambiguity with |V_ASN1_NEG| and |MBSTRING_FLAG|.
 //
 // TODO(davidben): Make this private.
 #define V_ASN1_MAX_UNIVERSAL 0xff
@@ -111,10 +111,6 @@
 // V_ASN1_UNDEF is used in some APIs to indicate an ASN.1 element is omitted.
 #define V_ASN1_UNDEF (-1)
 
-// V_ASN1_APP_CHOOSE is used in some APIs to specify a default ASN.1 type based
-// on the context.
-#define V_ASN1_APP_CHOOSE (-2)
-
 // V_ASN1_OTHER is used in |ASN1_TYPE| to indicate a non-universal ASN.1 type.
 #define V_ASN1_OTHER (-3)
 
@@ -157,6 +153,31 @@
 #define V_ASN1_NEG_INTEGER (V_ASN1_INTEGER | V_ASN1_NEG)
 #define V_ASN1_NEG_ENUMERATED (V_ASN1_ENUMERATED | V_ASN1_NEG)
 
+// The following constants are bitmask representations of ASN.1 types.
+#define B_ASN1_NUMERICSTRING 0x0001
+#define B_ASN1_PRINTABLESTRING 0x0002
+#define B_ASN1_T61STRING 0x0004
+#define B_ASN1_TELETEXSTRING 0x0004
+#define B_ASN1_VIDEOTEXSTRING 0x0008
+#define B_ASN1_IA5STRING 0x0010
+#define B_ASN1_GRAPHICSTRING 0x0020
+#define B_ASN1_ISO64STRING 0x0040
+#define B_ASN1_VISIBLESTRING 0x0040
+#define B_ASN1_GENERALSTRING 0x0080
+#define B_ASN1_UNIVERSALSTRING 0x0100
+#define B_ASN1_OCTET_STRING 0x0200
+#define B_ASN1_BIT_STRING 0x0400
+#define B_ASN1_BMPSTRING 0x0800
+#define B_ASN1_UNKNOWN 0x1000
+#define B_ASN1_UTF8STRING 0x2000
+#define B_ASN1_UTCTIME 0x4000
+#define B_ASN1_GENERALIZEDTIME 0x8000
+#define B_ASN1_SEQUENCE 0x10000
+
+// ASN1_tag2str returns a string representation of |tag|, interpret as a tag
+// number for a universal type, or |V_ASN1_NEG_*|.
+OPENSSL_EXPORT const char *ASN1_tag2str(int tag);
+
 
 // Strings.
 //
@@ -177,29 +198,19 @@
 // string.
 //
 // When representing a BIT STRING value, the type field is |V_ASN1_BIT_STRING|.
-// The data contains the encoded form of the BIT STRING, including any padding
-// bits added to round to a whole number of bytes, but excluding the leading
-// byte containing the number of padding bits. The number of padding bits is
-// encoded in the flags field. See |ASN1_STRING_FLAG_BITS_LEFT| for details. For
-// example, DER encodes the BIT STRING {1, 0} as {0x06, 0x80 = 0b10_000000}. The
-// |ASN1_STRING| representation has data of {0x80} and flags of
-// ASN1_STRING_FLAG_BITS_LEFT | 6.
+// See bit string documentation below for how the data and flags are used.
 //
-// When representing an INTEGER or ENUMERATED value, the data contains the
-// big-endian encoding of the absolute value of the integer. The sign bit is
-// encoded in the type: non-negative values have a type of |V_ASN1_INTEGER| or
-// |V_ASN1_ENUMERATED|, while negative values have a type of
-// |V_ASN1_NEG_INTEGER| or |V_ASN1_NEG_ENUMERATED|. Note this differs from DER's
-// two's complement representation.
+// When representing an INTEGER or ENUMERATED value, the type field is one of
+// |V_ASN1_INTEGER|, |V_ASN1_NEG_INTEGER|, |V_ASN1_ENUMERATED|, or
+// |V_ASN1_NEG_ENUMERATED|. See integer documentation below for details.
 //
 // When representing a GeneralizedTime or UTCTime value, the type field is
 // |V_ASN1_GENERALIZEDTIME| or |V_ASN1_UTCTIME|, respectively. The data contains
 // the DER encoding of the value. For example, the UNIX epoch would be
 // "19700101000000Z" for a GeneralizedTime and "700101000000Z" for a UTCTime.
 //
-// TODO(davidben): |ASN1_TYPE| additionally uses |ASN1_STRING| to represent
-// various other odd cases. It also likes to assume unknown universal tags are
-// string types. Make a note here when documenting |ASN1_TYPE|.
+// |ASN1_STRING|, when stored in an |ASN1_TYPE|, may also represent an element
+// with tag not directly supported by this library. See |ASN1_TYPE| for details.
 //
 // |ASN1_STRING| additionally has the following typedefs: |ASN1_BIT_STRING|,
 // |ASN1_BMPSTRING|, |ASN1_ENUMERATED|, |ASN1_GENERALIZEDTIME|,
@@ -242,14 +253,6 @@
 // treated as padding. This behavior is deprecated and should not be used.
 #define ASN1_STRING_FLAG_BITS_LEFT 0x08
 
-// ASN1_STRING_FLAG_MSTRING indicates that the |ASN1_STRING| is an MSTRING type,
-// which is how this library refers to a CHOICE type of several string types.
-// For example, DirectoryString as defined in RFC5280.
-//
-// TODO(davidben): This is only used in one place within the library and is easy
-// to accidentally drop. Can it be removed?
-#define ASN1_STRING_FLAG_MSTRING 0x040
-
 // ASN1_STRING_type_new returns a newly-allocated empty |ASN1_STRING| object of
 // type |type|, or NULL on error.
 OPENSSL_EXPORT ASN1_STRING *ASN1_STRING_type_new(int type);
@@ -291,11 +294,16 @@
 
 // ASN1_STRING_cmp compares |a| and |b|'s type and contents. It returns an
 // integer equal to, less than, or greater than zero if |a| is equal to, less
-// than, or greater than |b|, respectively. The comparison is suitable for
-// sorting, but callers should not rely on the particular comparison.
+// than, or greater than |b|, respectively. This function compares by length,
+// then data, then type. Note the data compared is the |ASN1_STRING| internal
+// representation and the type order is arbitrary. While this comparison is
+// suitable for sorting, callers should not rely on the exact order when |a|
+// and |b| are different types.
 //
-// Note if |a| or |b| are BIT STRINGs, this function does not compare the
-// |ASN1_STRING_FLAG_BITS_LEFT| flags.
+// If |a| or |b| are BIT STRINGs, this function does not compare the
+// |ASN1_STRING_FLAG_BITS_LEFT| flags. Additionally, if |a| and |b| are
+// INTEGERs, this comparison does not order the values numerically. For a
+// numerical comparison, use |ASN1_INTEGER_cmp|.
 //
 // TODO(davidben): The BIT STRING comparison seems like a bug. Fix it?
 OPENSSL_EXPORT int ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b);
@@ -309,61 +317,532 @@
 // |OPENSSL_malloc|.
 OPENSSL_EXPORT void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len);
 
-// TODO(davidben): Pull up and document functions specific to individual string
-// types.
+// ASN1_STRING_to_UTF8 converts |in| to UTF-8. On success, sets |*out| to a
+// newly-allocated buffer containing the resulting string and returns the length
+// of the string. The caller must call |OPENSSL_free| to release |*out| when
+// done. On error, it returns a negative number.
+OPENSSL_EXPORT int ASN1_STRING_to_UTF8(unsigned char **out,
+                                       const ASN1_STRING *in);
+
+// The following formats define encodings for use with functions like
+// |ASN1_mbstring_copy|.
+#define MBSTRING_FLAG 0x1000
+#define MBSTRING_UTF8 (MBSTRING_FLAG)
+// |MBSTRING_ASC| refers to Latin-1, not ASCII.
+#define MBSTRING_ASC (MBSTRING_FLAG | 1)
+#define MBSTRING_BMP (MBSTRING_FLAG | 2)
+#define MBSTRING_UNIV (MBSTRING_FLAG | 4)
+
+// DIRSTRING_TYPE contains the valid string types in an X.509 DirectoryString.
+#define DIRSTRING_TYPE                                            \
+  (B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_BMPSTRING | \
+   B_ASN1_UTF8STRING)
+
+// PKCS9STRING_TYPE contains the valid string types in a PKCS9String.
+#define PKCS9STRING_TYPE (DIRSTRING_TYPE | B_ASN1_IA5STRING)
+
+// ASN1_mbstring_copy converts |len| bytes from |in| to an ASN.1 string. If
+// |len| is -1, |in| must be NUL-terminated and the length is determined by
+// |strlen|. |in| is decoded according to |inform|, which must be one of
+// |MBSTRING_*|. |mask| determines the set of valid output types and is a
+// bitmask containing a subset of |B_ASN1_PRINTABLESTRING|, |B_ASN1_IA5STRING|,
+// |B_ASN1_T61STRING|, |B_ASN1_BMPSTRING|, |B_ASN1_UNIVERSALSTRING|, and
+// |B_ASN1_UTF8STRING|, in that preference order. This function chooses the
+// first output type in |mask| which can represent |in|. It interprets T61String
+// as Latin-1, rather than T.61.
+//
+// If |mask| is zero, |DIRSTRING_TYPE| is used by default.
+//
+// On success, this function returns the |V_ASN1_*| constant corresponding to
+// the selected output type and, if |out| and |*out| are both non-NULL, updates
+// the object at |*out| with the result. If |out| is non-NULL and |*out| is
+// NULL, it instead sets |*out| to a newly-allocated |ASN1_STRING| containing
+// the result. If |out| is NULL, it returns the selected output type without
+// constructing an |ASN1_STRING|. On error, this function returns -1.
+OPENSSL_EXPORT int ASN1_mbstring_copy(ASN1_STRING **out, const uint8_t *in,
+                                      int len, int inform, unsigned long mask);
+
+// ASN1_mbstring_ncopy behaves like |ASN1_mbstring_copy| but returns an error if
+// the input is less than |minsize| or greater than |maxsize| codepoints long. A
+// |maxsize| value of zero is ignored. Note the sizes are measured in
+// codepoints, not output bytes.
+OPENSSL_EXPORT int ASN1_mbstring_ncopy(ASN1_STRING **out, const uint8_t *in,
+                                       int len, int inform, unsigned long mask,
+                                       long minsize, long maxsize);
+
+// TODO(davidben): Expand and document function prototypes generated in macros.
+
+
+// Bit strings.
+//
+// An ASN.1 BIT STRING type represents a string of bits. The string may not
+// necessarily be a whole number of bytes. BIT STRINGs occur in ASN.1 structures
+// in several forms:
+//
+// Some BIT STRINGs represent a bitmask of named bits, such as the X.509 key
+// usage extension in RFC 5280, section 4.2.1.3. For such bit strings, DER
+// imposes an additional restriction that trailing zero bits are removed. Some
+// functions like |ASN1_BIT_STRING_set_bit| help in maintaining this.
+//
+// Other BIT STRINGs are arbitrary strings of bits used as identifiers and do
+// not have this constraint, such as the X.509 issuerUniqueID field.
+//
+// Finally, some structures use BIT STRINGs as a container for byte strings. For
+// example, the signatureValue field in X.509 and the subjectPublicKey field in
+// SubjectPublicKeyInfo are defined as BIT STRINGs with a value specific to the
+// AlgorithmIdentifier. While some unknown algorithm could choose to store
+// arbitrary bit strings, all supported algorithms use a byte string, with bit
+// order matching the DER encoding. Callers interpreting a BIT STRING as a byte
+// string should use |ASN1_BIT_STRING_num_bytes| instead of |ASN1_STRING_length|
+// and reject bit strings that are not a whole number of bytes.
+//
+// This library represents BIT STRINGs as |ASN1_STRING|s with type
+// |V_ASN1_BIT_STRING|. The data contains the encoded form of the BIT STRING,
+// including any padding bits added to round to a whole number of bytes, but
+// excluding the leading byte containing the number of padding bits. If
+// |ASN1_STRING_FLAG_BITS_LEFT| is set, the bottom three bits contains the
+// number of padding bits. For example, DER encodes the BIT STRING {1, 0} as
+// {0x06, 0x80 = 0b10_000000}. The |ASN1_STRING| representation has data of
+// {0x80} and flags of ASN1_STRING_FLAG_BITS_LEFT | 6. If
+// |ASN1_STRING_FLAG_BITS_LEFT| is unset, trailing zero bits are implicitly
+// removed. Callers should not rely this representation when constructing bit
+// strings.
+
+// ASN1_BIT_STRING_num_bytes computes the length of |str| in bytes. If |str|'s
+// bit length is a multiple of 8, it sets |*out| to the byte length and returns
+// one. Otherwise, it returns zero.
+//
+// This function may be used with |ASN1_STRING_get0_data| to interpret |str| as
+// a byte string.
+OPENSSL_EXPORT int ASN1_BIT_STRING_num_bytes(const ASN1_BIT_STRING *str,
+                                             size_t *out);
+
+// ASN1_BIT_STRING_set calls |ASN1_STRING_set|. It leaves flags unchanged, so
+// the caller must set the number of unused bits.
+//
+// TODO(davidben): Maybe it should? Wrapping a byte string in a bit string is a
+// common use case.
+OPENSSL_EXPORT int ASN1_BIT_STRING_set(ASN1_BIT_STRING *str,
+                                       const unsigned char *d, int length);
+
+// ASN1_BIT_STRING_set_bit sets bit |n| of |str| to one if |value| is non-zero
+// and zero if |value| is zero, resizing |str| as needed. It then truncates
+// trailing zeros in |str| to align with the DER represention for a bit string
+// with named bits. It returns one on success and zero on error. |n| is indexed
+// beginning from zero.
+OPENSSL_EXPORT int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *str, int n,
+                                           int value);
+
+// ASN1_BIT_STRING_get_bit returns one if bit |n| of |a| is in bounds and set,
+// and zero otherwise. |n| is indexed beginning from zero.
+OPENSSL_EXPORT int ASN1_BIT_STRING_get_bit(const ASN1_BIT_STRING *str, int n);
+
+// ASN1_BIT_STRING_check returns one if |str| only contains bits that are set in
+// the |flags_len| bytes pointed by |flags|. Otherwise it returns zero. Bits in
+// |flags| are arranged according to the DER representation, so bit 0
+// corresponds to the MSB of |flags[0]|.
+OPENSSL_EXPORT int ASN1_BIT_STRING_check(const ASN1_BIT_STRING *str,
+                                         const unsigned char *flags,
+                                         int flags_len);
+
+// TODO(davidben): Expand and document function prototypes generated in macros.
+
+
+// Integers and enumerated values.
+//
+// INTEGER and ENUMERATED values are represented as |ASN1_STRING|s where the
+// data contains the big-endian encoding of the absolute value of the integer.
+// The sign bit is encoded in the type: non-negative values have a type of
+// |V_ASN1_INTEGER| or |V_ASN1_ENUMERATED|, while negative values have a type of
+// |V_ASN1_NEG_INTEGER| or |V_ASN1_NEG_ENUMERATED|. Note this differs from DER's
+// two's complement representation.
+
+// ASN1_INTEGER_set sets |a| to an INTEGER with value |v|. It returns one on
+// success and zero on error.
+OPENSSL_EXPORT int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
+
+// ASN1_INTEGER_set sets |a| to an INTEGER with value |v|. It returns one on
+// success and zero on error.
+OPENSSL_EXPORT int ASN1_INTEGER_set_uint64(ASN1_INTEGER *out, uint64_t v);
+
+// ASN1_INTEGER_get returns the value of |a| as a |long|, or -1 if |a| is out of
+// range or the wrong type.
+OPENSSL_EXPORT long ASN1_INTEGER_get(const ASN1_INTEGER *a);
+
+// BN_to_ASN1_INTEGER sets |ai| to an INTEGER with value |bn| and returns |ai|
+// on success or NULL or error. If |ai| is NULL, it returns a newly-allocated
+// |ASN1_INTEGER| on success instead, which the caller must release with
+// |ASN1_INTEGER_free|.
+OPENSSL_EXPORT ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn,
+                                                ASN1_INTEGER *ai);
+
+// ASN1_INTEGER_to_BN sets |bn| to the value of |ai| and returns |bn| on success
+// or NULL or error. If |bn| is NULL, it returns a newly-allocated |BIGNUM| on
+// success instead, which the caller must release with |BN_free|.
+OPENSSL_EXPORT BIGNUM *ASN1_INTEGER_to_BN(const ASN1_INTEGER *ai, BIGNUM *bn);
+
+// ASN1_INTEGER_cmp compares the values of |x| and |y|. It returns an integer
+// equal to, less than, or greater than zero if |x| is equal to, less than, or
+// greater than |y|, respectively.
+OPENSSL_EXPORT int ASN1_INTEGER_cmp(const ASN1_INTEGER *x,
+                                    const ASN1_INTEGER *y);
+
+// ASN1_ENUMERATED_set sets |a| to an ENUMERATED with value |v|. It returns one
+// on success and zero on error.
+OPENSSL_EXPORT int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v);
+
+// ASN1_INTEGER_get returns the value of |a| as a |long|, or -1 if |a| is out of
+// range or the wrong type.
+OPENSSL_EXPORT long ASN1_ENUMERATED_get(const ASN1_ENUMERATED *a);
+
+// BN_to_ASN1_ENUMERATED sets |ai| to an ENUMERATED with value |bn| and returns
+// |ai| on success or NULL or error. If |ai| is NULL, it returns a
+// newly-allocated |ASN1_INTEGER| on success instead, which the caller must
+// release with |ASN1_INTEGER_free|.
+OPENSSL_EXPORT ASN1_ENUMERATED *BN_to_ASN1_ENUMERATED(const BIGNUM *bn,
+                                                      ASN1_ENUMERATED *ai);
+
+// ASN1_ENUMERATED_to_BN sets |bn| to the value of |ai| and returns |bn| on
+// success or NULL or error. If |bn| is NULL, it returns a newly-allocated
+// |BIGNUM| on success instead, which the caller must release with |BN_free|.
+OPENSSL_EXPORT BIGNUM *ASN1_ENUMERATED_to_BN(const ASN1_ENUMERATED *ai,
+                                             BIGNUM *bn);
+
+// TODO(davidben): Expand and document function prototypes generated in macros.
+
+
+// Time.
+//
+// GeneralizedTime and UTCTime values are represented as |ASN1_STRING|s. The
+// type field is |V_ASN1_GENERALIZEDTIME| or |V_ASN1_UTCTIME|, respectively. The
+// data field contains the DER encoding of the value. For example, the UNIX
+// epoch would be "19700101000000Z" for a GeneralizedTime and "700101000000Z"
+// for a UTCTime.
+//
+// ASN.1 does not define how to interpret UTCTime's two-digit year. RFC 5280
+// defines it as a range from 1950 to 2049 for X.509. The library uses the
+// RFC 5280 interpretation. It does not currently enforce the restrictions from
+// BER, and the additional restrictions from RFC 5280, but future versions may.
+// Callers should not rely on fractional seconds and non-UTC time zones.
+//
+// The |ASN1_TIME| typedef represents the X.509 Time type, which is a CHOICE of
+// GeneralizedTime and UTCTime, using UTCTime when the value is in range.
+
+// ASN1_UTCTIME_check returns one if |a| is a valid UTCTime and zero otherwise.
+OPENSSL_EXPORT int ASN1_UTCTIME_check(const ASN1_UTCTIME *a);
+
+// ASN1_UTCTIME_set represents |t| as a UTCTime and writes the result to |s|. It
+// returns |s| on success and NULL on error. If |s| is NULL, it returns a
+// newly-allocated |ASN1_UTCTIME| instead.
+//
+// Note this function may fail if the time is out of range for UTCTime.
+OPENSSL_EXPORT ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *s, time_t t);
+
+// ASN1_UTCTIME_adj adds |offset_day| days and |offset_sec| seconds to |t| and
+// writes the result to |s| as a UTCTime. It returns |s| on success and NULL on
+// error. If |s| is NULL, it returns a newly-allocated |ASN1_UTCTIME| instead.
+//
+// Note this function may fail if the time overflows or is out of range for
+// UTCTime.
+OPENSSL_EXPORT ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t,
+                                              int offset_day, long offset_sec);
+
+// ASN1_UTCTIME_set_string sets |s| to a UTCTime whose contents are a copy of
+// |str|. It returns one on success and zero on error or if |str| is not a valid
+// UTCTime.
+//
+// If |s| is NULL, this function validates |str| without copying it.
+OPENSSL_EXPORT int ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str);
+
+// ASN1_UTCTIME_cmp_time_t compares |s| to |t|. It returns -1 if |s| < |t|, 0 if
+// they are equal, 1 if |s| > |t|, and -2 on error.
+OPENSSL_EXPORT int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t);
+
+// ASN1_GENERALIZEDTIME_check returns one if |a| is a valid GeneralizedTime and
+// zero otherwise.
+OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *a);
+
+// ASN1_GENERALIZEDTIME_set represents |t| as a GeneralizedTime and writes the
+// result to |s|. It returns |s| on success and NULL on error. If |s| is NULL,
+// it returns a newly-allocated |ASN1_GENERALIZEDTIME| instead.
+//
+// Note this function may fail if the time is out of range for GeneralizedTime.
+OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(
+    ASN1_GENERALIZEDTIME *s, time_t t);
+
+// ASN1_GENERALIZEDTIME_adj adds |offset_day| days and |offset_sec| seconds to
+// |t| and writes the result to |s| as a GeneralizedTime. It returns |s| on
+// success and NULL on error. If |s| is NULL, it returns a newly-allocated
+// |ASN1_GENERALIZEDTIME| instead.
+//
+// Note this function may fail if the time overflows or is out of range for
+// GeneralizedTime.
+OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(
+    ASN1_GENERALIZEDTIME *s, time_t t, int offset_day, long offset_sec);
+
+// ASN1_GENERALIZEDTIME_set_string sets |s| to a GeneralizedTime whose contents
+// are a copy of |str|. It returns one on success and zero on error or if |str|
+// is not a valid GeneralizedTime.
+//
+// If |s| is NULL, this function validates |str| without copying it.
+OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s,
+                                                   const char *str);
+
+// ASN1_TIME_diff computes |to| - |from|. On success, it sets |*out_days| to the
+// difference in days, rounded towards zero, sets |*out_seconds| to the
+// remainder, and returns one. On error, it returns zero.
+//
+// If |from| is before |to|, both outputs will be <= 0, with at least one
+// negative. If |from| is after |to|, both will be >= 0, with at least one
+// positive. If they are equal, ignoring fractional seconds, both will be zero.
+//
+// Note this function may fail on overflow, or if |from| or |to| cannot be
+// decoded.
+OPENSSL_EXPORT int ASN1_TIME_diff(int *out_days, int *out_seconds,
+                                  const ASN1_TIME *from, const ASN1_TIME *to);
+
+// ASN1_TIME_set represents |t| as a GeneralizedTime or UTCTime and writes
+// the result to |s|. As in RFC 5280, section 4.1.2.5, it uses UTCTime when the
+// time fits and GeneralizedTime otherwise. It returns |s| on success and NULL
+// on error. If |s| is NULL, it returns a newly-allocated |ASN1_TIME| instead.
+//
+// Note this function may fail if the time is out of range for GeneralizedTime.
+OPENSSL_EXPORT ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t);
+
+// ASN1_TIME_adj adds |offset_day| days and |offset_sec| seconds to
+// |t| and writes the result to |s|. As in RFC 5280, section 4.1.2.5, it uses
+// UTCTime when the time fits and GeneralizedTime otherwise. It returns |s| on
+// success and NULL on error. If |s| is NULL, it returns a newly-allocated
+// |ASN1_GENERALIZEDTIME| instead.
+//
+// Note this function may fail if the time overflows or is out of range for
+// GeneralizedTime.
+OPENSSL_EXPORT ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t, int offset_day,
+                                        long offset_sec);
+
+// ASN1_TIME_check returns one if |t| is a valid UTCTime or GeneralizedTime, and
+// zero otherwise. |t|'s type determines which check is performed. This
+// function does not enforce that UTCTime was used when possible.
+OPENSSL_EXPORT int ASN1_TIME_check(const ASN1_TIME *t);
+
+// ASN1_TIME_to_generalizedtime converts |t| to a GeneralizedTime. If |out| is
+// NULL, it returns a newly-allocated |ASN1_GENERALIZEDTIME| on success, or NULL
+// on error. If |out| is non-NULL and |*out| is NULL, it additionally sets
+// |*out| to the result. If |out| and |*out| are non-NULL, it instead updates
+// the object pointed by |*out| and returns |*out| on success or NULL on error.
+OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(
+    const ASN1_TIME *t, ASN1_GENERALIZEDTIME **out);
+
+// ASN1_TIME_set_string behaves like |ASN1_UTCTIME_set_string| if |str| is a
+// valid UTCTime, and |ASN1_GENERALIZEDTIME_set_string| if |str| is a valid
+// GeneralizedTime. If |str| is neither, it returns zero.
+OPENSSL_EXPORT int ASN1_TIME_set_string(ASN1_TIME *s, const char *str);
+
+// TODO(davidben): Expand and document function prototypes generated in macros.
+
+
+// Arbitrary elements.
+
+// ASN1_VALUE_st (aka |ASN1_VALUE|) is an opaque type used internally in the
+// library.
+typedef struct ASN1_VALUE_st ASN1_VALUE;
+
+// An asn1_type_st (aka |ASN1_TYPE|) represents an arbitrary ASN.1 element,
+// typically used used for ANY types. It contains a |type| field and a |value|
+// union dependent on |type|.
+//
+// WARNING: This struct has a complex representation. Callers must not construct
+// |ASN1_TYPE| values manually. Use |ASN1_TYPE_set| and |ASN1_TYPE_set1|
+// instead. Additionally, callers performing non-trivial operations on this type
+// are encouraged to use |CBS| and |CBB| from <openssl/bytestring.h>, and
+// convert to or from |ASN1_TYPE| with |d2i_ASN1_TYPE| or |i2d_ASN1_TYPE|.
+//
+// The |type| field corresponds to the tag of the ASN.1 element being
+// represented:
+//
+// If |type| is a |V_ASN1_*| constant for an ASN.1 string-like type, as defined
+// by |ASN1_STRING|, the tag matches the constant. |value| contains an
+// |ASN1_STRING| pointer (equivalently, one of the more specific typedefs). See
+// |ASN1_STRING| for details on the representation. Unlike |ASN1_STRING|,
+// |ASN1_TYPE| does not use the |V_ASN1_NEG| flag for negative INTEGER and
+// ENUMERATE values. For a negative value, the |ASN1_TYPE|'s |type| will be
+// |V_ASN1_INTEGER| or |V_ASN1_ENUMERATED|, but |value| will an |ASN1_STRING|
+// whose |type| is |V_ASN1_NEG_INTEGER| or |V_ASN1_NEG_ENUMERATED|.
+//
+// If |type| is |V_ASN1_OBJECT|, the tag is OBJECT IDENTIFIER and |value|
+// contains an |ASN1_OBJECT| pointer.
+//
+// If |type| is |V_ASN1_NULL|, the tag is NULL. |value| contains a NULL pointer.
+//
+// If |type| is |V_ASN1_BOOLEAN|, the tag is BOOLEAN. |value| contains an
+// |ASN1_BOOLEAN|.
+//
+// If |type| is |V_ASN1_SEQUENCE|, |V_ASN1_SET|, or |V_ASN1_OTHER|, the tag is
+// SEQUENCE, SET, or some non-universal tag, respectively. |value| is an
+// |ASN1_STRING| containing the entire element, including the tag and length.
+// The |ASN1_STRING|'s |type| field matches the containing |ASN1_TYPE|'s |type|.
+//
+// Other positive values of |type|, up to |V_ASN1_MAX_UNIVERSAL|, correspond to
+// universal primitive tags not directly supported by this library. |value| is
+// an |ASN1_STRING| containing the body of the element, excluding the tag
+// and length. The |ASN1_STRING|'s |type| field matches the containing
+// |ASN1_TYPE|'s |type|.
+struct asn1_type_st {
+  int type;
+  union {
+    char *ptr;
+    ASN1_BOOLEAN boolean;
+    ASN1_STRING *asn1_string;
+    ASN1_OBJECT *object;
+    ASN1_INTEGER *integer;
+    ASN1_ENUMERATED *enumerated;
+    ASN1_BIT_STRING *bit_string;
+    ASN1_OCTET_STRING *octet_string;
+    ASN1_PRINTABLESTRING *printablestring;
+    ASN1_T61STRING *t61string;
+    ASN1_IA5STRING *ia5string;
+    ASN1_GENERALSTRING *generalstring;
+    ASN1_BMPSTRING *bmpstring;
+    ASN1_UNIVERSALSTRING *universalstring;
+    ASN1_UTCTIME *utctime;
+    ASN1_GENERALIZEDTIME *generalizedtime;
+    ASN1_VISIBLESTRING *visiblestring;
+    ASN1_UTF8STRING *utf8string;
+    // set and sequence are left complete and still contain the entire element.
+    ASN1_STRING *set;
+    ASN1_STRING *sequence;
+    ASN1_VALUE *asn1_value;
+  } value;
+};
+
+// ASN1_TYPE_get returns the type of |a|, which will be one of the |V_ASN1_*|
+// constants, or zero if |a| is not fully initialized.
+OPENSSL_EXPORT int ASN1_TYPE_get(const ASN1_TYPE *a);
+
+// ASN1_TYPE_set sets |a| to an |ASN1_TYPE| of type |type| and value |value|,
+// releasing the previous contents of |a|.
+//
+// If |type| is |V_ASN1_BOOLEAN|, |a| is set to FALSE if |value| is NULL and
+// TRUE otherwise. If setting |a| to TRUE, |value| may be an invalid pointer,
+// such as (void*)1.
+//
+// If |type| is |V_ASN1_NULL|, |value| must be NULL.
+//
+// For other values of |type|, this function takes ownership of |value|, which
+// must point to an object of the corresponding type. See |ASN1_TYPE| for
+// details.
+OPENSSL_EXPORT void ASN1_TYPE_set(ASN1_TYPE *a, int type, void *value);
+
+// ASN1_TYPE_set1 behaves like |ASN1_TYPE_set| except it does not take ownership
+// of |value|. It returns one on success and zero on error.
+OPENSSL_EXPORT int ASN1_TYPE_set1(ASN1_TYPE *a, int type, const void *value);
+
+// ASN1_TYPE_cmp returns zero if |a| and |b| are equal and some non-zero value
+// otherwise. Note this function can only be used for equality checks, not an
+// ordering.
+OPENSSL_EXPORT int ASN1_TYPE_cmp(const ASN1_TYPE *a, const ASN1_TYPE *b);
+
+// TODO(davidben): Most of |ASN1_TYPE|'s APIs are hidden behind macros. Expand
+// the macros, document them, and move them to this section.
+
+
+// Human-readable output.
+//
+// The following functions output types in some human-readable format. These
+// functions may be used for debugging and logging. However, the output should
+// not be consumed programmatically. They may be ambiguous or lose information.
+
+// ASN1_UTCTIME_print writes a human-readable representation of |a| to |out|. It
+// returns one on success and zero on error.
+OPENSSL_EXPORT int ASN1_UTCTIME_print(BIO *out, const ASN1_UTCTIME *a);
+
+// ASN1_GENERALIZEDTIME_print writes a human-readable representation of |a| to
+// |out|. It returns one on success and zero on error.
+OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_print(BIO *out,
+                                              const ASN1_GENERALIZEDTIME *a);
+
+// ASN1_TIME_print writes a human-readable representation of |a| to |out|. It
+// returns one on success and zero on error.
+OPENSSL_EXPORT int ASN1_TIME_print(BIO *out, const ASN1_TIME *a);
+
+// ASN1_STRING_print writes a human-readable representation of |str| to |out|.
+// It returns one on success and zero on error. Unprintable characters are
+// replaced with '.'.
+OPENSSL_EXPORT int ASN1_STRING_print(BIO *out, const ASN1_STRING *str);
+
+// ASN1_STRFLGS_ESC_2253 causes characters to be escaped as in RFC 2253, section
+// 2.4.
+#define ASN1_STRFLGS_ESC_2253 1
+
+// ASN1_STRFLGS_ESC_CTRL causes all control characters to be escaped.
+#define ASN1_STRFLGS_ESC_CTRL 2
+
+// ASN1_STRFLGS_ESC_MSB causes all characters above 127 to be escaped.
+#define ASN1_STRFLGS_ESC_MSB 4
+
+// ASN1_STRFLGS_ESC_QUOTE causes the string to be surrounded by quotes, rather
+// than using backslashes, when characters are escaped. Fewer characters will
+// require escapes in this case.
+#define ASN1_STRFLGS_ESC_QUOTE 8
+
+// ASN1_STRFLGS_UTF8_CONVERT causes the string to be encoded as UTF-8, with each
+// byte in the UTF-8 encoding treated as an individual character for purposes of
+// escape sequences. If not set, each Unicode codepoint in the string is treated
+// as a character, with wide characters escaped as "\Uxxxx" or "\Wxxxxxxxx".
+// Note this can be ambiguous if |ASN1_STRFLGS_ESC_*| are all unset. In that
+// case, backslashes are not escaped, but wide characters are.
+#define ASN1_STRFLGS_UTF8_CONVERT 0x10
+
+// ASN1_STRFLGS_IGNORE_TYPE causes the string type to be ignored. The
+// |ASN1_STRING| in-memory representation will be printed directly.
+#define ASN1_STRFLGS_IGNORE_TYPE 0x20
+
+// ASN1_STRFLGS_SHOW_TYPE causes the string type to be included in the output.
+#define ASN1_STRFLGS_SHOW_TYPE 0x40
+
+// ASN1_STRFLGS_DUMP_ALL causes all strings to be printed as a hexdump, using
+// RFC 2253 hexstring notation, such as "#0123456789ABCDEF".
+#define ASN1_STRFLGS_DUMP_ALL 0x80
+
+// ASN1_STRFLGS_DUMP_UNKNOWN behaves like |ASN1_STRFLGS_DUMP_ALL| but only
+// applies to values of unknown type. If unset, unknown values will print
+// their contents as single-byte characters with escape sequences.
+#define ASN1_STRFLGS_DUMP_UNKNOWN 0x100
+
+// ASN1_STRFLGS_DUMP_DER causes hexdumped strings (as determined by
+// |ASN1_STRFLGS_DUMP_ALL| or |ASN1_STRFLGS_DUMP_UNKNOWN|) to print the entire
+// DER element as in RFC 2253, rather than only the contents of the
+// |ASN1_STRING|.
+#define ASN1_STRFLGS_DUMP_DER 0x200
+
+// ASN1_STRFLGS_RFC2253 causes the string to be escaped as in RFC 2253,
+// additionally escaping control characters.
+#define ASN1_STRFLGS_RFC2253                                              \
+  (ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | \
+   ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_DUMP_UNKNOWN |                \
+   ASN1_STRFLGS_DUMP_DER)
+
+// ASN1_STRING_print_ex writes a human-readable representation of |str| to
+// |out|. It returns the number of bytes written on success and -1 on error. If
+// |out| is NULL, it returns the number of bytes it would have written, without
+// writing anything.
+//
+// The |flags| should be a combination of combination of |ASN1_STRFLGS_*|
+// constants. See the documentation for each flag for how it controls the
+// output. If unsure, use |ASN1_STRFLGS_RFC2253|.
+OPENSSL_EXPORT int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str,
+                                        unsigned long flags);
+
+// ASN1_STRING_print_ex_fp behaves like |ASN1_STRING_print_ex| but writes to a
+// |FILE| rather than a |BIO|.
+OPENSSL_EXPORT int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str,
+                                           unsigned long flags);
 
 
 // Underdocumented functions.
 //
 // The following functions are not yet documented and organized.
 
-// For use with d2i_ASN1_type_bytes()
-#define B_ASN1_NUMERICSTRING 0x0001
-#define B_ASN1_PRINTABLESTRING 0x0002
-#define B_ASN1_T61STRING 0x0004
-#define B_ASN1_TELETEXSTRING 0x0004
-#define B_ASN1_VIDEOTEXSTRING 0x0008
-#define B_ASN1_IA5STRING 0x0010
-#define B_ASN1_GRAPHICSTRING 0x0020
-#define B_ASN1_ISO64STRING 0x0040
-#define B_ASN1_VISIBLESTRING 0x0040
-#define B_ASN1_GENERALSTRING 0x0080
-#define B_ASN1_UNIVERSALSTRING 0x0100
-#define B_ASN1_OCTET_STRING 0x0200
-#define B_ASN1_BIT_STRING 0x0400
-#define B_ASN1_BMPSTRING 0x0800
-#define B_ASN1_UNKNOWN 0x1000
-#define B_ASN1_UTF8STRING 0x2000
-#define B_ASN1_UTCTIME 0x4000
-#define B_ASN1_GENERALIZEDTIME 0x8000
-#define B_ASN1_SEQUENCE 0x10000
-
-// For use with ASN1_mbstring_copy()
-#define MBSTRING_FLAG 0x1000
-#define MBSTRING_UTF8 (MBSTRING_FLAG)
-// |MBSTRING_ASC| refers to Latin-1, not ASCII. It is used with TeletexString
-// which, in turn, is treated as Latin-1 rather than T.61 by OpenSSL and most
-// other software.
-#define MBSTRING_ASC (MBSTRING_FLAG | 1)
-#define MBSTRING_BMP (MBSTRING_FLAG | 2)
-#define MBSTRING_UNIV (MBSTRING_FLAG | 4)
-
-#define DECLARE_ASN1_SET_OF(type)    // filled in by mkstack.pl
-#define IMPLEMENT_ASN1_SET_OF(type)  // nothing, no longer needed
-
-// These are used internally in the ASN1_OBJECT to keep track of
-// whether the names and data need to be free()ed
-#define ASN1_OBJECT_FLAG_DYNAMIC 0x01          // internal use
-#define ASN1_OBJECT_FLAG_DYNAMIC_STRINGS 0x04  // internal use
-#define ASN1_OBJECT_FLAG_DYNAMIC_DATA 0x08     // internal use
-struct asn1_object_st {
-  const char *sn, *ln;
-  int nid;
-  int length;
-  const unsigned char *data;  // data remains const after init
-  int flags;                  // Should we free this one
-};
-
 DEFINE_STACK_OF(ASN1_OBJECT)
 
 // ASN1_ENCODING structure: this is used to save the received
@@ -386,10 +865,6 @@
 
 #define STABLE_FLAGS_MALLOC 0x01
 #define STABLE_NO_MASK 0x02
-#define DIRSTRING_TYPE                                            \
-  (B_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_BMPSTRING | \
-   B_ASN1_UTF8STRING)
-#define PKCS9STRING_TYPE (DIRSTRING_TYPE | B_ASN1_IA5STRING)
 
 typedef struct asn1_string_table_st {
   int nid;
@@ -399,23 +874,10 @@
   unsigned long flags;
 } ASN1_STRING_TABLE;
 
-// size limits: this stuff is taken straight from RFC2459
-
-#define ub_name 32768
-#define ub_common_name 64
-#define ub_locality_name 128
-#define ub_state_name 128
-#define ub_organization_name 64
-#define ub_organization_unit_name 64
-#define ub_title 64
-#define ub_email_address 128
-
 // Declarations for template structures: for full definitions
 // see asn1t.h
 typedef struct ASN1_TEMPLATE_st ASN1_TEMPLATE;
 typedef struct ASN1_TLC_st ASN1_TLC;
-// This is just an opaque pointer
-typedef struct ASN1_VALUE_st ASN1_VALUE;
 
 // Declare ASN1 functions: the implement macro in in asn1t.h
 
@@ -509,129 +971,15 @@
 
 #define DECLARE_ASN1_ITEM(name) extern OPENSSL_EXPORT const ASN1_ITEM name##_it;
 
-// Parameters used by ASN1_STRING_print_ex()
-
-// These determine which characters to escape:
-// RFC2253 special characters, control characters and
-// MSB set characters
-
-#define ASN1_STRFLGS_ESC_2253 1
-#define ASN1_STRFLGS_ESC_CTRL 2
-#define ASN1_STRFLGS_ESC_MSB 4
-
-
-// This flag determines how we do escaping: normally
-// RC2253 backslash only, set this to use backslash and
-// quote.
-
-#define ASN1_STRFLGS_ESC_QUOTE 8
-
-
-// These three flags are internal use only.
-
-// Character is a valid PrintableString character
-#define CHARTYPE_PRINTABLESTRING 0x10
-// Character needs escaping if it is the first character
-#define CHARTYPE_FIRST_ESC_2253 0x20
-// Character needs escaping if it is the last character
-#define CHARTYPE_LAST_ESC_2253 0x40
-
-// NB the internal flags are safely reused below by flags
-// handled at the top level.
-
-// If this is set we convert all character strings
-// to UTF8 first
-
-#define ASN1_STRFLGS_UTF8_CONVERT 0x10
-
-// If this is set we don't attempt to interpret content:
-// just assume all strings are 1 byte per character. This
-// will produce some pretty odd looking output!
-
-#define ASN1_STRFLGS_IGNORE_TYPE 0x20
-
-// If this is set we include the string type in the output
-#define ASN1_STRFLGS_SHOW_TYPE 0x40
-
-// This determines which strings to display and which to
-// 'dump' (hex dump of content octets or DER encoding). We can
-// only dump non character strings or everything. If we
-// don't dump 'unknown' they are interpreted as character
-// strings with 1 octet per character and are subject to
-// the usual escaping options.
-
-#define ASN1_STRFLGS_DUMP_ALL 0x80
-#define ASN1_STRFLGS_DUMP_UNKNOWN 0x100
-
-// These determine what 'dumping' does, we can dump the
-// content octets or the DER encoding: both use the
-// RFC2253 #XXXXX notation.
-
-#define ASN1_STRFLGS_DUMP_DER 0x200
-
-// All the string flags consistent with RFC2253,
-// escaping control characters isn't essential in
-// RFC2253 but it is advisable anyway.
-
-#define ASN1_STRFLGS_RFC2253                                              \
-  (ASN1_STRFLGS_ESC_2253 | ASN1_STRFLGS_ESC_CTRL | ASN1_STRFLGS_ESC_MSB | \
-   ASN1_STRFLGS_UTF8_CONVERT | ASN1_STRFLGS_DUMP_UNKNOWN |                \
-   ASN1_STRFLGS_DUMP_DER)
-
 DEFINE_STACK_OF(ASN1_INTEGER)
-DECLARE_ASN1_SET_OF(ASN1_INTEGER)
-
-struct asn1_type_st {
-  int type;
-  union {
-    char *ptr;
-    ASN1_BOOLEAN boolean;
-    ASN1_STRING *asn1_string;
-    ASN1_OBJECT *object;
-    ASN1_INTEGER *integer;
-    ASN1_ENUMERATED *enumerated;
-    ASN1_BIT_STRING *bit_string;
-    ASN1_OCTET_STRING *octet_string;
-    ASN1_PRINTABLESTRING *printablestring;
-    ASN1_T61STRING *t61string;
-    ASN1_IA5STRING *ia5string;
-    ASN1_GENERALSTRING *generalstring;
-    ASN1_BMPSTRING *bmpstring;
-    ASN1_UNIVERSALSTRING *universalstring;
-    ASN1_UTCTIME *utctime;
-    ASN1_GENERALIZEDTIME *generalizedtime;
-    ASN1_VISIBLESTRING *visiblestring;
-    ASN1_UTF8STRING *utf8string;
-    // set and sequence are left complete and still
-    // contain the set or sequence bytes
-    ASN1_STRING *set;
-    ASN1_STRING *sequence;
-    ASN1_VALUE *asn1_value;
-  } value;
-};
 
 DEFINE_STACK_OF(ASN1_TYPE)
-DECLARE_ASN1_SET_OF(ASN1_TYPE)
 
 typedef STACK_OF(ASN1_TYPE) ASN1_SEQUENCE_ANY;
 
 DECLARE_ASN1_ENCODE_FUNCTIONS_const(ASN1_SEQUENCE_ANY, ASN1_SEQUENCE_ANY)
 DECLARE_ASN1_ENCODE_FUNCTIONS_const(ASN1_SEQUENCE_ANY, ASN1_SET_ANY)
 
-struct X509_algor_st {
-  ASN1_OBJECT *algorithm;
-  ASN1_TYPE *parameter;
-} /* X509_ALGOR */;
-
-DECLARE_ASN1_FUNCTIONS(X509_ALGOR)
-
-// This is used to contain a list of bit names
-typedef struct BIT_STRING_BITNAME_st {
-  int bitnum;
-  const char *lname;
-  const char *sname;
-} BIT_STRING_BITNAME;
-
 // M_ASN1_* are legacy aliases for various |ASN1_STRING| functions. Use the
 // functions themselves.
 #define M_ASN1_STRING_length(x) ASN1_STRING_length(x)
@@ -696,11 +1044,6 @@
 
 DECLARE_ASN1_FUNCTIONS_fname(ASN1_TYPE, ASN1_ANY, ASN1_TYPE)
 
-OPENSSL_EXPORT int ASN1_TYPE_get(const ASN1_TYPE *a);
-OPENSSL_EXPORT void ASN1_TYPE_set(ASN1_TYPE *a, int type, void *value);
-OPENSSL_EXPORT int ASN1_TYPE_set1(ASN1_TYPE *a, int type, const void *value);
-OPENSSL_EXPORT int ASN1_TYPE_cmp(const ASN1_TYPE *a, const ASN1_TYPE *b);
-
 OPENSSL_EXPORT ASN1_OBJECT *ASN1_OBJECT_new(void);
 OPENSSL_EXPORT void ASN1_OBJECT_free(ASN1_OBJECT *a);
 OPENSSL_EXPORT int i2d_ASN1_OBJECT(const ASN1_OBJECT *a, unsigned char **pp);
@@ -713,21 +1056,12 @@
 
 DECLARE_ASN1_ITEM(ASN1_OBJECT)
 
-DECLARE_ASN1_SET_OF(ASN1_OBJECT)
-
 DECLARE_ASN1_FUNCTIONS(ASN1_BIT_STRING)
 OPENSSL_EXPORT int i2c_ASN1_BIT_STRING(const ASN1_BIT_STRING *a,
                                        unsigned char **pp);
 OPENSSL_EXPORT ASN1_BIT_STRING *c2i_ASN1_BIT_STRING(ASN1_BIT_STRING **a,
                                                     const unsigned char **pp,
                                                     long length);
-OPENSSL_EXPORT int ASN1_BIT_STRING_set(ASN1_BIT_STRING *a, unsigned char *d,
-                                       int length);
-OPENSSL_EXPORT int ASN1_BIT_STRING_set_bit(ASN1_BIT_STRING *a, int n,
-                                           int value);
-OPENSSL_EXPORT int ASN1_BIT_STRING_get_bit(const ASN1_BIT_STRING *a, int n);
-OPENSSL_EXPORT int ASN1_BIT_STRING_check(const ASN1_BIT_STRING *a,
-                                         unsigned char *flags, int flags_len);
 
 OPENSSL_EXPORT int i2d_ASN1_BOOLEAN(int a, unsigned char **pp);
 OPENSSL_EXPORT int d2i_ASN1_BOOLEAN(int *a, const unsigned char **pp,
@@ -739,31 +1073,9 @@
                                               const unsigned char **pp,
                                               long length);
 OPENSSL_EXPORT ASN1_INTEGER *ASN1_INTEGER_dup(const ASN1_INTEGER *x);
-OPENSSL_EXPORT int ASN1_INTEGER_cmp(const ASN1_INTEGER *x,
-                                    const ASN1_INTEGER *y);
 
 DECLARE_ASN1_FUNCTIONS(ASN1_ENUMERATED)
 
-OPENSSL_EXPORT int ASN1_UTCTIME_check(const ASN1_UTCTIME *a);
-OPENSSL_EXPORT ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *s, time_t t);
-OPENSSL_EXPORT ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t,
-                                              int offset_day, long offset_sec);
-OPENSSL_EXPORT int ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str);
-OPENSSL_EXPORT int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t);
-#if 0
-time_t ASN1_UTCTIME_get(const ASN1_UTCTIME *s);
-#endif
-
-OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *a);
-OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(
-    ASN1_GENERALIZEDTIME *s, time_t t);
-OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(
-    ASN1_GENERALIZEDTIME *s, time_t t, int offset_day, long offset_sec);
-OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s,
-                                                   const char *str);
-OPENSSL_EXPORT int ASN1_TIME_diff(int *pday, int *psec, const ASN1_TIME *from,
-                                  const ASN1_TIME *to);
-
 DECLARE_ASN1_FUNCTIONS(ASN1_OCTET_STRING)
 OPENSSL_EXPORT ASN1_OCTET_STRING *ASN1_OCTET_STRING_dup(
     const ASN1_OCTET_STRING *a);
@@ -790,14 +1102,6 @@
 DECLARE_ASN1_FUNCTIONS(ASN1_GENERALIZEDTIME)
 DECLARE_ASN1_FUNCTIONS(ASN1_TIME)
 
-OPENSSL_EXPORT ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t);
-OPENSSL_EXPORT ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t, int offset_day,
-                                        long offset_sec);
-OPENSSL_EXPORT int ASN1_TIME_check(const ASN1_TIME *t);
-OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(
-    const ASN1_TIME *t, ASN1_GENERALIZEDTIME **out);
-OPENSSL_EXPORT int ASN1_TIME_set_string(ASN1_TIME *s, const char *str);
-
 OPENSSL_EXPORT int i2a_ASN1_INTEGER(BIO *bp, const ASN1_INTEGER *a);
 OPENSSL_EXPORT int i2a_ASN1_ENUMERATED(BIO *bp, const ASN1_ENUMERATED *a);
 OPENSSL_EXPORT int i2a_ASN1_OBJECT(BIO *bp, const ASN1_OBJECT *a);
@@ -805,27 +1109,16 @@
 OPENSSL_EXPORT int i2t_ASN1_OBJECT(char *buf, int buf_len,
                                    const ASN1_OBJECT *a);
 
-OPENSSL_EXPORT ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data,
+OPENSSL_EXPORT ASN1_OBJECT *ASN1_OBJECT_create(int nid,
+                                               const unsigned char *data,
                                                int len, const char *sn,
                                                const char *ln);
 
-OPENSSL_EXPORT int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
-OPENSSL_EXPORT int ASN1_INTEGER_set_uint64(ASN1_INTEGER *out, uint64_t v);
-OPENSSL_EXPORT long ASN1_INTEGER_get(const ASN1_INTEGER *a);
-OPENSSL_EXPORT ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn,
-                                                ASN1_INTEGER *ai);
-OPENSSL_EXPORT BIGNUM *ASN1_INTEGER_to_BN(const ASN1_INTEGER *ai, BIGNUM *bn);
-
-OPENSSL_EXPORT int ASN1_ENUMERATED_set(ASN1_ENUMERATED *a, long v);
-OPENSSL_EXPORT long ASN1_ENUMERATED_get(const ASN1_ENUMERATED *a);
-OPENSSL_EXPORT ASN1_ENUMERATED *BN_to_ASN1_ENUMERATED(const BIGNUM *bn,
-                                                      ASN1_ENUMERATED *ai);
-OPENSSL_EXPORT BIGNUM *ASN1_ENUMERATED_to_BN(const ASN1_ENUMERATED *ai,
-                                             BIGNUM *bn);
-
-// General
-// given a string, return the correct type, max is the maximum length
-OPENSSL_EXPORT int ASN1_PRINTABLE_type(const unsigned char *s, int max);
+// ASN1_PRINTABLE_type interprets |len| bytes from |s| as a Latin-1 string. It
+// returns the first of |V_ASN1_PRINTABLESTRING|, |V_ASN1_IA5STRING|, or
+// |V_ASN1_T61STRING| that can represent every character. If |len| is negative,
+// |strlen(s)| is used instead.
+OPENSSL_EXPORT int ASN1_PRINTABLE_type(const unsigned char *s, int len);
 
 OPENSSL_EXPORT unsigned long ASN1_tag2bit(int tag);
 
@@ -839,25 +1132,11 @@
 
 OPENSSL_EXPORT void *ASN1_item_dup(const ASN1_ITEM *it, void *x);
 
-#ifndef OPENSSL_NO_FP_API
 OPENSSL_EXPORT void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x);
 OPENSSL_EXPORT int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x);
-OPENSSL_EXPORT int ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str,
-                                           unsigned long flags);
-#endif
-
-OPENSSL_EXPORT int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in);
 
 OPENSSL_EXPORT void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x);
 OPENSSL_EXPORT int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x);
-OPENSSL_EXPORT int ASN1_UTCTIME_print(BIO *fp, const ASN1_UTCTIME *a);
-OPENSSL_EXPORT int ASN1_GENERALIZEDTIME_print(BIO *fp,
-                                              const ASN1_GENERALIZEDTIME *a);
-OPENSSL_EXPORT int ASN1_TIME_print(BIO *fp, const ASN1_TIME *a);
-OPENSSL_EXPORT int ASN1_STRING_print(BIO *bp, const ASN1_STRING *v);
-OPENSSL_EXPORT int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str,
-                                        unsigned long flags);
-OPENSSL_EXPORT const char *ASN1_tag2str(int tag);
 
 // Used to load and write netscape format cert
 
@@ -867,16 +1146,14 @@
 OPENSSL_EXPORT ASN1_STRING *ASN1_item_pack(void *obj, const ASN1_ITEM *it,
                                            ASN1_OCTET_STRING **oct);
 
+// ASN1_STRING_set_default_mask does nothing.
 OPENSSL_EXPORT void ASN1_STRING_set_default_mask(unsigned long mask);
+
+// ASN1_STRING_set_default_mask_asc returns one.
 OPENSSL_EXPORT int ASN1_STRING_set_default_mask_asc(const char *p);
+
+// ASN1_STRING_get_default_mask returns |B_ASN1_UTF8STRING|.
 OPENSSL_EXPORT unsigned long ASN1_STRING_get_default_mask(void);
-OPENSSL_EXPORT int ASN1_mbstring_copy(ASN1_STRING **out,
-                                      const unsigned char *in, int len,
-                                      int inform, unsigned long mask);
-OPENSSL_EXPORT int ASN1_mbstring_ncopy(ASN1_STRING **out,
-                                       const unsigned char *in, int len,
-                                       int inform, unsigned long mask,
-                                       long minsize, long maxsize);
 
 OPENSSL_EXPORT ASN1_STRING *ASN1_STRING_set_by_NID(ASN1_STRING **out,
                                                    const unsigned char *in,
diff --git a/deps/boringssl/src/include/openssl/asn1t.h b/deps/boringssl/src/include/openssl/asn1t.h
index c5e2685..20c1e95 100644
--- a/deps/boringssl/src/include/openssl/asn1t.h
+++ b/deps/boringssl/src/include/openssl/asn1t.h
@@ -389,13 +389,6 @@
 /* Field is a SEQUENCE OF */
 #define ASN1_TFLG_SEQUENCE_OF	(0x2 << 1)
 
-/* Special case: this refers to a SET OF that
- * will be sorted into DER order when encoded *and*
- * the corresponding STACK will be modified to match
- * the new order.
- */
-#define ASN1_TFLG_SET_ORDER	(0x3 << 1)
-
 /* Mask for SET OF or SEQUENCE OF */
 #define ASN1_TFLG_SK_MASK	(0x3 << 1)
 
diff --git a/deps/boringssl/src/include/openssl/base.h b/deps/boringssl/src/include/openssl/base.h
index 90924e6..dd6a146 100644
--- a/deps/boringssl/src/include/openssl/base.h
+++ b/deps/boringssl/src/include/openssl/base.h
@@ -105,6 +105,10 @@
 #elif defined(__MIPSEL__) && defined(__LP64__)
 #define OPENSSL_64_BIT
 #define OPENSSL_MIPS64
+#elif defined(__riscv) && __SIZEOF_POINTER__ == 8
+#define OPENSSL_64_BIT
+#elif defined(__riscv) && __SIZEOF_POINTER__ == 4
+#define OPENSSL_32_BIT
 #elif defined(__pnacl__)
 #define OPENSSL_32_BIT
 #define OPENSSL_PNACL
@@ -156,10 +160,10 @@
 
 #if defined(__ANDROID_API__)
 #define OPENSSL_ANDROID
-#if defined(BORINGSSL_FIPS)
-// The FIPS module on Android passively receives entropy.
-#define BORINGSSL_FIPS_PASSIVE_ENTROPY
 #endif
+
+#if defined(__FreeBSD__)
+#define OPENSSL_FREEBSD
 #endif
 
 // BoringSSL requires platform's locking APIs to make internal global state
@@ -191,7 +195,7 @@
 // A consumer may use this symbol in the preprocessor to temporarily build
 // against multiple revisions of BoringSSL at the same time. It is not
 // recommended to do so for longer than is necessary.
-#define BORINGSSL_API_VERSION 14
+#define BORINGSSL_API_VERSION 16
 
 #if defined(BORINGSSL_SHARED_LIBRARY)
 
@@ -361,14 +365,12 @@
 typedef struct X509_POLICY_TREE_st X509_POLICY_TREE;
 typedef struct X509_VERIFY_PARAM_st X509_VERIFY_PARAM;
 typedef struct X509_algor_st X509_ALGOR;
-typedef struct X509_crl_info_st X509_CRL_INFO;
 typedef struct X509_crl_st X509_CRL;
 typedef struct X509_extension_st X509_EXTENSION;
 typedef struct X509_info_st X509_INFO;
 typedef struct X509_name_entry_st X509_NAME_ENTRY;
 typedef struct X509_name_st X509_NAME;
 typedef struct X509_pubkey_st X509_PUBKEY;
-typedef struct X509_req_info_st X509_REQ_INFO;
 typedef struct X509_req_st X509_REQ;
 typedef struct X509_sig_st X509_SIG;
 typedef struct X509_val_st X509_VAL;
@@ -401,6 +403,11 @@
 typedef struct evp_cipher_ctx_st EVP_CIPHER_CTX;
 typedef struct evp_cipher_st EVP_CIPHER;
 typedef struct evp_encode_ctx_st EVP_ENCODE_CTX;
+typedef struct evp_hpke_aead_st EVP_HPKE_AEAD;
+typedef struct evp_hpke_ctx_st EVP_HPKE_CTX;
+typedef struct evp_hpke_kdf_st EVP_HPKE_KDF;
+typedef struct evp_hpke_kem_st EVP_HPKE_KEM;
+typedef struct evp_hpke_key_st EVP_HPKE_KEY;
 typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD;
 typedef struct evp_pkey_ctx_st EVP_PKEY_CTX;
 typedef struct evp_pkey_method_st EVP_PKEY_METHOD;
@@ -423,6 +430,7 @@
 typedef struct srtp_protection_profile_st SRTP_PROTECTION_PROFILE;
 typedef struct ssl_cipher_st SSL_CIPHER;
 typedef struct ssl_ctx_st SSL_CTX;
+typedef struct ssl_ech_keys_st SSL_ECH_KEYS;
 typedef struct ssl_method_st SSL_METHOD;
 typedef struct ssl_private_key_method_st SSL_PRIVATE_KEY_METHOD;
 typedef struct ssl_quic_method_st SSL_QUIC_METHOD;
@@ -437,9 +445,10 @@
 typedef struct v3_ext_ctx X509V3_CTX;
 typedef struct x509_attributes_st X509_ATTRIBUTE;
 typedef struct x509_cert_aux_st X509_CERT_AUX;
-typedef struct x509_cinf_st X509_CINF;
 typedef struct x509_crl_method_st X509_CRL_METHOD;
 typedef struct x509_lookup_st X509_LOOKUP;
+typedef struct x509_lookup_method_st X509_LOOKUP_METHOD;
+typedef struct x509_object_st X509_OBJECT;
 typedef struct x509_revoked_st X509_REVOKED;
 typedef struct x509_st X509;
 typedef struct x509_store_ctx_st X509_STORE_CTX;
@@ -528,8 +537,39 @@
   StackAllocated() { init(&ctx_); }
   ~StackAllocated() { cleanup(&ctx_); }
 
-  StackAllocated(const StackAllocated<T, CleanupRet, init, cleanup> &) = delete;
-  T& operator=(const StackAllocated<T, CleanupRet, init, cleanup> &) = delete;
+  StackAllocated(const StackAllocated &) = delete;
+  StackAllocated& operator=(const StackAllocated &) = delete;
+
+  T *get() { return &ctx_; }
+  const T *get() const { return &ctx_; }
+
+  T *operator->() { return &ctx_; }
+  const T *operator->() const { return &ctx_; }
+
+  void Reset() {
+    cleanup(&ctx_);
+    init(&ctx_);
+  }
+
+ private:
+  T ctx_;
+};
+
+template <typename T, typename CleanupRet, void (*init)(T *),
+          CleanupRet (*cleanup)(T *), void (*move)(T *, T *)>
+class StackAllocatedMovable {
+ public:
+  StackAllocatedMovable() { init(&ctx_); }
+  ~StackAllocatedMovable() { cleanup(&ctx_); }
+
+  StackAllocatedMovable(StackAllocatedMovable &&other) {
+    init(&ctx_);
+    move(&ctx_, &other.ctx_);
+  }
+  StackAllocatedMovable &operator=(StackAllocatedMovable &&other) {
+    move(&ctx_, &other.ctx_);
+    return *this;
+  }
 
   T *get() { return &ctx_; }
   const T *get() const { return &ctx_; }
diff --git a/deps/boringssl/src/include/openssl/bio.h b/deps/boringssl/src/include/openssl/bio.h
index f25492a..18bc893 100644
--- a/deps/boringssl/src/include/openssl/bio.h
+++ b/deps/boringssl/src/include/openssl/bio.h
@@ -377,7 +377,9 @@
 OPENSSL_EXPORT const BIO_METHOD *BIO_s_mem(void);
 
 // BIO_new_mem_buf creates read-only BIO that reads from |len| bytes at |buf|.
-// It does not take ownership of |buf|. It returns the BIO or NULL on error.
+// It returns the BIO or NULL on error. This function does not copy or take
+// ownership of |buf|. The caller must ensure the memory pointed to by |buf|
+// outlives the |BIO|.
 //
 // If |len| is negative, then |buf| is treated as a NUL-terminated string, but
 // don't depend on this in new code.
diff --git a/deps/boringssl/src/include/openssl/bytestring.h b/deps/boringssl/src/include/openssl/bytestring.h
index 5ae0348..5ef3742 100644
--- a/deps/boringssl/src/include/openssl/bytestring.h
+++ b/deps/boringssl/src/include/openssl/bytestring.h
@@ -51,6 +51,7 @@
   // Defining any constructors requires we explicitly default the others.
   cbs_st() = default;
   cbs_st(const cbs_st &) = default;
+  cbs_st &operator=(const cbs_st &) = default;
 #endif
 };
 
@@ -153,6 +154,11 @@
 // returns one on success and zero on error.
 OPENSSL_EXPORT int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out);
 
+// CBS_get_until_first finds the first instance of |c| in |cbs|. If found, it
+// sets |*out| to the text before the match, advances |cbs| over it, and returns
+// one. Otherwise, it returns zero and leaves |cbs| unmodified.
+OPENSSL_EXPORT int CBS_get_until_first(CBS *cbs, CBS *out, uint8_t c);
+
 
 // Parsing ASN.1
 //
@@ -462,6 +468,10 @@
 // success and zero otherwise.
 OPENSSL_EXPORT int CBB_add_bytes(CBB *cbb, const uint8_t *data, size_t len);
 
+// CBB_add_zeros append |len| bytes with value zero to |cbb|. It returns one on
+// success and zero otherwise.
+OPENSSL_EXPORT int CBB_add_zeros(CBB *cbb, size_t len);
+
 // CBB_add_space appends |len| bytes to |cbb| and sets |*out_data| to point to
 // the beginning of that space. The caller must then write |len| bytes of
 // actual contents to |*out_data|. It returns one on success and zero
diff --git a/deps/boringssl/src/include/openssl/chacha.h b/deps/boringssl/src/include/openssl/chacha.h
index 684fc5b..cfbaa75 100644
--- a/deps/boringssl/src/include/openssl/chacha.h
+++ b/deps/boringssl/src/include/openssl/chacha.h
@@ -23,7 +23,7 @@
 
 // ChaCha20.
 //
-// ChaCha20 is a stream cipher. See https://tools.ietf.org/html/rfc7539.
+// ChaCha20 is a stream cipher. See https://tools.ietf.org/html/rfc8439.
 
 
 // CRYPTO_chacha_20 encrypts |in_len| bytes from |in| with the given key and
diff --git a/deps/boringssl/src/include/openssl/cipher.h b/deps/boringssl/src/include/openssl/cipher.h
index c6bec48..badd496 100644
--- a/deps/boringssl/src/include/openssl/cipher.h
+++ b/deps/boringssl/src/include/openssl/cipher.h
@@ -556,10 +556,6 @@
   // final_used is non-zero if the |final| buffer contains plaintext.
   int final_used;
 
-  // block_mask contains |cipher->block_size| minus one. (The block size
-  // assumed to be a power of two.)
-  int block_mask;
-
   uint8_t final[EVP_MAX_BLOCK_LENGTH];  // possible final block
 } /* EVP_CIPHER_CTX */;
 
diff --git a/deps/boringssl/src/include/openssl/conf.h b/deps/boringssl/src/include/openssl/conf.h
index ae71869..6890c7d 100644
--- a/deps/boringssl/src/include/openssl/conf.h
+++ b/deps/boringssl/src/include/openssl/conf.h
@@ -100,22 +100,25 @@
 // |conf|. It returns one on success and zero on error. In the event of an
 // error, if |out_error_line| is not NULL, |*out_error_line| is set to the
 // number of the line that contained the error.
-int NCONF_load(CONF *conf, const char *filename, long *out_error_line);
+OPENSSL_EXPORT int NCONF_load(CONF *conf, const char *filename,
+                              long *out_error_line);
 
 // NCONF_load_bio acts like |NCONF_load| but reads from |bio| rather than from
 // a named file.
-int NCONF_load_bio(CONF *conf, BIO *bio, long *out_error_line);
+OPENSSL_EXPORT int NCONF_load_bio(CONF *conf, BIO *bio, long *out_error_line);
 
 // NCONF_get_section returns a stack of values for a given section in |conf|.
 // If |section| is NULL, the default section is returned. It returns NULL on
 // error.
-STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf, const char *section);
+OPENSSL_EXPORT STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf,
+                                                       const char *section);
 
 // NCONF_get_string returns the value of the key |name|, in section |section|.
 // The |section| argument may be NULL to indicate the default section. It
 // returns the value or NULL on error.
-const char *NCONF_get_string(const CONF *conf, const char *section,
-                             const char *name);
+OPENSSL_EXPORT const char *NCONF_get_string(const CONF *conf,
+                                            const char *section,
+                                            const char *name);
 
 
 // Utility functions
diff --git a/deps/boringssl/src/include/openssl/cpu.h b/deps/boringssl/src/include/openssl/cpu.h
index ae55967..91cf95e 100644
--- a/deps/boringssl/src/include/openssl/cpu.h
+++ b/deps/boringssl/src/include/openssl/cpu.h
@@ -111,26 +111,18 @@
 #endif
 
 #if !defined(OPENSSL_STATIC_ARMCAP)
-
 // CRYPTO_is_NEON_capable_at_runtime returns true if the current CPU has a NEON
 // unit. Note that |OPENSSL_armcap_P| also exists and contains the same
 // information in a form that's easier for assembly to use.
-OPENSSL_EXPORT char CRYPTO_is_NEON_capable_at_runtime(void);
+OPENSSL_EXPORT int CRYPTO_is_NEON_capable_at_runtime(void);
 
-// CRYPTO_is_NEON_capable returns true if the current CPU has a NEON unit. If
-// this is known statically then it returns one immediately.
-OPENSSL_INLINE int CRYPTO_is_NEON_capable(void) {
-  // Only statically skip the runtime lookup on aarch64. On arm, one CPU is
-  // known to have a broken NEON unit which is known to fail with on some
-  // hand-written NEON assembly. For now, continue to apply the workaround even
-  // when the compiler is instructed to freely emit NEON code. See
-  // https://crbug.com/341598 and https://crbug.com/606629.
-#if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && !defined(OPENSSL_ARM)
-  return 1;
-#else
-  return CRYPTO_is_NEON_capable_at_runtime();
-#endif
-}
+// CRYPTO_is_ARMv8_AES_capable_at_runtime returns true if the current CPU
+// supports the ARMv8 AES instruction.
+int CRYPTO_is_ARMv8_AES_capable_at_runtime(void);
+
+// CRYPTO_is_ARMv8_PMULL_capable_at_runtime returns true if the current CPU
+// supports the ARMv8 PMULL instruction.
+int CRYPTO_is_ARMv8_PMULL_capable_at_runtime(void);
 
 #if defined(OPENSSL_ARM)
 // CRYPTO_has_broken_NEON returns one if the current CPU is known to have a
@@ -141,43 +133,41 @@
 // workaround was needed. See https://crbug.com/boringssl/46.
 OPENSSL_EXPORT int CRYPTO_needs_hwcap2_workaround(void);
 #endif
+#endif  // !OPENSSL_STATIC_ARMCAP
 
-// CRYPTO_is_ARMv8_AES_capable returns true if the current CPU supports the
-// ARMv8 AES instruction.
-int CRYPTO_is_ARMv8_AES_capable(void);
-
-// CRYPTO_is_ARMv8_PMULL_capable returns true if the current CPU supports the
-// ARMv8 PMULL instruction.
-int CRYPTO_is_ARMv8_PMULL_capable(void);
-
-#else
-
+// CRYPTO_is_NEON_capable returns true if the current CPU has a NEON unit. If
+// this is known statically, it is a constant inline function.
 OPENSSL_INLINE int CRYPTO_is_NEON_capable(void) {
-#if defined(OPENSSL_STATIC_ARMCAP_NEON) || \
-    (defined(__ARM_NEON__) || defined(__ARM_NEON))
+#if defined(__ARM_NEON__) || defined(__ARM_NEON) || \
+    defined(OPENSSL_STATIC_ARMCAP_NEON)
   return 1;
-#else
+#elif defined(OPENSSL_STATIC_ARMCAP)
   return 0;
+#else
+  return CRYPTO_is_NEON_capable_at_runtime();
 #endif
 }
 
 OPENSSL_INLINE int CRYPTO_is_ARMv8_AES_capable(void) {
 #if defined(OPENSSL_STATIC_ARMCAP_AES) || defined(__ARM_FEATURE_CRYPTO)
   return 1;
-#else
+#elif defined(OPENSSL_STATIC_ARMCAP)
   return 0;
+#else
+  return CRYPTO_is_ARMv8_AES_capable_at_runtime();
 #endif
 }
 
 OPENSSL_INLINE int CRYPTO_is_ARMv8_PMULL_capable(void) {
 #if defined(OPENSSL_STATIC_ARMCAP_PMULL) || defined(__ARM_FEATURE_CRYPTO)
   return 1;
-#else
+#elif defined(OPENSSL_STATIC_ARMCAP)
   return 0;
+#else
+  return CRYPTO_is_ARMv8_PMULL_capable_at_runtime();
 #endif
 }
 
-#endif  // OPENSSL_STATIC_ARMCAP
 #endif  // OPENSSL_ARM || OPENSSL_AARCH64
 
 #if defined(OPENSSL_PPC64LE)
diff --git a/deps/boringssl/src/include/openssl/crypto.h b/deps/boringssl/src/include/openssl/crypto.h
index b820e40..93b1a9b 100644
--- a/deps/boringssl/src/include/openssl/crypto.h
+++ b/deps/boringssl/src/include/openssl/crypto.h
@@ -55,10 +55,6 @@
 // in which case it returns zero.
 OPENSSL_EXPORT int CRYPTO_has_asm(void);
 
-// FIPS_mode returns zero unless BoringSSL is built with BORINGSSL_FIPS, in
-// which case it returns one.
-OPENSSL_EXPORT int FIPS_mode(void);
-
 // BORINGSSL_self_test triggers the FIPS KAT-based self tests. It returns one on
 // success and zero on error.
 OPENSSL_EXPORT int BORINGSSL_self_test(void);
@@ -72,6 +68,30 @@
 OPENSSL_EXPORT void CRYPTO_pre_sandbox_init(void);
 
 
+// FIPS monitoring
+
+// FIPS_mode returns zero unless BoringSSL is built with BORINGSSL_FIPS, in
+// which case it returns one.
+OPENSSL_EXPORT int FIPS_mode(void);
+
+// fips_counter_t denotes specific APIs/algorithms. A counter is maintained for
+// each in FIPS mode so that tests can be written to assert that the expected,
+// FIPS functions are being called by a certain peice of code.
+enum fips_counter_t {
+  fips_counter_evp_aes_128_gcm = 0,
+  fips_counter_evp_aes_256_gcm = 1,
+  fips_counter_evp_aes_128_ctr = 2,
+  fips_counter_evp_aes_256_ctr = 3,
+
+  fips_counter_max = 3,
+};
+
+// FIPS_read_counter returns a counter of the number of times the specific
+// function denoted by |counter| has been used. This always returns zero unless
+// BoringSSL was built with BORINGSSL_FIPS_COUNTERS defined.
+OPENSSL_EXPORT size_t FIPS_read_counter(enum fips_counter_t counter);
+
+
 // Deprecated functions.
 
 // OPENSSL_VERSION_TEXT contains a string the identifies the version of
diff --git a/deps/boringssl/src/include/openssl/digest.h b/deps/boringssl/src/include/openssl/digest.h
index 66f1b5d..fa76168 100644
--- a/deps/boringssl/src/include/openssl/digest.h
+++ b/deps/boringssl/src/include/openssl/digest.h
@@ -124,6 +124,10 @@
 // copy of |in|. It returns one on success and zero on allocation failure.
 OPENSSL_EXPORT int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in);
 
+// EVP_MD_CTX_move sets |out|, which must already be initialised, to the hash
+// state in |in|. |in| is mutated and left in an empty state.
+OPENSSL_EXPORT void EVP_MD_CTX_move(EVP_MD_CTX *out, EVP_MD_CTX *in);
+
 // EVP_MD_CTX_reset calls |EVP_MD_CTX_cleanup| followed by |EVP_MD_CTX_init|. It
 // returns one.
 OPENSSL_EXPORT int EVP_MD_CTX_reset(EVP_MD_CTX *ctx);
@@ -293,6 +297,9 @@
 // their needs). Thus this exists only to allow code to compile.
 #define EVP_MD_CTX_FLAG_NON_FIPS_ALLOW 0
 
+// EVP_MD_nid calls |EVP_MD_type|.
+OPENSSL_EXPORT int EVP_MD_nid(const EVP_MD *md);
+
 
 struct evp_md_pctx_ops;
 
@@ -324,8 +331,8 @@
 BORINGSSL_MAKE_DELETER(EVP_MD_CTX, EVP_MD_CTX_free)
 
 using ScopedEVP_MD_CTX =
-    internal::StackAllocated<EVP_MD_CTX, int, EVP_MD_CTX_init,
-                             EVP_MD_CTX_cleanup>;
+    internal::StackAllocatedMovable<EVP_MD_CTX, int, EVP_MD_CTX_init,
+                                    EVP_MD_CTX_cleanup, EVP_MD_CTX_move>;
 
 BSSL_NAMESPACE_END
 
diff --git a/deps/boringssl/src/include/openssl/ec.h b/deps/boringssl/src/include/openssl/ec.h
index 363c096..cc8138d 100644
--- a/deps/boringssl/src/include/openssl/ec.h
+++ b/deps/boringssl/src/include/openssl/ec.h
@@ -343,11 +343,14 @@
 OPENSSL_EXPORT int EC_GROUP_get_order(const EC_GROUP *group, BIGNUM *order,
                                       BN_CTX *ctx);
 
+#define OPENSSL_EC_EXPLICIT_CURVE 0
+#define OPENSSL_EC_NAMED_CURVE 1
+
 // EC_GROUP_set_asn1_flag does nothing.
 OPENSSL_EXPORT void EC_GROUP_set_asn1_flag(EC_GROUP *group, int flag);
 
-#define OPENSSL_EC_NAMED_CURVE 0
-#define OPENSSL_EC_EXPLICIT_CURVE 1
+// EC_GROUP_get_asn1_flag returns |OPENSSL_EC_NAMED_CURVE|.
+OPENSSL_EXPORT int EC_GROUP_get_asn1_flag(const EC_GROUP *group);
 
 typedef struct ec_method_st EC_METHOD;
 
diff --git a/deps/boringssl/src/include/openssl/ecdsa.h b/deps/boringssl/src/include/openssl/ecdsa.h
index 9cd19a5..5443ef5 100644
--- a/deps/boringssl/src/include/openssl/ecdsa.h
+++ b/deps/boringssl/src/include/openssl/ecdsa.h
@@ -73,6 +73,9 @@
 // space. On successful exit, |*sig_len| is set to the actual number of bytes
 // written. The |type| argument should be zero. It returns one on success and
 // zero otherwise.
+//
+// WARNING: |digest| must be the output of some hash function on the data to be
+// signed. Passing unhashed inputs will not result in a secure signature scheme.
 OPENSSL_EXPORT int ECDSA_sign(int type, const uint8_t *digest,
                               size_t digest_len, uint8_t *sig,
                               unsigned int *sig_len, const EC_KEY *key);
@@ -81,6 +84,10 @@
 // signature by |key| of |digest|. (The |type| argument should be zero.) It
 // returns one on success or zero if the signature is invalid or an error
 // occurred.
+//
+// WARNING: |digest| must be the output of some hash function on the data to be
+// verified. Passing unhashed inputs will not result in a secure signature
+// scheme.
 OPENSSL_EXPORT int ECDSA_verify(int type, const uint8_t *digest,
                                 size_t digest_len, const uint8_t *sig,
                                 size_t sig_len, const EC_KEY *key);
@@ -124,12 +131,19 @@
 
 // ECDSA_do_sign signs |digest_len| bytes from |digest| with |key| and returns
 // the resulting signature structure, or NULL on error.
+//
+// WARNING: |digest| must be the output of some hash function on the data to be
+// signed. Passing unhashed inputs will not result in a secure signature scheme.
 OPENSSL_EXPORT ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest,
                                         size_t digest_len, const EC_KEY *key);
 
 // ECDSA_do_verify verifies that |sig| constitutes a valid signature by |key|
 // of |digest|. It returns one on success or zero if the signature is invalid
 // or on error.
+//
+// WARNING: |digest| must be the output of some hash function on the data to be
+// verified. Passing unhashed inputs will not result in a secure signature
+// scheme.
 OPENSSL_EXPORT int ECDSA_do_verify(const uint8_t *digest, size_t digest_len,
                                    const ECDSA_SIG *sig, const EC_KEY *key);
 
@@ -162,6 +176,25 @@
 OPENSSL_EXPORT size_t ECDSA_SIG_max_len(size_t order_len);
 
 
+// Testing-only functions.
+
+// ECDSA_sign_with_nonce_and_leak_private_key_for_testing behaves like
+// |ECDSA_do_sign| but uses |nonce| for the ECDSA nonce 'k', instead of a random
+// value. |nonce| is interpreted as a big-endian integer. It must be reduced
+// modulo the group order and padded with zeros up to |BN_num_bytes(order)|
+// bytes.
+//
+// WARNING: This function is only exported for testing purposes, when using test
+// vectors or fuzzing strategies. It must not be used outside tests and may leak
+// any private keys it is used with.
+OPENSSL_EXPORT ECDSA_SIG *
+ECDSA_sign_with_nonce_and_leak_private_key_for_testing(const uint8_t *digest,
+                                                       size_t digest_len,
+                                                       const EC_KEY *eckey,
+                                                       const uint8_t *nonce,
+                                                       size_t nonce_len);
+
+
 // Deprecated functions.
 
 // d2i_ECDSA_SIG parses an ASN.1, DER-encoded, signature from |len| bytes at
diff --git a/deps/boringssl/src/include/openssl/err.h b/deps/boringssl/src/include/openssl/err.h
index 0960d80..572340c 100644
--- a/deps/boringssl/src/include/openssl/err.h
+++ b/deps/boringssl/src/include/openssl/err.h
@@ -223,11 +223,12 @@
                                         size_t len);
 
 // ERR_lib_error_string returns a string representation of the library that
-// generated |packed_error|.
+// generated |packed_error|, or a placeholder string is the library is
+// unrecognized.
 OPENSSL_EXPORT const char *ERR_lib_error_string(uint32_t packed_error);
 
 // ERR_reason_error_string returns a string representation of the reason for
-// |packed_error|.
+// |packed_error|, or a placeholder string if the reason is unrecognized.
 OPENSSL_EXPORT const char *ERR_reason_error_string(uint32_t packed_error);
 
 // ERR_print_errors_callback_t is the type of a function used by
diff --git a/deps/boringssl/src/include/openssl/evp.h b/deps/boringssl/src/include/openssl/evp.h
index 0710792..c567875 100644
--- a/deps/boringssl/src/include/openssl/evp.h
+++ b/deps/boringssl/src/include/openssl/evp.h
@@ -59,6 +59,7 @@
 
 #include <openssl/base.h>
 
+#include <openssl/evp_errors.h>
 #include <openssl/thread.h>
 
 // OpenSSL included digest and cipher functions in this header so we include
@@ -544,14 +545,15 @@
 OPENSSL_EXPORT int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx);
 
 // EVP_PKEY_sign signs |digest_len| bytes from |digest| using |ctx|. If |sig| is
-// NULL, the maximum size of the signature is written to
-// |out_sig_len|. Otherwise, |*sig_len| must contain the number of bytes of
-// space available at |sig|. If sufficient, the signature will be written to
-// |sig| and |*sig_len| updated with the true length.
+// NULL, the maximum size of the signature is written to |out_sig_len|.
+// Otherwise, |*sig_len| must contain the number of bytes of space available at
+// |sig|. If sufficient, the signature will be written to |sig| and |*sig_len|
+// updated with the true length. This function will fail for signature
+// algorithms like Ed25519 that do not support signing pre-hashed inputs.
 //
-// This function expects a pre-hashed input and will fail for signature
-// algorithms which do not support this. Use |EVP_DigestSignInit| to sign an
-// unhashed input.
+// WARNING: |digest| must be the output of some hash function on the data to be
+// signed. Passing unhashed inputs will not result in a secure signature scheme.
+// Use |EVP_DigestSignInit| to sign an unhashed input.
 //
 // WARNING: Setting |sig| to NULL only gives the maximum size of the
 // signature. The actual signature may be smaller.
@@ -569,11 +571,13 @@
 OPENSSL_EXPORT int EVP_PKEY_verify_init(EVP_PKEY_CTX *ctx);
 
 // EVP_PKEY_verify verifies that |sig_len| bytes from |sig| are a valid
-// signature for |digest|.
+// signature for |digest|. This function will fail for signature
+// algorithms like Ed25519 that do not support signing pre-hashed inputs.
 //
-// This function expects a pre-hashed input and will fail for signature
-// algorithms which do not support this. Use |EVP_DigestVerifyInit| to verify a
-// signature given the unhashed input.
+// WARNING: |digest| must be the output of some hash function on the data to be
+// verified. Passing unhashed inputs will not result in a secure signature
+// scheme. Use |EVP_DigestVerifyInit| to verify a signature given the unhashed
+// input.
 //
 // It returns one on success or zero on error.
 OPENSSL_EXPORT int EVP_PKEY_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig,
@@ -832,6 +836,11 @@
 // Ed448 and attempts to create keys will fail.
 #define EVP_PKEY_ED448 NID_ED448
 
+// EVP_PKEY_get0 returns NULL. This function is provided for compatibility with
+// OpenSSL but does not return anything. Use the typed |EVP_PKEY_get0_*|
+// functions instead.
+OPENSSL_EXPORT void *EVP_PKEY_get0(const EVP_PKEY *pkey);
+
 // OpenSSL_add_all_algorithms does nothing.
 OPENSSL_EXPORT void OpenSSL_add_all_algorithms(void);
 
@@ -858,6 +867,12 @@
                                                           void *arg),
                                          void *arg);
 
+OPENSSL_EXPORT void EVP_MD_do_all(void (*callback)(const EVP_MD *cipher,
+                                                   const char *name,
+                                                   const char *unused,
+                                                   void *arg),
+                                  void *arg);
+
 // i2d_PrivateKey marshals a private key from |key| to an ASN.1, DER
 // structure. If |outp| is not NULL then the result is written to |*outp| and
 // |*outp| is advanced just past the output. It returns the number of bytes in
@@ -1091,42 +1106,4 @@
 
 #endif
 
-#define EVP_R_BUFFER_TOO_SMALL 100
-#define EVP_R_COMMAND_NOT_SUPPORTED 101
-#define EVP_R_DECODE_ERROR 102
-#define EVP_R_DIFFERENT_KEY_TYPES 103
-#define EVP_R_DIFFERENT_PARAMETERS 104
-#define EVP_R_ENCODE_ERROR 105
-#define EVP_R_EXPECTING_AN_EC_KEY_KEY 106
-#define EVP_R_EXPECTING_AN_RSA_KEY 107
-#define EVP_R_EXPECTING_A_DSA_KEY 108
-#define EVP_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE 109
-#define EVP_R_INVALID_DIGEST_LENGTH 110
-#define EVP_R_INVALID_DIGEST_TYPE 111
-#define EVP_R_INVALID_KEYBITS 112
-#define EVP_R_INVALID_MGF1_MD 113
-#define EVP_R_INVALID_OPERATION 114
-#define EVP_R_INVALID_PADDING_MODE 115
-#define EVP_R_INVALID_PSS_SALTLEN 116
-#define EVP_R_KEYS_NOT_SET 117
-#define EVP_R_MISSING_PARAMETERS 118
-#define EVP_R_NO_DEFAULT_DIGEST 119
-#define EVP_R_NO_KEY_SET 120
-#define EVP_R_NO_MDC2_SUPPORT 121
-#define EVP_R_NO_NID_FOR_CURVE 122
-#define EVP_R_NO_OPERATION_SET 123
-#define EVP_R_NO_PARAMETERS_SET 124
-#define EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE 125
-#define EVP_R_OPERATON_NOT_INITIALIZED 126
-#define EVP_R_UNKNOWN_PUBLIC_KEY_TYPE 127
-#define EVP_R_UNSUPPORTED_ALGORITHM 128
-#define EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE 129
-#define EVP_R_NOT_A_PRIVATE_KEY 130
-#define EVP_R_INVALID_SIGNATURE 131
-#define EVP_R_MEMORY_LIMIT_EXCEEDED 132
-#define EVP_R_INVALID_PARAMETERS 133
-#define EVP_R_INVALID_PEER_KEY 134
-#define EVP_R_NOT_XOF_OR_INVALID_LENGTH 135
-#define EVP_R_EMPTY_PSK 136
-
 #endif  // OPENSSL_HEADER_EVP_H
diff --git a/deps/boringssl/src/crypto/x509/x509_r2x.c b/deps/boringssl/src/include/openssl/evp_errors.h
similarity index 66%
rename from deps/boringssl/src/crypto/x509/x509_r2x.c
rename to deps/boringssl/src/include/openssl/evp_errors.h
index a44b172..8583f52 100644
--- a/deps/boringssl/src/crypto/x509/x509_r2x.c
+++ b/deps/boringssl/src/include/openssl/evp_errors.h
@@ -54,63 +54,46 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
-#include <openssl/asn1.h>
-#include <openssl/bn.h>
-#include <openssl/digest.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/obj.h>
-#include <openssl/x509.h>
+#ifndef OPENSSL_HEADER_EVP_ERRORS_H
+#define OPENSSL_HEADER_EVP_ERRORS_H
 
-X509 *X509_REQ_to_X509(X509_REQ *r, int days, EVP_PKEY *pkey)
-{
-    X509 *ret = NULL;
-    X509_CINF *xi = NULL;
-    X509_NAME *xn;
-    EVP_PKEY *pubkey = NULL;
-    int res;
+#define EVP_R_BUFFER_TOO_SMALL 100
+#define EVP_R_COMMAND_NOT_SUPPORTED 101
+#define EVP_R_DECODE_ERROR 102
+#define EVP_R_DIFFERENT_KEY_TYPES 103
+#define EVP_R_DIFFERENT_PARAMETERS 104
+#define EVP_R_ENCODE_ERROR 105
+#define EVP_R_EXPECTING_AN_EC_KEY_KEY 106
+#define EVP_R_EXPECTING_AN_RSA_KEY 107
+#define EVP_R_EXPECTING_A_DSA_KEY 108
+#define EVP_R_ILLEGAL_OR_UNSUPPORTED_PADDING_MODE 109
+#define EVP_R_INVALID_DIGEST_LENGTH 110
+#define EVP_R_INVALID_DIGEST_TYPE 111
+#define EVP_R_INVALID_KEYBITS 112
+#define EVP_R_INVALID_MGF1_MD 113
+#define EVP_R_INVALID_OPERATION 114
+#define EVP_R_INVALID_PADDING_MODE 115
+#define EVP_R_INVALID_PSS_SALTLEN 116
+#define EVP_R_KEYS_NOT_SET 117
+#define EVP_R_MISSING_PARAMETERS 118
+#define EVP_R_NO_DEFAULT_DIGEST 119
+#define EVP_R_NO_KEY_SET 120
+#define EVP_R_NO_MDC2_SUPPORT 121
+#define EVP_R_NO_NID_FOR_CURVE 122
+#define EVP_R_NO_OPERATION_SET 123
+#define EVP_R_NO_PARAMETERS_SET 124
+#define EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE 125
+#define EVP_R_OPERATON_NOT_INITIALIZED 126
+#define EVP_R_UNKNOWN_PUBLIC_KEY_TYPE 127
+#define EVP_R_UNSUPPORTED_ALGORITHM 128
+#define EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE 129
+#define EVP_R_NOT_A_PRIVATE_KEY 130
+#define EVP_R_INVALID_SIGNATURE 131
+#define EVP_R_MEMORY_LIMIT_EXCEEDED 132
+#define EVP_R_INVALID_PARAMETERS 133
+#define EVP_R_INVALID_PEER_KEY 134
+#define EVP_R_NOT_XOF_OR_INVALID_LENGTH 135
+#define EVP_R_EMPTY_PSK 136
+#define EVP_R_INVALID_BUFFER_SIZE 137
 
-    if ((ret = X509_new()) == NULL) {
-        OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
-        return NULL;
-    }
-
-    /* duplicate the request */
-    xi = ret->cert_info;
-
-    if (sk_X509_ATTRIBUTE_num(r->req_info->attributes) != 0) {
-        if ((xi->version = ASN1_INTEGER_new()) == NULL)
-            goto err;
-        if (!ASN1_INTEGER_set(xi->version, 2))
-            goto err;
-        /*
-         * xi->extensions=ri->attributes; <- bad, should not ever be done
-         * ri->attributes=NULL;
-         */
-    }
-
-    xn = X509_REQ_get_subject_name(r);
-    if (X509_set_subject_name(ret, xn) == 0)
-        goto err;
-    if (X509_set_issuer_name(ret, xn) == 0)
-        goto err;
-
-    if (X509_gmtime_adj(xi->validity->notBefore, 0) == NULL)
-        goto err;
-    if (X509_gmtime_adj(xi->validity->notAfter, (long)60 * 60 * 24 * days) ==
-        NULL)
-        goto err;
-
-    pubkey = X509_REQ_get_pubkey(r);
-    res = X509_set_pubkey(ret, pubkey);
-    EVP_PKEY_free(pubkey);
-
-    if (!res || !X509_sign(ret, pkey, EVP_md5()))
-        goto err;
-    if (0) {
- err:
-        X509_free(ret);
-        ret = NULL;
-    }
-    return (ret);
-}
+#endif  // OPENSSL_HEADER_EVP_ERRORS_H
diff --git a/deps/boringssl/src/include/openssl/hkdf.h b/deps/boringssl/src/include/openssl/hkdf.h
index 59aaa49..5b27acc 100644
--- a/deps/boringssl/src/include/openssl/hkdf.h
+++ b/deps/boringssl/src/include/openssl/hkdf.h
@@ -41,6 +41,10 @@
 // keying material |secret| and salt |salt| using |digest|, and outputs
 // |out_len| bytes to |out_key|. The maximum output size is |EVP_MAX_MD_SIZE|.
 // It returns one on success and zero on error.
+//
+// WARNING: This function orders the inputs differently from RFC 5869
+// specification. Double-check which parameter is the secret/IKM and which is
+// the salt when using.
 OPENSSL_EXPORT int HKDF_extract(uint8_t *out_key, size_t *out_len,
                                 const EVP_MD *digest, const uint8_t *secret,
                                 size_t secret_len, const uint8_t *salt,
diff --git a/deps/boringssl/src/include/openssl/hpke.h b/deps/boringssl/src/include/openssl/hpke.h
new file mode 100644
index 0000000..6958ef6
--- /dev/null
+++ b/deps/boringssl/src/include/openssl/hpke.h
@@ -0,0 +1,350 @@
+/* Copyright (c) 2020, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#ifndef OPENSSL_HEADER_CRYPTO_HPKE_INTERNAL_H
+#define OPENSSL_HEADER_CRYPTO_HPKE_INTERNAL_H
+
+#include <openssl/aead.h>
+#include <openssl/base.h>
+#include <openssl/curve25519.h>
+#include <openssl/digest.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+
+// Hybrid Public Key Encryption.
+//
+// Hybrid Public Key Encryption (HPKE) enables a sender to encrypt messages to a
+// receiver with a public key.
+//
+// See https://tools.ietf.org/html/draft-irtf-cfrg-hpke-08.
+
+
+// Parameters.
+//
+// An HPKE context is parameterized by KEM, KDF, and AEAD algorithms,
+// represented by |EVP_HPKE_KEM|, |EVP_HPKE_KDF|, and |EVP_HPKE_AEAD| types,
+// respectively.
+
+// The following constants are KEM identifiers.
+#define EVP_HPKE_DHKEM_X25519_HKDF_SHA256 0x0020
+
+// The following functions are KEM algorithms which may be used with HPKE. Note
+// that, while some HPKE KEMs use KDFs internally, this is separate from the
+// |EVP_HPKE_KDF| selection.
+OPENSSL_EXPORT const EVP_HPKE_KEM *EVP_hpke_x25519_hkdf_sha256(void);
+
+// EVP_HPKE_KEM_id returns the HPKE KEM identifier for |kem|, which
+// will be one of the |EVP_HPKE_KEM_*| constants.
+OPENSSL_EXPORT uint16_t EVP_HPKE_KEM_id(const EVP_HPKE_KEM *kem);
+
+// The following constants are KDF identifiers.
+#define EVP_HPKE_HKDF_SHA256 0x0001
+
+// The following functions are KDF algorithms which may be used with HPKE.
+OPENSSL_EXPORT const EVP_HPKE_KDF *EVP_hpke_hkdf_sha256(void);
+
+// EVP_HPKE_KDF_id returns the HPKE KDF identifier for |kdf|.
+OPENSSL_EXPORT uint16_t EVP_HPKE_KDF_id(const EVP_HPKE_KDF *kdf);
+
+// The following constants are AEAD identifiers.
+#define EVP_HPKE_AES_128_GCM 0x0001
+#define EVP_HPKE_AES_256_GCM 0x0002
+#define EVP_HPKE_CHACHA20_POLY1305 0x0003
+
+// The following functions are AEAD algorithms which may be used with HPKE.
+OPENSSL_EXPORT const EVP_HPKE_AEAD *EVP_hpke_aes_128_gcm(void);
+OPENSSL_EXPORT const EVP_HPKE_AEAD *EVP_hpke_aes_256_gcm(void);
+OPENSSL_EXPORT const EVP_HPKE_AEAD *EVP_hpke_chacha20_poly1305(void);
+
+// EVP_HPKE_AEAD_id returns the HPKE AEAD identifier for |aead|.
+OPENSSL_EXPORT uint16_t EVP_HPKE_AEAD_id(const EVP_HPKE_AEAD *aead);
+
+// EVP_HPKE_AEAD_aead returns the |EVP_AEAD| corresponding to |aead|.
+OPENSSL_EXPORT const EVP_AEAD *EVP_HPKE_AEAD_aead(const EVP_HPKE_AEAD *aead);
+
+
+// Recipient keys.
+//
+// An HPKE recipient maintains a long-term KEM key. This library represents keys
+// with the |EVP_HPKE_KEY| type.
+
+// EVP_HPKE_KEY_zero sets an uninitialized |EVP_HPKE_KEY| to the zero state. The
+// caller should then use |EVP_HPKE_KEY_init|, |EVP_HPKE_KEY_copy|, or
+// |EVP_HPKE_KEY_generate| to finish initializing |key|.
+//
+// It is safe, but not necessary to call |EVP_HPKE_KEY_cleanup| in this state.
+// This may be used for more uniform cleanup of |EVP_HPKE_KEY|.
+OPENSSL_EXPORT void EVP_HPKE_KEY_zero(EVP_HPKE_KEY *key);
+
+// EVP_HPKE_KEY_cleanup releases memory referenced by |key|.
+OPENSSL_EXPORT void EVP_HPKE_KEY_cleanup(EVP_HPKE_KEY *key);
+
+// EVP_HPKE_KEY_new returns a newly-allocated |EVP_HPKE_KEY|, or NULL on error.
+// The caller must call |EVP_HPKE_KEY_free| on the result to release it.
+//
+// This is a convenience function for callers that need a heap-allocated
+// |EVP_HPKE_KEY|.
+OPENSSL_EXPORT EVP_HPKE_KEY *EVP_HPKE_KEY_new(void);
+
+// EVP_HPKE_KEY_free releases memory associated with |key|, which must have been
+// created with |EVP_HPKE_KEY_new|.
+OPENSSL_EXPORT void EVP_HPKE_KEY_free(EVP_HPKE_KEY *key);
+
+// EVP_HPKE_KEY_copy sets |dst| to a copy of |src|. It returns one on success
+// and zero on error. On success, the caller must call |EVP_HPKE_KEY_cleanup| to
+// release |dst|. On failure, calling |EVP_HPKE_KEY_cleanup| is safe, but not
+// necessary.
+OPENSSL_EXPORT int EVP_HPKE_KEY_copy(EVP_HPKE_KEY *dst,
+                                     const EVP_HPKE_KEY *src);
+
+// EVP_HPKE_KEY_init decodes |priv_key| as a private key for |kem| and
+// initializes |key| with the result. It returns one on success and zero if
+// |priv_key| was invalid. On success, the caller must call
+// |EVP_HPKE_KEY_cleanup| to release the key. On failure, calling
+// |EVP_HPKE_KEY_cleanup| is safe, but not necessary.
+OPENSSL_EXPORT int EVP_HPKE_KEY_init(EVP_HPKE_KEY *key, const EVP_HPKE_KEM *kem,
+                                     const uint8_t *priv_key,
+                                     size_t priv_key_len);
+
+// EVP_HPKE_KEY_generate sets |key| to a newly-generated key using |kem|.
+OPENSSL_EXPORT int EVP_HPKE_KEY_generate(EVP_HPKE_KEY *key,
+                                         const EVP_HPKE_KEM *kem);
+
+// EVP_HPKE_KEY_kem returns the HPKE KEM used by |key|.
+OPENSSL_EXPORT const EVP_HPKE_KEM *EVP_HPKE_KEY_kem(const EVP_HPKE_KEY *key);
+
+// EVP_HPKE_MAX_PUBLIC_KEY_LENGTH is the maximum length of a public key for all
+// KEMs supported by this library.
+#define EVP_HPKE_MAX_PUBLIC_KEY_LENGTH 32
+
+// EVP_HPKE_KEY_public_key writes |key|'s public key to |out| and sets
+// |*out_len| to the number of bytes written. On success, it returns one and
+// writes at most |max_out| bytes. If |max_out| is too small, it returns zero.
+// Setting |max_out| to |EVP_HPKE_MAX_PUBLIC_KEY_LENGTH| will ensure the public
+// key fits.
+OPENSSL_EXPORT int EVP_HPKE_KEY_public_key(const EVP_HPKE_KEY *key,
+                                           uint8_t *out, size_t *out_len,
+                                           size_t max_out);
+
+// EVP_HPKE_MAX_PRIVATE_KEY_LENGTH is the maximum length of a private key for
+// all KEMs supported by this library.
+#define EVP_HPKE_MAX_PRIVATE_KEY_LENGTH 32
+
+// EVP_HPKE_KEY_private_key writes |key|'s private key to |out| and sets
+// |*out_len| to the number of bytes written. On success, it returns one and
+// writes at most |max_out| bytes. If |max_out| is too small, it returns zero.
+// Setting |max_out| to |EVP_HPKE_MAX_PRIVATE_KEY_LENGTH| will ensure the
+// private key fits.
+OPENSSL_EXPORT int EVP_HPKE_KEY_private_key(const EVP_HPKE_KEY *key,
+                                            uint8_t *out, size_t *out_len,
+                                            size_t max_out);
+
+
+// Encryption contexts.
+//
+// An HPKE encryption context is represented by the |EVP_HPKE_CTX| type.
+
+// EVP_HPKE_CTX_zero sets an uninitialized |EVP_HPKE_CTX| to the zero state. The
+// caller should then use one of the |EVP_HPKE_CTX_setup_*| functions to finish
+// setting up |ctx|.
+//
+// It is safe, but not necessary to call |EVP_HPKE_CTX_cleanup| in this state.
+// This may be used for more uniform cleanup of |EVP_HPKE_CTX|.
+OPENSSL_EXPORT void EVP_HPKE_CTX_zero(EVP_HPKE_CTX *ctx);
+
+// EVP_HPKE_CTX_cleanup releases memory referenced by |ctx|. |ctx| must have
+// been initialized with |EVP_HPKE_CTX_zero| or one of the
+// |EVP_HPKE_CTX_setup_*| functions.
+OPENSSL_EXPORT void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx);
+
+// EVP_HPKE_CTX_new returns a newly-allocated |EVP_HPKE_CTX|, or NULL on error.
+// The caller must call |EVP_HPKE_CTX_free| on the result to release it.
+//
+// This is a convenience function for callers that need a heap-allocated
+// |EVP_HPKE_CTX|.
+OPENSSL_EXPORT EVP_HPKE_CTX *EVP_HPKE_CTX_new(void);
+
+// EVP_HPKE_CTX_free releases memory associated with |ctx|, which must have been
+// created with |EVP_HPKE_CTX_new|.
+OPENSSL_EXPORT void EVP_HPKE_CTX_free(EVP_HPKE_CTX *ctx);
+
+// EVP_HPKE_MAX_ENC_LENGTH is the maximum length of "enc", the encapsulated
+// shared secret, for all supported KEMs in this library.
+#define EVP_HPKE_MAX_ENC_LENGTH 32
+
+// EVP_HPKE_CTX_setup_sender implements the SetupBaseS HPKE operation. It
+// encapsulates a shared secret for |peer_public_key| and sets up |ctx| as a
+// sender context. It writes the encapsulated shared secret to |out_enc| and
+// sets |*out_enc_len| to the number of bytes written. It writes at most
+// |max_enc| bytes and fails if the buffer is too small. Setting |max_enc| to at
+// least |EVP_HPKE_MAX_ENC_LENGTH| will ensure the buffer is large enough.
+//
+// This function returns one on success and zero on error. Note that
+// |peer_public_key| may be invalid, in which case this function will return an
+// error.
+//
+// On success, callers may call |EVP_HPKE_CTX_seal| to encrypt messages for the
+// recipient. Callers must then call |EVP_HPKE_CTX_cleanup| when done. On
+// failure, calling |EVP_HPKE_CTX_cleanup| is safe, but not required.
+OPENSSL_EXPORT int EVP_HPKE_CTX_setup_sender(
+    EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
+    const EVP_HPKE_KEM *kem, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
+    const uint8_t *peer_public_key, size_t peer_public_key_len,
+    const uint8_t *info, size_t info_len);
+
+// EVP_HPKE_CTX_setup_sender_with_seed_for_testing behaves like
+// |EVP_HPKE_CTX_setup_sender|, but takes a seed to behave deterministically.
+// The seed's format depends on |kem|. For X25519, it is the sender's
+// ephemeral private key.
+OPENSSL_EXPORT int EVP_HPKE_CTX_setup_sender_with_seed_for_testing(
+    EVP_HPKE_CTX *ctx, uint8_t *out_enc, size_t *out_enc_len, size_t max_enc,
+    const EVP_HPKE_KEM *kem, const EVP_HPKE_KDF *kdf, const EVP_HPKE_AEAD *aead,
+    const uint8_t *peer_public_key, size_t peer_public_key_len,
+    const uint8_t *info, size_t info_len, const uint8_t *seed, size_t seed_len);
+
+// EVP_HPKE_CTX_setup_recipient implements the SetupBaseR HPKE operation. It
+// decapsulates the shared secret in |enc| with |key| and sets up |ctx| as a
+// recipient context. It returns one on success and zero on failure. Note that
+// |enc| may be invalid, in which case this function will return an error.
+//
+// On success, callers may call |EVP_HPKE_CTX_open| to decrypt messages from the
+// sender. Callers must then call |EVP_HPKE_CTX_cleanup| when done. On failure,
+// calling |EVP_HPKE_CTX_cleanup| is safe, but not required.
+OPENSSL_EXPORT int EVP_HPKE_CTX_setup_recipient(
+    EVP_HPKE_CTX *ctx, const EVP_HPKE_KEY *key, const EVP_HPKE_KDF *kdf,
+    const EVP_HPKE_AEAD *aead, const uint8_t *enc, size_t enc_len,
+    const uint8_t *info, size_t info_len);
+
+
+// Using an HPKE context.
+//
+// Once set up, callers may encrypt or decrypt with an |EVP_HPKE_CTX| using the
+// following functions.
+
+// EVP_HPKE_CTX_open uses the HPKE context |ctx| to authenticate |in_len| bytes
+// from |in| and |ad_len| bytes from |ad| and to decrypt at most |in_len| bytes
+// into |out|. It returns one on success, and zero otherwise.
+//
+// This operation will fail if the |ctx| context is not set up as a receiver.
+//
+// Note that HPKE encryption is stateful and ordered. The sender's first call to
+// |EVP_HPKE_CTX_seal| must correspond to the recipient's first call to
+// |EVP_HPKE_CTX_open|, etc.
+//
+// At most |in_len| bytes are written to |out|. In order to ensure success,
+// |max_out_len| should be at least |in_len|. On successful return, |*out_len|
+// is set to the actual number of bytes written.
+OPENSSL_EXPORT int EVP_HPKE_CTX_open(EVP_HPKE_CTX *ctx, uint8_t *out,
+                                     size_t *out_len, size_t max_out_len,
+                                     const uint8_t *in, size_t in_len,
+                                     const uint8_t *ad, size_t ad_len);
+
+// EVP_HPKE_CTX_seal uses the HPKE context |ctx| to encrypt and authenticate
+// |in_len| bytes of ciphertext |in| and authenticate |ad_len| bytes from |ad|,
+// writing the result to |out|. It returns one on success and zero otherwise.
+//
+// This operation will fail if the |ctx| context is not set up as a sender.
+//
+// Note that HPKE encryption is stateful and ordered. The sender's first call to
+// |EVP_HPKE_CTX_seal| must correspond to the recipient's first call to
+// |EVP_HPKE_CTX_open|, etc.
+//
+// At most, |max_out_len| encrypted bytes are written to |out|. On successful
+// return, |*out_len| is set to the actual number of bytes written.
+//
+// To ensure success, |max_out_len| should be |in_len| plus the result of
+// |EVP_HPKE_CTX_max_overhead| or |EVP_HPKE_MAX_OVERHEAD|.
+OPENSSL_EXPORT int EVP_HPKE_CTX_seal(EVP_HPKE_CTX *ctx, uint8_t *out,
+                                     size_t *out_len, size_t max_out_len,
+                                     const uint8_t *in, size_t in_len,
+                                     const uint8_t *ad, size_t ad_len);
+
+// EVP_HPKE_CTX_export uses the HPKE context |ctx| to export a secret of
+// |secret_len| bytes into |out|. This function uses |context_len| bytes from
+// |context| as a context string for the secret. This is necessary to separate
+// different uses of exported secrets and bind relevant caller-specific context
+// into the output. It returns one on success and zero otherwise.
+OPENSSL_EXPORT int EVP_HPKE_CTX_export(const EVP_HPKE_CTX *ctx, uint8_t *out,
+                                       size_t secret_len,
+                                       const uint8_t *context,
+                                       size_t context_len);
+
+// EVP_HPKE_MAX_OVERHEAD contains the largest value that
+// |EVP_HPKE_CTX_max_overhead| would ever return for any context.
+#define EVP_HPKE_MAX_OVERHEAD EVP_AEAD_MAX_OVERHEAD
+
+// EVP_HPKE_CTX_max_overhead returns the maximum number of additional bytes
+// added by sealing data with |EVP_HPKE_CTX_seal|. The |ctx| context must be set
+// up as a sender.
+OPENSSL_EXPORT size_t EVP_HPKE_CTX_max_overhead(const EVP_HPKE_CTX *ctx);
+
+// EVP_HPKE_CTX_aead returns |ctx|'s configured AEAD, or NULL if the context has
+// not been set up.
+OPENSSL_EXPORT const EVP_HPKE_AEAD *EVP_HPKE_CTX_aead(const EVP_HPKE_CTX *ctx);
+
+// EVP_HPKE_CTX_kdf returns |ctx|'s configured KDF, or NULL if the context has
+// not been set up.
+OPENSSL_EXPORT const EVP_HPKE_KDF *EVP_HPKE_CTX_kdf(const EVP_HPKE_CTX *ctx);
+
+
+// Private structures.
+//
+// The following structures are exported so their types are stack-allocatable,
+// but accessing or modifying their fields is forbidden.
+
+struct evp_hpke_ctx_st {
+  const EVP_HPKE_AEAD *aead;
+  const EVP_HPKE_KDF *kdf;
+  EVP_AEAD_CTX aead_ctx;
+  uint8_t base_nonce[EVP_AEAD_MAX_NONCE_LENGTH];
+  uint8_t exporter_secret[EVP_MAX_MD_SIZE];
+  uint64_t seq;
+  int is_sender;
+};
+
+struct evp_hpke_key_st {
+  const EVP_HPKE_KEM *kem;
+  uint8_t private_key[X25519_PRIVATE_KEY_LEN];
+  uint8_t public_key[X25519_PUBLIC_VALUE_LEN];
+};
+
+
+#if defined(__cplusplus)
+}  // extern C
+#endif
+
+#if !defined(BORINGSSL_NO_CXX)
+extern "C++" {
+
+BSSL_NAMESPACE_BEGIN
+
+using ScopedEVP_HPKE_CTX =
+    internal::StackAllocated<EVP_HPKE_CTX, void, EVP_HPKE_CTX_zero,
+                             EVP_HPKE_CTX_cleanup>;
+using ScopedEVP_HPKE_KEY =
+    internal::StackAllocated<EVP_HPKE_KEY, void, EVP_HPKE_KEY_zero,
+                             EVP_HPKE_KEY_cleanup>;
+
+BORINGSSL_MAKE_DELETER(EVP_HPKE_CTX, EVP_HPKE_CTX_free)
+BORINGSSL_MAKE_DELETER(EVP_HPKE_KEY, EVP_HPKE_KEY_free)
+
+BSSL_NAMESPACE_END
+
+}  // extern C++
+#endif
+
+#endif  // OPENSSL_HEADER_CRYPTO_HPKE_INTERNAL_H
diff --git a/deps/boringssl/src/include/openssl/hrss.h b/deps/boringssl/src/include/openssl/hrss.h
index 5390696..016fe67 100644
--- a/deps/boringssl/src/include/openssl/hrss.h
+++ b/deps/boringssl/src/include/openssl/hrss.h
@@ -59,29 +59,31 @@
   (HRSS_POLY3_BYTES * 2 + HRSS_PUBLIC_KEY_BYTES + 2 + 32)
 
 // HRSS_generate_key is a deterministic function that outputs a public and
-// private key based on the given entropy.
-OPENSSL_EXPORT void HRSS_generate_key(
+// private key based on the given entropy. It returns one on success or zero
+// on malloc failure.
+OPENSSL_EXPORT int HRSS_generate_key(
     struct HRSS_public_key *out_pub, struct HRSS_private_key *out_priv,
     const uint8_t input[HRSS_GENERATE_KEY_BYTES]);
 
 // HRSS_encap is a deterministic function the generates and encrypts a random
 // session key from the given entropy, writing those values to |out_shared_key|
-// and |out_ciphertext|, respectively.
-OPENSSL_EXPORT void HRSS_encap(uint8_t out_ciphertext[HRSS_CIPHERTEXT_BYTES],
-                               uint8_t out_shared_key[HRSS_KEY_BYTES],
-                               const struct HRSS_public_key *in_pub,
-                               const uint8_t in[HRSS_ENCAP_BYTES]);
+// and |out_ciphertext|, respectively. It returns one on success or zero on
+// malloc failure.
+OPENSSL_EXPORT int HRSS_encap(uint8_t out_ciphertext[HRSS_CIPHERTEXT_BYTES],
+                              uint8_t out_shared_key[HRSS_KEY_BYTES],
+                              const struct HRSS_public_key *in_pub,
+                              const uint8_t in[HRSS_ENCAP_BYTES]);
 
 // HRSS_decap decrypts a session key from |ciphertext_len| bytes of
 // |ciphertext|. If the ciphertext is valid, the decrypted key is written to
 // |out_shared_key|. Otherwise the HMAC of |ciphertext| under a secret key (kept
 // in |in_priv|) is written. If the ciphertext is the wrong length then it will
 // leak which was done via side-channels. Otherwise it should perform either
-// action in constant-time.
-OPENSSL_EXPORT void HRSS_decap(uint8_t out_shared_key[HRSS_KEY_BYTES],
-                               const struct HRSS_private_key *in_priv,
-                               const uint8_t *ciphertext,
-                               size_t ciphertext_len);
+// action in constant-time. It returns one on success (whether the ciphertext
+// was valid or not) and zero on malloc failure.
+OPENSSL_EXPORT int HRSS_decap(uint8_t out_shared_key[HRSS_KEY_BYTES],
+                              const struct HRSS_private_key *in_priv,
+                              const uint8_t *ciphertext, size_t ciphertext_len);
 
 // HRSS_marshal_public_key serialises |in_pub| to |out|.
 OPENSSL_EXPORT void HRSS_marshal_public_key(
diff --git a/deps/boringssl/src/include/openssl/lhash.h b/deps/boringssl/src/include/openssl/lhash.h
index 29e09c8..1297541 100644
--- a/deps/boringssl/src/include/openssl/lhash.h
+++ b/deps/boringssl/src/include/openssl/lhash.h
@@ -58,223 +58,22 @@
 #define OPENSSL_HEADER_LHASH_H
 
 #include <openssl/base.h>
-#include <openssl/type_check.h>
 
 #if defined(__cplusplus)
 extern "C" {
 #endif
 
 
-// lhash is a traditional, chaining hash table that automatically expands and
-// contracts as needed. One should not use the lh_* functions directly, rather
-// use the type-safe macro wrappers:
-//
-// A hash table of a specific type of object has type |LHASH_OF(type)|. This
-// can be defined (once) with |DEFINE_LHASH_OF(type)| and declared where needed
-// with |DECLARE_LHASH_OF(type)|. For example:
-//
-//   struct foo {
-//     int bar;
-//   };
-//
-//   DEFINE_LHASH_OF(struct foo)
-//
-// Although note that the hash table will contain /pointers/ to |foo|.
-//
-// A macro will be defined for each of the lh_* functions below. For
-// LHASH_OF(foo), the macros would be lh_foo_new, lh_foo_num_items etc.
+// lhash is an internal library and not exported for use outside BoringSSL. This
+// header is provided for compatibility with code that expects OpenSSL.
 
 
+// These two macros are exported for compatibility with existing callers of
+// |X509V3_EXT_conf_nid|. Do not use these symbols outside BoringSSL.
 #define LHASH_OF(type) struct lhash_st_##type
-
 #define DECLARE_LHASH_OF(type) LHASH_OF(type);
 
 
-// lhash_item_st is an element of a hash chain. It points to the opaque data
-// for this element and to the next item in the chain. The linked-list is NULL
-// terminated.
-typedef struct lhash_item_st {
-  void *data;
-  struct lhash_item_st *next;
-  // hash contains the cached, hash value of |data|.
-  uint32_t hash;
-} LHASH_ITEM;
-
-// lhash_cmp_func is a comparison function that returns a value equal, or not
-// equal, to zero depending on whether |*a| is equal, or not equal to |*b|,
-// respectively. Note the difference between this and |stack_cmp_func| in that
-// this takes pointers to the objects directly.
-//
-// This function's actual type signature is int (*)(const T*, const T*). The
-// low-level |lh_*| functions will be passed a type-specific wrapper to call it
-// correctly.
-typedef int (*lhash_cmp_func)(const void *a, const void *b);
-typedef int (*lhash_cmp_func_helper)(lhash_cmp_func func, const void *a,
-                                     const void *b);
-
-// lhash_hash_func is a function that maps an object to a uniformly distributed
-// uint32_t.
-//
-// This function's actual type signature is uint32_t (*)(const T*). The
-// low-level |lh_*| functions will be passed a type-specific wrapper to call it
-// correctly.
-typedef uint32_t (*lhash_hash_func)(const void *a);
-typedef uint32_t (*lhash_hash_func_helper)(lhash_hash_func func, const void *a);
-
-typedef struct lhash_st _LHASH;
-
-// lh_new returns a new, empty hash table or NULL on error.
-OPENSSL_EXPORT _LHASH *lh_new(lhash_hash_func hash, lhash_cmp_func comp);
-
-// lh_free frees the hash table itself but none of the elements. See
-// |lh_doall|.
-OPENSSL_EXPORT void lh_free(_LHASH *lh);
-
-// lh_num_items returns the number of items in |lh|.
-OPENSSL_EXPORT size_t lh_num_items(const _LHASH *lh);
-
-// lh_retrieve finds an element equal to |data| in the hash table and returns
-// it. If no such element exists, it returns NULL.
-OPENSSL_EXPORT void *lh_retrieve(const _LHASH *lh, const void *data,
-                                 lhash_hash_func_helper call_hash_func,
-                                 lhash_cmp_func_helper call_cmp_func);
-
-// lh_retrieve_key finds an element matching |key|, given the specified hash and
-// comparison function. This differs from |lh_retrieve| in that the key may be a
-// different type than the values stored in |lh|. |key_hash| and |cmp_key| must
-// be compatible with the functions passed into |lh_new|.
-OPENSSL_EXPORT void *lh_retrieve_key(const _LHASH *lh, const void *key,
-                                     uint32_t key_hash,
-                                     int (*cmp_key)(const void *key,
-                                                    const void *value));
-
-// lh_insert inserts |data| into the hash table. If an existing element is
-// equal to |data| (with respect to the comparison function) then |*old_data|
-// will be set to that value and it will be replaced. Otherwise, or in the
-// event of an error, |*old_data| will be set to NULL. It returns one on
-// success or zero in the case of an allocation error.
-OPENSSL_EXPORT int lh_insert(_LHASH *lh, void **old_data, void *data,
-                             lhash_hash_func_helper call_hash_func,
-                             lhash_cmp_func_helper call_cmp_func);
-
-// lh_delete removes an element equal to |data| from the hash table and returns
-// it. If no such element is found, it returns NULL.
-OPENSSL_EXPORT void *lh_delete(_LHASH *lh, const void *data,
-                               lhash_hash_func_helper call_hash_func,
-                               lhash_cmp_func_helper call_cmp_func);
-
-// lh_doall_arg calls |func| on each element of the hash table and also passes
-// |arg| as the second argument.
-// TODO(fork): rename this
-OPENSSL_EXPORT void lh_doall_arg(_LHASH *lh, void (*func)(void *, void *),
-                                 void *arg);
-
-// lh_strhash is the default hash function which processes NUL-terminated
-// strings.
-OPENSSL_EXPORT uint32_t lh_strhash(const char *c);
-
-#define DEFINE_LHASH_OF(type)                                                  \
-  DECLARE_LHASH_OF(type)                                                       \
-                                                                               \
-  typedef int (*lhash_##type##_cmp_func)(const type *, const type *);          \
-  typedef uint32_t (*lhash_##type##_hash_func)(const type *);                  \
-                                                                               \
-  OPENSSL_INLINE int lh_##type##_call_cmp_func(lhash_cmp_func func,            \
-                                               const void *a, const void *b) { \
-    return ((lhash_##type##_cmp_func)func)((const type *)a, (const type *)b);  \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE uint32_t lh_##type##_call_hash_func(lhash_hash_func func,     \
-                                                     const void *a) {          \
-    return ((lhash_##type##_hash_func)func)((const type *)a);                  \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE LHASH_OF(type) *                                              \
-      lh_##type##_new(lhash_##type##_hash_func hash,                           \
-                      lhash_##type##_cmp_func comp) {                          \
-    return (LHASH_OF(type) *)lh_new((lhash_hash_func)hash,                     \
-                                    (lhash_cmp_func)comp);                     \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE void lh_##type##_free(LHASH_OF(type) *lh) {                   \
-    lh_free((_LHASH *)lh);                                                     \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE size_t lh_##type##_num_items(const LHASH_OF(type) *lh) {      \
-    return lh_num_items((const _LHASH *)lh);                                   \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE type *lh_##type##_retrieve(const LHASH_OF(type) *lh,          \
-                                            const type *data) {                \
-    return (type *)lh_retrieve((const _LHASH *)lh, data,                       \
-                               lh_##type##_call_hash_func,                     \
-                               lh_##type##_call_cmp_func);                     \
-  }                                                                            \
-                                                                               \
-  typedef struct {                                                             \
-    int (*cmp_key)(const void *key, const type *value);                        \
-    const void *key;                                                           \
-  } LHASH_CMP_KEY_##type;                                                      \
-                                                                               \
-  OPENSSL_INLINE int lh_##type##_call_cmp_key(const void *key,                 \
-                                              const void *value) {             \
-    const LHASH_CMP_KEY_##type *cb = (const LHASH_CMP_KEY_##type *)key;        \
-    return cb->cmp_key(cb->key, (const type *)value);                          \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE type *lh_##type##_retrieve_key(                               \
-      const LHASH_OF(type) *lh, const void *key, uint32_t key_hash,            \
-      int (*cmp_key)(const void *key, const type *value)) {                    \
-    LHASH_CMP_KEY_##type cb = {cmp_key, key};                                  \
-    return (type *)lh_retrieve_key((const _LHASH *)lh, &cb, key_hash,          \
-                                   lh_##type##_call_cmp_key);                  \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE int lh_##type##_insert(LHASH_OF(type) *lh, type **old_data,   \
-                                        type *data) {                          \
-    void *old_data_void = NULL;                                                \
-    int ret =                                                                  \
-        lh_insert((_LHASH *)lh, &old_data_void, data,                          \
-                  lh_##type##_call_hash_func, lh_##type##_call_cmp_func);      \
-    *old_data = (type *)old_data_void;                                         \
-    return ret;                                                                \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE type *lh_##type##_delete(LHASH_OF(type) *lh,                  \
-                                          const type *data) {                  \
-    return (type *)lh_delete((_LHASH *)lh, data, lh_##type##_call_hash_func,   \
-                             lh_##type##_call_cmp_func);                       \
-  }                                                                            \
-                                                                               \
-  typedef struct {                                                             \
-    void (*doall)(type *);                                                     \
-    void (*doall_arg)(type *, void *);                                         \
-    void *arg;                                                                 \
-  } LHASH_DOALL_##type;                                                        \
-                                                                               \
-  OPENSSL_INLINE void lh_##type##_call_doall(void *value, void *arg) {         \
-    const LHASH_DOALL_##type *cb = (const LHASH_DOALL_##type *)arg;            \
-    cb->doall((type *)value);                                                  \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE void lh_##type##_call_doall_arg(void *value, void *arg) {     \
-    const LHASH_DOALL_##type *cb = (const LHASH_DOALL_##type *)arg;            \
-    cb->doall_arg((type *)value, cb->arg);                                     \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE void lh_##type##_doall(LHASH_OF(type) *lh,                    \
-                                        void (*func)(type *)) {                \
-    LHASH_DOALL_##type cb = {func, NULL, NULL};                                \
-    lh_doall_arg((_LHASH *)lh, lh_##type##_call_doall, &cb);                   \
-  }                                                                            \
-                                                                               \
-  OPENSSL_INLINE void lh_##type##_doall_arg(                                   \
-      LHASH_OF(type) *lh, void (*func)(type *, void *), void *arg) {           \
-    LHASH_DOALL_##type cb = {NULL, func, arg};                                 \
-    lh_doall_arg((_LHASH *)lh, lh_##type##_call_doall_arg, &cb);               \
-  }
-
-
 #if defined(__cplusplus)
 }  // extern C
 #endif
diff --git a/deps/boringssl/src/include/openssl/mem.h b/deps/boringssl/src/include/openssl/mem.h
index cceabcd..9906d5b 100644
--- a/deps/boringssl/src/include/openssl/mem.h
+++ b/deps/boringssl/src/include/openssl/mem.h
@@ -101,6 +101,9 @@
 // OPENSSL_hash32 implements the 32 bit, FNV-1a hash.
 OPENSSL_EXPORT uint32_t OPENSSL_hash32(const void *ptr, size_t len);
 
+// OPENSSL_strhash calls |OPENSSL_hash32| on the NUL-terminated string |s|.
+OPENSSL_EXPORT uint32_t OPENSSL_strhash(const char *s);
+
 // OPENSSL_strdup has the same behaviour as strdup(3).
 OPENSSL_EXPORT char *OPENSSL_strdup(const char *s);
 
diff --git a/deps/boringssl/src/include/openssl/obj.h b/deps/boringssl/src/include/openssl/obj.h
index 764188f..ad7271e 100644
--- a/deps/boringssl/src/include/openssl/obj.h
+++ b/deps/boringssl/src/include/openssl/obj.h
@@ -84,17 +84,24 @@
 
 // Basic operations.
 
-// OBJ_dup returns a duplicate copy of |obj| or NULL on allocation failure.
+// OBJ_dup returns a duplicate copy of |obj| or NULL on allocation failure. The
+// caller must call |ASN1_OBJECT_free| on the result to release it.
 OPENSSL_EXPORT ASN1_OBJECT *OBJ_dup(const ASN1_OBJECT *obj);
 
 // OBJ_cmp returns a value less than, equal to or greater than zero if |a| is
 // less than, equal to or greater than |b|, respectively.
 OPENSSL_EXPORT int OBJ_cmp(const ASN1_OBJECT *a, const ASN1_OBJECT *b);
 
-// OBJ_get0_data returns a pointer to the DER representation of |obj|.
+// OBJ_get0_data returns a pointer to the DER representation of |obj|. This is
+// the contents of the DER-encoded identifier, not including the tag and length.
+// If |obj| does not have an associated object identifier (i.e. it is a nid-only
+// value), this value is the empty string.
 OPENSSL_EXPORT const uint8_t *OBJ_get0_data(const ASN1_OBJECT *obj);
 
-// OBJ_length returns the length of the DER representation of |obj|.
+// OBJ_length returns the length of the DER representation of |obj|. This is the
+// contents of the DER-encoded identifier, not including the tag and length. If
+// |obj| does not have an associated object identifier (i.e. it is a nid-only
+// value), this value is the empty string.
 OPENSSL_EXPORT size_t OBJ_length(const ASN1_OBJECT *obj);
 
 
@@ -124,9 +131,22 @@
 
 // Getting information about nids.
 
-// OBJ_nid2obj returns the ASN1_OBJECT corresponding to |nid|, or NULL if |nid|
-// is unknown.
-OPENSSL_EXPORT const ASN1_OBJECT *OBJ_nid2obj(int nid);
+// OBJ_nid2obj returns the |ASN1_OBJECT| corresponding to |nid|, or NULL if
+// |nid| is unknown.
+//
+// Although the output is not const, this function returns a static, immutable
+// |ASN1_OBJECT|. It is not necessary to release the object with
+// |ASN1_OBJECT_free|.
+//
+// However, functions like |X509_ALGOR_set0| expect to take ownership of a
+// possibly dynamically-allocated |ASN1_OBJECT|. |ASN1_OBJECT_free| is a no-op
+// for static |ASN1_OBJECT|s, so |OBJ_nid2obj| is compatible with such
+// functions.
+//
+// Callers are encouraged to store the result of this function in a const
+// pointer. However, if using functions like |X509_ALGOR_set0|, callers may use
+// a non-const pointer and manage ownership.
+OPENSSL_EXPORT ASN1_OBJECT *OBJ_nid2obj(int nid);
 
 // OBJ_nid2sn returns the short name for |nid|, or NULL if |nid| is unknown.
 OPENSSL_EXPORT const char *OBJ_nid2sn(int nid);
diff --git a/deps/boringssl/src/include/openssl/pem.h b/deps/boringssl/src/include/openssl/pem.h
index f39989e..a94f276 100644
--- a/deps/boringssl/src/include/openssl/pem.h
+++ b/deps/boringssl/src/include/openssl/pem.h
@@ -112,15 +112,6 @@
 // write. Now they are all implemented with either:
 // IMPLEMENT_PEM_rw(...) or IMPLEMENT_PEM_rw_cb(...)
 
-#ifdef OPENSSL_NO_FP_API
-
-#define IMPLEMENT_PEM_read_fp(name, type, str, asn1)            //
-#define IMPLEMENT_PEM_write_fp(name, type, str, asn1)           //
-#define IMPLEMENT_PEM_write_fp_const(name, type, str, asn1)     //
-#define IMPLEMENT_PEM_write_cb_fp(name, type, str, asn1)        //
-#define IMPLEMENT_PEM_write_cb_fp_const(name, type, str, asn1)  //
-
-#else
 
 #define IMPLEMENT_PEM_read_fp(name, type, str, asn1)                         \
   static void *pem_read_##name##_d2i(void **x, const unsigned char **inp,    \
@@ -173,7 +164,6 @@
                           cb, u);                                              \
   }
 
-#endif
 
 #define IMPLEMENT_PEM_read_bio(name, type, str, asn1)                         \
   static void *pem_read_bio_##name##_d2i(void **x, const unsigned char **inp, \
@@ -260,14 +250,6 @@
 
 // These are the same except they are for the declarations
 
-#if defined(OPENSSL_NO_FP_API)
-
-#define DECLARE_PEM_read_fp(name, type)      //
-#define DECLARE_PEM_write_fp(name, type)     //
-#define DECLARE_PEM_write_cb_fp(name, type)  //
-
-#else
-
 #define DECLARE_PEM_read_fp(name, type)                    \
   OPENSSL_EXPORT type *PEM_read_##name(FILE *fp, type **x, \
                                        pem_password_cb *cb, void *u);
@@ -283,8 +265,6 @@
       FILE *fp, type *x, const EVP_CIPHER *enc, unsigned char *kstr, int klen, \
       pem_password_cb *cb, void *u);
 
-#endif
-
 #define DECLARE_PEM_read_bio(name, type)                      \
   OPENSSL_EXPORT type *PEM_read_bio_##name(BIO *bp, type **x, \
                                            pem_password_cb *cb, void *u);
diff --git a/deps/boringssl/src/include/openssl/pkcs7.h b/deps/boringssl/src/include/openssl/pkcs7.h
index cb6155f..8f2a885 100644
--- a/deps/boringssl/src/include/openssl/pkcs7.h
+++ b/deps/boringssl/src/include/openssl/pkcs7.h
@@ -38,7 +38,10 @@
 // success and zero on error. |cbs| is advanced passed the structure.
 //
 // Note that a SignedData structure may contain no certificates, in which case
-// this function succeeds but does not append any certificates.
+// this function succeeds but does not append any certificates. Additionally,
+// certificates in SignedData structures are unordered. Callers should not
+// assume a particular order in |*out_certs| and may need to search for matches
+// or run path-building algorithms.
 OPENSSL_EXPORT int PKCS7_get_raw_certificates(
     STACK_OF(CRYPTO_BUFFER) *out_certs, CBS *cbs, CRYPTO_BUFFER_POOL *pool);
 
@@ -47,7 +50,9 @@
 OPENSSL_EXPORT int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs);
 
 // PKCS7_bundle_certificates appends a PKCS#7, SignedData structure containing
-// |certs| to |out|. It returns one on success and zero on error.
+// |certs| to |out|. It returns one on success and zero on error. Note that
+// certificates in SignedData structures are unordered. The order in |certs|
+// will not be preserved.
 OPENSSL_EXPORT int PKCS7_bundle_certificates(
     CBB *out, const STACK_OF(X509) *certs);
 
@@ -56,11 +61,15 @@
 // |cbs| is advanced passed the structure.
 //
 // Note that a SignedData structure may contain no CRLs, in which case this
-// function succeeds but does not append any CRLs.
+// function succeeds but does not append any CRLs. Additionally, CRLs in
+// SignedData structures are unordered. Callers should not assume an order in
+// |*out_crls| and may need to search for matches.
 OPENSSL_EXPORT int PKCS7_get_CRLs(STACK_OF(X509_CRL) *out_crls, CBS *cbs);
 
 // PKCS7_bundle_CRLs appends a PKCS#7, SignedData structure containing
-// |crls| to |out|. It returns one on success and zero on error.
+// |crls| to |out|. It returns one on success and zero on error. Note that CRLs
+// in SignedData structures are unordered. The order in |crls| will not be
+// preserved.
 OPENSSL_EXPORT int PKCS7_bundle_CRLs(CBB *out, const STACK_OF(X509_CRL) *crls);
 
 // PKCS7_get_PEM_certificates reads a PEM-encoded, PKCS#7, SignedData structure
@@ -68,7 +77,10 @@
 // returns one on success and zero on error.
 //
 // Note that a SignedData structure may contain no certificates, in which case
-// this function succeeds but does not append any certificates.
+// this function succeeds but does not append any certificates. Additionally,
+// certificates in SignedData structures are unordered. Callers should not
+// assume a particular order in |*out_certs| and may need to search for matches
+// or run path-building algorithms.
 OPENSSL_EXPORT int PKCS7_get_PEM_certificates(STACK_OF(X509) *out_certs,
                                               BIO *pem_bio);
 
@@ -77,7 +89,9 @@
 // success and zero on error.
 //
 // Note that a SignedData structure may contain no CRLs, in which case this
-// function succeeds but does not append any CRLs.
+// function succeeds but does not append any CRLs. Additionally, CRLs in
+// SignedData structures are unordered. Callers should not assume an order in
+// |*out_crls| and may need to search for matches.
 OPENSSL_EXPORT int PKCS7_get_PEM_CRLs(STACK_OF(X509_CRL) *out_crls,
                                       BIO *pem_bio);
 
@@ -101,6 +115,7 @@
 typedef void PKCS7_ENVELOPE;
 typedef void PKCS7_DIGEST;
 typedef void PKCS7_ENCRYPT;
+typedef void PKCS7_SIGNER_INFO;
 
 typedef struct {
   uint8_t *ber_bytes;
@@ -183,6 +198,7 @@
 #define PKCS7_NOATTR 0x100
 #define PKCS7_NOSMIMECAP 0x200
 #define PKCS7_STREAM 0x1000
+#define PKCS7_PARTIAL 0x4000
 
 // PKCS7_sign assembles |certs| into a PKCS#7 signed data ContentInfo with
 // external data and no signatures. It returns a newly-allocated |PKCS7| on
@@ -190,7 +206,9 @@
 // ignored. |flags| must be equal to |PKCS7_DETACHED|.
 //
 // Note this function only implements a subset of the corresponding OpenSSL
-// function. It is provided for backwards compatibility only.
+// function. It is provided for backwards compatibility only. Additionally,
+// certificates in SignedData structures are unordered. The order of |certs|
+// will not be preserved.
 OPENSSL_EXPORT PKCS7 *PKCS7_sign(X509 *sign_cert, EVP_PKEY *pkey,
                                  STACK_OF(X509) *certs, BIO *data, int flags);
 
diff --git a/deps/boringssl/src/include/openssl/pkcs8.h b/deps/boringssl/src/include/openssl/pkcs8.h
index 385b995..4f21ef3 100644
--- a/deps/boringssl/src/include/openssl/pkcs8.h
+++ b/deps/boringssl/src/include/openssl/pkcs8.h
@@ -175,7 +175,9 @@
 //
 // Note if |p12| does not contain a private key, both |*out_pkey| and
 // |*out_cert| will be set to NULL and all certificates will be returned via
-// |*out_ca_certs|.
+// |*out_ca_certs|. Also note this function differs from OpenSSL in that extra
+// certificates are returned in the order they appear in the file. OpenSSL 1.1.1
+// returns them in reverse order, but this will be fixed in OpenSSL 3.0.
 //
 // It returns one on success and zero on error.
 //
@@ -206,6 +208,12 @@
 // Each of |key_nid|, |cert_nid|, |iterations|, and |mac_iterations| may be zero
 // to use defaults, which are |NID_pbe_WithSHA1And3_Key_TripleDES_CBC|,
 // |NID_pbe_WithSHA1And40BitRC2_CBC|, 2048, and one, respectively.
+//
+// |key_nid| or |cert_nid| may also be -1 to disable encryption of the key or
+// certificate, respectively. This option is not recommended and is only
+// implemented for compatibility with external packages. Note the output still
+// requires a password for the MAC. Unencrypted keys in PKCS#12 are also not
+// widely supported and may not open in other implementations.
 OPENSSL_EXPORT PKCS12 *PKCS12_create(const char *password, const char *name,
                                      const EVP_PKEY *pkey, X509 *cert,
                                      const STACK_OF(X509) *chain, int key_nid,
diff --git a/deps/boringssl/src/include/openssl/rand.h b/deps/boringssl/src/include/openssl/rand.h
index b07015b..bd41f9e 100644
--- a/deps/boringssl/src/include/openssl/rand.h
+++ b/deps/boringssl/src/include/openssl/rand.h
@@ -103,8 +103,8 @@
 // RAND_get_rand_method returns |RAND_SSLeay()|.
 OPENSSL_EXPORT const RAND_METHOD *RAND_get_rand_method(void);
 
-// RAND_set_rand_method does nothing.
-OPENSSL_EXPORT void RAND_set_rand_method(const RAND_METHOD *);
+// RAND_set_rand_method returns one.
+OPENSSL_EXPORT int RAND_set_rand_method(const RAND_METHOD *);
 
 
 #if defined(__cplusplus)
diff --git a/deps/boringssl/src/include/openssl/rsa.h b/deps/boringssl/src/include/openssl/rsa.h
index ed6df69..27bc7bf 100644
--- a/deps/boringssl/src/include/openssl/rsa.h
+++ b/deps/boringssl/src/include/openssl/rsa.h
@@ -283,120 +283,155 @@
 // These functions are considered non-mutating for thread-safety purposes and
 // may be used concurrently.
 
-// RSA_sign signs |in_len| bytes of digest from |in| with |rsa| using
+// RSA_sign signs |digest_len| bytes of digest from |digest| with |rsa| using
 // RSASSA-PKCS1-v1_5. It writes, at most, |RSA_size(rsa)| bytes to |out|. On
 // successful return, the actual number of bytes written is written to
 // |*out_len|.
 //
-// The |hash_nid| argument identifies the hash function used to calculate |in|
-// and is embedded in the resulting signature. For example, it might be
+// The |hash_nid| argument identifies the hash function used to calculate
+// |digest| and is embedded in the resulting signature. For example, it might be
 // |NID_sha256|.
 //
 // It returns 1 on success and zero on error.
-OPENSSL_EXPORT int RSA_sign(int hash_nid, const uint8_t *in,
-                            unsigned int in_len, uint8_t *out,
-                            unsigned int *out_len, RSA *rsa);
-
-// RSA_sign_pss_mgf1 signs |in_len| bytes from |in| with the public key from
-// |rsa| using RSASSA-PSS with MGF1 as the mask generation function. It writes,
-// at most, |max_out| bytes of signature data to |out|. The |max_out| argument
-// must be, at least, |RSA_size| in order to ensure success. It returns 1 on
-// success or zero on error.
 //
-// The |md| and |mgf1_md| arguments identify the hash used to calculate |msg|
+// WARNING: |digest| must be the result of hashing the data to be signed with
+// |hash_nid|. Passing unhashed inputs will not result in a secure signature
+// scheme.
+OPENSSL_EXPORT int RSA_sign(int hash_nid, const uint8_t *digest,
+                            unsigned digest_len, uint8_t *out,
+                            unsigned *out_len, RSA *rsa);
+
+// RSA_sign_pss_mgf1 signs |digest_len| bytes from |digest| with the public key
+// from |rsa| using RSASSA-PSS with MGF1 as the mask generation function. It
+// writes, at most, |max_out| bytes of signature data to |out|. The |max_out|
+// argument must be, at least, |RSA_size| in order to ensure success. It returns
+// 1 on success or zero on error.
+//
+// The |md| and |mgf1_md| arguments identify the hash used to calculate |digest|
 // and the MGF1 hash, respectively. If |mgf1_md| is NULL, |md| is
 // used.
 //
 // |salt_len| specifies the expected salt length in bytes. If |salt_len| is -1,
 // then the salt length is the same as the hash length. If -2, then the salt
 // length is maximal given the size of |rsa|. If unsure, use -1.
+//
+// WARNING: |digest| must be the result of hashing the data to be signed with
+// |md|. Passing unhashed inputs will not result in a secure signature scheme.
 OPENSSL_EXPORT int RSA_sign_pss_mgf1(RSA *rsa, size_t *out_len, uint8_t *out,
-                                     size_t max_out, const uint8_t *in,
-                                     size_t in_len, const EVP_MD *md,
+                                     size_t max_out, const uint8_t *digest,
+                                     size_t digest_len, const EVP_MD *md,
                                      const EVP_MD *mgf1_md, int salt_len);
 
-// RSA_sign_raw signs |in_len| bytes from |in| with the public key from |rsa|
-// and writes, at most, |max_out| bytes of signature data to |out|. The
-// |max_out| argument must be, at least, |RSA_size| in order to ensure success.
+// RSA_sign_raw performs the private key portion of computing a signature with
+// |rsa|. It writes, at most, |max_out| bytes of signature data to |out|. The
+// |max_out| argument must be, at least, |RSA_size| in order to ensure the
+// output fits. It returns 1 on success or zero on error.
 //
-// It returns 1 on success or zero on error.
+// If |padding| is |RSA_PKCS1_PADDING|, this function wraps |in| with the
+// padding portion of RSASSA-PKCS1-v1_5 and then performs the raw private key
+// operation. The caller is responsible for hashing the input and wrapping it in
+// a DigestInfo structure.
 //
-// The |padding| argument must be one of the |RSA_*_PADDING| values. If in
-// doubt, |RSA_PKCS1_PADDING| is the most common but |RSA_PKCS1_PSS_PADDING|
-// (via |RSA_sign_pss_mgf1| or the |EVP_PKEY| interface) is preferred for new
-// protocols.
+// If |padding| is |RSA_NO_PADDING|, this function only performs the raw private
+// key operation, interpreting |in| as a integer modulo n. The caller is
+// responsible for hashing the input and encoding it for the signature scheme
+// being implemented.
+//
+// WARNING: This function is a building block for a signature scheme, not a
+// complete one. |in| must be the result of hashing and encoding the data as
+// needed for the scheme being implemented. Passing in arbitrary inputs will not
+// result in a secure signature scheme.
 OPENSSL_EXPORT int RSA_sign_raw(RSA *rsa, size_t *out_len, uint8_t *out,
                                 size_t max_out, const uint8_t *in,
                                 size_t in_len, int padding);
 
 // RSA_verify verifies that |sig_len| bytes from |sig| are a valid,
-// RSASSA-PKCS1-v1_5 signature of |msg_len| bytes at |msg| by |rsa|.
+// RSASSA-PKCS1-v1_5 signature of |digest_len| bytes at |digest| by |rsa|.
 //
-// The |hash_nid| argument identifies the hash function used to calculate |msg|
-// and is embedded in the resulting signature in order to prevent hash
+// The |hash_nid| argument identifies the hash function used to calculate
+// |digest| and is embedded in the resulting signature in order to prevent hash
 // confusion attacks. For example, it might be |NID_sha256|.
 //
 // It returns one if the signature is valid and zero otherwise.
 //
 // WARNING: this differs from the original, OpenSSL function which additionally
 // returned -1 on error.
-OPENSSL_EXPORT int RSA_verify(int hash_nid, const uint8_t *msg, size_t msg_len,
-                              const uint8_t *sig, size_t sig_len, RSA *rsa);
+//
+// WARNING: |digest| must be the result of hashing the data to be verified with
+// |hash_nid|. Passing unhashed input will not result in a secure signature
+// scheme.
+OPENSSL_EXPORT int RSA_verify(int hash_nid, const uint8_t *digest,
+                              size_t digest_len, const uint8_t *sig,
+                              size_t sig_len, RSA *rsa);
 
 // RSA_verify_pss_mgf1 verifies that |sig_len| bytes from |sig| are a valid,
-// RSASSA-PSS signature of |msg_len| bytes at |msg| by |rsa|. It returns one if
-// the signature is valid and zero otherwise. MGF1 is used as the mask
+// RSASSA-PSS signature of |digest_len| bytes at |digest| by |rsa|. It returns
+// one if the signature is valid and zero otherwise. MGF1 is used as the mask
 // generation function.
 //
-// The |md| and |mgf1_md| arguments identify the hash used to calculate |msg|
+// The |md| and |mgf1_md| arguments identify the hash used to calculate |digest|
 // and the MGF1 hash, respectively. If |mgf1_md| is NULL, |md| is
 // used. |salt_len| specifies the expected salt length in bytes.
 //
 // If |salt_len| is -1, then the salt length is the same as the hash length. If
 // -2, then the salt length is recovered and all values accepted. If unsure, use
 // -1.
-OPENSSL_EXPORT int RSA_verify_pss_mgf1(RSA *rsa, const uint8_t *msg,
-                                       size_t msg_len, const EVP_MD *md,
+//
+// WARNING: |digest| must be the result of hashing the data to be verified with
+// |md|. Passing unhashed input will not result in a secure signature scheme.
+OPENSSL_EXPORT int RSA_verify_pss_mgf1(RSA *rsa, const uint8_t *digest,
+                                       size_t digest_len, const EVP_MD *md,
                                        const EVP_MD *mgf1_md, int salt_len,
                                        const uint8_t *sig, size_t sig_len);
 
-// RSA_verify_raw verifies |in_len| bytes of signature from |in| using the
-// public key from |rsa| and writes, at most, |max_out| bytes of plaintext to
-// |out|. The |max_out| argument must be, at least, |RSA_size| in order to
-// ensure success.
+// RSA_verify_raw performs the public key portion of verifying |in_len| bytes of
+// signature from |in| using the public key from |rsa|. On success, it returns
+// one and writes, at most, |max_out| bytes of output to |out|. The |max_out|
+// argument must be, at least, |RSA_size| in order to ensure the output fits. On
+// failure or invalid input, it returns zero.
 //
-// It returns 1 on success or zero on error.
+// If |padding| is |RSA_PKCS1_PADDING|, this function checks the padding portion
+// of RSASSA-PKCS1-v1_5 and outputs the remainder of the encoded digest. The
+// caller is responsible for checking the output is a DigestInfo-wrapped digest
+// of the message.
 //
-// The |padding| argument must be one of the |RSA_*_PADDING| values. If in
-// doubt, |RSA_PKCS1_PADDING| is the most common but |RSA_PKCS1_PSS_PADDING|
-// (via |RSA_verify_pss_mgf1| or the |EVP_PKEY| interface) is preferred for new
-// protocols.
+// If |padding| is |RSA_NO_PADDING|, this function only performs the raw public
+// key operation. The caller is responsible for checking the output is a valid
+// result for the signature scheme being implemented.
+//
+// WARNING: This function is a building block for a signature scheme, not a
+// complete one. Checking for arbitrary strings in |out| will not result in a
+// secure signature scheme.
 OPENSSL_EXPORT int RSA_verify_raw(RSA *rsa, size_t *out_len, uint8_t *out,
                                   size_t max_out, const uint8_t *in,
                                   size_t in_len, int padding);
 
-// RSA_private_encrypt encrypts |flen| bytes from |from| with the private key in
-// |rsa| and writes the encrypted data to |to|. The |to| buffer must have at
-// least |RSA_size| bytes of space. It returns the number of bytes written, or
-// -1 on error. The |padding| argument must be one of the |RSA_*_PADDING|
-// values. If in doubt, |RSA_PKCS1_PADDING| is the most common but
-// |RSA_PKCS1_PSS_PADDING| (via the |EVP_PKEY| interface) is preferred for new
-// protocols.
+// RSA_private_encrypt performs the private key portion of computing a signature
+// with |rsa|. It takes |flen| bytes from |from| as input and writes the result
+// to |to|. The |to| buffer must have at least |RSA_size| bytes of space. It
+// returns the number of bytes written, or -1 on error.
 //
-// WARNING: this function is dangerous because it breaks the usual return value
+// For the interpretation of |padding| and the input, see |RSA_sign_raw|.
+//
+// WARNING: This function is a building block for a signature scheme, not a
+// complete one. See |RSA_sign_raw| for details.
+//
+// WARNING: This function is dangerous because it breaks the usual return value
 // convention. Use |RSA_sign_raw| instead.
 OPENSSL_EXPORT int RSA_private_encrypt(size_t flen, const uint8_t *from,
                                        uint8_t *to, RSA *rsa, int padding);
 
-// RSA_public_decrypt verifies |flen| bytes of signature from |from| using the
-// public key in |rsa| and writes the plaintext to |to|. The |to| buffer must
-// have at least |RSA_size| bytes of space. It returns the number of bytes
-// written, or -1 on error. The |padding| argument must be one of the
-// |RSA_*_PADDING| values. If in doubt, |RSA_PKCS1_PADDING| is the most common
-// but |RSA_PKCS1_PSS_PADDING| (via the |EVP_PKEY| interface) is preferred for
-// new protocols.
+// RSA_public_decrypt performs the public key portion of verifying |flen| bytes
+// of signature from |from| using the public key from |rsa|. It writes the
+// result to |to|, which must have at least |RSA_size| bytes of space. It
+// returns the number of bytes written, or -1 on error.
 //
-// WARNING: this function is dangerous because it breaks the usual return value
+// For the interpretation of |padding| and the result, see |RSA_verify_raw|.
+//
+// WARNING: This function is a building block for a signature scheme, not a
+// complete one. See |RSA_verify_raw| for details.
+//
+// WARNING: This function is dangerous because it breaks the usual return value
 // convention. Use |RSA_verify_raw| instead.
 OPENSSL_EXPORT int RSA_public_decrypt(size_t flen, const uint8_t *from,
                                       uint8_t *to, RSA *rsa, int padding);
@@ -479,13 +514,14 @@
     const uint8_t *param, size_t param_len, const EVP_MD *md,
     const EVP_MD *mgf1md);
 
-// RSA_add_pkcs1_prefix builds a version of |msg| prefixed with the DigestInfo
-// header for the given hash function and sets |out_msg| to point to it. On
-// successful return, if |*is_alloced| is one, the caller must release
+// RSA_add_pkcs1_prefix builds a version of |digest| prefixed with the
+// DigestInfo header for the given hash function and sets |out_msg| to point to
+// it. On successful return, if |*is_alloced| is one, the caller must release
 // |*out_msg| with |OPENSSL_free|.
 OPENSSL_EXPORT int RSA_add_pkcs1_prefix(uint8_t **out_msg, size_t *out_msg_len,
                                         int *is_alloced, int hash_nid,
-                                        const uint8_t *msg, size_t msg_len);
+                                        const uint8_t *digest,
+                                        size_t digest_len);
 
 
 // ASN.1 functions.
diff --git a/deps/boringssl/src/include/openssl/span.h b/deps/boringssl/src/include/openssl/span.h
index 7410bf9..79f1d41 100644
--- a/deps/boringssl/src/include/openssl/span.h
+++ b/deps/boringssl/src/include/openssl/span.h
@@ -94,18 +94,6 @@
 template <typename T>
 class Span : private internal::SpanBase<const T> {
  private:
-  // Heuristically test whether C is a container type that can be converted into
-  // a Span by checking for data() and size() member functions.
-  //
-  // TODO(davidben): Switch everything to std::enable_if_t when we remove
-  // support for MSVC 2015. Although we could write our own enable_if_t and MSVC
-  // 2015 has std::enable_if_t anyway, MSVC 2015's SFINAE implementation is
-  // problematic and does not work below unless we write the ::type at use.
-  template <typename C>
-  using EnableIfContainer = std::enable_if<
-      std::is_convertible<decltype(std::declval<C>().data()), T *>::value &&
-      std::is_integral<decltype(std::declval<C>().size())>::value>;
-
   static const size_t npos = static_cast<size_t>(-1);
 
  public:
@@ -116,12 +104,27 @@
   constexpr Span(T (&array)[N]) : Span(array, N) {}
 
   template <
-      typename C, typename = typename EnableIfContainer<C>::type,
+      typename C,
+      // TODO(davidben): Switch everything to std::enable_if_t when we remove
+      // support for MSVC 2015. Although we could write our own enable_if_t and
+      // MSVC 2015 has std::enable_if_t anyway, MSVC 2015's SFINAE
+      // implementation is problematic and does not work below unless we write
+      // the ::type at use.
+      //
+      // TODO(davidben): Move this and the identical copy below into an
+      // EnableIfContainer alias when we drop MSVC 2015 support. MSVC 2015's
+      // SFINAE support cannot handle type aliases.
+      typename = typename std::enable_if<
+          std::is_convertible<decltype(std::declval<C>().data()), T *>::value &&
+          std::is_integral<decltype(std::declval<C>().size())>::value>::type,
       typename = typename std::enable_if<std::is_const<T>::value, C>::type>
   Span(const C &container) : data_(container.data()), size_(container.size()) {}
 
   template <
-      typename C, typename = typename EnableIfContainer<C>::type,
+      typename C,
+      typename = typename std::enable_if<
+          std::is_convertible<decltype(std::declval<C>().data()), T *>::value &&
+          std::is_integral<decltype(std::declval<C>().size())>::value>::type,
       typename = typename std::enable_if<!std::is_const<T>::value, C>::type>
   explicit Span(C &container)
       : data_(container.data()), size_(container.size()) {}
@@ -158,11 +161,30 @@
 
   Span subspan(size_t pos = 0, size_t len = npos) const {
     if (pos > size_) {
-      abort();  // absl::Span throws an exception here.
+      // absl::Span throws an exception here. Note std::span and Chromium
+      // base::span additionally forbid pos + len being out of range, with a
+      // special case at npos/dynamic_extent, while absl::Span::subspan clips
+      // the span. For now, we align with absl::Span in case we switch to it in
+      // the future.
+      abort();
     }
     return Span(data_ + pos, std::min(size_ - pos, len));
   }
 
+  Span first(size_t len) {
+    if (len > size_) {
+      abort();
+    }
+    return Span(data_, len);
+  }
+
+  Span last(size_t len) {
+    if (len > size_) {
+      abort();
+    }
+    return Span(data_ + size_ - len, len);
+  }
+
  private:
   T *data_;
   size_t size_;
diff --git a/deps/boringssl/src/include/openssl/ssl.h b/deps/boringssl/src/include/openssl/ssl.h
index 7ff7e72..eae3c4b 100644
--- a/deps/boringssl/src/include/openssl/ssl.h
+++ b/deps/boringssl/src/include/openssl/ssl.h
@@ -508,12 +508,10 @@
 // TODO(davidben): Remove this. It's used by accept BIOs which are bizarre.
 #define SSL_ERROR_WANT_ACCEPT 8
 
-// SSL_ERROR_WANT_CHANNEL_ID_LOOKUP indicates the operation failed looking up
-// the Channel ID key. The caller may retry the operation when |channel_id_cb|
-// is ready to return a key or one has been configured with
-// |SSL_set1_tls_channel_id|.
+// SSL_ERROR_WANT_CHANNEL_ID_LOOKUP is never used.
 //
-// See also |SSL_CTX_set_channel_id_cb|.
+// TODO(davidben): Remove this. Some callers reference it when stringifying
+// errors. They should use |SSL_error_description| instead.
 #define SSL_ERROR_WANT_CHANNEL_ID_LOOKUP 9
 
 // SSL_ERROR_PENDING_SESSION indicates the operation failed because the session
@@ -567,6 +565,11 @@
 // See also |ssl_renegotiate_explicit|.
 #define SSL_ERROR_WANT_RENEGOTIATE 19
 
+// SSL_ERROR_HANDSHAKE_HINTS_READY indicates the handshake has progressed enough
+// for |SSL_serialize_handshake_hints| to be called. See also
+// |SSL_request_handshake_hints|.
+#define SSL_ERROR_HANDSHAKE_HINTS_READY 20
+
 // SSL_error_description returns a string representation of |err|, where |err|
 // is one of the |SSL_ERROR_*| constants returned by |SSL_get_error|, or NULL
 // if the value is unrecognized.
@@ -1216,6 +1219,11 @@
 // key hooks. This is used to off-load signing operations to a custom,
 // potentially asynchronous, backend. Metadata about the key such as the type
 // and size are parsed out of the certificate.
+//
+// Callers that use this structure should additionally call
+// |SSL_set_signing_algorithm_prefs| or |SSL_CTX_set_signing_algorithm_prefs|
+// with the private key's capabilities. This ensures BoringSSL will select a
+// suitable signature algorithm for the private key.
 struct ssl_private_key_method_st {
   // sign signs the message |in| in using the specified signature algorithm. On
   // success, it returns |ssl_private_key_success| and writes at most |max_out|
@@ -1276,6 +1284,15 @@
 OPENSSL_EXPORT void SSL_CTX_set_private_key_method(
     SSL_CTX *ctx, const SSL_PRIVATE_KEY_METHOD *key_method);
 
+// SSL_can_release_private_key returns one if |ssl| will no longer call into the
+// private key and zero otherwise. If the function returns one, the caller can
+// release state associated with the private key.
+//
+// NOTE: This function assumes the caller does not use |SSL_clear| to reuse
+// |ssl| for a second connection. If |SSL_clear| is used, BoringSSL may still
+// use the private key on the second connection.
+OPENSSL_EXPORT int SSL_can_release_private_key(const SSL *ssl);
+
 
 // Cipher suites.
 //
@@ -1779,8 +1796,10 @@
 // used without leaking a correlator.
 OPENSSL_EXPORT int SSL_SESSION_should_be_single_use(const SSL_SESSION *session);
 
-// SSL_SESSION_is_resumable returns one if |session| is resumable and zero
-// otherwise.
+// SSL_SESSION_is_resumable returns one if |session| is complete and contains a
+// session ID or ticket. It returns zero otherwise. Note this function does not
+// ensure |session| will be resumed. It may be expired, dropped by the server,
+// or associated with incompatible parameters.
 OPENSSL_EXPORT int SSL_SESSION_is_resumable(const SSL_SESSION *session);
 
 // SSL_SESSION_has_ticket returns one if |session| has a ticket and zero
@@ -2723,8 +2742,9 @@
 
 // SSL_CTX_set_alpn_protos sets the client ALPN protocol list on |ctx| to
 // |protos|. |protos| must be in wire-format (i.e. a series of non-empty, 8-bit
-// length-prefixed strings). It returns zero on success and one on failure.
-// Configuring this list enables ALPN on a client.
+// length-prefixed strings), or the empty string to disable ALPN. It returns
+// zero on success and one on failure. Configuring a non-empty string enables
+// ALPN on a client.
 //
 // WARNING: this function is dangerous because it breaks the usual return value
 // convention.
@@ -2733,8 +2753,9 @@
 
 // SSL_set_alpn_protos sets the client ALPN protocol list on |ssl| to |protos|.
 // |protos| must be in wire-format (i.e. a series of non-empty, 8-bit
-// length-prefixed strings). It returns zero on success and one on failure.
-// Configuring this list enables ALPN on a client.
+// length-prefixed strings), or the empty string to disable ALPN. It returns
+// zero on success and one on failure. Configuring a non-empty string enables
+// ALPN on a client.
 //
 // WARNING: this function is dangerous because it breaks the usual return value
 // convention.
@@ -2743,18 +2764,34 @@
 
 // SSL_CTX_set_alpn_select_cb sets a callback function on |ctx| that is called
 // during ClientHello processing in order to select an ALPN protocol from the
-// client's list of offered protocols. Configuring this callback enables ALPN on
-// a server.
+// client's list of offered protocols.
 //
 // The callback is passed a wire-format (i.e. a series of non-empty, 8-bit
-// length-prefixed strings) ALPN protocol list in |in|. It should set |*out| and
-// |*out_len| to the selected protocol and return |SSL_TLSEXT_ERR_OK| on
-// success. It does not pass ownership of the buffer. Otherwise, it should
-// return |SSL_TLSEXT_ERR_NOACK|. Other |SSL_TLSEXT_ERR_*| values are
-// unimplemented and will be treated as |SSL_TLSEXT_ERR_NOACK|.
+// length-prefixed strings) ALPN protocol list in |in|. To select a protocol,
+// the callback should set |*out| and |*out_len| to the selected protocol and
+// return |SSL_TLSEXT_ERR_OK| on success. It does not pass ownership of the
+// buffer, so |*out| should point to a static string, a buffer that outlives the
+// callback call, or the corresponding entry in |in|.
+//
+// If the server supports ALPN, but there are no protocols in common, the
+// callback should return |SSL_TLSEXT_ERR_ALERT_FATAL| to abort the connection
+// with a no_application_protocol alert.
+//
+// If the server does not support ALPN, it can return |SSL_TLSEXT_ERR_NOACK| to
+// continue the handshake without negotiating a protocol. This may be useful if
+// multiple server configurations share an |SSL_CTX|, only some of which have
+// ALPN protocols configured.
+//
+// |SSL_TLSEXT_ERR_ALERT_WARNING| is ignored and will be treated as
+// |SSL_TLSEXT_ERR_NOACK|.
+//
+// The callback will only be called if the client supports ALPN. Callers that
+// wish to require ALPN for all clients must check |SSL_get0_alpn_selected|
+// after the handshake. In QUIC connections, this is done automatically.
 //
 // The cipher suite is selected before negotiating ALPN. The callback may use
-// |SSL_get_pending_cipher| to query the cipher suite.
+// |SSL_get_pending_cipher| to query the cipher suite. This may be used to
+// implement HTTP/2's cipher suite constraints.
 OPENSSL_EXPORT void SSL_CTX_set_alpn_select_cb(
     SSL_CTX *ctx, int (*cb)(SSL *ssl, const uint8_t **out, uint8_t *out_len,
                             const uint8_t *in, unsigned in_len, void *arg),
@@ -2940,15 +2977,16 @@
 
 // Channel ID.
 //
-// See draft-balfanz-tls-channelid-01.
+// See draft-balfanz-tls-channelid-01. This is an old, experimental mechanism
+// and should not be used in new code.
 
 // SSL_CTX_set_tls_channel_id_enabled configures whether connections associated
-// with |ctx| should enable Channel ID.
+// with |ctx| should enable Channel ID as a server.
 OPENSSL_EXPORT void SSL_CTX_set_tls_channel_id_enabled(SSL_CTX *ctx,
                                                        int enabled);
 
 // SSL_set_tls_channel_id_enabled configures whether |ssl| should enable Channel
-// ID.
+// ID as a server.
 OPENSSL_EXPORT void SSL_set_tls_channel_id_enabled(SSL *ssl, int enabled);
 
 // SSL_CTX_set1_tls_channel_id configures a TLS client to send a TLS Channel ID
@@ -2962,55 +3000,15 @@
 // success and zero on error.
 OPENSSL_EXPORT int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key);
 
-// SSL_get_tls_channel_id gets the client's TLS Channel ID from a server |SSL*|
+// SSL_get_tls_channel_id gets the client's TLS Channel ID from a server |SSL|
 // and copies up to the first |max_out| bytes into |out|. The Channel ID
 // consists of the client's P-256 public key as an (x,y) pair where each is a
 // 32-byte, big-endian field element. It returns 0 if the client didn't offer a
-// Channel ID and the length of the complete Channel ID otherwise.
+// Channel ID and the length of the complete Channel ID otherwise. This function
+// always returns zero if |ssl| is a client.
 OPENSSL_EXPORT size_t SSL_get_tls_channel_id(SSL *ssl, uint8_t *out,
                                              size_t max_out);
 
-// SSL_CTX_set_channel_id_cb sets a callback to be called when a TLS Channel ID
-// is requested. The callback may set |*out_pkey| to a key, passing a reference
-// to the caller. If none is returned, the handshake will pause and
-// |SSL_get_error| will return |SSL_ERROR_WANT_CHANNEL_ID_LOOKUP|.
-//
-// See also |SSL_ERROR_WANT_CHANNEL_ID_LOOKUP|.
-OPENSSL_EXPORT void SSL_CTX_set_channel_id_cb(
-    SSL_CTX *ctx, void (*channel_id_cb)(SSL *ssl, EVP_PKEY **out_pkey));
-
-// SSL_CTX_get_channel_id_cb returns the callback set by
-// |SSL_CTX_set_channel_id_cb|.
-OPENSSL_EXPORT void (*SSL_CTX_get_channel_id_cb(SSL_CTX *ctx))(
-    SSL *ssl, EVP_PKEY **out_pkey);
-
-
-// Token Binding.
-//
-// See draft-ietf-tokbind-protocol-16.
-
-// SSL_set_token_binding_params sets |params| as the Token Binding Key
-// parameters (section 3 of draft-ietf-tokbind-protocol-16) to negotiate on the
-// connection. If this function is not called, or if |len| is 0, then this
-// endpoint will not attempt to negotiate Token Binding. |params| are provided
-// in preference order, with the more preferred parameters at the beginning of
-// the list. This function returns 1 on success and 0 on failure.
-OPENSSL_EXPORT int SSL_set_token_binding_params(SSL *ssl, const uint8_t *params,
-                                                size_t len);
-
-// SSL_is_token_binding_negotiated returns 1 if Token Binding was negotiated
-// on this connection and 0 otherwise. On a server, it is possible for this
-// function to return 1 when the client's view of the connection is that Token
-// Binding was not negotiated. This occurs when the server indicates a version
-// of Token Binding less than the client's minimum version.
-OPENSSL_EXPORT int SSL_is_token_binding_negotiated(const SSL *ssl);
-
-// SSL_get_negotiated_token_binding_param returns the TokenBindingKeyParameters
-// enum value that was negotiated. It is only valid to call this function if
-// SSL_is_token_binding_negotiated returned 1, otherwise this function returns
-// an undefined value.
-OPENSSL_EXPORT uint8_t SSL_get_negotiated_token_binding_param(const SSL *ssl);
-
 
 // DTLS-SRTP.
 //
@@ -3047,8 +3045,8 @@
 OPENSSL_EXPORT int SSL_set_srtp_profiles(SSL *ssl, const char *profiles);
 
 // SSL_get_srtp_profiles returns the SRTP profiles supported by |ssl|.
-OPENSSL_EXPORT STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(
-    SSL *ssl);
+OPENSSL_EXPORT const STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(
+    const SSL *ssl);
 
 // SSL_get_selected_srtp_profile returns the selected SRTP profile, or NULL if
 // SRTP was not negotiated.
@@ -3179,7 +3177,7 @@
 //
 // QUIC acts as an underlying transport for the TLS 1.3 handshake. The following
 // functions allow a QUIC implementation to serve as the underlying transport as
-// described in draft-ietf-quic-tls.
+// described in RFC 9001.
 //
 // When configured for QUIC, |SSL_do_handshake| will drive the handshake as
 // before, but it will not use the configured |BIO|. It will call functions on
@@ -3199,8 +3197,7 @@
 // confirm the handshake. As a client, |SSL_ERROR_EARLY_DATA_REJECTED| and
 // |SSL_reset_early_data_reject| behave as usual.
 //
-// See https://tools.ietf.org/html/draft-ietf-quic-tls-15#section-4.1 for more
-// details.
+// See https://www.rfc-editor.org/rfc/rfc9001.html#section-4.1 for more details.
 //
 // To avoid DoS attacks, the QUIC implementation must limit the amount of data
 // being queued up. The implementation can call
@@ -3211,7 +3208,8 @@
 // |SSL_set_quic_transport_params|. |SSL_get_peer_quic_transport_params| may be
 // used to query the value received from the peer. BoringSSL handles this
 // extension as an opaque byte string. The caller is responsible for serializing
-// and parsing them. See draft-ietf-quic-transport (section 7.3) for details.
+// and parsing them. See https://www.rfc-editor.org/rfc/rfc9000#section-7.4 for
+// details.
 //
 // QUIC additionally imposes restrictions on 0-RTT. In particular, the QUIC
 // transport layer requires that if a server accepts 0-RTT data, then the
@@ -3323,7 +3321,7 @@
 // that may be received at the given encryption level. This function should be
 // used to limit buffering in the QUIC implementation.
 //
-// See https://tools.ietf.org/html/draft-ietf-quic-transport-16#section-4.4.
+// See https://www.rfc-editor.org/rfc/rfc9000#section-7.5
 OPENSSL_EXPORT size_t SSL_quic_max_handshake_flight_len(
     const SSL *ssl, enum ssl_encryption_level_t level);
 
@@ -3386,8 +3384,8 @@
 
 // SSL_set_quic_use_legacy_codepoint configures whether to use the legacy QUIC
 // extension codepoint 0xffa5 as opposed to the official value 57. Call with
-// |use_legacy| set to 1 to use 0xffa5 and call with 0 to use 57. The default
-// value for this is currently 1 but it will change to 0 at a later date.
+// |use_legacy| set to 1 to use 0xffa5 and call with 0 to use 57. By default,
+// the standard code point is used.
 OPENSSL_EXPORT void SSL_set_quic_use_legacy_codepoint(SSL *ssl, int use_legacy);
 
 // SSL_set_quic_early_data_context configures a context string in QUIC servers
@@ -3536,8 +3534,7 @@
   ssl_early_data_alpn_mismatch = 9,
   // The connection negotiated Channel ID, which is incompatible with 0-RTT.
   ssl_early_data_channel_id = 10,
-  // The connection negotiated token binding, which is incompatible with 0-RTT.
-  ssl_early_data_token_binding = 11,
+  // Value 11 is reserved. (It has historically |ssl_early_data_token_binding|.)
   // The client and server ticket age were too far apart.
   ssl_early_data_ticket_age_skew = 12,
   // QUIC parameters differ between this connection and the original.
@@ -3559,20 +3556,183 @@
     enum ssl_early_data_reason_t reason);
 
 
-// Encrypted Client Hello.
+// Encrypted ClientHello.
 //
 // ECH is a mechanism for encrypting the entire ClientHello message in TLS 1.3.
 // This can prevent observers from seeing cleartext information about the
 // connection, such as the server_name extension.
 //
+// By default, BoringSSL will treat the server name, session ticket, and client
+// certificate as secret, but most other parameters, such as the ALPN protocol
+// list will be treated as public and sent in the cleartext ClientHello. Other
+// APIs may be added for applications with different secrecy requirements.
+//
 // ECH support in BoringSSL is still experimental and under development.
 //
-// See https://tools.ietf.org/html/draft-ietf-tls-esni-09.
+// See https://tools.ietf.org/html/draft-ietf-tls-esni-13.
 
-// SSL_set_enable_ech_grease configures whether the client may send ECH GREASE
-// as part of this connection.
+// SSL_set_enable_ech_grease configures whether the client will send a GREASE
+// ECH extension when no supported ECHConfig is available.
 OPENSSL_EXPORT void SSL_set_enable_ech_grease(SSL *ssl, int enable);
 
+// SSL_set1_ech_config_list configures |ssl| to, as a client, offer ECH with the
+// specified configuration. |ech_config_list| should contain a serialized
+// ECHConfigList structure. It returns one on success and zero on error.
+//
+// This function returns an error if the input is malformed. If the input is
+// valid but none of the ECHConfigs implement supported parameters, it will
+// return success and proceed without ECH.
+//
+// If a supported ECHConfig is found, |ssl| will encrypt the true ClientHello
+// parameters. If the server cannot decrypt it, e.g. due to a key mismatch, ECH
+// has a recovery flow. |ssl| will handshake using the cleartext parameters,
+// including a public name in the ECHConfig. If using
+// |SSL_CTX_set_custom_verify|, callers should use |SSL_get0_ech_name_override|
+// to verify the certificate with the public name. If using the built-in
+// verifier, the |X509_STORE_CTX| will be configured automatically.
+//
+// If no other errors are found in this handshake, it will fail with
+// |SSL_R_ECH_REJECTED|. Since it didn't use the true parameters, the connection
+// cannot be used for application data. Instead, callers should handle this
+// error by calling |SSL_get0_ech_retry_configs| and retrying the connection
+// with updated ECH parameters. If the retry also fails with
+// |SSL_R_ECH_REJECTED|, the caller should report a connection failure.
+OPENSSL_EXPORT int SSL_set1_ech_config_list(SSL *ssl,
+                                            const uint8_t *ech_config_list,
+                                            size_t ech_config_list_len);
+
+// SSL_get0_ech_name_override, if |ssl| is a client and the server rejected ECH,
+// sets |*out_name| and |*out_name_len| to point to a buffer containing the ECH
+// public name. Otherwise, the buffer will be empty.
+//
+// When offering ECH as a client, this function should be called during the
+// certificate verification callback (see |SSL_CTX_set_custom_verify|). If
+// |*out_name_len| is non-zero, the caller should verify the certificate against
+// the result, interpreted as a DNS name, rather than the true server name. In
+// this case, the handshake will never succeed and is only used to authenticate
+// retry configs. See also |SSL_get0_ech_retry_configs|.
+OPENSSL_EXPORT void SSL_get0_ech_name_override(const SSL *ssl,
+                                               const char **out_name,
+                                               size_t *out_name_len);
+
+// SSL_get0_ech_retry_configs sets |*out_retry_configs| and
+// |*out_retry_configs_len| to a buffer containing a serialized ECHConfigList.
+// If the server did not provide an ECHConfigList, |*out_retry_configs_len| will
+// be zero.
+//
+// When handling an |SSL_R_ECH_REJECTED| error code as a client, callers should
+// use this function to recover from potential key mismatches. If the result is
+// non-empty, the caller should retry the connection, passing this buffer to
+// |SSL_set1_ech_config_list|. If the result is empty, the server has rolled
+// back ECH support, and the caller should retry without ECH.
+//
+// This function must only be called in response to an |SSL_R_ECH_REJECTED|
+// error code. Calling this function on |ssl|s that have not authenticated the
+// rejection handshake will assert in debug builds and otherwise return an
+// unparsable list.
+OPENSSL_EXPORT void SSL_get0_ech_retry_configs(
+    const SSL *ssl, const uint8_t **out_retry_configs,
+    size_t *out_retry_configs_len);
+
+// SSL_marshal_ech_config constructs a new serialized ECHConfig. On success, it
+// sets |*out| to a newly-allocated buffer containing the result and |*out_len|
+// to the size of the buffer. The caller must call |OPENSSL_free| on |*out| to
+// release the memory. On failure, it returns zero.
+//
+// The |config_id| field is a single byte identifer for the ECHConfig. Reusing
+// config IDs is allowed, but if multiple ECHConfigs with the same config ID are
+// active at a time, server load may increase. See
+// |SSL_ECH_KEYS_has_duplicate_config_id|.
+//
+// The public key and KEM algorithm are taken from |key|. |public_name| is the
+// DNS name used to authenticate the recovery flow. |max_name_len| should be the
+// length of the longest name in the ECHConfig's anonymity set and influences
+// client padding decisions.
+OPENSSL_EXPORT int SSL_marshal_ech_config(uint8_t **out, size_t *out_len,
+                                          uint8_t config_id,
+                                          const EVP_HPKE_KEY *key,
+                                          const char *public_name,
+                                          size_t max_name_len);
+
+// SSL_ECH_KEYS_new returns a newly-allocated |SSL_ECH_KEYS| or NULL on error.
+OPENSSL_EXPORT SSL_ECH_KEYS *SSL_ECH_KEYS_new(void);
+
+// SSL_ECH_KEYS_up_ref increments the reference count of |keys|.
+OPENSSL_EXPORT void SSL_ECH_KEYS_up_ref(SSL_ECH_KEYS *keys);
+
+// SSL_ECH_KEYS_free releases memory associated with |keys|.
+OPENSSL_EXPORT void SSL_ECH_KEYS_free(SSL_ECH_KEYS *keys);
+
+// SSL_ECH_KEYS_add decodes |ech_config| as an ECHConfig and appends it with
+// |key| to |keys|. If |is_retry_config| is non-zero, this config will be
+// returned to the client on configuration mismatch. It returns one on success
+// and zero on error.
+//
+// This function should be called successively to register each ECHConfig in
+// decreasing order of preference. This configuration must be completed before
+// setting |keys| on an |SSL_CTX| with |SSL_CTX_set1_ech_keys|. After that
+// point, |keys| is immutable; no more ECHConfig values may be added.
+//
+// See also |SSL_CTX_set1_ech_keys|.
+OPENSSL_EXPORT int SSL_ECH_KEYS_add(SSL_ECH_KEYS *keys, int is_retry_config,
+                                    const uint8_t *ech_config,
+                                    size_t ech_config_len,
+                                    const EVP_HPKE_KEY *key);
+
+// SSL_ECH_KEYS_has_duplicate_config_id returns one if |keys| has duplicate
+// config IDs or zero otherwise. Duplicate config IDs still work, but may
+// increase server load due to trial decryption.
+OPENSSL_EXPORT int SSL_ECH_KEYS_has_duplicate_config_id(
+    const SSL_ECH_KEYS *keys);
+
+// SSL_ECH_KEYS_marshal_retry_configs serializes the retry configs in |keys| as
+// an ECHConfigList. On success, it sets |*out| to a newly-allocated buffer
+// containing the result and |*out_len| to the size of the buffer. The caller
+// must call |OPENSSL_free| on |*out| to release the memory. On failure, it
+// returns zero.
+//
+// This output may be advertised to clients in DNS.
+OPENSSL_EXPORT int SSL_ECH_KEYS_marshal_retry_configs(const SSL_ECH_KEYS *keys,
+                                                      uint8_t **out,
+                                                      size_t *out_len);
+
+// SSL_CTX_set1_ech_keys configures |ctx| to use |keys| to decrypt encrypted
+// ClientHellos. It returns one on success, and zero on failure. If |keys| does
+// not contain any retry configs, this function will fail. Retry configs are
+// marked as such when they are added to |keys| with |SSL_ECH_KEYS_add|.
+//
+// Once |keys| has been passed to this function, it is immutable. Unlike most
+// |SSL_CTX| configuration functions, this function may be called even if |ctx|
+// already has associated connections on multiple threads. This may be used to
+// rotate keys in a long-lived server process.
+//
+// The configured ECHConfig values should also be advertised out-of-band via DNS
+// (see draft-ietf-dnsop-svcb-https). Before advertising an ECHConfig in DNS,
+// deployments should ensure all instances of the service are configured with
+// the ECHConfig and corresponding private key.
+//
+// Only the most recent fully-deployed ECHConfigs should be advertised in DNS.
+// |keys| may contain a newer set if those ECHConfigs are mid-deployment. It
+// should also contain older sets, until the DNS change has rolled out and the
+// old records have expired from caches.
+//
+// If there is a mismatch, |SSL| objects associated with |ctx| will complete the
+// handshake using the cleartext ClientHello and send updated ECHConfig values
+// to the client. The client will then retry to recover, but with a latency
+// penalty. This recovery flow depends on the public name in the ECHConfig.
+// Before advertising an ECHConfig in DNS, deployments must ensure all instances
+// of the service can present a valid certificate for the public name.
+//
+// BoringSSL negotiates ECH before certificate selection callbacks are called,
+// including |SSL_CTX_set_select_certificate_cb|. If ECH is negotiated, the
+// reported |SSL_CLIENT_HELLO| structure and |SSL_get_servername| function will
+// transparently reflect the inner ClientHello. Callers should select parameters
+// based on these values to correctly handle ECH as well as the recovery flow.
+OPENSSL_EXPORT int SSL_CTX_set1_ech_keys(SSL_CTX *ctx, SSL_ECH_KEYS *keys);
+
+// SSL_ech_accepted returns one if |ssl| negotiated ECH and zero otherwise.
+OPENSSL_EXPORT int SSL_ech_accepted(const SSL *ssl);
+
 
 // Alerts.
 //
@@ -3627,6 +3787,7 @@
 #define SSL_AD_UNKNOWN_PSK_IDENTITY TLS1_AD_UNKNOWN_PSK_IDENTITY
 #define SSL_AD_CERTIFICATE_REQUIRED TLS1_AD_CERTIFICATE_REQUIRED
 #define SSL_AD_NO_APPLICATION_PROTOCOL TLS1_AD_NO_APPLICATION_PROTOCOL
+#define SSL_AD_ECH_REQUIRED TLS1_AD_ECH_REQUIRED
 
 // SSL_alert_type_string_long returns a string description of |value| as an
 // alert type (warning or fatal).
@@ -3709,6 +3870,101 @@
 OPENSSL_EXPORT uint64_t SSL_get_write_sequence(const SSL *ssl);
 
 
+// Handshake hints.
+//
+// *** EXPERIMENTAL — DO NOT USE WITHOUT CHECKING ***
+//
+// Some server deployments make asynchronous RPC calls in both ClientHello
+// dispatch and private key operations. In TLS handshakes where the private key
+// operation occurs in the first round-trip, this results in two consecutive RPC
+// round-trips. Handshake hints allow the RPC service to predicte a signature.
+// If correctly predicted, this can skip the second RPC call.
+//
+// First, the server installs a certificate selection callback (see
+// |SSL_CTX_set_select_certificate_cb|). When that is called, it performs the
+// RPC as before, but includes the ClientHello and a capabilities string from
+// |SSL_serialize_capabilities|.
+//
+// Next, the RPC service creates its own |SSL| object, applies the results of
+// certificate selection, calls |SSL_request_handshake_hints|, and runs the
+// handshake. If this successfully computes handshake hints (see
+// |SSL_serialize_handshake_hints|), the RPC server should send the hints
+// alongside any certificate selection results.
+//
+// Finally, the server calls |SSL_set_handshake_hints| and applies any
+// configuration from the RPC server. It then completes the handshake as before.
+// If the hints apply, BoringSSL will use the predicted signature and skip the
+// private key callbacks. Otherwise, BoringSSL will call private key callbacks
+// to generate a signature as before.
+//
+// Callers should synchronize configuration across the two services.
+// Configuration mismatches and some cases of version skew are not fatal, but
+// may result in the hints not applying. Additionally, some handshake flows use
+// the private key in later round-trips, such as TLS 1.3 HelloRetryRequest. In
+// those cases, BoringSSL will not predict a signature as there is no benefit.
+// Callers must allow for handshakes to complete without a predicted signature.
+//
+// For now, only TLS 1.3 is hinted. TLS 1.2 will work, but the hints will be
+// empty.
+
+// SSL_serialize_capabilities writes an opaque byte string to |out| describing
+// some of |ssl|'s capabilities. It returns one on success and zero on error.
+//
+// This string is used by BoringSSL internally to reduce the impact of version
+// skew.
+OPENSSL_EXPORT int SSL_serialize_capabilities(const SSL *ssl, CBB *out);
+
+// SSL_request_handshake_hints configures |ssl| to generate a handshake hint for
+// |client_hello|. It returns one on success and zero on error. |client_hello|
+// should contain a serialized ClientHello structure, from the |client_hello|
+// and |client_hello_len| fields of the |SSL_CLIENT_HELLO| structure.
+// |capabilities| should contain the output of |SSL_serialize_capabilities|.
+//
+// When configured, |ssl| will perform no I/O (so there is no need to configure
+// |BIO|s). For QUIC, the caller should still configure an |SSL_QUIC_METHOD|,
+// but the callbacks themselves will never be called and may be left NULL or
+// report failure. |SSL_provide_quic_data| also should not be called.
+//
+// If hint generation is successful, |SSL_do_handshake| will stop the handshake
+// early with |SSL_get_error| returning |SSL_ERROR_HANDSHAKE_HINTS_READY|. At
+// this point, the caller should run |SSL_serialize_handshake_hints| to extract
+// the resulting hints.
+//
+// Hint generation may fail if, e.g., |ssl| was unable to process the
+// ClientHello. Callers should then complete the certificate selection RPC and
+// continue the original handshake with no hint. It will likely fail, but this
+// reports the correct alert to the client and is more robust in case of
+// mismatch.
+OPENSSL_EXPORT int SSL_request_handshake_hints(SSL *ssl,
+                                               const uint8_t *client_hello,
+                                               size_t client_hello_len,
+                                               const uint8_t *capabilities,
+                                               size_t capabilities_len);
+
+// SSL_serialize_handshake_hints writes an opaque byte string to |out|
+// containing the handshake hints computed by |out|. It returns one on success
+// and zero on error. This function should only be called if
+// |SSL_request_handshake_hints| was configured and the handshake terminated
+// with |SSL_ERROR_HANDSHAKE_HINTS_READY|.
+//
+// This string may be passed to |SSL_set_handshake_hints| on another |SSL| to
+// avoid an extra signature call.
+OPENSSL_EXPORT int SSL_serialize_handshake_hints(const SSL *ssl, CBB *out);
+
+// SSL_set_handshake_hints configures |ssl| to use |hints| as handshake hints.
+// It returns one on success and zero on error. The handshake will then continue
+// as before, but apply predicted values from |hints| where applicable.
+//
+// Hints may contain connection and session secrets, so they must not leak and
+// must come from a source trusted to terminate the connection. However, they
+// will not change |ssl|'s configuration. The caller is responsible for
+// serializing and applying options from the RPC server as needed. This ensures
+// |ssl|'s behavior is self-consistent and consistent with the caller's local
+// decisions.
+OPENSSL_EXPORT int SSL_set_handshake_hints(SSL *ssl, const uint8_t *hints,
+                                           size_t hints_len);
+
+
 // Obscure functions.
 
 // SSL_CTX_set_msg_callback installs |cb| as the message callback for |ctx|.
@@ -4093,9 +4349,17 @@
                                                                    int enable);
 
 // SSL_CTX_set_grease_enabled configures whether sockets on |ctx| should enable
-// GREASE. See draft-davidben-tls-grease-01.
+// GREASE. See RFC 8701.
 OPENSSL_EXPORT void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled);
 
+// SSL_CTX_set_permute_extensions configures whether sockets on |ctx| should
+// permute extensions. For now, this is only implemented for the ClientHello.
+OPENSSL_EXPORT void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled);
+
+// SSL_set_permute_extensions configures whether sockets on |ssl| should
+// permute extensions. For now, this is only implemented for the ClientHello.
+OPENSSL_EXPORT void SSL_set_permute_extensions(SSL *ssl, int enabled);
+
 // SSL_max_seal_overhead returns the maximum overhead, in bytes, of sealing a
 // record with |ssl|.
 OPENSSL_EXPORT size_t SSL_max_seal_overhead(const SSL *ssl);
@@ -4630,12 +4894,6 @@
 OPENSSL_EXPORT int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *out,
                                                       const char *dir);
 
-// SSL_set_verify_result calls |abort| unless |result| is |X509_V_OK|.
-//
-// TODO(davidben): Remove this function once it has been removed from
-// netty-tcnative.
-OPENSSL_EXPORT void SSL_set_verify_result(SSL *ssl, long result);
-
 // SSL_CTX_enable_tls_channel_id calls |SSL_CTX_set_tls_channel_id_enabled|.
 OPENSSL_EXPORT int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx);
 
@@ -4782,18 +5040,6 @@
 // name and remove this one.
 OPENSSL_EXPORT uint16_t SSL_CIPHER_get_value(const SSL_CIPHER *cipher);
 
-// SSL_CTX_set_ignore_tls13_downgrade does nothing.
-OPENSSL_EXPORT void SSL_CTX_set_ignore_tls13_downgrade(SSL_CTX *ctx,
-                                                       int ignore);
-
-// SSL_set_ignore_tls13_downgrade does nothing.
-OPENSSL_EXPORT void SSL_set_ignore_tls13_downgrade(SSL *ssl, int ignore);
-
-// SSL_is_tls13_downgrade returns zero. Historically, this function returned
-// whether the TLS 1.3 downgrade signal would have been enforced if not
-// disabled. The TLS 1.3 downgrade signal is now always enforced.
-OPENSSL_EXPORT int SSL_is_tls13_downgrade(const SSL *ssl);
-
 
 // Nodejs compatibility section (hidden).
 //
@@ -4956,6 +5202,8 @@
 BORINGSSL_MAKE_DELETER(SSL, SSL_free)
 BORINGSSL_MAKE_DELETER(SSL_CTX, SSL_CTX_free)
 BORINGSSL_MAKE_UP_REF(SSL_CTX, SSL_CTX_up_ref)
+BORINGSSL_MAKE_DELETER(SSL_ECH_KEYS, SSL_ECH_KEYS_free)
+BORINGSSL_MAKE_UP_REF(SSL_ECH_KEYS, SSL_ECH_KEYS_up_ref)
 BORINGSSL_MAKE_DELETER(SSL_SESSION, SSL_SESSION_free)
 BORINGSSL_MAKE_UP_REF(SSL_SESSION, SSL_SESSION_up_ref)
 
@@ -5072,6 +5320,7 @@
     const SSL *ssl, Span<const uint8_t> *out_read_traffic_secret,
     Span<const uint8_t> *out_write_traffic_secret);
 
+
 BSSL_NAMESPACE_END
 
 }  // extern C++
@@ -5286,9 +5535,21 @@
 #define SSL_R_CIPHER_MISMATCH_ON_EARLY_DATA 304
 #define SSL_R_QUIC_TRANSPORT_PARAMETERS_MISCONFIGURED 305
 #define SSL_R_UNEXPECTED_COMPATIBILITY_MODE 306
-#define SSL_R_MISSING_ALPN 307
+#define SSL_R_NO_APPLICATION_PROTOCOL 307
 #define SSL_R_NEGOTIATED_ALPS_WITHOUT_ALPN 308
 #define SSL_R_ALPS_MISMATCH_ON_EARLY_DATA 309
+#define SSL_R_ECH_SERVER_CONFIG_AND_PRIVATE_KEY_MISMATCH 310
+#define SSL_R_ECH_SERVER_CONFIG_UNSUPPORTED_EXTENSION 311
+#define SSL_R_UNSUPPORTED_ECH_SERVER_CONFIG 312
+#define SSL_R_ECH_SERVER_WOULD_HAVE_NO_RETRY_CONFIGS 313
+#define SSL_R_INVALID_CLIENT_HELLO_INNER 314
+#define SSL_R_INVALID_ALPN_PROTOCOL_LIST 315
+#define SSL_R_COULD_NOT_PARSE_HINTS 316
+#define SSL_R_INVALID_ECH_PUBLIC_NAME 317
+#define SSL_R_INVALID_ECH_CONFIG_LIST 318
+#define SSL_R_ECH_REJECTED 319
+#define SSL_R_OUTER_EXTENSION_NOT_FOUND 320
+#define SSL_R_INCONSISTENT_ECH_NEGOTIATION 321
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
@@ -5322,5 +5583,6 @@
 #define SSL_R_TLSV1_ALERT_UNKNOWN_PSK_IDENTITY 1115
 #define SSL_R_TLSV1_ALERT_CERTIFICATE_REQUIRED 1116
 #define SSL_R_TLSV1_ALERT_NO_APPLICATION_PROTOCOL 1120
+#define SSL_R_TLSV1_ALERT_ECH_REQUIRED 1121
 
 #endif  // OPENSSL_HEADER_SSL_H
diff --git a/deps/boringssl/src/include/openssl/tls1.h b/deps/boringssl/src/include/openssl/tls1.h
index da79a08..a3136c0 100644
--- a/deps/boringssl/src/include/openssl/tls1.h
+++ b/deps/boringssl/src/include/openssl/tls1.h
@@ -171,7 +171,6 @@
 #define TLS1_AD_USER_CANCELLED 90
 #define TLS1_AD_NO_RENEGOTIATION 100
 #define TLS1_AD_MISSING_EXTENSION 109
-// codes 110-114 are from RFC3546
 #define TLS1_AD_UNSUPPORTED_EXTENSION 110
 #define TLS1_AD_CERTIFICATE_UNOBTAINABLE 111
 #define TLS1_AD_UNRECOGNIZED_NAME 112
@@ -180,59 +179,53 @@
 #define TLS1_AD_UNKNOWN_PSK_IDENTITY 115
 #define TLS1_AD_CERTIFICATE_REQUIRED 116
 #define TLS1_AD_NO_APPLICATION_PROTOCOL 120
+#define TLS1_AD_ECH_REQUIRED 121  // draft-ietf-tls-esni-13
 
-// ExtensionType values from RFC6066
+// ExtensionType values from RFC 6066
 #define TLSEXT_TYPE_server_name 0
 #define TLSEXT_TYPE_status_request 5
 
-// ExtensionType values from RFC4492
+// ExtensionType values from RFC 4492
 #define TLSEXT_TYPE_ec_point_formats 11
 
-// ExtensionType values from RFC5246
+// ExtensionType values from RFC 5246
 #define TLSEXT_TYPE_signature_algorithms 13
 
-// ExtensionType value from RFC5764
+// ExtensionType value from RFC 5764
 #define TLSEXT_TYPE_srtp 14
 
-// ExtensionType value from RFC7301
+// ExtensionType value from RFC 7301
 #define TLSEXT_TYPE_application_layer_protocol_negotiation 16
 
-// ExtensionType value from RFC7685
+// ExtensionType value from RFC 7685
 #define TLSEXT_TYPE_padding 21
 
-// ExtensionType value from RFC7627
+// ExtensionType value from RFC 7627
 #define TLSEXT_TYPE_extended_master_secret 23
 
-// ExtensionType value from draft-ietf-tokbind-negotiation-10
-#define TLSEXT_TYPE_token_binding 24
-
 // ExtensionType value from draft-ietf-quic-tls. Drafts 00 through 32 use
 // 0xffa5 which is part of the Private Use section of the registry, and it
 // collides with TLS-LTS and, based on scans, something else too (though this
 // hasn't been a problem in practice since it's QUIC-only). Drafts 33 onward
 // use the value 57 which was officially registered with IANA.
 #define TLSEXT_TYPE_quic_transport_parameters_legacy 0xffa5
-#define TLSEXT_TYPE_quic_transport_parameters_standard 57
 
-// TLSEXT_TYPE_quic_transport_parameters is an alias for
-// |TLSEXT_TYPE_quic_transport_parameters_legacy|. It will switch to
-// |TLSEXT_TYPE_quic_transport_parameters_standard| at a later date.
-//
-// Callers using |SSL_set_quic_use_legacy_codepoint| should use
-// |TLSEXT_TYPE_quic_transport_parameters_legacy| or
-// |TLSEXT_TYPE_quic_transport_parameters_standard| rather than this constant.
-// When the default code point is switched to the standard one, this value will
-// be updated and we will transition callers back to the unsuffixed constant.
-#define TLSEXT_TYPE_quic_transport_parameters \
-  TLSEXT_TYPE_quic_transport_parameters_legacy
+// ExtensionType value from RFC 9000
+#define TLSEXT_TYPE_quic_transport_parameters 57
 
-// ExtensionType value from RFC8879
+// TLSEXT_TYPE_quic_transport_parameters_standard is an alias for
+// |TLSEXT_TYPE_quic_transport_parameters|. Use
+// |TLSEXT_TYPE_quic_transport_parameters| instead.
+#define TLSEXT_TYPE_quic_transport_parameters_standard \
+  TLSEXT_TYPE_quic_transport_parameters
+
+// ExtensionType value from RFC 8879
 #define TLSEXT_TYPE_cert_compression 27
 
-// ExtensionType value from RFC4507
+// ExtensionType value from RFC 4507
 #define TLSEXT_TYPE_session_ticket 35
 
-// ExtensionType values from RFC8446
+// ExtensionType values from RFC 8446
 #define TLSEXT_TYPE_supported_groups 10
 #define TLSEXT_TYPE_pre_shared_key 41
 #define TLSEXT_TYPE_early_data 42
@@ -243,7 +236,7 @@
 #define TLSEXT_TYPE_signature_algorithms_cert 50
 #define TLSEXT_TYPE_key_share 51
 
-// ExtensionType value from RFC5746
+// ExtensionType value from RFC 5746
 #define TLSEXT_TYPE_renegotiate 0xff01
 
 // ExtensionType value from draft-ietf-tls-subcerts.
@@ -253,12 +246,12 @@
 // extension number.
 #define TLSEXT_TYPE_application_settings 17513
 
-// ExtensionType values from draft-ietf-tls-esni-09. This is not an IANA defined
+// ExtensionType values from draft-ietf-tls-esni-13. This is not an IANA defined
 // extension number.
-#define TLSEXT_TYPE_encrypted_client_hello 0xfe09
-#define TLSEXT_TYPE_ech_is_inner 0xda09
+#define TLSEXT_TYPE_encrypted_client_hello 0xfe0d
+#define TLSEXT_TYPE_ech_outer_extensions 0xfd00
 
-// ExtensionType value from RFC6962
+// ExtensionType value from RFC 6962
 #define TLSEXT_TYPE_certificate_timestamp 18
 
 // This is not an IANA defined extension number
@@ -319,7 +312,7 @@
 #define TLS1_CK_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA 0x03000065
 #define TLS1_CK_DHE_DSS_WITH_RC4_128_SHA 0x03000066
 
-// AES ciphersuites from RFC3268
+// AES ciphersuites from RFC 3268
 
 #define TLS1_CK_RSA_WITH_AES_128_SHA 0x0300002F
 #define TLS1_CK_DH_DSS_WITH_AES_128_SHA 0x03000030
@@ -343,7 +336,7 @@
 #define TLS1_CK_DH_RSA_WITH_AES_128_SHA256 0x0300003F
 #define TLS1_CK_DHE_DSS_WITH_AES_128_SHA256 0x03000040
 
-// Camellia ciphersuites from RFC4132
+// Camellia ciphersuites from RFC 4132
 #define TLS1_CK_RSA_WITH_CAMELLIA_128_CBC_SHA 0x03000041
 #define TLS1_CK_DH_DSS_WITH_CAMELLIA_128_CBC_SHA 0x03000042
 #define TLS1_CK_DH_RSA_WITH_CAMELLIA_128_CBC_SHA 0x03000043
@@ -360,7 +353,7 @@
 #define TLS1_CK_ADH_WITH_AES_128_SHA256 0x0300006C
 #define TLS1_CK_ADH_WITH_AES_256_SHA256 0x0300006D
 
-// Camellia ciphersuites from RFC4132
+// Camellia ciphersuites from RFC 4132
 #define TLS1_CK_RSA_WITH_CAMELLIA_256_CBC_SHA 0x03000084
 #define TLS1_CK_DH_DSS_WITH_CAMELLIA_256_CBC_SHA 0x03000085
 #define TLS1_CK_DH_RSA_WITH_CAMELLIA_256_CBC_SHA 0x03000086
@@ -368,7 +361,7 @@
 #define TLS1_CK_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA 0x03000088
 #define TLS1_CK_ADH_WITH_CAMELLIA_256_CBC_SHA 0x03000089
 
-// SEED ciphersuites from RFC4162
+// SEED ciphersuites from RFC 4162
 #define TLS1_CK_RSA_WITH_SEED_SHA 0x03000096
 #define TLS1_CK_DH_DSS_WITH_SEED_SHA 0x03000097
 #define TLS1_CK_DH_RSA_WITH_SEED_SHA 0x03000098
@@ -376,7 +369,7 @@
 #define TLS1_CK_DHE_RSA_WITH_SEED_SHA 0x0300009A
 #define TLS1_CK_ADH_WITH_SEED_SHA 0x0300009B
 
-// TLS v1.2 GCM ciphersuites from RFC5288
+// TLS v1.2 GCM ciphersuites from RFC 5288
 #define TLS1_CK_RSA_WITH_AES_128_GCM_SHA256 0x0300009C
 #define TLS1_CK_RSA_WITH_AES_256_GCM_SHA384 0x0300009D
 #define TLS1_CK_DHE_RSA_WITH_AES_128_GCM_SHA256 0x0300009E
@@ -390,7 +383,7 @@
 #define TLS1_CK_ADH_WITH_AES_128_GCM_SHA256 0x030000A6
 #define TLS1_CK_ADH_WITH_AES_256_GCM_SHA384 0x030000A7
 
-// ECC ciphersuites from RFC4492
+// ECC ciphersuites from RFC 4492
 #define TLS1_CK_ECDH_ECDSA_WITH_NULL_SHA 0x0300C001
 #define TLS1_CK_ECDH_ECDSA_WITH_RC4_128_SHA 0x0300C002
 #define TLS1_CK_ECDH_ECDSA_WITH_DES_192_CBC3_SHA 0x0300C003
@@ -432,7 +425,7 @@
 #define TLS1_CK_SRP_SHA_RSA_WITH_AES_256_CBC_SHA 0x0300C021
 #define TLS1_CK_SRP_SHA_DSS_WITH_AES_256_CBC_SHA 0x0300C022
 
-// ECDH HMAC based ciphersuites from RFC5289
+// ECDH HMAC based ciphersuites from RFC 5289
 
 #define TLS1_CK_ECDHE_ECDSA_WITH_AES_128_SHA256 0x0300C023
 #define TLS1_CK_ECDHE_ECDSA_WITH_AES_256_SHA384 0x0300C024
@@ -443,7 +436,7 @@
 #define TLS1_CK_ECDH_RSA_WITH_AES_128_SHA256 0x0300C029
 #define TLS1_CK_ECDH_RSA_WITH_AES_256_SHA384 0x0300C02A
 
-// ECDH GCM based ciphersuites from RFC5289
+// ECDH GCM based ciphersuites from RFC 5289
 #define TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0x0300C02B
 #define TLS1_CK_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0x0300C02C
 #define TLS1_CK_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0x0300C02D
@@ -479,7 +472,7 @@
 #define TLS1_TXT_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA "EXP1024-DHE-DSS-RC4-SHA"
 #define TLS1_TXT_DHE_DSS_WITH_RC4_128_SHA "DHE-DSS-RC4-SHA"
 
-// AES ciphersuites from RFC3268
+// AES ciphersuites from RFC 3268
 #define TLS1_TXT_RSA_WITH_AES_128_SHA "AES128-SHA"
 #define TLS1_TXT_DH_DSS_WITH_AES_128_SHA "DH-DSS-AES128-SHA"
 #define TLS1_TXT_DH_RSA_WITH_AES_128_SHA "DH-RSA-AES128-SHA"
@@ -494,7 +487,7 @@
 #define TLS1_TXT_DHE_RSA_WITH_AES_256_SHA "DHE-RSA-AES256-SHA"
 #define TLS1_TXT_ADH_WITH_AES_256_SHA "ADH-AES256-SHA"
 
-// ECC ciphersuites from RFC4492
+// ECC ciphersuites from RFC 4492
 #define TLS1_TXT_ECDH_ECDSA_WITH_NULL_SHA "ECDH-ECDSA-NULL-SHA"
 #define TLS1_TXT_ECDH_ECDSA_WITH_RC4_128_SHA "ECDH-ECDSA-RC4-SHA"
 #define TLS1_TXT_ECDH_ECDSA_WITH_DES_192_CBC3_SHA "ECDH-ECDSA-DES-CBC3-SHA"
@@ -546,7 +539,7 @@
 #define TLS1_TXT_SRP_SHA_RSA_WITH_AES_256_CBC_SHA "SRP-RSA-AES-256-CBC-SHA"
 #define TLS1_TXT_SRP_SHA_DSS_WITH_AES_256_CBC_SHA "SRP-DSS-AES-256-CBC-SHA"
 
-// Camellia ciphersuites from RFC4132
+// Camellia ciphersuites from RFC 4132
 #define TLS1_TXT_RSA_WITH_CAMELLIA_128_CBC_SHA "CAMELLIA128-SHA"
 #define TLS1_TXT_DH_DSS_WITH_CAMELLIA_128_CBC_SHA "DH-DSS-CAMELLIA128-SHA"
 #define TLS1_TXT_DH_RSA_WITH_CAMELLIA_128_CBC_SHA "DH-RSA-CAMELLIA128-SHA"
@@ -561,7 +554,7 @@
 #define TLS1_TXT_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA "DHE-RSA-CAMELLIA256-SHA"
 #define TLS1_TXT_ADH_WITH_CAMELLIA_256_CBC_SHA "ADH-CAMELLIA256-SHA"
 
-// SEED ciphersuites from RFC4162
+// SEED ciphersuites from RFC 4162
 #define TLS1_TXT_RSA_WITH_SEED_SHA "SEED-SHA"
 #define TLS1_TXT_DH_DSS_WITH_SEED_SHA "DH-DSS-SEED-SHA"
 #define TLS1_TXT_DH_RSA_WITH_SEED_SHA "DH-RSA-SEED-SHA"
@@ -584,7 +577,7 @@
 #define TLS1_TXT_ADH_WITH_AES_128_SHA256 "ADH-AES128-SHA256"
 #define TLS1_TXT_ADH_WITH_AES_256_SHA256 "ADH-AES256-SHA256"
 
-// TLS v1.2 GCM ciphersuites from RFC5288
+// TLS v1.2 GCM ciphersuites from RFC 5288
 #define TLS1_TXT_RSA_WITH_AES_128_GCM_SHA256 "AES128-GCM-SHA256"
 #define TLS1_TXT_RSA_WITH_AES_256_GCM_SHA384 "AES256-GCM-SHA384"
 #define TLS1_TXT_DHE_RSA_WITH_AES_128_GCM_SHA256 "DHE-RSA-AES128-GCM-SHA256"
@@ -598,7 +591,7 @@
 #define TLS1_TXT_ADH_WITH_AES_128_GCM_SHA256 "ADH-AES128-GCM-SHA256"
 #define TLS1_TXT_ADH_WITH_AES_256_GCM_SHA384 "ADH-AES256-GCM-SHA384"
 
-// ECDH HMAC based ciphersuites from RFC5289
+// ECDH HMAC based ciphersuites from RFC 5289
 
 #define TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_SHA256 "ECDHE-ECDSA-AES128-SHA256"
 #define TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_SHA384 "ECDHE-ECDSA-AES256-SHA384"
@@ -609,7 +602,7 @@
 #define TLS1_TXT_ECDH_RSA_WITH_AES_128_SHA256 "ECDH-RSA-AES128-SHA256"
 #define TLS1_TXT_ECDH_RSA_WITH_AES_256_SHA384 "ECDH-RSA-AES256-SHA384"
 
-// ECDH GCM based ciphersuites from RFC5289
+// ECDH GCM based ciphersuites from RFC 5289
 #define TLS1_TXT_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 \
   "ECDHE-ECDSA-AES128-GCM-SHA256"
 #define TLS1_TXT_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 \
diff --git a/deps/boringssl/src/include/openssl/x509.h b/deps/boringssl/src/include/openssl/x509.h
index a75442f..f4444c9 100644
--- a/deps/boringssl/src/include/openssl/x509.h
+++ b/deps/boringssl/src/include/openssl/x509.h
@@ -110,148 +110,36 @@
 #define X509v3_KU_DECIPHER_ONLY 0x8000
 #define X509v3_KU_UNDEF 0xffff
 
+struct X509_algor_st {
+  ASN1_OBJECT *algorithm;
+  ASN1_TYPE *parameter;
+} /* X509_ALGOR */;
+
+DECLARE_ASN1_FUNCTIONS(X509_ALGOR)
+
 DEFINE_STACK_OF(X509_ALGOR)
-DECLARE_ASN1_SET_OF(X509_ALGOR)
 
 typedef STACK_OF(X509_ALGOR) X509_ALGORS;
 
-struct X509_val_st {
-  ASN1_TIME *notBefore;
-  ASN1_TIME *notAfter;
-} /* X509_VAL */;
-
-struct X509_pubkey_st {
-  X509_ALGOR *algor;
-  ASN1_BIT_STRING *public_key;
-  EVP_PKEY *pkey;
-};
-
-struct X509_sig_st {
-  X509_ALGOR *algor;
-  ASN1_OCTET_STRING *digest;
-} /* X509_SIG */;
-
-struct X509_name_entry_st {
-  ASN1_OBJECT *object;
-  ASN1_STRING *value;
-  int set;
-  int size;  // temp variable
-} /* X509_NAME_ENTRY */;
-
 DEFINE_STACK_OF(X509_NAME_ENTRY)
-DECLARE_ASN1_SET_OF(X509_NAME_ENTRY)
-
-// we always keep X509_NAMEs in 2 forms.
-struct X509_name_st {
-  STACK_OF(X509_NAME_ENTRY) *entries;
-  int modified;  // true if 'bytes' needs to be built
-  BUF_MEM *bytes;
-  // unsigned long hash; Keep the hash around for lookups
-  unsigned char *canon_enc;
-  int canon_enclen;
-} /* X509_NAME */;
 
 DEFINE_STACK_OF(X509_NAME)
 
-struct X509_extension_st {
-  ASN1_OBJECT *object;
-  ASN1_BOOLEAN critical;
-  ASN1_OCTET_STRING *value;
-} /* X509_EXTENSION */;
-
 typedef STACK_OF(X509_EXTENSION) X509_EXTENSIONS;
 
 DEFINE_STACK_OF(X509_EXTENSION)
-DECLARE_ASN1_SET_OF(X509_EXTENSION)
-
-// a sequence of these are used
-struct x509_attributes_st {
-  ASN1_OBJECT *object;
-  int single;  // 0 for a set, 1 for a single item (which is wrong)
-  union {
-    char *ptr;
-    /* 0 */ STACK_OF(ASN1_TYPE) *set;
-    /* 1 */ ASN1_TYPE *single;
-  } value;
-} /* X509_ATTRIBUTE */;
 
 DEFINE_STACK_OF(X509_ATTRIBUTE)
-DECLARE_ASN1_SET_OF(X509_ATTRIBUTE)
-
-
-struct X509_req_info_st {
-  ASN1_ENCODING enc;
-  ASN1_INTEGER *version;
-  X509_NAME *subject;
-  X509_PUBKEY *pubkey;
-  //  d=2 hl=2 l=  0 cons: cont: 00
-  STACK_OF(X509_ATTRIBUTE) *attributes;  // [ 0 ]
-} /* X509_REQ_INFO */;
-
-struct X509_req_st {
-  X509_REQ_INFO *req_info;
-  X509_ALGOR *sig_alg;
-  ASN1_BIT_STRING *signature;
-  CRYPTO_refcount_t references;
-} /* X509_REQ */;
-
-struct x509_cinf_st {
-  ASN1_INTEGER *version;  // [ 0 ] default of v1
-  ASN1_INTEGER *serialNumber;
-  X509_ALGOR *signature;
-  X509_NAME *issuer;
-  X509_VAL *validity;
-  X509_NAME *subject;
-  X509_PUBKEY *key;
-  ASN1_BIT_STRING *issuerUID;            // [ 1 ] optional in v2
-  ASN1_BIT_STRING *subjectUID;           // [ 2 ] optional in v2
-  STACK_OF(X509_EXTENSION) *extensions;  // [ 3 ] optional in v3
-  ASN1_ENCODING enc;
-} /* X509_CINF */;
 
 // This stuff is certificate "auxiliary info"
 // it contains details which are useful in certificate
 // stores and databases. When used this is tagged onto
 // the end of the certificate itself
 
-struct x509_cert_aux_st {
-  STACK_OF(ASN1_OBJECT) *trust;   // trusted uses
-  STACK_OF(ASN1_OBJECT) *reject;  // rejected uses
-  ASN1_UTF8STRING *alias;         // "friendly name"
-  ASN1_OCTET_STRING *keyid;       // key id of private key
-  STACK_OF(X509_ALGOR) *other;    // other unspecified info
-} /* X509_CERT_AUX */;
-
 DECLARE_STACK_OF(DIST_POINT)
 DECLARE_STACK_OF(GENERAL_NAME)
 
-struct x509_st {
-  X509_CINF *cert_info;
-  X509_ALGOR *sig_alg;
-  ASN1_BIT_STRING *signature;
-  CRYPTO_refcount_t references;
-  CRYPTO_EX_DATA ex_data;
-  // These contain copies of various extension values
-  long ex_pathlen;
-  long ex_pcpathlen;
-  unsigned long ex_flags;
-  unsigned long ex_kusage;
-  unsigned long ex_xkusage;
-  unsigned long ex_nscert;
-  ASN1_OCTET_STRING *skid;
-  AUTHORITY_KEYID *akid;
-  X509_POLICY_CACHE *policy_cache;
-  STACK_OF(DIST_POINT) *crldp;
-  STACK_OF(GENERAL_NAME) *altname;
-  NAME_CONSTRAINTS *nc;
-  unsigned char sha1_hash[SHA_DIGEST_LENGTH];
-  X509_CERT_AUX *aux;
-  CRYPTO_BUFFER *buf;
-  CRYPTO_MUTEX lock;
-} /* X509 */;
-
 DEFINE_STACK_OF(X509)
-DECLARE_ASN1_SET_OF(X509)
 
 // This is used for a table of trust checking functions
 
@@ -318,7 +206,7 @@
 #define XN_FLAG_SEP_MASK (0xf << 16)
 
 #define XN_FLAG_COMPAT 0  // Traditional SSLeay: use old X509_NAME_print
-#define XN_FLAG_SEP_COMMA_PLUS (1 << 16)  // RFC2253 ,+
+#define XN_FLAG_SEP_COMMA_PLUS (1 << 16)  // RFC 2253 ,+
 #define XN_FLAG_SEP_CPLUS_SPC (2 << 16)   // ,+ spaced: more readable
 #define XN_FLAG_SEP_SPLUS_SPC (3 << 16)   // ;+ spaced
 #define XN_FLAG_SEP_MULTILINE (4 << 16)   // One line per field
@@ -337,13 +225,13 @@
 #define XN_FLAG_SPC_EQ (1 << 23)  // Put spaces round '='
 
 // This determines if we dump fields we don't recognise:
-// RFC2253 requires this.
+// RFC 2253 requires this.
 
 #define XN_FLAG_DUMP_UNKNOWN_FIELDS (1 << 24)
 
 #define XN_FLAG_FN_ALIGN (1 << 25)  // Align field names to 20 characters
 
-// Complete set of RFC2253 flags
+// Complete set of RFC 2253 flags
 
 #define XN_FLAG_RFC2253                                             \
   (ASN1_STRFLGS_RFC2253 | XN_FLAG_SEP_COMMA_PLUS | XN_FLAG_DN_REV | \
@@ -373,45 +261,10 @@
 };
 
 DEFINE_STACK_OF(X509_REVOKED)
-DECLARE_ASN1_SET_OF(X509_REVOKED)
-
-struct X509_crl_info_st {
-  ASN1_INTEGER *version;
-  X509_ALGOR *sig_alg;
-  X509_NAME *issuer;
-  ASN1_TIME *lastUpdate;
-  ASN1_TIME *nextUpdate;
-  STACK_OF(X509_REVOKED) *revoked;
-  STACK_OF(X509_EXTENSION) /* [0] */ *extensions;
-  ASN1_ENCODING enc;
-} /* X509_CRL_INFO */;
 
 DECLARE_STACK_OF(GENERAL_NAMES)
 
-struct X509_crl_st {
-  // actual signature
-  X509_CRL_INFO *crl;
-  X509_ALGOR *sig_alg;
-  ASN1_BIT_STRING *signature;
-  CRYPTO_refcount_t references;
-  int flags;
-  // Copies of various extensions
-  AUTHORITY_KEYID *akid;
-  ISSUING_DIST_POINT *idp;
-  // Convenient breakdown of IDP
-  int idp_flags;
-  int idp_reasons;
-  // CRL and base CRL numbers for delta processing
-  ASN1_INTEGER *crl_number;
-  ASN1_INTEGER *base_crl_number;
-  unsigned char sha1_hash[SHA_DIGEST_LENGTH];
-  STACK_OF(GENERAL_NAMES) *issuers;
-  const X509_CRL_METHOD *meth;
-  void *meth_data;
-} /* X509_CRL */;
-
 DEFINE_STACK_OF(X509_CRL)
-DECLARE_ASN1_SET_OF(X509_CRL)
 
 struct private_key_st {
   int version;
@@ -475,16 +328,33 @@
 // it is safe to call mutating functions is a little tricky due to various
 // internal caches.
 
-// X509_get_version returns the numerical value of |x509|'s version. That is,
-// it returns zero for X.509v1, one for X.509v2, and two for X.509v3. Unknown
-// versions are rejected by the parser, but a manually-created |X509| object may
-// encode invalid versions. In that case, the function will return the invalid
-// version, or -1 on overflow.
+// X509_VERSION_* are X.509 version numbers. Note the numerical values of all
+// defined X.509 versions are one less than the named version.
+#define X509_VERSION_1 0
+#define X509_VERSION_2 1
+#define X509_VERSION_3 2
+
+// X509_get_version returns the numerical value of |x509|'s version. Callers may
+// compare the result to the |X509_VERSION_*| constants. Unknown versions are
+// rejected by the parser, but a manually-created |X509| object may encode
+// invalid versions. In that case, the function will return the invalid version,
+// or -1 on overflow.
 OPENSSL_EXPORT long X509_get_version(const X509 *x509);
 
+// X509_set_version sets |x509|'s version to |version|, which should be one of
+// the |X509V_VERSION_*| constants. It returns one on success and zero on error.
+//
+// If unsure, use |X509_VERSION_3|.
+OPENSSL_EXPORT int X509_set_version(X509 *x509, long version);
+
 // X509_get0_serialNumber returns |x509|'s serial number.
 OPENSSL_EXPORT const ASN1_INTEGER *X509_get0_serialNumber(const X509 *x509);
 
+// X509_set_serialNumber sets |x509|'s serial number to |serial|. It returns one
+// on success and zero on error.
+OPENSSL_EXPORT int X509_set_serialNumber(X509 *x509,
+                                         const ASN1_INTEGER *serial);
+
 // X509_get0_notBefore returns |x509|'s notBefore time.
 OPENSSL_EXPORT const ASN1_TIME *X509_get0_notBefore(const X509 *x509);
 
@@ -523,26 +393,22 @@
 // instead.
 OPENSSL_EXPORT int X509_set_notAfter(X509 *x509, const ASN1_TIME *tm);
 
-// X509_get0_uids sets |*out_issuer_uid| and |*out_subject_uid| to non-owning
-// pointers to the issuerUID and subjectUID fields, respectively, of |x509|.
-// Either output pointer may be NULL to skip the field.
+// X509_get0_uids sets |*out_issuer_uid| to a non-owning pointer to the
+// issuerUID field of |x509|, or NULL if |x509| has no issuerUID. It similarly
+// outputs |x509|'s subjectUID field to |*out_subject_uid|.
+//
+// Callers may pass NULL to either |out_issuer_uid| or |out_subject_uid| to
+// ignore the corresponding field.
 OPENSSL_EXPORT void X509_get0_uids(const X509 *x509,
                                    const ASN1_BIT_STRING **out_issuer_uid,
                                    const ASN1_BIT_STRING **out_subject_uid);
 
-// X509_get_cert_info returns |x509|'s TBSCertificate structure. Note this
-// function is not const-correct for legacy reasons.
-//
-// This function is deprecated and may be removed in the future. It is not
-// present in OpenSSL and constrains some improvements to the library.
-OPENSSL_EXPORT X509_CINF *X509_get_cert_info(const X509 *x509);
-
 // X509_extract_key is a legacy alias to |X509_get_pubkey|. Use
 // |X509_get_pubkey| instead.
 #define X509_extract_key(x) X509_get_pubkey(x)
 
 // X509_get_pathlen returns path length constraint from the basic constraints
-// extension in |x509|. (See RFC5280, section 4.2.1.9.) It returns -1 if the
+// extension in |x509|. (See RFC 5280, section 4.2.1.9.) It returns -1 if the
 // constraint is not present, or if some extension in |x509| was invalid.
 //
 // Note that decoding an |X509| object will not check for invalid extensions. To
@@ -550,9 +416,15 @@
 // |EXFLAG_INVALID| bit.
 OPENSSL_EXPORT long X509_get_pathlen(X509 *x509);
 
-// X509_REQ_get_version returns the numerical value of |req|'s version. That is,
-// it returns zero for a v1 request. If |req| is invalid, it may return another
-// value, or -1 on overflow.
+// X509_REQ_VERSION_1 is the version constant for |X509_REQ| objects. Note no
+// other versions are defined.
+#define X509_REQ_VERSION_1 0
+
+// X509_REQ_get_version returns the numerical value of |req|'s version. This
+// will be |X509_REQ_VERSION_1| for valid certificate requests. If |req| is
+// invalid, it may return another value, or -1 on overflow.
+//
+// TODO(davidben): Enforce the version number in the parser.
 OPENSSL_EXPORT long X509_REQ_get_version(const X509_REQ *req);
 
 // X509_REQ_get_subject_name returns |req|'s subject name. Note this function is
@@ -565,9 +437,14 @@
 // X509_name_cmp is a legacy alias for |X509_NAME_cmp|.
 #define X509_name_cmp(a, b) X509_NAME_cmp((a), (b))
 
-// X509_REQ_get_version returns the numerical value of |crl|'s version. That is,
-// it returns zero for a v1 CRL and one for a v2 CRL. If |crl| is invalid, it
-// may return another value, or -1 on overflow.
+#define X509_CRL_VERSION_1 0
+#define X509_CRL_VERSION_2 1
+
+// X509_CRL_get_version returns the numerical value of |crl|'s version. Callers
+// may compare the result to |X509_CRL_VERSION_*| constants. If |crl| is
+// invalid, it may return another value, or -1 on overflow.
+//
+// TODO(davidben): Enforce the version number in the parser.
 OPENSSL_EXPORT long X509_CRL_get_version(const X509_CRL *crl);
 
 // X509_CRL_get0_lastUpdate returns |crl|'s lastUpdate time.
@@ -602,33 +479,19 @@
 // const-correct for legacy reasons.
 OPENSSL_EXPORT X509_NAME *X509_CRL_get_issuer(const X509_CRL *crl);
 
-// X509_CRL_get_REVOKED returns the list of revoked certificates in |crl|.
+// X509_CRL_get_REVOKED returns the list of revoked certificates in |crl|, or
+// NULL if |crl| omits it.
 //
 // TOOD(davidben): This function was originally a macro, without clear const
 // semantics. It should take a const input and give const output, but the latter
 // would break existing callers. For now, we match upstream.
 OPENSSL_EXPORT STACK_OF(X509_REVOKED) *X509_CRL_get_REVOKED(X509_CRL *crl);
 
-// X509_CRL_get0_extensions returns |crl|'s extension list.
+// X509_CRL_get0_extensions returns |crl|'s extension list, or NULL if |crl|
+// omits it.
 OPENSSL_EXPORT const STACK_OF(X509_EXTENSION) *X509_CRL_get0_extensions(
     const X509_CRL *crl);
 
-// X509_CINF_set_modified marks |cinf| as modified so that changes will be
-// reflected in serializing the structure.
-//
-// This function is deprecated and may be removed in the future. It is not
-// present in OpenSSL and constrains some improvements to the library.
-OPENSSL_EXPORT void X509_CINF_set_modified(X509_CINF *cinf);
-
-// X509_CINF_get_signature returns the signature algorithm in |cinf|. Note this
-// isn't the signature itself, but the extra copy of the signature algorithm
-// in the TBSCertificate.
-//
-// This function is deprecated and may be removed in the future. It is not
-// present in OpenSSL and constrains some improvements to the library. Use
-// |X509_get0_tbs_sigalg| instead.
-OPENSSL_EXPORT const X509_ALGOR *X509_CINF_get_signature(const X509_CINF *cinf);
-
 // X509_SIG_get0 sets |*out_alg| and |*out_digest| to non-owning pointers to
 // |sig|'s algorithm and digest fields, respectively. Either |out_alg| and
 // |out_digest| may be NULL to skip those fields.
@@ -813,7 +676,6 @@
 // copying parts of it as a normal |d2i_X509| call would do.
 OPENSSL_EXPORT X509 *X509_parse_from_buffer(CRYPTO_BUFFER *buf);
 
-#ifndef OPENSSL_NO_FP_API
 OPENSSL_EXPORT X509 *d2i_X509_fp(FILE *fp, X509 **x509);
 OPENSSL_EXPORT int i2d_X509_fp(FILE *fp, X509 *x509);
 OPENSSL_EXPORT X509_CRL *d2i_X509_CRL_fp(FILE *fp, X509_CRL **crl);
@@ -847,7 +709,6 @@
 OPENSSL_EXPORT EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a);
 OPENSSL_EXPORT int i2d_PUBKEY_fp(FILE *fp, EVP_PKEY *pkey);
 OPENSSL_EXPORT EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a);
-#endif
 
 OPENSSL_EXPORT X509 *d2i_X509_bio(BIO *bp, X509 **x509);
 OPENSSL_EXPORT int i2d_X509_bio(BIO *bp, X509 *x509);
@@ -892,12 +753,54 @@
 OPENSSL_EXPORT X509_REVOKED *X509_REVOKED_dup(X509_REVOKED *rev);
 OPENSSL_EXPORT X509_REQ *X509_REQ_dup(X509_REQ *req);
 OPENSSL_EXPORT X509_ALGOR *X509_ALGOR_dup(X509_ALGOR *xn);
-OPENSSL_EXPORT int X509_ALGOR_set0(X509_ALGOR *alg, const ASN1_OBJECT *aobj,
-                                   int ptype, void *pval);
-OPENSSL_EXPORT void X509_ALGOR_get0(const ASN1_OBJECT **paobj, int *pptype,
-                                    const void **ppval,
-                                    const X509_ALGOR *algor);
+
+// X509_ALGOR_set0 sets |alg| to an AlgorithmIdentifier with algorithm |obj| and
+// parameter determined by |param_type| and |param_value|. It returns one on
+// success and zero on error. This function takes ownership of |obj| and
+// |param_value| on success.
+//
+// If |param_type| is |V_ASN1_UNDEF|, the parameter is omitted. If |param_type|
+// is zero, the parameter is left unchanged. Otherwise, |param_type| and
+// |param_value| are interpreted as in |ASN1_TYPE_set|.
+//
+// Note omitting the parameter (|V_ASN1_UNDEF|) and encoding an explicit NULL
+// value (|V_ASN1_NULL|) are different. Some algorithms require one and some the
+// other. Consult the relevant specification before calling this function. The
+// correct parameter for an RSASSA-PKCS1-v1_5 signature is |V_ASN1_NULL|. The
+// correct one for an ECDSA or Ed25519 signature is |V_ASN1_UNDEF|.
+OPENSSL_EXPORT int X509_ALGOR_set0(X509_ALGOR *alg, ASN1_OBJECT *obj,
+                                   int param_type, void *param_value);
+
+// X509_ALGOR_get0 sets |*out_obj| to the |alg|'s algorithm. If |alg|'s
+// parameter is omitted, it sets |*out_param_type| and |*out_param_value| to
+// |V_ASN1_UNDEF| and NULL. Otherwise, it sets |*out_param_type| and
+// |*out_param_value| to the parameter, using the same representation as
+// |ASN1_TYPE_set0|. See |ASN1_TYPE_set0| and |ASN1_TYPE| for details.
+//
+// Callers that require the parameter in serialized form should, after checking
+// for |V_ASN1_UNDEF|, use |ASN1_TYPE_set1| and |d2i_ASN1_TYPE|, rather than
+// inspecting |*out_param_value|.
+//
+// Each of |out_obj|, |out_param_type|, and |out_param_value| may be NULL to
+// ignore the output. If |out_param_type| is NULL, |out_param_value| is ignored.
+//
+// WARNING: If |*out_param_type| is set to |V_ASN1_UNDEF|, OpenSSL and older
+// revisions of BoringSSL leave |*out_param_value| unset rather than setting it
+// to NULL. Callers that support both OpenSSL and BoringSSL should not assume
+// |*out_param_value| is uniformly initialized.
+OPENSSL_EXPORT void X509_ALGOR_get0(const ASN1_OBJECT **out_obj,
+                                    int *out_param_type,
+                                    const void **out_param_value,
+                                    const X509_ALGOR *alg);
+
+// X509_ALGOR_set_md sets |alg| to the hash function |md|. Note this
+// AlgorithmIdentifier represents the hash function itself, not a signature
+// algorithm that uses |md|.
 OPENSSL_EXPORT void X509_ALGOR_set_md(X509_ALGOR *alg, const EVP_MD *md);
+
+// X509_ALGOR_cmp returns zero if |a| and |b| are equal, and some non-zero value
+// otherwise. Note this function can only be used for equality checks, not an
+// ordering.
 OPENSSL_EXPORT int X509_ALGOR_cmp(const X509_ALGOR *a, const X509_ALGOR *b);
 
 OPENSSL_EXPORT X509_NAME *X509_NAME_dup(X509_NAME *xn);
@@ -907,12 +810,30 @@
 OPENSSL_EXPORT int X509_NAME_get0_der(X509_NAME *nm, const unsigned char **pder,
                                       size_t *pderlen);
 
+// X509_cmp_time compares |s| against |*t|. On success, it returns a negative
+// number if |s| <= |*t| and a positive number if |s| > |*t|. On error, it
+// returns zero. If |t| is NULL, it uses the current time instead of |*t|.
+//
+// WARNING: Unlike most comparison functions, this function returns zero on
+// error, not equality.
 OPENSSL_EXPORT int X509_cmp_time(const ASN1_TIME *s, time_t *t);
+
+// X509_cmp_current_time behaves like |X509_cmp_time| but compares |s| against
+// the current time.
 OPENSSL_EXPORT int X509_cmp_current_time(const ASN1_TIME *s);
-OPENSSL_EXPORT ASN1_TIME *X509_time_adj(ASN1_TIME *s, long adj, time_t *t);
+
+// X509_time_adj calls |X509_time_adj_ex| with |offset_day| equal to zero.
+OPENSSL_EXPORT ASN1_TIME *X509_time_adj(ASN1_TIME *s, long offset_sec,
+                                        time_t *t);
+
+// X509_time_adj_ex behaves like |ASN1_TIME_adj|, but adds an offset to |*t|. If
+// |t| is NULL, it uses the current time instead of |*t|.
 OPENSSL_EXPORT ASN1_TIME *X509_time_adj_ex(ASN1_TIME *s, int offset_day,
                                            long offset_sec, time_t *t);
-OPENSSL_EXPORT ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long adj);
+
+// X509_gmtime_adj behaves like |X509_time_adj_ex| but adds |offset_sec| to the
+// current time.
+OPENSSL_EXPORT ASN1_TIME *X509_gmtime_adj(ASN1_TIME *s, long offset_sec);
 
 OPENSSL_EXPORT const char *X509_get_default_cert_area(void);
 OPENSSL_EXPORT const char *X509_get_default_cert_dir(void);
@@ -923,22 +844,33 @@
 
 OPENSSL_EXPORT X509_REQ *X509_to_X509_REQ(X509 *x, EVP_PKEY *pkey,
                                           const EVP_MD *md);
-OPENSSL_EXPORT X509 *X509_REQ_to_X509(X509_REQ *r, int days, EVP_PKEY *pkey);
 
 DECLARE_ASN1_ENCODE_FUNCTIONS(X509_ALGORS, X509_ALGORS, X509_ALGORS)
 DECLARE_ASN1_FUNCTIONS(X509_VAL)
 
 DECLARE_ASN1_FUNCTIONS(X509_PUBKEY)
 
+// X509_PUBKEY_set serializes |pkey| into a newly-allocated |X509_PUBKEY|
+// structure. On success, it frees |*x|, sets |*x| to the new object, and
+// returns one. Otherwise, it returns zero.
 OPENSSL_EXPORT int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey);
+
+// X509_PUBKEY_get decodes the public key in |key| and returns an |EVP_PKEY| on
+// success, or NULL on error. The caller must release the result with
+// |EVP_PKEY_free| when done. The |EVP_PKEY| is cached in |key|, so callers must
+// not mutate the result.
 OPENSSL_EXPORT EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key);
 
 DECLARE_ASN1_FUNCTIONS(X509_SIG)
-DECLARE_ASN1_FUNCTIONS(X509_REQ_INFO)
 DECLARE_ASN1_FUNCTIONS(X509_REQ)
 
 DECLARE_ASN1_FUNCTIONS(X509_ATTRIBUTE)
-OPENSSL_EXPORT X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int atrtype,
+
+// X509_ATTRIBUTE_create returns a newly-allocated |X509_ATTRIBUTE|, or NULL on
+// error. The attribute has type |nid| and contains a single value determined by
+// |attrtype| and |value|, which are interpreted as in |ASN1_TYPE_set|. Note
+// this function takes ownership of |value|.
+OPENSSL_EXPORT X509_ATTRIBUTE *X509_ATTRIBUTE_create(int nid, int attrtype,
                                                      void *value);
 
 DECLARE_ASN1_FUNCTIONS(X509_EXTENSION)
@@ -948,15 +880,15 @@
 
 DECLARE_ASN1_FUNCTIONS(X509_NAME)
 
+// X509_NAME_set makes a copy of |name|. On success, it frees |*xn|, sets |*xn|
+// to the copy, and returns one. Otherwise, it returns zero.
 OPENSSL_EXPORT int X509_NAME_set(X509_NAME **xn, X509_NAME *name);
 
-DECLARE_ASN1_FUNCTIONS(X509_CINF)
-
 DECLARE_ASN1_FUNCTIONS(X509)
 DECLARE_ASN1_FUNCTIONS(X509_CERT_AUX)
 
-// X509_up_ref adds one to the reference count of |x| and returns one.
-OPENSSL_EXPORT int X509_up_ref(X509 *x);
+// X509_up_ref adds one to the reference count of |x509| and returns one.
+OPENSSL_EXPORT int X509_up_ref(X509 *x509);
 
 OPENSSL_EXPORT int X509_get_ex_new_index(long argl, void *argp,
                                          CRYPTO_EX_unused *unused,
@@ -1014,9 +946,22 @@
 OPENSSL_EXPORT int X509_set1_signature_value(X509 *x509, const uint8_t *sig,
                                              size_t sig_len);
 
-OPENSSL_EXPORT void X509_get0_signature(const ASN1_BIT_STRING **psig,
-                                        const X509_ALGOR **palg, const X509 *x);
-OPENSSL_EXPORT int X509_get_signature_nid(const X509 *x);
+// X509_get0_signature sets |*out_sig| and |*out_alg| to the signature and
+// signature algorithm of |x509|, respectively. Either output pointer may be
+// NULL to ignore the value.
+//
+// This function outputs the outer signature algorithm. For the one in the
+// TBSCertificate, see |X509_get0_tbs_sigalg|. Certificates with mismatched
+// signature algorithms will successfully parse, but they will be rejected when
+// verifying.
+OPENSSL_EXPORT void X509_get0_signature(const ASN1_BIT_STRING **out_sig,
+                                        const X509_ALGOR **out_alg,
+                                        const X509 *x509);
+
+// X509_get_signature_nid returns the NID corresponding to |x509|'s signature
+// algorithm, or |NID_undef| if the signature algorithm does not correspond to
+// a known NID.
+OPENSSL_EXPORT int X509_get_signature_nid(const X509 *x509);
 
 OPENSSL_EXPORT int X509_alias_set1(X509 *x, const unsigned char *name, int len);
 OPENSSL_EXPORT int X509_keyid_set1(X509 *x, const unsigned char *id, int len);
@@ -1032,7 +977,6 @@
 OPENSSL_EXPORT void X509_reject_clear(X509 *x);
 
 DECLARE_ASN1_FUNCTIONS(X509_REVOKED)
-DECLARE_ASN1_FUNCTIONS(X509_CRL_INFO)
 DECLARE_ASN1_FUNCTIONS(X509_CRL)
 
 OPENSSL_EXPORT int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev);
@@ -1058,9 +1002,10 @@
                                     void *data, unsigned char *md,
                                     unsigned int *len);
 
-OPENSSL_EXPORT int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *algor1,
-                                    ASN1_BIT_STRING *signature, void *data,
-                                    EVP_PKEY *pkey);
+OPENSSL_EXPORT int ASN1_item_verify(const ASN1_ITEM *it,
+                                    const X509_ALGOR *algor1,
+                                    const ASN1_BIT_STRING *signature,
+                                    void *data, EVP_PKEY *pkey);
 
 OPENSSL_EXPORT int ASN1_item_sign(const ASN1_ITEM *it, X509_ALGOR *algor1,
                                   X509_ALGOR *algor2,
@@ -1071,66 +1016,220 @@
                                       ASN1_BIT_STRING *signature, void *asn,
                                       EVP_MD_CTX *ctx);
 
-OPENSSL_EXPORT int X509_set_version(X509 *x, long version);
-OPENSSL_EXPORT int X509_set_serialNumber(X509 *x, ASN1_INTEGER *serial);
-OPENSSL_EXPORT ASN1_INTEGER *X509_get_serialNumber(X509 *x);
-OPENSSL_EXPORT int X509_set_issuer_name(X509 *x, X509_NAME *name);
-OPENSSL_EXPORT X509_NAME *X509_get_issuer_name(const X509 *a);
-OPENSSL_EXPORT int X509_set_subject_name(X509 *x, X509_NAME *name);
-OPENSSL_EXPORT X509_NAME *X509_get_subject_name(const X509 *a);
-OPENSSL_EXPORT int X509_set_pubkey(X509 *x, EVP_PKEY *pkey);
-OPENSSL_EXPORT EVP_PKEY *X509_get_pubkey(X509 *x);
-OPENSSL_EXPORT ASN1_BIT_STRING *X509_get0_pubkey_bitstr(const X509 *x);
-OPENSSL_EXPORT const STACK_OF(X509_EXTENSION) *X509_get0_extensions(
-    const X509 *x);
-OPENSSL_EXPORT const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x);
+// X509_get_serialNumber returns a mutable pointer to |x509|'s serial number.
+// Prefer |X509_get0_serialNumber|.
+OPENSSL_EXPORT ASN1_INTEGER *X509_get_serialNumber(X509 *x509);
 
-OPENSSL_EXPORT int X509_REQ_set_version(X509_REQ *x, long version);
+// X509_set_issuer_name sets |x509|'s issuer to a copy of |name|. It returns one
+// on success and zero on error.
+OPENSSL_EXPORT int X509_set_issuer_name(X509 *x509, X509_NAME *name);
+
+// X509_get_issuer_name returns |x509|'s issuer.
+OPENSSL_EXPORT X509_NAME *X509_get_issuer_name(const X509 *x509);
+
+// X509_set_subject_name sets |x509|'s subject to a copy of |name|. It returns
+// one on success and zero on error.
+OPENSSL_EXPORT int X509_set_subject_name(X509 *x509, X509_NAME *name);
+
+// X509_get_issuer_name returns |x509|'s subject.
+OPENSSL_EXPORT X509_NAME *X509_get_subject_name(const X509 *x509);
+
+// X509_set_pubkey sets |x509|'s public key to |pkey|. It returns one on success
+// and zero on error. This function does not take ownership of |pkey| and
+// internally copies and updates reference counts as needed.
+OPENSSL_EXPORT int X509_set_pubkey(X509 *x509, EVP_PKEY *pkey);
+
+// X509_get_pubkey returns |x509|'s public key as an |EVP_PKEY|, or NULL if the
+// public key was unsupported or could not be decoded. This function returns a
+// reference to the |EVP_PKEY|. The caller must release the result with
+// |EVP_PKEY_free| when done.
+OPENSSL_EXPORT EVP_PKEY *X509_get_pubkey(X509 *x509);
+
+// X509_get0_pubkey_bitstr returns the BIT STRING portion of |x509|'s public
+// key. Note this does not contain the AlgorithmIdentifier portion.
+//
+// WARNING: This function returns a non-const pointer for OpenSSL compatibility,
+// but the caller must not modify the resulting object. Doing so will break
+// internal invariants in |x509|.
+OPENSSL_EXPORT ASN1_BIT_STRING *X509_get0_pubkey_bitstr(const X509 *x509);
+
+// X509_get0_extensions returns |x509|'s extension list, or NULL if |x509| omits
+// it.
+OPENSSL_EXPORT const STACK_OF(X509_EXTENSION) *X509_get0_extensions(
+    const X509 *x509);
+
+// X509_get0_tbs_sigalg returns the signature algorithm in |x509|'s
+// TBSCertificate. For the outer signature algorithm, see |X509_get0_signature|.
+//
+// Certificates with mismatched signature algorithms will successfully parse,
+// but they will be rejected when verifying.
+OPENSSL_EXPORT const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x509);
+
+// X509_REQ_set_version sets |req|'s version to |version|, which should be
+// |X509_REQ_VERSION_1|. It returns one on success and zero on error.
+//
+// Note no versions other than |X509_REQ_VERSION_1| are defined for CSRs.
+OPENSSL_EXPORT int X509_REQ_set_version(X509_REQ *req, long version);
+
+// X509_REQ_set_subject_name sets |req|'s subject to a copy of |name|. It
+// returns one on success and zero on error.
 OPENSSL_EXPORT int X509_REQ_set_subject_name(X509_REQ *req, X509_NAME *name);
+
+// X509_REQ_get0_signature sets |*out_sig| and |*out_alg| to the signature and
+// signature algorithm of |req|, respectively. Either output pointer may be NULL
+// to ignore the value.
 OPENSSL_EXPORT void X509_REQ_get0_signature(const X509_REQ *req,
-                                            const ASN1_BIT_STRING **psig,
-                                            const X509_ALGOR **palg);
+                                            const ASN1_BIT_STRING **out_sig,
+                                            const X509_ALGOR **out_alg);
+
+// X509_REQ_get_signature_nid returns the NID corresponding to |req|'s signature
+// algorithm, or |NID_undef| if the signature algorithm does not correspond to
+// a known NID.
 OPENSSL_EXPORT int X509_REQ_get_signature_nid(const X509_REQ *req);
-OPENSSL_EXPORT int i2d_re_X509_REQ_tbs(X509_REQ *req, unsigned char **pp);
-OPENSSL_EXPORT int X509_REQ_set_pubkey(X509_REQ *x, EVP_PKEY *pkey);
+
+// i2d_re_X509_REQ_tbs serializes the CertificationRequestInfo (see RFC 2986)
+// portion of |req|. If |outp| is NULL, nothing is written. Otherwise, if
+// |*outp| is not NULL, the result is written to |*outp|, which must have enough
+// space available, and |*outp| is advanced just past the output. If |outp| is
+// non-NULL and |*outp| is NULL, it sets |*outp| to a newly-allocated buffer
+// containing the result. The caller is responsible for releasing the buffer
+// with |OPENSSL_free|. In all cases, this function returns the number of bytes
+// in the result, whether written or not, or a negative value on error.
+//
+// This function re-encodes the CertificationRequestInfo and may not reflect
+// |req|'s original encoding. It may be used to manually generate a signature
+// for a new certificate request.
+OPENSSL_EXPORT int i2d_re_X509_REQ_tbs(X509_REQ *req, uint8_t **outp);
+
+// X509_REQ_set_pubkey sets |req|'s public key to |pkey|. It returns one on
+// success and zero on error. This function does not take ownership of |pkey|
+// and internally copies and updates reference counts as needed.
+OPENSSL_EXPORT int X509_REQ_set_pubkey(X509_REQ *req, EVP_PKEY *pkey);
+
+// X509_REQ_get_pubkey returns |req|'s public key as an |EVP_PKEY|, or NULL if
+// the public key was unsupported or could not be decoded. This function returns
+// a reference to the |EVP_PKEY|. The caller must release the result with
+// |EVP_PKEY_free| when done.
 OPENSSL_EXPORT EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *req);
+
+// X509_REQ_extension_nid returns one if |nid| is a supported CSR attribute type
+// for carrying extensions and zero otherwise. The supported types are
+// |NID_ext_req| (pkcs-9-at-extensionRequest from RFC 2985) and |NID_ms_ext_req|
+// (a Microsoft szOID_CERT_EXTENSIONS variant).
 OPENSSL_EXPORT int X509_REQ_extension_nid(int nid);
-OPENSSL_EXPORT const int *X509_REQ_get_extension_nids(void);
-OPENSSL_EXPORT void X509_REQ_set_extension_nids(const int *nids);
+
+// X509_REQ_get_extensions decodes the list of requested extensions in |req| and
+// returns a newly-allocated |STACK_OF(X509_EXTENSION)| containing the result.
+// It returns NULL on error, or if |req| did not request extensions.
+//
+// This function supports both pkcs-9-at-extensionRequest from RFC 2985 and the
+// Microsoft szOID_CERT_EXTENSIONS variant.
 OPENSSL_EXPORT STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req);
-OPENSSL_EXPORT int X509_REQ_add_extensions_nid(X509_REQ *req,
-                                               STACK_OF(X509_EXTENSION) *exts,
-                                               int nid);
-OPENSSL_EXPORT int X509_REQ_add_extensions(X509_REQ *req,
-                                           STACK_OF(X509_EXTENSION) *exts);
+
+// X509_REQ_add_extensions_nid adds an attribute to |req| of type |nid|, to
+// request the certificate extensions in |exts|. It returns one on success and
+// zero on error. |nid| should be |NID_ext_req| or |NID_ms_ext_req|.
+OPENSSL_EXPORT int X509_REQ_add_extensions_nid(
+    X509_REQ *req, const STACK_OF(X509_EXTENSION) *exts, int nid);
+
+// X509_REQ_add_extensions behaves like |X509_REQ_add_extensions_nid|, using the
+// standard |NID_ext_req| for the attribute type.
+OPENSSL_EXPORT int X509_REQ_add_extensions(
+    X509_REQ *req, const STACK_OF(X509_EXTENSION) *exts);
+
+// X509_REQ_get_attr_count returns the number of attributes in |req|.
 OPENSSL_EXPORT int X509_REQ_get_attr_count(const X509_REQ *req);
+
+// X509_REQ_get_attr_by_NID returns the index of the attribute in |req| of type
+// |nid|, or a negative number if not found. If found, callers can use
+// |X509_REQ_get_attr| to look up the attribute by index.
+//
+// If |lastpos| is non-negative, it begins searching at |lastpos| + 1. Callers
+// can thus loop over all matching attributes by first passing -1 and then
+// passing the previously-returned value until no match is returned.
 OPENSSL_EXPORT int X509_REQ_get_attr_by_NID(const X509_REQ *req, int nid,
                                             int lastpos);
+
+// X509_REQ_get_attr_by_OBJ behaves like |X509_REQ_get_attr_by_NID| but looks
+// for attributes of type |obj|.
 OPENSSL_EXPORT int X509_REQ_get_attr_by_OBJ(const X509_REQ *req,
-                                            ASN1_OBJECT *obj, int lastpos);
+                                            const ASN1_OBJECT *obj,
+                                            int lastpos);
+
+// X509_REQ_get_attr returns the attribute at index |loc| in |req|, or NULL if
+// out of bounds.
 OPENSSL_EXPORT X509_ATTRIBUTE *X509_REQ_get_attr(const X509_REQ *req, int loc);
+
+// X509_REQ_delete_attr removes the attribute at index |loc| in |req|. It
+// returns the removed attribute to the caller, or NULL if |loc| was out of
+// bounds. If non-NULL, the caller must release the result with
+// |X509_ATTRIBUTE_free| when done. It is also safe, but not necessary, to call
+// |X509_ATTRIBUTE_free| if the result is NULL.
 OPENSSL_EXPORT X509_ATTRIBUTE *X509_REQ_delete_attr(X509_REQ *req, int loc);
+
+// X509_REQ_add1_attr appends a copy of |attr| to |req|'s list of attributes. It
+// returns one on success and zero on error.
+//
+// TODO(https://crbug.com/boringssl/407): |attr| should be const.
 OPENSSL_EXPORT int X509_REQ_add1_attr(X509_REQ *req, X509_ATTRIBUTE *attr);
+
+// X509_REQ_add1_attr_by_OBJ appends a new attribute to |req| with type |obj|.
+// It returns one on success and zero on error. The value is determined by
+// |X509_ATTRIBUTE_set1_data|.
+//
+// WARNING: The interpretation of |attrtype|, |data|, and |len| is complex and
+// error-prone. See |X509_ATTRIBUTE_set1_data| for details.
 OPENSSL_EXPORT int X509_REQ_add1_attr_by_OBJ(X509_REQ *req,
-                                             const ASN1_OBJECT *obj, int type,
-                                             const unsigned char *bytes,
-                                             int len);
-OPENSSL_EXPORT int X509_REQ_add1_attr_by_NID(X509_REQ *req, int nid, int type,
-                                             const unsigned char *bytes,
-                                             int len);
-OPENSSL_EXPORT int X509_REQ_add1_attr_by_txt(X509_REQ *req,
-                                             const char *attrname, int type,
-                                             const unsigned char *bytes,
+                                             const ASN1_OBJECT *obj,
+                                             int attrtype,
+                                             const unsigned char *data,
                                              int len);
 
-OPENSSL_EXPORT int X509_CRL_set_version(X509_CRL *x, long version);
-OPENSSL_EXPORT int X509_CRL_set_issuer_name(X509_CRL *x, X509_NAME *name);
+// X509_REQ_add1_attr_by_NID behaves like |X509_REQ_add1_attr_by_OBJ| except the
+// attribute type is determined by |nid|.
+OPENSSL_EXPORT int X509_REQ_add1_attr_by_NID(X509_REQ *req, int nid,
+                                             int attrtype,
+                                             const unsigned char *data,
+                                             int len);
+
+// X509_REQ_add1_attr_by_txt behaves like |X509_REQ_add1_attr_by_OBJ| except the
+// attribute type is determined by calling |OBJ_txt2obj| with |attrname|.
+OPENSSL_EXPORT int X509_REQ_add1_attr_by_txt(X509_REQ *req,
+                                             const char *attrname, int attrtype,
+                                             const unsigned char *data,
+                                             int len);
+
+// X509_CRL_set_version sets |crl|'s version to |version|, which should be one
+// of the |X509_CRL_VERSION_*| constants. It returns one on success and zero on
+// error.
+//
+// If unsure, use |X509_CRL_VERSION_2|. Note that, unlike certificates, CRL
+// versions are only defined up to v2. Callers should not use |X509_VERSION_3|.
+OPENSSL_EXPORT int X509_CRL_set_version(X509_CRL *crl, long version);
+
+// X509_CRL_set_issuer_name sets |crl|'s issuer to a copy of |name|. It returns
+// one on success and zero on error.
+OPENSSL_EXPORT int X509_CRL_set_issuer_name(X509_CRL *crl, X509_NAME *name);
+
 OPENSSL_EXPORT int X509_CRL_sort(X509_CRL *crl);
+
+// X509_CRL_up_ref adds one to the reference count of |crl| and returns one.
 OPENSSL_EXPORT int X509_CRL_up_ref(X509_CRL *crl);
 
+// X509_CRL_get0_signature sets |*out_sig| and |*out_alg| to the signature and
+// signature algorithm of |crl|, respectively. Either output pointer may be NULL
+// to ignore the value.
+//
+// This function outputs the outer signature algorithm, not the one in the
+// TBSCertList. CRLs with mismatched signature algorithms will successfully
+// parse, but they will be rejected when verifying.
 OPENSSL_EXPORT void X509_CRL_get0_signature(const X509_CRL *crl,
-                                            const ASN1_BIT_STRING **psig,
-                                            const X509_ALGOR **palg);
+                                            const ASN1_BIT_STRING **out_sig,
+                                            const X509_ALGOR **out_alg);
+
+// X509_CRL_get_signature_nid returns the NID corresponding to |crl|'s signature
+// algorithm, or |NID_undef| if the signature algorithm does not correspond to
+// a known NID.
 OPENSSL_EXPORT int X509_CRL_get_signature_nid(const X509_CRL *crl);
 
 // i2d_re_X509_CRL_tbs serializes the TBSCertList portion of |crl|. If |outp| is
@@ -1162,6 +1261,25 @@
 // instead.
 OPENSSL_EXPORT int i2d_X509_CRL_tbs(X509_CRL *crl, unsigned char **outp);
 
+// X509_CRL_set1_signature_algo sets |crl|'s signature algorithm to |algo| and
+// returns one on success or zero on error. It updates both the signature field
+// of the TBSCertList structure, and the signatureAlgorithm field of the CRL.
+OPENSSL_EXPORT int X509_CRL_set1_signature_algo(X509_CRL *crl,
+                                                const X509_ALGOR *algo);
+
+// X509_CRL_set1_signature_value sets |crl|'s signature to a copy of the
+// |sig_len| bytes pointed by |sig|. It returns one on success and zero on
+// error.
+//
+// Due to a specification error, X.509 CRLs store signatures in ASN.1 BIT
+// STRINGs, but signature algorithms return byte strings rather than bit
+// strings. This function creates a BIT STRING containing a whole number of
+// bytes, with the bit order matching the DER encoding. This matches the
+// encoding used by all X.509 signature algorithms.
+OPENSSL_EXPORT int X509_CRL_set1_signature_value(X509_CRL *crl,
+                                                 const uint8_t *sig,
+                                                 size_t sig_len);
+
 // X509_REVOKED_get0_serialNumber returns the serial number of the certificate
 // revoked by |revoked|.
 OPENSSL_EXPORT const ASN1_INTEGER *X509_REVOKED_get0_serialNumber(
@@ -1182,7 +1300,8 @@
 OPENSSL_EXPORT int X509_REVOKED_set_revocationDate(X509_REVOKED *revoked,
                                                    const ASN1_TIME *tm);
 
-// X509_REVOKED_get0_extensions returns |r|'s extensions.
+// X509_REVOKED_get0_extensions returns |r|'s extensions list, or NULL if |r|
+// omits it.
 OPENSSL_EXPORT const STACK_OF(X509_EXTENSION) *X509_REVOKED_get0_extensions(
     const X509_REVOKED *r);
 
@@ -1198,10 +1317,14 @@
                                            unsigned long flags);
 OPENSSL_EXPORT int X509_CRL_check_suiteb(X509_CRL *crl, EVP_PKEY *pk,
                                          unsigned long flags);
+
+// X509_chain_up_ref returns a newly-allocated |STACK_OF(X509)| containing a
+// shallow copy of |chain|, or NULL on error. That is, the return value has the
+// same contents as |chain|, and each |X509|'s reference count is incremented by
+// one.
 OPENSSL_EXPORT STACK_OF(X509) *X509_chain_up_ref(STACK_OF(X509) *chain);
 
 OPENSSL_EXPORT int X509_issuer_and_serial_cmp(const X509 *a, const X509 *b);
-OPENSSL_EXPORT unsigned long X509_issuer_and_serial_hash(X509 *a);
 
 OPENSSL_EXPORT int X509_issuer_name_cmp(const X509 *a, const X509 *b);
 OPENSSL_EXPORT unsigned long X509_issuer_name_hash(X509 *a);
@@ -1219,7 +1342,6 @@
 
 OPENSSL_EXPORT int X509_CRL_cmp(const X509_CRL *a, const X509_CRL *b);
 OPENSSL_EXPORT int X509_CRL_match(const X509_CRL *a, const X509_CRL *b);
-#ifndef OPENSSL_NO_FP_API
 OPENSSL_EXPORT int X509_print_ex_fp(FILE *bp, X509 *x, unsigned long nmflag,
                                     unsigned long cflag);
 OPENSSL_EXPORT int X509_print_fp(FILE *bp, X509 *x);
@@ -1227,7 +1349,6 @@
 OPENSSL_EXPORT int X509_REQ_print_fp(FILE *bp, X509_REQ *req);
 OPENSSL_EXPORT int X509_NAME_print_ex_fp(FILE *fp, const X509_NAME *nm,
                                          int indent, unsigned long flags);
-#endif
 
 OPENSSL_EXPORT int X509_NAME_print(BIO *bp, const X509_NAME *name, int obase);
 OPENSSL_EXPORT int X509_NAME_print_ex(BIO *out, const X509_NAME *nm, int indent,
@@ -1292,28 +1413,90 @@
     const X509_NAME_ENTRY *ne);
 OPENSSL_EXPORT ASN1_STRING *X509_NAME_ENTRY_get_data(const X509_NAME_ENTRY *ne);
 
+// X509v3_get_ext_count returns the number of extensions in |x|.
 OPENSSL_EXPORT int X509v3_get_ext_count(const STACK_OF(X509_EXTENSION) *x);
+
+// X509v3_get_ext_by_NID returns the index of the first extension in |x| with
+// type |nid|, or a negative number if not found. If found, callers can use
+// |X509v3_get_ext| to look up the extension by index.
+//
+// If |lastpos| is non-negative, it begins searching at |lastpos| + 1. Callers
+// can thus loop over all matching extensions by first passing -1 and then
+// passing the previously-returned value until no match is returned.
 OPENSSL_EXPORT int X509v3_get_ext_by_NID(const STACK_OF(X509_EXTENSION) *x,
                                          int nid, int lastpos);
+
+// X509v3_get_ext_by_OBJ behaves like |X509v3_get_ext_by_NID| but looks for
+// extensions matching |obj|.
 OPENSSL_EXPORT int X509v3_get_ext_by_OBJ(const STACK_OF(X509_EXTENSION) *x,
                                          const ASN1_OBJECT *obj, int lastpos);
+
+// X509v3_get_ext_by_critical returns the index of the first extension in |x|
+// whose critical bit matches |crit|, or a negative number if no such extension
+// was found.
+//
+// If |lastpos| is non-negative, it begins searching at |lastpos| + 1. Callers
+// can thus loop over all matching extensions by first passing -1 and then
+// passing the previously-returned value until no match is returned.
 OPENSSL_EXPORT int X509v3_get_ext_by_critical(const STACK_OF(X509_EXTENSION) *x,
                                               int crit, int lastpos);
+
+// X509v3_get_ext returns the extension in |x| at index |loc|, or NULL if |loc|
+// is out of bounds.
 OPENSSL_EXPORT X509_EXTENSION *X509v3_get_ext(const STACK_OF(X509_EXTENSION) *x,
                                               int loc);
+
+// X509v3_delete_ext removes the extension in |x| at index |loc| and returns the
+// removed extension, or NULL if |loc| was out of bounds. If an extension was
+// returned, the caller must release it with |X509_EXTENSION_free|.
 OPENSSL_EXPORT X509_EXTENSION *X509v3_delete_ext(STACK_OF(X509_EXTENSION) *x,
                                                  int loc);
+
+// X509v3_add_ext adds a copy of |ex| to the extension list in |*x|. If |*x| is
+// NULL, it allocates a new |STACK_OF(X509_EXTENSION)| to hold the copy and sets
+// |*x| to the new list. It returns |*x| on success and NULL on error. The
+// caller retains ownership of |ex| and can release it independently of |*x|.
+//
+// The new extension is inserted at index |loc|, shifting extensions to the
+// right. If |loc| is -1 or out of bounds, the new extension is appended to the
+// list.
 OPENSSL_EXPORT STACK_OF(X509_EXTENSION) *X509v3_add_ext(
     STACK_OF(X509_EXTENSION) **x, X509_EXTENSION *ex, int loc);
 
+// X509_get_ext_count returns the number of extensions in |x|.
 OPENSSL_EXPORT int X509_get_ext_count(const X509 *x);
+
+// X509_get_ext_by_NID behaves like |X509v3_get_ext_by_NID| but searches for
+// extensions in |x|.
 OPENSSL_EXPORT int X509_get_ext_by_NID(const X509 *x, int nid, int lastpos);
+
+// X509_get_ext_by_OBJ behaves like |X509v3_get_ext_by_OBJ| but searches for
+// extensions in |x|.
 OPENSSL_EXPORT int X509_get_ext_by_OBJ(const X509 *x, const ASN1_OBJECT *obj,
                                        int lastpos);
+
+// X509_get_ext_by_critical behaves like |X509v3_get_ext_by_critical| but
+// searches for extensions in |x|.
 OPENSSL_EXPORT int X509_get_ext_by_critical(const X509 *x, int crit,
                                             int lastpos);
+
+// X509_get_ext returns the extension in |x| at index |loc|, or NULL if |loc| is
+// out of bounds.
 OPENSSL_EXPORT X509_EXTENSION *X509_get_ext(const X509 *x, int loc);
+
+// X509_delete_ext removes the extension in |x| at index |loc| and returns the
+// removed extension, or NULL if |loc| was out of bounds. If non-NULL, the
+// caller must release the result with |X509_EXTENSION_free|. It is also safe,
+// but not necessary, to call |X509_EXTENSION_free| if the result is NULL.
 OPENSSL_EXPORT X509_EXTENSION *X509_delete_ext(X509 *x, int loc);
+
+// X509_add_ext adds a copy of |ex| to |x|. It returns one on success and zero
+// on failure. The caller retains ownership of |ex| and can release it
+// independently of |x|.
+//
+// The new extension is inserted at index |loc|, shifting extensions to the
+// right. If |loc| is -1 or out of bounds, the new extension is appended to the
+// list.
 OPENSSL_EXPORT int X509_add_ext(X509 *x, X509_EXTENSION *ex, int loc);
 
 // X509_get_ext_d2i behaves like |X509V3_get_d2i| but looks for the extension in
@@ -1333,15 +1516,41 @@
 OPENSSL_EXPORT int X509_add1_ext_i2d(X509 *x, int nid, void *value, int crit,
                                      unsigned long flags);
 
+// X509_CRL_get_ext_count returns the number of extensions in |x|.
 OPENSSL_EXPORT int X509_CRL_get_ext_count(const X509_CRL *x);
+
+// X509_CRL_get_ext_by_NID behaves like |X509v3_get_ext_by_NID| but searches for
+// extensions in |x|.
 OPENSSL_EXPORT int X509_CRL_get_ext_by_NID(const X509_CRL *x, int nid,
                                            int lastpos);
+
+// X509_CRL_get_ext_by_OBJ behaves like |X509v3_get_ext_by_OBJ| but searches for
+// extensions in |x|.
 OPENSSL_EXPORT int X509_CRL_get_ext_by_OBJ(const X509_CRL *x,
                                            const ASN1_OBJECT *obj, int lastpos);
+
+// X509_CRL_get_ext_by_critical behaves like |X509v3_get_ext_by_critical| but
+// searches for extensions in |x|.
 OPENSSL_EXPORT int X509_CRL_get_ext_by_critical(const X509_CRL *x, int crit,
                                                 int lastpos);
+
+// X509_CRL_get_ext returns the extension in |x| at index |loc|, or NULL if
+// |loc| is out of bounds.
 OPENSSL_EXPORT X509_EXTENSION *X509_CRL_get_ext(const X509_CRL *x, int loc);
+
+// X509_CRL_delete_ext removes the extension in |x| at index |loc| and returns
+// the removed extension, or NULL if |loc| was out of bounds. If non-NULL, the
+// caller must release the result with |X509_EXTENSION_free|. It is also safe,
+// but not necessary, to call |X509_EXTENSION_free| if the result is NULL.
 OPENSSL_EXPORT X509_EXTENSION *X509_CRL_delete_ext(X509_CRL *x, int loc);
+
+// X509_CRL_add_ext adds a copy of |ex| to |x|. It returns one on success and
+// zero on failure. The caller retains ownership of |ex| and can release it
+// independently of |x|.
+//
+// The new extension is inserted at index |loc|, shifting extensions to the
+// right. If |loc| is -1 or out of bounds, the new extension is appended to the
+// list.
 OPENSSL_EXPORT int X509_CRL_add_ext(X509_CRL *x, X509_EXTENSION *ex, int loc);
 
 // X509_CRL_get_ext_d2i behaves like |X509V3_get_d2i| but looks for the
@@ -1361,18 +1570,45 @@
 OPENSSL_EXPORT int X509_CRL_add1_ext_i2d(X509_CRL *x, int nid, void *value,
                                          int crit, unsigned long flags);
 
+// X509_REVOKED_get_ext_count returns the number of extensions in |x|.
 OPENSSL_EXPORT int X509_REVOKED_get_ext_count(const X509_REVOKED *x);
+
+// X509_REVOKED_get_ext_by_NID behaves like |X509v3_get_ext_by_NID| but searches
+// for extensions in |x|.
 OPENSSL_EXPORT int X509_REVOKED_get_ext_by_NID(const X509_REVOKED *x, int nid,
                                                int lastpos);
+
+// X509_REVOKED_get_ext_by_OBJ behaves like |X509v3_get_ext_by_OBJ| but searches
+// for extensions in |x|.
 OPENSSL_EXPORT int X509_REVOKED_get_ext_by_OBJ(const X509_REVOKED *x,
                                                const ASN1_OBJECT *obj,
                                                int lastpos);
+
+// X509_REVOKED_get_ext_by_critical behaves like |X509v3_get_ext_by_critical|
+// but searches for extensions in |x|.
 OPENSSL_EXPORT int X509_REVOKED_get_ext_by_critical(const X509_REVOKED *x,
                                                     int crit, int lastpos);
+
+// X509_REVOKED_get_ext returns the extension in |x| at index |loc|, or NULL if
+// |loc| is out of bounds.
 OPENSSL_EXPORT X509_EXTENSION *X509_REVOKED_get_ext(const X509_REVOKED *x,
                                                     int loc);
+
+// X509_REVOKED_delete_ext removes the extension in |x| at index |loc| and
+// returns the removed extension, or NULL if |loc| was out of bounds. If
+// non-NULL, the caller must release the result with |X509_EXTENSION_free|. It
+// is also safe, but not necessary, to call |X509_EXTENSION_free| if the result
+// is NULL.
 OPENSSL_EXPORT X509_EXTENSION *X509_REVOKED_delete_ext(X509_REVOKED *x,
                                                        int loc);
+
+// X509_REVOKED_add_ext adds a copy of |ex| to |x|. It returns one on success
+// and zero on failure. The caller retains ownership of |ex| and can release it
+// independently of |x|.
+//
+// The new extension is inserted at index |loc|, shifting extensions to the
+// right. If |loc| is -1 or out of bounds, the new extension is appended to the
+// list.
 OPENSSL_EXPORT int X509_REVOKED_add_ext(X509_REVOKED *x, X509_EXTENSION *ex,
                                         int loc);
 
@@ -1395,59 +1631,193 @@
                                              void *value, int crit,
                                              unsigned long flags);
 
+// X509_EXTENSION_create_by_NID creates a new |X509_EXTENSION| with type |nid|,
+// value |data|, and critical bit |crit|. It returns the newly-allocated
+// |X509_EXTENSION| on success, and false on error. |nid| should be a |NID_*|
+// constant.
+//
+// If |ex| and |*ex| are both non-NULL, it modifies and returns |*ex| instead of
+// creating a new object. If |ex| is non-NULL, but |*ex| is NULL, it sets |*ex|
+// to the new |X509_EXTENSION|, in addition to returning the result.
 OPENSSL_EXPORT X509_EXTENSION *X509_EXTENSION_create_by_NID(
     X509_EXTENSION **ex, int nid, int crit, const ASN1_OCTET_STRING *data);
+
+// X509_EXTENSION_create_by_OBJ behaves like |X509_EXTENSION_create_by_NID|, but
+// the extension type is determined by an |ASN1_OBJECT|.
 OPENSSL_EXPORT X509_EXTENSION *X509_EXTENSION_create_by_OBJ(
     X509_EXTENSION **ex, const ASN1_OBJECT *obj, int crit,
     const ASN1_OCTET_STRING *data);
+
+// X509_EXTENSION_set_object sets |ex|'s extension type to |obj|. It returns one
+// on success and zero on error.
 OPENSSL_EXPORT int X509_EXTENSION_set_object(X509_EXTENSION *ex,
                                              const ASN1_OBJECT *obj);
+
+// X509_EXTENSION_set_critical sets |ex| to critical if |crit| is non-zero and
+// to non-critical if |crit| is zero.
 OPENSSL_EXPORT int X509_EXTENSION_set_critical(X509_EXTENSION *ex, int crit);
+
+// X509_EXTENSION_set_data set's |ex|'s extension value to a copy of |data|. It
+// returns one on success and zero on error.
 OPENSSL_EXPORT int X509_EXTENSION_set_data(X509_EXTENSION *ex,
                                            const ASN1_OCTET_STRING *data);
-OPENSSL_EXPORT ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *ex);
-OPENSSL_EXPORT ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne);
-OPENSSL_EXPORT int X509_EXTENSION_get_critical(X509_EXTENSION *ex);
 
+// X509_EXTENSION_get_object returns |ex|'s extension type.
+OPENSSL_EXPORT ASN1_OBJECT *X509_EXTENSION_get_object(X509_EXTENSION *ex);
+
+// X509_EXTENSION_get_data returns |ne|'s extension value.
+OPENSSL_EXPORT ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ne);
+
+// X509_EXTENSION_get_critical returns one if |ex| is critical and zero
+// otherwise.
+OPENSSL_EXPORT int X509_EXTENSION_get_critical(const X509_EXTENSION *ex);
+
+// X509at_get_attr_count returns the number of attributes in |x|.
 OPENSSL_EXPORT int X509at_get_attr_count(const STACK_OF(X509_ATTRIBUTE) *x);
+
+// X509at_get_attr_by_NID returns the index of the attribute in |x| of type
+// |nid|, or a negative number if not found. If found, callers can use
+// |X509at_get_attr| to look up the attribute by index.
+//
+// If |lastpos| is non-negative, it begins searching at |lastpos| + 1. Callers
+// can thus loop over all matching attributes by first passing -1 and then
+// passing the previously-returned value until no match is returned.
 OPENSSL_EXPORT int X509at_get_attr_by_NID(const STACK_OF(X509_ATTRIBUTE) *x,
                                           int nid, int lastpos);
+
+// X509at_get_attr_by_OBJ behaves like |X509at_get_attr_by_NID| but looks for
+// attributes of type |obj|.
 OPENSSL_EXPORT int X509at_get_attr_by_OBJ(const STACK_OF(X509_ATTRIBUTE) *sk,
                                           const ASN1_OBJECT *obj, int lastpos);
+
+// X509at_get_attr returns the attribute at index |loc| in |x|, or NULL if
+// out of bounds.
 OPENSSL_EXPORT X509_ATTRIBUTE *X509at_get_attr(
     const STACK_OF(X509_ATTRIBUTE) *x, int loc);
+
+// X509at_delete_attr removes the attribute at index |loc| in |x|. It returns
+// the removed attribute to the caller, or NULL if |loc| was out of bounds. If
+// non-NULL, the caller must release the result with |X509_ATTRIBUTE_free| when
+// done. It is also safe, but not necessary, to call |X509_ATTRIBUTE_free| if
+// the result is NULL.
 OPENSSL_EXPORT X509_ATTRIBUTE *X509at_delete_attr(STACK_OF(X509_ATTRIBUTE) *x,
                                                   int loc);
+
+// X509at_add1_attr appends a copy of |attr| to the attribute list in |*x|. If
+// |*x| is NULL, it allocates a new |STACK_OF(X509_ATTRIBUTE)| to hold the copy
+// and sets |*x| to the new list. It returns |*x| on success and NULL on error.
+// The caller retains ownership of |attr| and can release it independently of
+// |*x|.
 OPENSSL_EXPORT STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr(
     STACK_OF(X509_ATTRIBUTE) **x, X509_ATTRIBUTE *attr);
+
+// X509at_add1_attr_by_OBJ behaves like |X509at_add1_attr|, but adds an
+// attribute created by |X509_ATTRIBUTE_create_by_OBJ|.
 OPENSSL_EXPORT STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_OBJ(
     STACK_OF(X509_ATTRIBUTE) **x, const ASN1_OBJECT *obj, int type,
     const unsigned char *bytes, int len);
+
+// X509at_add1_attr_by_NID behaves like |X509at_add1_attr|, but adds an
+// attribute created by |X509_ATTRIBUTE_create_by_NID|.
 OPENSSL_EXPORT STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_NID(
     STACK_OF(X509_ATTRIBUTE) **x, int nid, int type, const unsigned char *bytes,
     int len);
+
+// X509at_add1_attr_by_txt behaves like |X509at_add1_attr|, but adds an
+// attribute created by |X509_ATTRIBUTE_create_by_txt|.
 OPENSSL_EXPORT STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_txt(
     STACK_OF(X509_ATTRIBUTE) **x, const char *attrname, int type,
     const unsigned char *bytes, int len);
-OPENSSL_EXPORT void *X509at_get0_data_by_OBJ(STACK_OF(X509_ATTRIBUTE) *x,
-                                             ASN1_OBJECT *obj, int lastpos,
-                                             int type);
+
+// X509_ATTRIBUTE_create_by_NID returns a newly-allocated |X509_ATTRIBUTE| of
+// type |nid|, or NULL on error. The value is determined as in
+// |X509_ATTRIBUTE_set1_data|.
+//
+// If |attr| is non-NULL, the resulting |X509_ATTRIBUTE| is also written to
+// |*attr|. If |*attr| was non-NULL when the function was called, |*attr| is
+// reused instead of creating a new object.
+//
+// WARNING: The interpretation of |attrtype|, |data|, and |len| is complex and
+// error-prone. See |X509_ATTRIBUTE_set1_data| for details.
+//
+// WARNING: The object reuse form is deprecated and may be removed in the
+// future. It also currently incorrectly appends to the reused object's value
+// set rather than overwriting it.
 OPENSSL_EXPORT X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(
-    X509_ATTRIBUTE **attr, int nid, int atrtype, const void *data, int len);
+    X509_ATTRIBUTE **attr, int nid, int attrtype, const void *data, int len);
+
+// X509_ATTRIBUTE_create_by_OBJ behaves like |X509_ATTRIBUTE_create_by_NID|
+// except the attribute's type is determined by |obj|.
 OPENSSL_EXPORT X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(
-    X509_ATTRIBUTE **attr, const ASN1_OBJECT *obj, int atrtype,
+    X509_ATTRIBUTE **attr, const ASN1_OBJECT *obj, int attrtype,
     const void *data, int len);
+
+// X509_ATTRIBUTE_create_by_txt behaves like |X509_ATTRIBUTE_create_by_NID|
+// except the attribute's type is determined by calling |OBJ_txt2obj| with
+// |attrname|.
 OPENSSL_EXPORT X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_txt(
-    X509_ATTRIBUTE **attr, const char *atrname, int type,
+    X509_ATTRIBUTE **attr, const char *attrname, int type,
     const unsigned char *bytes, int len);
+
+// X509_ATTRIBUTE_set1_object sets |attr|'s type to |obj|. It returns one on
+// success and zero on error.
 OPENSSL_EXPORT int X509_ATTRIBUTE_set1_object(X509_ATTRIBUTE *attr,
                                               const ASN1_OBJECT *obj);
+
+// X509_ATTRIBUTE_set1_data appends a value to |attr|'s value set and returns
+// one on success or zero on error. The value is determined as follows:
+//
+// If |attrtype| is a |MBSTRING_*| constant, the value is an ASN.1 string. The
+// string is determined by decoding |len| bytes from |data| in the encoding
+// specified by |attrtype|, and then re-encoding it in a form appropriate for
+// |attr|'s type. If |len| is -1, |strlen(data)| is used instead. See
+// |ASN1_STRING_set_by_NID| for details.
+//
+// TODO(davidben): Document |ASN1_STRING_set_by_NID| so the reference is useful.
+//
+// Otherwise, if |len| is not -1, the value is an ASN.1 string. |attrtype| is an
+// |ASN1_STRING| type value and the |len| bytes from |data| are copied as the
+// type-specific representation of |ASN1_STRING|. See |ASN1_STRING| for details.
+//
+// WARNING: If this form is used to construct a negative INTEGER or ENUMERATED,
+// |attrtype| includes the |V_ASN1_NEG| flag for |ASN1_STRING|, but the function
+// forgets to clear the flag for |ASN1_TYPE|. This matches OpenSSL but is
+// probably a bug. For now, do not use this form with negative values.
+//
+// Otherwise, if |len| is -1, the value is constructed by passing |attrtype| and
+// |data| to |ASN1_TYPE_set1|. That is, |attrtype| is an |ASN1_TYPE| type value,
+// and |data| is cast to the corresponding pointer type.
+//
+// WARNING: Despite the name, this function appends to |attr|'s value set,
+// rather than overwriting it. To overwrite the value set, create a new
+// |X509_ATTRIBUTE| with |X509_ATTRIBUTE_new|.
+//
+// WARNING: If using the |MBSTRING_*| form, pass a length rather than relying on
+// |strlen|. In particular, |strlen| will not behave correctly if the input is
+// |MBSTRING_BMP| or |MBSTRING_UNIV|.
+//
+// WARNING: This function currently misinterprets |V_ASN1_OTHER| as an
+// |MBSTRING_*| constant. This matches OpenSSL but means it is impossible to
+// construct a value with a non-universal tag.
 OPENSSL_EXPORT int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype,
                                             const void *data, int len);
+
+// X509_ATTRIBUTE_get0_data returns the |idx|th value of |attr| in a
+// type-specific representation to |attrtype|, or NULL if out of bounds or the
+// type does not match. |attrtype| is one of the type values in |ASN1_TYPE|. On
+// match, the return value uses the same representation as |ASN1_TYPE_set0|. See
+// |ASN1_TYPE| for details.
 OPENSSL_EXPORT void *X509_ATTRIBUTE_get0_data(X509_ATTRIBUTE *attr, int idx,
-                                              int atrtype, void *data);
-OPENSSL_EXPORT int X509_ATTRIBUTE_count(X509_ATTRIBUTE *attr);
+                                              int attrtype, void *unused);
+
+// X509_ATTRIBUTE_count returns the number of values in |attr|.
+OPENSSL_EXPORT int X509_ATTRIBUTE_count(const X509_ATTRIBUTE *attr);
+
+// X509_ATTRIBUTE_get0_object returns the type of |attr|.
 OPENSSL_EXPORT ASN1_OBJECT *X509_ATTRIBUTE_get0_object(X509_ATTRIBUTE *attr);
+
+// X509_ATTRIBUTE_get0_type returns the |idx|th value in |attr|, or NULL if out
+// of bounds. Note this function returns one of |attr|'s values, not the type.
 OPENSSL_EXPORT ASN1_TYPE *X509_ATTRIBUTE_get0_type(X509_ATTRIBUTE *attr,
                                                    int idx);
 
@@ -1473,13 +1843,36 @@
                                    const unsigned char **pk, int *ppklen,
                                    X509_ALGOR **pa, PKCS8_PRIV_KEY_INFO *p8);
 
-OPENSSL_EXPORT int X509_PUBKEY_set0_param(X509_PUBKEY *pub,
-                                          const ASN1_OBJECT *aobj, int ptype,
-                                          void *pval, unsigned char *penc,
-                                          int penclen);
-OPENSSL_EXPORT int X509_PUBKEY_get0_param(ASN1_OBJECT **ppkalg,
-                                          const unsigned char **pk, int *ppklen,
-                                          X509_ALGOR **pa, X509_PUBKEY *pub);
+// X509_PUBKEY_set0_param sets |pub| to a key with AlgorithmIdentifier
+// determined by |obj|, |param_type|, and |param_value|, and an encoded
+// public key of |key|. On success, it takes ownership of all its parameters and
+// returns one. Otherwise, it returns zero. |key| must have been allocated by
+// |OPENSSL_malloc|.
+//
+// |obj|, |param_type|, and |param_value| are interpreted as in
+// |X509_ALGOR_set0|. See |X509_ALGOR_set0| for details.
+OPENSSL_EXPORT int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *obj,
+                                          int param_type, void *param_value,
+                                          uint8_t *key, int key_len);
+
+// X509_PUBKEY_get0_param outputs fields of |pub| and returns one. If |out_obj|
+// is not NULL, it sets |*out_obj| to AlgorithmIdentifier's OID. If |out_key|
+// is not NULL, it sets |*out_key| and |*out_key_len| to the encoded public key.
+// If |out_alg| is not NULL, it sets |*out_alg| to the AlgorithmIdentifier.
+//
+// Note: X.509 SubjectPublicKeyInfo structures store the encoded public key as a
+// BIT STRING. |*out_key| and |*out_key_len| will silently pad the key with zero
+// bits if |pub| did not contain a whole number of bytes. Use
+// |X509_PUBKEY_get0_public_key| to preserve this information.
+OPENSSL_EXPORT int X509_PUBKEY_get0_param(ASN1_OBJECT **out_obj,
+                                          const uint8_t **out_key,
+                                          int *out_key_len,
+                                          X509_ALGOR **out_alg,
+                                          X509_PUBKEY *pub);
+
+// X509_PUBKEY_get0_public_key returns |pub|'s encoded public key.
+OPENSSL_EXPORT const ASN1_BIT_STRING *X509_PUBKEY_get0_public_key(
+    const X509_PUBKEY *pub);
 
 OPENSSL_EXPORT int X509_check_trust(X509 *x, int id, int flags);
 OPENSSL_EXPORT int X509_TRUST_get_count(void);
@@ -1519,6 +1912,7 @@
 BORINGSSL_MAKE_DELETER(X509, X509_free)
 BORINGSSL_MAKE_UP_REF(X509, X509_up_ref)
 BORINGSSL_MAKE_DELETER(X509_ALGOR, X509_ALGOR_free)
+BORINGSSL_MAKE_DELETER(X509_ATTRIBUTE, X509_ATTRIBUTE_free)
 BORINGSSL_MAKE_DELETER(X509_CRL, X509_CRL_free)
 BORINGSSL_MAKE_UP_REF(X509_CRL, X509_CRL_up_ref)
 BORINGSSL_MAKE_DELETER(X509_CRL_METHOD, X509_CRL_METHOD_free)
@@ -1534,13 +1928,10 @@
 BORINGSSL_MAKE_DELETER(X509_REVOKED, X509_REVOKED_free)
 BORINGSSL_MAKE_DELETER(X509_SIG, X509_SIG_free)
 BORINGSSL_MAKE_DELETER(X509_STORE, X509_STORE_free)
+BORINGSSL_MAKE_UP_REF(X509_STORE, X509_STORE_up_ref)
 BORINGSSL_MAKE_DELETER(X509_STORE_CTX, X509_STORE_CTX_free)
 BORINGSSL_MAKE_DELETER(X509_VERIFY_PARAM, X509_VERIFY_PARAM_free)
 
-using ScopedX509_STORE_CTX =
-    internal::StackAllocated<X509_STORE_CTX, void, X509_STORE_CTX_zero,
-                             X509_STORE_CTX_cleanup>;
-
 BSSL_NAMESPACE_END
 
 }  // extern C++
diff --git a/deps/boringssl/src/include/openssl/x509_vfy.h b/deps/boringssl/src/include/openssl/x509_vfy.h
index 73dd470..d8781af 100644
--- a/deps/boringssl/src/include/openssl/x509_vfy.h
+++ b/deps/boringssl/src/include/openssl/x509_vfy.h
@@ -4,21 +4,21 @@
  * This package is an SSL implementation written
  * by Eric Young (eay@cryptsoft.com).
  * The implementation was written so as to conform with Netscapes SSL.
- * 
+ *
  * This library is free for commercial and non-commercial use as long as
  * the following conditions are aheared to.  The following conditions
  * apply to all code found in this distribution, be it the RC4, RSA,
  * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
  * included with this distribution is covered by the same copyright terms
  * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- * 
+ *
  * Copyright remains Eric Young's, and as such any Copyright notices in
  * the code are not to be removed.
  * If this package is used in a product, Eric Young should be given attribution
  * as the author of the parts of the library used.
  * This can be in the form of a textual message at program startup or
  * in documentation (online or textual) provided with the package.
- * 
+ *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * are met:
@@ -33,10 +33,10 @@
  *     Eric Young (eay@cryptsoft.com)"
  *    The word 'cryptographic' can be left out if the rouines from the library
  *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from 
+ * 4. If you include any Windows specific code (or a derivative thereof) from
  *    the apps directory (application code) you must include an acknowledgement:
  *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- * 
+ *
  * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -48,7 +48,7 @@
  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
- * 
+ *
  * The licence and distribution terms for any publically available version or
  * derivative of this code cannot be changed.  i.e. this code cannot simply be
  * copied and put under another distribution licence
@@ -57,8 +57,8 @@
 
 #ifndef HEADER_X509_H
 #include <openssl/x509.h>
-/* openssl/x509.h ends up #include-ing this file at about the only
- * appropriate moment. */
+// openssl/x509.h ends up #include-ing this file at about the only
+// appropriate moment.
 #endif
 
 #ifndef HEADER_X509_VFY_H
@@ -66,28 +66,27 @@
 
 #include <openssl/thread.h>
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 extern "C" {
 #endif
 
-/* Legacy X.509 library.
- *
- * This header is part of OpenSSL's X.509 implementation. It is retained for
- * compatibility but otherwise underdocumented and not actively maintained. In
- * the future, a replacement library will be available. Meanwhile, minimize
- * dependencies on this header where possible. */
+// Legacy X.509 library.
+//
+// This header is part of OpenSSL's X.509 implementation. It is retained for
+// compatibility but otherwise underdocumented and not actively maintained. In
+// the future, a replacement library will be available. Meanwhile, minimize
+// dependencies on this header where possible.
 
 
-/*******************************/
 /*
-SSL_CTX -> X509_STORE    
-		-> X509_LOOKUP
-			->X509_LOOKUP_METHOD
-		-> X509_LOOKUP
-			->X509_LOOKUP_METHOD
- 
+SSL_CTX -> X509_STORE
+                -> X509_LOOKUP
+                        ->X509_LOOKUP_METHOD
+                -> X509_LOOKUP
+                        ->X509_LOOKUP_METHOD
+
 SSL	-> X509_STORE_CTX
-		->X509_STORE    
+                ->X509_STORE
 
 The X509_STORE holds the tables etc for verification stuff.
 A X509_STORE_CTX is used while validating a single certificate.
@@ -96,366 +95,220 @@
 certificate chain.
 */
 
-#define X509_LU_X509		1
-#define X509_LU_CRL		2
-#define X509_LU_PKEY		3
-
-typedef struct x509_object_st
-	{
-	/* one of the above types */
-	int type;
-	union	{
-		char *ptr;
-		X509 *x509;
-		X509_CRL *crl;
-		EVP_PKEY *pkey;
-		} data;
-	} X509_OBJECT;
+#define X509_LU_X509 1
+#define X509_LU_CRL 2
+#define X509_LU_PKEY 3
 
 DEFINE_STACK_OF(X509_LOOKUP)
 DEFINE_STACK_OF(X509_OBJECT)
-
-/* This is a static that defines the function interface */
-typedef struct x509_lookup_method_st
-	{
-	const char *name;
-	int (*new_item)(X509_LOOKUP *ctx);
-	void (*free)(X509_LOOKUP *ctx);
-	int (*init)(X509_LOOKUP *ctx);
-	int (*shutdown)(X509_LOOKUP *ctx);
-	int (*ctrl)(X509_LOOKUP *ctx,int cmd,const char *argc,long argl,
-			char **ret);
-	int (*get_by_subject)(X509_LOOKUP *ctx,int type,X509_NAME *name,
-			      X509_OBJECT *ret);
-	int (*get_by_issuer_serial)(X509_LOOKUP *ctx,int type,X509_NAME *name,
-				    ASN1_INTEGER *serial,X509_OBJECT *ret);
-	int (*get_by_fingerprint)(X509_LOOKUP *ctx,int type,
-				  unsigned char *bytes,int len,
-				  X509_OBJECT *ret);
-	int (*get_by_alias)(X509_LOOKUP *ctx,int type,char *str,int len,
-			    X509_OBJECT *ret);
-	} X509_LOOKUP_METHOD;
-
-typedef struct X509_VERIFY_PARAM_ID_st X509_VERIFY_PARAM_ID;
-
-/* This structure hold all parameters associated with a verify operation
- * by including an X509_VERIFY_PARAM structure in related structures the
- * parameters used can be customized
- */
-
-struct X509_VERIFY_PARAM_st
-	{
-	char *name;
-	time_t check_time;	/* Time to use */
-	unsigned long inh_flags; /* Inheritance flags */
-	unsigned long flags;	/* Various verify flags */
-	int purpose;		/* purpose to check untrusted certificates */
-	int trust;		/* trust setting to check */
-	int depth;		/* Verify depth */
-	STACK_OF(ASN1_OBJECT) *policies;	/* Permissible policies */
-	X509_VERIFY_PARAM_ID *id;	/* opaque ID data */
-	};
-
 DEFINE_STACK_OF(X509_VERIFY_PARAM)
 
 typedef int (*X509_STORE_CTX_verify_cb)(int, X509_STORE_CTX *);
 typedef int (*X509_STORE_CTX_verify_fn)(X509_STORE_CTX *);
-typedef int (*X509_STORE_CTX_get_issuer_fn)(X509 **issuer,
-                                            X509_STORE_CTX *ctx, X509 *x);
-typedef int (*X509_STORE_CTX_check_issued_fn)(X509_STORE_CTX *ctx,
-                                              X509 *x, X509 *issuer);
+typedef int (*X509_STORE_CTX_get_issuer_fn)(X509 **issuer, X509_STORE_CTX *ctx,
+                                            X509 *x);
+typedef int (*X509_STORE_CTX_check_issued_fn)(X509_STORE_CTX *ctx, X509 *x,
+                                              X509 *issuer);
 typedef int (*X509_STORE_CTX_check_revocation_fn)(X509_STORE_CTX *ctx);
-typedef int (*X509_STORE_CTX_get_crl_fn)(X509_STORE_CTX *ctx,
-                                         X509_CRL **crl, X509 *x);
+typedef int (*X509_STORE_CTX_get_crl_fn)(X509_STORE_CTX *ctx, X509_CRL **crl,
+                                         X509 *x);
 typedef int (*X509_STORE_CTX_check_crl_fn)(X509_STORE_CTX *ctx, X509_CRL *crl);
-typedef int (*X509_STORE_CTX_cert_crl_fn)(X509_STORE_CTX *ctx,
-                                          X509_CRL *crl, X509 *x);
+typedef int (*X509_STORE_CTX_cert_crl_fn)(X509_STORE_CTX *ctx, X509_CRL *crl,
+                                          X509 *x);
 typedef int (*X509_STORE_CTX_check_policy_fn)(X509_STORE_CTX *ctx);
 typedef STACK_OF(X509) *(*X509_STORE_CTX_lookup_certs_fn)(X509_STORE_CTX *ctx,
                                                           X509_NAME *nm);
-typedef STACK_OF(X509_CRL) *(*X509_STORE_CTX_lookup_crls_fn)(X509_STORE_CTX *ctx,
-                                                             X509_NAME *nm);
+typedef STACK_OF(X509_CRL) *(*X509_STORE_CTX_lookup_crls_fn)(
+    X509_STORE_CTX *ctx, X509_NAME *nm);
 typedef int (*X509_STORE_CTX_cleanup_fn)(X509_STORE_CTX *ctx);
 
-/* This is used to hold everything.  It is used for all certificate
- * validation.  Once we have a certificate chain, the 'verify'
- * function is then called to actually check the cert chain. */
-struct x509_store_st
-	{
-	/* The following is a cache of trusted certs */
-	int cache; 	/* if true, stash any hits */
-	STACK_OF(X509_OBJECT) *objs;	/* Cache of all objects */
-	CRYPTO_MUTEX objs_lock;
-	STACK_OF(X509) *additional_untrusted;
-
-	/* These are external lookup methods */
-	STACK_OF(X509_LOOKUP) *get_cert_methods;
-
-	X509_VERIFY_PARAM *param;
-
-	/* Callbacks for various operations */
-	X509_STORE_CTX_verify_fn verify;	/* called to verify a certificate */
-	X509_STORE_CTX_verify_cb verify_cb;	/* error callback */
-	X509_STORE_CTX_get_issuer_fn get_issuer;	/* get issuers cert from ctx */
-	X509_STORE_CTX_check_issued_fn check_issued; /* check issued */
-	X509_STORE_CTX_check_revocation_fn check_revocation; /* Check revocation status of chain */
-	X509_STORE_CTX_get_crl_fn get_crl; /* retrieve CRL */
-	X509_STORE_CTX_check_crl_fn check_crl; /* Check CRL validity */
-	X509_STORE_CTX_cert_crl_fn cert_crl; /* Check certificate against CRL */
-	X509_STORE_CTX_lookup_certs_fn lookup_certs;
-	X509_STORE_CTX_lookup_crls_fn lookup_crls;
-	X509_STORE_CTX_cleanup_fn cleanup;
-
-	CRYPTO_refcount_t references;
-	} /* X509_STORE */;
-
 OPENSSL_EXPORT int X509_STORE_set_depth(X509_STORE *store, int depth);
 
-/* This is the functions plus an instance of the local variables. */
-struct x509_lookup_st
-	{
-	int init;			/* have we been started */
-	int skip;			/* don't use us. */
-	X509_LOOKUP_METHOD *method;	/* the functions */
-	char *method_data;		/* method data */
-
-	X509_STORE *store_ctx;	/* who owns us */
-	} /* X509_LOOKUP */;
-
-/* This is a used when verifying cert chains.  Since the
- * gathering of the cert chain can take some time (and have to be
- * 'retried', this needs to be kept and passed around. */
-struct x509_store_ctx_st      /* X509_STORE_CTX */
-	{
-	X509_STORE *ctx;
-
-	/* The following are set by the caller */
-	X509 *cert;		/* The cert to check */
-	STACK_OF(X509) *untrusted;	/* chain of X509s - untrusted - passed in */
-	STACK_OF(X509_CRL) *crls;	/* set of CRLs passed in */
-
-	X509_VERIFY_PARAM *param;
-	void *other_ctx;	/* Other info for use with get_issuer() */
-
-	/* Callbacks for various operations */
-	X509_STORE_CTX_verify_fn verify;	/* called to verify a certificate */
-	X509_STORE_CTX_verify_cb verify_cb;		/* error callback */
-	X509_STORE_CTX_get_issuer_fn get_issuer;	/* get issuers cert from ctx */
-	X509_STORE_CTX_check_issued_fn check_issued; /* check issued */
-	X509_STORE_CTX_check_revocation_fn check_revocation; /* Check revocation status of chain */
-	X509_STORE_CTX_get_crl_fn get_crl; /* retrieve CRL */
-	X509_STORE_CTX_check_crl_fn check_crl; /* Check CRL validity */
-	X509_STORE_CTX_cert_crl_fn cert_crl; /* Check certificate against CRL */
-	X509_STORE_CTX_check_policy_fn check_policy;
-	X509_STORE_CTX_lookup_certs_fn lookup_certs;
-	X509_STORE_CTX_lookup_crls_fn lookup_crls;
-	X509_STORE_CTX_cleanup_fn cleanup;
-
-	/* The following is built up */
-	int valid;		/* if 0, rebuild chain */
-	int last_untrusted;	/* index of last untrusted cert */
-	STACK_OF(X509) *chain; 		/* chain of X509s - built up and trusted */
-	X509_POLICY_TREE *tree;	/* Valid policy tree */
-
-	int explicit_policy;	/* Require explicit policy value */
-
-	/* When something goes wrong, this is why */
-	int error_depth;
-	int error;
-	X509 *current_cert;
-	X509 *current_issuer;	/* cert currently being tested as valid issuer */
-	X509_CRL *current_crl;	/* current CRL */
-
-	int current_crl_score;  /* score of current CRL */
-	unsigned int current_reasons;  /* Reason mask */
-
-	X509_STORE_CTX *parent; /* For CRL path validation: parent context */
-
-	CRYPTO_EX_DATA ex_data;
-	} /* X509_STORE_CTX */;
-
 OPENSSL_EXPORT void X509_STORE_CTX_set_depth(X509_STORE_CTX *ctx, int depth);
 
-#define X509_STORE_CTX_set_app_data(ctx,data) \
-	X509_STORE_CTX_set_ex_data(ctx,0,data)
-#define X509_STORE_CTX_get_app_data(ctx) \
-	X509_STORE_CTX_get_ex_data(ctx,0)
+#define X509_STORE_CTX_set_app_data(ctx, data) \
+  X509_STORE_CTX_set_ex_data(ctx, 0, data)
+#define X509_STORE_CTX_get_app_data(ctx) X509_STORE_CTX_get_ex_data(ctx, 0)
 
-#define X509_L_FILE_LOAD	1
-#define X509_L_ADD_DIR		2
+#define X509_L_FILE_LOAD 1
+#define X509_L_ADD_DIR 2
 
-#define X509_LOOKUP_load_file(x,name,type) \
-		X509_LOOKUP_ctrl((x),X509_L_FILE_LOAD,(name),(long)(type),NULL)
+#define X509_LOOKUP_load_file(x, name, type) \
+  X509_LOOKUP_ctrl((x), X509_L_FILE_LOAD, (name), (long)(type), NULL)
 
-#define X509_LOOKUP_add_dir(x,name,type) \
-		X509_LOOKUP_ctrl((x),X509_L_ADD_DIR,(name),(long)(type),NULL)
+#define X509_LOOKUP_add_dir(x, name, type) \
+  X509_LOOKUP_ctrl((x), X509_L_ADD_DIR, (name), (long)(type), NULL)
 
-#define		X509_V_OK					0
-#define		X509_V_ERR_UNSPECIFIED				1
+#define X509_V_OK 0
+#define X509_V_ERR_UNSPECIFIED 1
 
-#define		X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT		2
-#define		X509_V_ERR_UNABLE_TO_GET_CRL			3
-#define		X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE	4
-#define		X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE	5
-#define		X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY	6
-#define		X509_V_ERR_CERT_SIGNATURE_FAILURE		7
-#define		X509_V_ERR_CRL_SIGNATURE_FAILURE		8
-#define		X509_V_ERR_CERT_NOT_YET_VALID			9
-#define		X509_V_ERR_CERT_HAS_EXPIRED			10
-#define		X509_V_ERR_CRL_NOT_YET_VALID			11
-#define		X509_V_ERR_CRL_HAS_EXPIRED			12
-#define		X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD	13
-#define		X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD	14
-#define		X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD	15
-#define		X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD	16
-#define		X509_V_ERR_OUT_OF_MEM				17
-#define		X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT		18
-#define		X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN		19
-#define		X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY	20
-#define		X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE	21
-#define		X509_V_ERR_CERT_CHAIN_TOO_LONG			22
-#define		X509_V_ERR_CERT_REVOKED				23
-#define		X509_V_ERR_INVALID_CA				24
-#define		X509_V_ERR_PATH_LENGTH_EXCEEDED			25
-#define		X509_V_ERR_INVALID_PURPOSE			26
-#define		X509_V_ERR_CERT_UNTRUSTED			27
-#define		X509_V_ERR_CERT_REJECTED			28
-/* These are 'informational' when looking for issuer cert */
-#define		X509_V_ERR_SUBJECT_ISSUER_MISMATCH		29
-#define		X509_V_ERR_AKID_SKID_MISMATCH			30
-#define		X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH		31
-#define		X509_V_ERR_KEYUSAGE_NO_CERTSIGN			32
+#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT 2
+#define X509_V_ERR_UNABLE_TO_GET_CRL 3
+#define X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE 4
+#define X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE 5
+#define X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY 6
+#define X509_V_ERR_CERT_SIGNATURE_FAILURE 7
+#define X509_V_ERR_CRL_SIGNATURE_FAILURE 8
+#define X509_V_ERR_CERT_NOT_YET_VALID 9
+#define X509_V_ERR_CERT_HAS_EXPIRED 10
+#define X509_V_ERR_CRL_NOT_YET_VALID 11
+#define X509_V_ERR_CRL_HAS_EXPIRED 12
+#define X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD 13
+#define X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD 14
+#define X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD 15
+#define X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD 16
+#define X509_V_ERR_OUT_OF_MEM 17
+#define X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT 18
+#define X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN 19
+#define X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY 20
+#define X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE 21
+#define X509_V_ERR_CERT_CHAIN_TOO_LONG 22
+#define X509_V_ERR_CERT_REVOKED 23
+#define X509_V_ERR_INVALID_CA 24
+#define X509_V_ERR_PATH_LENGTH_EXCEEDED 25
+#define X509_V_ERR_INVALID_PURPOSE 26
+#define X509_V_ERR_CERT_UNTRUSTED 27
+#define X509_V_ERR_CERT_REJECTED 28
+// These are 'informational' when looking for issuer cert
+#define X509_V_ERR_SUBJECT_ISSUER_MISMATCH 29
+#define X509_V_ERR_AKID_SKID_MISMATCH 30
+#define X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH 31
+#define X509_V_ERR_KEYUSAGE_NO_CERTSIGN 32
 
-#define		X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER		33
-#define		X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION		34
-#define		X509_V_ERR_KEYUSAGE_NO_CRL_SIGN			35
-#define		X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION	36
-#define		X509_V_ERR_INVALID_NON_CA			37
-#define		X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED		38
-#define		X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE	39
-#define		X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED	40
+#define X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER 33
+#define X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION 34
+#define X509_V_ERR_KEYUSAGE_NO_CRL_SIGN 35
+#define X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION 36
+#define X509_V_ERR_INVALID_NON_CA 37
+#define X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED 38
+#define X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE 39
+#define X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED 40
 
-#define		X509_V_ERR_INVALID_EXTENSION			41
-#define		X509_V_ERR_INVALID_POLICY_EXTENSION		42
-#define		X509_V_ERR_NO_EXPLICIT_POLICY			43
-#define		X509_V_ERR_DIFFERENT_CRL_SCOPE			44
-#define		X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE	45
+#define X509_V_ERR_INVALID_EXTENSION 41
+#define X509_V_ERR_INVALID_POLICY_EXTENSION 42
+#define X509_V_ERR_NO_EXPLICIT_POLICY 43
+#define X509_V_ERR_DIFFERENT_CRL_SCOPE 44
+#define X509_V_ERR_UNSUPPORTED_EXTENSION_FEATURE 45
 
-#define		X509_V_ERR_UNNESTED_RESOURCE			46
+#define X509_V_ERR_UNNESTED_RESOURCE 46
 
-#define		X509_V_ERR_PERMITTED_VIOLATION			47
-#define		X509_V_ERR_EXCLUDED_VIOLATION			48
-#define		X509_V_ERR_SUBTREE_MINMAX			49
-#define		X509_V_ERR_APPLICATION_VERIFICATION		50
-#define		X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE		51
-#define		X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX	52
-#define		X509_V_ERR_UNSUPPORTED_NAME_SYNTAX		53
-#define		X509_V_ERR_CRL_PATH_VALIDATION_ERROR		54
+#define X509_V_ERR_PERMITTED_VIOLATION 47
+#define X509_V_ERR_EXCLUDED_VIOLATION 48
+#define X509_V_ERR_SUBTREE_MINMAX 49
+#define X509_V_ERR_APPLICATION_VERIFICATION 50
+#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE 51
+#define X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX 52
+#define X509_V_ERR_UNSUPPORTED_NAME_SYNTAX 53
+#define X509_V_ERR_CRL_PATH_VALIDATION_ERROR 54
 
-/* Suite B mode algorithm violation */
-#define		X509_V_ERR_SUITE_B_INVALID_VERSION		56
-#define		X509_V_ERR_SUITE_B_INVALID_ALGORITHM		57
-#define		X509_V_ERR_SUITE_B_INVALID_CURVE		58
-#define		X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM	59
-#define		X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED		60
-#define		X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256	61
+// Suite B mode algorithm violation
+#define X509_V_ERR_SUITE_B_INVALID_VERSION 56
+#define X509_V_ERR_SUITE_B_INVALID_ALGORITHM 57
+#define X509_V_ERR_SUITE_B_INVALID_CURVE 58
+#define X509_V_ERR_SUITE_B_INVALID_SIGNATURE_ALGORITHM 59
+#define X509_V_ERR_SUITE_B_LOS_NOT_ALLOWED 60
+#define X509_V_ERR_SUITE_B_CANNOT_SIGN_P_384_WITH_P_256 61
 
-/* Host, email and IP check errors */
-#define		X509_V_ERR_HOSTNAME_MISMATCH			62
-#define		X509_V_ERR_EMAIL_MISMATCH			63
-#define		X509_V_ERR_IP_ADDRESS_MISMATCH			64
+// Host, email and IP check errors
+#define X509_V_ERR_HOSTNAME_MISMATCH 62
+#define X509_V_ERR_EMAIL_MISMATCH 63
+#define X509_V_ERR_IP_ADDRESS_MISMATCH 64
 
-/* Caller error */
-#define		X509_V_ERR_INVALID_CALL				65
-/* Issuer lookup error */
-#define		X509_V_ERR_STORE_LOOKUP				66
+// Caller error
+#define X509_V_ERR_INVALID_CALL 65
+// Issuer lookup error
+#define X509_V_ERR_STORE_LOOKUP 66
 
-#define		X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS	67
+#define X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS 67
 
-/* Certificate verify flags */
+// Certificate verify flags
 
-/* Send issuer+subject checks to verify_cb */
-#define	X509_V_FLAG_CB_ISSUER_CHECK		0x1
-/* Use check time instead of current time */
-#define	X509_V_FLAG_USE_CHECK_TIME		0x2
-/* Lookup CRLs */
-#define	X509_V_FLAG_CRL_CHECK			0x4
-/* Lookup CRLs for whole chain */
-#define	X509_V_FLAG_CRL_CHECK_ALL		0x8
-/* Ignore unhandled critical extensions */
-#define	X509_V_FLAG_IGNORE_CRITICAL		0x10
-/* Does nothing as its functionality has been enabled by default. */
-#define	X509_V_FLAG_X509_STRICT			0x00
-/* Enable proxy certificate validation */
-#define	X509_V_FLAG_ALLOW_PROXY_CERTS		0x40
-/* Enable policy checking */
-#define X509_V_FLAG_POLICY_CHECK		0x80
-/* Policy variable require-explicit-policy */
-#define X509_V_FLAG_EXPLICIT_POLICY		0x100
-/* Policy variable inhibit-any-policy */
-#define	X509_V_FLAG_INHIBIT_ANY			0x200
-/* Policy variable inhibit-policy-mapping */
-#define X509_V_FLAG_INHIBIT_MAP			0x400
-/* Notify callback that policy is OK */
-#define X509_V_FLAG_NOTIFY_POLICY		0x800
-/* Extended CRL features such as indirect CRLs, alternate CRL signing keys */
-#define X509_V_FLAG_EXTENDED_CRL_SUPPORT	0x1000
-/* Delta CRL support */
-#define X509_V_FLAG_USE_DELTAS			0x2000
-/* Check selfsigned CA signature */
-#define X509_V_FLAG_CHECK_SS_SIGNATURE		0x4000
-/* Use trusted store first */
-#define X509_V_FLAG_TRUSTED_FIRST		0x8000
-/* Suite B 128 bit only mode: not normally used */
-#define X509_V_FLAG_SUITEB_128_LOS_ONLY		0x10000
-/* Suite B 192 bit only mode */
-#define X509_V_FLAG_SUITEB_192_LOS		0x20000
-/* Suite B 128 bit mode allowing 192 bit algorithms */
-#define X509_V_FLAG_SUITEB_128_LOS		0x30000
+// Send issuer+subject checks to verify_cb
+#define X509_V_FLAG_CB_ISSUER_CHECK 0x1
+// Use check time instead of current time
+#define X509_V_FLAG_USE_CHECK_TIME 0x2
+// Lookup CRLs
+#define X509_V_FLAG_CRL_CHECK 0x4
+// Lookup CRLs for whole chain
+#define X509_V_FLAG_CRL_CHECK_ALL 0x8
+// Ignore unhandled critical extensions
+#define X509_V_FLAG_IGNORE_CRITICAL 0x10
+// Does nothing as its functionality has been enabled by default.
+#define X509_V_FLAG_X509_STRICT 0x00
+// Enable proxy certificate validation
+#define X509_V_FLAG_ALLOW_PROXY_CERTS 0x40
+// Enable policy checking
+#define X509_V_FLAG_POLICY_CHECK 0x80
+// Policy variable require-explicit-policy
+#define X509_V_FLAG_EXPLICIT_POLICY 0x100
+// Policy variable inhibit-any-policy
+#define X509_V_FLAG_INHIBIT_ANY 0x200
+// Policy variable inhibit-policy-mapping
+#define X509_V_FLAG_INHIBIT_MAP 0x400
+// Notify callback that policy is OK
+#define X509_V_FLAG_NOTIFY_POLICY 0x800
+// Extended CRL features such as indirect CRLs, alternate CRL signing keys
+#define X509_V_FLAG_EXTENDED_CRL_SUPPORT 0x1000
+// Delta CRL support
+#define X509_V_FLAG_USE_DELTAS 0x2000
+// Check selfsigned CA signature
+#define X509_V_FLAG_CHECK_SS_SIGNATURE 0x4000
+// Use trusted store first
+#define X509_V_FLAG_TRUSTED_FIRST 0x8000
+// Suite B 128 bit only mode: not normally used
+#define X509_V_FLAG_SUITEB_128_LOS_ONLY 0x10000
+// Suite B 192 bit only mode
+#define X509_V_FLAG_SUITEB_192_LOS 0x20000
+// Suite B 128 bit mode allowing 192 bit algorithms
+#define X509_V_FLAG_SUITEB_128_LOS 0x30000
 
-/* Allow partial chains if at least one certificate is in trusted store */
-#define X509_V_FLAG_PARTIAL_CHAIN		0x80000
+// Allow partial chains if at least one certificate is in trusted store
+#define X509_V_FLAG_PARTIAL_CHAIN 0x80000
 
-/* If the initial chain is not trusted, do not attempt to build an alternative
- * chain. Alternate chain checking was introduced in 1.0.2b. Setting this flag
- * will force the behaviour to match that of previous versions. */
-#define X509_V_FLAG_NO_ALT_CHAINS		0x100000
+// If the initial chain is not trusted, do not attempt to build an alternative
+// chain. Alternate chain checking was introduced in 1.0.2b. Setting this flag
+// will force the behaviour to match that of previous versions.
+#define X509_V_FLAG_NO_ALT_CHAINS 0x100000
 
-#define X509_VP_FLAG_DEFAULT			0x1
-#define X509_VP_FLAG_OVERWRITE			0x2
-#define X509_VP_FLAG_RESET_FLAGS		0x4
-#define X509_VP_FLAG_LOCKED			0x8
-#define X509_VP_FLAG_ONCE			0x10
+#define X509_VP_FLAG_DEFAULT 0x1
+#define X509_VP_FLAG_OVERWRITE 0x2
+#define X509_VP_FLAG_RESET_FLAGS 0x4
+#define X509_VP_FLAG_LOCKED 0x8
+#define X509_VP_FLAG_ONCE 0x10
 
-/* Internal use: mask of policy related options */
-#define X509_V_FLAG_POLICY_MASK (X509_V_FLAG_POLICY_CHECK \
-				| X509_V_FLAG_EXPLICIT_POLICY \
-				| X509_V_FLAG_INHIBIT_ANY \
-				| X509_V_FLAG_INHIBIT_MAP)
+// Internal use: mask of policy related options
+#define X509_V_FLAG_POLICY_MASK                             \
+  (X509_V_FLAG_POLICY_CHECK | X509_V_FLAG_EXPLICIT_POLICY | \
+   X509_V_FLAG_INHIBIT_ANY | X509_V_FLAG_INHIBIT_MAP)
 
-OPENSSL_EXPORT int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h, int type,
-	     X509_NAME *name);
-OPENSSL_EXPORT X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h,int type,X509_NAME *name);
-OPENSSL_EXPORT X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h, X509_OBJECT *x);
+OPENSSL_EXPORT int X509_OBJECT_idx_by_subject(STACK_OF(X509_OBJECT) *h,
+                                              int type, X509_NAME *name);
+OPENSSL_EXPORT X509_OBJECT *X509_OBJECT_retrieve_by_subject(
+    STACK_OF(X509_OBJECT) *h, int type, X509_NAME *name);
+OPENSSL_EXPORT X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h,
+                                                       X509_OBJECT *x);
 OPENSSL_EXPORT int X509_OBJECT_up_ref_count(X509_OBJECT *a);
 OPENSSL_EXPORT void X509_OBJECT_free_contents(X509_OBJECT *a);
 OPENSSL_EXPORT int X509_OBJECT_get_type(const X509_OBJECT *a);
 OPENSSL_EXPORT X509 *X509_OBJECT_get0_X509(const X509_OBJECT *a);
-OPENSSL_EXPORT X509_STORE *X509_STORE_new(void );
+OPENSSL_EXPORT X509_STORE *X509_STORE_new(void);
 OPENSSL_EXPORT int X509_STORE_up_ref(X509_STORE *store);
 OPENSSL_EXPORT void X509_STORE_free(X509_STORE *v);
 
 OPENSSL_EXPORT STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *st);
-OPENSSL_EXPORT STACK_OF(X509)* X509_STORE_get1_certs(X509_STORE_CTX *st, X509_NAME *nm);
-OPENSSL_EXPORT STACK_OF(X509_CRL)* X509_STORE_get1_crls(X509_STORE_CTX *st, X509_NAME *nm);
+OPENSSL_EXPORT STACK_OF(X509) *X509_STORE_get1_certs(X509_STORE_CTX *st,
+                                                     X509_NAME *nm);
+OPENSSL_EXPORT STACK_OF(X509_CRL) *X509_STORE_get1_crls(X509_STORE_CTX *st,
+                                                        X509_NAME *nm);
 OPENSSL_EXPORT int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags);
 OPENSSL_EXPORT int X509_STORE_set_purpose(X509_STORE *ctx, int purpose);
 OPENSSL_EXPORT int X509_STORE_set_trust(X509_STORE *ctx, int trust);
-OPENSSL_EXPORT int X509_STORE_set1_param(X509_STORE *ctx, X509_VERIFY_PARAM *pm);
+OPENSSL_EXPORT int X509_STORE_set1_param(X509_STORE *ctx,
+                                         X509_VERIFY_PARAM *pm);
 OPENSSL_EXPORT X509_VERIFY_PARAM *X509_STORE_get0_param(X509_STORE *ctx);
-/* X509_STORE_set0_additional_untrusted sets a stack of additional, untrusted
- * certificates that are available for chain building. This function does not
- * take ownership of the stack. */
+// X509_STORE_set0_additional_untrusted sets a stack of additional, untrusted
+// certificates that are available for chain building. This function does not
+// take ownership of the stack.
 OPENSSL_EXPORT void X509_STORE_set0_additional_untrusted(
     X509_STORE *ctx, STACK_OF(X509) *untrusted);
 
@@ -514,19 +367,22 @@
 
 OPENSSL_EXPORT X509_STORE_CTX *X509_STORE_CTX_new(void);
 
-OPENSSL_EXPORT int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x);
+OPENSSL_EXPORT int X509_STORE_CTX_get1_issuer(X509 **issuer,
+                                              X509_STORE_CTX *ctx, X509 *x);
 
 OPENSSL_EXPORT void X509_STORE_CTX_zero(X509_STORE_CTX *ctx);
 OPENSSL_EXPORT void X509_STORE_CTX_free(X509_STORE_CTX *ctx);
 OPENSSL_EXPORT int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store,
-			 X509 *x509, STACK_OF(X509) *chain);
-OPENSSL_EXPORT void X509_STORE_CTX_trusted_stack(X509_STORE_CTX *ctx, STACK_OF(X509) *sk);
+                                       X509 *x509, STACK_OF(X509) *chain);
+OPENSSL_EXPORT void X509_STORE_CTX_trusted_stack(X509_STORE_CTX *ctx,
+                                                 STACK_OF(X509) *sk);
 OPENSSL_EXPORT void X509_STORE_CTX_cleanup(X509_STORE_CTX *ctx);
 
 OPENSSL_EXPORT X509_STORE *X509_STORE_CTX_get0_store(X509_STORE_CTX *ctx);
 OPENSSL_EXPORT X509 *X509_STORE_CTX_get0_cert(X509_STORE_CTX *ctx);
 
-OPENSSL_EXPORT X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m);
+OPENSSL_EXPORT X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v,
+                                                  X509_LOOKUP_METHOD *m);
 
 OPENSSL_EXPORT X509_LOOKUP_METHOD *X509_LOOKUP_hash_dir(void);
 OPENSSL_EXPORT X509_LOOKUP_METHOD *X509_LOOKUP_file(void);
@@ -534,148 +390,182 @@
 OPENSSL_EXPORT int X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
 OPENSSL_EXPORT int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x);
 
-OPENSSL_EXPORT int X509_STORE_get_by_subject(X509_STORE_CTX *vs,int type,X509_NAME *name,
-	X509_OBJECT *ret);
+OPENSSL_EXPORT int X509_STORE_get_by_subject(X509_STORE_CTX *vs, int type,
+                                             X509_NAME *name, X509_OBJECT *ret);
 
 OPENSSL_EXPORT int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
-	long argl, char **ret);
+                                    long argl, char **ret);
 
 #ifndef OPENSSL_NO_STDIO
-OPENSSL_EXPORT int X509_load_cert_file(X509_LOOKUP *ctx, const char *file, int type);
-OPENSSL_EXPORT int X509_load_crl_file(X509_LOOKUP *ctx, const char *file, int type);
-OPENSSL_EXPORT int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file, int type);
+OPENSSL_EXPORT int X509_load_cert_file(X509_LOOKUP *ctx, const char *file,
+                                       int type);
+OPENSSL_EXPORT int X509_load_crl_file(X509_LOOKUP *ctx, const char *file,
+                                      int type);
+OPENSSL_EXPORT int X509_load_cert_crl_file(X509_LOOKUP *ctx, const char *file,
+                                           int type);
 #endif
 
 
 OPENSSL_EXPORT X509_LOOKUP *X509_LOOKUP_new(X509_LOOKUP_METHOD *method);
 OPENSSL_EXPORT void X509_LOOKUP_free(X509_LOOKUP *ctx);
 OPENSSL_EXPORT int X509_LOOKUP_init(X509_LOOKUP *ctx);
-OPENSSL_EXPORT int X509_LOOKUP_by_subject(X509_LOOKUP *ctx, int type, X509_NAME *name,
-	X509_OBJECT *ret);
-OPENSSL_EXPORT int X509_LOOKUP_by_issuer_serial(X509_LOOKUP *ctx, int type, X509_NAME *name,
-	ASN1_INTEGER *serial, X509_OBJECT *ret);
+OPENSSL_EXPORT int X509_LOOKUP_by_subject(X509_LOOKUP *ctx, int type,
+                                          X509_NAME *name, X509_OBJECT *ret);
+OPENSSL_EXPORT int X509_LOOKUP_by_issuer_serial(X509_LOOKUP *ctx, int type,
+                                                X509_NAME *name,
+                                                ASN1_INTEGER *serial,
+                                                X509_OBJECT *ret);
 OPENSSL_EXPORT int X509_LOOKUP_by_fingerprint(X509_LOOKUP *ctx, int type,
-	unsigned char *bytes, int len, X509_OBJECT *ret);
+                                              unsigned char *bytes, int len,
+                                              X509_OBJECT *ret);
 OPENSSL_EXPORT int X509_LOOKUP_by_alias(X509_LOOKUP *ctx, int type, char *str,
-	int len, X509_OBJECT *ret);
+                                        int len, X509_OBJECT *ret);
 OPENSSL_EXPORT int X509_LOOKUP_shutdown(X509_LOOKUP *ctx);
 
 #ifndef OPENSSL_NO_STDIO
-OPENSSL_EXPORT int	X509_STORE_load_locations (X509_STORE *ctx,
-		const char *file, const char *dir);
-OPENSSL_EXPORT int	X509_STORE_set_default_paths(X509_STORE *ctx);
+OPENSSL_EXPORT int X509_STORE_load_locations(X509_STORE *ctx, const char *file,
+                                             const char *dir);
+OPENSSL_EXPORT int X509_STORE_set_default_paths(X509_STORE *ctx);
 #endif
 
-OPENSSL_EXPORT int X509_STORE_CTX_get_ex_new_index(long argl, void *argp, CRYPTO_EX_unused *unused,
-	CRYPTO_EX_dup *dup_unused, CRYPTO_EX_free *free_func);
-OPENSSL_EXPORT int	X509_STORE_CTX_set_ex_data(X509_STORE_CTX *ctx,int idx,void *data);
-OPENSSL_EXPORT void *	X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx,int idx);
-OPENSSL_EXPORT int	X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
-OPENSSL_EXPORT void	X509_STORE_CTX_set_error(X509_STORE_CTX *ctx,int s);
-OPENSSL_EXPORT int	X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx);
-OPENSSL_EXPORT X509 *	X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx);
+OPENSSL_EXPORT int X509_STORE_CTX_get_ex_new_index(long argl, void *argp,
+                                                   CRYPTO_EX_unused *unused,
+                                                   CRYPTO_EX_dup *dup_unused,
+                                                   CRYPTO_EX_free *free_func);
+OPENSSL_EXPORT int X509_STORE_CTX_set_ex_data(X509_STORE_CTX *ctx, int idx,
+                                              void *data);
+OPENSSL_EXPORT void *X509_STORE_CTX_get_ex_data(X509_STORE_CTX *ctx, int idx);
+OPENSSL_EXPORT int X509_STORE_CTX_get_error(X509_STORE_CTX *ctx);
+OPENSSL_EXPORT void X509_STORE_CTX_set_error(X509_STORE_CTX *ctx, int s);
+OPENSSL_EXPORT int X509_STORE_CTX_get_error_depth(X509_STORE_CTX *ctx);
+OPENSSL_EXPORT X509 *X509_STORE_CTX_get_current_cert(X509_STORE_CTX *ctx);
 OPENSSL_EXPORT X509 *X509_STORE_CTX_get0_current_issuer(X509_STORE_CTX *ctx);
 OPENSSL_EXPORT X509_CRL *X509_STORE_CTX_get0_current_crl(X509_STORE_CTX *ctx);
-OPENSSL_EXPORT X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(X509_STORE_CTX *ctx);
+OPENSSL_EXPORT X509_STORE_CTX *X509_STORE_CTX_get0_parent_ctx(
+    X509_STORE_CTX *ctx);
 OPENSSL_EXPORT STACK_OF(X509) *X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);
 OPENSSL_EXPORT STACK_OF(X509) *X509_STORE_CTX_get0_chain(X509_STORE_CTX *ctx);
 OPENSSL_EXPORT STACK_OF(X509) *X509_STORE_CTX_get1_chain(X509_STORE_CTX *ctx);
-OPENSSL_EXPORT void	X509_STORE_CTX_set_cert(X509_STORE_CTX *c,X509 *x);
-OPENSSL_EXPORT void	X509_STORE_CTX_set_chain(X509_STORE_CTX *c,STACK_OF(X509) *sk);
-OPENSSL_EXPORT STACK_OF(X509) *
-    X509_STORE_CTX_get0_untrusted(X509_STORE_CTX *ctx);
-OPENSSL_EXPORT void	X509_STORE_CTX_set0_crls(X509_STORE_CTX *c,STACK_OF(X509_CRL) *sk);
+OPENSSL_EXPORT void X509_STORE_CTX_set_cert(X509_STORE_CTX *c, X509 *x);
+OPENSSL_EXPORT void X509_STORE_CTX_set_chain(X509_STORE_CTX *c,
+                                             STACK_OF(X509) *sk);
+OPENSSL_EXPORT STACK_OF(X509) *X509_STORE_CTX_get0_untrusted(
+    X509_STORE_CTX *ctx);
+OPENSSL_EXPORT void X509_STORE_CTX_set0_crls(X509_STORE_CTX *c,
+                                             STACK_OF(X509_CRL) *sk);
 OPENSSL_EXPORT int X509_STORE_CTX_set_purpose(X509_STORE_CTX *ctx, int purpose);
 OPENSSL_EXPORT int X509_STORE_CTX_set_trust(X509_STORE_CTX *ctx, int trust);
-OPENSSL_EXPORT int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx, int def_purpose,
-				int purpose, int trust);
-OPENSSL_EXPORT void X509_STORE_CTX_set_flags(X509_STORE_CTX *ctx, unsigned long flags);
-OPENSSL_EXPORT void X509_STORE_CTX_set_time(X509_STORE_CTX *ctx, unsigned long flags,
-								time_t t);
-OPENSSL_EXPORT void X509_STORE_CTX_set_verify_cb(X509_STORE_CTX *ctx,
-				  int (*verify_cb)(int, X509_STORE_CTX *));
-  
-OPENSSL_EXPORT X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(X509_STORE_CTX *ctx);
+OPENSSL_EXPORT int X509_STORE_CTX_purpose_inherit(X509_STORE_CTX *ctx,
+                                                  int def_purpose, int purpose,
+                                                  int trust);
+OPENSSL_EXPORT void X509_STORE_CTX_set_flags(X509_STORE_CTX *ctx,
+                                             unsigned long flags);
+OPENSSL_EXPORT void X509_STORE_CTX_set_time(X509_STORE_CTX *ctx,
+                                            unsigned long flags, time_t t);
+OPENSSL_EXPORT void X509_STORE_CTX_set_verify_cb(
+    X509_STORE_CTX *ctx, int (*verify_cb)(int, X509_STORE_CTX *));
+
+OPENSSL_EXPORT X509_POLICY_TREE *X509_STORE_CTX_get0_policy_tree(
+    X509_STORE_CTX *ctx);
 OPENSSL_EXPORT int X509_STORE_CTX_get_explicit_policy(X509_STORE_CTX *ctx);
 
-OPENSSL_EXPORT X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(X509_STORE_CTX *ctx);
-OPENSSL_EXPORT void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param);
-OPENSSL_EXPORT int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx, const char *name);
+OPENSSL_EXPORT X509_VERIFY_PARAM *X509_STORE_CTX_get0_param(
+    X509_STORE_CTX *ctx);
+OPENSSL_EXPORT void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx,
+                                              X509_VERIFY_PARAM *param);
+OPENSSL_EXPORT int X509_STORE_CTX_set_default(X509_STORE_CTX *ctx,
+                                              const char *name);
 
-/* X509_VERIFY_PARAM functions */
+// X509_VERIFY_PARAM functions
 
 OPENSSL_EXPORT X509_VERIFY_PARAM *X509_VERIFY_PARAM_new(void);
 OPENSSL_EXPORT void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param);
 OPENSSL_EXPORT int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *to,
-						const X509_VERIFY_PARAM *from);
-OPENSSL_EXPORT int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to, 
-						const X509_VERIFY_PARAM *from);
-OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_name(X509_VERIFY_PARAM *param, const char *name);
-OPENSSL_EXPORT int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param, unsigned long flags);
+                                             const X509_VERIFY_PARAM *from);
+OPENSSL_EXPORT int X509_VERIFY_PARAM_set1(X509_VERIFY_PARAM *to,
+                                          const X509_VERIFY_PARAM *from);
+OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_name(X509_VERIFY_PARAM *param,
+                                               const char *name);
+OPENSSL_EXPORT int X509_VERIFY_PARAM_set_flags(X509_VERIFY_PARAM *param,
+                                               unsigned long flags);
 OPENSSL_EXPORT int X509_VERIFY_PARAM_clear_flags(X509_VERIFY_PARAM *param,
-							unsigned long flags);
-OPENSSL_EXPORT unsigned long X509_VERIFY_PARAM_get_flags(X509_VERIFY_PARAM *param);
-OPENSSL_EXPORT int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param, int purpose);
-OPENSSL_EXPORT int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param, int trust);
-OPENSSL_EXPORT void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param, int depth);
-OPENSSL_EXPORT void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param, time_t t);
+                                                 unsigned long flags);
+OPENSSL_EXPORT unsigned long X509_VERIFY_PARAM_get_flags(
+    X509_VERIFY_PARAM *param);
+OPENSSL_EXPORT int X509_VERIFY_PARAM_set_purpose(X509_VERIFY_PARAM *param,
+                                                 int purpose);
+OPENSSL_EXPORT int X509_VERIFY_PARAM_set_trust(X509_VERIFY_PARAM *param,
+                                               int trust);
+OPENSSL_EXPORT void X509_VERIFY_PARAM_set_depth(X509_VERIFY_PARAM *param,
+                                                int depth);
+OPENSSL_EXPORT void X509_VERIFY_PARAM_set_time(X509_VERIFY_PARAM *param,
+                                               time_t t);
 OPENSSL_EXPORT int X509_VERIFY_PARAM_add0_policy(X509_VERIFY_PARAM *param,
-						ASN1_OBJECT *policy);
-OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_policies(X509_VERIFY_PARAM *param, 
-					STACK_OF(ASN1_OBJECT) *policies);
+                                                 ASN1_OBJECT *policy);
+OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_policies(
+    X509_VERIFY_PARAM *param, STACK_OF(ASN1_OBJECT) *policies);
 
 OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_host(X509_VERIFY_PARAM *param,
-				const char *name, size_t namelen);
+                                               const char *name,
+                                               size_t namelen);
 OPENSSL_EXPORT int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param,
-					       const char *name,
-					       size_t namelen);
+                                               const char *name,
+                                               size_t namelen);
 OPENSSL_EXPORT void X509_VERIFY_PARAM_set_hostflags(X509_VERIFY_PARAM *param,
-					unsigned int flags);
+                                                    unsigned int flags);
 OPENSSL_EXPORT char *X509_VERIFY_PARAM_get0_peername(X509_VERIFY_PARAM *);
 OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_email(X509_VERIFY_PARAM *param,
-				const char *email, size_t emaillen);
+                                                const char *email,
+                                                size_t emaillen);
 OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param,
-					const unsigned char *ip, size_t iplen);
-OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc);
+                                             const unsigned char *ip,
+                                             size_t iplen);
+OPENSSL_EXPORT int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param,
+                                                 const char *ipasc);
 
 OPENSSL_EXPORT int X509_VERIFY_PARAM_get_depth(const X509_VERIFY_PARAM *param);
-OPENSSL_EXPORT const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param);
+OPENSSL_EXPORT const char *X509_VERIFY_PARAM_get0_name(
+    const X509_VERIFY_PARAM *param);
 
 OPENSSL_EXPORT int X509_VERIFY_PARAM_add0_table(X509_VERIFY_PARAM *param);
 OPENSSL_EXPORT int X509_VERIFY_PARAM_get_count(void);
 OPENSSL_EXPORT const X509_VERIFY_PARAM *X509_VERIFY_PARAM_get0(int id);
-OPENSSL_EXPORT const X509_VERIFY_PARAM *X509_VERIFY_PARAM_lookup(const char *name);
+OPENSSL_EXPORT const X509_VERIFY_PARAM *X509_VERIFY_PARAM_lookup(
+    const char *name);
 OPENSSL_EXPORT void X509_VERIFY_PARAM_table_cleanup(void);
 
-OPENSSL_EXPORT int X509_policy_check(X509_POLICY_TREE **ptree, int *pexplicit_policy,
-			STACK_OF(X509) *certs,
-			STACK_OF(ASN1_OBJECT) *policy_oids,
-			unsigned int flags);
+OPENSSL_EXPORT int X509_policy_check(X509_POLICY_TREE **ptree,
+                                     int *pexplicit_policy,
+                                     STACK_OF(X509) *certs,
+                                     STACK_OF(ASN1_OBJECT) *policy_oids,
+                                     unsigned int flags);
 
 OPENSSL_EXPORT void X509_policy_tree_free(X509_POLICY_TREE *tree);
 
 OPENSSL_EXPORT int X509_policy_tree_level_count(const X509_POLICY_TREE *tree);
-OPENSSL_EXPORT X509_POLICY_LEVEL *
-	X509_policy_tree_get0_level(const X509_POLICY_TREE *tree, int i);
+OPENSSL_EXPORT X509_POLICY_LEVEL *X509_policy_tree_get0_level(
+    const X509_POLICY_TREE *tree, int i);
 
-OPENSSL_EXPORT STACK_OF(X509_POLICY_NODE) *
-	X509_policy_tree_get0_policies(const X509_POLICY_TREE *tree);
+OPENSSL_EXPORT STACK_OF(X509_POLICY_NODE) *X509_policy_tree_get0_policies(
+    const X509_POLICY_TREE *tree);
 
-OPENSSL_EXPORT STACK_OF(X509_POLICY_NODE) *
-	X509_policy_tree_get0_user_policies(const X509_POLICY_TREE *tree);
+OPENSSL_EXPORT STACK_OF(X509_POLICY_NODE) *X509_policy_tree_get0_user_policies(
+    const X509_POLICY_TREE *tree);
 
 OPENSSL_EXPORT int X509_policy_level_node_count(X509_POLICY_LEVEL *level);
 
-OPENSSL_EXPORT X509_POLICY_NODE *X509_policy_level_get0_node(X509_POLICY_LEVEL *level, int i);
+OPENSSL_EXPORT X509_POLICY_NODE *X509_policy_level_get0_node(
+    X509_POLICY_LEVEL *level, int i);
 
-OPENSSL_EXPORT const ASN1_OBJECT *X509_policy_node_get0_policy(const X509_POLICY_NODE *node);
+OPENSSL_EXPORT const ASN1_OBJECT *X509_policy_node_get0_policy(
+    const X509_POLICY_NODE *node);
 
-OPENSSL_EXPORT STACK_OF(POLICYQUALINFO) *
-	X509_policy_node_get0_qualifiers(const X509_POLICY_NODE *node);
-OPENSSL_EXPORT const X509_POLICY_NODE *
-	X509_policy_node_get0_parent(const X509_POLICY_NODE *node);
+OPENSSL_EXPORT STACK_OF(POLICYQUALINFO) *X509_policy_node_get0_qualifiers(
+    const X509_POLICY_NODE *node);
+OPENSSL_EXPORT const X509_POLICY_NODE *X509_policy_node_get0_parent(
+    const X509_POLICY_NODE *node);
 
-#ifdef  __cplusplus
+#ifdef __cplusplus
 }
 #endif
 #endif
diff --git a/deps/boringssl/src/include/openssl/x509v3.h b/deps/boringssl/src/include/openssl/x509v3.h
index 2c9ba73..8e4a511 100644
--- a/deps/boringssl/src/include/openssl/x509v3.h
+++ b/deps/boringssl/src/include/openssl/x509v3.h
@@ -154,8 +154,6 @@
 #define X509V3_EXT_CTX_DEP 0x2
 #define X509V3_EXT_MULTILINE 0x4
 
-typedef BIT_STRING_BITNAME ENUMERATED_NAMES;
-
 struct BASIC_CONSTRAINTS_st {
   int ca;
   ASN1_INTEGER *pathlen;
@@ -206,7 +204,6 @@
 } GENERAL_NAME;
 
 DEFINE_STACK_OF(GENERAL_NAME)
-DECLARE_ASN1_SET_OF(GENERAL_NAME)
 
 typedef STACK_OF(GENERAL_NAME) GENERAL_NAMES;
 
@@ -218,7 +215,6 @@
 } ACCESS_DESCRIPTION;
 
 DEFINE_STACK_OF(ACCESS_DESCRIPTION)
-DECLARE_ASN1_SET_OF(ACCESS_DESCRIPTION)
 
 typedef STACK_OF(ACCESS_DESCRIPTION) AUTHORITY_INFO_ACCESS;
 
@@ -258,7 +254,6 @@
 typedef STACK_OF(DIST_POINT) CRL_DIST_POINTS;
 
 DEFINE_STACK_OF(DIST_POINT)
-DECLARE_ASN1_SET_OF(DIST_POINT)
 
 struct AUTHORITY_KEYID_st {
   ASN1_OCTET_STRING *keyid;
@@ -286,7 +281,6 @@
 } POLICYQUALINFO;
 
 DEFINE_STACK_OF(POLICYQUALINFO)
-DECLARE_ASN1_SET_OF(POLICYQUALINFO)
 
 typedef struct POLICYINFO_st {
   ASN1_OBJECT *policyid;
@@ -296,7 +290,6 @@
 typedef STACK_OF(POLICYINFO) CERTIFICATEPOLICIES;
 
 DEFINE_STACK_OF(POLICYINFO)
-DECLARE_ASN1_SET_OF(POLICYINFO)
 
 typedef struct POLICY_MAPPING_st {
   ASN1_OBJECT *issuerDomainPolicy;
@@ -490,12 +483,30 @@
     X509V3_EXT_METHOD *method, ASN1_BIT_STRING *bits,
     STACK_OF(CONF_VALUE) *extlist);
 
+// i2v_GENERAL_NAME serializes |gen| as a |CONF_VALUE|. If |ret| is non-NULL, it
+// appends the value to |ret| and returns |ret| on success or NULL on error. If
+// it returns NULL, the caller is still responsible for freeing |ret|. If |ret|
+// is NULL, it returns a newly-allocated |STACK_OF(CONF_VALUE)| containing the
+// result. |method| is ignored.
+//
+// Do not use this function. This is an internal implementation detail of the
+// human-readable print functions. If extracting a SAN list from a certificate,
+// look at |gen| directly.
 OPENSSL_EXPORT STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(
     X509V3_EXT_METHOD *method, GENERAL_NAME *gen, STACK_OF(CONF_VALUE) *ret);
 OPENSSL_EXPORT int GENERAL_NAME_print(BIO *out, GENERAL_NAME *gen);
 
 DECLARE_ASN1_FUNCTIONS(GENERAL_NAMES)
 
+// i2v_GENERAL_NAMES serializes |gen| as a list of |CONF_VALUE|s. If |ret| is
+// non-NULL, it appends the values to |ret| and returns |ret| on success or NULL
+// on error. If it returns NULL, the caller is still responsible for freeing
+// |ret|. If |ret| is NULL, it returns a newly-allocated |STACK_OF(CONF_VALUE)|
+// containing the results. |method| is ignored.
+//
+// Do not use this function. This is an internal implementation detail of the
+// human-readable print functions. If extracting a SAN list from a certificate,
+// look at |gen| directly.
 OPENSSL_EXPORT STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES(
     X509V3_EXT_METHOD *method, GENERAL_NAMES *gen,
     STACK_OF(CONF_VALUE) *extlist);
@@ -609,15 +620,35 @@
 OPENSSL_EXPORT void X509V3_set_ctx(X509V3_CTX *ctx, X509 *issuer, X509 *subject,
                                    X509_REQ *req, X509_CRL *crl, int flags);
 
+// X509V3_add_value appends a |CONF_VALUE| containing |name| and |value| to
+// |*extlist|. It returns one on success and zero on error. If |*extlist| is
+// NULL, it sets |*extlist| to a newly-allocated |STACK_OF(CONF_VALUE)|
+// containing the result. Either |name| or |value| may be NULL to omit the
+// field.
+//
+// On failure, if |*extlist| was NULL, |*extlist| will remain NULL when the
+// function returns.
 OPENSSL_EXPORT int X509V3_add_value(const char *name, const char *value,
                                     STACK_OF(CONF_VALUE) **extlist);
+
+// X509V3_add_value_uchar behaves like |X509V3_add_value| but takes an
+// |unsigned char| pointer.
 OPENSSL_EXPORT int X509V3_add_value_uchar(const char *name,
                                           const unsigned char *value,
                                           STACK_OF(CONF_VALUE) **extlist);
+
+// X509V3_add_value_bool behaves like |X509V3_add_value| but stores the value
+// "TRUE" if |asn1_bool| is non-zero and "FALSE" otherwise.
 OPENSSL_EXPORT int X509V3_add_value_bool(const char *name, int asn1_bool,
                                          STACK_OF(CONF_VALUE) **extlist);
-OPENSSL_EXPORT int X509V3_add_value_int(const char *name, ASN1_INTEGER *aint,
+
+// X509V3_add_value_bool behaves like |X509V3_add_value| but stores a string
+// representation of |aint|. Note this string representation may be decimal or
+// hexadecimal, depending on the size of |aint|.
+OPENSSL_EXPORT int X509V3_add_value_int(const char *name,
+                                        const ASN1_INTEGER *aint,
                                         STACK_OF(CONF_VALUE) **extlist);
+
 OPENSSL_EXPORT char *i2s_ASN1_INTEGER(X509V3_EXT_METHOD *meth,
                                       const ASN1_INTEGER *aint);
 OPENSSL_EXPORT ASN1_INTEGER *s2i_ASN1_INTEGER(X509V3_EXT_METHOD *meth,
@@ -664,7 +695,7 @@
 // extension, or -1 if not found. If |out_idx| is non-NULL, duplicate extensions
 // are not treated as an error. Callers, however, should not rely on this
 // behavior as it may be removed in the future. Duplicate extensions are
-// forbidden in RFC5280.
+// forbidden in RFC 5280.
 //
 // WARNING: This function is difficult to use correctly. Callers should pass a
 // non-NULL |out_critical| and check both the return value and |*out_critical|
@@ -794,7 +825,7 @@
 OPENSSL_EXPORT uint32_t X509_get_extended_key_usage(X509 *x);
 
 // X509_get0_subject_key_id returns |x509|'s subject key identifier, if present.
-// (See RFC5280, section 4.2.1.2.) It returns NULL if the extension is not
+// (See RFC 5280, section 4.2.1.2.) It returns NULL if the extension is not
 // present or if some extension in |x509| was invalid.
 //
 // Note that decoding an |X509| object will not check for invalid extensions. To
@@ -803,7 +834,7 @@
 OPENSSL_EXPORT const ASN1_OCTET_STRING *X509_get0_subject_key_id(X509 *x509);
 
 // X509_get0_authority_key_id returns keyIdentifier of |x509|'s authority key
-// identifier, if the extension and field are present. (See RFC5280,
+// identifier, if the extension and field are present. (See RFC 5280,
 // section 4.2.1.1.) It returns NULL if the extension is not present, if it is
 // present but lacks a keyIdentifier field, or if some extension in |x509| was
 // invalid.
@@ -815,7 +846,7 @@
 
 // X509_get0_authority_issuer returns the authorityCertIssuer of |x509|'s
 // authority key identifier, if the extension and field are present. (See
-// RFC5280, section 4.2.1.1.) It returns NULL if the extension is not present,
+// RFC 5280, section 4.2.1.1.) It returns NULL if the extension is not present,
 // if it is present but lacks a authorityCertIssuer field, or if some extension
 // in |x509| was invalid.
 //
@@ -826,7 +857,7 @@
 
 // X509_get0_authority_serial returns the authorityCertSerialNumber of |x509|'s
 // authority key identifier, if the extension and field are present. (See
-// RFC5280, section 4.2.1.1.) It returns NULL if the extension is not present,
+// RFC 5280, section 4.2.1.1.) It returns NULL if the extension is not present,
 // if it is present but lacks a authorityCertSerialNumber field, or if some
 // extension in |x509| was invalid.
 //
@@ -884,7 +915,6 @@
 
 OPENSSL_EXPORT ASN1_OCTET_STRING *a2i_IPADDRESS(const char *ipasc);
 OPENSSL_EXPORT ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc);
-OPENSSL_EXPORT int a2i_ipadd(unsigned char *ipout, const char *ipasc);
 OPENSSL_EXPORT int X509V3_NAME_from_section(X509_NAME *nm,
                                             STACK_OF(CONF_VALUE) *dn_sk,
                                             unsigned long chtype);
@@ -908,8 +938,13 @@
 BORINGSSL_MAKE_DELETER(ACCESS_DESCRIPTION, ACCESS_DESCRIPTION_free)
 BORINGSSL_MAKE_DELETER(AUTHORITY_KEYID, AUTHORITY_KEYID_free)
 BORINGSSL_MAKE_DELETER(BASIC_CONSTRAINTS, BASIC_CONSTRAINTS_free)
+// TODO(davidben): Move this to conf.h and rename to CONF_VALUE_free.
+BORINGSSL_MAKE_DELETER(CONF_VALUE, X509V3_conf_free)
 BORINGSSL_MAKE_DELETER(DIST_POINT, DIST_POINT_free)
 BORINGSSL_MAKE_DELETER(GENERAL_NAME, GENERAL_NAME_free)
+BORINGSSL_MAKE_DELETER(GENERAL_SUBTREE, GENERAL_SUBTREE_free)
+BORINGSSL_MAKE_DELETER(NAME_CONSTRAINTS, NAME_CONSTRAINTS_free)
+BORINGSSL_MAKE_DELETER(POLICY_MAPPING, POLICY_MAPPING_free)
 BORINGSSL_MAKE_DELETER(POLICYINFO, POLICYINFO_free)
 
 BSSL_NAMESPACE_END
@@ -980,5 +1015,6 @@
 #define X509V3_R_UNSUPPORTED_OPTION 160
 #define X509V3_R_UNSUPPORTED_TYPE 161
 #define X509V3_R_USER_TOO_LONG 162
+#define X509V3_R_INVALID_VALUE 163
 
 #endif
diff --git a/deps/boringssl/src/sources.cmake b/deps/boringssl/src/sources.cmake
index 9ac55d5..ef9cac1 100644
--- a/deps/boringssl/src/sources.cmake
+++ b/deps/boringssl/src/sources.cmake
@@ -9,7 +9,6 @@
   crypto/blake2/blake2b256_tests.txt
   crypto/cipher_extra/test/aes_128_cbc_sha1_tls_implicit_iv_tests.txt
   crypto/cipher_extra/test/aes_128_cbc_sha1_tls_tests.txt
-  crypto/cipher_extra/test/aes_128_cbc_sha256_tls_tests.txt
   crypto/cipher_extra/test/aes_128_ccm_bluetooth_tests.txt
   crypto/cipher_extra/test/aes_128_ccm_bluetooth_8_tests.txt
   crypto/cipher_extra/test/aes_128_ctr_hmac_sha256.txt
@@ -19,8 +18,6 @@
   crypto/cipher_extra/test/aes_192_gcm_tests.txt
   crypto/cipher_extra/test/aes_256_cbc_sha1_tls_implicit_iv_tests.txt
   crypto/cipher_extra/test/aes_256_cbc_sha1_tls_tests.txt
-  crypto/cipher_extra/test/aes_256_cbc_sha256_tls_tests.txt
-  crypto/cipher_extra/test/aes_256_cbc_sha384_tls_tests.txt
   crypto/cipher_extra/test/aes_256_ctr_hmac_sha256.txt
   crypto/cipher_extra/test/aes_256_gcm_randnonce_tests.txt
   crypto/cipher_extra/test/aes_256_gcm_siv_tests.txt
@@ -59,6 +56,15 @@
   crypto/fipsmodule/rand/ctrdrbg_vectors.txt
   crypto/hmac_extra/hmac_tests.txt
   crypto/hpke/hpke_test_vectors.txt
+  crypto/pkcs8/test/empty_password.p12
+  crypto/pkcs8/test/no_encryption.p12
+  crypto/pkcs8/test/nss.p12
+  crypto/pkcs8/test/null_password.p12
+  crypto/pkcs8/test/openssl.p12
+  crypto/pkcs8/test/pbes2_sha1.p12
+  crypto/pkcs8/test/pbes2_sha256.p12
+  crypto/pkcs8/test/unicode_password.p12
+  crypto/pkcs8/test/windows.p12
   crypto/poly1305/poly1305_tests.txt
   crypto/siphash/siphash_tests.txt
   crypto/x509/test/basic_constraints_ca.pem
diff --git a/deps/boringssl/src/ssl/CMakeLists.txt b/deps/boringssl/src/ssl/CMakeLists.txt
index 0fb532e..4f4abf8 100644
--- a/deps/boringssl/src/ssl/CMakeLists.txt
+++ b/deps/boringssl/src/ssl/CMakeLists.txt
@@ -10,6 +10,8 @@
   d1_srtp.cc
   dtls_method.cc
   dtls_record.cc
+  encrypted_client_hello.cc
+  extensions.cc
   handoff.cc
   handshake.cc
   handshake_client.cc
@@ -32,7 +34,6 @@
   ssl_versions.cc
   ssl_x509.cc
   t1_enc.cc
-  t1_lib.cc
   tls_method.cc
   tls_record.cc
   tls13_both.cc
diff --git a/deps/boringssl/src/ssl/d1_both.cc b/deps/boringssl/src/ssl/d1_both.cc
index c7f85c7..f8c04b7 100644
--- a/deps/boringssl/src/ssl/d1_both.cc
+++ b/deps/boringssl/src/ssl/d1_both.cc
@@ -503,7 +503,7 @@
   ssl->d1->flight_has_reply = false;
 }
 
-bool dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
+bool dtls1_init_message(const SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
   // Pick a modest size hint to save most of the |realloc| calls.
   if (!CBB_init(cbb, 64) ||
       !CBB_add_u8(cbb, type) ||
@@ -517,7 +517,7 @@
   return true;
 }
 
-bool dtls1_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg) {
+bool dtls1_finish_message(const SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg) {
   if (!CBBFinishArray(cbb, out_msg) ||
       out_msg->size() < DTLS1_HM_HEADER_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -771,13 +771,16 @@
     return -1;
   }
 
+  if (ssl->wbio == nullptr) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET);
+    return -1;
+  }
+
   dtls1_update_mtu(ssl);
 
-  int ret = -1;
-  uint8_t *packet = (uint8_t *)OPENSSL_malloc(ssl->d1->mtu);
-  if (packet == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto err;
+  Array<uint8_t> packet;
+  if (!packet.Init(ssl->d1->mtu)) {
+    return -1;
   }
 
   while (ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len) {
@@ -785,31 +788,26 @@
     uint32_t old_offset = ssl->d1->outgoing_offset;
 
     size_t packet_len;
-    if (!seal_next_packet(ssl, packet, &packet_len, ssl->d1->mtu)) {
-      goto err;
+    if (!seal_next_packet(ssl, packet.data(), &packet_len, packet.size())) {
+      return -1;
     }
 
-    int bio_ret = BIO_write(ssl->wbio.get(), packet, packet_len);
+    int bio_ret = BIO_write(ssl->wbio.get(), packet.data(), packet_len);
     if (bio_ret <= 0) {
       // Retry this packet the next time around.
       ssl->d1->outgoing_written = old_written;
       ssl->d1->outgoing_offset = old_offset;
       ssl->s3->rwstate = SSL_ERROR_WANT_WRITE;
-      ret = bio_ret;
-      goto err;
+      return bio_ret;
     }
   }
 
   if (BIO_flush(ssl->wbio.get()) <= 0) {
     ssl->s3->rwstate = SSL_ERROR_WANT_WRITE;
-    goto err;
+    return -1;
   }
 
-  ret = 1;
-
-err:
-  OPENSSL_free(packet);
-  return ret;
+  return 1;
 }
 
 int dtls1_flush_flight(SSL *ssl) {
diff --git a/deps/boringssl/src/ssl/d1_srtp.cc b/deps/boringssl/src/ssl/d1_srtp.cc
index 96d7d51..12c8075 100644
--- a/deps/boringssl/src/ssl/d1_srtp.cc
+++ b/deps/boringssl/src/ssl/d1_srtp.cc
@@ -202,7 +202,7 @@
          ssl_ctx_make_profiles(profiles, &ssl->config->srtp_profiles);
 }
 
-STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(SSL *ssl) {
+const STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(const SSL *ssl) {
   if (ssl == nullptr) {
     return nullptr;
   }
diff --git a/deps/boringssl/src/ssl/encrypted_client_hello.cc b/deps/boringssl/src/ssl/encrypted_client_hello.cc
new file mode 100644
index 0000000..5192cc6
--- /dev/null
+++ b/deps/boringssl/src/ssl/encrypted_client_hello.cc
@@ -0,0 +1,1126 @@
+/* Copyright (c) 2021, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/ssl.h>
+
+#include <assert.h>
+#include <string.h>
+
+#include <algorithm>
+#include <utility>
+
+#include <openssl/aead.h>
+#include <openssl/bytestring.h>
+#include <openssl/curve25519.h>
+#include <openssl/err.h>
+#include <openssl/hkdf.h>
+#include <openssl/hpke.h>
+#include <openssl/rand.h>
+
+#include "internal.h"
+
+
+BSSL_NAMESPACE_BEGIN
+
+// ECH reuses the extension code point for the version number.
+static constexpr uint16_t kECHConfigVersion =
+    TLSEXT_TYPE_encrypted_client_hello;
+
+static const decltype(&EVP_hpke_aes_128_gcm) kSupportedAEADs[] = {
+    &EVP_hpke_aes_128_gcm,
+    &EVP_hpke_aes_256_gcm,
+    &EVP_hpke_chacha20_poly1305,
+};
+
+static const EVP_HPKE_AEAD *get_ech_aead(uint16_t aead_id) {
+  for (const auto aead_func : kSupportedAEADs) {
+    const EVP_HPKE_AEAD *aead = aead_func();
+    if (aead_id == EVP_HPKE_AEAD_id(aead)) {
+      return aead;
+    }
+  }
+  return nullptr;
+}
+
+// ssl_client_hello_write_without_extensions serializes |client_hello| into
+// |out|, omitting the length-prefixed extensions. It serializes individual
+// fields, starting with |client_hello->version|, and ignores the
+// |client_hello->client_hello| field. It returns true on success and false on
+// failure.
+static bool ssl_client_hello_write_without_extensions(
+    const SSL_CLIENT_HELLO *client_hello, CBB *out) {
+  CBB cbb;
+  if (!CBB_add_u16(out, client_hello->version) ||
+      !CBB_add_bytes(out, client_hello->random, client_hello->random_len) ||
+      !CBB_add_u8_length_prefixed(out, &cbb) ||
+      !CBB_add_bytes(&cbb, client_hello->session_id,
+                     client_hello->session_id_len) ||
+      !CBB_add_u16_length_prefixed(out, &cbb) ||
+      !CBB_add_bytes(&cbb, client_hello->cipher_suites,
+                     client_hello->cipher_suites_len) ||
+      !CBB_add_u8_length_prefixed(out, &cbb) ||
+      !CBB_add_bytes(&cbb, client_hello->compression_methods,
+                     client_hello->compression_methods_len) ||
+      !CBB_flush(out)) {
+    return false;
+  }
+  return true;
+}
+
+static bool is_valid_client_hello_inner(SSL *ssl, uint8_t *out_alert,
+                                        Span<const uint8_t> body) {
+  // See draft-ietf-tls-esni-13, section 7.1.
+  SSL_CLIENT_HELLO client_hello;
+  CBS extension;
+  if (!ssl_client_hello_init(ssl, &client_hello, body) ||
+      !ssl_client_hello_get_extension(&client_hello, &extension,
+                                      TLSEXT_TYPE_encrypted_client_hello) ||
+      CBS_len(&extension) != 1 ||  //
+      CBS_data(&extension)[0] != ECH_CLIENT_INNER ||
+      !ssl_client_hello_get_extension(&client_hello, &extension,
+                                      TLSEXT_TYPE_supported_versions)) {
+    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_CLIENT_HELLO_INNER);
+    return false;
+  }
+  // Parse supported_versions and reject TLS versions prior to TLS 1.3. Older
+  // versions are incompatible with ECH.
+  CBS versions;
+  if (!CBS_get_u8_length_prefixed(&extension, &versions) ||
+      CBS_len(&extension) != 0 ||  //
+      CBS_len(&versions) == 0) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return false;
+  }
+  while (CBS_len(&versions) != 0) {
+    uint16_t version;
+    if (!CBS_get_u16(&versions, &version)) {
+      *out_alert = SSL_AD_DECODE_ERROR;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      return false;
+    }
+    if (version == SSL3_VERSION || version == TLS1_VERSION ||
+        version == TLS1_1_VERSION || version == TLS1_2_VERSION ||
+        version == DTLS1_VERSION || version == DTLS1_2_VERSION) {
+      *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_CLIENT_HELLO_INNER);
+      return false;
+    }
+  }
+  return true;
+}
+
+bool ssl_decode_client_hello_inner(
+    SSL *ssl, uint8_t *out_alert, Array<uint8_t> *out_client_hello_inner,
+    Span<const uint8_t> encoded_client_hello_inner,
+    const SSL_CLIENT_HELLO *client_hello_outer) {
+  SSL_CLIENT_HELLO client_hello_inner;
+  CBS cbs = encoded_client_hello_inner;
+  if (!ssl_parse_client_hello_with_trailing_data(ssl, &cbs,
+                                                 &client_hello_inner)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return false;
+  }
+  // The remaining data is padding.
+  uint8_t padding;
+  while (CBS_get_u8(&cbs, &padding)) {
+    if (padding != 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+      return false;
+    }
+  }
+
+  // TLS 1.3 ClientHellos must have extensions, and EncodedClientHelloInners use
+  // ClientHelloOuter's session_id.
+  if (client_hello_inner.extensions_len == 0 ||
+      client_hello_inner.session_id_len != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return false;
+  }
+  client_hello_inner.session_id = client_hello_outer->session_id;
+  client_hello_inner.session_id_len = client_hello_outer->session_id_len;
+
+  // Begin serializing a message containing the ClientHelloInner in |cbb|.
+  ScopedCBB cbb;
+  CBB body, extensions_cbb;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CLIENT_HELLO) ||
+      !ssl_client_hello_write_without_extensions(&client_hello_inner, &body) ||
+      !CBB_add_u16_length_prefixed(&body, &extensions_cbb)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  auto inner_extensions = MakeConstSpan(client_hello_inner.extensions,
+                                        client_hello_inner.extensions_len);
+  CBS ext_list_wrapper;
+  if (!ssl_client_hello_get_extension(&client_hello_inner, &ext_list_wrapper,
+                                      TLSEXT_TYPE_ech_outer_extensions)) {
+    // No ech_outer_extensions. Copy everything.
+    if (!CBB_add_bytes(&extensions_cbb, inner_extensions.data(),
+                       inner_extensions.size())) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return false;
+    }
+  } else {
+    const size_t offset = CBS_data(&ext_list_wrapper) - inner_extensions.data();
+    auto inner_extensions_before =
+        inner_extensions.subspan(0, offset - 4 /* extension header */);
+    auto inner_extensions_after =
+        inner_extensions.subspan(offset + CBS_len(&ext_list_wrapper));
+    if (!CBB_add_bytes(&extensions_cbb, inner_extensions_before.data(),
+                       inner_extensions_before.size())) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return false;
+    }
+
+    // Expand ech_outer_extensions. See draft-ietf-tls-esni-13, Appendix B.
+    CBS ext_list;
+    if (!CBS_get_u8_length_prefixed(&ext_list_wrapper, &ext_list) ||
+        CBS_len(&ext_list) == 0 || CBS_len(&ext_list_wrapper) != 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      return false;
+    }
+    CBS outer_extensions;
+    CBS_init(&outer_extensions, client_hello_outer->extensions,
+             client_hello_outer->extensions_len);
+    while (CBS_len(&ext_list) != 0) {
+      // Find the next extension to copy.
+      uint16_t want;
+      if (!CBS_get_u16(&ext_list, &want)) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+        return false;
+      }
+      // Seek to |want| in |outer_extensions|. |ext_list| is required to match
+      // ClientHelloOuter in order.
+      uint16_t found;
+      CBS ext_body;
+      do {
+        if (CBS_len(&outer_extensions) == 0) {
+          *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+          OPENSSL_PUT_ERROR(SSL, SSL_R_OUTER_EXTENSION_NOT_FOUND);
+          return false;
+        }
+        if (!CBS_get_u16(&outer_extensions, &found) ||
+            !CBS_get_u16_length_prefixed(&outer_extensions, &ext_body)) {
+          OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+          return false;
+        }
+      } while (found != want);
+      // Copy the extension.
+      if (!CBB_add_u16(&extensions_cbb, found) ||
+          !CBB_add_u16(&extensions_cbb, CBS_len(&ext_body)) ||
+          !CBB_add_bytes(&extensions_cbb, CBS_data(&ext_body),
+                         CBS_len(&ext_body))) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+        return false;
+      }
+    }
+
+    if (!CBB_add_bytes(&extensions_cbb, inner_extensions_after.data(),
+                       inner_extensions_after.size())) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return false;
+    }
+  }
+  if (!CBB_flush(&body)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  if (!is_valid_client_hello_inner(
+          ssl, out_alert, MakeConstSpan(CBB_data(&body), CBB_len(&body)))) {
+    return false;
+  }
+
+  if (!ssl->method->finish_message(ssl, cbb.get(), out_client_hello_inner)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+  return true;
+}
+
+bool ssl_client_hello_decrypt(EVP_HPKE_CTX *hpke_ctx, Array<uint8_t> *out,
+                              bool *out_is_decrypt_error,
+                              const SSL_CLIENT_HELLO *client_hello_outer,
+                              Span<const uint8_t> payload) {
+  *out_is_decrypt_error = false;
+
+  // The ClientHelloOuterAAD is |client_hello_outer| with |payload| (which must
+  // point within |client_hello_outer->extensions|) replaced with zeros. See
+  // draft-ietf-tls-esni-13, section 5.2.
+  Array<uint8_t> aad;
+  if (!aad.CopyFrom(MakeConstSpan(client_hello_outer->client_hello,
+                                  client_hello_outer->client_hello_len))) {
+    return false;
+  }
+
+  // We assert with |uintptr_t| because the comparison would be UB if they
+  // didn't alias.
+  assert(reinterpret_cast<uintptr_t>(client_hello_outer->extensions) <=
+         reinterpret_cast<uintptr_t>(payload.data()));
+  assert(reinterpret_cast<uintptr_t>(client_hello_outer->extensions +
+                                     client_hello_outer->extensions_len) >=
+         reinterpret_cast<uintptr_t>(payload.data() + payload.size()));
+  Span<uint8_t> payload_aad = MakeSpan(aad).subspan(
+      payload.data() - client_hello_outer->client_hello, payload.size());
+  OPENSSL_memset(payload_aad.data(), 0, payload_aad.size());
+
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  // In fuzzer mode, disable encryption to improve coverage. We reserve a short
+  // input to signal decryption failure, so the fuzzer can explore fallback to
+  // ClientHelloOuter.
+  const uint8_t kBadPayload[] = {0xff};
+  if (payload == kBadPayload) {
+    *out_is_decrypt_error = true;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
+    return false;
+  }
+  if (!out->CopyFrom(payload)) {
+    return false;
+  }
+#else
+  // Attempt to decrypt into |out|.
+  if (!out->Init(payload.size())) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return false;
+  }
+  size_t len;
+  if (!EVP_HPKE_CTX_open(hpke_ctx, out->data(), &len, out->size(),
+                         payload.data(), payload.size(), aad.data(),
+                         aad.size())) {
+    *out_is_decrypt_error = true;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
+    return false;
+  }
+  out->Shrink(len);
+#endif
+  return true;
+}
+
+static bool parse_ipv4_number(Span<const uint8_t> in, uint32_t *out) {
+  // See https://url.spec.whatwg.org/#ipv4-number-parser.
+  uint32_t base = 10;
+  if (in.size() >= 2 && in[0] == '0' && (in[1] == 'x' || in[1] == 'X')) {
+    in = in.subspan(2);
+    base = 16;
+  } else if (in.size() >= 1 && in[0] == '0') {
+    in = in.subspan(1);
+    base = 8;
+  }
+  *out = 0;
+  for (uint8_t c : in) {
+    uint32_t d;
+    if ('0' <= c && c <= '9') {
+      d = c - '0';
+    } else if ('a' <= c && c <= 'f') {
+      d = c - 'a' + 10;
+    } else if ('A' <= c && c <= 'F') {
+      d = c - 'A' + 10;
+    } else {
+      return false;
+    }
+    if (d >= base ||
+        *out > UINT32_MAX / base) {
+      return false;
+    }
+    *out *= base;
+    if (*out > UINT32_MAX - d) {
+      return false;
+    }
+    *out += d;
+  }
+  return true;
+}
+
+static bool is_ipv4_address(Span<const uint8_t> in) {
+  // See https://url.spec.whatwg.org/#concept-ipv4-parser
+  //
+  // TODO(https://crbug.com/boringssl/275): Revise this, and maybe the spec, per
+  // https://groups.google.com/a/chromium.org/g/blink-dev/c/7QN5nxjwIfM/m/q9dw9MxoAwAJ
+  uint32_t numbers[4];
+  size_t num_numbers = 0;
+  while (!in.empty()) {
+    if (num_numbers == 4) {
+      // Too many components.
+      return false;
+    }
+    // Find the next dot-separated component.
+    auto dot = std::find(in.begin(), in.end(), '.');
+    if (dot == in.begin()) {
+      // Empty components are not allowed.
+      return false;
+    }
+    Span<const uint8_t> component;
+    if (dot == in.end()) {
+      component = in;
+      in = Span<const uint8_t>();
+    } else {
+      component = in.subspan(0, dot - in.begin());
+      in = in.subspan(dot - in.begin() + 1);  // Skip the dot.
+    }
+    if (!parse_ipv4_number(component, &numbers[num_numbers])) {
+      return false;
+    }
+    num_numbers++;
+  }
+  if (num_numbers == 0) {
+    return false;
+  }
+  for (size_t i = 0; i < num_numbers - 1; i++) {
+    if (numbers[i] > 255) {
+      return false;
+    }
+  }
+  return num_numbers == 1 ||
+         numbers[num_numbers - 1] < 1u << (8 * (5 - num_numbers));
+}
+
+bool ssl_is_valid_ech_public_name(Span<const uint8_t> public_name) {
+  // See draft-ietf-tls-esni-13, Section 4 and RFC 5890, Section 2.3.1. The
+  // public name must be a dot-separated sequence of LDH labels and not begin or
+  // end with a dot.
+  auto copy = public_name;
+  if (copy.empty()) {
+    return false;
+  }
+  while (!copy.empty()) {
+    // Find the next dot-separated component.
+    auto dot = std::find(copy.begin(), copy.end(), '.');
+    Span<const uint8_t> component;
+    if (dot == copy.end()) {
+      component = copy;
+      copy = Span<const uint8_t>();
+    } else {
+      component = copy.subspan(0, dot - copy.begin());
+      copy = copy.subspan(dot - copy.begin() + 1);  // Skip the dot.
+      if (copy.empty()) {
+        // Trailing dots are not allowed.
+        return false;
+      }
+    }
+    // |component| must be a valid LDH label. Checking for empty components also
+    // rejects leading dots.
+    if (component.empty() || component.size() > 63 ||
+        component.front() == '-' || component.back() == '-') {
+      return false;
+    }
+    for (uint8_t c : component) {
+      if (!('a' <= c && c <= 'z') && !('A' <= c && c <= 'Z') &&
+          !('0' <= c && c <= '9') && c != '-') {
+        return false;
+      }
+    }
+  }
+
+  return !is_ipv4_address(public_name);
+}
+
+static bool parse_ech_config(CBS *cbs, ECHConfig *out, bool *out_supported,
+                             bool all_extensions_mandatory) {
+  uint16_t version;
+  CBS orig = *cbs;
+  CBS contents;
+  if (!CBS_get_u16(cbs, &version) ||
+      !CBS_get_u16_length_prefixed(cbs, &contents)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return false;
+  }
+
+  if (version != kECHConfigVersion) {
+    *out_supported = false;
+    return true;
+  }
+
+  // Make a copy of the ECHConfig and parse from it, so the results alias into
+  // the saved copy.
+  if (!out->raw.CopyFrom(
+          MakeConstSpan(CBS_data(&orig), CBS_len(&orig) - CBS_len(cbs)))) {
+    return false;
+  }
+
+  CBS ech_config(out->raw);
+  CBS public_name, public_key, cipher_suites, extensions;
+  if (!CBS_skip(&ech_config, 2) || // version
+      !CBS_get_u16_length_prefixed(&ech_config, &contents) ||
+      !CBS_get_u8(&contents, &out->config_id) ||
+      !CBS_get_u16(&contents, &out->kem_id) ||
+      !CBS_get_u16_length_prefixed(&contents, &public_key) ||
+      CBS_len(&public_key) == 0 ||
+      !CBS_get_u16_length_prefixed(&contents, &cipher_suites) ||
+      CBS_len(&cipher_suites) == 0 || CBS_len(&cipher_suites) % 4 != 0 ||
+      !CBS_get_u8(&contents, &out->maximum_name_length) ||
+      !CBS_get_u8_length_prefixed(&contents, &public_name) ||
+      CBS_len(&public_name) == 0 ||
+      !CBS_get_u16_length_prefixed(&contents, &extensions) ||
+      CBS_len(&contents) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return false;
+  }
+
+  if (!ssl_is_valid_ech_public_name(public_name)) {
+    // TODO(https://crbug.com/boringssl/275): The draft says ECHConfigs with
+    // invalid public names should be ignored, but LDH syntax failures are
+    // unambiguously invalid.
+    *out_supported = false;
+    return true;
+  }
+
+  out->public_key = public_key;
+  out->public_name = public_name;
+  // This function does not ensure |out->kem_id| and |out->cipher_suites| use
+  // supported algorithms. The caller must do this.
+  out->cipher_suites = cipher_suites;
+
+  bool has_unknown_mandatory_extension = false;
+  while (CBS_len(&extensions) != 0) {
+    uint16_t type;
+    CBS body;
+    if (!CBS_get_u16(&extensions, &type) ||
+        !CBS_get_u16_length_prefixed(&extensions, &body)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      return false;
+    }
+    // We currently do not support any extensions.
+    if (type & 0x8000 || all_extensions_mandatory) {
+      // Extension numbers with the high bit set are mandatory. Continue parsing
+      // to enforce syntax, but we will ultimately ignore this ECHConfig as a
+      // client and reject it as a server.
+      has_unknown_mandatory_extension = true;
+    }
+  }
+
+  *out_supported = !has_unknown_mandatory_extension;
+  return true;
+}
+
+bool ECHServerConfig::Init(Span<const uint8_t> ech_config,
+                           const EVP_HPKE_KEY *key, bool is_retry_config) {
+  is_retry_config_ = is_retry_config;
+
+  // Parse the ECHConfig, rejecting all unsupported parameters and extensions.
+  // Unlike most server options, ECH's server configuration is serialized and
+  // configured in both the server and DNS. If the caller configures an
+  // unsupported parameter, this is a deployment error. To catch these errors,
+  // we fail early.
+  CBS cbs = ech_config;
+  bool supported;
+  if (!parse_ech_config(&cbs, &ech_config_, &supported,
+                        /*all_extensions_mandatory=*/true)) {
+    return false;
+  }
+  if (CBS_len(&cbs) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return false;
+  }
+  if (!supported) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ECH_SERVER_CONFIG);
+    return false;
+  }
+
+  CBS cipher_suites = ech_config_.cipher_suites;
+  while (CBS_len(&cipher_suites) > 0) {
+    uint16_t kdf_id, aead_id;
+    if (!CBS_get_u16(&cipher_suites, &kdf_id) ||
+        !CBS_get_u16(&cipher_suites, &aead_id)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      return false;
+    }
+    // The server promises to support every option in the ECHConfig, so reject
+    // any unsupported cipher suites.
+    if (kdf_id != EVP_HPKE_HKDF_SHA256 || get_ech_aead(aead_id) == nullptr) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_ECH_SERVER_CONFIG);
+      return false;
+    }
+  }
+
+  // Check the public key in the ECHConfig matches |key|.
+  uint8_t expected_public_key[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
+  size_t expected_public_key_len;
+  if (!EVP_HPKE_KEY_public_key(key, expected_public_key,
+                               &expected_public_key_len,
+                               sizeof(expected_public_key))) {
+    return false;
+  }
+  if (ech_config_.kem_id != EVP_HPKE_KEM_id(EVP_HPKE_KEY_kem(key)) ||
+      MakeConstSpan(expected_public_key, expected_public_key_len) !=
+          ech_config_.public_key) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_ECH_SERVER_CONFIG_AND_PRIVATE_KEY_MISMATCH);
+    return false;
+  }
+
+  if (!EVP_HPKE_KEY_copy(key_.get(), key)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool ECHServerConfig::SetupContext(EVP_HPKE_CTX *ctx, uint16_t kdf_id,
+                                   uint16_t aead_id,
+                                   Span<const uint8_t> enc) const {
+  // Check the cipher suite is supported by this ECHServerConfig.
+  CBS cbs(ech_config_.cipher_suites);
+  bool cipher_ok = false;
+  while (CBS_len(&cbs) != 0) {
+    uint16_t supported_kdf_id, supported_aead_id;
+    if (!CBS_get_u16(&cbs, &supported_kdf_id) ||
+        !CBS_get_u16(&cbs, &supported_aead_id)) {
+      return false;
+    }
+    if (kdf_id == supported_kdf_id && aead_id == supported_aead_id) {
+      cipher_ok = true;
+      break;
+    }
+  }
+  if (!cipher_ok) {
+    return false;
+  }
+
+  static const uint8_t kInfoLabel[] = "tls ech";
+  ScopedCBB info_cbb;
+  if (!CBB_init(info_cbb.get(), sizeof(kInfoLabel) + ech_config_.raw.size()) ||
+      !CBB_add_bytes(info_cbb.get(), kInfoLabel,
+                     sizeof(kInfoLabel) /* includes trailing NUL */) ||
+      !CBB_add_bytes(info_cbb.get(), ech_config_.raw.data(),
+                     ech_config_.raw.size())) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return false;
+  }
+
+  assert(kdf_id == EVP_HPKE_HKDF_SHA256);
+  assert(get_ech_aead(aead_id) != NULL);
+  return EVP_HPKE_CTX_setup_recipient(
+      ctx, key_.get(), EVP_hpke_hkdf_sha256(), get_ech_aead(aead_id), enc.data(),
+      enc.size(), CBB_data(info_cbb.get()), CBB_len(info_cbb.get()));
+}
+
+bool ssl_is_valid_ech_config_list(Span<const uint8_t> ech_config_list) {
+  CBS cbs = ech_config_list, child;
+  if (!CBS_get_u16_length_prefixed(&cbs, &child) ||  //
+      CBS_len(&child) == 0 ||                        //
+      CBS_len(&cbs) > 0) {
+    return false;
+  }
+  while (CBS_len(&child) > 0) {
+    ECHConfig ech_config;
+    bool supported;
+    if (!parse_ech_config(&child, &ech_config, &supported,
+                          /*all_extensions_mandatory=*/false)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+static bool select_ech_cipher_suite(const EVP_HPKE_KDF **out_kdf,
+                                    const EVP_HPKE_AEAD **out_aead,
+                                    Span<const uint8_t> cipher_suites) {
+  const bool has_aes_hardware = EVP_has_aes_hardware();
+  const EVP_HPKE_AEAD *aead = nullptr;
+  CBS cbs = cipher_suites;
+  while (CBS_len(&cbs) != 0) {
+    uint16_t kdf_id, aead_id;
+    if (!CBS_get_u16(&cbs, &kdf_id) ||  //
+        !CBS_get_u16(&cbs, &aead_id)) {
+      return false;
+    }
+    // Pick the first common cipher suite, but prefer ChaCha20-Poly1305 if we
+    // don't have AES hardware.
+    const EVP_HPKE_AEAD *candidate = get_ech_aead(aead_id);
+    if (kdf_id != EVP_HPKE_HKDF_SHA256 || candidate == nullptr) {
+      continue;
+    }
+    if (aead == nullptr ||
+        (!has_aes_hardware && aead_id == EVP_HPKE_CHACHA20_POLY1305)) {
+      aead = candidate;
+    }
+  }
+  if (aead == nullptr) {
+    return false;
+  }
+
+  *out_kdf = EVP_hpke_hkdf_sha256();
+  *out_aead = aead;
+  return true;
+}
+
+bool ssl_select_ech_config(SSL_HANDSHAKE *hs, Span<uint8_t> out_enc,
+                           size_t *out_enc_len) {
+  *out_enc_len = 0;
+  if (hs->max_version < TLS1_3_VERSION) {
+    // ECH requires TLS 1.3.
+    return true;
+  }
+
+  if (!hs->config->client_ech_config_list.empty()) {
+    CBS cbs = MakeConstSpan(hs->config->client_ech_config_list);
+    CBS child;
+    if (!CBS_get_u16_length_prefixed(&cbs, &child) ||  //
+        CBS_len(&child) == 0 ||                        //
+        CBS_len(&cbs) > 0) {
+      return false;
+    }
+    // Look for the first ECHConfig with supported parameters.
+    while (CBS_len(&child) > 0) {
+      ECHConfig ech_config;
+      bool supported;
+      if (!parse_ech_config(&child, &ech_config, &supported,
+                            /*all_extensions_mandatory=*/false)) {
+        return false;
+      }
+      const EVP_HPKE_KEM *kem = EVP_hpke_x25519_hkdf_sha256();
+      const EVP_HPKE_KDF *kdf;
+      const EVP_HPKE_AEAD *aead;
+      if (supported &&  //
+          ech_config.kem_id == EVP_HPKE_DHKEM_X25519_HKDF_SHA256 &&
+          select_ech_cipher_suite(&kdf, &aead, ech_config.cipher_suites)) {
+        ScopedCBB info;
+        static const uint8_t kInfoLabel[] = "tls ech";  // includes trailing NUL
+        if (!CBB_init(info.get(), sizeof(kInfoLabel) + ech_config.raw.size()) ||
+            !CBB_add_bytes(info.get(), kInfoLabel, sizeof(kInfoLabel)) ||
+            !CBB_add_bytes(info.get(), ech_config.raw.data(),
+                           ech_config.raw.size())) {
+          OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+          return false;
+        }
+
+        if (!EVP_HPKE_CTX_setup_sender(
+                hs->ech_hpke_ctx.get(), out_enc.data(), out_enc_len,
+                out_enc.size(), kem, kdf, aead, ech_config.public_key.data(),
+                ech_config.public_key.size(), CBB_data(info.get()),
+                CBB_len(info.get())) ||
+            !hs->inner_transcript.Init()) {
+          return false;
+        }
+
+        hs->selected_ech_config = MakeUnique<ECHConfig>(std::move(ech_config));
+        return hs->selected_ech_config != nullptr;
+      }
+    }
+  }
+
+  return true;
+}
+
+static size_t aead_overhead(const EVP_HPKE_AEAD *aead) {
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  // TODO(https://crbug.com/boringssl/275): Having to adjust the overhead
+  // everywhere is tedious. Change fuzzer mode to append a fake tag but still
+  // otherwise be cleartext, refresh corpora, and then inline this function.
+  return 0;
+#else
+  return EVP_AEAD_max_overhead(EVP_HPKE_AEAD_aead(aead));
+#endif
+}
+
+// random_size returns a random value between |min| and |max|, inclusive.
+static size_t random_size(size_t min, size_t max) {
+  assert(min < max);
+  size_t value;
+  RAND_bytes(reinterpret_cast<uint8_t *>(&value), sizeof(value));
+  return value % (max - min + 1) + min;
+}
+
+static bool setup_ech_grease(SSL_HANDSHAKE *hs) {
+  assert(!hs->selected_ech_config);
+  if (hs->max_version < TLS1_3_VERSION || !hs->config->ech_grease_enabled) {
+    return true;
+  }
+
+  const uint16_t kdf_id = EVP_HPKE_HKDF_SHA256;
+  const EVP_HPKE_AEAD *aead = EVP_has_aes_hardware()
+                                  ? EVP_hpke_aes_128_gcm()
+                                  : EVP_hpke_chacha20_poly1305();
+  static_assert(ssl_grease_ech_config_id < sizeof(hs->grease_seed),
+                "hs->grease_seed is too small");
+  uint8_t config_id = hs->grease_seed[ssl_grease_ech_config_id];
+
+  uint8_t enc[X25519_PUBLIC_VALUE_LEN];
+  uint8_t private_key_unused[X25519_PRIVATE_KEY_LEN];
+  X25519_keypair(enc, private_key_unused);
+
+  // To determine a plausible length for the payload, we estimate the size of a
+  // typical EncodedClientHelloInner without resumption:
+  //
+  //   2+32+1+2   version, random, legacy_session_id, legacy_compression_methods
+  //   2+4*2      cipher_suites (three TLS 1.3 ciphers, GREASE)
+  //   2          extensions prefix
+  //   5          inner encrypted_client_hello
+  //   4+1+2*2    supported_versions (TLS 1.3, GREASE)
+  //   4+1+10*2   outer_extensions (key_share, sigalgs, sct, alpn,
+  //              supported_groups, status_request, psk_key_exchange_modes,
+  //              compress_certificate, GREASE x2)
+  //
+  // The server_name extension has an overhead of 9 bytes. For now, arbitrarily
+  // estimate maximum_name_length to be between 32 and 100 bytes. Then round up
+  // to a multiple of 32, to match draft-ietf-tls-esni-13, section 6.1.3.
+  const size_t payload_len =
+      32 * random_size(128 / 32, 224 / 32) + aead_overhead(aead);
+  bssl::ScopedCBB cbb;
+  CBB enc_cbb, payload_cbb;
+  uint8_t *payload;
+  if (!CBB_init(cbb.get(), 256) ||
+      !CBB_add_u16(cbb.get(), kdf_id) ||
+      !CBB_add_u16(cbb.get(), EVP_HPKE_AEAD_id(aead)) ||
+      !CBB_add_u8(cbb.get(), config_id) ||
+      !CBB_add_u16_length_prefixed(cbb.get(), &enc_cbb) ||
+      !CBB_add_bytes(&enc_cbb, enc, sizeof(enc)) ||
+      !CBB_add_u16_length_prefixed(cbb.get(), &payload_cbb) ||
+      !CBB_add_space(&payload_cbb, &payload, payload_len) ||
+      !RAND_bytes(payload, payload_len) ||
+      !CBBFinishArray(cbb.get(), &hs->ech_client_outer)) {
+    return false;
+  }
+  return true;
+}
+
+bool ssl_encrypt_client_hello(SSL_HANDSHAKE *hs, Span<const uint8_t> enc) {
+  SSL *const ssl = hs->ssl;
+  if (!hs->selected_ech_config) {
+    return setup_ech_grease(hs);
+  }
+
+  // Construct ClientHelloInner and EncodedClientHelloInner. See
+  // draft-ietf-tls-esni-13, sections 5.1 and 6.1.
+  ScopedCBB cbb, encoded_cbb;
+  CBB body;
+  bool needs_psk_binder;
+  Array<uint8_t> hello_inner;
+  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CLIENT_HELLO) ||
+      !CBB_init(encoded_cbb.get(), 256) ||
+      !ssl_write_client_hello_without_extensions(hs, &body,
+                                                 ssl_client_hello_inner,
+                                                 /*empty_session_id=*/false) ||
+      !ssl_write_client_hello_without_extensions(hs, encoded_cbb.get(),
+                                                 ssl_client_hello_inner,
+                                                 /*empty_session_id=*/true) ||
+      !ssl_add_clienthello_tlsext(hs, &body, encoded_cbb.get(),
+                                  &needs_psk_binder, ssl_client_hello_inner,
+                                  CBB_len(&body)) ||
+      !ssl->method->finish_message(ssl, cbb.get(), &hello_inner)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  if (needs_psk_binder) {
+    size_t binder_len;
+    if (!tls13_write_psk_binder(hs, hs->inner_transcript, MakeSpan(hello_inner),
+                                &binder_len)) {
+      return false;
+    }
+    // Also update the EncodedClientHelloInner.
+    auto encoded_binder =
+        MakeSpan(const_cast<uint8_t *>(CBB_data(encoded_cbb.get())),
+                 CBB_len(encoded_cbb.get()))
+            .last(binder_len);
+    auto hello_inner_binder = MakeConstSpan(hello_inner).last(binder_len);
+    OPENSSL_memcpy(encoded_binder.data(), hello_inner_binder.data(),
+                   binder_len);
+  }
+
+  if (!hs->inner_transcript.Update(hello_inner)) {
+    return false;
+  }
+
+  // Pad the EncodedClientHelloInner. See draft-ietf-tls-esni-13, section 6.1.3.
+  size_t padding_len = 0;
+  size_t maximum_name_length = hs->selected_ech_config->maximum_name_length;
+  if (ssl->hostname) {
+    size_t hostname_len = strlen(ssl->hostname.get());
+    if (hostname_len <= maximum_name_length) {
+      padding_len = maximum_name_length - hostname_len;
+    }
+  } else {
+    // No SNI. Pad up to |maximum_name_length|, including server_name extension
+    // overhead.
+    padding_len = 9 + maximum_name_length;
+  }
+  // Pad the whole thing to a multiple of 32 bytes.
+  padding_len += 31 - ((CBB_len(encoded_cbb.get()) + padding_len - 1) % 32);
+  Array<uint8_t> encoded;
+  if (!CBB_add_zeros(encoded_cbb.get(), padding_len) ||
+      !CBBFinishArray(encoded_cbb.get(), &encoded)) {
+    return false;
+  }
+
+  // Encrypt |encoded|. See draft-ietf-tls-esni-13, section 6.1.1. First,
+  // assemble the extension with a placeholder value for ClientHelloOuterAAD.
+  // See draft-ietf-tls-esni-13, section 5.2.
+  const EVP_HPKE_KDF *kdf = EVP_HPKE_CTX_kdf(hs->ech_hpke_ctx.get());
+  const EVP_HPKE_AEAD *aead = EVP_HPKE_CTX_aead(hs->ech_hpke_ctx.get());
+  size_t payload_len = encoded.size() + aead_overhead(aead);
+  CBB enc_cbb, payload_cbb;
+  if (!CBB_init(cbb.get(), 256) ||
+      !CBB_add_u16(cbb.get(), EVP_HPKE_KDF_id(kdf)) ||
+      !CBB_add_u16(cbb.get(), EVP_HPKE_AEAD_id(aead)) ||
+      !CBB_add_u8(cbb.get(), hs->selected_ech_config->config_id) ||
+      !CBB_add_u16_length_prefixed(cbb.get(), &enc_cbb) ||
+      !CBB_add_bytes(&enc_cbb, enc.data(), enc.size()) ||
+      !CBB_add_u16_length_prefixed(cbb.get(), &payload_cbb) ||
+      !CBB_add_zeros(&payload_cbb, payload_len) ||
+      !CBBFinishArray(cbb.get(), &hs->ech_client_outer)) {
+    return false;
+  }
+
+  // Construct ClientHelloOuterAAD.
+  // TODO(https://crbug.com/boringssl/275): This ends up constructing the
+  // ClientHelloOuter twice. Instead, reuse |aad| for the ClientHello, now that
+  // draft-12 made the length prefixes match.
+  bssl::ScopedCBB aad;
+  if (!CBB_init(aad.get(), 256) ||
+      !ssl_write_client_hello_without_extensions(hs, aad.get(),
+                                                 ssl_client_hello_outer,
+                                                 /*empty_session_id=*/false) ||
+      !ssl_add_clienthello_tlsext(hs, aad.get(), /*out_encoded=*/nullptr,
+                                  &needs_psk_binder, ssl_client_hello_outer,
+                                  CBB_len(aad.get()))) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  // ClientHelloOuter may not require a PSK binder. Otherwise, we have a
+  // circular dependency.
+  assert(!needs_psk_binder);
+
+  // Replace the payload in |hs->ech_client_outer| with the encrypted value.
+  auto payload_span = MakeSpan(hs->ech_client_outer).last(payload_len);
+#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
+  // In fuzzer mode, the server expects a cleartext payload.
+  assert(payload_span.size() == encoded.size());
+  OPENSSL_memcpy(payload_span.data(), encoded.data(), encoded.size());
+#else
+  if (!EVP_HPKE_CTX_seal(hs->ech_hpke_ctx.get(), payload_span.data(),
+                         &payload_len, payload_span.size(), encoded.data(),
+                         encoded.size(), CBB_data(aad.get()),
+                         CBB_len(aad.get())) ||
+      payload_len != payload_span.size()) {
+    return false;
+  }
+#endif // BORINGSSL_UNSAFE_FUZZER_MODE
+
+  return true;
+}
+
+BSSL_NAMESPACE_END
+
+using namespace bssl;
+
+void SSL_set_enable_ech_grease(SSL *ssl, int enable) {
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->ech_grease_enabled = !!enable;
+}
+
+int SSL_set1_ech_config_list(SSL *ssl, const uint8_t *ech_config_list,
+                             size_t ech_config_list_len) {
+  if (!ssl->config) {
+    return 0;
+  }
+
+  auto span = MakeConstSpan(ech_config_list, ech_config_list_len);
+  if (!ssl_is_valid_ech_config_list(span)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ECH_CONFIG_LIST);
+    return 0;
+  }
+  return ssl->config->client_ech_config_list.CopyFrom(span);
+}
+
+void SSL_get0_ech_name_override(const SSL *ssl, const char **out_name,
+                                size_t *out_name_len) {
+  // When ECH is rejected, we use the public name. Note that, if
+  // |SSL_CTX_set_reverify_on_resume| is enabled, we reverify the certificate
+  // before the 0-RTT point. If also offering ECH, we verify as if
+  // ClientHelloInner was accepted and do not override. This works because, at
+  // this point, |ech_status| will be |ssl_ech_none|. See the
+  // ECH-Client-Reject-EarlyDataReject-OverrideNameOnRetry tests in runner.go.
+  const SSL_HANDSHAKE *hs = ssl->s3->hs.get();
+  if (!ssl->server && hs && ssl->s3->ech_status == ssl_ech_rejected) {
+    *out_name = reinterpret_cast<const char *>(
+        hs->selected_ech_config->public_name.data());
+    *out_name_len = hs->selected_ech_config->public_name.size();
+  } else {
+    *out_name = nullptr;
+    *out_name_len = 0;
+  }
+}
+
+void SSL_get0_ech_retry_configs(
+    const SSL *ssl, const uint8_t **out_retry_configs,
+    size_t *out_retry_configs_len) {
+  const SSL_HANDSHAKE *hs = ssl->s3->hs.get();
+  if (!hs || !hs->ech_authenticated_reject) {
+    // It is an error to call this function except in response to
+    // |SSL_R_ECH_REJECTED|. Returning an empty string risks the caller
+    // mistakenly believing the server has disabled ECH. Instead, return a
+    // non-empty ECHConfigList with a syntax error, so the subsequent
+    // |SSL_set1_ech_config_list| call will fail.
+    assert(0);
+    static const uint8_t kPlaceholder[] = {
+        kECHConfigVersion >> 8, kECHConfigVersion & 0xff, 0xff, 0xff, 0xff};
+    *out_retry_configs = kPlaceholder;
+    *out_retry_configs_len = sizeof(kPlaceholder);
+    return;
+  }
+
+  *out_retry_configs = hs->ech_retry_configs.data();
+  *out_retry_configs_len = hs->ech_retry_configs.size();
+}
+
+int SSL_marshal_ech_config(uint8_t **out, size_t *out_len, uint8_t config_id,
+                           const EVP_HPKE_KEY *key, const char *public_name,
+                           size_t max_name_len) {
+  Span<const uint8_t> public_name_u8 = MakeConstSpan(
+      reinterpret_cast<const uint8_t *>(public_name), strlen(public_name));
+  if (!ssl_is_valid_ech_public_name(public_name_u8)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ECH_PUBLIC_NAME);
+    return 0;
+  }
+
+  // The maximum name length is encoded in one byte.
+  if (max_name_len > 0xff) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_LENGTH);
+    return 0;
+  }
+
+  // See draft-ietf-tls-esni-13, section 4.
+  ScopedCBB cbb;
+  CBB contents, child;
+  uint8_t *public_key;
+  size_t public_key_len;
+  if (!CBB_init(cbb.get(), 128) ||  //
+      !CBB_add_u16(cbb.get(), kECHConfigVersion) ||
+      !CBB_add_u16_length_prefixed(cbb.get(), &contents) ||
+      !CBB_add_u8(&contents, config_id) ||
+      !CBB_add_u16(&contents, EVP_HPKE_KEM_id(EVP_HPKE_KEY_kem(key))) ||
+      !CBB_add_u16_length_prefixed(&contents, &child) ||
+      !CBB_reserve(&child, &public_key, EVP_HPKE_MAX_PUBLIC_KEY_LENGTH) ||
+      !EVP_HPKE_KEY_public_key(key, public_key, &public_key_len,
+                               EVP_HPKE_MAX_PUBLIC_KEY_LENGTH) ||
+      !CBB_did_write(&child, public_key_len) ||
+      !CBB_add_u16_length_prefixed(&contents, &child) ||
+      // Write a default cipher suite configuration.
+      !CBB_add_u16(&child, EVP_HPKE_HKDF_SHA256) ||
+      !CBB_add_u16(&child, EVP_HPKE_AES_128_GCM) ||
+      !CBB_add_u16(&child, EVP_HPKE_HKDF_SHA256) ||
+      !CBB_add_u16(&child, EVP_HPKE_CHACHA20_POLY1305) ||
+      !CBB_add_u8(&contents, max_name_len) ||
+      !CBB_add_u8_length_prefixed(&contents, &child) ||
+      !CBB_add_bytes(&child, public_name_u8.data(), public_name_u8.size()) ||
+      // TODO(https://crbug.com/boringssl/275): Reserve some GREASE extensions
+      // and include some.
+      !CBB_add_u16(&contents, 0 /* no extensions */) ||
+      !CBB_finish(cbb.get(), out, out_len)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return 0;
+  }
+  return 1;
+}
+
+SSL_ECH_KEYS *SSL_ECH_KEYS_new() { return New<SSL_ECH_KEYS>(); }
+
+void SSL_ECH_KEYS_up_ref(SSL_ECH_KEYS *keys) {
+  CRYPTO_refcount_inc(&keys->references);
+}
+
+void SSL_ECH_KEYS_free(SSL_ECH_KEYS *keys) {
+  if (keys == nullptr ||
+      !CRYPTO_refcount_dec_and_test_zero(&keys->references)) {
+    return;
+  }
+
+  keys->~ssl_ech_keys_st();
+  OPENSSL_free(keys);
+}
+
+int SSL_ECH_KEYS_add(SSL_ECH_KEYS *configs, int is_retry_config,
+                     const uint8_t *ech_config, size_t ech_config_len,
+                     const EVP_HPKE_KEY *key) {
+  UniquePtr<ECHServerConfig> parsed_config = MakeUnique<ECHServerConfig>();
+  if (!parsed_config) {
+    return 0;
+  }
+  if (!parsed_config->Init(MakeConstSpan(ech_config, ech_config_len), key,
+                           !!is_retry_config)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    return 0;
+  }
+  if (!configs->configs.Push(std::move(parsed_config))) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  return 1;
+}
+
+int SSL_ECH_KEYS_has_duplicate_config_id(const SSL_ECH_KEYS *keys) {
+  bool seen[256] = {false};
+  for (const auto &config : keys->configs) {
+    if (seen[config->ech_config().config_id]) {
+      return 1;
+    }
+    seen[config->ech_config().config_id] = true;
+  }
+  return 0;
+}
+
+int SSL_ECH_KEYS_marshal_retry_configs(const SSL_ECH_KEYS *keys, uint8_t **out,
+                                       size_t *out_len) {
+  ScopedCBB cbb;
+  CBB child;
+  if (!CBB_init(cbb.get(), 128) ||
+      !CBB_add_u16_length_prefixed(cbb.get(), &child)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return false;
+  }
+  for (const auto &config : keys->configs) {
+    if (config->is_retry_config() &&
+        !CBB_add_bytes(&child, config->ech_config().raw.data(),
+                       config->ech_config().raw.size())) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      return false;
+    }
+  }
+  return CBB_finish(cbb.get(), out, out_len);
+}
+
+int SSL_CTX_set1_ech_keys(SSL_CTX *ctx, SSL_ECH_KEYS *keys) {
+  bool has_retry_config = false;
+  for (const auto &config : keys->configs) {
+    if (config->is_retry_config()) {
+      has_retry_config = true;
+      break;
+    }
+  }
+  if (!has_retry_config) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_ECH_SERVER_WOULD_HAVE_NO_RETRY_CONFIGS);
+    return 0;
+  }
+  UniquePtr<SSL_ECH_KEYS> owned_keys = UpRef(keys);
+  MutexWriteLock lock(&ctx->lock);
+  ctx->ech_keys.swap(owned_keys);
+  return 1;
+}
+
+int SSL_ech_accepted(const SSL *ssl) {
+  if (SSL_in_early_data(ssl) && !ssl->server) {
+    // In the client early data state, we report properties as if the server
+    // accepted early data. The server can only accept early data with
+    // ClientHelloInner.
+    return ssl->s3->hs->selected_ech_config != nullptr;
+  }
+
+  return ssl->s3->ech_status == ssl_ech_accepted;
+}
diff --git a/deps/boringssl/src/ssl/t1_lib.cc b/deps/boringssl/src/ssl/extensions.cc
similarity index 78%
rename from deps/boringssl/src/ssl/t1_lib.cc
rename to deps/boringssl/src/ssl/extensions.cc
index 342c170..ba55c93 100644
--- a/deps/boringssl/src/ssl/t1_lib.cc
+++ b/deps/boringssl/src/ssl/extensions.cc
@@ -124,11 +124,11 @@
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/hmac.h>
+#include <openssl/hpke.h>
 #include <openssl/mem.h>
 #include <openssl/nid.h>
 #include <openssl/rand.h>
 
-#include "../crypto/hpke/internal.h"
 #include "../crypto/internal.h"
 #include "internal.h"
 
@@ -209,17 +209,25 @@
 }
 
 bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out,
-                           const SSLMessage &msg) {
+                           Span<const uint8_t> body) {
+  CBS cbs = body;
+  if (!ssl_parse_client_hello_with_trailing_data(ssl, &cbs, out) ||
+      CBS_len(&cbs) != 0) {
+    return false;
+  }
+  return true;
+}
+
+bool ssl_parse_client_hello_with_trailing_data(const SSL *ssl, CBS *cbs,
+                                               SSL_CLIENT_HELLO *out) {
   OPENSSL_memset(out, 0, sizeof(*out));
   out->ssl = const_cast<SSL *>(ssl);
-  out->client_hello = CBS_data(&msg.body);
-  out->client_hello_len = CBS_len(&msg.body);
 
-  CBS client_hello, random, session_id;
-  CBS_init(&client_hello, out->client_hello, out->client_hello_len);
-  if (!CBS_get_u16(&client_hello, &out->version) ||
-      !CBS_get_bytes(&client_hello, &random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&client_hello, &session_id) ||
+  CBS copy = *cbs;
+  CBS random, session_id;
+  if (!CBS_get_u16(cbs, &out->version) ||
+      !CBS_get_bytes(cbs, &random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(cbs, &session_id) ||
       CBS_len(&session_id) > SSL_MAX_SSL_SESSION_ID_LENGTH) {
     return false;
   }
@@ -232,16 +240,16 @@
   // Skip past DTLS cookie
   if (SSL_is_dtls(out->ssl)) {
     CBS cookie;
-    if (!CBS_get_u8_length_prefixed(&client_hello, &cookie) ||
+    if (!CBS_get_u8_length_prefixed(cbs, &cookie) ||
         CBS_len(&cookie) > DTLS1_COOKIE_LENGTH) {
       return false;
     }
   }
 
   CBS cipher_suites, compression_methods;
-  if (!CBS_get_u16_length_prefixed(&client_hello, &cipher_suites) ||
+  if (!CBS_get_u16_length_prefixed(cbs, &cipher_suites) ||
       CBS_len(&cipher_suites) < 2 || (CBS_len(&cipher_suites) & 1) != 0 ||
-      !CBS_get_u8_length_prefixed(&client_hello, &compression_methods) ||
+      !CBS_get_u8_length_prefixed(cbs, &compression_methods) ||
       CBS_len(&compression_methods) < 1) {
     return false;
   }
@@ -253,23 +261,22 @@
 
   // If the ClientHello ends here then it's valid, but doesn't have any
   // extensions.
-  if (CBS_len(&client_hello) == 0) {
-    out->extensions = NULL;
+  if (CBS_len(cbs) == 0) {
+    out->extensions = nullptr;
     out->extensions_len = 0;
-    return true;
+  } else {
+    // Extract extensions and check it is valid.
+    CBS extensions;
+    if (!CBS_get_u16_length_prefixed(cbs, &extensions) ||
+        !tls1_check_duplicate_extensions(&extensions)) {
+      return false;
+    }
+    out->extensions = CBS_data(&extensions);
+    out->extensions_len = CBS_len(&extensions);
   }
 
-  // Extract extensions and check it is valid.
-  CBS extensions;
-  if (!CBS_get_u16_length_prefixed(&client_hello, &extensions) ||
-      !tls1_check_duplicate_extensions(&extensions) ||
-      CBS_len(&client_hello) != 0) {
-    return false;
-  }
-
-  out->extensions = CBS_data(&extensions);
-  out->extensions_len = CBS_len(&extensions);
-
+  out->client_hello = CBS_data(&copy);
+  out->client_hello_len = CBS_len(&copy) - CBS_len(cbs);
   return true;
 }
 
@@ -405,6 +412,11 @@
     return false;
   }
 
+  // We internally assume zero is never allocated as a group ID.
+  if (group_id == 0) {
+    return false;
+  }
+
   for (uint16_t supported : tls1_get_grouplist(hs)) {
     if (supported == group_id) {
       return true;
@@ -488,9 +500,7 @@
   return false;
 }
 
-// tls_extension represents a TLS extension that is handled internally. The
-// |init| function is called for each handshake, before any other functions of
-// the extension. Then the add and parse callbacks are called as needed.
+// tls_extension represents a TLS extension that is handled internally.
 //
 // The parse callbacks receive a |CBS| that contains the contents of the
 // extension (i.e. not including the type and length bytes). If an extension is
@@ -500,14 +510,27 @@
 // The add callbacks receive a |CBB| to which the extension can be appended but
 // the function is responsible for appending the type and length bytes too.
 //
+// |add_clienthello| may be called multiple times and must not mutate |hs|. It
+// is additionally passed two output |CBB|s. If the extension is the same
+// independent of the value of |type|, the callback may write to
+// |out_compressible| instead of |out|. When serializing the ClientHelloInner,
+// all compressible extensions will be made continguous and replaced with
+// ech_outer_extensions when encrypted. When serializing the ClientHelloOuter
+// or not offering ECH, |out| will be equal to |out_compressible|, so writing to
+// |out_compressible| still works.
+//
+// Note the |parse_serverhello| and |add_serverhello| callbacks refer to the
+// TLS 1.2 ServerHello. In TLS 1.3, these callbacks act on EncryptedExtensions,
+// with ServerHello extensions handled elsewhere in the handshake.
+//
 // All callbacks return true for success and false for error. If a parse
 // function returns zero then a fatal alert with value |*out_alert| will be
 // sent. If |*out_alert| isn't set, then a |decode_error| alert will be sent.
 struct tls_extension {
   uint16_t value;
-  void (*init)(SSL_HANDSHAKE *hs);
 
-  bool (*add_clienthello)(SSL_HANDSHAKE *hs, CBB *out);
+  bool (*add_clienthello)(const SSL_HANDSHAKE *hs, CBB *out,
+                          CBB *out_compressible, ssl_client_hello_type_t type);
   bool (*parse_serverhello)(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                             CBS *contents);
 
@@ -542,10 +565,21 @@
 //
 // https://tools.ietf.org/html/rfc6066#section-3.
 
-static bool ext_sni_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-  if (ssl->hostname == nullptr) {
-    return true;
+static bool ext_sni_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                    CBB *out_compressible,
+                                    ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
+  // If offering ECH, send the public name instead of the configured name.
+  Span<const uint8_t> hostname;
+  if (type == ssl_client_hello_outer) {
+    hostname = hs->selected_ech_config->public_name;
+  } else {
+    if (ssl->hostname == nullptr) {
+      return true;
+    }
+    hostname =
+        MakeConstSpan(reinterpret_cast<const uint8_t *>(ssl->hostname.get()),
+                      strlen(ssl->hostname.get()));
   }
 
   CBB contents, server_name_list, name;
@@ -554,8 +588,7 @@
       !CBB_add_u16_length_prefixed(&contents, &server_name_list) ||
       !CBB_add_u8(&server_name_list, TLSEXT_NAMETYPE_host_name) ||
       !CBB_add_u16_length_prefixed(&server_name_list, &name) ||
-      !CBB_add_bytes(&name, (const uint8_t *)ssl->hostname.get(),
-                     strlen(ssl->hostname.get())) ||
+      !CBB_add_bytes(&name, hostname.data(), hostname.size()) ||
       !CBB_flush(out)) {
     return false;
   }
@@ -591,190 +624,131 @@
 }
 
 
-// Encrypted Client Hello (ECH)
+// Encrypted ClientHello (ECH)
 //
-// https://tools.ietf.org/html/draft-ietf-tls-esni-09
+// https://tools.ietf.org/html/draft-ietf-tls-esni-13
 
-// random_size returns a random value between |min| and |max|, inclusive.
-static size_t random_size(size_t min, size_t max) {
-  assert(min < max);
-  size_t value;
-  RAND_bytes(reinterpret_cast<uint8_t *>(&value), sizeof(value));
-  return value % (max - min + 1) + min;
-}
-
-static bool ext_ech_add_clienthello_grease(SSL_HANDSHAKE *hs, CBB *out) {
-  // If we are responding to the server's HelloRetryRequest, we repeat the bytes
-  // of the first ECH GREASE extension.
-  if (hs->ssl->s3->used_hello_retry_request) {
-    CBB ech_body;
+static bool ext_ech_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                    CBB *out_compressible,
+                                    ssl_client_hello_type_t type) {
+  if (type == ssl_client_hello_inner) {
     if (!CBB_add_u16(out, TLSEXT_TYPE_encrypted_client_hello) ||
-        !CBB_add_u16_length_prefixed(out, &ech_body) ||
-        !CBB_add_bytes(&ech_body, hs->ech_grease.data(),
-                       hs->ech_grease.size()) ||
-        !CBB_flush(out)) {
+        !CBB_add_u16(out, /* length */ 1) ||
+        !CBB_add_u8(out, ECH_CLIENT_INNER)) {
       return false;
     }
     return true;
   }
 
-  constexpr uint16_t kdf_id = EVP_HPKE_HKDF_SHA256;
-  const uint16_t aead_id = EVP_has_aes_hardware()
-                               ? EVP_HPKE_AEAD_AES_GCM_128
-                               : EVP_HPKE_AEAD_CHACHA20POLY1305;
-  const EVP_AEAD *aead = EVP_HPKE_get_aead(aead_id);
-  assert(aead != nullptr);
-
-  uint8_t ech_config_id[8];
-  RAND_bytes(ech_config_id, sizeof(ech_config_id));
-
-  uint8_t ech_enc[X25519_PUBLIC_VALUE_LEN];
-  uint8_t private_key_unused[X25519_PRIVATE_KEY_LEN];
-  X25519_keypair(ech_enc, private_key_unused);
-
-  // To determine a plausible length for the payload, we first estimate the size
-  // of a typical EncodedClientHelloInner, with an expected use of
-  // outer_extensions. To limit the size, we only consider initial ClientHellos
-  // that do not offer resumption.
-  //
-  //   Field/Extension                           Size
-  // ---------------------------------------------------------------------
-  //   version                                      2
-  //   random                                      32
-  //   legacy_session_id                            1
-  //      - Has a U8 length prefix, but body is
-  //        always empty string in inner CH.
-  //   cipher_suites                                2  (length prefix)
-  //      - Only includes TLS 1.3 ciphers (3).      6
-  //      - Maybe also include a GREASE suite.      2
-  //   legacy_compression_methods                   2  (length prefix)
-  //      - Always has "null" compression method.   1
-  //   extensions:                                  2  (length prefix)
-  //      - encrypted_client_hello (empty).         4  (id + length prefix)
-  //      - supported_versions.                     4  (id + length prefix)
-  //        - U8 length prefix                      1
-  //        - U16 protocol version (TLS 1.3)        2
-  //      - outer_extensions.                       4  (id + length prefix)
-  //        - U8 length prefix                      1
-  //        - N extension IDs (2 bytes each):
-  //          - key_share                           2
-  //          - sigalgs                             2
-  //          - sct                                 2
-  //          - alpn                                2
-  //          - supported_groups.                   2
-  //          - status_request.                     2
-  //          - psk_key_exchange_modes.             2
-  //          - compress_certificate.               2
-  //
-  // The server_name extension has an overhead of 9 bytes, plus up to an
-  // estimated 100 bytes of hostname. Rounding up to a multiple of 32 yields a
-  // range of 96 to 192. Note that this estimate does not fully capture
-  // optional extensions like GREASE, but the rounding gives some leeway.
-
-  uint8_t payload[EVP_AEAD_MAX_OVERHEAD + 192];
-  const size_t payload_len =
-      EVP_AEAD_max_overhead(aead) + 32 * random_size(96 / 32, 192 / 32);
-  assert(payload_len <= sizeof(payload));
-  RAND_bytes(payload, payload_len);
-
-  // Inside the TLS extension contents, write a serialized ClientEncryptedCH.
-  CBB ech_body, config_id_cbb, enc_cbb, payload_cbb;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_encrypted_client_hello) ||
-      !CBB_add_u16_length_prefixed(out, &ech_body) ||
-      !CBB_add_u16(&ech_body, kdf_id) ||  //
-      !CBB_add_u16(&ech_body, aead_id) ||
-      !CBB_add_u8_length_prefixed(&ech_body, &config_id_cbb) ||
-      !CBB_add_bytes(&config_id_cbb, ech_config_id, sizeof(ech_config_id)) ||
-      !CBB_add_u16_length_prefixed(&ech_body, &enc_cbb) ||
-      !CBB_add_bytes(&enc_cbb, ech_enc, OPENSSL_ARRAY_SIZE(ech_enc)) ||
-      !CBB_add_u16_length_prefixed(&ech_body, &payload_cbb) ||
-      !CBB_add_bytes(&payload_cbb, payload, payload_len) ||  //
-      !CBB_flush(&ech_body)) {
-    return false;
-  }
-  // Save the bytes of the newly-generated extension in case the server sends
-  // a HelloRetryRequest.
-  if (!hs->ech_grease.CopyFrom(
-          MakeConstSpan(CBB_data(&ech_body), CBB_len(&ech_body)))) {
-    return false;
-  }
-  return CBB_flush(out);
-}
-
-static bool ext_ech_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  if (hs->max_version < TLS1_3_VERSION) {
+  if (hs->ech_client_outer.empty()) {
     return true;
   }
-  if (hs->config->ech_grease_enabled) {
-    return ext_ech_add_clienthello_grease(hs, out);
+
+  CBB ech_body;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_encrypted_client_hello) ||
+      !CBB_add_u16_length_prefixed(out, &ech_body) ||
+      !CBB_add_u8(&ech_body, ECH_CLIENT_OUTER) ||
+      !CBB_add_bytes(&ech_body, hs->ech_client_outer.data(),
+                     hs->ech_client_outer.size()) ||
+      !CBB_flush(out)) {
+    return false;
   }
-  // Nothing to do, since we don't yet implement the non-GREASE parts of ECH.
   return true;
 }
 
 static bool ext_ech_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                       CBS *contents) {
+  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return true;
   }
 
-  // If the client only sent GREASE, we must check the extension syntactically.
-  CBS ech_configs;
-  if (!CBS_get_u16_length_prefixed(contents, &ech_configs) ||
-      CBS_len(&ech_configs) == 0 ||  //
-      CBS_len(contents) > 0) {
+  // The ECH extension may not be sent in TLS 1.2 ServerHello, only TLS 1.3
+  // EncryptedExtensions. It also may not be sent in response to an inner ECH
+  // extension.
+  if (ssl_protocol_version(ssl) < TLS1_3_VERSION ||
+      ssl->s3->ech_status == ssl_ech_accepted) {
+    *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+    return false;
+  }
+
+  if (!ssl_is_valid_ech_config_list(*contents)) {
     *out_alert = SSL_AD_DECODE_ERROR;
     return false;
   }
-  while (CBS_len(&ech_configs) > 0) {
-    // Do a top-level parse of the ECHConfig, stopping before ECHConfigContents.
-    uint16_t version;
-    CBS ech_config_contents;
-    if (!CBS_get_u16(&ech_configs, &version) ||
-        !CBS_get_u16_length_prefixed(&ech_configs, &ech_config_contents)) {
-      *out_alert = SSL_AD_DECODE_ERROR;
-      return false;
-    }
+
+  if (ssl->s3->ech_status == ssl_ech_rejected &&
+      !hs->ech_retry_configs.CopyFrom(*contents)) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return false;
   }
+
   return true;
 }
 
 static bool ext_ech_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                       CBS *contents) {
-  if (contents != nullptr) {
-    hs->ech_present = true;
-    return true;
-  }
-  return true;
-}
-
-static bool ext_ech_is_inner_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  return true;
-}
-
-static bool ext_ech_is_inner_parse_clienthello(SSL_HANDSHAKE *hs,
-                                               uint8_t *out_alert,
-                                               CBS *contents) {
   if (contents == nullptr) {
     return true;
   }
-  if (CBS_len(contents) > 0) {
-    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+
+  uint8_t type;
+  if (!CBS_get_u8(contents, &type)) {
     return false;
   }
-  hs->ech_is_inner_present = true;
+  if (type == ECH_CLIENT_OUTER) {
+    // Outer ECH extensions are handled outside the callback.
+    return true;
+  }
+  if (type != ECH_CLIENT_INNER || CBS_len(contents) != 0) {
+    return false;
+  }
+
+  hs->ech_is_inner = true;
   return true;
 }
 
+static bool ext_ech_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
+  SSL *const ssl = hs->ssl;
+  if (ssl_protocol_version(ssl) < TLS1_3_VERSION ||
+      ssl->s3->ech_status == ssl_ech_accepted ||  //
+      hs->ech_keys == nullptr) {
+    return true;
+  }
+
+  // Write the list of retry configs to |out|. Note |SSL_CTX_set1_ech_keys|
+  // ensures |ech_keys| contains at least one retry config.
+  CBB body, retry_configs;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_encrypted_client_hello) ||
+      !CBB_add_u16_length_prefixed(out, &body) ||
+      !CBB_add_u16_length_prefixed(&body, &retry_configs)) {
+    return false;
+  }
+  for (const auto &config : hs->ech_keys->configs) {
+    if (!config->is_retry_config()) {
+      continue;
+    }
+    if (!CBB_add_bytes(&retry_configs, config->ech_config().raw.data(),
+                       config->ech_config().raw.size())) {
+      return false;
+    }
+  }
+  return CBB_flush(out);
+}
+
 
 // Renegotiation indication.
 //
 // https://tools.ietf.org/html/rfc5746
 
-static bool ext_ri_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
+static bool ext_ri_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                   CBB *out_compressible,
+                                   ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
   // Renegotiation indication is not necessary in TLS 1.3.
-  if (hs->min_version >= TLS1_3_VERSION) {
+  if (hs->min_version >= TLS1_3_VERSION ||
+     type == ssl_client_hello_inner) {
     return true;
   }
 
@@ -936,9 +910,11 @@
 //
 // https://tools.ietf.org/html/rfc7627
 
-static bool ext_ems_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ems_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                    CBB *out_compressible,
+                                    ssl_client_hello_type_t type) {
   // Extended master secret is not necessary in TLS 1.3.
-  if (hs->min_version >= TLS1_3_VERSION) {
+  if (hs->min_version >= TLS1_3_VERSION || type == ssl_client_hello_inner) {
     return true;
   }
 
@@ -1011,10 +987,12 @@
 //
 // https://tools.ietf.org/html/rfc5077
 
-static bool ext_ticket_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
+static bool ext_ticket_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                       CBB *out_compressible,
+                                       ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
   // TLS 1.3 uses a different ticket extension.
-  if (hs->min_version >= TLS1_3_VERSION ||
+  if (hs->min_version >= TLS1_3_VERSION || type == ssl_client_hello_inner ||
       SSL_get_options(ssl) & SSL_OP_NO_TICKET) {
     return true;
   }
@@ -1089,17 +1067,19 @@
 //
 // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
 
-static bool ext_sigalgs_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_sigalgs_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                        CBB *out_compressible,
+                                        ssl_client_hello_type_t type) {
   if (hs->max_version < TLS1_2_VERSION) {
     return true;
   }
 
   CBB contents, sigalgs_cbb;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_signature_algorithms) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_signature_algorithms) ||
+      !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &sigalgs_cbb) ||
       !tls12_add_verify_sigalgs(hs, &sigalgs_cbb) ||
-      !CBB_flush(out)) {
+      !CBB_flush(out_compressible)) {
     return false;
   }
 
@@ -1128,18 +1108,20 @@
 //
 // https://tools.ietf.org/html/rfc6066#section-8
 
-static bool ext_ocsp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ocsp_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                     CBB *out_compressible,
+                                     ssl_client_hello_type_t type) {
   if (!hs->config->ocsp_stapling_enabled) {
     return true;
   }
 
   CBB contents;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_status_request) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_status_request) ||
+      !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
       !CBB_add_u8(&contents, TLSEXT_STATUSTYPE_ocsp) ||
       !CBB_add_u16(&contents, 0 /* empty responder ID list */) ||
       !CBB_add_u16(&contents, 0 /* empty request extensions */) ||
-      !CBB_flush(out)) {
+      !CBB_flush(out_compressible)) {
     return false;
   }
 
@@ -1210,11 +1192,16 @@
 //
 // https://htmlpreview.github.io/?https://github.com/agl/technotes/blob/master/nextprotoneg.html
 
-static bool ext_npn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-  if (ssl->s3->initial_handshake_complete ||
-      ssl->ctx->next_proto_select_cb == NULL ||
-      SSL_is_dtls(ssl)) {
+static bool ext_npn_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                    CBB *out_compressible,
+                                    ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
+  if (ssl->ctx->next_proto_select_cb == NULL ||
+      // Do not allow NPN to change on renegotiation.
+      ssl->s3->initial_handshake_complete ||
+      // NPN is not defined in DTLS or TLS 1.3.
+      SSL_is_dtls(ssl) || hs->min_version >= TLS1_3_VERSION ||
+      type == ssl_client_hello_inner) {
     return true;
   }
 
@@ -1333,13 +1320,15 @@
 //
 // https://tools.ietf.org/html/rfc6962#section-3.3.1
 
-static bool ext_sct_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_sct_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                    CBB *out_compressible,
+                                    ssl_client_hello_type_t type) {
   if (!hs->config->signed_cert_timestamps_enabled) {
     return true;
   }
 
-  if (!CBB_add_u16(out, TLSEXT_TYPE_certificate_timestamp) ||
-      !CBB_add_u16(out, 0 /* length */)) {
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_certificate_timestamp) ||
+      !CBB_add_u16(out_compressible, 0 /* length */)) {
     return false;
   }
 
@@ -1424,11 +1413,13 @@
 //
 // https://tools.ietf.org/html/rfc7301
 
-static bool ext_alpn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
+static bool ext_alpn_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                     CBB *out_compressible,
+                                     ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
   if (hs->config->alpn_client_proto_list.empty() && ssl->quic_method) {
     // ALPN MUST be used with QUIC.
-    OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_ALPN);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_APPLICATION_PROTOCOL);
     return false;
   }
 
@@ -1438,12 +1429,13 @@
   }
 
   CBB contents, proto_list;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_application_layer_protocol_negotiation) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
+  if (!CBB_add_u16(out_compressible,
+                   TLSEXT_TYPE_application_layer_protocol_negotiation) ||
+      !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &proto_list) ||
       !CBB_add_bytes(&proto_list, hs->config->alpn_client_proto_list.data(),
                      hs->config->alpn_client_proto_list.size()) ||
-      !CBB_flush(out)) {
+      !CBB_flush(out_compressible)) {
     return false;
   }
 
@@ -1456,7 +1448,7 @@
   if (contents == NULL) {
     if (ssl->quic_method) {
       // ALPN is required when QUIC is used.
-      OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_ALPN);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_APPLICATION_PROTOCOL);
       *out_alert = SSL_AD_NO_APPLICATION_PROTOCOL;
       return false;
     }
@@ -1499,6 +1491,22 @@
   return true;
 }
 
+bool ssl_is_valid_alpn_list(Span<const uint8_t> in) {
+  CBS protocol_name_list = in;
+  if (CBS_len(&protocol_name_list) == 0) {
+    return false;
+  }
+  while (CBS_len(&protocol_name_list) > 0) {
+    CBS protocol_name;
+    if (!CBS_get_u8_length_prefixed(&protocol_name_list, &protocol_name) ||
+        // Empty protocol names are forbidden.
+        CBS_len(&protocol_name) == 0) {
+      return false;
+    }
+  }
+  return true;
+}
+
 bool ssl_is_alpn_protocol_allowed(const SSL_HANDSHAKE *hs,
                                   Span<const uint8_t> protocol) {
   if (hs->config->alpn_client_proto_list.empty()) {
@@ -1537,7 +1545,7 @@
           TLSEXT_TYPE_application_layer_protocol_negotiation)) {
     if (ssl->quic_method) {
       // ALPN is required when QUIC is used.
-      OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_ALPN);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_APPLICATION_PROTOCOL);
       *out_alert = SSL_AD_NO_APPLICATION_PROTOCOL;
       return false;
     }
@@ -1551,46 +1559,47 @@
   CBS protocol_name_list;
   if (!CBS_get_u16_length_prefixed(&contents, &protocol_name_list) ||
       CBS_len(&contents) != 0 ||
-      CBS_len(&protocol_name_list) < 2) {
+      !ssl_is_valid_alpn_list(protocol_name_list)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     *out_alert = SSL_AD_DECODE_ERROR;
     return false;
   }
 
-  // Validate the protocol list.
-  CBS protocol_name_list_copy = protocol_name_list;
-  while (CBS_len(&protocol_name_list_copy) > 0) {
-    CBS protocol_name;
-    if (!CBS_get_u8_length_prefixed(&protocol_name_list_copy, &protocol_name) ||
-        // Empty protocol names are forbidden.
-        CBS_len(&protocol_name) == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
-      *out_alert = SSL_AD_DECODE_ERROR;
-      return false;
-    }
-  }
-
   const uint8_t *selected;
   uint8_t selected_len;
-  if (ssl->ctx->alpn_select_cb(
-          ssl, &selected, &selected_len, CBS_data(&protocol_name_list),
-          CBS_len(&protocol_name_list),
-          ssl->ctx->alpn_select_cb_arg) == SSL_TLSEXT_ERR_OK) {
-    if (selected_len == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
-      *out_alert = SSL_AD_INTERNAL_ERROR;
+  int ret = ssl->ctx->alpn_select_cb(
+      ssl, &selected, &selected_len, CBS_data(&protocol_name_list),
+      CBS_len(&protocol_name_list), ssl->ctx->alpn_select_cb_arg);
+  // ALPN is required when QUIC is used.
+  if (ssl->quic_method &&
+      (ret == SSL_TLSEXT_ERR_NOACK || ret == SSL_TLSEXT_ERR_ALERT_WARNING)) {
+    ret = SSL_TLSEXT_ERR_ALERT_FATAL;
+  }
+  switch (ret) {
+    case SSL_TLSEXT_ERR_OK:
+      if (selected_len == 0) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL);
+        *out_alert = SSL_AD_INTERNAL_ERROR;
+        return false;
+      }
+      if (!ssl->s3->alpn_selected.CopyFrom(
+              MakeConstSpan(selected, selected_len))) {
+        *out_alert = SSL_AD_INTERNAL_ERROR;
+        return false;
+      }
+      break;
+    case SSL_TLSEXT_ERR_NOACK:
+    case SSL_TLSEXT_ERR_ALERT_WARNING:
+      break;
+    case SSL_TLSEXT_ERR_ALERT_FATAL:
+      *out_alert = SSL_AD_NO_APPLICATION_PROTOCOL;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_APPLICATION_PROTOCOL);
       return false;
-    }
-    if (!ssl->s3->alpn_selected.CopyFrom(
-            MakeConstSpan(selected, selected_len))) {
+    default:
+      // Invalid return value.
       *out_alert = SSL_AD_INTERNAL_ERROR;
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       return false;
-    }
-  } else if (ssl->quic_method) {
-    // ALPN is required when QUIC is used.
-    OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_ALPN);
-    *out_alert = SSL_AD_NO_APPLICATION_PROTOCOL;
-    return false;
   }
 
   return true;
@@ -1621,13 +1630,20 @@
 //
 // https://tools.ietf.org/html/draft-balfanz-tls-channelid-01
 
-static void ext_channel_id_init(SSL_HANDSHAKE *hs) {
-  hs->ssl->s3->channel_id_valid = false;
-}
-
-static bool ext_channel_id_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-  if (!hs->config->channel_id_enabled || SSL_is_dtls(ssl)) {
+static bool ext_channel_id_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                           CBB *out_compressible,
+                                           ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
+  if (!hs->config->channel_id_private || SSL_is_dtls(ssl) ||
+      // Don't offer Channel ID in ClientHelloOuter. ClientHelloOuter handshakes
+      // are not authenticated for the name that can learn the Channel ID.
+      //
+      // We could alternatively offer the extension but sign with a random key.
+      // For other extensions, we try to align |ssl_client_hello_outer| and
+      // |ssl_client_hello_unencrypted|, to improve the effectiveness of ECH
+      // GREASE. However, Channel ID is deprecated and unlikely to be used with
+      // ECH, so do the simplest thing.
+      type == ssl_client_hello_outer) {
     return true;
   }
 
@@ -1642,19 +1658,18 @@
 static bool ext_channel_id_parse_serverhello(SSL_HANDSHAKE *hs,
                                              uint8_t *out_alert,
                                              CBS *contents) {
-  SSL *const ssl = hs->ssl;
   if (contents == NULL) {
     return true;
   }
 
-  assert(!SSL_is_dtls(ssl));
-  assert(hs->config->channel_id_enabled);
+  assert(!SSL_is_dtls(hs->ssl));
+  assert(hs->config->channel_id_private);
 
   if (CBS_len(contents) != 0) {
     return false;
   }
 
-  ssl->s3->channel_id_valid = true;
+  hs->channel_id_negotiated = true;
   return true;
 }
 
@@ -1670,13 +1685,12 @@
     return false;
   }
 
-  ssl->s3->channel_id_valid = true;
+  hs->channel_id_negotiated = true;
   return true;
 }
 
 static bool ext_channel_id_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-  if (!ssl->s3->channel_id_valid) {
+  if (!hs->channel_id_negotiated) {
     return true;
   }
 
@@ -1693,22 +1707,21 @@
 //
 // https://tools.ietf.org/html/rfc5764
 
-
-static void ext_srtp_init(SSL_HANDSHAKE *hs) {
-  hs->ssl->s3->srtp_profile = NULL;
-}
-
-static bool ext_srtp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-  STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl);
+static bool ext_srtp_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                     CBB *out_compressible,
+                                     ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
+  const STACK_OF(SRTP_PROTECTION_PROFILE) *profiles =
+      SSL_get_srtp_profiles(ssl);
   if (profiles == NULL ||
-      sk_SRTP_PROTECTION_PROFILE_num(profiles) == 0) {
+      sk_SRTP_PROTECTION_PROFILE_num(profiles) == 0 ||
+      !SSL_is_dtls(ssl)) {
     return true;
   }
 
   CBB contents, profile_ids;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_srtp) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_srtp) ||
+      !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &profile_ids)) {
     return false;
   }
@@ -1720,7 +1733,7 @@
   }
 
   if (!CBB_add_u8(&contents, 0 /* empty use_mki value */) ||
-      !CBB_flush(out)) {
+      !CBB_flush(out_compressible)) {
     return false;
   }
 
@@ -1738,6 +1751,7 @@
   // single uint16_t profile ID, then followed by a u8-prefixed srtp_mki field.
   //
   // See https://tools.ietf.org/html/rfc5764#section-4.1.1
+  assert(SSL_is_dtls(ssl));
   CBS profile_ids, srtp_mki;
   uint16_t profile_id;
   if (!CBS_get_u16_length_prefixed(contents, &profile_ids) ||
@@ -1756,11 +1770,8 @@
     return false;
   }
 
-  STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl);
-
-  // Check to see if the server gave us something we support (and presumably
-  // offered).
-  for (const SRTP_PROTECTION_PROFILE *profile : profiles) {
+  // Check to see if the server gave us something we support and offered.
+  for (const SRTP_PROTECTION_PROFILE *profile : SSL_get_srtp_profiles(ssl)) {
     if (profile->id == profile_id) {
       ssl->s3->srtp_profile = profile;
       return true;
@@ -1775,7 +1786,8 @@
 static bool ext_srtp_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                                        CBS *contents) {
   SSL *const ssl = hs->ssl;
-  if (contents == NULL) {
+  // DTLS-SRTP is only defined for DTLS.
+  if (contents == NULL || !SSL_is_dtls(ssl)) {
     return true;
   }
 
@@ -1819,6 +1831,7 @@
     return true;
   }
 
+  assert(SSL_is_dtls(ssl));
   CBB contents, profile_ids;
   if (!CBB_add_u16(out, TLSEXT_TYPE_srtp) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
@@ -1837,7 +1850,7 @@
 //
 // https://tools.ietf.org/html/rfc4492#section-5.1.2
 
-static bool ext_ec_point_add_extension(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ec_point_add_extension(const SSL_HANDSHAKE *hs, CBB *out) {
   CBB contents, formats;
   if (!CBB_add_u16(out, TLSEXT_TYPE_ec_point_formats) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
@@ -1850,9 +1863,11 @@
   return true;
 }
 
-static bool ext_ec_point_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_ec_point_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                         CBB *out_compressible,
+                                         ssl_client_hello_type_t type) {
   // The point format extension is unnecessary in TLS 1.3.
-  if (hs->min_version >= TLS1_3_VERSION) {
+  if (hs->min_version >= TLS1_3_VERSION || type == ssl_client_hello_inner) {
     return true;
   }
 
@@ -1918,10 +1933,34 @@
 //
 // https://tools.ietf.org/html/rfc8446#section-4.2.11
 
-static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
+static bool should_offer_psk(const SSL_HANDSHAKE *hs,
+                             ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
   if (hs->max_version < TLS1_3_VERSION || ssl->session == nullptr ||
-      ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION) {
+      ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
+      // TODO(https://crbug.com/boringssl/275): Should we synthesize a
+      // placeholder PSK, at least when we offer early data? Otherwise
+      // ClientHelloOuter will contain an early_data extension without a
+      // pre_shared_key extension and potentially break the recovery flow.
+      type == ssl_client_hello_outer) {
+    return false;
+  }
+
+  // Per RFC 8446 section 4.1.4, skip offering the session if the selected
+  // cipher in HelloRetryRequest does not match. This avoids performing the
+  // transcript hash transformation for multiple hashes.
+  if (ssl->s3->used_hello_retry_request &&
+      ssl->session->cipher->algorithm_prf != hs->new_cipher->algorithm_prf) {
+    return false;
+  }
+
+  return true;
+}
+
+static size_t ext_pre_shared_key_clienthello_length(
+    const SSL_HANDSHAKE *hs, ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
+  if (!should_offer_psk(hs, type)) {
     return 0;
   }
 
@@ -1929,19 +1968,12 @@
   return 15 + ssl->session->ticket.size() + binder_len;
 }
 
-static bool ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-  hs->needs_psk_binder = false;
-  if (hs->max_version < TLS1_3_VERSION || ssl->session == nullptr ||
-      ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION) {
-    return true;
-  }
-
-  // Per RFC 8446 section 4.1.4, skip offering the session if the selected
-  // cipher in HelloRetryRequest does not match. This avoids performing the
-  // transcript hash transformation for multiple hashes.
-  if (ssl->s3 && ssl->s3->used_hello_retry_request &&
-      ssl->session->cipher->algorithm_prf != hs->new_cipher->algorithm_prf) {
+static bool ext_pre_shared_key_add_clienthello(const SSL_HANDSHAKE *hs,
+                                               CBB *out, bool *out_needs_binder,
+                                               ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
+  *out_needs_binder = false;
+  if (!should_offer_psk(hs, type)) {
     return true;
   }
 
@@ -1952,7 +1984,6 @@
 
   // Fill in a placeholder zero binder of the appropriate length. It will be
   // computed and filled in later after length prefixes are computed.
-  uint8_t zero_binder[EVP_MAX_MD_SIZE] = {0};
   size_t binder_len = EVP_MD_size(ssl_session_get_digest(ssl->session.get()));
 
   CBB contents, identity, ticket, binders, binder;
@@ -1965,11 +1996,11 @@
       !CBB_add_u32(&identity, obfuscated_ticket_age) ||
       !CBB_add_u16_length_prefixed(&contents, &binders) ||
       !CBB_add_u8_length_prefixed(&binders, &binder) ||
-      !CBB_add_bytes(&binder, zero_binder, binder_len)) {
+      !CBB_add_zeros(&binder, binder_len)) {
     return false;
   }
 
-  hs->needs_psk_binder = true;
+  *out_needs_binder = true;
   return CBB_flush(out);
 }
 
@@ -2082,21 +2113,22 @@
 //
 // https://tools.ietf.org/html/rfc8446#section-4.2.9
 
-static bool ext_psk_key_exchange_modes_add_clienthello(SSL_HANDSHAKE *hs,
-                                                       CBB *out) {
+static bool ext_psk_key_exchange_modes_add_clienthello(
+    const SSL_HANDSHAKE *hs, CBB *out, CBB *out_compressible,
+    ssl_client_hello_type_t type) {
   if (hs->max_version < TLS1_3_VERSION) {
     return true;
   }
 
   CBB contents, ke_modes;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_psk_key_exchange_modes) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_psk_key_exchange_modes) ||
+      !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
       !CBB_add_u8_length_prefixed(&contents, &ke_modes) ||
       !CBB_add_u8(&ke_modes, SSL_PSK_DHE_KE)) {
     return false;
   }
 
-  return CBB_flush(out);
+  return CBB_flush(out_compressible);
 }
 
 static bool ext_psk_key_exchange_modes_parse_clienthello(SSL_HANDSHAKE *hs,
@@ -2126,23 +2158,10 @@
 //
 // https://tools.ietf.org/html/rfc8446#section-4.2.10
 
-// ssl_get_local_application_settings looks up the configured ALPS value for
-// |protocol|. If found, it sets |*out_settings| to the value and returns true.
-// Otherwise, it returns false.
-static bool ssl_get_local_application_settings(
-    const SSL_HANDSHAKE *hs, Span<const uint8_t> *out_settings,
-    Span<const uint8_t> protocol) {
-  for (const ALPSConfig &config : hs->config->alps_configs) {
-    if (protocol == config.protocol) {
-      *out_settings = config.settings;
-      return true;
-    }
-  }
-  return false;
-}
-
-static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
+static bool ext_early_data_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                           CBB *out_compressible,
+                                           ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
   // The second ClientHello never offers early data, and we must have already
   // filled in |early_data_reason| by this point.
   if (ssl->s3->used_hello_retry_request) {
@@ -2150,56 +2169,20 @@
     return true;
   }
 
-  if (!ssl->enable_early_data) {
-    ssl->s3->early_data_reason = ssl_early_data_disabled;
+  if (!hs->early_data_offered) {
     return true;
   }
 
-  if (hs->max_version < TLS1_3_VERSION) {
-    // We discard inapplicable sessions, so this is redundant with the session
-    // checks below, but we check give a more useful reason.
-    ssl->s3->early_data_reason = ssl_early_data_protocol_version;
-    return true;
-  }
-
-  if (ssl->session == nullptr) {
-    ssl->s3->early_data_reason = ssl_early_data_no_session_offered;
-    return true;
-  }
-
-  if (ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
-      ssl->session->ticket_max_early_data == 0) {
-    ssl->s3->early_data_reason = ssl_early_data_unsupported_for_session;
-    return true;
-  }
-
-  if (!ssl->session->early_alpn.empty()) {
-    if (!ssl_is_alpn_protocol_allowed(hs, ssl->session->early_alpn)) {
-      // Avoid reporting a confusing value in |SSL_get0_alpn_selected|.
-      ssl->s3->early_data_reason = ssl_early_data_alpn_mismatch;
-      return true;
-    }
-
-    // If the previous connection negotiated ALPS, only offer 0-RTT when the
-    // local are settings are consistent with what we'd offer for this
-    // connection.
-    if (ssl->session->has_application_settings) {
-      Span<const uint8_t> settings;
-      if (!ssl_get_local_application_settings(hs, &settings,
-                                              ssl->session->early_alpn) ||
-          settings != ssl->session->local_application_settings) {
-        ssl->s3->early_data_reason = ssl_early_data_alps_mismatch;
-        return true;
-      }
-    }
-  }
-
-  // |early_data_reason| will be filled in later when the server responds.
-  hs->early_data_offered = true;
-
-  if (!CBB_add_u16(out, TLSEXT_TYPE_early_data) ||
-      !CBB_add_u16(out, 0) ||
-      !CBB_flush(out)) {
+  // If offering ECH, the extension only applies to ClientHelloInner, but we
+  // send the extension in both ClientHellos. This ensures that, if the server
+  // handshakes with ClientHelloOuter, it can skip past early data. See
+  // https://github.com/tlswg/draft-ietf-tls-esni/pull/415
+  //
+  // TODO(https://crbug.com/boringssl/275): Replace this with a reference to the
+  // right section in the next draft.
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_early_data) ||
+      !CBB_add_u16(out_compressible, 0) ||
+      !CBB_flush(out_compressible)) {
     return false;
   }
 
@@ -2280,43 +2263,33 @@
 //
 // https://tools.ietf.org/html/rfc8446#section-4.2.8
 
-static bool ext_key_share_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+bool ssl_setup_key_shares(SSL_HANDSHAKE *hs, uint16_t override_group_id) {
   SSL *const ssl = hs->ssl;
+  hs->key_shares[0].reset();
+  hs->key_shares[1].reset();
+  hs->key_share_bytes.Reset();
+
   if (hs->max_version < TLS1_3_VERSION) {
     return true;
   }
 
-  CBB contents, kse_bytes;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_key_share) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
-      !CBB_add_u16_length_prefixed(&contents, &kse_bytes)) {
+  bssl::ScopedCBB cbb;
+  if (!CBB_init(cbb.get(), 64)) {
     return false;
   }
 
-  uint16_t group_id = hs->retry_group;
-  uint16_t second_group_id = 0;
-  if (ssl->s3 && ssl->s3->used_hello_retry_request) {
-    // We received a HelloRetryRequest without a new curve, so there is no new
-    // share to append. Leave |hs->key_share| as-is.
-    if (group_id == 0 &&
-        !CBB_add_bytes(&kse_bytes, hs->key_share_bytes.data(),
-                       hs->key_share_bytes.size())) {
+  if (override_group_id == 0 && ssl->ctx->grease_enabled) {
+    // Add a fake group. See RFC 8701.
+    if (!CBB_add_u16(cbb.get(), ssl_get_grease_value(hs, ssl_grease_group)) ||
+        !CBB_add_u16(cbb.get(), 1 /* length */) ||
+        !CBB_add_u8(cbb.get(), 0 /* one byte key share */)) {
       return false;
     }
-    hs->key_share_bytes.Reset();
-    if (group_id == 0) {
-      return CBB_flush(out);
-    }
-  } else {
-    // Add a fake group. See draft-davidben-tls-grease-01.
-    if (ssl->ctx->grease_enabled &&
-        (!CBB_add_u16(&kse_bytes,
-                      ssl_get_grease_value(hs, ssl_grease_group)) ||
-         !CBB_add_u16(&kse_bytes, 1 /* length */) ||
-         !CBB_add_u8(&kse_bytes, 0 /* one byte key share */))) {
-      return false;
-    }
+  }
 
+  uint16_t group_id = override_group_id;
+  uint16_t second_group_id = 0;
+  if (override_group_id == 0) {
     // Predict the most preferred group.
     Span<const uint16_t> groups = tls1_get_grouplist(hs);
     if (groups.empty()) {
@@ -2336,34 +2309,45 @@
 
   CBB key_exchange;
   hs->key_shares[0] = SSLKeyShare::Create(group_id);
-  if (!hs->key_shares[0] ||
-      !CBB_add_u16(&kse_bytes, group_id) ||
-      !CBB_add_u16_length_prefixed(&kse_bytes, &key_exchange) ||
-      !hs->key_shares[0]->Offer(&key_exchange) ||
-      !CBB_flush(&kse_bytes)) {
+  if (!hs->key_shares[0] ||  //
+      !CBB_add_u16(cbb.get(), group_id) ||
+      !CBB_add_u16_length_prefixed(cbb.get(), &key_exchange) ||
+      !hs->key_shares[0]->Offer(&key_exchange)) {
     return false;
   }
 
   if (second_group_id != 0) {
     hs->key_shares[1] = SSLKeyShare::Create(second_group_id);
-    if (!hs->key_shares[1] ||
-        !CBB_add_u16(&kse_bytes, second_group_id) ||
-        !CBB_add_u16_length_prefixed(&kse_bytes, &key_exchange) ||
-        !hs->key_shares[1]->Offer(&key_exchange) ||
-        !CBB_flush(&kse_bytes)) {
+    if (!hs->key_shares[1] ||  //
+        !CBB_add_u16(cbb.get(), second_group_id) ||
+        !CBB_add_u16_length_prefixed(cbb.get(), &key_exchange) ||
+        !hs->key_shares[1]->Offer(&key_exchange)) {
       return false;
     }
   }
 
-  // Save the contents of the extension to repeat it in the second
-  // ClientHello.
-  if (ssl->s3 && !ssl->s3->used_hello_retry_request &&
-      !hs->key_share_bytes.CopyFrom(
-          MakeConstSpan(CBB_data(&kse_bytes), CBB_len(&kse_bytes)))) {
+  return CBBFinishArray(cbb.get(), &hs->key_share_bytes);
+}
+
+static bool ext_key_share_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                          CBB *out_compressible,
+                                          ssl_client_hello_type_t type) {
+  if (hs->max_version < TLS1_3_VERSION) {
+    return true;
+  }
+
+  assert(!hs->key_share_bytes.empty());
+  CBB contents, kse_bytes;
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_key_share) ||
+      !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
+      !CBB_add_u16_length_prefixed(&contents, &kse_bytes) ||
+      !CBB_add_bytes(&kse_bytes, hs->key_share_bytes.data(),
+                     hs->key_share_bytes.size()) ||
+      !CBB_flush(out_compressible)) {
     return false;
   }
 
-  return CBB_flush(out);
+  return true;
 }
 
 bool ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs,
@@ -2401,25 +2385,29 @@
 }
 
 bool ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, bool *out_found,
-                                         Array<uint8_t> *out_secret,
-                                         uint8_t *out_alert, CBS *contents) {
-  uint16_t group_id;
-  CBS key_shares;
-  if (!tls1_get_shared_group(hs, &group_id)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_GROUP);
-    *out_alert = SSL_AD_HANDSHAKE_FAILURE;
+                                         Span<const uint8_t> *out_peer_key,
+                                         uint8_t *out_alert,
+                                         const SSL_CLIENT_HELLO *client_hello) {
+  // We only support connections that include an ECDHE key exchange.
+  CBS contents;
+  if (!ssl_client_hello_get_extension(client_hello, &contents,
+                                      TLSEXT_TYPE_key_share)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
+    *out_alert = SSL_AD_MISSING_EXTENSION;
     return false;
   }
 
-  if (!CBS_get_u16_length_prefixed(contents, &key_shares) ||
-      CBS_len(contents) != 0) {
+  CBS key_shares;
+  if (!CBS_get_u16_length_prefixed(&contents, &key_shares) ||
+      CBS_len(&contents) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     return false;
   }
 
   // Find the corresponding key share.
+  const uint16_t group_id = hs->new_session->group_id;
   CBS peer_key;
-  CBS_init(&peer_key, NULL, 0);
+  CBS_init(&peer_key, nullptr, 0);
   while (CBS_len(&key_shares) > 0) {
     uint16_t id;
     CBS peer_key_tmp;
@@ -2442,47 +2430,24 @@
     }
   }
 
-  if (CBS_len(&peer_key) == 0) {
-    *out_found = false;
-    out_secret->Reset();
-    return true;
+  if (out_peer_key != nullptr) {
+    *out_peer_key = peer_key;
   }
-
-  // Compute the DH secret.
-  Array<uint8_t> secret;
-  ScopedCBB public_key;
-  UniquePtr<SSLKeyShare> key_share = SSLKeyShare::Create(group_id);
-  if (!key_share ||
-      !CBB_init(public_key.get(), 32) ||
-      !key_share->Accept(public_key.get(), &secret, out_alert, peer_key) ||
-      !CBBFinishArray(public_key.get(), &hs->ecdh_public_key)) {
-    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return false;
-  }
-
-  *out_secret = std::move(secret);
-  *out_found = true;
+  *out_found = CBS_len(&peer_key) != 0;
   return true;
 }
 
-bool ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out,
-                                       bool dry_run) {
-  uint16_t group_id;
+bool ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
   CBB kse_bytes, public_key;
-  if (!tls1_get_shared_group(hs, &group_id) ||
-      !CBB_add_u16(out, TLSEXT_TYPE_key_share) ||
+  if (!CBB_add_u16(out, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16_length_prefixed(out, &kse_bytes) ||
-      !CBB_add_u16(&kse_bytes, group_id) ||
+      !CBB_add_u16(&kse_bytes, hs->new_session->group_id) ||
       !CBB_add_u16_length_prefixed(&kse_bytes, &public_key) ||
       !CBB_add_bytes(&public_key, hs->ecdh_public_key.data(),
                      hs->ecdh_public_key.size()) ||
       !CBB_flush(out)) {
     return false;
   }
-  if (!dry_run) {
-    hs->ecdh_public_key.Reset();
-    hs->new_session->group_id = group_id;
-  }
   return true;
 }
 
@@ -2491,12 +2456,20 @@
 //
 // https://tools.ietf.org/html/rfc8446#section-4.2.1
 
-static bool ext_supported_versions_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
+static bool ext_supported_versions_add_clienthello(
+    const SSL_HANDSHAKE *hs, CBB *out, CBB *out_compressible,
+    ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
   if (hs->max_version <= TLS1_2_VERSION) {
     return true;
   }
 
+  // supported_versions is compressible in ECH if ClientHelloOuter already
+  // requires TLS 1.3. Otherwise the extensions differ in the older versions.
+  if (hs->min_version >= TLS1_3_VERSION) {
+    out = out_compressible;
+  }
+
   CBB contents, versions;
   if (!CBB_add_u16(out, TLSEXT_TYPE_supported_versions) ||
       !CBB_add_u16_length_prefixed(out, &contents) ||
@@ -2504,13 +2477,16 @@
     return false;
   }
 
-  // Add a fake version. See draft-davidben-tls-grease-01.
+  // Add a fake version. See RFC 8701.
   if (ssl->ctx->grease_enabled &&
       !CBB_add_u16(&versions, ssl_get_grease_value(hs, ssl_grease_version))) {
     return false;
   }
 
-  if (!ssl_add_supported_versions(hs, &versions) ||
+  // Encrypted ClientHellos requires TLS 1.3 or later.
+  uint16_t extra_min_version =
+      type == ssl_client_hello_inner ? TLS1_3_VERSION : 0;
+  if (!ssl_add_supported_versions(hs, &versions, extra_min_version) ||
       !CBB_flush(out)) {
     return false;
   }
@@ -2523,22 +2499,22 @@
 //
 // https://tools.ietf.org/html/rfc8446#section-4.2.2
 
-static bool ext_cookie_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool ext_cookie_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                       CBB *out_compressible,
+                                       ssl_client_hello_type_t type) {
   if (hs->cookie.empty()) {
     return true;
   }
 
   CBB contents, cookie;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_cookie) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_cookie) ||
+      !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &cookie) ||
       !CBB_add_bytes(&cookie, hs->cookie.data(), hs->cookie.size()) ||
-      !CBB_flush(out)) {
+      !CBB_flush(out_compressible)) {
     return false;
   }
 
-  // The cookie is no longer needed in memory.
-  hs->cookie.Reset();
   return true;
 }
 
@@ -2548,16 +2524,19 @@
 // https://tools.ietf.org/html/rfc4492#section-5.1.1
 // https://tools.ietf.org/html/rfc8446#section-4.2.7
 
-static bool ext_supported_groups_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
+static bool ext_supported_groups_add_clienthello(const SSL_HANDSHAKE *hs,
+                                                 CBB *out,
+                                                 CBB *out_compressible,
+                                                 ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
   CBB contents, groups_bytes;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_supported_groups) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_supported_groups) ||
+      !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &groups_bytes)) {
     return false;
   }
 
-  // Add a fake group. See draft-davidben-tls-grease-01.
+  // Add a fake group. See RFC 8701.
   if (ssl->ctx->grease_enabled &&
       !CBB_add_u16(&groups_bytes,
                    ssl_get_grease_value(hs, ssl_grease_group))) {
@@ -2574,7 +2553,7 @@
     }
   }
 
-  return CBB_flush(out);
+  return CBB_flush(out_compressible);
 }
 
 static bool ext_supported_groups_parse_serverhello(SSL_HANDSHAKE *hs,
@@ -2626,158 +2605,11 @@
   return true;
 }
 
-// Token Binding
-//
-// https://tools.ietf.org/html/draft-ietf-tokbind-negotiation-10
-
-// The Token Binding version number currently matches the draft number of
-// draft-ietf-tokbind-protocol, and when published as an RFC it will be 0x0100.
-// Since there are no wire changes to the protocol from draft 13 through the
-// current draft (16), this implementation supports all versions in that range.
-static uint16_t kTokenBindingMaxVersion = 16;
-static uint16_t kTokenBindingMinVersion = 13;
-
-static bool ext_token_binding_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-  if (hs->config->token_binding_params.empty() || SSL_is_dtls(ssl)) {
-    return true;
-  }
-
-  CBB contents, params;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_token_binding) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
-      !CBB_add_u16(&contents, kTokenBindingMaxVersion) ||
-      !CBB_add_u8_length_prefixed(&contents, &params) ||
-      !CBB_add_bytes(&params, hs->config->token_binding_params.data(),
-                     hs->config->token_binding_params.size()) ||
-      !CBB_flush(out)) {
-    return false;
-  }
-
-  return true;
-}
-
-static bool ext_token_binding_parse_serverhello(SSL_HANDSHAKE *hs,
-                                                uint8_t *out_alert,
-                                                CBS *contents) {
-  SSL *const ssl = hs->ssl;
-  if (contents == nullptr) {
-    return true;
-  }
-
-  CBS params_list;
-  uint16_t version;
-  uint8_t param;
-  if (!CBS_get_u16(contents, &version) ||
-      !CBS_get_u8_length_prefixed(contents, &params_list) ||
-      !CBS_get_u8(&params_list, &param) ||
-      CBS_len(&params_list) > 0 ||
-      CBS_len(contents) > 0) {
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return false;
-  }
-
-  // The server-negotiated version must be less than or equal to our version.
-  if (version > kTokenBindingMaxVersion) {
-    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return false;
-  }
-
-  // If the server-selected version is less than what we support, then Token
-  // Binding wasn't negotiated (but the extension was parsed successfully).
-  if (version < kTokenBindingMinVersion) {
-    return true;
-  }
-
-  for (uint8_t config_param : hs->config->token_binding_params) {
-    if (param == config_param) {
-      ssl->s3->negotiated_token_binding_param = param;
-      ssl->s3->token_binding_negotiated = true;
-      return true;
-    }
-  }
-
-  *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-  return false;
-}
-
-// select_tb_param looks for the first token binding param in
-// |hs->ssl->token_binding_params| that is also in |params| and puts it in
-// |hs->ssl->negotiated_token_binding_param|. It returns true if a token binding
-// param is found, and false otherwise.
-static bool select_tb_param(SSL_HANDSHAKE *hs,
-                            Span<const uint8_t> peer_params) {
-  for (uint8_t tb_param : hs->config->token_binding_params) {
-    for (uint8_t peer_param : peer_params) {
-      if (tb_param == peer_param) {
-        hs->ssl->s3->negotiated_token_binding_param = tb_param;
-        return true;
-      }
-    }
-  }
-  return false;
-}
-
-static bool ext_token_binding_parse_clienthello(SSL_HANDSHAKE *hs,
-                                                uint8_t *out_alert,
-                                                CBS *contents) {
-  SSL *const ssl = hs->ssl;
-  if (contents == nullptr || hs->config->token_binding_params.empty()) {
-    return true;
-  }
-
-  CBS params;
-  uint16_t version;
-  if (!CBS_get_u16(contents, &version) ||
-      !CBS_get_u8_length_prefixed(contents, &params) ||
-      CBS_len(&params) == 0 ||
-      CBS_len(contents) > 0) {
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return false;
-  }
-
-  // If the client-selected version is less than what we support, then Token
-  // Binding wasn't negotiated (but the extension was parsed successfully).
-  if (version < kTokenBindingMinVersion) {
-    return true;
-  }
-
-  // If the client-selected version is higher than we support, use our max
-  // version. Otherwise, use the client's version.
-  hs->negotiated_token_binding_version =
-      std::min(version, kTokenBindingMaxVersion);
-  if (!select_tb_param(hs, params)) {
-    return true;
-  }
-
-  ssl->s3->token_binding_negotiated = true;
-  return true;
-}
-
-static bool ext_token_binding_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
-
-  if (!ssl->s3->token_binding_negotiated) {
-    return true;
-  }
-
-  CBB contents, params;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_token_binding) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
-      !CBB_add_u16(&contents, hs->negotiated_token_binding_version) ||
-      !CBB_add_u8_length_prefixed(&contents, &params) ||
-      !CBB_add_u8(&params, ssl->s3->negotiated_token_binding_param) ||
-      !CBB_flush(out)) {
-    return false;
-  }
-
-  return true;
-}
 
 // QUIC Transport Parameters
 
 static bool ext_quic_transport_params_add_clienthello_impl(
-    SSL_HANDSHAKE *hs, CBB *out, bool use_legacy_codepoint) {
+    const SSL_HANDSHAKE *hs, CBB *out, bool use_legacy_codepoint) {
   if (hs->config->quic_transport_params.empty() && !hs->ssl->quic_method) {
     return true;
   }
@@ -2794,7 +2626,7 @@
     return true;
   }
 
-  uint16_t extension_type = TLSEXT_TYPE_quic_transport_parameters_standard;
+  uint16_t extension_type = TLSEXT_TYPE_quic_transport_parameters;
   if (hs->config->quic_use_legacy_codepoint) {
     extension_type = TLSEXT_TYPE_quic_transport_parameters_legacy;
   }
@@ -2810,16 +2642,18 @@
   return true;
 }
 
-static bool ext_quic_transport_params_add_clienthello(SSL_HANDSHAKE *hs,
-                                                      CBB *out) {
+static bool ext_quic_transport_params_add_clienthello(
+    const SSL_HANDSHAKE *hs, CBB *out, CBB *out_compressible,
+    ssl_client_hello_type_t type) {
   return ext_quic_transport_params_add_clienthello_impl(
-      hs, out, /*use_legacy_codepoint=*/false);
+      hs, out_compressible, /*use_legacy_codepoint=*/false);
 }
 
-static bool ext_quic_transport_params_add_clienthello_legacy(SSL_HANDSHAKE *hs,
-                                                             CBB *out) {
+static bool ext_quic_transport_params_add_clienthello_legacy(
+    const SSL_HANDSHAKE *hs, CBB *out, CBB *out_compressible,
+    ssl_client_hello_type_t type) {
   return ext_quic_transport_params_add_clienthello_impl(
-      hs, out, /*use_legacy_codepoint=*/true);
+      hs, out_compressible, /*use_legacy_codepoint=*/true);
 }
 
 static bool ext_quic_transport_params_parse_serverhello_impl(
@@ -2930,7 +2764,7 @@
     return true;
   }
 
-  uint16_t extension_type = TLSEXT_TYPE_quic_transport_parameters_standard;
+  uint16_t extension_type = TLSEXT_TYPE_quic_transport_parameters;
   if (hs->config->quic_use_legacy_codepoint) {
     extension_type = TLSEXT_TYPE_quic_transport_parameters_legacy;
   }
@@ -2963,8 +2797,9 @@
 //
 // https://tools.ietf.org/html/draft-ietf-tls-subcerts
 
-static bool ext_delegated_credential_add_clienthello(SSL_HANDSHAKE *hs,
-                                                     CBB *out) {
+static bool ext_delegated_credential_add_clienthello(
+    const SSL_HANDSHAKE *hs, CBB *out, CBB *out_compressible,
+    ssl_client_hello_type_t type) {
   return true;
 }
 
@@ -2993,7 +2828,9 @@
 
 // Certificate compression
 
-static bool cert_compression_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
+static bool cert_compression_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                             CBB *out_compressible,
+                                             ssl_client_hello_type_t type) {
   bool first = true;
   CBB contents, algs;
 
@@ -3002,9 +2839,10 @@
       continue;
     }
 
-    if (first && (!CBB_add_u16(out, TLSEXT_TYPE_cert_compression) ||
-                  !CBB_add_u16_length_prefixed(out, &contents) ||
-                  !CBB_add_u8_length_prefixed(&contents, &algs))) {
+    if (first &&
+        (!CBB_add_u16(out_compressible, TLSEXT_TYPE_cert_compression) ||
+         !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
+         !CBB_add_u8_length_prefixed(&contents, &algs))) {
       return false;
     }
     first = false;
@@ -3013,7 +2851,7 @@
     }
   }
 
-  return first || CBB_flush(out);
+  return first || CBB_flush(out_compressible);
 }
 
 static bool cert_compression_parse_serverhello(SSL_HANDSHAKE *hs,
@@ -3099,8 +2937,22 @@
 //
 // https://tools.ietf.org/html/draft-vvv-tls-alps-01
 
-static bool ext_alps_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
+bool ssl_get_local_application_settings(const SSL_HANDSHAKE *hs,
+                                        Span<const uint8_t> *out_settings,
+                                        Span<const uint8_t> protocol) {
+  for (const ALPSConfig &config : hs->config->alps_configs) {
+    if (protocol == config.protocol) {
+      *out_settings = config.settings;
+      return true;
+    }
+  }
+  return false;
+}
+
+static bool ext_alps_add_clienthello(const SSL_HANDSHAKE *hs, CBB *out,
+                                     CBB *out_compressible,
+                                     ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
   if (// ALPS requires TLS 1.3.
       hs->max_version < TLS1_3_VERSION ||
       // Do not offer ALPS without ALPN.
@@ -3113,8 +2965,8 @@
   }
 
   CBB contents, proto_list, proto;
-  if (!CBB_add_u16(out, TLSEXT_TYPE_application_settings) ||
-      !CBB_add_u16_length_prefixed(out, &contents) ||
+  if (!CBB_add_u16(out_compressible, TLSEXT_TYPE_application_settings) ||
+      !CBB_add_u16_length_prefixed(out_compressible, &contents) ||
       !CBB_add_u16_length_prefixed(&contents, &proto_list)) {
     return false;
   }
@@ -3127,7 +2979,7 @@
     }
   }
 
-  return CBB_flush(out);
+  return CBB_flush(out_compressible);
 }
 
 static bool ext_alps_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
@@ -3238,7 +3090,6 @@
 static const struct tls_extension kExtensions[] = {
   {
     TLSEXT_TYPE_server_name,
-    NULL,
     ext_sni_add_clienthello,
     ext_sni_parse_serverhello,
     ext_sni_parse_clienthello,
@@ -3246,23 +3097,13 @@
   },
   {
     TLSEXT_TYPE_encrypted_client_hello,
-    NULL,
     ext_ech_add_clienthello,
     ext_ech_parse_serverhello,
     ext_ech_parse_clienthello,
-    dont_add_serverhello,
-  },
-  {
-    TLSEXT_TYPE_ech_is_inner,
-    NULL,
-    ext_ech_is_inner_add_clienthello,
-    forbid_parse_serverhello,
-    ext_ech_is_inner_parse_clienthello,
-    dont_add_serverhello,
+    ext_ech_add_serverhello,
   },
   {
     TLSEXT_TYPE_extended_master_secret,
-    NULL,
     ext_ems_add_clienthello,
     ext_ems_parse_serverhello,
     ext_ems_parse_clienthello,
@@ -3270,7 +3111,6 @@
   },
   {
     TLSEXT_TYPE_renegotiate,
-    NULL,
     ext_ri_add_clienthello,
     ext_ri_parse_serverhello,
     ext_ri_parse_clienthello,
@@ -3278,7 +3118,6 @@
   },
   {
     TLSEXT_TYPE_supported_groups,
-    NULL,
     ext_supported_groups_add_clienthello,
     ext_supported_groups_parse_serverhello,
     ext_supported_groups_parse_clienthello,
@@ -3286,7 +3125,6 @@
   },
   {
     TLSEXT_TYPE_ec_point_formats,
-    NULL,
     ext_ec_point_add_clienthello,
     ext_ec_point_parse_serverhello,
     ext_ec_point_parse_clienthello,
@@ -3294,7 +3132,6 @@
   },
   {
     TLSEXT_TYPE_session_ticket,
-    NULL,
     ext_ticket_add_clienthello,
     ext_ticket_parse_serverhello,
     // Ticket extension client parsing is handled in ssl_session.c
@@ -3303,7 +3140,6 @@
   },
   {
     TLSEXT_TYPE_application_layer_protocol_negotiation,
-    NULL,
     ext_alpn_add_clienthello,
     ext_alpn_parse_serverhello,
     // ALPN is negotiated late in |ssl_negotiate_alpn|.
@@ -3312,7 +3148,6 @@
   },
   {
     TLSEXT_TYPE_status_request,
-    NULL,
     ext_ocsp_add_clienthello,
     ext_ocsp_parse_serverhello,
     ext_ocsp_parse_clienthello,
@@ -3320,7 +3155,6 @@
   },
   {
     TLSEXT_TYPE_signature_algorithms,
-    NULL,
     ext_sigalgs_add_clienthello,
     forbid_parse_serverhello,
     ext_sigalgs_parse_clienthello,
@@ -3328,7 +3162,6 @@
   },
   {
     TLSEXT_TYPE_next_proto_neg,
-    NULL,
     ext_npn_add_clienthello,
     ext_npn_parse_serverhello,
     ext_npn_parse_clienthello,
@@ -3336,7 +3169,6 @@
   },
   {
     TLSEXT_TYPE_certificate_timestamp,
-    NULL,
     ext_sct_add_clienthello,
     ext_sct_parse_serverhello,
     ext_sct_parse_clienthello,
@@ -3344,7 +3176,6 @@
   },
   {
     TLSEXT_TYPE_channel_id,
-    ext_channel_id_init,
     ext_channel_id_add_clienthello,
     ext_channel_id_parse_serverhello,
     ext_channel_id_parse_clienthello,
@@ -3352,7 +3183,6 @@
   },
   {
     TLSEXT_TYPE_srtp,
-    ext_srtp_init,
     ext_srtp_add_clienthello,
     ext_srtp_parse_serverhello,
     ext_srtp_parse_clienthello,
@@ -3360,7 +3190,6 @@
   },
   {
     TLSEXT_TYPE_key_share,
-    NULL,
     ext_key_share_add_clienthello,
     forbid_parse_serverhello,
     ignore_parse_clienthello,
@@ -3368,7 +3197,6 @@
   },
   {
     TLSEXT_TYPE_psk_key_exchange_modes,
-    NULL,
     ext_psk_key_exchange_modes_add_clienthello,
     forbid_parse_serverhello,
     ext_psk_key_exchange_modes_parse_clienthello,
@@ -3376,7 +3204,6 @@
   },
   {
     TLSEXT_TYPE_early_data,
-    NULL,
     ext_early_data_add_clienthello,
     ext_early_data_parse_serverhello,
     ext_early_data_parse_clienthello,
@@ -3384,7 +3211,6 @@
   },
   {
     TLSEXT_TYPE_supported_versions,
-    NULL,
     ext_supported_versions_add_clienthello,
     forbid_parse_serverhello,
     ignore_parse_clienthello,
@@ -3392,15 +3218,13 @@
   },
   {
     TLSEXT_TYPE_cookie,
-    NULL,
     ext_cookie_add_clienthello,
     forbid_parse_serverhello,
     ignore_parse_clienthello,
     dont_add_serverhello,
   },
   {
-    TLSEXT_TYPE_quic_transport_parameters_standard,
-    NULL,
+    TLSEXT_TYPE_quic_transport_parameters,
     ext_quic_transport_params_add_clienthello,
     ext_quic_transport_params_parse_serverhello,
     ext_quic_transport_params_parse_clienthello,
@@ -3408,23 +3232,13 @@
   },
   {
     TLSEXT_TYPE_quic_transport_parameters_legacy,
-    NULL,
     ext_quic_transport_params_add_clienthello_legacy,
     ext_quic_transport_params_parse_serverhello_legacy,
     ext_quic_transport_params_parse_clienthello_legacy,
     ext_quic_transport_params_add_serverhello_legacy,
   },
   {
-    TLSEXT_TYPE_token_binding,
-    NULL,
-    ext_token_binding_add_clienthello,
-    ext_token_binding_parse_serverhello,
-    ext_token_binding_parse_clienthello,
-    ext_token_binding_add_serverhello,
-  },
-  {
     TLSEXT_TYPE_cert_compression,
-    NULL,
     cert_compression_add_clienthello,
     cert_compression_parse_serverhello,
     cert_compression_parse_clienthello,
@@ -3432,7 +3246,6 @@
   },
   {
     TLSEXT_TYPE_delegated_credential,
-    NULL,
     ext_delegated_credential_add_clienthello,
     forbid_parse_serverhello,
     ext_delegated_credential_parse_clienthello,
@@ -3440,7 +3253,6 @@
   },
   {
     TLSEXT_TYPE_application_settings,
-    NULL,
     ext_alps_add_clienthello,
     ext_alps_parse_serverhello,
     // ALPS is negotiated late in |ssl_negotiate_alpn|.
@@ -3458,6 +3270,30 @@
                   sizeof(((SSL_HANDSHAKE *)NULL)->extensions.received) * 8,
               "too many extensions for received bitset");
 
+bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs) {
+  if (!hs->config->permute_extensions) {
+    return true;
+  }
+
+  static_assert(kNumExtensions <= UINT8_MAX,
+                "extensions_permutation type is too small");
+  uint32_t seeds[kNumExtensions - 1];
+  Array<uint8_t> permutation;
+  if (!RAND_bytes(reinterpret_cast<uint8_t *>(seeds), sizeof(seeds)) ||
+      !permutation.Init(kNumExtensions)) {
+    return false;
+  }
+  for (size_t i = 0; i < kNumExtensions; i++) {
+    permutation[i] = i;
+  }
+  for (size_t i = kNumExtensions - 1; i > 0; i--) {
+    // Set element |i| to a randomly-selected element 0 <= j <= i.
+    std::swap(permutation[i], permutation[seeds[i - 1] % (i + 1)]);
+  }
+  hs->extension_permutation = std::move(permutation);
+  return true;
+}
+
 static const struct tls_extension *tls_extension_find(uint32_t *out_index,
                                                       uint16_t value) {
   unsigned i;
@@ -3471,8 +3307,137 @@
   return NULL;
 }
 
-bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out,
+static bool add_padding_extension(CBB *cbb, uint16_t ext, size_t len) {
+  CBB child;
+  if (!CBB_add_u16(cbb, ext) ||  //
+      !CBB_add_u16_length_prefixed(cbb, &child) ||
+      !CBB_add_zeros(&child, len)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+  return CBB_flush(cbb);
+}
+
+static bool ssl_add_clienthello_tlsext_inner(SSL_HANDSHAKE *hs, CBB *out,
+                                             CBB *out_encoded,
+                                             bool *out_needs_psk_binder) {
+  // When writing ClientHelloInner, we construct the real and encoded
+  // ClientHellos concurrently, to handle compression. Uncompressed extensions
+  // are written to |extensions| and copied to |extensions_encoded|. Compressed
+  // extensions are buffered in |compressed| and written to the end. (ECH can
+  // only compress continguous extensions.)
+  SSL *const ssl = hs->ssl;
+  bssl::ScopedCBB compressed, outer_extensions;
+  CBB extensions, extensions_encoded;
+  if (!CBB_add_u16_length_prefixed(out, &extensions) ||
+      !CBB_add_u16_length_prefixed(out_encoded, &extensions_encoded) ||
+      !CBB_init(compressed.get(), 64) ||
+      !CBB_init(outer_extensions.get(), 64)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  hs->inner_extensions_sent = 0;
+
+  if (ssl->ctx->grease_enabled) {
+    // Add a fake empty extension. See RFC 8701. This always matches
+    // |ssl_add_clienthello_tlsext|, so compress it.
+    uint16_t grease_ext = ssl_get_grease_value(hs, ssl_grease_extension1);
+    if (!add_padding_extension(compressed.get(), grease_ext, 0) ||
+        !CBB_add_u16(outer_extensions.get(), grease_ext)) {
+      return false;
+    }
+  }
+
+  for (size_t unpermuted = 0; unpermuted < kNumExtensions; unpermuted++) {
+    size_t i = hs->extension_permutation.empty()
+                   ? unpermuted
+                   : hs->extension_permutation[unpermuted];
+    const size_t len_before = CBB_len(&extensions);
+    const size_t len_compressed_before = CBB_len(compressed.get());
+    if (!kExtensions[i].add_clienthello(hs, &extensions, compressed.get(),
+                                        ssl_client_hello_inner)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
+      ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
+      return false;
+    }
+
+    const size_t bytes_written = CBB_len(&extensions) - len_before;
+    const size_t bytes_written_compressed =
+        CBB_len(compressed.get()) - len_compressed_before;
+    // The callback may write to at most one output.
+    assert(bytes_written == 0 || bytes_written_compressed == 0);
+    if (bytes_written != 0 || bytes_written_compressed != 0) {
+      hs->inner_extensions_sent |= (1u << i);
+    }
+    // If compressed, update the running ech_outer_extensions extension.
+    if (bytes_written_compressed != 0 &&
+        !CBB_add_u16(outer_extensions.get(), kExtensions[i].value)) {
+      return false;
+    }
+  }
+
+  if (ssl->ctx->grease_enabled) {
+    // Add a fake non-empty extension. See RFC 8701. This always matches
+    // |ssl_add_clienthello_tlsext|, so compress it.
+    uint16_t grease_ext = ssl_get_grease_value(hs, ssl_grease_extension2);
+    if (!add_padding_extension(compressed.get(), grease_ext, 1) ||
+        !CBB_add_u16(outer_extensions.get(), grease_ext)) {
+      return false;
+    }
+  }
+
+  // Uncompressed extensions are encoded as-is.
+  if (!CBB_add_bytes(&extensions_encoded, CBB_data(&extensions),
+                     CBB_len(&extensions))) {
+    return false;
+  }
+
+  // Flush all the compressed extensions.
+  if (CBB_len(compressed.get()) != 0) {
+    CBB extension, child;
+    // Copy them as-is in the real ClientHelloInner.
+    if (!CBB_add_bytes(&extensions, CBB_data(compressed.get()),
+                       CBB_len(compressed.get())) ||
+        // Replace with ech_outer_extensions in the encoded form.
+        !CBB_add_u16(&extensions_encoded, TLSEXT_TYPE_ech_outer_extensions) ||
+        !CBB_add_u16_length_prefixed(&extensions_encoded, &extension) ||
+        !CBB_add_u8_length_prefixed(&extension, &child) ||
+        !CBB_add_bytes(&child, CBB_data(outer_extensions.get()),
+                       CBB_len(outer_extensions.get())) ||
+        !CBB_flush(&extensions_encoded)) {
+      return false;
+    }
+  }
+
+  // The PSK extension must be last. It is never compressed. Note, if there is a
+  // binder, the caller will need to update both ClientHelloInner and
+  // EncodedClientHelloInner after computing it.
+  const size_t len_before = CBB_len(&extensions);
+  if (!ext_pre_shared_key_add_clienthello(hs, &extensions, out_needs_psk_binder,
+                                          ssl_client_hello_inner) ||
+      !CBB_add_bytes(&extensions_encoded, CBB_data(&extensions) + len_before,
+                     CBB_len(&extensions) - len_before) ||
+      !CBB_flush(out) ||  //
+      !CBB_flush(out_encoded)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded,
+                                bool *out_needs_psk_binder,
+                                ssl_client_hello_type_t type,
                                 size_t header_len) {
+  *out_needs_psk_binder = false;
+
+  if (type == ssl_client_hello_inner) {
+    return ssl_add_clienthello_tlsext_inner(hs, out, out_encoded,
+                                            out_needs_psk_binder);
+  }
+
+  assert(out_encoded == nullptr);  // Only ClientHelloInner needs two outputs.
   SSL *const ssl = hs->ssl;
   CBB extensions;
   if (!CBB_add_u16_length_prefixed(out, &extensions)) {
@@ -3485,27 +3450,20 @@
   // important to reset this value.
   hs->extensions.sent = 0;
 
-  for (size_t i = 0; i < kNumExtensions; i++) {
-    if (kExtensions[i].init != NULL) {
-      kExtensions[i].init(hs);
-    }
-  }
-
-  uint16_t grease_ext1 = 0;
-  if (ssl->ctx->grease_enabled) {
-    // Add a fake empty extension. See draft-davidben-tls-grease-01.
-    grease_ext1 = ssl_get_grease_value(hs, ssl_grease_extension1);
-    if (!CBB_add_u16(&extensions, grease_ext1) ||
-        !CBB_add_u16(&extensions, 0 /* zero length */)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return false;
-    }
+  // Add a fake empty extension. See RFC 8701.
+  if (ssl->ctx->grease_enabled &&
+      !add_padding_extension(
+          &extensions, ssl_get_grease_value(hs, ssl_grease_extension1), 0)) {
+    return false;
   }
 
   bool last_was_empty = false;
-  for (size_t i = 0; i < kNumExtensions; i++) {
+  for (size_t unpermuted = 0; unpermuted < kNumExtensions; unpermuted++) {
+    size_t i = hs->extension_permutation.empty()
+                   ? unpermuted
+                   : hs->extension_permutation[unpermuted];
     const size_t len_before = CBB_len(&extensions);
-    if (!kExtensions[i].add_clienthello(hs, &extensions)) {
+    if (!kExtensions[i].add_clienthello(hs, &extensions, &extensions, type)) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_ADDING_EXTENSION);
       ERR_add_error_dataf("extension %u", (unsigned)kExtensions[i].value);
       return false;
@@ -3521,29 +3479,22 @@
   }
 
   if (ssl->ctx->grease_enabled) {
-    // Add a fake non-empty extension. See draft-davidben-tls-grease-01.
-    uint16_t grease_ext2 = ssl_get_grease_value(hs, ssl_grease_extension2);
-
-    // The two fake extensions must not have the same value. GREASE values are
-    // of the form 0x1a1a, 0x2a2a, 0x3a3a, etc., so XOR to generate a different
-    // one.
-    if (grease_ext1 == grease_ext2) {
-      grease_ext2 ^= 0x1010;
-    }
-
-    if (!CBB_add_u16(&extensions, grease_ext2) ||
-        !CBB_add_u16(&extensions, 1 /* one byte length */) ||
-        !CBB_add_u8(&extensions, 0 /* single zero byte as contents */)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    // Add a fake non-empty extension. See RFC 8701.
+    if (!add_padding_extension(
+            &extensions, ssl_get_grease_value(hs, ssl_grease_extension2), 1)) {
       return false;
     }
-
     last_was_empty = false;
   }
 
-  if (!SSL_is_dtls(ssl) && !ssl->quic_method) {
-    size_t psk_extension_len = ext_pre_shared_key_clienthello_length(hs);
-    header_len += 2 + CBB_len(&extensions) + psk_extension_len;
+  // In cleartext ClientHellos, we add the padding extension to work around
+  // bugs. We also apply this padding to ClientHelloOuter, to keep the wire
+  // images aligned.
+  size_t psk_extension_len = ext_pre_shared_key_clienthello_length(hs, type);
+  if (!SSL_is_dtls(ssl) && !ssl->quic_method &&
+      !ssl->s3->used_hello_retry_request) {
+    header_len +=
+        SSL3_HM_HEADER_LENGTH + 2 + CBB_len(&extensions) + psk_extension_len;
     size_t padding_len = 0;
 
     // The final extension must be non-empty. WebSphere Application
@@ -3577,24 +3528,21 @@
       }
     }
 
-    if (padding_len != 0) {
-      uint8_t *padding_bytes;
-      if (!CBB_add_u16(&extensions, TLSEXT_TYPE_padding) ||
-          !CBB_add_u16(&extensions, padding_len) ||
-          !CBB_add_space(&extensions, &padding_bytes, padding_len)) {
-        OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-        return false;
-      }
-
-      OPENSSL_memset(padding_bytes, 0, padding_len);
+    if (padding_len != 0 &&
+        !add_padding_extension(&extensions, TLSEXT_TYPE_padding, padding_len)) {
+      return false;
     }
   }
 
   // The PSK extension must be last, including after the padding.
-  if (!ext_pre_shared_key_add_clienthello(hs, &extensions)) {
+  const size_t len_before = CBB_len(&extensions);
+  if (!ext_pre_shared_key_add_clienthello(hs, &extensions, out_needs_psk_binder,
+                                          type)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
   }
+  assert(psk_extension_len == CBB_len(&extensions) - len_before);
+  (void)len_before;  // |assert| is omitted in release builds.
 
   // Discard empty extensions blocks.
   if (CBB_len(&extensions) == 0) {
@@ -3640,12 +3588,6 @@
 static bool ssl_scan_clienthello_tlsext(SSL_HANDSHAKE *hs,
                                         const SSL_CLIENT_HELLO *client_hello,
                                         int *out_alert) {
-  for (size_t i = 0; i < kNumExtensions; i++) {
-    if (kExtensions[i].init != NULL) {
-      kExtensions[i].init(hs);
-    }
-  }
-
   hs->extensions.received = 0;
   CBS extensions;
   CBS_init(&extensions, client_hello->extensions, client_hello->extensions_len);
@@ -3726,18 +3668,10 @@
   return true;
 }
 
-static bool ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs,
+static bool ssl_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *cbs,
                                         int *out_alert) {
-  SSL *const ssl = hs->ssl;
-  // Before TLS 1.3, ServerHello extensions blocks may be omitted if empty.
-  if (CBS_len(cbs) == 0 && ssl_protocol_version(ssl) < TLS1_3_VERSION) {
-    return true;
-  }
-
-  // Decode the extensions block and check it is valid.
-  CBS extensions;
-  if (!CBS_get_u16_length_prefixed(cbs, &extensions) ||
-      !tls1_check_duplicate_extensions(&extensions)) {
+  CBS extensions = *cbs;
+  if (!tls1_check_duplicate_extensions(&extensions)) {
     *out_alert = SSL_AD_DECODE_ERROR;
     return false;
   }
@@ -3806,18 +3740,8 @@
 
 static bool ssl_check_clienthello_tlsext(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-
-  if (ssl->s3->token_binding_negotiated &&
-      !(SSL_get_secure_renegotiation_support(ssl) &&
-        SSL_get_extms_support(ssl))) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_TB_WITHOUT_EMS_OR_RI);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
-    return false;
-  }
-
   int ret = SSL_TLSEXT_ERR_NOACK;
   int al = SSL_AD_UNRECOGNIZED_NAME;
-
   if (ssl->ctx->servername_callback != 0) {
     ret = ssl->ctx->servername_callback(ssl, &al, ssl->ctx->servername_arg);
   } else if (ssl->session_ctx->servername_callback != 0) {
@@ -3869,7 +3793,7 @@
   return true;
 }
 
-bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs) {
+bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *cbs) {
   SSL *const ssl = hs->ssl;
   int alert = SSL_AD_DECODE_ERROR;
   if (!ssl_scan_serverhello_tlsext(hs, cbs, &alert)) {
@@ -3897,8 +3821,8 @@
     return ssl_ticket_aead_ignore_ticket;
   }
   // Split the ticket into the ticket and the MAC.
-  auto ticket_mac = ticket.subspan(ticket.size() - mac_len);
-  ticket = ticket.subspan(0, ticket.size() - mac_len);
+  auto ticket_mac = ticket.last(mac_len);
+  ticket = ticket.first(ticket.size() - mac_len);
   HMAC_Update(hmac_ctx, ticket.data(), ticket.size());
   HMAC_Final(hmac_ctx, mac, NULL);
   assert(mac_len == ticket_mac.size());
@@ -4032,6 +3956,7 @@
     SSL_HANDSHAKE *hs, UniquePtr<SSL_SESSION> *out_session,
     bool *out_renew_ticket, Span<const uint8_t> ticket,
     Span<const uint8_t> session_id) {
+  SSL *const ssl = hs->ssl;
   *out_renew_ticket = false;
   out_session->reset();
 
@@ -4040,9 +3965,21 @@
     return ssl_ticket_aead_ignore_ticket;
   }
 
+  // Tickets in TLS 1.3 are tied into pre-shared keys (PSKs), unlike in TLS 1.2
+  // where that concept doesn't exist. The |decrypted_psk| and |ignore_psk|
+  // hints only apply to PSKs. We check the version to determine which this is.
+  const bool is_psk = ssl_protocol_version(ssl) >= TLS1_3_VERSION;
+
   Array<uint8_t> plaintext;
   enum ssl_ticket_aead_result_t result;
-  if (hs->ssl->session_ctx->ticket_aead_method != NULL) {
+  SSL_HANDSHAKE_HINTS *const hints = hs->hints.get();
+  if (is_psk && hints && !hs->hints_requested &&
+      !hints->decrypted_psk.empty()) {
+    result = plaintext.CopyFrom(hints->decrypted_psk) ? ssl_ticket_aead_success
+                                                      : ssl_ticket_aead_error;
+  } else if (is_psk && hints && !hs->hints_requested && hints->ignore_psk) {
+    result = ssl_ticket_aead_ignore_ticket;
+  } else if (ssl->session_ctx->ticket_aead_method != NULL) {
     result = ssl_decrypt_ticket_with_method(hs, &plaintext, out_renew_ticket,
                                             ticket);
   } else {
@@ -4051,9 +3988,8 @@
     // length should be well under the minimum size for the session material and
     // HMAC.
     if (ticket.size() < SSL_TICKET_KEY_NAME_LEN + EVP_MAX_IV_LENGTH) {
-      return ssl_ticket_aead_ignore_ticket;
-    }
-    if (hs->ssl->session_ctx->ticket_key_cb != NULL) {
+      result = ssl_ticket_aead_ignore_ticket;
+    } else if (ssl->session_ctx->ticket_key_cb != NULL) {
       result =
           ssl_decrypt_ticket_with_cb(hs, &plaintext, out_renew_ticket, ticket);
     } else {
@@ -4061,22 +3997,33 @@
     }
   }
 
+  if (is_psk && hints && hs->hints_requested) {
+    if (result == ssl_ticket_aead_ignore_ticket) {
+      hints->ignore_psk = true;
+    } else if (result == ssl_ticket_aead_success &&
+               !hints->decrypted_psk.CopyFrom(plaintext)) {
+      return ssl_ticket_aead_error;
+    }
+  }
+
   if (result != ssl_ticket_aead_success) {
     return result;
   }
 
   // Decode the session.
   UniquePtr<SSL_SESSION> session(SSL_SESSION_from_bytes(
-      plaintext.data(), plaintext.size(), hs->ssl->ctx.get()));
+      plaintext.data(), plaintext.size(), ssl->ctx.get()));
   if (!session) {
     ERR_clear_error();  // Don't leave an error on the queue.
     return ssl_ticket_aead_ignore_ticket;
   }
 
-  // Copy the client's session ID into the new session, to denote the ticket has
-  // been accepted.
-  OPENSSL_memcpy(session->session_id, session_id.data(), session_id.size());
-  session->session_id_length = session_id.size();
+  // Envoy's tests expect the session to have a session ID that matches the
+  // placeholder used by the client. It's unclear whether this is a good idea,
+  // but we maintain it for now.
+  SHA256(ticket.data(), ticket.size(), session->session_id);
+  // Other consumers may expect a non-empty session ID to indicate resumption.
+  session->session_id_length = SHA256_DIGEST_LENGTH;
 
   *out_session = std::move(session);
   return ssl_ticket_aead_success;
@@ -4224,11 +4171,11 @@
   if (!sig_ok) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
-    ssl->s3->channel_id_valid = false;
     return false;
   }
 
   OPENSSL_memcpy(ssl->s3->channel_id, p, 64);
+  ssl->s3->channel_id_valid = true;
   return true;
 }
 
@@ -4339,23 +4286,6 @@
   return true;
 }
 
-bool ssl_do_channel_id_callback(SSL_HANDSHAKE *hs) {
-  if (hs->config->channel_id_private != NULL ||
-      hs->ssl->ctx->channel_id_cb == NULL) {
-    return true;
-  }
-
-  EVP_PKEY *key = NULL;
-  hs->ssl->ctx->channel_id_cb(hs->ssl, &key);
-  if (key == NULL) {
-    // The caller should try again later.
-    return true;
-  }
-
-  UniquePtr<EVP_PKEY> free_key(key);
-  return SSL_set1_tls_channel_id(hs->ssl, key);
-}
-
 bool ssl_is_sct_list_valid(const CBS *contents) {
   // Shallow parse the SCT list for sanity. By the RFC
   // (https://tools.ietf.org/html/rfc6962#section-3.3) neither the list nor any
diff --git a/deps/boringssl/src/ssl/handoff.cc b/deps/boringssl/src/ssl/handoff.cc
index 16cbdf7..883f832 100644
--- a/deps/boringssl/src/ssl/handoff.cc
+++ b/deps/boringssl/src/ssl/handoff.cc
@@ -15,6 +15,7 @@
 #include <openssl/ssl.h>
 
 #include <openssl/bytestring.h>
+#include <openssl/err.h>
 
 #include "internal.h"
 
@@ -93,7 +94,7 @@
       !serialize_features(&seq) ||
       !CBB_flush(out) ||
       !ssl->method->get_message(ssl, &msg) ||
-      !ssl_client_hello_init(ssl, out_hello, msg)) {
+      !ssl_client_hello_init(ssl, out_hello, msg.body)) {
     return false;
   }
 
@@ -231,7 +232,7 @@
 // disqualifies it for split handshakes.
 static bool uses_disallowed_feature(const SSL *ssl) {
   return ssl->method->is_dtls || (ssl->config->cert && ssl->config->cert->dc) ||
-         ssl->config->quic_transport_params.size() > 0;
+         ssl->config->quic_transport_params.size() > 0 || ssl->ctx->ech_keys;
 }
 
 bool SSL_apply_handoff(SSL *ssl, Span<const uint8_t> handoff) {
@@ -337,6 +338,7 @@
   } else {
     session = s3->session_reused ? ssl->session.get() : hs->new_session.get();
   }
+  static const uint8_t kUnusedChannelID[64] = {0};
   if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) ||
       !CBB_add_asn1_uint64(&seq, kHandbackVersion) ||
       !CBB_add_asn1_uint64(&seq, type) ||
@@ -351,7 +353,7 @@
       !CBB_add_asn1_octet_string(&seq, read_iv, read_iv_len) ||
       !CBB_add_asn1_octet_string(&seq, write_iv, write_iv_len) ||
       !CBB_add_asn1_bool(&seq, s3->session_reused) ||
-      !CBB_add_asn1_bool(&seq, s3->channel_id_valid) ||
+      !CBB_add_asn1_bool(&seq, hs->channel_id_negotiated) ||
       !ssl_session_serialize(session, &seq) ||
       !CBB_add_asn1_octet_string(&seq, s3->next_proto_negotiated.data(),
                                  s3->next_proto_negotiated.size()) ||
@@ -360,10 +362,12 @@
       !CBB_add_asn1_octet_string(
           &seq, reinterpret_cast<uint8_t *>(s3->hostname.get()),
           hostname_len) ||
-      !CBB_add_asn1_octet_string(&seq, s3->channel_id,
-                                 sizeof(s3->channel_id)) ||
-      !CBB_add_asn1_bool(&seq, ssl->s3->token_binding_negotiated) ||
-      !CBB_add_asn1_uint64(&seq, ssl->s3->negotiated_token_binding_param) ||
+      !CBB_add_asn1_octet_string(&seq, kUnusedChannelID,
+                                 sizeof(kUnusedChannelID)) ||
+      // These two fields were historically |token_binding_negotiated| and
+      // |negotiated_token_binding_param|.
+      !CBB_add_asn1_bool(&seq, 0) ||
+      !CBB_add_asn1_uint64(&seq, 0) ||
       !CBB_add_asn1_bool(&seq, s3->hs->next_proto_neg_seen) ||
       !CBB_add_asn1_bool(&seq, s3->hs->cert_request) ||
       !CBB_add_asn1_bool(&seq, s3->hs->extended_master_secret) ||
@@ -442,12 +446,13 @@
   }
 
   SSL3_STATE *const s3 = ssl->s3;
-  uint64_t handback_version, negotiated_token_binding_param, cipher, type_u64;
+  uint64_t handback_version, unused_token_binding_param, cipher, type_u64;
 
   CBS seq, read_seq, write_seq, server_rand, client_rand, read_iv, write_iv,
-      next_proto, alpn, hostname, channel_id, transcript, key_share;
-  int session_reused, channel_id_valid, cert_request, extended_master_secret,
-      ticket_expected, token_binding_negotiated, next_proto_neg_seen;
+      next_proto, alpn, hostname, unused_channel_id, transcript, key_share;
+  int session_reused, channel_id_negotiated, cert_request,
+      extended_master_secret, ticket_expected, unused_token_binding,
+      next_proto_neg_seen;
   SSL_SESSION *session = nullptr;
 
   CBS handback_cbs(handback);
@@ -475,7 +480,7 @@
       !CBS_get_asn1(&seq, &read_iv, CBS_ASN1_OCTETSTRING) ||
       !CBS_get_asn1(&seq, &write_iv, CBS_ASN1_OCTETSTRING) ||
       !CBS_get_asn1_bool(&seq, &session_reused) ||
-      !CBS_get_asn1_bool(&seq, &channel_id_valid)) {
+      !CBS_get_asn1_bool(&seq, &channel_id_negotiated)) {
     return false;
   }
 
@@ -494,12 +499,9 @@
   if (!session || !CBS_get_asn1(&seq, &next_proto, CBS_ASN1_OCTETSTRING) ||
       !CBS_get_asn1(&seq, &alpn, CBS_ASN1_OCTETSTRING) ||
       !CBS_get_asn1(&seq, &hostname, CBS_ASN1_OCTETSTRING) ||
-      !CBS_get_asn1(&seq, &channel_id, CBS_ASN1_OCTETSTRING) ||
-      CBS_len(&channel_id) != sizeof(s3->channel_id) ||
-      !CBS_copy_bytes(&channel_id, s3->channel_id,
-                      sizeof(s3->channel_id)) ||
-      !CBS_get_asn1_bool(&seq, &token_binding_negotiated) ||
-      !CBS_get_asn1_uint64(&seq, &negotiated_token_binding_param) ||
+      !CBS_get_asn1(&seq, &unused_channel_id, CBS_ASN1_OCTETSTRING) ||
+      !CBS_get_asn1_bool(&seq, &unused_token_binding) ||
+      !CBS_get_asn1_uint64(&seq, &unused_token_binding_param) ||
       !CBS_get_asn1_bool(&seq, &next_proto_neg_seen) ||
       !CBS_get_asn1_bool(&seq, &cert_request) ||
       !CBS_get_asn1_bool(&seq, &extended_master_secret) ||
@@ -613,7 +615,7 @@
       return false;
   }
   s3->session_reused = session_reused;
-  s3->channel_id_valid = channel_id_valid;
+  hs->channel_id_negotiated = channel_id_negotiated;
   s3->next_proto_negotiated.CopyFrom(next_proto);
   s3->alpn_selected.CopyFrom(alpn);
 
@@ -628,9 +630,6 @@
     s3->hostname.reset(hostname_str);
   }
 
-  s3->token_binding_negotiated = token_binding_negotiated;
-  s3->negotiated_token_binding_param =
-      static_cast<uint8_t>(negotiated_token_binding_param);
   hs->next_proto_neg_seen = next_proto_neg_seen;
   hs->wait = ssl_hs_flush;
   hs->extended_master_secret = extended_master_secret;
@@ -708,3 +707,280 @@
 }
 
 BSSL_NAMESPACE_END
+
+using namespace bssl;
+
+int SSL_serialize_capabilities(const SSL *ssl, CBB *out) {
+  CBB seq;
+  if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) ||
+      !serialize_features(&seq) ||  //
+      !CBB_flush(out)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+int SSL_request_handshake_hints(SSL *ssl, const uint8_t *client_hello,
+                                size_t client_hello_len,
+                                const uint8_t *capabilities,
+                                size_t capabilities_len) {
+  if (SSL_is_dtls(ssl)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
+  CBS cbs, seq;
+  CBS_init(&cbs, capabilities, capabilities_len);
+  UniquePtr<SSL_HANDSHAKE_HINTS> hints = MakeUnique<SSL_HANDSHAKE_HINTS>();
+  if (hints == nullptr ||
+      !CBS_get_asn1(&cbs, &seq, CBS_ASN1_SEQUENCE) ||
+      !apply_remote_features(ssl, &seq)) {
+    return 0;
+  }
+
+  SSL3_STATE *const s3 = ssl->s3;
+  s3->v2_hello_done = true;
+  s3->has_message = true;
+
+  Array<uint8_t> client_hello_msg;
+  ScopedCBB client_hello_cbb;
+  CBB client_hello_body;
+  if (!ssl->method->init_message(ssl, client_hello_cbb.get(),
+                                 &client_hello_body, SSL3_MT_CLIENT_HELLO) ||
+      !CBB_add_bytes(&client_hello_body, client_hello, client_hello_len) ||
+      !ssl->method->finish_message(ssl, client_hello_cbb.get(),
+                                   &client_hello_msg)) {
+    return 0;
+  }
+
+  s3->hs_buf.reset(BUF_MEM_new());
+  if (!s3->hs_buf || !BUF_MEM_append(s3->hs_buf.get(), client_hello_msg.data(),
+                                     client_hello_msg.size())) {
+    return 0;
+  }
+
+  s3->hs->hints_requested = true;
+  s3->hs->hints = std::move(hints);
+  return 1;
+}
+
+// |SSL_HANDSHAKE_HINTS| is serialized as the following ASN.1 structure. We use
+// implicit tagging to make it a little more compact.
+//
+// HandshakeHints ::= SEQUENCE {
+//     serverRandom            [0] IMPLICIT OCTET STRING OPTIONAL,
+//     keyShareHint            [1] IMPLICIT KeyShareHint OPTIONAL,
+//     signatureHint           [2] IMPLICIT SignatureHint OPTIONAL,
+//     -- At most one of decryptedPSKHint or ignorePSKHint may be present. It
+//     -- corresponds to the first entry in pre_shared_keys. TLS 1.2 session
+//     -- tickets will use a separate hint, to ensure the caller does not mix
+//     -- them up.
+//     decryptedPSKHint        [3] IMPLICIT OCTET STRING OPTIONAL,
+//     ignorePSKHint           [4] IMPLICIT NULL OPTIONAL,
+//     compressCertificateHint [5] IMPLICIT CompressCertificateHint OPTIONAL,
+// }
+//
+// KeyShareHint ::= SEQUENCE {
+//     groupId                 INTEGER,
+//     publicKey               OCTET STRING,
+//     secret                  OCTET STRING,
+// }
+//
+// SignatureHint ::= SEQUENCE {
+//     algorithm               INTEGER,
+//     input                   OCTET STRING,
+//     subjectPublicKeyInfo    OCTET STRING,
+//     signature               OCTET STRING,
+// }
+//
+// CompressCertificateHint ::= SEQUENCE {
+//     algorithm               INTEGER,
+//     input                   OCTET STRING,
+//     compressed              OCTET STRING,
+// }
+
+// HandshakeHints tags.
+static const unsigned kServerRandomTag = CBS_ASN1_CONTEXT_SPECIFIC | 0;
+static const unsigned kKeyShareHintTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1;
+static const unsigned kSignatureHintTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2;
+static const unsigned kDecryptedPSKTag = CBS_ASN1_CONTEXT_SPECIFIC | 3;
+static const unsigned kIgnorePSKTag = CBS_ASN1_CONTEXT_SPECIFIC | 4;
+static const unsigned kCompressCertificateTag = CBS_ASN1_CONTEXT_SPECIFIC | 5;
+
+int SSL_serialize_handshake_hints(const SSL *ssl, CBB *out) {
+  const SSL_HANDSHAKE *hs = ssl->s3->hs.get();
+  if (!ssl->server || !hs->hints_requested) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
+  const SSL_HANDSHAKE_HINTS *hints = hs->hints.get();
+  CBB seq, child;
+  if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE)) {
+    return 0;
+  }
+
+  if (!hints->server_random.empty()) {
+    if (!CBB_add_asn1(&seq, &child, kServerRandomTag) ||
+        !CBB_add_bytes(&child, hints->server_random.data(),
+                       hints->server_random.size())) {
+      return 0;
+    }
+  }
+
+  if (hints->key_share_group_id != 0 && !hints->key_share_public_key.empty() &&
+      !hints->key_share_secret.empty()) {
+    if (!CBB_add_asn1(&seq, &child, kKeyShareHintTag) ||
+        !CBB_add_asn1_uint64(&child, hints->key_share_group_id) ||
+        !CBB_add_asn1_octet_string(&child, hints->key_share_public_key.data(),
+                                   hints->key_share_public_key.size()) ||
+        !CBB_add_asn1_octet_string(&child, hints->key_share_secret.data(),
+                                   hints->key_share_secret.size())) {
+      return 0;
+    }
+  }
+
+  if (hints->signature_algorithm != 0 && !hints->signature_input.empty() &&
+      !hints->signature.empty()) {
+    if (!CBB_add_asn1(&seq, &child, kSignatureHintTag) ||
+        !CBB_add_asn1_uint64(&child, hints->signature_algorithm) ||
+        !CBB_add_asn1_octet_string(&child, hints->signature_input.data(),
+                                   hints->signature_input.size()) ||
+        !CBB_add_asn1_octet_string(&child, hints->signature_spki.data(),
+                                   hints->signature_spki.size()) ||
+        !CBB_add_asn1_octet_string(&child, hints->signature.data(),
+                                   hints->signature.size())) {
+      return 0;
+    }
+  }
+
+  if (!hints->decrypted_psk.empty()) {
+    if (!CBB_add_asn1(&seq, &child, kDecryptedPSKTag) ||
+        !CBB_add_bytes(&child, hints->decrypted_psk.data(),
+                       hints->decrypted_psk.size())) {
+      return 0;
+    }
+  }
+
+  if (hints->ignore_psk &&  //
+      !CBB_add_asn1(&seq, &child, kIgnorePSKTag)) {
+    return 0;
+  }
+
+  if (hints->cert_compression_alg_id != 0 &&
+      !hints->cert_compression_input.empty() &&
+      !hints->cert_compression_output.empty()) {
+    if (!CBB_add_asn1(&seq, &child, kCompressCertificateTag) ||
+        !CBB_add_asn1_uint64(&child, hints->cert_compression_alg_id) ||
+        !CBB_add_asn1_octet_string(&child, hints->cert_compression_input.data(),
+                                   hints->cert_compression_input.size()) ||
+        !CBB_add_asn1_octet_string(&child,
+                                   hints->cert_compression_output.data(),
+                                   hints->cert_compression_output.size())) {
+      return 0;
+    }
+  }
+
+  return CBB_flush(out);
+}
+
+int SSL_set_handshake_hints(SSL *ssl, const uint8_t *hints, size_t hints_len) {
+  if (SSL_is_dtls(ssl)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
+  UniquePtr<SSL_HANDSHAKE_HINTS> hints_obj = MakeUnique<SSL_HANDSHAKE_HINTS>();
+  if (hints_obj == nullptr) {
+    return 0;
+  }
+
+  CBS cbs, seq, server_random, key_share, signature_hint, ticket, ignore_psk,
+      cert_compression;
+  int has_server_random, has_key_share, has_signature_hint, has_ticket,
+      has_ignore_psk, has_cert_compression;
+  CBS_init(&cbs, hints, hints_len);
+  if (!CBS_get_asn1(&cbs, &seq, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_optional_asn1(&seq, &server_random, &has_server_random,
+                             kServerRandomTag) ||
+      !CBS_get_optional_asn1(&seq, &key_share, &has_key_share,
+                             kKeyShareHintTag) ||
+      !CBS_get_optional_asn1(&seq, &signature_hint, &has_signature_hint,
+                             kSignatureHintTag) ||
+      !CBS_get_optional_asn1(&seq, &ticket, &has_ticket, kDecryptedPSKTag) ||
+      !CBS_get_optional_asn1(&seq, &ignore_psk, &has_ignore_psk,
+                             kIgnorePSKTag) ||
+      !CBS_get_optional_asn1(&seq, &cert_compression, &has_cert_compression,
+                             kCompressCertificateTag)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
+    return 0;
+  }
+
+  if (has_server_random && !hints_obj->server_random.CopyFrom(server_random)) {
+    return 0;
+  }
+
+  if (has_key_share) {
+    uint64_t group_id;
+    CBS public_key, secret;
+    if (!CBS_get_asn1_uint64(&key_share, &group_id) ||  //
+        group_id == 0 || group_id > 0xffff ||
+        !CBS_get_asn1(&key_share, &public_key, CBS_ASN1_OCTETSTRING) ||
+        !hints_obj->key_share_public_key.CopyFrom(public_key) ||
+        !CBS_get_asn1(&key_share, &secret, CBS_ASN1_OCTETSTRING) ||
+        !hints_obj->key_share_secret.CopyFrom(secret)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
+      return 0;
+    }
+    hints_obj->key_share_group_id = static_cast<uint16_t>(group_id);
+  }
+
+  if (has_signature_hint) {
+    uint64_t sig_alg;
+    CBS input, spki, signature;
+    if (!CBS_get_asn1_uint64(&signature_hint, &sig_alg) ||  //
+        sig_alg == 0 || sig_alg > 0xffff ||
+        !CBS_get_asn1(&signature_hint, &input, CBS_ASN1_OCTETSTRING) ||
+        !hints_obj->signature_input.CopyFrom(input) ||
+        !CBS_get_asn1(&signature_hint, &spki, CBS_ASN1_OCTETSTRING) ||
+        !hints_obj->signature_spki.CopyFrom(spki) ||
+        !CBS_get_asn1(&signature_hint, &signature, CBS_ASN1_OCTETSTRING) ||
+        !hints_obj->signature.CopyFrom(signature)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
+      return 0;
+    }
+    hints_obj->signature_algorithm = static_cast<uint16_t>(sig_alg);
+  }
+
+  if (has_ticket && !hints_obj->decrypted_psk.CopyFrom(ticket)) {
+    return 0;
+  }
+
+  if (has_ignore_psk) {
+    if (CBS_len(&ignore_psk) != 0) {
+      return 0;
+    }
+    hints_obj->ignore_psk = true;
+  }
+
+  if (has_cert_compression) {
+    uint64_t alg;
+    CBS input, output;
+    if (!CBS_get_asn1_uint64(&cert_compression, &alg) ||  //
+        alg == 0 || alg > 0xffff ||
+        !CBS_get_asn1(&cert_compression, &input, CBS_ASN1_OCTETSTRING) ||
+        !hints_obj->cert_compression_input.CopyFrom(input) ||
+        !CBS_get_asn1(&cert_compression, &output, CBS_ASN1_OCTETSTRING) ||
+        !hints_obj->cert_compression_output.CopyFrom(output)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
+      return 0;
+    }
+    hints_obj->cert_compression_alg_id = static_cast<uint16_t>(alg);
+  }
+
+  ssl->s3->hs->hints = std::move(hints_obj);
+  return 1;
+}
diff --git a/deps/boringssl/src/ssl/handshake.cc b/deps/boringssl/src/ssl/handshake.cc
index b38f96a..fc85e21 100644
--- a/deps/boringssl/src/ssl/handshake.cc
+++ b/deps/boringssl/src/ssl/handshake.cc
@@ -126,10 +126,9 @@
 
 SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg)
     : ssl(ssl_arg),
-      ech_present(false),
-      ech_is_inner_present(false),
+      ech_is_inner(false),
+      ech_authenticated_reject(false),
       scts_requested(false),
-      needs_psk_binder(false),
       handshake_finalized(false),
       accept_psk_mode(false),
       cert_request(false),
@@ -146,11 +145,19 @@
       ticket_expected(false),
       extended_master_secret(false),
       pending_private_key_op(false),
-      grease_seeded(false),
       handback(false),
+      hints_requested(false),
       cert_compression_negotiated(false),
-      apply_jdk11_workaround(false) {
+      apply_jdk11_workaround(false),
+      can_release_private_key(false),
+      channel_id_negotiated(false) {
   assert(ssl);
+
+  // Draw entropy for all GREASE values at once. This avoids calling
+  // |RAND_bytes| repeatedly and makes the values consistent within a
+  // connection. The latter is so the second ClientHello matches after
+  // HelloRetryRequest and so supported_groups and key_shares are consistent.
+  RAND_bytes(grease_seed, sizeof(grease_seed));
 }
 
 SSL_HANDSHAKE::~SSL_HANDSHAKE() {
@@ -164,6 +171,28 @@
   hash_len_ = hash_len;
 }
 
+bool SSL_HANDSHAKE::GetClientHello(SSLMessage *out_msg,
+                                   SSL_CLIENT_HELLO *out_client_hello) {
+  if (!ech_client_hello_buf.empty()) {
+    // If the backing buffer is non-empty, the ClientHelloInner has been set.
+    out_msg->is_v2_hello = false;
+    out_msg->type = SSL3_MT_CLIENT_HELLO;
+    out_msg->raw = CBS(ech_client_hello_buf);
+    out_msg->body = MakeConstSpan(ech_client_hello_buf).subspan(4);
+  } else if (!ssl->method->get_message(ssl, out_msg)) {
+    // The message has already been read, so this cannot fail.
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  if (!ssl_client_hello_init(ssl, out_client_hello, out_msg->body)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+    return false;
+  }
+  return true;
+}
+
 UniquePtr<SSL_HANDSHAKE> ssl_handshake_new(SSL *ssl) {
   UniquePtr<SSL_HANDSHAKE> hs = MakeUnique<SSL_HANDSHAKE>(ssl);
   if (!hs || !hs->transcript.Init()) {
@@ -238,12 +267,15 @@
 }
 
 bool ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
-                          Span<const SSL_EXTENSION_TYPE> ext_types,
+                          std::initializer_list<SSLExtension *> extensions,
                           bool ignore_unknown) {
   // Reset everything.
-  for (const SSL_EXTENSION_TYPE &ext_type : ext_types) {
-    *ext_type.out_present = false;
-    CBS_init(ext_type.out_data, nullptr, 0);
+  for (SSLExtension *ext : extensions) {
+    ext->present = false;
+    CBS_init(&ext->data, nullptr, 0);
+    if (!ext->allowed) {
+      assert(!ignore_unknown);
+    }
   }
 
   CBS copy = *cbs;
@@ -257,10 +289,10 @@
       return false;
     }
 
-    const SSL_EXTENSION_TYPE *found = nullptr;
-    for (const SSL_EXTENSION_TYPE &ext_type : ext_types) {
-      if (type == ext_type.type) {
-        found = &ext_type;
+    SSLExtension *found = nullptr;
+    for (SSLExtension *ext : extensions) {
+      if (type == ext->type && ext->allowed) {
+        found = ext;
         break;
       }
     }
@@ -275,14 +307,14 @@
     }
 
     // Duplicate ext_types are forbidden.
-    if (*found->out_present) {
+    if (found->present) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
       *out_alert = SSL_AD_ILLEGAL_PARAMETER;
       return false;
     }
 
-    *found->out_present = 1;
-    *found->out_data = data;
+    found->present = true;
+    found->data = data;
   }
 
   return true;
@@ -410,17 +442,8 @@
   return ret;
 }
 
-uint16_t ssl_get_grease_value(SSL_HANDSHAKE *hs,
-                              enum ssl_grease_index_t index) {
-  // Draw entropy for all GREASE values at once. This avoids calling
-  // |RAND_bytes| repeatedly and makes the values consistent within a
-  // connection. The latter is so the second ClientHello matches after
-  // HelloRetryRequest and so supported_groups and key_shares are consistent.
-  if (!hs->grease_seeded) {
-    RAND_bytes(hs->grease_seed, sizeof(hs->grease_seed));
-    hs->grease_seeded = true;
-  }
-
+static uint16_t grease_index_to_value(const SSL_HANDSHAKE *hs,
+                                      enum ssl_grease_index_t index) {
   // This generates a random value of the form 0xωaωa, for all 0 ≤ ω < 16.
   uint16_t ret = hs->grease_seed[index];
   ret = (ret & 0xf0) | 0x0a;
@@ -428,6 +451,19 @@
   return ret;
 }
 
+uint16_t ssl_get_grease_value(const SSL_HANDSHAKE *hs,
+                              enum ssl_grease_index_t index) {
+  uint16_t ret = grease_index_to_value(hs, index);
+  if (index == ssl_grease_extension2 &&
+      ret == grease_index_to_value(hs, ssl_grease_extension1)) {
+    // The two fake extensions must not have the same value. GREASE values are
+    // of the form 0x1a1a, 0x2a2a, 0x3a3a, etc., so XOR to generate a different
+    // one.
+    ret ^= 0x1010;
+  }
+  return ret;
+}
+
 enum ssl_hs_wait_t ssl_get_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   SSLMessage msg;
@@ -552,7 +588,11 @@
 int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) {
   SSL *const ssl = hs->ssl;
   for (;;) {
-    // Resolve the operation the handshake was waiting on.
+    // Resolve the operation the handshake was waiting on. Each condition may
+    // halt the handshake by returning, or continue executing if the handshake
+    // may immediately proceed. Cases which halt the handshake can clear
+    // |hs->wait| to re-enter the state machine on the next iteration, or leave
+    // it set to keep the condition sticky.
     switch (hs->wait) {
       case ssl_hs_error:
         ERR_restore_state(hs->error.get());
@@ -570,13 +610,13 @@
       case ssl_hs_read_message:
       case ssl_hs_read_change_cipher_spec: {
         if (ssl->quic_method) {
+          // QUIC has no ChangeCipherSpec messages.
+          assert(hs->wait != ssl_hs_read_change_cipher_spec);
+          // The caller should call |SSL_provide_quic_data|. Clear |hs->wait| so
+          // the handshake can check if there is sufficient data next iteration.
+          ssl->s3->rwstate = SSL_ERROR_WANT_READ;
           hs->wait = ssl_hs_ok;
-          // The change cipher spec is omitted in QUIC.
-          if (hs->wait != ssl_hs_read_change_cipher_spec) {
-            ssl->s3->rwstate = SSL_ERROR_WANT_READ;
-            return -1;
-          }
-          break;
+          return -1;
         }
 
         uint8_t alert = SSL_AD_DECODE_ERROR;
@@ -646,31 +686,26 @@
         return -1;
       }
 
+        // The following cases are associated with callback APIs which expect to
+        // be called each time the state machine runs. Thus they set |hs->wait|
+        // to |ssl_hs_ok| so that, next time, we re-enter the state machine and
+        // call the callback again.
       case ssl_hs_x509_lookup:
         ssl->s3->rwstate = SSL_ERROR_WANT_X509_LOOKUP;
         hs->wait = ssl_hs_ok;
         return -1;
-
-      case ssl_hs_channel_id_lookup:
-        ssl->s3->rwstate = SSL_ERROR_WANT_CHANNEL_ID_LOOKUP;
-        hs->wait = ssl_hs_ok;
-        return -1;
-
       case ssl_hs_private_key_operation:
         ssl->s3->rwstate = SSL_ERROR_WANT_PRIVATE_KEY_OPERATION;
         hs->wait = ssl_hs_ok;
         return -1;
-
       case ssl_hs_pending_session:
         ssl->s3->rwstate = SSL_ERROR_PENDING_SESSION;
         hs->wait = ssl_hs_ok;
         return -1;
-
       case ssl_hs_pending_ticket:
         ssl->s3->rwstate = SSL_ERROR_PENDING_TICKET;
         hs->wait = ssl_hs_ok;
         return -1;
-
       case ssl_hs_certificate_verify:
         ssl->s3->rwstate = SSL_ERROR_WANT_CERTIFICATE_VERIFY;
         hs->wait = ssl_hs_ok;
@@ -683,10 +718,18 @@
         return -1;
 
       case ssl_hs_early_return:
+        if (!ssl->server) {
+          // On ECH reject, the handshake should never complete.
+          assert(ssl->s3->ech_status != ssl_ech_rejected);
+        }
         *out_early_return = true;
         hs->wait = ssl_hs_ok;
         return 1;
 
+      case ssl_hs_hints_ready:
+        ssl->s3->rwstate = SSL_ERROR_HANDSHAKE_HINTS_READY;
+        return -1;
+
       case ssl_hs_ok:
         break;
     }
@@ -698,6 +741,10 @@
       return -1;
     }
     if (hs->wait == ssl_hs_ok) {
+      if (!ssl->server) {
+        // On ECH reject, the handshake should never complete.
+        assert(ssl->s3->ech_status != ssl_ech_rejected);
+      }
       // The handshake has completed.
       *out_early_return = false;
       return 1;
diff --git a/deps/boringssl/src/ssl/handshake_client.cc b/deps/boringssl/src/ssl/handshake_client.cc
index 59ef6ec..17b41e0 100644
--- a/deps/boringssl/src/ssl/handshake_client.cc
+++ b/deps/boringssl/src/ssl/handshake_client.cc
@@ -162,6 +162,7 @@
 #include <openssl/ecdsa.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
+#include <openssl/hpke.h>
 #include <openssl/md5.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
@@ -201,7 +202,8 @@
 
 // ssl_get_client_disabled sets |*out_mask_a| and |*out_mask_k| to masks of
 // disabled algorithms.
-static void ssl_get_client_disabled(SSL_HANDSHAKE *hs, uint32_t *out_mask_a,
+static void ssl_get_client_disabled(const SSL_HANDSHAKE *hs,
+                                    uint32_t *out_mask_a,
                                     uint32_t *out_mask_k) {
   *out_mask_a = 0;
   *out_mask_k = 0;
@@ -213,8 +215,9 @@
   }
 }
 
-static bool ssl_write_client_cipher_list(SSL_HANDSHAKE *hs, CBB *out) {
-  SSL *const ssl = hs->ssl;
+static bool ssl_write_client_cipher_list(const SSL_HANDSHAKE *hs, CBB *out,
+                                         ssl_client_hello_type_t type) {
+  const SSL *const ssl = hs->ssl;
   uint32_t mask_a, mask_k;
   ssl_get_client_disabled(hs, &mask_a, &mask_k);
 
@@ -223,7 +226,7 @@
     return false;
   }
 
-  // Add a fake cipher suite. See draft-davidben-tls-grease-01.
+  // Add a fake cipher suite. See RFC 8701.
   if (ssl->ctx->grease_enabled &&
       !CBB_add_u16(&child, ssl_get_grease_value(hs, ssl_grease_cipher))) {
     return false;
@@ -246,7 +249,7 @@
     }
   }
 
-  if (hs->min_version < TLS1_3_VERSION) {
+  if (hs->min_version < TLS1_3_VERSION && type != ssl_client_hello_inner) {
     bool any_enabled = false;
     for (const SSL_CIPHER *cipher : SSL_get_ciphers(ssl)) {
       // Skip disabled ciphers
@@ -280,100 +283,161 @@
   return CBB_flush(out);
 }
 
-bool ssl_write_client_hello(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  ScopedCBB cbb;
-  CBB body;
-  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CLIENT_HELLO)) {
-    return false;
-  }
-
+bool ssl_write_client_hello_without_extensions(const SSL_HANDSHAKE *hs,
+                                               CBB *cbb,
+                                               ssl_client_hello_type_t type,
+                                               bool empty_session_id) {
+  const SSL *const ssl = hs->ssl;
   CBB child;
-  if (!CBB_add_u16(&body, hs->client_version) ||
-      !CBB_add_bytes(&body, ssl->s3->client_random, SSL3_RANDOM_SIZE) ||
-      !CBB_add_u8_length_prefixed(&body, &child)) {
+  if (!CBB_add_u16(cbb, hs->client_version) ||
+      !CBB_add_bytes(cbb,
+                     type == ssl_client_hello_inner ? hs->inner_client_random
+                                                    : ssl->s3->client_random,
+                     SSL3_RANDOM_SIZE) ||
+      !CBB_add_u8_length_prefixed(cbb, &child)) {
     return false;
   }
 
   // Do not send a session ID on renegotiation.
   if (!ssl->s3->initial_handshake_complete &&
+      !empty_session_id &&
       !CBB_add_bytes(&child, hs->session_id, hs->session_id_len)) {
     return false;
   }
 
   if (SSL_is_dtls(ssl)) {
-    if (!CBB_add_u8_length_prefixed(&body, &child) ||
+    if (!CBB_add_u8_length_prefixed(cbb, &child) ||
         !CBB_add_bytes(&child, ssl->d1->cookie, ssl->d1->cookie_len)) {
       return false;
     }
   }
 
-  size_t header_len =
-      SSL_is_dtls(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH;
-  if (!ssl_write_client_cipher_list(hs, &body) ||
-      !CBB_add_u8(&body, 1 /* one compression method */) ||
-      !CBB_add_u8(&body, 0 /* null compression */) ||
-      !ssl_add_clienthello_tlsext(hs, &body, header_len + CBB_len(&body))) {
+  if (!ssl_write_client_cipher_list(hs, cbb, type) ||
+      !CBB_add_u8(cbb, 1 /* one compression method */) ||
+      !CBB_add_u8(cbb, 0 /* null compression */)) {
     return false;
   }
+  return true;
+}
 
+bool ssl_add_client_hello(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+  ScopedCBB cbb;
+  CBB body;
+  ssl_client_hello_type_t type = hs->selected_ech_config
+                                     ? ssl_client_hello_outer
+                                     : ssl_client_hello_unencrypted;
+  bool needs_psk_binder;
   Array<uint8_t> msg;
-  if (!ssl->method->finish_message(ssl, cbb.get(), &msg)) {
+  if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CLIENT_HELLO) ||
+      !ssl_write_client_hello_without_extensions(hs, &body, type,
+                                                 /*empty_session_id*/ false) ||
+      !ssl_add_clienthello_tlsext(hs, &body, /*out_encoded=*/nullptr,
+                                  &needs_psk_binder, type, CBB_len(&body)) ||
+      !ssl->method->finish_message(ssl, cbb.get(), &msg)) {
     return false;
   }
 
   // Now that the length prefixes have been computed, fill in the placeholder
   // PSK binder.
-  if (hs->needs_psk_binder &&
-      !tls13_write_psk_binder(hs, MakeSpan(msg))) {
-    return false;
+  if (needs_psk_binder) {
+    // ClientHelloOuter cannot have a PSK binder. Otherwise the
+    // ClientHellOuterAAD computation would break.
+    assert(type != ssl_client_hello_outer);
+    if (!tls13_write_psk_binder(hs, hs->transcript, MakeSpan(msg),
+                                /*out_binder_len=*/0)) {
+      return false;
+    }
   }
 
   return ssl->method->add_message(ssl, std::move(msg));
 }
 
-static bool parse_supported_versions(SSL_HANDSHAKE *hs, uint16_t *version,
-                                     const CBS *in) {
-  // If the outer version is not TLS 1.2, or there is no extensions block, use
-  // the outer version.
-  if (*version != TLS1_2_VERSION || CBS_len(in) == 0) {
+static bool parse_server_version(const SSL_HANDSHAKE *hs, uint16_t *out_version,
+                                 uint8_t *out_alert,
+                                 const ParsedServerHello &server_hello) {
+  // If the outer version is not TLS 1.2, use it.
+  // TODO(davidben): This function doesn't quite match the RFC8446 formulation.
+  if (server_hello.legacy_version != TLS1_2_VERSION) {
+    *out_version = server_hello.legacy_version;
     return true;
   }
 
-  SSL *const ssl = hs->ssl;
-  CBS copy = *in, extensions;
-  if (!CBS_get_u16_length_prefixed(&copy, &extensions) ||
-      CBS_len(&copy) != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return false;
-  }
-
-  bool have_supported_versions;
-  CBS supported_versions;
-  const SSL_EXTENSION_TYPE ext_types[] = {
-    {TLSEXT_TYPE_supported_versions, &have_supported_versions,
-     &supported_versions},
-  };
-
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+  SSLExtension supported_versions(TLSEXT_TYPE_supported_versions);
+  CBS extensions = server_hello.extensions;
+  if (!ssl_parse_extensions(&extensions, out_alert, {&supported_versions},
                             /*ignore_unknown=*/true)) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return false;
   }
 
-  // Override the outer version with the extension, if present.
-  if (have_supported_versions &&
-      (!CBS_get_u16(&supported_versions, version) ||
-       CBS_len(&supported_versions) != 0)) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+  if (!supported_versions.present) {
+    *out_version = server_hello.legacy_version;
+    return true;
+  }
+
+  if (!CBS_get_u16(&supported_versions.data, out_version) ||
+       CBS_len(&supported_versions.data) != 0) {
+    *out_alert = SSL_AD_DECODE_ERROR;
     return false;
   }
 
   return true;
 }
 
+// should_offer_early_data returns |ssl_early_data_accepted| if |hs| should
+// offer early data, and some other reason code otherwise.
+static ssl_early_data_reason_t should_offer_early_data(
+    const SSL_HANDSHAKE *hs) {
+  const SSL *const ssl = hs->ssl;
+  assert(!ssl->server);
+  if (!ssl->enable_early_data) {
+    return ssl_early_data_disabled;
+  }
+
+  if (hs->max_version < TLS1_3_VERSION) {
+    // We discard inapplicable sessions, so this is redundant with the session
+    // checks below, but reporting that TLS 1.3 was disabled is more useful.
+    return ssl_early_data_protocol_version;
+  }
+
+  if (ssl->session == nullptr) {
+    return ssl_early_data_no_session_offered;
+  }
+
+  if (ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
+      ssl->session->ticket_max_early_data == 0) {
+    return ssl_early_data_unsupported_for_session;
+  }
+
+  if (!ssl->session->early_alpn.empty()) {
+    if (!ssl_is_alpn_protocol_allowed(hs, ssl->session->early_alpn)) {
+      // Avoid reporting a confusing value in |SSL_get0_alpn_selected|.
+      return ssl_early_data_alpn_mismatch;
+    }
+
+    // If the previous connection negotiated ALPS, only offer 0-RTT when the
+    // local are settings are consistent with what we'd offer for this
+    // connection.
+    if (ssl->session->has_application_settings) {
+      Span<const uint8_t> settings;
+      if (!ssl_get_local_application_settings(hs, &settings,
+                                              ssl->session->early_alpn) ||
+          settings != ssl->session->local_application_settings) {
+        return ssl_early_data_alps_mismatch;
+      }
+    }
+  }
+
+  // Early data has not yet been accepted, but we use it as a success code.
+  return ssl_early_data_accepted;
+}
+
+void ssl_done_writing_client_hello(SSL_HANDSHAKE *hs) {
+  hs->ech_client_outer.Reset();
+  hs->cookie.Reset();
+  hs->key_share_bytes.Reset();
+}
+
 static enum ssl_hs_wait_t do_start_connect(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
@@ -386,6 +450,12 @@
     return ssl_hs_error;
   }
 
+  uint8_t ech_enc[EVP_HPKE_MAX_ENC_LENGTH];
+  size_t ech_enc_len;
+  if (!ssl_select_ech_config(hs, ech_enc, &ech_enc_len)) {
+    return ssl_hs_error;
+  }
+
   // Always advertise the ClientHello version from the original maximum version,
   // even on renegotiation. The static RSA key exchange uses this field, and
   // some servers fail when it changes across handshakes.
@@ -397,34 +467,47 @@
         hs->max_version >= TLS1_2_VERSION ? TLS1_2_VERSION : hs->max_version;
   }
 
-  // If the configured session has expired or was created at a disabled
-  // version, drop it.
-  if (ssl->session != NULL) {
+  // If the configured session has expired or is not usable, drop it. We also do
+  // not offer sessions on renegotiation.
+  if (ssl->session != nullptr) {
     if (ssl->session->is_server ||
         !ssl_supports_version(hs, ssl->session->ssl_version) ||
-        (ssl->session->session_id_length == 0 &&
-         ssl->session->ticket.empty()) ||
-        ssl->session->not_resumable ||
+        // Do not offer TLS 1.2 sessions with ECH. ClientHelloInner does not
+        // offer TLS 1.2, and the cleartext session ID may leak the server
+        // identity.
+        (hs->selected_ech_config &&
+         ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION) ||
+        !SSL_SESSION_is_resumable(ssl->session.get()) ||
         !ssl_session_is_time_valid(ssl, ssl->session.get()) ||
-        (ssl->quic_method != nullptr) != ssl->session->is_quic) {
-      ssl_set_session(ssl, NULL);
+        (ssl->quic_method != nullptr) != ssl->session->is_quic ||
+        ssl->s3->initial_handshake_complete) {
+      ssl_set_session(ssl, nullptr);
     }
   }
 
   if (!RAND_bytes(ssl->s3->client_random, sizeof(ssl->s3->client_random))) {
     return ssl_hs_error;
   }
+  if (hs->selected_ech_config &&
+      !RAND_bytes(hs->inner_client_random, sizeof(hs->inner_client_random))) {
+    return ssl_hs_error;
+  }
 
   // Never send a session ID in QUIC. QUIC uses TLS 1.3 at a minimum and
   // disables TLS 1.3 middlebox compatibility mode.
   if (ssl->quic_method == nullptr) {
-    if (ssl->session != nullptr && !ssl->s3->initial_handshake_complete &&
-        ssl->session->session_id_length > 0) {
+    const bool has_id_session = ssl->session != nullptr &&
+                                ssl->session->session_id_length > 0 &&
+                                ssl->session->ticket.empty();
+    const bool has_ticket_session =
+        ssl->session != nullptr && !ssl->session->ticket.empty();
+    if (has_id_session) {
       hs->session_id_len = ssl->session->session_id_length;
       OPENSSL_memcpy(hs->session_id, ssl->session->session_id,
                      hs->session_id_len);
-    } else if (hs->max_version >= TLS1_3_VERSION) {
-      // Initialize a random session ID.
+    } else if (has_ticket_session || hs->max_version >= TLS1_3_VERSION) {
+      // Send a random session ID. TLS 1.3 always sends one, and TLS 1.2 session
+      // tickets require a placeholder value to signal resumption.
       hs->session_id_len = sizeof(hs->session_id);
       if (!RAND_bytes(hs->session_id, hs->session_id_len)) {
         return ssl_hs_error;
@@ -432,7 +515,17 @@
     }
   }
 
-  if (!ssl_write_client_hello(hs)) {
+  ssl_early_data_reason_t reason = should_offer_early_data(hs);
+  if (reason != ssl_early_data_accepted) {
+    ssl->s3->early_data_reason = reason;
+  } else {
+    hs->early_data_offered = true;
+  }
+
+  if (!ssl_setup_key_shares(hs, /*override_group_id=*/0) ||
+      !ssl_setup_extension_permutation(hs) ||
+      !ssl_encrypt_client_hello(hs, MakeConstSpan(ech_enc, ech_enc_len)) ||
+      !ssl_add_client_hello(hs)) {
     return ssl_hs_error;
   }
 
@@ -458,9 +551,7 @@
     return ssl_hs_error;
   }
 
-  if (!tls13_init_early_key_schedule(
-          hs,
-          MakeConstSpan(ssl->session->secret, ssl->session->secret_length)) ||
+  if (!tls13_init_early_key_schedule(hs, ssl->session.get()) ||
       !tls13_derive_early_secret(hs)) {
     return ssl_hs_error;
   }
@@ -511,6 +602,10 @@
 
   assert(SSL_is_dtls(ssl));
 
+  // When implementing DTLS 1.3, we need to handle the interactions between
+  // HelloVerifyRequest, DTLS 1.3's HelloVerifyRequest removal, and ECH.
+  assert(hs->max_version < TLS1_3_VERSION);
+
   SSLMessage msg;
   if (!ssl->method->get_message(ssl, &msg)) {
     return ssl_hs_read_message;
@@ -542,7 +637,7 @@
     return ssl_hs_error;
   }
 
-  if (!ssl_write_client_hello(hs)) {
+  if (!ssl_add_client_hello(hs)) {
     return ssl_hs_error;
   }
 
@@ -550,6 +645,38 @@
   return ssl_hs_flush;
 }
 
+bool ssl_parse_server_hello(ParsedServerHello *out, uint8_t *out_alert,
+                            const SSLMessage &msg) {
+  if (msg.type != SSL3_MT_SERVER_HELLO) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
+    return false;
+  }
+  out->raw = msg.raw;
+  CBS body = msg.body;
+  if (!CBS_get_u16(&body, &out->legacy_version) ||
+      !CBS_get_bytes(&body, &out->random, SSL3_RANDOM_SIZE) ||
+      !CBS_get_u8_length_prefixed(&body, &out->session_id) ||
+      CBS_len(&out->session_id) > SSL3_SESSION_ID_SIZE ||
+      !CBS_get_u16(&body, &out->cipher_suite) ||
+      !CBS_get_u8(&body, &out->compression_method)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return false;
+  }
+  // In TLS 1.2 and below, empty extensions blocks may be omitted. In TLS 1.3,
+  // ServerHellos always have extensions, so this can be applied generically.
+  CBS_init(&out->extensions, nullptr, 0);
+  if ((CBS_len(&body) != 0 &&
+       !CBS_get_u16_length_prefixed(&body, &out->extensions)) ||
+      CBS_len(&body) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return false;
+  }
+  return true;
+}
+
 static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   SSLMessage msg;
@@ -557,26 +684,12 @@
     return ssl_hs_read_server_hello;
   }
 
-  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
-    return ssl_hs_error;
-  }
-
-  CBS server_hello = msg.body, server_random, session_id;
-  uint16_t server_version, cipher_suite;
-  uint8_t compression_method;
-  if (!CBS_get_u16(&server_hello, &server_version) ||
-      !CBS_get_bytes(&server_hello, &server_random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&server_hello, &session_id) ||
-      CBS_len(&session_id) > SSL3_SESSION_ID_SIZE ||
-      !CBS_get_u16(&server_hello, &cipher_suite) ||
-      !CBS_get_u8(&server_hello, &compression_method)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return ssl_hs_error;
-  }
-
-  // Use the supported_versions extension if applicable.
-  if (!parse_supported_versions(hs, &server_version, &server_hello)) {
+  ParsedServerHello server_hello;
+  uint16_t server_version;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!ssl_parse_server_hello(&server_hello, &alert, msg) ||
+      !parse_server_version(hs, &server_version, &alert, server_hello)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
@@ -607,19 +720,30 @@
   // Clear some TLS 1.3 state that no longer needs to be retained.
   hs->key_shares[0].reset();
   hs->key_shares[1].reset();
-  hs->key_share_bytes.Reset();
+  ssl_done_writing_client_hello(hs);
 
   // A TLS 1.2 server would not know to skip the early data we offered. Report
   // an error code sooner. The caller may use this error code to implement the
   // fallback described in RFC 8446 appendix D.3.
   if (hs->early_data_offered) {
+    // Disconnect early writes. This ensures subsequent |SSL_write| calls query
+    // the handshake which, in turn, will replay the error code rather than fail
+    // at the |write_shutdown| check. See https://crbug.com/1078515.
+    // TODO(davidben): Should all handshake errors do this? What about record
+    // decryption failures?
+    hs->can_early_write = false;
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_ON_EARLY_DATA);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_PROTOCOL_VERSION);
     return ssl_hs_error;
   }
 
+  // TLS 1.2 handshakes cannot accept ECH.
+  if (hs->selected_ech_config) {
+    ssl->s3->ech_status = ssl_ech_rejected;
+  }
+
   // Copy over the server random.
-  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
+  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_hello.random),
                  SSL3_RANDOM_SIZE);
 
   // Enforce the TLS 1.3 anti-downgrade feature.
@@ -642,64 +766,44 @@
     }
   }
 
-  if (!ssl->s3->initial_handshake_complete && ssl->session != nullptr &&
-      ssl->session->session_id_length != 0 &&
-      CBS_mem_equal(&session_id, ssl->session->session_id,
-                    ssl->session->session_id_length)) {
-    ssl->s3->session_reused = true;
-  } else {
-    // The server may also have echoed back the TLS 1.3 compatibility mode
-    // session ID. As we know this is not a session the server knows about, any
-    // server resuming it is in error. Reject the first connection
-    // deterministicly, rather than installing an invalid session into the
-    // session cache. https://crbug.com/796910
-    if (hs->session_id_len != 0 &&
-        CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_SERVER_ECHOED_INVALID_SESSION_ID);
-      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-      return ssl_hs_error;
-    }
-
-    // The session wasn't resumed. Create a fresh SSL_SESSION to
-    // fill out.
-    ssl_set_session(ssl, NULL);
-    if (!ssl_get_new_session(hs, 0 /* client */)) {
-      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-      return ssl_hs_error;
-    }
-    // Note: session_id could be empty.
-    hs->new_session->session_id_length = CBS_len(&session_id);
-    OPENSSL_memcpy(hs->new_session->session_id, CBS_data(&session_id),
-                   CBS_len(&session_id));
-  }
-
-  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
-  if (cipher == NULL) {
-    // unknown cipher
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CIPHER_RETURNED);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    return ssl_hs_error;
-  }
-
   // The cipher must be allowed in the selected version and enabled.
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
   uint32_t mask_a, mask_k;
   ssl_get_client_disabled(hs, &mask_a, &mask_k);
-  if ((cipher->algorithm_mkey & mask_k) || (cipher->algorithm_auth & mask_a) ||
+  if (cipher == nullptr ||
+      (cipher->algorithm_mkey & mask_k) ||
+      (cipher->algorithm_auth & mask_a) ||
       SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
       SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl) ||
-      !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, cipher)) {
+      !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), nullptr, cipher)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
-  if (ssl->session != NULL) {
+  hs->new_cipher = cipher;
+
+  if (hs->session_id_len != 0 &&
+      CBS_mem_equal(&server_hello.session_id, hs->session_id,
+                    hs->session_id_len)) {
+    // Echoing the ClientHello session ID in TLS 1.2, whether from the session
+    // or a synthetic one, indicates resumption. If there was no session (or if
+    // the session was only offered in ECH ClientHelloInner), this was the
+    // TLS 1.3 compatibility mode session ID. As we know this is not a session
+    // the server knows about, any server resuming it is in error. Reject the
+    // first connection deterministicly, rather than installing an invalid
+    // session into the session cache. https://crbug.com/796910
+    if (ssl->session == nullptr || ssl->s3->ech_status == ssl_ech_rejected) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_SERVER_ECHOED_INVALID_SESSION_ID);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      return ssl_hs_error;
+    }
     if (ssl->session->ssl_version != ssl->version) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_VERSION_NOT_RETURNED);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
-    if (ssl->session->cipher != cipher) {
+    if (ssl->session->cipher != hs->new_cipher) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
@@ -711,10 +815,23 @@
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
+    // We never offer sessions on renegotiation.
+    assert(!ssl->s3->initial_handshake_complete);
+    ssl->s3->session_reused = true;
   } else {
-    hs->new_session->cipher = cipher;
+    // The session wasn't resumed. Create a fresh SSL_SESSION to fill out.
+    ssl_set_session(ssl, NULL);
+    if (!ssl_get_new_session(hs)) {
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return ssl_hs_error;
+    }
+    // Note: session_id could be empty.
+    hs->new_session->session_id_length = CBS_len(&server_hello.session_id);
+    OPENSSL_memcpy(hs->new_session->session_id,
+                   CBS_data(&server_hello.session_id),
+                   CBS_len(&server_hello.session_id));
+    hs->new_session->cipher = hs->new_cipher;
   }
-  hs->new_cipher = cipher;
 
   // Now that the cipher is known, initialize the handshake hash and hash the
   // ServerHello.
@@ -733,26 +850,17 @@
   }
 
   // Only the NULL compression algorithm is supported.
-  if (compression_method != 0) {
+  if (server_hello.compression_method != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
 
-  // TLS extensions
-  if (!ssl_parse_serverhello_tlsext(hs, &server_hello)) {
+  if (!ssl_parse_serverhello_tlsext(hs, &server_hello.extensions)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
     return ssl_hs_error;
   }
 
-  // There should be nothing left over in the record.
-  if (CBS_len(&server_hello) != 0) {
-    // wrong packet length
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return ssl_hs_error;
-  }
-
   if (ssl->session != NULL &&
       hs->extended_master_secret != ssl->session->extended_master_secret) {
     if (ssl->session->extended_master_secret) {
@@ -764,13 +872,6 @@
     return ssl_hs_error;
   }
 
-  if (ssl->s3->token_binding_negotiated &&
-      (!hs->extended_master_secret || !ssl->s3->send_connection_binding)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NEGOTIATED_TB_WITHOUT_EMS_OR_RI);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
-    return ssl_hs_error;
-  }
-
   ssl->method->next_message(ssl);
 
   if (ssl->session != NULL) {
@@ -1219,8 +1320,12 @@
     return ssl_hs_ok;
   }
 
-  // Call cert_cb to update the certificate.
-  if (hs->config->cert->cert_cb != NULL) {
+  if (ssl->s3->ech_status == ssl_ech_rejected) {
+    // Do not send client certificates on ECH reject. We have not authenticated
+    // the server for the name that can learn the certificate.
+    SSL_certs_clear(ssl);
+  } else if (hs->config->cert->cert_cb != nullptr) {
+    // Call cert_cb to update the certificate.
     int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg);
     if (rv == 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
@@ -1482,18 +1587,7 @@
 
 static enum ssl_hs_wait_t do_send_client_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  // Resolve Channel ID first, before any non-idempotent operations.
-  if (ssl->s3->channel_id_valid) {
-    if (!ssl_do_channel_id_callback(hs)) {
-      return ssl_hs_error;
-    }
-
-    if (hs->config->channel_id_private == NULL) {
-      hs->state = state_send_client_finished;
-      return ssl_hs_channel_id_lookup;
-    }
-  }
-
+  hs->can_release_private_key = true;
   if (!ssl->method->add_change_cipher_spec(ssl) ||
       !tls1_change_cipher_state(hs, evp_aead_seal)) {
     return ssl_hs_error;
@@ -1518,7 +1612,7 @@
     }
   }
 
-  if (ssl->s3->channel_id_valid) {
+  if (hs->channel_id_negotiated) {
     ScopedCBB cbb;
     CBB body;
     if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CHANNEL_ID) ||
@@ -1538,7 +1632,7 @@
 }
 
 static bool can_false_start(const SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
+  const SSL *const ssl = hs->ssl;
 
   // False Start bypasses the Finished check's downgrade protection. This can
   // enable attacks where we send data under weaker settings than supported
@@ -1556,6 +1650,13 @@
     return false;
   }
 
+  // If ECH was rejected, disable False Start. We run the handshake to
+  // completion, including the Finished downgrade check, to authenticate the
+  // recovery flow.
+  if (ssl->s3->ech_status == ssl_ech_rejected) {
+    return false;
+  }
+
   // Additionally require ALPN or NPN by default.
   //
   // TODO(davidben): Can this constraint be relaxed globally now that cipher
@@ -1635,40 +1736,30 @@
     return ssl_hs_read_change_cipher_spec;
   }
 
-  SSL_SESSION *session = hs->new_session.get();
-  UniquePtr<SSL_SESSION> renewed_session;
-  if (ssl->session != NULL) {
+  if (ssl->session != nullptr) {
     // The server is sending a new ticket for an existing session. Sessions are
     // immutable once established, so duplicate all but the ticket of the
     // existing session.
-    renewed_session =
+    assert(!hs->new_session);
+    hs->new_session =
         SSL_SESSION_dup(ssl->session.get(), SSL_SESSION_INCLUDE_NONAUTH);
-    if (!renewed_session) {
-      // This should never happen.
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    if (!hs->new_session) {
       return ssl_hs_error;
     }
-    session = renewed_session.get();
   }
 
   // |ticket_lifetime_hint| is measured from when the ticket was issued.
-  ssl_session_rebase_time(ssl, session);
+  ssl_session_rebase_time(ssl, hs->new_session.get());
 
-  if (!session->ticket.CopyFrom(ticket)) {
+  if (!hs->new_session->ticket.CopyFrom(ticket)) {
     return ssl_hs_error;
   }
-  session->ticket_lifetime_hint = ticket_lifetime_hint;
+  hs->new_session->ticket_lifetime_hint = ticket_lifetime_hint;
 
-  // Generate a session ID for this session. Some callers expect all sessions to
-  // have a session ID. Additionally, it acts as the session ID to signal
-  // resumption.
-  SHA256(CBS_data(&ticket), CBS_len(&ticket), session->session_id);
-  session->session_id_length = SHA256_DIGEST_LENGTH;
-
-  if (renewed_session) {
-    session->not_resumable = false;
-    ssl->session = std::move(renewed_session);
-  }
+  // Historically, OpenSSL filled in fake session IDs for ticket-based sessions.
+  // TODO(davidben): Are external callers relying on this? Try removing this.
+  SHA256(CBS_data(&ticket), CBS_len(&ticket), hs->new_session->session_id);
+  hs->new_session->session_id_length = SHA256_DIGEST_LENGTH;
 
   ssl->method->next_message(ssl);
   hs->state = state_process_change_cipher_spec;
@@ -1702,15 +1793,25 @@
 
 static enum ssl_hs_wait_t do_finish_client_handshake(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+  if (ssl->s3->ech_status == ssl_ech_rejected) {
+    // Release the retry configs.
+    hs->ech_authenticated_reject = true;
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ECH_REQUIRED);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_ECH_REJECTED);
+    return ssl_hs_error;
+  }
 
   ssl->method->on_handshake_complete(ssl);
 
-  if (ssl->session != NULL) {
-    ssl->s3->established_session = UpRef(ssl->session);
-  } else {
-    // We make a copy of the session in order to maintain the immutability
-    // of the new established_session due to False Start. The caller may
-    // have taken a reference to the temporary session.
+  // Note TLS 1.2 resumptions with ticket renewal have both |ssl->session| (the
+  // resumed session) and |hs->new_session| (the session with the new ticket).
+  bool has_new_session = hs->new_session != nullptr;
+  if (has_new_session) {
+    // When False Start is enabled, the handshake reports completion early. The
+    // caller may then have passed the (then unresuable) |hs->new_session| to
+    // another thread via |SSL_get0_session| for resumption. To avoid potential
+    // race conditions in such callers, we duplicate the session before
+    // clearing |not_resumable|.
     ssl->s3->established_session =
         SSL_SESSION_dup(hs->new_session.get(), SSL_SESSION_DUP_ALL);
     if (!ssl->s3->established_session) {
@@ -1722,11 +1823,16 @@
     }
 
     hs->new_session.reset();
+  } else {
+    assert(ssl->session != nullptr);
+    ssl->s3->established_session = UpRef(ssl->session);
   }
 
   hs->handshake_finalized = true;
   ssl->s3->initial_handshake_complete = true;
-  ssl_update_cache(hs, SSL_SESS_CACHE_CLIENT);
+  if (has_new_session) {
+    ssl_update_cache(ssl);
+  }
 
   hs->state = state_done;
   return ssl_hs_ok;
diff --git a/deps/boringssl/src/ssl/handshake_server.cc b/deps/boringssl/src/ssl/handshake_server.cc
index bc0a0d1..fdf9511 100644
--- a/deps/boringssl/src/ssl/handshake_server.cc
+++ b/deps/boringssl/src/ssl/handshake_server.cc
@@ -154,6 +154,8 @@
 #include <openssl/bn.h>
 #include <openssl/bytestring.h>
 #include <openssl/cipher.h>
+#include <openssl/curve25519.h>
+#include <openssl/digest.h>
 #include <openssl/ec.h>
 #include <openssl/ecdsa.h>
 #include <openssl/err.h>
@@ -502,6 +504,91 @@
   return true;
 }
 
+static bool decrypt_ech(SSL_HANDSHAKE *hs, uint8_t *out_alert,
+                        const SSL_CLIENT_HELLO *client_hello) {
+  SSL *const ssl = hs->ssl;
+  CBS body;
+  if (!ssl_client_hello_get_extension(client_hello, &body,
+                                      TLSEXT_TYPE_encrypted_client_hello)) {
+    return true;
+  }
+  uint8_t type;
+  if (!CBS_get_u8(&body, &type)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return false;
+  }
+  if (type != ECH_CLIENT_OUTER) {
+    return true;
+  }
+  // This is a ClientHelloOuter ECH extension. Attempt to decrypt it.
+  uint8_t config_id;
+  uint16_t kdf_id, aead_id;
+  CBS enc, payload;
+  if (!CBS_get_u16(&body, &kdf_id) ||   //
+      !CBS_get_u16(&body, &aead_id) ||  //
+      !CBS_get_u8(&body, &config_id) ||
+      !CBS_get_u16_length_prefixed(&body, &enc) ||
+      !CBS_get_u16_length_prefixed(&body, &payload) ||  //
+      CBS_len(&body) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return false;
+  }
+
+  {
+    MutexReadLock lock(&ssl->ctx->lock);
+    hs->ech_keys = UpRef(ssl->ctx->ech_keys);
+  }
+
+  if (!hs->ech_keys) {
+    ssl->s3->ech_status = ssl_ech_rejected;
+    return true;
+  }
+
+  for (const auto &config : hs->ech_keys->configs) {
+    hs->ech_hpke_ctx.Reset();
+    if (config_id != config->ech_config().config_id ||
+        !config->SetupContext(hs->ech_hpke_ctx.get(), kdf_id, aead_id, enc)) {
+      // Ignore the error and try another ECHConfig.
+      ERR_clear_error();
+      continue;
+    }
+    Array<uint8_t> encoded_client_hello_inner;
+    bool is_decrypt_error;
+    if (!ssl_client_hello_decrypt(hs->ech_hpke_ctx.get(),
+                                  &encoded_client_hello_inner,
+                                  &is_decrypt_error, client_hello, payload)) {
+      if (is_decrypt_error) {
+        // Ignore the error and try another ECHConfig.
+        ERR_clear_error();
+        continue;
+      }
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
+      return false;
+    }
+
+    // Recover the ClientHelloInner from the EncodedClientHelloInner.
+    bssl::Array<uint8_t> client_hello_inner;
+    if (!ssl_decode_client_hello_inner(ssl, out_alert, &client_hello_inner,
+                                       encoded_client_hello_inner,
+                                       client_hello)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      return false;
+    }
+    hs->ech_client_hello_buf = std::move(client_hello_inner);
+    hs->ech_config_id = config_id;
+    ssl->s3->ech_status = ssl_ech_accepted;
+    return true;
+  }
+
+  // If we did not accept ECH, proceed with the ClientHelloOuter. Note this
+  // could be key mismatch or ECH GREASE, so we must complete the handshake
+  // as usual, except EncryptedExtensions will contain retry configs.
+  ssl->s3->ech_status = ssl_ech_rejected;
+  return true;
+}
+
 static bool extract_sni(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                         const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
@@ -563,7 +650,7 @@
   }
 
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
+  if (!ssl_client_hello_init(ssl, &client_hello, msg.body)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
@@ -582,11 +669,36 @@
   }
 
   uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!decrypt_ech(hs, &alert, &client_hello)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return ssl_hs_error;
+  }
+
+  // ECH may have changed which ClientHello we process. Update |msg| and
+  // |client_hello| in case.
+  if (!hs->GetClientHello(&msg, &client_hello)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return ssl_hs_error;
+  }
+
   if (!extract_sni(hs, &alert, &client_hello)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
+  hs->state = state12_read_client_hello_after_ech;
+  return ssl_hs_ok;
+}
+
+static enum ssl_hs_wait_t do_read_client_hello_after_ech(SSL_HANDSHAKE *hs) {
+  SSL *const ssl = hs->ssl;
+
+  SSLMessage msg_unused;
+  SSL_CLIENT_HELLO client_hello;
+  if (!hs->GetClientHello(&msg_unused, &client_hello)) {
+    return ssl_hs_error;
+  }
+
   // Run the early callback.
   if (ssl->ctx->select_certificate_cb != NULL) {
     switch (ssl->ctx->select_certificate_cb(&client_hello)) {
@@ -614,6 +726,7 @@
     hs->apply_jdk11_workaround = true;
   }
 
+  uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!negotiate_version(hs, &alert, &client_hello)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
@@ -644,12 +757,6 @@
     return ssl_hs_error;
   }
 
-  if (hs->ech_present && hs->ech_is_inner_present) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    return ssl_hs_error;
-  }
-
   hs->state = state12_select_certificate;
   return ssl_hs_ok;
 }
@@ -657,11 +764,6 @@
 static enum ssl_hs_wait_t do_select_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
-  SSLMessage msg;
-  if (!ssl->method->get_message(ssl, &msg)) {
-    return ssl_hs_read_message;
-  }
-
   // Call |cert_cb| to update server certificates if required.
   if (hs->config->cert->cert_cb != NULL) {
     int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg);
@@ -701,10 +803,22 @@
     return ssl_hs_ok;
   }
 
+  // It should not be possible to negotiate TLS 1.2 with ECH. The
+  // ClientHelloInner decoding function rejects ClientHellos which offer TLS 1.2
+  // or below.
+  assert(ssl->s3->ech_status != ssl_ech_accepted);
+
+  // TODO(davidben): Also compute hints for TLS 1.2. When doing so, update the
+  // check in bssl_shim.cc to test this.
+  if (hs->hints_requested) {
+    return ssl_hs_hints_ready;
+  }
+
   ssl->s3->early_data_reason = ssl_early_data_protocol_version;
 
+  SSLMessage msg_unused;
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
+  if (!hs->GetClientHello(&msg_unused, &client_hello)) {
     return ssl_hs_error;
   }
 
@@ -743,10 +857,15 @@
   }
 
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
+  if (!ssl_client_hello_init(ssl, &client_hello, msg.body)) {
     return ssl_hs_error;
   }
 
+  hs->session_id_len = client_hello.session_id_len;
+  // This is checked in |ssl_client_hello_init|.
+  assert(hs->session_id_len <= sizeof(hs->session_id));
+  OPENSSL_memcpy(hs->session_id, client_hello.session_id, hs->session_id_len);
+
   // Determine whether we are doing session resumption.
   UniquePtr<SSL_SESSION> session;
   bool tickets_supported = false, renew_ticket = false;
@@ -778,16 +897,20 @@
     hs->ticket_expected = renew_ticket;
     ssl->session = std::move(session);
     ssl->s3->session_reused = true;
+    hs->can_release_private_key = true;
   } else {
     hs->ticket_expected = tickets_supported;
-    ssl_set_session(ssl, NULL);
-    if (!ssl_get_new_session(hs, 1 /* server */)) {
+    ssl_set_session(ssl, nullptr);
+    if (!ssl_get_new_session(hs)) {
       return ssl_hs_error;
     }
 
-    // Clear the session ID if we want the session to be single-use.
-    if (!(ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) {
-      hs->new_session->session_id_length = 0;
+    // Assign a session ID if not using session tickets.
+    if (!hs->ticket_expected &&
+        (ssl->ctx->session_cache_mode & SSL_SESS_CACHE_SERVER)) {
+      hs->new_session->session_id_length = SSL3_SSL_SESSION_ID_LENGTH;
+      RAND_bytes(hs->new_session->session_id,
+                 hs->new_session->session_id_length);
     }
   }
 
@@ -806,7 +929,7 @@
     hs->cert_request = !!(hs->config->verify_mode & SSL_VERIFY_PEER);
     // Only request a certificate if Channel ID isn't negotiated.
     if ((hs->config->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
-        ssl->s3->channel_id_valid) {
+        hs->channel_id_negotiated) {
       hs->cert_request = false;
     }
     // CertificateRequest may only be sent in certificate-based ciphers.
@@ -850,8 +973,7 @@
 }
 
 static void copy_suffix(Span<uint8_t> out, Span<const uint8_t> in) {
-  out = out.subspan(out.size() - in.size());
-  assert(out.size() == in.size());
+  out = out.last(in.size());
   OPENSSL_memcpy(out.data(), in.data(), in.size());
 }
 
@@ -860,9 +982,9 @@
 
   // We only accept ChannelIDs on connections with ECDHE in order to avoid a
   // known attack while we fix ChannelID itself.
-  if (ssl->s3->channel_id_valid &&
+  if (hs->channel_id_negotiated &&
       (hs->new_cipher->algorithm_mkey & SSL_kECDHE) == 0) {
-    ssl->s3->channel_id_valid = false;
+    hs->channel_id_negotiated = false;
   }
 
   // If this is a resumption and the original handshake didn't support
@@ -870,7 +992,7 @@
   // session and so cannot resume with ChannelIDs.
   if (ssl->session != NULL &&
       ssl->session->original_handshake_hash_len == 0) {
-    ssl->s3->channel_id_valid = false;
+    hs->channel_id_negotiated = false;
   }
 
   struct OPENSSL_timeval now;
@@ -901,19 +1023,22 @@
     }
   }
 
-  const SSL_SESSION *session = hs->new_session.get();
+  Span<const uint8_t> session_id;
   if (ssl->session != nullptr) {
-    session = ssl->session.get();
+    // Echo the session ID from the ClientHello to indicate resumption.
+    session_id = MakeConstSpan(hs->session_id, hs->session_id_len);
+  } else {
+    session_id = MakeConstSpan(hs->new_session->session_id,
+                               hs->new_session->session_id_length);
   }
 
   ScopedCBB cbb;
-  CBB body, session_id;
+  CBB body, session_id_bytes;
   if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_SERVER_HELLO) ||
       !CBB_add_u16(&body, ssl->version) ||
       !CBB_add_bytes(&body, ssl->s3->server_random, SSL3_RANDOM_SIZE) ||
-      !CBB_add_u8_length_prefixed(&body, &session_id) ||
-      !CBB_add_bytes(&session_id, session->session_id,
-                     session->session_id_length) ||
+      !CBB_add_u8_length_prefixed(&body, &session_id_bytes) ||
+      !CBB_add_bytes(&session_id_bytes, session_id.data(), session_id.size()) ||
       !CBB_add_u16(&body, SSL_CIPHER_get_protocol_id(hs->new_cipher)) ||
       !CBB_add_u8(&body, 0 /* no compression */) ||
       !ssl_add_serverhello_tlsext(hs, &body) ||
@@ -1083,6 +1208,7 @@
     }
   }
 
+  hs->can_release_private_key = true;
   if (!ssl_add_message_cbb(ssl, cbb.get())) {
     return ssl_hs_error;
   }
@@ -1415,6 +1541,7 @@
   }
   hs->new_session->extended_master_secret = hs->extended_master_secret;
   CONSTTIME_DECLASSIFY(hs->new_session->secret, hs->new_session->secret_length);
+  hs->can_release_private_key = true;
 
   ssl->method->next_message(ssl);
   hs->state = state12_read_client_certificate_verify;
@@ -1556,7 +1683,7 @@
 static enum ssl_hs_wait_t do_read_channel_id(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
 
-  if (!ssl->s3->channel_id_valid) {
+  if (!hs->channel_id_negotiated) {
     hs->state = state12_read_client_finished;
     return ssl_hs_ok;
   }
@@ -1666,16 +1793,21 @@
     ssl->ctx->x509_method->session_clear(hs->new_session.get());
   }
 
-  if (ssl->session != NULL) {
-    ssl->s3->established_session = UpRef(ssl->session);
-  } else {
+  bool has_new_session = hs->new_session != nullptr;
+  if (has_new_session) {
+    assert(ssl->session == nullptr);
     ssl->s3->established_session = std::move(hs->new_session);
     ssl->s3->established_session->not_resumable = false;
+  } else {
+    assert(ssl->session != nullptr);
+    ssl->s3->established_session = UpRef(ssl->session);
   }
 
   hs->handshake_finalized = true;
   ssl->s3->initial_handshake_complete = true;
-  ssl_update_cache(hs, SSL_SESS_CACHE_SERVER);
+  if (has_new_session) {
+    ssl_update_cache(ssl);
+  }
 
   hs->state = state12_done;
   return ssl_hs_ok;
@@ -1693,6 +1825,9 @@
       case state12_read_client_hello:
         ret = do_read_client_hello(hs);
         break;
+      case state12_read_client_hello_after_ech:
+        ret = do_read_client_hello_after_ech(hs);
+        break;
       case state12_select_certificate:
         ret = do_select_certificate(hs);
         break;
@@ -1773,6 +1908,8 @@
       return "TLS server start_accept";
     case state12_read_client_hello:
       return "TLS server read_client_hello";
+    case state12_read_client_hello_after_ech:
+      return "TLS server read_client_hello_after_ech";
     case state12_select_certificate:
       return "TLS server select_certificate";
     case state12_tls13:
diff --git a/deps/boringssl/src/ssl/internal.h b/deps/boringssl/src/ssl/internal.h
index b3b7540..ab23d29 100644
--- a/deps/boringssl/src/ssl/internal.h
+++ b/deps/boringssl/src/ssl/internal.h
@@ -146,13 +146,16 @@
 
 #include <stdlib.h>
 
+#include <initializer_list>
 #include <limits>
 #include <new>
 #include <type_traits>
 #include <utility>
 
 #include <openssl/aead.h>
+#include <openssl/curve25519.h>
 #include <openssl/err.h>
+#include <openssl/hpke.h>
 #include <openssl/lhash.h>
 #include <openssl/mem.h>
 #include <openssl/span.h>
@@ -161,6 +164,7 @@
 
 #include "../crypto/err/internal.h"
 #include "../crypto/internal.h"
+#include "../crypto/lhash/internal.h"
 
 
 #if defined(OPENSSL_WINDOWS)
@@ -276,9 +280,9 @@
   T &operator[](size_t i) { return data_[i]; }
 
   T *begin() { return data_; }
-  const T *cbegin() const { return data_; }
+  const T *begin() const { return data_; }
   T *end() { return data_ + size_; }
-  const T *cend() const { return data_ + size_; }
+  const T *end() const { return data_ + size_; }
 
   void Reset() { Reset(nullptr, 0); }
 
@@ -378,6 +382,8 @@
     return *this;
   }
 
+  const T *data() const { return array_.data(); }
+  T *data() { return array_.data(); }
   size_t size() const { return size_; }
   bool empty() const { return size_ == 0; }
 
@@ -385,9 +391,9 @@
   T &operator[](size_t i) { return array_[i]; }
 
   T *begin() { return array_.data(); }
-  const T *cbegin() const { return array_.data(); }
+  const T *begin() const { return array_.data(); }
   T *end() { return array_.data() + size_; }
-  const T *cend() const { return array_.data() + size_; }
+  const T *end() const { return array_.data() + size_; }
 
   void clear() {
     size_ = 0;
@@ -484,15 +490,17 @@
                            uint16_t *out_max_version);
 
 // ssl_supports_version returns whether |hs| supports |version|.
-bool ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version);
+bool ssl_supports_version(const SSL_HANDSHAKE *hs, uint16_t version);
 
 // ssl_method_supports_version returns whether |method| supports |version|.
 bool ssl_method_supports_version(const SSL_PROTOCOL_METHOD *method,
                                  uint16_t version);
 
 // ssl_add_supported_versions writes the supported versions of |hs| to |cbb|, in
-// decreasing preference order.
-bool ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb);
+// decreasing preference order. The version list is filtered to those whose
+// protocol version is at least |extra_min_version|.
+bool ssl_add_supported_versions(const SSL_HANDSHAKE *hs, CBB *cbb,
+                                uint16_t extra_min_version);
 
 // ssl_negotiate_version negotiates a common version based on |hs|'s preferences
 // and the peer preference list in |peer_versions|. On success, it returns true
@@ -675,6 +683,9 @@
   SSLTranscript();
   ~SSLTranscript();
 
+  SSLTranscript(SSLTranscript &&other) = default;
+  SSLTranscript &operator=(SSLTranscript &&other) = default;
+
   // Init initializes the handshake transcript. If called on an existing
   // transcript, it resets the transcript and hash. It returns true on success
   // and false on failure.
@@ -683,7 +694,8 @@
   // InitHash initializes the handshake hash based on the PRF and contents of
   // the handshake transcript. Subsequent calls to |Update| will update the
   // rolling hash. It returns one on success and zero on failure. It is an error
-  // to call this function after the handshake buffer is released.
+  // to call this function after the handshake buffer is released. This may be
+  // called multiple times to change the hash function.
   bool InitHash(uint16_t version, const SSL_CIPHER *cipher);
 
   // UpdateForHelloRetryRequest resets the rolling hash with the
@@ -696,9 +708,9 @@
   // the transcript. It returns true on success and false on failure. If the
   // handshake buffer is still present, |digest| may be any supported digest.
   // Otherwise, |digest| must match the transcript hash.
-  bool CopyToHashContext(EVP_MD_CTX *ctx, const EVP_MD *digest);
+  bool CopyToHashContext(EVP_MD_CTX *ctx, const EVP_MD *digest) const;
 
-  Span<const uint8_t> buffer() {
+  Span<const uint8_t> buffer() const {
     return MakeConstSpan(reinterpret_cast<const uint8_t *>(buffer_->data),
                          buffer_->length);
   }
@@ -721,14 +733,14 @@
   // GetHash writes the handshake hash to |out| which must have room for at
   // least |DigestLen| bytes. On success, it returns true and sets |*out_len| to
   // the number of bytes written. Otherwise, it returns false.
-  bool GetHash(uint8_t *out, size_t *out_len);
+  bool GetHash(uint8_t *out, size_t *out_len) const;
 
   // GetFinishedMAC computes the MAC for the Finished message into the bytes
   // pointed by |out| and writes the number of bytes to |*out_len|. |out| must
   // have room for |EVP_MAX_MD_SIZE| bytes. It returns true on success and false
   // on failure.
   bool GetFinishedMAC(uint8_t *out, size_t *out_len, const SSL_SESSION *session,
-                      bool from_server);
+                      bool from_server) const;
 
  private:
   // buffer_, if non-null, contains the handshake transcript.
@@ -1066,6 +1078,10 @@
   // |Serialize|.
   static UniquePtr<SSLKeyShare> Create(CBS *in);
 
+  // Serializes writes the group ID and private key, in a format that can be
+  // read by |Create|.
+  bool Serialize(CBB *out);
+
   // GroupID returns the group ID.
   virtual uint16_t GroupID() const PURE_VIRTUAL;
 
@@ -1090,13 +1106,13 @@
   virtual bool Finish(Array<uint8_t> *out_secret, uint8_t *out_alert,
                       Span<const uint8_t> peer_key) PURE_VIRTUAL;
 
-  // Serialize writes the state of the key exchange to |out|, returning true if
-  // successful and false otherwise.
-  virtual bool Serialize(CBB *out) { return false; }
+  // SerializePrivateKey writes the private key to |out|, returning true if
+  // successful and false otherwise. It should be called after |Offer|.
+  virtual bool SerializePrivateKey(CBB *out) { return false; }
 
-  // Deserialize initializes the state of the key exchange from |in|, returning
-  // true if successful and false otherwise.  It is called by |Create|.
-  virtual bool Deserialize(CBS *in) { return false; }
+  // DeserializePrivateKey initializes the state of the key exchange from |in|,
+  // returning true if successful and false otherwise.
+  virtual bool DeserializePrivateKey(CBS *in) { return false; }
 };
 
 struct NamedGroup {
@@ -1352,9 +1368,10 @@
 bool tls13_init_key_schedule(SSL_HANDSHAKE *hs, Span<const uint8_t> psk);
 
 // tls13_init_early_key_schedule initializes the handshake hash and key
-// derivation state from the resumption secret and incorporates the PSK to
-// derive the early secrets. It returns one on success and zero on error.
-bool tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, Span<const uint8_t> psk);
+// derivation state from |session| for use with 0-RTT. It returns one on success
+// and zero on error.
+bool tls13_init_early_key_schedule(SSL_HANDSHAKE *hs,
+                                   const SSL_SESSION *session);
 
 // tls13_advance_key_schedule incorporates |in| into the key schedule with
 // HKDF-Extract. It returns true on success and false on error.
@@ -1407,25 +1424,184 @@
 // on failure.
 bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce);
 
-// tls13_write_psk_binder calculates the PSK binder value and replaces the last
-// bytes of |msg| with the resulting value. It returns true on success, and
-// false on failure.
-bool tls13_write_psk_binder(SSL_HANDSHAKE *hs, Span<uint8_t> msg);
+// tls13_write_psk_binder calculates the PSK binder value over |transcript| and
+// |msg|, and replaces the last bytes of |msg| with the resulting value. It
+// returns true on success, and false on failure. If |out_binder_len| is
+// non-NULL, it sets |*out_binder_len| to the length of the value computed.
+bool tls13_write_psk_binder(const SSL_HANDSHAKE *hs,
+                            const SSLTranscript &transcript, Span<uint8_t> msg,
+                            size_t *out_binder_len);
 
 // tls13_verify_psk_binder verifies that the handshake transcript, truncated up
 // to the binders has a valid signature using the value of |session|'s
 // resumption secret. It returns true on success, and false on failure.
-bool tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
-                             const SSLMessage &msg, CBS *binders);
+bool tls13_verify_psk_binder(const SSL_HANDSHAKE *hs,
+                             const SSL_SESSION *session, const SSLMessage &msg,
+                             CBS *binders);
 
 
-// Encrypted Client Hello.
+// Encrypted ClientHello.
 
-// tls13_ech_accept_confirmation computes the server's ECH acceptance signal,
-// writing it to |out|. It returns true on success, and false on failure.
-bool tls13_ech_accept_confirmation(
-    SSL_HANDSHAKE *hs, bssl::Span<uint8_t> out,
-    bssl::Span<const uint8_t> server_hello_ech_conf);
+struct ECHConfig {
+  static constexpr bool kAllowUniquePtr = true;
+  // raw contains the serialized ECHConfig.
+  Array<uint8_t> raw;
+  // The following fields alias into |raw|.
+  Span<const uint8_t> public_key;
+  Span<const uint8_t> public_name;
+  Span<const uint8_t> cipher_suites;
+  uint16_t kem_id = 0;
+  uint8_t maximum_name_length = 0;
+  uint8_t config_id = 0;
+};
+
+class ECHServerConfig {
+ public:
+  static constexpr bool kAllowUniquePtr = true;
+  ECHServerConfig() = default;
+  ECHServerConfig(const ECHServerConfig &other) = delete;
+  ECHServerConfig &operator=(ECHServerConfig &&) = delete;
+
+  // Init parses |ech_config| as an ECHConfig and saves a copy of |key|.
+  // It returns true on success and false on error.
+  bool Init(Span<const uint8_t> ech_config, const EVP_HPKE_KEY *key,
+            bool is_retry_config);
+
+  // SetupContext sets up |ctx| for a new connection, given the specified
+  // HPKE ciphersuite and encapsulated KEM key. It returns true on success and
+  // false on error. This function may only be called on an initialized object.
+  bool SetupContext(EVP_HPKE_CTX *ctx, uint16_t kdf_id, uint16_t aead_id,
+                    Span<const uint8_t> enc) const;
+
+  const ECHConfig &ech_config() const { return ech_config_; }
+  bool is_retry_config() const { return is_retry_config_; }
+
+ private:
+  ECHConfig ech_config_;
+  ScopedEVP_HPKE_KEY key_;
+  bool is_retry_config_ = false;
+};
+
+enum ssl_client_hello_type_t {
+  ssl_client_hello_unencrypted,
+  ssl_client_hello_inner,
+  ssl_client_hello_outer,
+};
+
+// ECH_CLIENT_* are types for the ClientHello encrypted_client_hello extension.
+#define ECH_CLIENT_OUTER 0
+#define ECH_CLIENT_INNER 1
+
+// ssl_decode_client_hello_inner recovers the full ClientHelloInner from the
+// EncodedClientHelloInner |encoded_client_hello_inner| by replacing its
+// outer_extensions extension with the referenced extensions from the
+// ClientHelloOuter |client_hello_outer|. If successful, it writes the recovered
+// ClientHelloInner to |out_client_hello_inner|. It returns true on success and
+// false on failure.
+OPENSSL_EXPORT bool ssl_decode_client_hello_inner(
+    SSL *ssl, uint8_t *out_alert, Array<uint8_t> *out_client_hello_inner,
+    Span<const uint8_t> encoded_client_hello_inner,
+    const SSL_CLIENT_HELLO *client_hello_outer);
+
+// ssl_client_hello_decrypt attempts to decrypt the |payload| and writes the
+// result to |*out|. |payload| must point into |client_hello_outer|. It returns
+// true on success and false on error. On error, it sets |*out_is_decrypt_error|
+// to whether the failure was due to a bad ciphertext.
+bool ssl_client_hello_decrypt(EVP_HPKE_CTX *hpke_ctx, Array<uint8_t> *out,
+                              bool *out_is_decrypt_error,
+                              const SSL_CLIENT_HELLO *client_hello_outer,
+                              Span<const uint8_t> payload);
+
+#define ECH_CONFIRMATION_SIGNAL_LEN 8
+
+// ssl_ech_confirmation_signal_hello_offset returns the offset of the ECH
+// confirmation signal in a ServerHello message, including the handshake header.
+size_t ssl_ech_confirmation_signal_hello_offset(const SSL *ssl);
+
+// ssl_ech_accept_confirmation computes the server's ECH acceptance signal,
+// writing it to |out|. The transcript portion is the concatenation of
+// |transcript| with |msg|. The |ECH_CONFIRMATION_SIGNAL_LEN| bytes from
+// |offset| in |msg| are replaced with zeros before hashing. This function
+// returns true on success, and false on failure.
+bool ssl_ech_accept_confirmation(const SSL_HANDSHAKE *hs, Span<uint8_t> out,
+                                 Span<const uint8_t> client_random,
+                                 const SSLTranscript &transcript, bool is_hrr,
+                                 Span<const uint8_t> msg, size_t offset);
+
+// ssl_is_valid_ech_public_name returns true if |public_name| is a valid ECH
+// public name and false otherwise. It is exported for testing.
+OPENSSL_EXPORT bool ssl_is_valid_ech_public_name(
+    Span<const uint8_t> public_name);
+
+// ssl_is_valid_ech_config_list returns true if |ech_config_list| is a valid
+// ECHConfigList structure and false otherwise.
+bool ssl_is_valid_ech_config_list(Span<const uint8_t> ech_config_list);
+
+// ssl_select_ech_config selects an ECHConfig and associated parameters to offer
+// on the client and updates |hs|. It returns true on success, whether an
+// ECHConfig was found or not, and false on internal error. On success, the
+// encapsulated key is written to |out_enc| and |*out_enc_len| is set to the
+// number of bytes written. If the function did not select an ECHConfig, the
+// encapsulated key is the empty string.
+bool ssl_select_ech_config(SSL_HANDSHAKE *hs, Span<uint8_t> out_enc,
+                           size_t *out_enc_len);
+
+// ssl_ech_extension_body_length returns the length of the body of a ClientHello
+// ECH extension that encrypts |in_len| bytes with |aead| and an 'enc' value of
+// length |enc_len|. The result does not include the four-byte extension header.
+size_t ssl_ech_extension_body_length(const EVP_HPKE_AEAD *aead, size_t enc_len,
+                                     size_t in_len);
+
+// ssl_encrypt_client_hello constructs a new ClientHelloInner, adds it to the
+// inner transcript, and encrypts for inclusion in the ClientHelloOuter. |enc|
+// is the encapsulated key to include in the extension. It returns true on
+// success and false on error. If not offering ECH, |enc| is ignored and the
+// function will compute a GREASE ECH extension if necessary, and otherwise
+// return success while doing nothing.
+//
+// Encrypting the ClientHelloInner incorporates all extensions in the
+// ClientHelloOuter, so all other state necessary for |ssl_add_client_hello|
+// must already be computed.
+bool ssl_encrypt_client_hello(SSL_HANDSHAKE *hs, Span<const uint8_t> enc);
+
+
+// Delegated credentials.
+
+// This structure stores a delegated credential (DC) as defined by
+// draft-ietf-tls-subcerts-03.
+struct DC {
+  static constexpr bool kAllowUniquePtr = true;
+  ~DC();
+
+  // Dup returns a copy of this DC and takes references to |raw| and |pkey|.
+  UniquePtr<DC> Dup();
+
+  // Parse parses the delegated credential stored in |in|. If successful it
+  // returns the parsed structure, otherwise it returns |nullptr| and sets
+  // |*out_alert|.
+  static UniquePtr<DC> Parse(CRYPTO_BUFFER *in, uint8_t *out_alert);
+
+  // raw is the delegated credential encoded as specified in draft-ietf-tls-
+  // subcerts-03.
+  UniquePtr<CRYPTO_BUFFER> raw;
+
+  // expected_cert_verify_algorithm is the signature scheme of the DC public
+  // key.
+  uint16_t expected_cert_verify_algorithm = 0;
+
+  // pkey is the public key parsed from |public_key|.
+  UniquePtr<EVP_PKEY> pkey;
+
+ private:
+  friend DC* New<DC>();
+  DC();
+};
+
+// ssl_signing_with_dc returns true if the peer has indicated support for
+// delegated credentials and this host has sent a delegated credential in
+// response. If this is true then we've committed to using the DC in the
+// handshake.
+bool ssl_signing_with_dc(const SSL_HANDSHAKE *hs);
 
 
 // Handshake functions.
@@ -1440,7 +1616,6 @@
   ssl_hs_handoff,
   ssl_hs_handback,
   ssl_hs_x509_lookup,
-  ssl_hs_channel_id_lookup,
   ssl_hs_private_key_operation,
   ssl_hs_pending_session,
   ssl_hs_pending_ticket,
@@ -1449,6 +1624,7 @@
   ssl_hs_read_end_of_early_data,
   ssl_hs_read_change_cipher_spec,
   ssl_hs_certificate_verify,
+  ssl_hs_hints_ready,
 };
 
 enum ssl_grease_index_t {
@@ -1458,12 +1634,14 @@
   ssl_grease_extension2,
   ssl_grease_version,
   ssl_grease_ticket_extension,
-  ssl_grease_last_index = ssl_grease_ticket_extension,
+  ssl_grease_ech_config_id,
+  ssl_grease_last_index = ssl_grease_ech_config_id,
 };
 
 enum tls12_server_hs_state_t {
   state12_start_accept = 0,
   state12_read_client_hello,
+  state12_read_client_hello_after_ech,
   state12_select_certificate,
   state12_tls13,
   state12_select_parameters,
@@ -1515,46 +1693,30 @@
   handback_max_value = handback_tls13,
 };
 
-
-// Delegated credentials.
-
-// This structure stores a delegated credential (DC) as defined by
-// draft-ietf-tls-subcerts-03.
-struct DC {
+// SSL_HANDSHAKE_HINTS contains handshake hints for a connection. See
+// |SSL_request_handshake_hints| and related functions.
+struct SSL_HANDSHAKE_HINTS {
   static constexpr bool kAllowUniquePtr = true;
-  ~DC();
 
-  // Dup returns a copy of this DC and takes references to |raw| and |pkey|.
-  UniquePtr<DC> Dup();
+  Array<uint8_t> server_random;
 
-  // Parse parses the delegated credential stored in |in|. If successful it
-  // returns the parsed structure, otherwise it returns |nullptr| and sets
-  // |*out_alert|.
-  static UniquePtr<DC> Parse(CRYPTO_BUFFER *in, uint8_t *out_alert);
+  uint16_t key_share_group_id = 0;
+  Array<uint8_t> key_share_public_key;
+  Array<uint8_t> key_share_secret;
 
-  // raw is the delegated credential encoded as specified in draft-ietf-tls-
-  // subcerts-03.
-  UniquePtr<CRYPTO_BUFFER> raw;
+  uint16_t signature_algorithm = 0;
+  Array<uint8_t> signature_input;
+  Array<uint8_t> signature_spki;
+  Array<uint8_t> signature;
 
-  // expected_cert_verify_algorithm is the signature scheme of the DC public
-  // key.
-  uint16_t expected_cert_verify_algorithm = 0;
+  Array<uint8_t> decrypted_psk;
+  bool ignore_psk = false;
 
-  // pkey is the public key parsed from |public_key|.
-  UniquePtr<EVP_PKEY> pkey;
-
- private:
-  friend DC* New<DC>();
-  DC();
+  uint16_t cert_compression_alg_id = 0;
+  Array<uint8_t> cert_compression_input;
+  Array<uint8_t> cert_compression_output;
 };
 
-// ssl_signing_with_dc returns true if the peer has indicated support for
-// delegated credentials and this host has sent a delegated credential in
-// response. If this is true then we've committed to using the DC in the
-// handshake.
-bool ssl_signing_with_dc(const SSL_HANDSHAKE *hs);
-
-
 struct SSL_HANDSHAKE {
   explicit SSL_HANDSHAKE(SSL *ssl);
   ~SSL_HANDSHAKE();
@@ -1599,7 +1761,21 @@
  public:
   void ResizeSecrets(size_t hash_len);
 
+  // GetClientHello, on the server, returns either the normal ClientHello
+  // message or the ClientHelloInner if it has been serialized to
+  // |ech_client_hello_buf|. This function should only be called when the
+  // current message is a ClientHello. It returns true on success and false on
+  // error.
+  //
+  // Note that fields of the returned |out_msg| and |out_client_hello| point
+  // into a handshake-owned buffer, so their lifetimes should not exceed this
+  // SSL_HANDSHAKE.
+  bool GetClientHello(SSLMessage *out_msg, SSL_CLIENT_HELLO *out_client_hello);
+
   Span<uint8_t> secret() { return MakeSpan(secret_, hash_len_); }
+  Span<const uint8_t> secret() const {
+    return MakeConstSpan(secret_, hash_len_);
+  }
   Span<uint8_t> early_traffic_secret() {
     return MakeSpan(early_traffic_secret_, hash_len_);
   }
@@ -1621,7 +1797,7 @@
 
   union {
     // sent is a bitset where the bits correspond to elements of kExtensions
-    // in t1_lib.c. Each bit is set if that extension was sent in a
+    // in extensions.cc. Each bit is set if that extension was sent in a
     // ClientHello. It's not used by servers.
     uint32_t sent = 0;
     // received is a bitset, like |sent|, but is used by servers to record
@@ -1629,9 +1805,9 @@
     uint32_t received;
   } extensions;
 
-  // retry_group is the group ID selected by the server in HelloRetryRequest in
-  // TLS 1.3.
-  uint16_t retry_group = 0;
+  // inner_extensions_sent, on clients that offer ECH, is |extensions.sent| for
+  // the ClientHelloInner.
+  uint32_t inner_extensions_sent = 0;
 
   // error, if |wait| is |ssl_hs_error|, is the error the handshake failed on.
   UniquePtr<ERR_SAVE_STATE> error;
@@ -1644,15 +1820,31 @@
   // transcript is the current handshake transcript.
   SSLTranscript transcript;
 
+  // inner_transcript, on the client, is the handshake transcript for the
+  // ClientHelloInner handshake. It is moved to |transcript| if the server
+  // accepts ECH.
+  SSLTranscript inner_transcript;
+
+  // inner_client_random is the ClientHello random value used with
+  // ClientHelloInner.
+  uint8_t inner_client_random[SSL3_RANDOM_SIZE] = {0};
+
   // cookie is the value of the cookie received from the server, if any.
   Array<uint8_t> cookie;
 
-  // ech_grease contains the bytes of the GREASE ECH extension that was sent in
-  // the first ClientHello.
-  Array<uint8_t> ech_grease;
+  // ech_client_outer contains the outer ECH extension to send in the
+  // ClientHello, excluding the header and type byte.
+  Array<uint8_t> ech_client_outer;
 
-  // key_share_bytes is the value of the previously sent KeyShare extension by
-  // the client in TLS 1.3.
+  // ech_retry_configs, on the client, contains the retry configs from the
+  // server as a serialized ECHConfigList.
+  Array<uint8_t> ech_retry_configs;
+
+  // ech_client_hello_buf, on the server, contains the bytes of the
+  // reconstructed ClientHelloInner message.
+  Array<uint8_t> ech_client_hello_buf;
+
+  // key_share_bytes is the key_share extension that the client should send.
   Array<uint8_t> key_share_bytes;
 
   // ecdh_public_key, for servers, is the key share to be sent to the client in
@@ -1676,17 +1868,21 @@
   // peer_key is the peer's ECDH key for a TLS 1.2 client.
   Array<uint8_t> peer_key;
 
-  // negotiated_token_binding_version is used by a server to store the
-  // on-the-wire encoding of the Token Binding protocol version to advertise in
-  // the ServerHello/EncryptedExtensions if the Token Binding extension is to be
-  // sent.
-  uint16_t negotiated_token_binding_version;
+  // extension_permutation is the permutation to apply to ClientHello
+  // extensions. It maps indices into the |kExtensions| table into other
+  // indices.
+  Array<uint8_t> extension_permutation;
 
   // cert_compression_alg_id, for a server, contains the negotiated certificate
   // compression algorithm for this client. It is only valid if
   // |cert_compression_negotiated| is true.
   uint16_t cert_compression_alg_id;
 
+  // ech_hpke_ctx is the HPKE context used in ECH. On the server, it is
+  // initialized if |ech_status| is |ssl_ech_accepted|. On the client, it is
+  // initialized if |selected_ech_config| is not nullptr.
+  ScopedEVP_HPKE_CTX ech_hpke_ctx;
+
   // server_params, in a TLS 1.2 server, stores the ServerKeyExchange
   // parameters. It has client and server randoms prepended for signing
   // convenience.
@@ -1723,27 +1919,40 @@
   // the client if |in_early_data| is true.
   UniquePtr<SSL_SESSION> early_session;
 
+  // ssl_ech_keys, for servers, is the set of ECH keys to use with this
+  // handshake. This is copied from |SSL_CTX| to ensure consistent behavior as
+  // |SSL_CTX| rotates keys.
+  UniquePtr<SSL_ECH_KEYS> ech_keys;
+
+  // selected_ech_config, for clients, is the ECHConfig the client uses to offer
+  // ECH, or nullptr if ECH is not being offered. If non-NULL, |ech_hpke_ctx|
+  // will be initialized.
+  UniquePtr<ECHConfig> selected_ech_config;
+
   // new_cipher is the cipher being negotiated in this handshake.
   const SSL_CIPHER *new_cipher = nullptr;
 
   // key_block is the record-layer key block for TLS 1.2 and earlier.
   Array<uint8_t> key_block;
 
-  // ech_present, on the server, indicates whether the ClientHello contained an
-  // encrypted_client_hello extension.
-  bool ech_present : 1;
+  // hints contains the handshake hints for this connection. If
+  // |hints_requested| is true, this field is non-null and contains the pending
+  // hints to filled as the predicted handshake progresses. Otherwise, this
+  // field, if non-null, contains hints configured by the caller and will
+  // influence the handshake on match.
+  UniquePtr<SSL_HANDSHAKE_HINTS> hints;
 
-  // ech_is_inner_present, on the server, indicates whether the ClientHello
-  // contained an ech_is_inner extension.
-  bool ech_is_inner_present : 1;
+  // ech_is_inner, on the server, indicates whether the ClientHello contained an
+  // inner ECH extension.
+  bool ech_is_inner : 1;
+
+  // ech_authenticated_reject, on the client, indicates whether an ECH rejection
+  // handshake has been authenticated.
+  bool ech_authenticated_reject : 1;
 
   // scts_requested is true if the SCT extension is in the ClientHello.
   bool scts_requested : 1;
 
-  // needs_psk_binder is true if the ClientHello has a placeholder PSK binder to
-  // be filled in.
-  bool needs_psk_binder : 1;
-
   // handshake_finalized is true once the handshake has completed, at which
   // point accessors should use the established state.
   bool handshake_finalized : 1;
@@ -1805,15 +2014,17 @@
   // in progress.
   bool pending_private_key_op : 1;
 
-  // grease_seeded is true if |grease_seed| has been initialized.
-  bool grease_seeded : 1;
-
   // handback indicates that a server should pause the handshake after
   // finishing operations that require private key material, in such a way that
   // |SSL_get_error| returns |SSL_ERROR_HANDBACK|.  It is set by
   // |SSL_apply_handoff|.
   bool handback : 1;
 
+  // hints_requested indicates the caller has requested handshake hints. Only
+  // the first round-trip of the handshake will complete, after which the
+  // |hints| structure can be serialized.
+  bool hints_requested : 1;
+
   // cert_compression_negotiated is true iff |cert_compression_alg_id| is valid.
   bool cert_compression_negotiated : 1;
 
@@ -1821,6 +2032,14 @@
   // which implemented TLS 1.3 incorrectly.
   bool apply_jdk11_workaround : 1;
 
+  // can_release_private_key is true if the private key will no longer be used
+  // in this handshake.
+  bool can_release_private_key : 1;
+
+  // channel_id_negotiated is true if Channel ID should be used in this
+  // handshake.
+  bool channel_id_negotiated : 1;
+
   // client_version is the value sent or received in the ClientHello version.
   uint16_t client_version = 0;
 
@@ -1832,12 +2051,14 @@
   // record layer.
   uint16_t early_data_written = 0;
 
+  // ech_config_id is the ECH config sent by the client.
+  uint8_t ech_config_id = 0;
+
   // session_id is the session ID in the ClientHello.
   uint8_t session_id[SSL_MAX_SSL_SESSION_ID_LENGTH] = {0};
   uint8_t session_id_len = 0;
 
-  // grease_seed is the entropy for GREASE values. It is valid if
-  // |grease_seeded| is true.
+  // grease_seed is the entropy for GREASE values.
   uint8_t grease_seed[ssl_grease_last_index + 1] = {0};
 };
 
@@ -1897,14 +2118,24 @@
 bssl::UniquePtr<SSL_SESSION> tls13_create_session_with_ticket(SSL *ssl,
                                                               CBS *body);
 
+// ssl_setup_extension_permutation computes a ClientHello extension permutation
+// for |hs|, if applicable. It returns true on success and false on error.
+bool ssl_setup_extension_permutation(SSL_HANDSHAKE *hs);
+
+// ssl_setup_key_shares computes client key shares and saves them in |hs|. It
+// returns true on success and false on failure. If |override_group_id| is zero,
+// it offers the default groups, including GREASE. If it is non-zero, it offers
+// a single key share of the specified group.
+bool ssl_setup_key_shares(SSL_HANDSHAKE *hs, uint16_t override_group_id);
+
 bool ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs,
                                          Array<uint8_t> *out_secret,
                                          uint8_t *out_alert, CBS *contents);
 bool ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, bool *out_found,
-                                         Array<uint8_t> *out_secret,
-                                         uint8_t *out_alert, CBS *contents);
-bool ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out,
-                                       bool dry_run);
+                                         Span<const uint8_t> *out_peer_key,
+                                         uint8_t *out_alert,
+                                         const SSL_CLIENT_HELLO *client_hello);
+bool ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out);
 
 bool ssl_ext_pre_shared_key_parse_serverhello(SSL_HANDSHAKE *hs,
                                               uint8_t *out_alert,
@@ -1919,7 +2150,33 @@
 // returns whether it's valid.
 bool ssl_is_sct_list_valid(const CBS *contents);
 
-bool ssl_write_client_hello(SSL_HANDSHAKE *hs);
+// ssl_write_client_hello_without_extensions writes a ClientHello to |out|,
+// up to the extensions field. |type| determines the type of ClientHello to
+// write. If |omit_session_id| is true, the session ID is empty.
+bool ssl_write_client_hello_without_extensions(const SSL_HANDSHAKE *hs,
+                                               CBB *cbb,
+                                               ssl_client_hello_type_t type,
+                                               bool empty_session_id);
+
+// ssl_add_client_hello constructs a ClientHello and adds it to the outgoing
+// flight. It returns true on success and false on error.
+bool ssl_add_client_hello(SSL_HANDSHAKE *hs);
+
+struct ParsedServerHello {
+  CBS raw;
+  uint16_t legacy_version = 0;
+  CBS random;
+  CBS session_id;
+  uint16_t cipher_suite = 0;
+  uint8_t compression_method = 0;
+  CBS extensions;
+};
+
+// ssl_parse_server_hello parses |msg| as a ServerHello. On success, it writes
+// the result to |*out| and returns true. Otherwise, it returns false and sets
+// |*out_alert| to an alert to send to the peer.
+bool ssl_parse_server_hello(ParsedServerHello *out, uint8_t *out_alert,
+                            const SSLMessage &msg);
 
 enum ssl_cert_verify_context_t {
   ssl_cert_verify_server,
@@ -1935,6 +2192,9 @@
     SSL_HANDSHAKE *hs, Array<uint8_t> *out,
     enum ssl_cert_verify_context_t cert_verify_context);
 
+// ssl_is_valid_alpn_list returns whether |in| is a valid ALPN protocol list.
+bool ssl_is_valid_alpn_list(Span<const uint8_t> in);
+
 // ssl_is_alpn_protocol_allowed returns whether |protocol| is a valid server
 // selection for |hs->ssl|'s client preferences.
 bool ssl_is_alpn_protocol_allowed(const SSL_HANDSHAKE *hs,
@@ -1946,25 +2206,38 @@
 bool ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                         const SSL_CLIENT_HELLO *client_hello);
 
+// ssl_get_local_application_settings looks up the configured ALPS value for
+// |protocol|. If found, it sets |*out_settings| to the value and returns true.
+// Otherwise, it returns false.
+bool ssl_get_local_application_settings(const SSL_HANDSHAKE *hs,
+                                        Span<const uint8_t> *out_settings,
+                                        Span<const uint8_t> protocol);
+
 // ssl_negotiate_alps negotiates the ALPS extension, if applicable. It returns
 // true on successful negotiation or if nothing was negotiated. It returns false
 // and sets |*out_alert| to an alert on error.
 bool ssl_negotiate_alps(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                         const SSL_CLIENT_HELLO *client_hello);
 
-struct SSL_EXTENSION_TYPE {
+struct SSLExtension {
+  SSLExtension(uint16_t type_arg, bool allowed_arg = true)
+      : type(type_arg), allowed(allowed_arg), present(false) {
+    CBS_init(&data, nullptr, 0);
+  }
+
   uint16_t type;
-  bool *out_present;
-  CBS *out_data;
+  bool allowed;
+  bool present;
+  CBS data;
 };
 
 // ssl_parse_extensions parses a TLS extensions block out of |cbs| and advances
-// it. It writes the parsed extensions to pointers denoted by |ext_types|. On
-// success, it fills in the |out_present| and |out_data| fields and returns
-// true. Otherwise, it sets |*out_alert| to an alert to send and returns false.
-// Unknown extensions are rejected unless |ignore_unknown| is true.
+// it. It writes the parsed extensions to pointers in |extensions|. On success,
+// it fills in the |present| and |data| fields and returns true. Otherwise, it
+// sets |*out_alert| to an alert to send and returns false. Unknown extensions
+// are rejected unless |ignore_unknown| is true.
 bool ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert,
-                          Span<const SSL_EXTENSION_TYPE> ext_types,
+                          std::initializer_list<SSLExtension *> extensions,
                           bool ignore_unknown);
 
 // ssl_verify_peer_cert verifies the peer certificate for |hs|.
@@ -1982,6 +2255,10 @@
 // handshake. Note, in TLS 1.2 resumptions, this session is immutable.
 const SSL_SESSION *ssl_handshake_session(const SSL_HANDSHAKE *hs);
 
+// ssl_done_writing_client_hello is called after the last ClientHello is written
+// by |hs|. It releases some memory that is no longer needed.
+void ssl_done_writing_client_hello(SSL_HANDSHAKE *hs);
+
 
 // SSLKEYLOGFILE functions.
 
@@ -1993,8 +2270,14 @@
 
 // ClientHello functions.
 
-bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out,
-                           const SSLMessage &msg);
+// ssl_client_hello_init parses |body| as a ClientHello message, excluding the
+// message header, and writes the result to |*out|. It returns true on success
+// and false on error. This function is exported for testing.
+OPENSSL_EXPORT bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out,
+                                          Span<const uint8_t> body);
+
+bool ssl_parse_client_hello_with_trailing_data(const SSL *ssl, CBS *cbs,
+                                               SSL_CLIENT_HELLO *out);
 
 bool ssl_client_hello_get_extension(const SSL_CLIENT_HELLO *client_hello,
                                     CBS *out, uint16_t extension_type);
@@ -2009,7 +2292,8 @@
 // connection, the values for each index will be deterministic. This allows the
 // same ClientHello be sent twice for a HelloRetryRequest or the same group be
 // advertised in both supported_groups and key_shares.
-uint16_t ssl_get_grease_value(SSL_HANDSHAKE *hs, enum ssl_grease_index_t index);
+uint16_t ssl_get_grease_value(const SSL_HANDSHAKE *hs,
+                              enum ssl_grease_index_t index);
 
 
 // Signature algorithms.
@@ -2055,7 +2339,7 @@
 
 #define TLSEXT_CHANNEL_ID_SIZE 128
 
-// From RFC4492, used in encoding the curve type in ECParameters
+// From RFC 4492, used in encoding the curve type in ECParameters
 #define NAMED_CURVE_TYPE 3
 
 struct CERT {
@@ -2169,10 +2453,11 @@
   // init_message begins a new handshake message of type |type|. |cbb| is the
   // root CBB to be passed into |finish_message|. |*body| is set to a child CBB
   // the caller should write to. It returns true on success and false on error.
-  bool (*init_message)(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
+  bool (*init_message)(const SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
   // finish_message finishes a handshake message. It sets |*out_msg| to the
   // serialized message. It returns true on success and false on error.
-  bool (*finish_message)(SSL *ssl, CBB *cbb, bssl::Array<uint8_t> *out_msg);
+  bool (*finish_message)(const SSL *ssl, CBB *cbb,
+                         bssl::Array<uint8_t> *out_msg);
   // add_message adds a handshake message to the pending flight. It returns
   // true on success and false on error.
   bool (*add_message)(SSL *ssl, bssl::Array<uint8_t> msg);
@@ -2321,6 +2606,16 @@
   ssl_shutdown_error = 2,
 };
 
+enum ssl_ech_status_t {
+  // ssl_ech_none indicates ECH was not offered, or we have not gotten far
+  // enough in the handshake to determine the status.
+  ssl_ech_none,
+  // ssl_ech_accepted indicates the server accepted ECH.
+  ssl_ech_accepted,
+  // ssl_ech_rejected indicates the server was offered ECH but rejected it.
+  ssl_ech_rejected,
+};
+
 struct SSL3_STATE {
   static constexpr bool kAllowUniquePtr = true;
 
@@ -2383,9 +2678,8 @@
   // key_update_count is the number of consecutive KeyUpdates received.
   uint8_t key_update_count = 0;
 
-  // The negotiated Token Binding key parameter. Only valid if
-  // |token_binding_negotiated| is set.
-  uint8_t negotiated_token_binding_param = 0;
+  // ech_status indicates whether ECH was accepted by the server.
+  ssl_ech_status_t ech_status = ssl_ech_none;
 
   // skip_early_data instructs the record layer to skip unexpected early data
   // messages when 0RTT is rejected.
@@ -2420,9 +2714,8 @@
 
   bool send_connection_binding : 1;
 
-  // In a client, this means that the server supported Channel ID and that a
-  // Channel ID was sent. In a server it means that we echoed support for
-  // Channel IDs and that |channel_id| will be valid after the handshake.
+  // channel_id_valid is true if, on the server, the client has negotiated a
+  // Channel ID and the |channel_id| field is filled in.
   bool channel_id_valid : 1;
 
   // key_update_pending is true if we have a KeyUpdate acknowledgment
@@ -2435,9 +2728,6 @@
   // early_data_accepted is true if early data was accepted by the server.
   bool early_data_accepted : 1;
 
-  // token_binding_negotiated is set if Token Binding was negotiated.
-  bool token_binding_negotiated : 1;
-
   // alert_dispatch is true there is an alert in |send_alert| to be sent.
   bool alert_dispatch : 1;
 
@@ -2720,7 +3010,8 @@
 
   Array<uint16_t> supported_group_list;  // our list
 
-  // The client's Channel ID private key.
+  // channel_id_private is the client's Channel ID private key, or null if
+  // Channel ID should not be offered on this connection.
   UniquePtr<EVP_PKEY> channel_id_private;
 
   // For a client, this contains the list of supported protocols in wire
@@ -2731,9 +3022,6 @@
   // along with their corresponding ALPS values.
   GrowableArray<ALPSConfig> alps_configs;
 
-  // Contains a list of supported Token Binding key parameters.
-  Array<uint8_t> token_binding_params;
-
   // Contains the QUIC transport params that this endpoint will send.
   Array<uint8_t> quic_transport_params;
 
@@ -2748,6 +3036,10 @@
   // DTLS-SRTP.
   UniquePtr<STACK_OF(SRTP_PROTECTION_PROFILE)> srtp_profiles;
 
+  // client_ech_config_list, if not empty, is a serialized ECHConfigList
+  // structure for the client to use when negotiating ECH.
+  Array<uint8_t> client_ech_config_list;
+
   // verify_mode is a bitmask of |SSL_VERIFY_*| values.
   uint8_t verify_mode = SSL_VERIFY_NONE;
 
@@ -2762,9 +3054,8 @@
   // whether OCSP stapling will be requested.
   bool ocsp_stapling_enabled : 1;
 
-  // channel_id_enabled is copied from the |SSL_CTX|. For a server, means that
-  // we'll accept Channel IDs from clients. For a client, means that we'll
-  // advertise support.
+  // channel_id_enabled is copied from the |SSL_CTX|. For a server, it means
+  // that we'll accept Channel IDs from clients. It is ignored on the client.
   bool channel_id_enabled : 1;
 
   // If enforce_rsa_key_usage is true, the handshake will fail if the
@@ -2794,6 +3085,9 @@
   // QUIC drafts up to and including 32 used a different TLS extension
   // codepoint to convey QUIC's transport parameters.
   bool quic_use_legacy_codepoint : 1;
+
+  // permute_extensions is whether to permute extensions when sending messages.
+  bool permute_extensions : 1;
 };
 
 // From RFC 8446, used in determining PSK modes.
@@ -2814,7 +3108,7 @@
 bool ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
                                        const EVP_PKEY *privkey);
 bool ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey);
-int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server);
+bool ssl_get_new_session(SSL_HANDSHAKE *hs);
 int ssl_encrypt_ticket(SSL_HANDSHAKE *hs, CBB *out, const SSL_SESSION *session);
 int ssl_ctx_rotate_ticket_encryption_key(SSL_CTX *ctx);
 
@@ -2895,7 +3189,7 @@
 void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session,
                                uint32_t timeout);
 
-void ssl_update_cache(SSL_HANDSHAKE *hs, int mode);
+void ssl_update_cache(SSL *ssl);
 
 void ssl_send_alert(SSL *ssl, int level, int desc);
 int ssl_send_alert_impl(SSL *ssl, int level, int desc);
@@ -2917,14 +3211,14 @@
 bool tls_new(SSL *ssl);
 void tls_free(SSL *ssl);
 
-bool tls_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
-bool tls_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg);
+bool tls_init_message(const SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
+bool tls_finish_message(const SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg);
 bool tls_add_message(SSL *ssl, Array<uint8_t> msg);
 bool tls_add_change_cipher_spec(SSL *ssl);
 int tls_flush_flight(SSL *ssl);
 
-bool dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
-bool dtls1_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg);
+bool dtls1_init_message(const SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
+bool dtls1_finish_message(const SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg);
 bool dtls1_add_message(SSL *ssl, Array<uint8_t> msg);
 bool dtls1_add_change_cipher_spec(SSL *ssl);
 int dtls1_flush_flight(SSL *ssl);
@@ -3009,16 +3303,28 @@
 // false.
 bool tls1_set_curves_list(Array<uint16_t> *out_group_ids, const char *curves);
 
-// ssl_add_clienthello_tlsext writes ClientHello extensions to |out|. It returns
-// true on success and false on failure. The |header_len| argument is the length
-// of the ClientHello written so far and is used to compute the padding length.
-// (It does not include the record header.)
-bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, size_t header_len);
+// ssl_add_clienthello_tlsext writes ClientHello extensions to |out| for |type|.
+// It returns true on success and false on failure. The |header_len| argument is
+// the length of the ClientHello written so far and is used to compute the
+// padding length. (It does not include the record header or handshake headers.)
+//
+// If |type| is |ssl_client_hello_inner|, this function also writes the
+// compressed extensions to |out_encoded|. Otherwise, |out_encoded| should be
+// nullptr.
+//
+// On success, the function sets |*out_needs_psk_binder| to whether the last
+// ClientHello extension was the pre_shared_key extension and needs a PSK binder
+// filled in. The caller should then update |out| and, if applicable,
+// |out_encoded| with the binder after completing the whole message.
+bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, CBB *out_encoded,
+                                bool *out_needs_psk_binder,
+                                ssl_client_hello_type_t type,
+                                size_t header_len);
 
 bool ssl_add_serverhello_tlsext(SSL_HANDSHAKE *hs, CBB *out);
 bool ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs,
                                   const SSL_CLIENT_HELLO *client_hello);
-bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs);
+bool ssl_parse_serverhello_tlsext(SSL_HANDSHAKE *hs, const CBS *extensions);
 
 #define tlsext_tick_md EVP_sha256
 
@@ -3056,12 +3362,6 @@
 // data.
 bool tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs);
 
-// ssl_do_channel_id_callback checks runs |hs->ssl->ctx->channel_id_cb| if
-// necessary. It returns true on success and false on fatal error. Note that, on
-// success, |hs->ssl->channel_id_private| may be unset, in which case the
-// operation should be retried later.
-bool ssl_do_channel_id_callback(SSL_HANDSHAKE *hs);
-
 // ssl_can_write returns whether |ssl| is allowed to write.
 bool ssl_can_write(const SSL *ssl);
 
@@ -3185,9 +3485,6 @@
   int (*client_cert_cb)(SSL *ssl, X509 **out_x509,
                         EVP_PKEY **out_pkey) = nullptr;
 
-  // get channel id callback
-  void (*channel_id_cb)(SSL *ssl, EVP_PKEY **out_pkey) = nullptr;
-
   CRYPTO_EX_DATA ex_data;
 
   // Default values used when no per-SSL value is defined follow
@@ -3315,9 +3612,15 @@
   // Supported group values inherited by SSL structure
   bssl::Array<uint16_t> supported_group_list;
 
-  // The client's Channel ID private key.
+  // channel_id_private is the client's Channel ID private key, or null if
+  // Channel ID should not be offered on this connection.
   bssl::UniquePtr<EVP_PKEY> channel_id_private;
 
+  // ech_keys contains the server's list of ECHConfig values and associated
+  // private keys. This list may be swapped out at any time, so all access must
+  // be synchronized through |lock|.
+  bssl::UniquePtr<SSL_ECH_KEYS> ech_keys;
+
   // keylog_callback, if not NULL, is the key logging callback. See
   // |SSL_CTX_set_keylog_callback|.
   void (*keylog_callback)(const SSL *ssl, const char *line) = nullptr;
@@ -3365,9 +3668,12 @@
   // advertise support.
   bool channel_id_enabled : 1;
 
-  // grease_enabled is whether draft-davidben-tls-grease-01 is enabled.
+  // grease_enabled is whether GREASE (RFC 8701) is enabled.
   bool grease_enabled : 1;
 
+  // permute_extensions is whether to permute extensions when sending messages.
+  bool permute_extensions : 1;
+
   // allow_unknown_alpn_protos is whether the client allows unsolicited ALPN
   // protocols from the peer.
   bool allow_unknown_alpn_protos : 1;
@@ -3631,5 +3937,17 @@
   friend void SSL_SESSION_free(SSL_SESSION *);
 };
 
+struct ssl_ech_keys_st {
+  ssl_ech_keys_st() = default;
+  ssl_ech_keys_st(const ssl_ech_keys_st &) = delete;
+  ssl_ech_keys_st &operator=(const ssl_ech_keys_st &) = delete;
+
+  bssl::GrowableArray<bssl::UniquePtr<bssl::ECHServerConfig>> configs;
+  CRYPTO_refcount_t references = 1;
+
+ private:
+  ~ssl_ech_keys_st() = default;
+  friend void SSL_ECH_KEYS_free(SSL_ECH_KEYS *);
+};
 
 #endif  // OPENSSL_HEADER_SSL_INTERNAL_H
diff --git a/deps/boringssl/src/ssl/s3_both.cc b/deps/boringssl/src/ssl/s3_both.cc
index 4415bd7..cddeb3f 100644
--- a/deps/boringssl/src/ssl/s3_both.cc
+++ b/deps/boringssl/src/ssl/s3_both.cc
@@ -168,7 +168,7 @@
   return true;
 }
 
-bool tls_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
+bool tls_init_message(const SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
   // Pick a modest size hint to save most of the |realloc| calls.
   if (!CBB_init(cbb, 64) ||
       !CBB_add_u8(cbb, type) ||
@@ -181,7 +181,7 @@
   return true;
 }
 
-bool tls_finish_message(SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg) {
+bool tls_finish_message(const SSL *ssl, CBB *cbb, Array<uint8_t> *out_msg) {
   return CBBFinishArray(cbb, out_msg);
 }
 
@@ -251,7 +251,8 @@
       MakeConstSpan(reinterpret_cast<const uint8_t *>(pending_hs_data->data),
                     pending_hs_data->length);
   if (ssl->quic_method) {
-    if (!ssl->quic_method->add_handshake_data(ssl, ssl->s3->write_level,
+    if ((ssl->s3->hs == nullptr || !ssl->s3->hs->hints_requested) &&
+        !ssl->quic_method->add_handshake_data(ssl, ssl->s3->write_level,
                                               data.data(), data.size())) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_QUIC_INTERNAL_ERROR);
       return false;
@@ -322,6 +323,11 @@
     }
   }
 
+  if (ssl->wbio == nullptr) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET);
+    return -1;
+  }
+
   // Write the pending flight.
   while (ssl->s3->pending_flight_offset < ssl->s3->pending_flight->length) {
     int ret = BIO_write(
diff --git a/deps/boringssl/src/ssl/s3_lib.cc b/deps/boringssl/src/ssl/s3_lib.cc
index 3e12492..fa73d34 100644
--- a/deps/boringssl/src/ssl/s3_lib.cc
+++ b/deps/boringssl/src/ssl/s3_lib.cc
@@ -177,7 +177,6 @@
       key_update_pending(false),
       wpend_pending(false),
       early_data_accepted(false),
-      token_binding_negotiated(false),
       alert_dispatch(false),
       renegotiate_pending(false),
       used_hello_retry_request(false) {}
diff --git a/deps/boringssl/src/ssl/s3_pkt.cc b/deps/boringssl/src/ssl/s3_pkt.cc
index 457696d..450f7dc 100644
--- a/deps/boringssl/src/ssl/s3_pkt.cc
+++ b/deps/boringssl/src/ssl/s3_pkt.cc
@@ -112,6 +112,8 @@
 #include <limits.h>
 #include <string.h>
 
+#include <algorithm>
+
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
@@ -138,10 +140,9 @@
     return -1;
   }
 
-  unsigned tot, n, nw;
-
+  // TODO(davidben): Switch this logic to |size_t| and |bssl::Span|.
   assert(ssl->s3->wnum <= INT_MAX);
-  tot = ssl->s3->wnum;
+  unsigned tot = ssl->s3->wnum;
   ssl->s3->wnum = 0;
 
   // Ensure that if we end up with a smaller value of data to write out than
@@ -159,29 +160,23 @@
   const int is_early_data_write =
       !ssl->server && SSL_in_early_data(ssl) && ssl->s3->hs->can_early_write;
 
-  n = len - tot;
+  unsigned n = len - tot;
   for (;;) {
-    // max contains the maximum number of bytes that we can put into a record.
-    unsigned max = ssl->max_send_fragment;
-    if (is_early_data_write &&
-        max > ssl->session->ticket_max_early_data -
-                  ssl->s3->hs->early_data_written) {
-      max =
-          ssl->session->ticket_max_early_data - ssl->s3->hs->early_data_written;
-      if (max == 0) {
+    size_t max_send_fragment = ssl->max_send_fragment;
+    if (is_early_data_write) {
+      SSL_HANDSHAKE *hs = ssl->s3->hs.get();
+      if (hs->early_data_written >= hs->early_session->ticket_max_early_data) {
         ssl->s3->wnum = tot;
-        ssl->s3->hs->can_early_write = false;
+        hs->can_early_write = false;
         *out_needs_handshake = true;
         return -1;
       }
+      max_send_fragment = std::min(
+          max_send_fragment, size_t{hs->early_session->ticket_max_early_data -
+                                    hs->early_data_written});
     }
 
-    if (n > max) {
-      nw = max;
-    } else {
-      nw = n;
-    }
-
+    const size_t nw = std::min(max_send_fragment, size_t{n});
     int ret = do_tls_write(ssl, SSL3_RT_APPLICATION_DATA, &in[tot], nw);
     if (ret <= 0) {
       ssl->s3->wnum = tot;
diff --git a/deps/boringssl/src/ssl/ssl_cert.cc b/deps/boringssl/src/ssl/ssl_cert.cc
index c64303a..68e010a 100644
--- a/deps/boringssl/src/ssl/ssl_cert.cc
+++ b/deps/boringssl/src/ssl/ssl_cert.cc
@@ -548,13 +548,11 @@
       // subjectPublicKeyInfo
       !CBS_get_asn1(&tbs_cert, NULL, CBS_ASN1_SEQUENCE) ||
       // issuerUniqueID
-      !CBS_get_optional_asn1(
-          &tbs_cert, NULL, NULL,
-          CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1) ||
+      !CBS_get_optional_asn1(&tbs_cert, NULL, NULL,
+                             CBS_ASN1_CONTEXT_SPECIFIC | 1) ||
       // subjectUniqueID
-      !CBS_get_optional_asn1(
-          &tbs_cert, NULL, NULL,
-          CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2) ||
+      !CBS_get_optional_asn1(&tbs_cert, NULL, NULL,
+                             CBS_ASN1_CONTEXT_SPECIFIC | 2) ||
       !CBS_get_optional_asn1(
           &tbs_cert, &outer_extensions, &has_extensions,
           CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3)) {
diff --git a/deps/boringssl/src/ssl/ssl_cipher.cc b/deps/boringssl/src/ssl/ssl_cipher.cc
index 4f5049c..60b3e2c 100644
--- a/deps/boringssl/src/ssl/ssl_cipher.cc
+++ b/deps/boringssl/src/ssl/ssl_cipher.cc
@@ -234,7 +234,7 @@
      SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
-    // GCM ciphersuites from RFC5288
+    // GCM ciphersuites from RFC 5288
 
     // Cipher 9C
     {
@@ -346,7 +346,7 @@
      SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
-    // GCM based TLS v1.2 ciphersuites from RFC5289
+    // GCM based TLS v1.2 ciphersuites from RFC 5289
 
     // Cipher C02B
     {
diff --git a/deps/boringssl/src/ssl/ssl_key_share.cc b/deps/boringssl/src/ssl/ssl_key_share.cc
index 6cac3cf..c847a0a 100644
--- a/deps/boringssl/src/ssl/ssl_key_share.cc
+++ b/deps/boringssl/src/ssl/ssl_key_share.cc
@@ -124,29 +124,17 @@
     return true;
   }
 
-  bool Serialize(CBB *out) override {
+  bool SerializePrivateKey(CBB *out) override {
     assert(private_key_);
-    CBB cbb;
     UniquePtr<EC_GROUP> group(EC_GROUP_new_by_curve_name(nid_));
     // Padding is added to avoid leaking the length.
     size_t len = BN_num_bytes(EC_GROUP_get0_order(group.get()));
-    if (!CBB_add_asn1_uint64(out, group_id_) ||
-        !CBB_add_asn1(out, &cbb, CBS_ASN1_OCTETSTRING) ||
-        !BN_bn2cbb_padded(&cbb, len, private_key_.get()) ||
-        !CBB_flush(out)) {
-      return false;
-    }
-    return true;
+    return BN_bn2cbb_padded(out, len, private_key_.get());
   }
 
-  bool Deserialize(CBS *in) override {
+  bool DeserializePrivateKey(CBS *in) override {
     assert(!private_key_);
-    CBS private_key;
-    if (!CBS_get_asn1(in, &private_key, CBS_ASN1_OCTETSTRING)) {
-      return false;
-    }
-    private_key_.reset(BN_bin2bn(CBS_data(&private_key),
-                                 CBS_len(&private_key), nullptr));
+    private_key_.reset(BN_bin2bn(CBS_data(in), CBS_len(in), nullptr));
     return private_key_ != nullptr;
   }
 
@@ -189,16 +177,13 @@
     return true;
   }
 
-  bool Serialize(CBB *out) override {
-    return (CBB_add_asn1_uint64(out, GroupID()) &&
-            CBB_add_asn1_octet_string(out, private_key_, sizeof(private_key_)));
+  bool SerializePrivateKey(CBB *out) override {
+    return CBB_add_bytes(out, private_key_, sizeof(private_key_));
   }
 
-  bool Deserialize(CBS *in) override {
-    CBS key;
-    if (!CBS_get_asn1(in, &key, CBS_ASN1_OCTETSTRING) ||
-        CBS_len(&key) != sizeof(private_key_) ||
-        !CBS_copy_bytes(&key, private_key_, sizeof(private_key_))) {
+  bool DeserializePrivateKey(CBS *in) override {
+    if (CBS_len(in) != sizeof(private_key_) ||
+        !CBS_copy_bytes(in, private_key_, sizeof(private_key_))) {
       return false;
     }
     return true;
@@ -221,7 +206,10 @@
     uint8_t hrss_entropy[HRSS_GENERATE_KEY_BYTES];
     HRSS_public_key hrss_public_key;
     RAND_bytes(hrss_entropy, sizeof(hrss_entropy));
-    HRSS_generate_key(&hrss_public_key, &hrss_private_key_, hrss_entropy);
+    if (!HRSS_generate_key(&hrss_public_key, &hrss_private_key_,
+                           hrss_entropy)) {
+      return false;
+    }
 
     uint8_t hrss_public_key_bytes[HRSS_PUBLIC_KEY_BYTES];
     HRSS_marshal_public_key(hrss_public_key_bytes, &hrss_public_key);
@@ -258,9 +246,10 @@
     uint8_t ciphertext[HRSS_CIPHERTEXT_BYTES];
     uint8_t entropy[HRSS_ENCAP_BYTES];
     RAND_bytes(entropy, sizeof(entropy));
-    HRSS_encap(ciphertext, secret.data() + 32, &peer_public_key, entropy);
 
-    if (!CBB_add_bytes(out_public_key, x25519_public_key,
+    if (!HRSS_encap(ciphertext, secret.data() + 32, &peer_public_key,
+                    entropy) ||
+        !CBB_add_bytes(out_public_key, x25519_public_key,
                        sizeof(x25519_public_key)) ||
         !CBB_add_bytes(out_public_key, ciphertext, sizeof(ciphertext))) {
       return false;
@@ -287,8 +276,10 @@
       return false;
     }
 
-    HRSS_decap(secret.data() + 32, &hrss_private_key_, peer_key.data() + 32,
-               peer_key.size() - 32);
+    if (!HRSS_decap(secret.data() + 32, &hrss_private_key_,
+                    peer_key.data() + 32, peer_key.size() - 32)) {
+      return false;
+    }
 
     *out_secret = std::move(secret);
     return true;
@@ -339,16 +330,28 @@
 
 UniquePtr<SSLKeyShare> SSLKeyShare::Create(CBS *in) {
   uint64_t group;
-  if (!CBS_get_asn1_uint64(in, &group) || group > 0xffff) {
+  CBS private_key;
+  if (!CBS_get_asn1_uint64(in, &group) || group > 0xffff ||
+      !CBS_get_asn1(in, &private_key, CBS_ASN1_OCTETSTRING)) {
     return nullptr;
   }
   UniquePtr<SSLKeyShare> key_share = Create(static_cast<uint16_t>(group));
-  if (!key_share || !key_share->Deserialize(in)) {
+  if (!key_share || !key_share->DeserializePrivateKey(&private_key)) {
     return nullptr;
   }
   return key_share;
 }
 
+bool SSLKeyShare::Serialize(CBB *out) {
+  CBB private_key;
+  if (!CBB_add_asn1_uint64(out, GroupID()) ||
+      !CBB_add_asn1(out, &private_key, CBS_ASN1_OCTETSTRING) ||
+      !SerializePrivateKey(&private_key) ||  //
+      !CBB_flush(out)) {
+    return false;
+  }
+  return true;
+}
 
 bool SSLKeyShare::Accept(CBB *out_public_key, Array<uint8_t> *out_secret,
                          uint8_t *out_alert, Span<const uint8_t> peer_key) {
diff --git a/deps/boringssl/src/ssl/ssl_lib.cc b/deps/boringssl/src/ssl/ssl_lib.cc
index 7c7bbbf..03864e1 100644
--- a/deps/boringssl/src/ssl/ssl_lib.cc
+++ b/deps/boringssl/src/ssl/ssl_lib.cc
@@ -272,57 +272,6 @@
   return ret;
 }
 
-void ssl_update_cache(SSL_HANDSHAKE *hs, int mode) {
-  SSL *const ssl = hs->ssl;
-  SSL_CTX *ctx = ssl->session_ctx.get();
-  // Never cache sessions with empty session IDs.
-  if (ssl->s3->established_session->session_id_length == 0 ||
-      ssl->s3->established_session->not_resumable ||
-      (ctx->session_cache_mode & mode) != mode) {
-    return;
-  }
-
-  // Clients never use the internal session cache.
-  int use_internal_cache = ssl->server && !(ctx->session_cache_mode &
-                                            SSL_SESS_CACHE_NO_INTERNAL_STORE);
-
-  // A client may see new sessions on abbreviated handshakes if the server
-  // decides to renew the ticket. Once the handshake is completed, it should be
-  // inserted into the cache.
-  if (ssl->s3->established_session.get() != ssl->session.get() ||
-      (!ssl->server && hs->ticket_expected)) {
-    if (use_internal_cache) {
-      SSL_CTX_add_session(ctx, ssl->s3->established_session.get());
-    }
-    if (ctx->new_session_cb != NULL) {
-      UniquePtr<SSL_SESSION> ref = UpRef(ssl->s3->established_session);
-      if (ctx->new_session_cb(ssl, ref.get())) {
-        // |new_session_cb|'s return value signals whether it took ownership.
-        ref.release();
-      }
-    }
-  }
-
-  if (use_internal_cache &&
-      !(ctx->session_cache_mode & SSL_SESS_CACHE_NO_AUTO_CLEAR)) {
-    // Automatically flush the internal session cache every 255 connections.
-    int flush_cache = 0;
-    CRYPTO_MUTEX_lock_write(&ctx->lock);
-    ctx->handshakes_since_cache_flush++;
-    if (ctx->handshakes_since_cache_flush >= 255) {
-      flush_cache = 1;
-      ctx->handshakes_since_cache_flush = 0;
-    }
-    CRYPTO_MUTEX_unlock_write(&ctx->lock);
-
-    if (flush_cache) {
-      struct OPENSSL_timeval now;
-      ssl_get_current_time(ssl, &now);
-      SSL_CTX_flush_sessions(ctx, now.tv_sec);
-    }
-  }
-}
-
 static bool cbb_add_hex(CBB *cbb, Span<const uint8_t> in) {
   static const char hextable[] = "0123456789abcdef";
   uint8_t *out;
@@ -463,7 +412,8 @@
     return false;
   }
 
-  if (ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
+  if (ssl->s3->have_version &&
+      ssl_protocol_version(ssl) >= TLS1_3_VERSION) {
     return false;
   }
 
@@ -563,6 +513,7 @@
       signed_cert_timestamps_enabled(false),
       channel_id_enabled(false),
       grease_enabled(false),
+      permute_extensions(false),
       allow_unknown_alpn_protos(false),
       false_start_allowed_without_alpn(false),
       handoff(false),
@@ -685,6 +636,7 @@
   ssl->config->custom_verify_callback = ctx->custom_verify_callback;
   ssl->config->retain_only_sha256_of_client_certs =
       ctx->retain_only_sha256_of_client_certs;
+  ssl->config->permute_extensions = ctx->permute_extensions;
 
   if (!ssl->config->supported_group_list.CopyFrom(ctx->supported_group_list) ||
       !ssl->config->alpn_client_proto_list.CopyFrom(
@@ -731,7 +683,8 @@
       handoff(false),
       shed_handshake_config(false),
       jdk11_workaround(false),
-      quic_use_legacy_codepoint(true) {
+      quic_use_legacy_codepoint(false),
+      permute_extensions(false) {
   assert(ssl);
 }
 
@@ -1070,7 +1023,7 @@
 int SSL_peek(SSL *ssl, void *buf, int num) {
   if (ssl->quic_method != nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return 0;
+    return -1;
   }
 
   int ret = ssl_read_impl(ssl);
@@ -1091,7 +1044,7 @@
 
   if (ssl->quic_method != nullptr) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return 0;
+    return -1;
   }
 
   if (ssl->do_handshake == NULL) {
@@ -1099,11 +1052,6 @@
     return -1;
   }
 
-  if (ssl->s3->write_shutdown != ssl_shutdown_none) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_PROTOCOL_IS_SHUTDOWN);
-    return -1;
-  }
-
   int ret = 0;
   bool needs_handshake = false;
   do {
@@ -1317,8 +1265,6 @@
       return "alpn_mismatch";
     case ssl_early_data_channel_id:
       return "channel_id";
-    case ssl_early_data_token_binding:
-      return "token_binding";
     case ssl_early_data_ticket_age_skew:
       return "ticket_age_skew";
     case ssl_early_data_quic_parameter_mismatch:
@@ -1372,12 +1318,12 @@
     case SSL_ERROR_HANDOFF:
     case SSL_ERROR_HANDBACK:
     case SSL_ERROR_WANT_X509_LOOKUP:
-    case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP:
     case SSL_ERROR_WANT_PRIVATE_KEY_OPERATION:
     case SSL_ERROR_PENDING_TICKET:
     case SSL_ERROR_EARLY_DATA_REJECTED:
     case SSL_ERROR_WANT_CERTIFICATE_VERIFY:
     case SSL_ERROR_WANT_RENEGOTIATE:
+    case SSL_ERROR_HANDSHAKE_HINTS_READY:
       return ssl->s3->rwstate;
 
     case SSL_ERROR_WANT_READ: {
@@ -1445,8 +1391,6 @@
       return "WANT_CONNECT";
     case SSL_ERROR_WANT_ACCEPT:
       return "WANT_ACCEPT";
-    case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP:
-      return "WANT_CHANNEL_ID_LOOKUP";
     case SSL_ERROR_PENDING_SESSION:
       return "PENDING_SESSION";
     case SSL_ERROR_PENDING_CERTIFICATE:
@@ -1463,18 +1407,15 @@
       return "HANDOFF";
     case SSL_ERROR_HANDBACK:
       return "HANDBACK";
+    case SSL_ERROR_WANT_RENEGOTIATE:
+      return "WANT_RENEGOTIATE";
+    case SSL_ERROR_HANDSHAKE_HINTS_READY:
+      return "HANDSHAKE_HINTS_READY";
     default:
       return nullptr;
   }
 }
 
-void SSL_set_enable_ech_grease(SSL *ssl, int enable) {
-  if (!ssl->config) {
-    return;
-  }
-  ssl->config->ech_grease_enabled = !!enable;
-}
-
 uint32_t SSL_CTX_set_options(SSL_CTX *ctx, uint32_t options) {
   ctx->options |= options;
   return ctx->options;
@@ -1785,6 +1726,9 @@
     return 0;
   }
 
+  // We should not have told the caller to release the private key.
+  assert(!SSL_can_release_private_key(ssl));
+
   // Renegotiation is only supported at quiescent points in the application
   // protocol, namely in HTTPS, just before reading the HTTP response.
   // Require the record-layer be idle and avoid complexities of sending a
@@ -2243,21 +2187,26 @@
 
 int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const uint8_t *protos,
                             unsigned protos_len) {
-  // Note this function's calling convention is backwards.
-  return ctx->alpn_client_proto_list.CopyFrom(MakeConstSpan(protos, protos_len))
-             ? 0
-             : 1;
+  // Note this function's return value is backwards.
+  auto span = MakeConstSpan(protos, protos_len);
+  if (!span.empty() && !ssl_is_valid_alpn_list(span)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL_LIST);
+    return 1;
+  }
+  return ctx->alpn_client_proto_list.CopyFrom(span) ? 0 : 1;
 }
 
 int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, unsigned protos_len) {
-  // Note this function's calling convention is backwards.
+  // Note this function's return value is backwards.
   if (!ssl->config) {
     return 1;
   }
-  return ssl->config->alpn_client_proto_list.CopyFrom(
-             MakeConstSpan(protos, protos_len))
-             ? 0
-             : 1;
+  auto span = MakeConstSpan(protos, protos_len);
+  if (!span.empty() && !ssl_is_valid_alpn_list(span)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_ALPN_PROTOCOL_LIST);
+    return 1;
+  }
+  return ssl->config->alpn_client_proto_list.CopyFrom(span) ? 0 : 1;
 }
 
 void SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
@@ -2367,8 +2316,6 @@
   }
 
   ctx->channel_id_private = UpRef(private_key);
-  ctx->channel_id_enabled = true;
-
   return 1;
 }
 
@@ -2382,8 +2329,6 @@
   }
 
   ssl->config->channel_id_private = UpRef(private_key);
-  ssl->config->channel_id_enabled = true;
-
   return 1;
 }
 
@@ -2395,25 +2340,6 @@
   return 64;
 }
 
-int SSL_set_token_binding_params(SSL *ssl, const uint8_t *params, size_t len) {
-  if (!ssl->config) {
-    return 0;
-  }
-  if (len > 256) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
-    return 0;
-  }
-  return ssl->config->token_binding_params.CopyFrom(MakeConstSpan(params, len));
-}
-
-int SSL_is_token_binding_negotiated(const SSL *ssl) {
-  return ssl->s3->token_binding_negotiated;
-}
-
-uint8_t SSL_get_negotiated_token_binding_param(const SSL *ssl) {
-  return ssl->s3->negotiated_token_binding_param;
-}
-
 size_t SSL_get0_certificate_types(const SSL *ssl, const uint8_t **out_types) {
   Span<const uint8_t> types;
   if (!ssl->server && ssl->s3->hs != nullptr) {
@@ -2775,6 +2701,17 @@
   ctx->current_time_cb = cb;
 }
 
+int SSL_can_release_private_key(const SSL *ssl) {
+  if (ssl_can_renegotiate(ssl)) {
+    // If the connection can renegotiate (client only), the private key may be
+    // used in a future handshake.
+    return 0;
+  }
+
+  // Otherwise, this is determined by the current handshake.
+  return !ssl->s3->hs || ssl->s3->hs->can_release_private_key;
+}
+
 int SSL_is_init_finished(const SSL *ssl) {
   return !SSL_in_init(ssl);
 }
@@ -2927,6 +2864,17 @@
   ctx->grease_enabled = !!enabled;
 }
 
+void SSL_CTX_set_permute_extensions(SSL_CTX *ctx, int enabled) {
+  ctx->permute_extensions = !!enabled;
+}
+
+void SSL_set_permute_extensions(SSL *ssl, int enabled) {
+  if (!ssl->config) {
+    return;
+  }
+  ssl->config->permute_extensions = !!enabled;
+}
+
 int32_t SSL_get_ticket_age_skew(const SSL *ssl) {
   return ssl->s3->ticket_age_skew;
 }
@@ -2935,16 +2883,10 @@
   ctx->false_start_allowed_without_alpn = !!allowed;
 }
 
-int SSL_is_tls13_downgrade(const SSL *ssl) { return 0; }
-
 int SSL_used_hello_retry_request(const SSL *ssl) {
   return ssl->s3->used_hello_retry_request;
 }
 
-void SSL_CTX_set_ignore_tls13_downgrade(SSL_CTX *ctx, int ignore) {}
-
-void SSL_set_ignore_tls13_downgrade(SSL *ssl, int ignore) {}
-
 void SSL_set_shed_handshake_config(SSL *ssl, int enable) {
   if (!ssl->config) {
     return;
diff --git a/deps/boringssl/src/ssl/ssl_privkey.cc b/deps/boringssl/src/ssl/ssl_privkey.cc
index e800136..8462ebf 100644
--- a/deps/boringssl/src/ssl/ssl_privkey.cc
+++ b/deps/boringssl/src/ssl/ssl_privkey.cc
@@ -203,6 +203,7 @@
   SSL *const ssl = hs->ssl;
   const SSL_PRIVATE_KEY_METHOD *key_method = hs->config->cert->key_method;
   EVP_PKEY *privatekey = hs->config->cert->privatekey.get();
+  assert(!hs->can_release_private_key);
   if (ssl_signing_with_dc(hs)) {
     key_method = hs->config->cert->dc_key_method;
     privatekey = hs->config->cert->dc_privatekey.get();
@@ -254,6 +255,7 @@
                                                       size_t max_out,
                                                       Span<const uint8_t> in) {
   SSL *const ssl = hs->ssl;
+  assert(!hs->can_release_private_key);
   if (hs->config->cert->key_method != NULL) {
     enum ssl_private_key_result_t ret;
     if (hs->pending_private_key_op) {
diff --git a/deps/boringssl/src/ssl/ssl_session.cc b/deps/boringssl/src/ssl/ssl_session.cc
index 91b2fff..76e6dc4 100644
--- a/deps/boringssl/src/ssl/ssl_session.cc
+++ b/deps/boringssl/src/ssl/ssl_session.cc
@@ -163,7 +163,6 @@
 
 static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *session);
 static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *session);
-static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock);
 
 UniquePtr<SSL_SESSION> ssl_session_new(const SSL_X509_METHOD *x509_method) {
   return MakeUnique<SSL_SESSION>(x509_method);
@@ -350,19 +349,19 @@
                                   session->cipher);
 }
 
-int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) {
+bool ssl_get_new_session(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   if (ssl->mode & SSL_MODE_NO_SESSION_CREATION) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SESSION_MAY_NOT_BE_CREATED);
-    return 0;
+    return false;
   }
 
   UniquePtr<SSL_SESSION> session = ssl_session_new(ssl->ctx->x509_method);
   if (session == NULL) {
-    return 0;
+    return false;
   }
 
-  session->is_server = is_server;
+  session->is_server = ssl->server;
   session->ssl_version = ssl->version;
   session->is_quic = ssl->quic_method != nullptr;
 
@@ -384,24 +383,9 @@
     session->auth_timeout = ssl->session_ctx->session_timeout;
   }
 
-  if (is_server) {
-    if (hs->ticket_expected || version >= TLS1_3_VERSION) {
-      // Don't set session IDs for sessions resumed with tickets. This will keep
-      // them out of the session cache.
-      session->session_id_length = 0;
-    } else {
-      session->session_id_length = SSL3_SSL_SESSION_ID_LENGTH;
-      if (!RAND_bytes(session->session_id, session->session_id_length)) {
-        return 0;
-      }
-    }
-  } else {
-    session->session_id_length = 0;
-  }
-
   if (hs->config->cert->sid_ctx_length > sizeof(session->sid_ctx)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
   OPENSSL_memcpy(session->sid_ctx, hs->config->cert->sid_ctx,
                  hs->config->cert->sid_ctx_length);
@@ -413,7 +397,7 @@
 
   hs->new_session = std::move(session);
   ssl_set_session(ssl, NULL);
-  return 1;
+  return true;
 }
 
 int ssl_ctx_rotate_ticket_encryption_key(SSL_CTX *ctx) {
@@ -769,34 +753,36 @@
   return ssl_hs_ok;
 }
 
-static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock) {
-  int ret = 0;
-
-  if (session != NULL && session->session_id_length != 0) {
-    if (lock) {
-      CRYPTO_MUTEX_lock_write(&ctx->lock);
-    }
-    SSL_SESSION *found_session = lh_SSL_SESSION_retrieve(ctx->sessions,
-                                                         session);
-    if (found_session == session) {
-      ret = 1;
-      found_session = lh_SSL_SESSION_delete(ctx->sessions, session);
-      SSL_SESSION_list_remove(ctx, session);
-    }
-
-    if (lock) {
-      CRYPTO_MUTEX_unlock_write(&ctx->lock);
-    }
-
-    if (ret) {
-      if (ctx->remove_session_cb != NULL) {
-        ctx->remove_session_cb(ctx, found_session);
-      }
-      SSL_SESSION_free(found_session);
-    }
+static bool remove_session(SSL_CTX *ctx, SSL_SESSION *session, bool lock) {
+  if (session == nullptr || session->session_id_length == 0) {
+    return false;
   }
 
-  return ret;
+  if (lock) {
+    CRYPTO_MUTEX_lock_write(&ctx->lock);
+  }
+
+  SSL_SESSION *found_session = lh_SSL_SESSION_retrieve(ctx->sessions, session);
+  bool found = found_session == session;
+  if (found) {
+    found_session = lh_SSL_SESSION_delete(ctx->sessions, session);
+    SSL_SESSION_list_remove(ctx, session);
+  }
+
+  if (lock) {
+    CRYPTO_MUTEX_unlock_write(&ctx->lock);
+  }
+
+  if (found) {
+    // TODO(https://crbug.com/boringssl/251): Callbacks should not be called
+    // under a lock.
+    if (ctx->remove_session_cb != nullptr) {
+      ctx->remove_session_cb(ctx, found_session);
+    }
+    SSL_SESSION_free(found_session);
+  }
+
+  return found;
 }
 
 void ssl_set_session(SSL *ssl, SSL_SESSION *session) {
@@ -854,6 +840,98 @@
   }
 }
 
+static bool add_session_locked(SSL_CTX *ctx, UniquePtr<SSL_SESSION> session) {
+  SSL_SESSION *new_session = session.get();
+  SSL_SESSION *old_session;
+  if (!lh_SSL_SESSION_insert(ctx->sessions, &old_session, new_session)) {
+    return false;
+  }
+  // |ctx->sessions| took ownership of |new_session| and gave us back a
+  // reference to |old_session|. (|old_session| may be the same as
+  // |new_session|, in which case we traded identical references with
+  // |ctx->sessions|.)
+  session.release();
+  session.reset(old_session);
+
+  if (old_session != nullptr) {
+    if (old_session == new_session) {
+      // |session| was already in the cache. There are no linked list pointers
+      // to update.
+      return false;
+    }
+
+    // There was a session ID collision. |old_session| was replaced with
+    // |session| in the hash table, so |old_session| must be removed from the
+    // linked list to match.
+    SSL_SESSION_list_remove(ctx, old_session);
+  }
+
+  // This does not increment the reference count. Although |session| is inserted
+  // into two structures (a doubly-linked list and the hash table), |ctx| only
+  // takes one reference.
+  SSL_SESSION_list_add(ctx, new_session);
+
+  // Enforce any cache size limits.
+  if (SSL_CTX_sess_get_cache_size(ctx) > 0) {
+    while (lh_SSL_SESSION_num_items(ctx->sessions) >
+           SSL_CTX_sess_get_cache_size(ctx)) {
+      if (!remove_session(ctx, ctx->session_cache_tail,
+                          /*lock=*/false)) {
+        break;
+      }
+    }
+  }
+
+  return true;
+}
+
+void ssl_update_cache(SSL *ssl) {
+  SSL_CTX *ctx = ssl->session_ctx.get();
+  SSL_SESSION *session = ssl->s3->established_session.get();
+  int mode = SSL_is_server(ssl) ? SSL_SESS_CACHE_SERVER : SSL_SESS_CACHE_CLIENT;
+  if (!SSL_SESSION_is_resumable(session) ||
+      (ctx->session_cache_mode & mode) != mode) {
+    return;
+  }
+
+  // Clients never use the internal session cache.
+  if (ssl->server &&
+      !(ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_STORE)) {
+    UniquePtr<SSL_SESSION> ref = UpRef(session);
+    bool remove_expired_sessions = false;
+    {
+      MutexWriteLock lock(&ctx->lock);
+      add_session_locked(ctx, std::move(ref));
+
+      if (!(ctx->session_cache_mode & SSL_SESS_CACHE_NO_AUTO_CLEAR)) {
+        // Automatically flush the internal session cache every 255 connections.
+        ctx->handshakes_since_cache_flush++;
+        if (ctx->handshakes_since_cache_flush >= 255) {
+          remove_expired_sessions = true;
+          ctx->handshakes_since_cache_flush = 0;
+        }
+      }
+    }
+
+    if (remove_expired_sessions) {
+      // |SSL_CTX_flush_sessions| takes the lock we just released. We could
+      // merge the critical sections, but we'd then call user code under a
+      // lock, or compute |now| earlier, even when not flushing.
+      OPENSSL_timeval now;
+      ssl_get_current_time(ssl, &now);
+      SSL_CTX_flush_sessions(ctx, now.tv_sec);
+    }
+  }
+
+  if (ctx->new_session_cb != nullptr) {
+    UniquePtr<SSL_SESSION> ref = UpRef(session);
+    if (ctx->new_session_cb(ssl, ref.get())) {
+      // |new_session_cb|'s return value signals whether it took ownership.
+      ref.release();
+    }
+  }
+}
+
 BSSL_NAMESPACE_END
 
 using namespace bssl;
@@ -1019,7 +1097,8 @@
 }
 
 int SSL_SESSION_is_resumable(const SSL_SESSION *session) {
-  return !session->not_resumable;
+  return !session->not_resumable &&
+         (session->session_id_length != 0 || !session->ticket.empty());
 }
 
 int SSL_SESSION_has_ticket(const SSL_SESSION *session) {
@@ -1135,51 +1214,13 @@
 }
 
 int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *session) {
-  // Although |session| is inserted into two structures (a doubly-linked list
-  // and the hash table), |ctx| only takes one reference.
   UniquePtr<SSL_SESSION> owned_session = UpRef(session);
-
-  SSL_SESSION *old_session;
   MutexWriteLock lock(&ctx->lock);
-  if (!lh_SSL_SESSION_insert(ctx->sessions, &old_session, session)) {
-    return 0;
-  }
-  // |ctx->sessions| took ownership of |session| and gave us back a reference to
-  // |old_session|. (|old_session| may be the same as |session|, in which case
-  // we traded identical references with |ctx->sessions|.)
-  owned_session.release();
-  owned_session.reset(old_session);
-
-  if (old_session != NULL) {
-    if (old_session == session) {
-      // |session| was already in the cache. There are no linked list pointers
-      // to update.
-      return 0;
-    }
-
-    // There was a session ID collision. |old_session| was replaced with
-    // |session| in the hash table, so |old_session| must be removed from the
-    // linked list to match.
-    SSL_SESSION_list_remove(ctx, old_session);
-  }
-
-  SSL_SESSION_list_add(ctx, session);
-
-  // Enforce any cache size limits.
-  if (SSL_CTX_sess_get_cache_size(ctx) > 0) {
-    while (lh_SSL_SESSION_num_items(ctx->sessions) >
-           SSL_CTX_sess_get_cache_size(ctx)) {
-      if (!remove_session_lock(ctx, ctx->session_cache_tail, 0)) {
-        break;
-      }
-    }
-  }
-
-  return 1;
+  return add_session_locked(ctx, std::move(owned_session));
 }
 
 int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *session) {
-  return remove_session_lock(ctx, session, 1);
+  return remove_session(ctx, session, /*lock=*/true);
 }
 
 int SSL_set_session(SSL *ssl, SSL_SESSION *session) {
@@ -1233,10 +1274,11 @@
   if (param->time == 0 ||
       session->time + session->timeout < session->time ||
       param->time > (session->time + session->timeout)) {
-    // The reason we don't call SSL_CTX_remove_session() is to
-    // save on locking overhead
+    // TODO(davidben): This can probably just call |remove_session|.
     (void) lh_SSL_SESSION_delete(param->cache, session);
     SSL_SESSION_list_remove(param->ctx, session);
+    // TODO(https://crbug.com/boringssl/251): Callbacks should not be called
+    // under a lock.
     if (param->ctx->remove_session_cb != NULL) {
       param->ctx->remove_session_cb(param->ctx, session);
     }
@@ -1298,12 +1340,3 @@
                                                 int value) {
   return ctx->info_callback;
 }
-
-void SSL_CTX_set_channel_id_cb(SSL_CTX *ctx,
-                               void (*cb)(SSL *ssl, EVP_PKEY **pkey)) {
-  ctx->channel_id_cb = cb;
-}
-
-void (*SSL_CTX_get_channel_id_cb(SSL_CTX *ctx))(SSL *ssl, EVP_PKEY **pkey) {
-  return ctx->channel_id_cb;
-}
diff --git a/deps/boringssl/src/ssl/ssl_stat.cc b/deps/boringssl/src/ssl/ssl_stat.cc
index 5770aac..f7e1675 100644
--- a/deps/boringssl/src/ssl/ssl_stat.cc
+++ b/deps/boringssl/src/ssl/ssl_stat.cc
@@ -224,6 +224,9 @@
     case TLS1_AD_NO_APPLICATION_PROTOCOL:
       return "no application protocol";
 
+    case TLS1_AD_ECH_REQUIRED:
+      return "ECH required";
+
     default:
       return "unknown";
   }
diff --git a/deps/boringssl/src/ssl/ssl_test.cc b/deps/boringssl/src/ssl/ssl_test.cc
index 637f4d5..60d820b 100644
--- a/deps/boringssl/src/ssl/ssl_test.cc
+++ b/deps/boringssl/src/ssl/ssl_test.cc
@@ -26,11 +26,14 @@
 
 #include <openssl/aead.h>
 #include <openssl/base64.h>
+#include <openssl/bytestring.h>
 #include <openssl/bio.h>
 #include <openssl/cipher.h>
 #include <openssl/crypto.h>
+#include <openssl/curve25519.h>
 #include <openssl/err.h>
 #include <openssl/hmac.h>
+#include <openssl/hpke.h>
 #include <openssl/pem.h>
 #include <openssl/sha.h>
 #include <openssl/ssl.h>
@@ -1192,6 +1195,24 @@
   }
 }
 
+static bssl::UniquePtr<X509> CertFromPEM(const char *pem) {
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem)));
+  if (!bio) {
+    return nullptr;
+  }
+  return bssl::UniquePtr<X509>(
+      PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+}
+
+static bssl::UniquePtr<EVP_PKEY> KeyFromPEM(const char *pem) {
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, strlen(pem)));
+  if (!bio) {
+    return nullptr;
+  }
+  return bssl::UniquePtr<EVP_PKEY>(
+      PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
+}
+
 static bssl::UniquePtr<X509> GetTestCertificate() {
   static const char kCertPEM[] =
       "-----BEGIN CERTIFICATE-----\n"
@@ -1209,9 +1230,7 @@
       "T5oQpHL9z/cCDLAKCKRa4uV0fhEdOWBqyR9p8y5jJtye72t6CuFUV5iqcpF4BH4f\n"
       "j2VNHwsSrJwkD4QUGlUtH7vwnQmyCFxZMmWAJg==\n"
       "-----END CERTIFICATE-----\n";
-  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM)));
-  return bssl::UniquePtr<X509>(
-      PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+  return CertFromPEM(kCertPEM);
 }
 
 static bssl::UniquePtr<EVP_PKEY> GetTestKey() {
@@ -1231,9 +1250,20 @@
       "tfDwbqkta4xcux67//khAkEAvvRXLHTaa6VFzTaiiO8SaFsHV3lQyXOtMrBpB5jd\n"
       "moZWgjHvB2W9Ckn7sDqsPB+U2tyX0joDdQEyuiMECDY8oQ==\n"
       "-----END RSA PRIVATE KEY-----\n";
-  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kKeyPEM, strlen(kKeyPEM)));
-  return bssl::UniquePtr<EVP_PKEY>(
-      PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
+  return KeyFromPEM(kKeyPEM);
+}
+
+static bssl::UniquePtr<SSL_CTX> CreateContextWithTestCertificate(
+    const SSL_METHOD *method) {
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+  if (!ctx || !cert || !key ||
+      !SSL_CTX_use_certificate(ctx.get(), cert.get()) ||
+      !SSL_CTX_use_PrivateKey(ctx.get(), key.get())) {
+    return nullptr;
+  }
+  return ctx;
 }
 
 static bssl::UniquePtr<X509> GetECDSATestCertificate() {
@@ -1250,8 +1280,7 @@
       "BgcqhkjOPQQBA0gAMEUCIQDyoDVeUTo2w4J5m+4nUIWOcAZ0lVfSKXQA9L4Vh13E\n"
       "BwIgfB55FGohg/B6dGh5XxSZmmi08cueFV7mHzJSYV51yRQ=\n"
       "-----END CERTIFICATE-----\n";
-  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kCertPEM, strlen(kCertPEM)));
-  return bssl::UniquePtr<X509>(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr));
+  return CertFromPEM(kCertPEM);
 }
 
 static bssl::UniquePtr<EVP_PKEY> GetECDSATestKey() {
@@ -1261,9 +1290,7 @@
       "TYlodwi1b8ldMHcO6NHJzgqLtGqhRANCAATmK2niv2Wfl74vHg2UikzVl2u3qR4N\n"
       "Rvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUdfvGULUvPciLB\n"
       "-----END PRIVATE KEY-----\n";
-  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kKeyPEM, strlen(kKeyPEM)));
-  return bssl::UniquePtr<EVP_PKEY>(
-      PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
+  return KeyFromPEM(kKeyPEM);
 }
 
 static bssl::UniquePtr<CRYPTO_BUFFER> BufferFromPEM(const char *pem) {
@@ -1377,9 +1404,165 @@
       "buB7ERSdaNbO21zXt9FEA3+z0RfMd/Zv2vlIWOSB5nzl/7UKti3sribK6s9ZVLfi\n"
       "SxpiPQ8d/hmSGwn4ksrWUsJD\n"
       "-----END PRIVATE KEY-----\n";
-  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kKeyPEM, strlen(kKeyPEM)));
-  return bssl::UniquePtr<EVP_PKEY>(
-      PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
+  return KeyFromPEM(kKeyPEM);
+}
+
+static bool CompleteHandshakes(SSL *client, SSL *server) {
+  // Drive both their handshakes to completion.
+  for (;;) {
+    int client_ret = SSL_do_handshake(client);
+    int client_err = SSL_get_error(client, client_ret);
+    if (client_err != SSL_ERROR_NONE &&
+        client_err != SSL_ERROR_WANT_READ &&
+        client_err != SSL_ERROR_WANT_WRITE &&
+        client_err != SSL_ERROR_PENDING_TICKET) {
+      fprintf(stderr, "Client error: %s\n", SSL_error_description(client_err));
+      return false;
+    }
+
+    int server_ret = SSL_do_handshake(server);
+    int server_err = SSL_get_error(server, server_ret);
+    if (server_err != SSL_ERROR_NONE &&
+        server_err != SSL_ERROR_WANT_READ &&
+        server_err != SSL_ERROR_WANT_WRITE &&
+        server_err != SSL_ERROR_PENDING_TICKET) {
+      fprintf(stderr, "Server error: %s\n", SSL_error_description(server_err));
+      return false;
+    }
+
+    if (client_ret == 1 && server_ret == 1) {
+      break;
+    }
+  }
+
+  return true;
+}
+
+static bool FlushNewSessionTickets(SSL *client, SSL *server) {
+  // NewSessionTickets are deferred on the server to |SSL_write|, and clients do
+  // not pick them up until |SSL_read|.
+  for (;;) {
+    int server_ret = SSL_write(server, nullptr, 0);
+    int server_err = SSL_get_error(server, server_ret);
+    // The server may either succeed (|server_ret| is zero) or block on write
+    // (|server_ret| is -1 and |server_err| is |SSL_ERROR_WANT_WRITE|).
+    if (server_ret > 0 ||
+        (server_ret < 0 && server_err != SSL_ERROR_WANT_WRITE)) {
+      fprintf(stderr, "Unexpected server result: %d %d\n", server_ret,
+              server_err);
+      return false;
+    }
+
+    int client_ret = SSL_read(client, nullptr, 0);
+    int client_err = SSL_get_error(client, client_ret);
+    // The client must always block on read.
+    if (client_ret != -1 || client_err != SSL_ERROR_WANT_READ) {
+      fprintf(stderr, "Unexpected client result: %d %d\n", client_ret,
+              client_err);
+      return false;
+    }
+
+    // The server flushed everything it had to write.
+    if (server_ret == 0) {
+      return true;
+    }
+  }
+}
+
+// CreateClientAndServer creates a client and server |SSL| objects whose |BIO|s
+// are paired with each other. It does not run the handshake. The caller is
+// expected to configure the objects and drive the handshake as needed.
+static bool CreateClientAndServer(bssl::UniquePtr<SSL> *out_client,
+                                  bssl::UniquePtr<SSL> *out_server,
+                                  SSL_CTX *client_ctx, SSL_CTX *server_ctx) {
+  bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
+  if (!client || !server) {
+    return false;
+  }
+  SSL_set_connect_state(client.get());
+  SSL_set_accept_state(server.get());
+
+  BIO *bio1, *bio2;
+  if (!BIO_new_bio_pair(&bio1, 0, &bio2, 0)) {
+    return false;
+  }
+  // SSL_set_bio takes ownership.
+  SSL_set_bio(client.get(), bio1, bio1);
+  SSL_set_bio(server.get(), bio2, bio2);
+
+  *out_client = std::move(client);
+  *out_server = std::move(server);
+  return true;
+}
+
+struct ClientConfig {
+  SSL_SESSION *session = nullptr;
+  std::string servername;
+  bool early_data = false;
+};
+
+static bool ConnectClientAndServer(bssl::UniquePtr<SSL> *out_client,
+                                   bssl::UniquePtr<SSL> *out_server,
+                                   SSL_CTX *client_ctx, SSL_CTX *server_ctx,
+                                   const ClientConfig &config = ClientConfig(),
+                                   bool shed_handshake_config = true) {
+  bssl::UniquePtr<SSL> client, server;
+  if (!CreateClientAndServer(&client, &server, client_ctx, server_ctx)) {
+    return false;
+  }
+  if (config.early_data) {
+    SSL_set_early_data_enabled(client.get(), 1);
+  }
+  if (config.session) {
+    SSL_set_session(client.get(), config.session);
+  }
+  if (!config.servername.empty() &&
+      !SSL_set_tlsext_host_name(client.get(), config.servername.c_str())) {
+    return false;
+  }
+
+  SSL_set_shed_handshake_config(client.get(), shed_handshake_config);
+  SSL_set_shed_handshake_config(server.get(), shed_handshake_config);
+
+  if (!CompleteHandshakes(client.get(), server.get())) {
+    return false;
+  }
+
+  *out_client = std::move(client);
+  *out_server = std::move(server);
+  return true;
+}
+
+static bssl::UniquePtr<SSL_SESSION> g_last_session;
+
+static int SaveLastSession(SSL *ssl, SSL_SESSION *session) {
+  // Save the most recent session.
+  g_last_session.reset(session);
+  return 1;
+}
+
+static bssl::UniquePtr<SSL_SESSION> CreateClientSession(
+    SSL_CTX *client_ctx, SSL_CTX *server_ctx,
+    const ClientConfig &config = ClientConfig()) {
+  g_last_session = nullptr;
+  SSL_CTX_sess_set_new_cb(client_ctx, SaveLastSession);
+
+  // Connect client and server to get a session.
+  bssl::UniquePtr<SSL> client, server;
+  if (!ConnectClientAndServer(&client, &server, client_ctx, server_ctx,
+                              config) ||
+      !FlushNewSessionTickets(client.get(), server.get())) {
+    fprintf(stderr, "Failed to connect client and server.\n");
+    return nullptr;
+  }
+
+  SSL_CTX_sess_set_new_cb(client_ctx, nullptr);
+
+  if (!g_last_session) {
+    fprintf(stderr, "Client did not receive a session.\n");
+    return nullptr;
+  }
+  return std::move(g_last_session);
 }
 
 // Test that |SSL_get_client_CA_list| echoes back the configured parameter even
@@ -1440,6 +1623,746 @@
   EXPECT_EQ(0, X509_NAME_cmp(sk_X509_NAME_value(list, 2), name1));
 }
 
+struct ECHConfigParams {
+  uint16_t version = TLSEXT_TYPE_encrypted_client_hello;
+  uint16_t config_id = 1;
+  std::string public_name = "example.com";
+  const EVP_HPKE_KEY *key = nullptr;
+  // kem_id, if zero, takes its value from |key|.
+  uint16_t kem_id = 0;
+  // public_key, if empty takes its value from |key|.
+  std::vector<uint8_t> public_key;
+  size_t max_name_len = 16;
+  // cipher_suites is a list of code points which should contain pairs of KDF
+  // and AEAD IDs.
+  std::vector<uint16_t> cipher_suites = {EVP_HPKE_HKDF_SHA256,
+                                         EVP_HPKE_AES_128_GCM};
+  std::vector<uint8_t> extensions;
+};
+
+// MakeECHConfig serializes an ECHConfig from |params| and writes it to
+// |*out|.
+bool MakeECHConfig(std::vector<uint8_t> *out,
+                         const ECHConfigParams &params) {
+  uint16_t kem_id = params.kem_id == 0
+                        ? EVP_HPKE_KEM_id(EVP_HPKE_KEY_kem(params.key))
+                        : params.kem_id;
+  std::vector<uint8_t> public_key = params.public_key;
+  if (public_key.empty()) {
+    public_key.resize(EVP_HPKE_MAX_PUBLIC_KEY_LENGTH);
+    size_t len;
+    if (!EVP_HPKE_KEY_public_key(params.key, public_key.data(), &len,
+                                 public_key.size())) {
+      return false;
+    }
+    public_key.resize(len);
+  }
+
+  bssl::ScopedCBB cbb;
+  CBB contents, child;
+  if (!CBB_init(cbb.get(), 64) ||
+      !CBB_add_u16(cbb.get(), params.version) ||
+      !CBB_add_u16_length_prefixed(cbb.get(), &contents) ||
+      !CBB_add_u8(&contents, params.config_id) ||
+      !CBB_add_u16(&contents, kem_id) ||
+      !CBB_add_u16_length_prefixed(&contents, &child) ||
+      !CBB_add_bytes(&child, public_key.data(), public_key.size()) ||
+      !CBB_add_u16_length_prefixed(&contents, &child)) {
+    return false;
+  }
+  for (uint16_t cipher_suite : params.cipher_suites) {
+    if (!CBB_add_u16(&child, cipher_suite)) {
+      return false;
+    }
+  }
+  if (!CBB_add_u8(&contents, params.max_name_len) ||
+      !CBB_add_u8_length_prefixed(&contents, &child) ||
+      !CBB_add_bytes(
+          &child, reinterpret_cast<const uint8_t *>(params.public_name.data()),
+          params.public_name.size()) ||
+      !CBB_add_u16_length_prefixed(&contents, &child) ||
+      !CBB_add_bytes(&child, params.extensions.data(),
+                     params.extensions.size()) ||
+      !CBB_flush(cbb.get())) {
+    return false;
+  }
+
+  out->assign(CBB_data(cbb.get()), CBB_data(cbb.get()) + CBB_len(cbb.get()));
+  return true;
+}
+
+static bssl::UniquePtr<SSL_ECH_KEYS> MakeTestECHKeys(uint8_t config_id = 1) {
+  bssl::ScopedEVP_HPKE_KEY key;
+  uint8_t *ech_config;
+  size_t ech_config_len;
+  if (!EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()) ||
+      !SSL_marshal_ech_config(&ech_config, &ech_config_len, config_id,
+                              key.get(), "public.example", 16)) {
+    return nullptr;
+  }
+  bssl::UniquePtr<uint8_t> free_ech_config(ech_config);
+
+  // Install a non-retry config.
+  bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+  if (!keys || !SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1, ech_config,
+                                 ech_config_len, key.get())) {
+    return nullptr;
+  }
+  return keys;
+}
+
+static bool InstallECHConfigList(SSL *client, const SSL_ECH_KEYS *keys) {
+  uint8_t *ech_config_list;
+  size_t ech_config_list_len;
+  if (!SSL_ECH_KEYS_marshal_retry_configs(keys, &ech_config_list,
+                                          &ech_config_list_len)) {
+    return false;
+  }
+  bssl::UniquePtr<uint8_t> free_ech_config_list(ech_config_list);
+  return SSL_set1_ech_config_list(client, ech_config_list, ech_config_list_len);
+}
+
+// Test that |SSL_marshal_ech_config| and |SSL_ECH_KEYS_marshal_retry_configs|
+// output values as expected.
+TEST(SSLTest, MarshalECHConfig) {
+  static const uint8_t kPrivateKey[X25519_PRIVATE_KEY_LEN] = {
+      0xbc, 0xb5, 0x51, 0x29, 0x31, 0x10, 0x30, 0xc9, 0xed, 0x26, 0xde,
+      0xd4, 0xb3, 0xdf, 0x3a, 0xce, 0x06, 0x8a, 0xee, 0x17, 0xab, 0xce,
+      0xd7, 0xdb, 0xf3, 0x11, 0xe5, 0xa8, 0xf3, 0xb1, 0x8e, 0x24};
+  bssl::ScopedEVP_HPKE_KEY key;
+  ASSERT_TRUE(EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(),
+                                kPrivateKey, sizeof(kPrivateKey)));
+
+  static const uint8_t kECHConfig[] = {
+      // version
+      0xfe, 0x0d,
+      // length
+      0x00, 0x41,
+      // contents.config_id
+      0x01,
+      // contents.kem_id
+      0x00, 0x20,
+      // contents.public_key
+      0x00, 0x20, 0xa6, 0x9a, 0x41, 0x48, 0x5d, 0x32, 0x96, 0xa4, 0xe0, 0xc3,
+      0x6a, 0xee, 0xf6, 0x63, 0x0f, 0x59, 0x32, 0x6f, 0xdc, 0xff, 0x81, 0x29,
+      0x59, 0xa5, 0x85, 0xd3, 0x9b, 0x3b, 0xde, 0x98, 0x55, 0x5c,
+      // contents.cipher_suites
+      0x00, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x03,
+      // contents.maximum_name_length
+      0x10,
+      // contents.public_name
+      0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2e, 0x65, 0x78, 0x61, 0x6d,
+      0x70, 0x6c, 0x65,
+      // contents.extensions
+      0x00, 0x00};
+  uint8_t *ech_config;
+  size_t ech_config_len;
+  ASSERT_TRUE(SSL_marshal_ech_config(&ech_config, &ech_config_len,
+                                     /*config_id=*/1, key.get(),
+                                     "public.example", 16));
+  bssl::UniquePtr<uint8_t> free_ech_config(ech_config);
+  EXPECT_EQ(Bytes(kECHConfig), Bytes(ech_config, ech_config_len));
+
+  // Generate a second ECHConfig.
+  bssl::ScopedEVP_HPKE_KEY key2;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key2.get(), EVP_hpke_x25519_hkdf_sha256()));
+  uint8_t *ech_config2;
+  size_t ech_config2_len;
+  ASSERT_TRUE(SSL_marshal_ech_config(&ech_config2, &ech_config2_len,
+                                     /*config_id=*/2, key2.get(),
+                                     "public.example", 16));
+  bssl::UniquePtr<uint8_t> free_ech_config2(ech_config2);
+
+  // Install both ECHConfigs in an |SSL_ECH_KEYS|.
+  bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+  ASSERT_TRUE(keys);
+  ASSERT_TRUE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1, ech_config,
+                               ech_config_len, key.get()));
+  ASSERT_TRUE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1, ech_config2,
+                               ech_config2_len, key2.get()));
+
+  // The ECHConfigList should be correctly serialized.
+  uint8_t *ech_config_list;
+  size_t ech_config_list_len;
+  ASSERT_TRUE(SSL_ECH_KEYS_marshal_retry_configs(keys.get(), &ech_config_list,
+                                                 &ech_config_list_len));
+  bssl::UniquePtr<uint8_t> free_ech_config_list(ech_config_list);
+
+  // ECHConfigList is just the concatenation with a length prefix.
+  size_t len = ech_config_len + ech_config2_len;
+  std::vector<uint8_t> expected = {uint8_t(len >> 8), uint8_t(len)};
+  expected.insert(expected.end(), ech_config, ech_config + ech_config_len);
+  expected.insert(expected.end(), ech_config2, ech_config2 + ech_config2_len);
+  EXPECT_EQ(Bytes(expected), Bytes(ech_config_list, ech_config_list_len));
+}
+
+TEST(SSLTest, ECHHasDuplicateConfigID) {
+  const struct {
+    std::vector<uint8_t> ids;
+    bool has_duplicate;
+  } kTests[] = {
+      {{}, false},
+      {{1}, false},
+      {{1, 2, 3, 255}, false},
+      {{1, 2, 3, 1}, true},
+  };
+  for (const auto &test : kTests) {
+    bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+    ASSERT_TRUE(keys);
+    for (const uint8_t id : test.ids) {
+      bssl::ScopedEVP_HPKE_KEY key;
+      ASSERT_TRUE(
+          EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
+      uint8_t *ech_config;
+      size_t ech_config_len;
+      ASSERT_TRUE(SSL_marshal_ech_config(&ech_config, &ech_config_len, id,
+                                         key.get(), "public.example", 16));
+      bssl::UniquePtr<uint8_t> free_ech_config(ech_config);
+      ASSERT_TRUE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                                   ech_config, ech_config_len, key.get()));
+    }
+
+    EXPECT_EQ(test.has_duplicate ? 1 : 0,
+              SSL_ECH_KEYS_has_duplicate_config_id(keys.get()));
+  }
+}
+
+// Test that |SSL_ECH_KEYS_add| checks consistency between the public and
+// private key.
+TEST(SSLTest, ECHKeyConsistency) {
+  bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+  ASSERT_TRUE(keys);
+  bssl::ScopedEVP_HPKE_KEY key;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
+  uint8_t public_key[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
+  size_t public_key_len;
+  ASSERT_TRUE(EVP_HPKE_KEY_public_key(key.get(), public_key, &public_key_len,
+                                      sizeof(public_key)));
+
+  // Adding an ECHConfig with the matching public key succeeds.
+  ECHConfigParams params;
+  params.key = key.get();
+  std::vector<uint8_t> ech_config;
+  ASSERT_TRUE(MakeECHConfig(&ech_config, params));
+  EXPECT_TRUE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                               ech_config.data(), ech_config.size(),
+                               key.get()));
+
+  // Adding an ECHConfig with the wrong public key is an error.
+  bssl::ScopedEVP_HPKE_KEY wrong_key;
+  ASSERT_TRUE(
+      EVP_HPKE_KEY_generate(wrong_key.get(), EVP_hpke_x25519_hkdf_sha256()));
+  EXPECT_FALSE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                                ech_config.data(), ech_config.size(),
+                                wrong_key.get()));
+
+  // Adding an ECHConfig with a truncated public key is an error.
+  ECHConfigParams truncated;
+  truncated.key = key.get();
+  truncated.public_key.assign(public_key, public_key + public_key_len - 1);
+  ASSERT_TRUE(MakeECHConfig(&ech_config, truncated));
+  EXPECT_FALSE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                                ech_config.data(), ech_config.size(), key.get()));
+
+  // Adding an ECHConfig with the right public key, but wrong KEM ID, is an
+  // error.
+  ECHConfigParams wrong_kem;
+  wrong_kem.key = key.get();
+  wrong_kem.kem_id = 0x0010;  // DHKEM(P-256, HKDF-SHA256)
+  ASSERT_TRUE(MakeECHConfig(&ech_config, wrong_kem));
+  EXPECT_FALSE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                                ech_config.data(), ech_config.size(),
+                                key.get()));
+}
+
+// Test that |SSL_CTX_set1_ech_keys| fails when the config list
+// has no retry configs.
+TEST(SSLTest, ECHServerConfigsWithoutRetryConfigs) {
+  bssl::ScopedEVP_HPKE_KEY key;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
+  uint8_t *ech_config;
+  size_t ech_config_len;
+  ASSERT_TRUE(SSL_marshal_ech_config(&ech_config, &ech_config_len,
+                                     /*config_id=*/1, key.get(),
+                                     "public.example", 16));
+  bssl::UniquePtr<uint8_t> free_ech_config(ech_config);
+
+  // Install a non-retry config.
+  bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+  ASSERT_TRUE(keys);
+  ASSERT_TRUE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/0, ech_config,
+                               ech_config_len, key.get()));
+
+  // |keys| has no retry configs.
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(ctx);
+  EXPECT_FALSE(SSL_CTX_set1_ech_keys(ctx.get(), keys.get()));
+
+  // Add the same ECHConfig to the list, but this time mark it as a retry
+  // config.
+  ASSERT_TRUE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1, ech_config,
+                               ech_config_len, key.get()));
+  EXPECT_TRUE(SSL_CTX_set1_ech_keys(ctx.get(), keys.get()));
+}
+
+// Test that the server APIs reject ECHConfigs with unsupported features.
+TEST(SSLTest, UnsupportedECHConfig) {
+  bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+  ASSERT_TRUE(keys);
+  bssl::ScopedEVP_HPKE_KEY key;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()));
+
+  // Unsupported versions are rejected.
+  ECHConfigParams unsupported_version;
+  unsupported_version.version = 0xffff;
+  unsupported_version.key = key.get();
+  std::vector<uint8_t> ech_config;
+  ASSERT_TRUE(MakeECHConfig(&ech_config, unsupported_version));
+  EXPECT_FALSE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                                ech_config.data(), ech_config.size(),
+                                key.get()));
+
+  // Unsupported cipher suites are rejected. (We only support HKDF-SHA256.)
+  ECHConfigParams unsupported_kdf;
+  unsupported_kdf.key = key.get();
+  unsupported_kdf.cipher_suites = {0x002 /* HKDF-SHA384 */,
+                                   EVP_HPKE_AES_128_GCM};
+  ASSERT_TRUE(MakeECHConfig(&ech_config, unsupported_kdf));
+  EXPECT_FALSE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                                ech_config.data(), ech_config.size(),
+                                key.get()));
+  ECHConfigParams unsupported_aead;
+  unsupported_aead.key = key.get();
+  unsupported_aead.cipher_suites = {EVP_HPKE_HKDF_SHA256, 0xffff};
+  ASSERT_TRUE(MakeECHConfig(&ech_config, unsupported_aead));
+  EXPECT_FALSE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                                ech_config.data(), ech_config.size(),
+                                key.get()));
+
+
+  // Unsupported extensions are rejected.
+  ECHConfigParams extensions;
+  extensions.key = key.get();
+  extensions.extensions = {0x00, 0x01, 0x00, 0x00};
+  ASSERT_TRUE(MakeECHConfig(&ech_config, extensions));
+  EXPECT_FALSE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                                ech_config.data(), ech_config.size(),
+                                key.get()));
+
+  // Invalid public names are rejected.
+  ECHConfigParams invalid_public_name;
+  invalid_public_name.key = key.get();
+  invalid_public_name.public_name = "dns_names_have_no_underscores.example";
+  ASSERT_TRUE(MakeECHConfig(&ech_config, invalid_public_name));
+  EXPECT_FALSE(SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1,
+                                ech_config.data(), ech_config.size(),
+                                key.get()));
+}
+
+// Test that |SSL_get_client_random| reports the correct value on both client
+// and server in ECH. The client sends two different random values. When ECH is
+// accepted, we should report the inner one.
+TEST(SSLTest, ECHClientRandomsMatch) {
+  bssl::UniquePtr<SSL_CTX> server_ctx =
+      CreateContextWithTestCertificate(TLS_method());
+  bssl::UniquePtr<SSL_ECH_KEYS> keys = MakeTestECHKeys();
+  ASSERT_TRUE(keys);
+  ASSERT_TRUE(SSL_CTX_set1_ech_keys(server_ctx.get(), keys.get()));
+
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(client_ctx);
+  bssl::UniquePtr<SSL> client, server;
+  ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                    server_ctx.get()));
+  ASSERT_TRUE(InstallECHConfigList(client.get(), keys.get()));
+  ASSERT_TRUE(CompleteHandshakes(client.get(), server.get()));
+
+  EXPECT_TRUE(SSL_ech_accepted(client.get()));
+  EXPECT_TRUE(SSL_ech_accepted(server.get()));
+
+  // An ECH server will fairly naturally record the inner ClientHello random,
+  // but an ECH client may forget to update the random once ClientHelloInner is
+  // selected.
+  uint8_t client_random1[SSL3_RANDOM_SIZE];
+  uint8_t client_random2[SSL3_RANDOM_SIZE];
+  ASSERT_EQ(sizeof(client_random1),
+            SSL_get_client_random(client.get(), client_random1,
+                                  sizeof(client_random1)));
+  ASSERT_EQ(sizeof(client_random2),
+            SSL_get_client_random(server.get(), client_random2,
+                                  sizeof(client_random2)));
+  EXPECT_EQ(Bytes(client_random1), Bytes(client_random2));
+}
+
+// GetECHLength sets |*out_client_hello_len| and |*out_ech_len| to the lengths
+// of the ClientHello and ECH extension, respectively, when a client created
+// from |ctx| constructs a ClientHello with name |name| and an ECHConfig with
+// maximum name length |max_name_len|.
+static bool GetECHLength(SSL_CTX *ctx, size_t *out_client_hello_len,
+                         size_t *out_ech_len, size_t max_name_len,
+                         const char *name) {
+  bssl::ScopedEVP_HPKE_KEY key;
+  uint8_t *ech_config;
+  size_t ech_config_len;
+  if (!EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()) ||
+      !SSL_marshal_ech_config(&ech_config, &ech_config_len,
+                              /*config_id=*/1, key.get(), "public.example",
+                              max_name_len)) {
+    return false;
+  }
+  bssl::UniquePtr<uint8_t> free_ech_config(ech_config);
+
+  bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+  if (!keys || !SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/1, ech_config,
+                                 ech_config_len, key.get())) {
+    return false;
+  }
+
+  bssl::UniquePtr<SSL> ssl(SSL_new(ctx));
+  if (!ssl || !InstallECHConfigList(ssl.get(), keys.get()) ||
+      (name != nullptr && !SSL_set_tlsext_host_name(ssl.get(), name))) {
+    return false;
+  }
+  SSL_set_connect_state(ssl.get());
+
+  std::vector<uint8_t> client_hello;
+  SSL_CLIENT_HELLO parsed;
+  const uint8_t *unused;
+  if (!GetClientHello(ssl.get(), &client_hello) ||
+      !ssl_client_hello_init(
+          ssl.get(), &parsed,
+          // Skip record and handshake headers. This assumes the ClientHello
+          // fits in one record.
+          MakeConstSpan(client_hello)
+              .subspan(SSL3_RT_HEADER_LENGTH + SSL3_HM_HEADER_LENGTH)) ||
+      !SSL_early_callback_ctx_extension_get(
+          &parsed, TLSEXT_TYPE_encrypted_client_hello, &unused, out_ech_len)) {
+    return false;
+  }
+  *out_client_hello_len = client_hello.size();
+  return true;
+}
+
+TEST(SSLTest, ECHPadding) {
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(ctx);
+
+  // Sample lengths with max_name_len = 128 as baseline.
+  size_t client_hello_len_baseline, ech_len_baseline;
+  ASSERT_TRUE(GetECHLength(ctx.get(), &client_hello_len_baseline,
+                           &ech_len_baseline, 128, "example.com"));
+
+  // Check that all name lengths under the server's maximum look the same.
+  for (size_t name_len : {1, 2, 32, 64, 127, 128}) {
+    SCOPED_TRACE(name_len);
+    size_t client_hello_len, ech_len;
+    ASSERT_TRUE(GetECHLength(ctx.get(), &client_hello_len, &ech_len, 128,
+                             std::string(name_len, 'a').c_str()));
+    EXPECT_EQ(client_hello_len, client_hello_len_baseline);
+    EXPECT_EQ(ech_len, ech_len_baseline);
+  }
+
+  // When sending no SNI, we must still pad as if we are sending one.
+  size_t client_hello_len, ech_len;
+  ASSERT_TRUE(
+      GetECHLength(ctx.get(), &client_hello_len, &ech_len, 128, nullptr));
+  EXPECT_EQ(client_hello_len, client_hello_len_baseline);
+  EXPECT_EQ(ech_len, ech_len_baseline);
+
+  // Name lengths above the maximum do not get named-based padding, but the
+  // overall input is padded to a multiple of 32.
+  size_t client_hello_len_baseline2, ech_len_baseline2;
+  ASSERT_TRUE(GetECHLength(ctx.get(), &client_hello_len_baseline2,
+                           &ech_len_baseline2, 128,
+                           std::string(128 + 32, 'a').c_str()));
+  EXPECT_EQ(ech_len_baseline2, ech_len_baseline + 32);
+  // The ClientHello lengths may match if we are still under the threshold for
+  // padding extension.
+  EXPECT_GE(client_hello_len_baseline2, client_hello_len_baseline);
+
+  for (size_t name_len = 128 + 1; name_len < 128 + 32; name_len++) {
+    SCOPED_TRACE(name_len);
+    ASSERT_TRUE(GetECHLength(ctx.get(), &client_hello_len, &ech_len, 128,
+                             std::string(name_len, 'a').c_str()));
+    EXPECT_TRUE(ech_len == ech_len_baseline || ech_len == ech_len_baseline2)
+        << ech_len;
+    EXPECT_TRUE(client_hello_len == client_hello_len_baseline ||
+                client_hello_len == client_hello_len_baseline2)
+        << client_hello_len;
+  }
+}
+
+TEST(SSLTest, ECHPublicName) {
+  auto str_to_span = [](const char *str) -> Span<const uint8_t> {
+    return MakeConstSpan(reinterpret_cast<const uint8_t *>(str), strlen(str));
+  };
+
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("example.com")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span(".example.com")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("example.com.")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("example..com")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("www.-example.com")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("www.example-.com")));
+  EXPECT_FALSE(
+      ssl_is_valid_ech_public_name(str_to_span("no_underscores.example")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(
+      str_to_span("invalid_chars.\x01.example")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(
+      str_to_span("invalid_chars.\xff.example")));
+  static const uint8_t kWithNUL[] = {'t', 'e', 's', 't', 0};
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(kWithNUL));
+
+  // Test an LDH label with every character and the maximum length.
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span(
+      "abcdefhijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span(
+      "abcdefhijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ-01234567899")));
+
+  // Inputs that parse as IPv4 addresses are rejected.
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("127.0.0.1")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0177.0.0.01")));
+  EXPECT_FALSE(
+      ssl_is_valid_ech_public_name(str_to_span("0x7f.0x.0x.0x00000001")));
+  EXPECT_FALSE(
+      ssl_is_valid_ech_public_name(str_to_span("0XAB.0XCD.0XEF.0X01")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0.0.0.0")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("255.255.255.255")));
+  // Out-of-bounds or overflowing components are not IP addresses.
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("256.255.255.255")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("255.0x100.255.255")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("255.255.255.0400")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(
+      str_to_span("255.255.255.0x100000000")));
+  // Invalid characters for the base are not IP addresses.
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("12a.0.0.1")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0xg.0.0.1")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("08.0.0.1")));
+  // Trailing components can be merged into a single component.
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("127.0.1")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("127.1")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("2130706433")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0x7f000001")));
+  // Merged components must respect their limits.
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0.0.0.0xff")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0.0.0xffff")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0.0xffffff")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("0xffffffff")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0.0.0.0x100")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0.0.0x10000")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0.0x1000000")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("0x100000000")));
+  // Correctly handle overflow in decimal and octal.
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("037777777777")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("040000000000")));
+  EXPECT_FALSE(ssl_is_valid_ech_public_name(str_to_span("4294967295")));
+  EXPECT_TRUE(ssl_is_valid_ech_public_name(str_to_span("4294967296")));
+}
+
+// When using the built-in verifier, test that |SSL_get0_ech_name_override| is
+// applied automatically.
+TEST(SSLTest, ECHBuiltinVerifier) {
+  // These test certificates generated with the following Go program.
+  /* clang-format off
+func main() {
+  notBefore := time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
+  notAfter := time.Date(2099, time.January, 1, 0, 0, 0, 0, time.UTC)
+  rootKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+  rootTemplate := &x509.Certificate{
+    SerialNumber:          big.NewInt(1),
+    Subject:               pkix.Name{CommonName: "Test CA"},
+    NotBefore:             notBefore,
+    NotAfter:              notAfter,
+    BasicConstraintsValid: true,
+    IsCA:                  true,
+  }
+  rootDER, _ := x509.CreateCertificate(rand.Reader, rootTemplate, rootTemplate, &rootKey.PublicKey, rootKey)
+  root, _ := x509.ParseCertificate(rootDER)
+  pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: rootDER})
+  leafKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+  leafKeyDER, _ := x509.MarshalPKCS8PrivateKey(leafKey)
+  pem.Encode(os.Stdout, &pem.Block{Type: "PRIVATE KEY", Bytes: leafKeyDER})
+  for i, name := range []string{"public.example", "secret.example"} {
+    leafTemplate := &x509.Certificate{
+      SerialNumber:          big.NewInt(int64(i) + 2),
+      Subject:               pkix.Name{CommonName: name},
+      NotBefore:             notBefore,
+      NotAfter:              notAfter,
+      BasicConstraintsValid: true,
+      DNSNames:              []string{name},
+    }
+    leafDER, _ := x509.CreateCertificate(rand.Reader, leafTemplate, root, &leafKey.PublicKey, rootKey)
+    pem.Encode(os.Stdout, &pem.Block{Type: "CERTIFICATE", Bytes: leafDER})
+  }
+}
+clang-format on */
+  bssl::UniquePtr<X509> root = CertFromPEM(R"(
+-----BEGIN CERTIFICATE-----
+MIIBRzCB7aADAgECAgEBMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMTB1Rlc3QgQ0Ew
+IBcNMDAwMTAxMDAwMDAwWhgPMjA5OTAxMDEwMDAwMDBaMBIxEDAOBgNVBAMTB1Rl
+c3QgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAT5JUjrI1DAxSpEl88UkmJw
+tAJqxo/YrSFo9V3MkcNkfTixi5p6MUtO8DazhEgekBcd2+tBAWtl7dy0qpvTqx92
+ozIwMDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTw6ftkexAI6o4r5FntJIfL
+GU5F4zAKBggqhkjOPQQDAgNJADBGAiEAiiNowddQeHZaZFIygwe6RW5/WG4sUXWC
+dkyl9CQzRaYCIQCFS1EvwZbZtMny27fYm1eeYciY0TkJTEi34H1KwyzzIA==
+-----END CERTIFICATE-----
+)");
+  ASSERT_TRUE(root);
+  bssl::UniquePtr<EVP_PKEY> leaf_key = KeyFromPEM(R"(
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgj5WKHwHnziiyPauf
+7QukxTwtTyGZkk8qNdms4puJfxqhRANCAARNrkhxabALDlJrHtvkuDwvCWUF/oVC
+hr6PDITHi1lDlJzvVT4aXBH87sH2n2UV5zpx13NHkq1bIC8eRT8eOIe0
+-----END PRIVATE KEY-----
+)");
+  ASSERT_TRUE(leaf_key);
+  bssl::UniquePtr<X509> leaf_public = CertFromPEM(R"(
+-----BEGIN CERTIFICATE-----
+MIIBaDCCAQ6gAwIBAgIBAjAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdUZXN0IENB
+MCAXDTAwMDEwMTAwMDAwMFoYDzIwOTkwMTAxMDAwMDAwWjAZMRcwFQYDVQQDEw5w
+dWJsaWMuZXhhbXBsZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE2uSHFpsAsO
+Umse2+S4PC8JZQX+hUKGvo8MhMeLWUOUnO9VPhpcEfzuwfafZRXnOnHXc0eSrVsg
+Lx5FPx44h7SjTDBKMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU8On7ZHsQCOqO
+K+RZ7SSHyxlOReMwGQYDVR0RBBIwEIIOcHVibGljLmV4YW1wbGUwCgYIKoZIzj0E
+AwIDSAAwRQIhANqZRhDR/+QL05hsWXMYEwaiHifd9iakKoFEhKFchcF3AiBRAeXw
+wRGGT6+iPmTYM6N5/IDyAb5B9Ke38O6lLEsUwA==
+-----END CERTIFICATE-----
+)");
+  ASSERT_TRUE(leaf_public);
+  bssl::UniquePtr<X509> leaf_secret = CertFromPEM(R"(
+-----BEGIN CERTIFICATE-----
+MIIBaTCCAQ6gAwIBAgIBAzAKBggqhkjOPQQDAjASMRAwDgYDVQQDEwdUZXN0IENB
+MCAXDTAwMDEwMTAwMDAwMFoYDzIwOTkwMTAxMDAwMDAwWjAZMRcwFQYDVQQDEw5z
+ZWNyZXQuZXhhbXBsZTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABE2uSHFpsAsO
+Umse2+S4PC8JZQX+hUKGvo8MhMeLWUOUnO9VPhpcEfzuwfafZRXnOnHXc0eSrVsg
+Lx5FPx44h7SjTDBKMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAU8On7ZHsQCOqO
+K+RZ7SSHyxlOReMwGQYDVR0RBBIwEIIOc2VjcmV0LmV4YW1wbGUwCgYIKoZIzj0E
+AwIDSQAwRgIhAPQdIz1xCFkc9WuSkxOxJDpywZiEp9SnKcxJ9nwrlRp3AiEA+O3+
+XRqE7XFhHL+7TNC2a9OOAjQsEF137YPWo+rhgko=
+-----END CERTIFICATE-----
+)");
+  ASSERT_TRUE(leaf_secret);
+
+  // Use different config IDs so that fuzzer mode, which breaks trial
+  // decryption, will observe the key mismatch.
+  bssl::UniquePtr<SSL_ECH_KEYS> keys = MakeTestECHKeys(/*config_id=*/1);
+  ASSERT_TRUE(keys);
+  bssl::UniquePtr<SSL_ECH_KEYS> wrong_keys = MakeTestECHKeys(/*config_id=*/2);
+  ASSERT_TRUE(wrong_keys);
+  bssl::UniquePtr<SSL_CTX> server_ctx =
+      CreateContextWithTestCertificate(TLS_method());
+  ASSERT_TRUE(server_ctx);
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(client_ctx);
+
+  // Configure the client to verify certificates and expect the secret name.
+  // This is the name the client is trying to connect to. If ECH is rejected,
+  // BoringSSL will internally override this setting with the public name.
+  bssl::UniquePtr<X509_STORE> store(X509_STORE_new());
+  ASSERT_TRUE(store);
+  ASSERT_TRUE(X509_STORE_add_cert(store.get(), root.get()));
+  SSL_CTX_set_cert_store(client_ctx.get(), store.release());
+  SSL_CTX_set_verify(client_ctx.get(), SSL_VERIFY_PEER, nullptr);
+  static const char kSecretName[] = "secret.example";
+  ASSERT_TRUE(X509_VERIFY_PARAM_set1_host(SSL_CTX_get0_param(client_ctx.get()),
+                                          kSecretName, strlen(kSecretName)));
+
+  // For simplicity, we only run through a pair of representative scenarios here
+  // and rely on runner.go to verify that |SSL_get0_ech_name_override| behaves
+  // correctly.
+  for (bool accept_ech : {false, true}) {
+    SCOPED_TRACE(accept_ech);
+    for (bool use_leaf_secret : {false, true}) {
+      SCOPED_TRACE(use_leaf_secret);
+
+      // The server will reject ECH when configured with the wrong keys.
+      ASSERT_TRUE(SSL_CTX_set1_ech_keys(
+          server_ctx.get(), accept_ech ? keys.get() : wrong_keys.get()));
+
+      bssl::UniquePtr<SSL> client, server;
+      ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                        server_ctx.get()));
+      ASSERT_TRUE(InstallECHConfigList(client.get(), keys.get()));
+
+      // Configure the server with the selected certificate.
+      ASSERT_TRUE(SSL_use_certificate(server.get(), use_leaf_secret
+                                                        ? leaf_secret.get()
+                                                        : leaf_public.get()));
+      ASSERT_TRUE(SSL_use_PrivateKey(server.get(), leaf_key.get()));
+
+      // The handshake may fail due to name mismatch or ECH reject. We check
+      // |SSL_get_verify_result| to confirm the handshake got far enough.
+      CompleteHandshakes(client.get(), server.get());
+      EXPECT_EQ(accept_ech == use_leaf_secret ? X509_V_OK
+                                              : X509_V_ERR_HOSTNAME_MISMATCH,
+                SSL_get_verify_result(client.get()));
+    }
+  }
+}
+
+#if defined(OPENSSL_THREADS)
+// Test that the server ECH config can be swapped out while the |SSL_CTX| is
+// in use on other threads. This test is intended to be run with TSan.
+TEST(SSLTest, ECHThreads) {
+  // Generate a pair of ECHConfigs.
+  bssl::ScopedEVP_HPKE_KEY key1;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key1.get(), EVP_hpke_x25519_hkdf_sha256()));
+  uint8_t *ech_config1;
+  size_t ech_config1_len;
+  ASSERT_TRUE(SSL_marshal_ech_config(&ech_config1, &ech_config1_len,
+                                     /*config_id=*/1, key1.get(),
+                                     "public.example", 16));
+  bssl::UniquePtr<uint8_t> free_ech_config1(ech_config1);
+  bssl::ScopedEVP_HPKE_KEY key2;
+  ASSERT_TRUE(EVP_HPKE_KEY_generate(key2.get(), EVP_hpke_x25519_hkdf_sha256()));
+  uint8_t *ech_config2;
+  size_t ech_config2_len;
+  ASSERT_TRUE(SSL_marshal_ech_config(&ech_config2, &ech_config2_len,
+                                     /*config_id=*/2, key2.get(),
+                                     "public.example", 16));
+  bssl::UniquePtr<uint8_t> free_ech_config2(ech_config2);
+
+  // |keys1| contains the first config. |keys12| contains both.
+  bssl::UniquePtr<SSL_ECH_KEYS> keys1(SSL_ECH_KEYS_new());
+  ASSERT_TRUE(keys1);
+  ASSERT_TRUE(SSL_ECH_KEYS_add(keys1.get(), /*is_retry_config=*/1, ech_config1,
+                               ech_config1_len, key1.get()));
+  bssl::UniquePtr<SSL_ECH_KEYS> keys12(SSL_ECH_KEYS_new());
+  ASSERT_TRUE(keys12);
+  ASSERT_TRUE(SSL_ECH_KEYS_add(keys12.get(), /*is_retry_config=*/1, ech_config2,
+                               ech_config2_len, key2.get()));
+  ASSERT_TRUE(SSL_ECH_KEYS_add(keys12.get(), /*is_retry_config=*/0, ech_config1,
+                               ech_config1_len, key1.get()));
+
+  bssl::UniquePtr<SSL_CTX> server_ctx =
+      CreateContextWithTestCertificate(TLS_method());
+  ASSERT_TRUE(SSL_CTX_set1_ech_keys(server_ctx.get(), keys1.get()));
+
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(client_ctx);
+  bssl::UniquePtr<SSL> client, server;
+  ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                    server_ctx.get()));
+  ASSERT_TRUE(InstallECHConfigList(client.get(), keys1.get()));
+
+  // In parallel, complete the connection and reconfigure the ECHConfig. Note
+  // |keys12| supports all the keys in |keys1|, so the handshake should complete
+  // the same whichever the server uses.
+  std::vector<std::thread> threads;
+  threads.emplace_back([&] {
+    ASSERT_TRUE(CompleteHandshakes(client.get(), server.get()));
+    EXPECT_TRUE(SSL_ech_accepted(client.get()));
+    EXPECT_TRUE(SSL_ech_accepted(server.get()));
+  });
+  threads.emplace_back([&] {
+    EXPECT_TRUE(SSL_CTX_set1_ech_keys(server_ctx.get(), keys12.get()));
+  });
+  for (auto &thread : threads) {
+    thread.join();
+  }
+}
+#endif  // OPENSSL_THREADS
+
 static void AppendSession(SSL_SESSION *session, void *arg) {
   std::vector<SSL_SESSION*> *out =
       reinterpret_cast<std::vector<SSL_SESSION*>*>(arg);
@@ -1565,118 +2488,6 @@
     0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64,
 };
 
-static bool CompleteHandshakes(SSL *client, SSL *server) {
-  // Drive both their handshakes to completion.
-  for (;;) {
-    int client_ret = SSL_do_handshake(client);
-    int client_err = SSL_get_error(client, client_ret);
-    if (client_err != SSL_ERROR_NONE &&
-        client_err != SSL_ERROR_WANT_READ &&
-        client_err != SSL_ERROR_WANT_WRITE &&
-        client_err != SSL_ERROR_PENDING_TICKET) {
-      fprintf(stderr, "Client error: %s\n", SSL_error_description(client_err));
-      return false;
-    }
-
-    int server_ret = SSL_do_handshake(server);
-    int server_err = SSL_get_error(server, server_ret);
-    if (server_err != SSL_ERROR_NONE &&
-        server_err != SSL_ERROR_WANT_READ &&
-        server_err != SSL_ERROR_WANT_WRITE &&
-        server_err != SSL_ERROR_PENDING_TICKET) {
-      fprintf(stderr, "Server error: %s\n", SSL_error_description(server_err));
-      return false;
-    }
-
-    if (client_ret == 1 && server_ret == 1) {
-      break;
-    }
-  }
-
-  return true;
-}
-
-static bool FlushNewSessionTickets(SSL *client, SSL *server) {
-  // NewSessionTickets are deferred on the server to |SSL_write|, and clients do
-  // not pick them up until |SSL_read|.
-  for (;;) {
-    int server_ret = SSL_write(server, nullptr, 0);
-    int server_err = SSL_get_error(server, server_ret);
-    // The server may either succeed (|server_ret| is zero) or block on write
-    // (|server_ret| is -1 and |server_err| is |SSL_ERROR_WANT_WRITE|).
-    if (server_ret > 0 ||
-        (server_ret < 0 && server_err != SSL_ERROR_WANT_WRITE)) {
-      fprintf(stderr, "Unexpected server result: %d %d\n", server_ret,
-              server_err);
-      return false;
-    }
-
-    int client_ret = SSL_read(client, nullptr, 0);
-    int client_err = SSL_get_error(client, client_ret);
-    // The client must always block on read.
-    if (client_ret != -1 || client_err != SSL_ERROR_WANT_READ) {
-      fprintf(stderr, "Unexpected client result: %d %d\n", client_ret,
-              client_err);
-      return false;
-    }
-
-    // The server flushed everything it had to write.
-    if (server_ret == 0) {
-      return true;
-    }
-  }
-}
-
-struct ClientConfig {
-  SSL_SESSION *session = nullptr;
-  std::string servername;
-  bool early_data = false;
-};
-
-static bool ConnectClientAndServer(bssl::UniquePtr<SSL> *out_client,
-                                   bssl::UniquePtr<SSL> *out_server,
-                                   SSL_CTX *client_ctx, SSL_CTX *server_ctx,
-                                   const ClientConfig &config = ClientConfig(),
-                                   bool do_handshake = true,
-                                   bool shed_handshake_config = true) {
-  bssl::UniquePtr<SSL> client(SSL_new(client_ctx)), server(SSL_new(server_ctx));
-  if (!client || !server) {
-    return false;
-  }
-  if (config.early_data) {
-    SSL_set_early_data_enabled(client.get(), 1);
-  }
-  SSL_set_connect_state(client.get());
-  SSL_set_accept_state(server.get());
-
-  if (config.session) {
-    SSL_set_session(client.get(), config.session);
-  }
-  if (!config.servername.empty() &&
-      !SSL_set_tlsext_host_name(client.get(), config.servername.c_str())) {
-    return false;
-  }
-
-  BIO *bio1, *bio2;
-  if (!BIO_new_bio_pair(&bio1, 0, &bio2, 0)) {
-    return false;
-  }
-  // SSL_set_bio takes ownership.
-  SSL_set_bio(client.get(), bio1, bio1);
-  SSL_set_bio(server.get(), bio2, bio2);
-
-  SSL_set_shed_handshake_config(client.get(), shed_handshake_config);
-  SSL_set_shed_handshake_config(server.get(), shed_handshake_config);
-
-  if (do_handshake && !CompleteHandshakes(client.get(), server.get())) {
-    return false;
-  }
-
-  *out_client = std::move(client);
-  *out_server = std::move(server);
-  return true;
-}
-
 // SSLVersionTest executes its test cases under all available protocol versions.
 // Test cases call |Connect| to create a connection using context objects with
 // the protocol version fixed to the current version under test.
@@ -1714,7 +2525,7 @@
 
   bool Connect(const ClientConfig &config = ClientConfig()) {
     return ConnectClientAndServer(&client_, &server_, client_ctx_.get(),
-                                  server_ctx_.get(), config, true,
+                                  server_ctx_.get(), config,
                                   shed_handshake_config_);
   }
 
@@ -1783,7 +2594,7 @@
   }
   ASSERT_TRUE(Connect());
 
-  // Shut down half the connection. SSL_shutdown will return 0 to signal only
+  // Shut down half the connection. |SSL_shutdown| will return 0 to signal only
   // one side has shut down.
   ASSERT_EQ(SSL_shutdown(client_.get()), 0);
 
@@ -1804,18 +2615,223 @@
   EXPECT_EQ(SSL_shutdown(client_.get()), 1);
 }
 
-TEST(SSLTest, SessionDuplication) {
+// Test that, after calling |SSL_shutdown|, |SSL_write| fails.
+TEST_P(SSLVersionTest, WriteAfterShutdown) {
+  ASSERT_TRUE(Connect());
+
+  for (SSL *ssl : {client_.get(), server_.get()}) {
+    SCOPED_TRACE(SSL_is_server(ssl) ? "server" : "client");
+
+    bssl::UniquePtr<BIO> mem(BIO_new(BIO_s_mem()));
+    ASSERT_TRUE(mem);
+    SSL_set0_wbio(ssl, bssl::UpRef(mem).release());
+
+    // Shut down half the connection. |SSL_shutdown| will return 0 to signal
+    // only one side has shut down.
+    ASSERT_EQ(SSL_shutdown(ssl), 0);
+
+    // |ssl| should have written an alert to the transport.
+    const uint8_t *unused;
+    size_t len;
+    ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len));
+    EXPECT_NE(0u, len);
+    EXPECT_TRUE(BIO_reset(mem.get()));
+
+    // Writing should fail.
+    EXPECT_EQ(-1, SSL_write(ssl, "a", 1));
+
+    // Nothing should be written to the transport.
+    ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len));
+    EXPECT_EQ(0u, len);
+  }
+}
+
+// Test that, after sending a fatal alert in a failed |SSL_read|, |SSL_write|
+// fails.
+TEST_P(SSLVersionTest, WriteAfterReadSentFatalAlert) {
+  // Decryption failures are not fatal in DTLS.
+  if (is_dtls()) {
+    return;
+  }
+
+  ASSERT_TRUE(Connect());
+
+  // Save the write |BIO|s as the test will overwrite them.
+  bssl::UniquePtr<BIO> client_wbio = bssl::UpRef(SSL_get_wbio(client_.get()));
+  bssl::UniquePtr<BIO> server_wbio = bssl::UpRef(SSL_get_wbio(server_.get()));
+
+  for (bool test_server : {false, true}) {
+    SCOPED_TRACE(test_server ? "server" : "client");
+    SSL *ssl = test_server ? server_.get() : client_.get();
+    BIO *other_wbio = test_server ? client_wbio.get() : server_wbio.get();
+
+    bssl::UniquePtr<BIO> mem(BIO_new(BIO_s_mem()));
+    ASSERT_TRUE(mem);
+    SSL_set0_wbio(ssl, bssl::UpRef(mem).release());
+
+    // Read an invalid record from the peer.
+    static const uint8_t kInvalidRecord[] = "invalid record";
+    EXPECT_EQ(int{sizeof(kInvalidRecord)},
+              BIO_write(other_wbio, kInvalidRecord, sizeof(kInvalidRecord)));
+    char buf[256];
+    EXPECT_EQ(-1, SSL_read(ssl, buf, sizeof(buf)));
+
+    // |ssl| should have written an alert to the transport.
+    const uint8_t *unused;
+    size_t len;
+    ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len));
+    EXPECT_NE(0u, len);
+    EXPECT_TRUE(BIO_reset(mem.get()));
+
+    // Writing should fail.
+    EXPECT_EQ(-1, SSL_write(ssl, "a", 1));
+
+    // Nothing should be written to the transport.
+    ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len));
+    EXPECT_EQ(0u, len);
+  }
+}
+
+// Test that, after sending a fatal alert from the handshake, |SSL_write| fails.
+TEST_P(SSLVersionTest, WriteAfterHandshakeSentFatalAlert) {
+  for (bool test_server : {false, true}) {
+    SCOPED_TRACE(test_server ? "server" : "client");
+
+    bssl::UniquePtr<SSL> ssl(
+        SSL_new(test_server ? server_ctx_.get() : client_ctx_.get()));
+    ASSERT_TRUE(ssl);
+    if (test_server) {
+      SSL_set_accept_state(ssl.get());
+    } else {
+      SSL_set_connect_state(ssl.get());
+    }
+
+    std::vector<uint8_t> invalid;
+    if (is_dtls()) {
+      // In DTLS, invalid records are discarded. To cause the handshake to fail,
+      // use a valid handshake record with invalid contents.
+      invalid.push_back(SSL3_RT_HANDSHAKE);
+      invalid.push_back(DTLS1_VERSION >> 8);
+      invalid.push_back(DTLS1_VERSION & 0xff);
+      // epoch and sequence_number
+      for (int i = 0; i < 8; i++) {
+        invalid.push_back(0);
+      }
+      // A one-byte fragment is invalid.
+      invalid.push_back(0);
+      invalid.push_back(1);
+      // Arbitrary contents.
+      invalid.push_back(0);
+    } else {
+      invalid = {'i', 'n', 'v', 'a', 'l', 'i', 'd'};
+    }
+    bssl::UniquePtr<BIO> rbio(
+        BIO_new_mem_buf(invalid.data(), invalid.size()));
+    ASSERT_TRUE(rbio);
+    SSL_set0_rbio(ssl.get(), rbio.release());
+
+    bssl::UniquePtr<BIO> mem(BIO_new(BIO_s_mem()));
+    ASSERT_TRUE(mem);
+    SSL_set0_wbio(ssl.get(), bssl::UpRef(mem).release());
+
+    // The handshake should fail.
+    EXPECT_EQ(-1, SSL_do_handshake(ssl.get()));
+    EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(ssl.get(), -1));
+    uint32_t err = ERR_get_error();
+
+    // |ssl| should have written an alert (and, in the client's case, a
+    // ClientHello) to the transport.
+    const uint8_t *unused;
+    size_t len;
+    ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len));
+    EXPECT_NE(0u, len);
+    EXPECT_TRUE(BIO_reset(mem.get()));
+
+    // Writing should fail, with the same error as the handshake.
+    EXPECT_EQ(-1, SSL_write(ssl.get(), "a", 1));
+    EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(ssl.get(), -1));
+    EXPECT_EQ(err, ERR_get_error());
+
+    // Nothing should be written to the transport.
+    ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len));
+    EXPECT_EQ(0u, len);
+  }
+}
+
+// Test that, after seeing TLS 1.2 in response to early data, |SSL_write|
+// continues to report |SSL_R_WRONG_VERSION_ON_EARLY_DATA|. See
+// https://crbug.com/1078515.
+TEST(SSLTest, WriteAfterWrongVersionOnEarlyData) {
+  // Set up some 0-RTT-enabled contexts.
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx =
+      CreateContextWithTestCertificate(TLS_method());
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
+  SSL_CTX_set_early_data_enabled(client_ctx.get(), 1);
+  SSL_CTX_set_early_data_enabled(server_ctx.get(), 1);
+  SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
+  SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
+  // Get an early-data-capable session.
+  bssl::UniquePtr<SSL_SESSION> session =
+      CreateClientSession(client_ctx.get(), server_ctx.get());
+  ASSERT_TRUE(session);
+  EXPECT_TRUE(SSL_SESSION_early_data_capable(session.get()));
+
+  // Offer the session to the server, but now the server speaks TLS 1.2.
+  bssl::UniquePtr<SSL> client, server;
+  ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                    server_ctx.get()));
+  SSL_set_session(client.get(), session.get());
+  EXPECT_TRUE(SSL_set_max_proto_version(server.get(), TLS1_2_VERSION));
+
+  // The client handshake initially succeeds in the early data state.
+  EXPECT_EQ(1, SSL_do_handshake(client.get()));
+  EXPECT_TRUE(SSL_in_early_data(client.get()));
+
+  // The server processes the ClientHello and negotiates TLS 1.2.
+  EXPECT_EQ(-1, SSL_do_handshake(server.get()));
+  EXPECT_EQ(SSL_ERROR_WANT_READ, SSL_get_error(server.get(), -1));
+  EXPECT_EQ(TLS1_2_VERSION, SSL_version(server.get()));
+
+  // Capture the client's output.
+  bssl::UniquePtr<BIO> mem(BIO_new(BIO_s_mem()));
+  ASSERT_TRUE(mem);
+  SSL_set0_wbio(client.get(), bssl::UpRef(mem).release());
+
+  // The client processes the ServerHello and fails.
+  EXPECT_EQ(-1, SSL_do_handshake(client.get()));
+  EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(client.get(), -1));
+  uint32_t err = ERR_get_error();
+  EXPECT_EQ(ERR_LIB_SSL, ERR_GET_LIB(err));
+  EXPECT_EQ(SSL_R_WRONG_VERSION_ON_EARLY_DATA, ERR_GET_REASON(err));
+
+  // The client should have written an alert to the transport.
+  const uint8_t *unused;
+  size_t len;
+  ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len));
+  EXPECT_NE(0u, len);
+  EXPECT_TRUE(BIO_reset(mem.get()));
+
+  // Writing should fail, with the same error as the handshake.
+  EXPECT_EQ(-1, SSL_write(client.get(), "a", 1));
+  EXPECT_EQ(SSL_ERROR_SSL, SSL_get_error(client.get(), -1));
+  err = ERR_get_error();
+  EXPECT_EQ(ERR_LIB_SSL, ERR_GET_LIB(err));
+  EXPECT_EQ(SSL_R_WRONG_VERSION_ON_EARLY_DATA, ERR_GET_REASON(err));
+
+  // Nothing should be written to the transport.
+  ASSERT_TRUE(BIO_mem_contents(mem.get(), &unused, &len));
+  EXPECT_EQ(0u, len);
+}
+
+TEST(SSLTest, SessionDuplication) {
+  bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx =
+      CreateContextWithTestCertificate(TLS_method());
+  ASSERT_TRUE(client_ctx);
+  ASSERT_TRUE(server_ctx);
 
   bssl::UniquePtr<SSL> client, server;
   ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
@@ -2147,38 +3163,6 @@
   }
 }
 
-static bssl::UniquePtr<SSL_SESSION> g_last_session;
-
-static int SaveLastSession(SSL *ssl, SSL_SESSION *session) {
-  // Save the most recent session.
-  g_last_session.reset(session);
-  return 1;
-}
-
-static bssl::UniquePtr<SSL_SESSION> CreateClientSession(
-    SSL_CTX *client_ctx, SSL_CTX *server_ctx,
-    const ClientConfig &config = ClientConfig()) {
-  g_last_session = nullptr;
-  SSL_CTX_sess_set_new_cb(client_ctx, SaveLastSession);
-
-  // Connect client and server to get a session.
-  bssl::UniquePtr<SSL> client, server;
-  if (!ConnectClientAndServer(&client, &server, client_ctx, server_ctx,
-                              config) ||
-      !FlushNewSessionTickets(client.get(), server.get())) {
-    fprintf(stderr, "Failed to connect client and server.\n");
-    return nullptr;
-  }
-
-  SSL_CTX_sess_set_new_cb(client_ctx, nullptr);
-
-  if (!g_last_session) {
-    fprintf(stderr, "Client did not receive a session.\n");
-    return nullptr;
-  }
-  return std::move(g_last_session);
-}
-
 static void ExpectSessionReused(SSL_CTX *client_ctx, SSL_CTX *server_ctx,
                                 SSL_SESSION *session, bool want_reused) {
   bssl::UniquePtr<SSL> client, server;
@@ -2644,16 +3628,11 @@
 
 // Test that the early callback can swap the maximum version.
 TEST(SSLTest, EarlyCallbackVersionSwitch) {
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx =
+      CreateContextWithTestCertificate(TLS_method());
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
   ASSERT_TRUE(server_ctx);
   ASSERT_TRUE(client_ctx);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION));
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION));
 
@@ -3624,12 +4603,8 @@
     : public ::testing::TestWithParam<TicketAEADMethodParam> {};
 
 TEST_P(TicketAEADMethodTest, Resume) {
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  ASSERT_TRUE(cert);
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(key);
-
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx =
+      CreateContextWithTestCertificate(TLS_method());
   ASSERT_TRUE(server_ctx);
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
   ASSERT_TRUE(client_ctx);
@@ -3639,8 +4614,6 @@
   const ssl_test_ticket_aead_failure_mode failure_mode =
       testing::get<2>(GetParam());
 
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
   ASSERT_TRUE(SSL_CTX_set_min_proto_version(client_ctx.get(), version));
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), version));
   ASSERT_TRUE(SSL_CTX_set_min_proto_version(server_ctx.get(), version));
@@ -3784,17 +4757,10 @@
 
 TEST(SSLTest, SealRecord) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLSv1_2_method())),
-      server_ctx(SSL_CTX_new(TLSv1_2_method()));
+      server_ctx(CreateContextWithTestCertificate(TLSv1_2_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
-
   bssl::UniquePtr<SSL> client, server;
   ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
                                      server_ctx.get()));
@@ -3827,17 +4793,10 @@
 
 TEST(SSLTest, SealRecordInPlace) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLSv1_2_method())),
-      server_ctx(SSL_CTX_new(TLSv1_2_method()));
+      server_ctx(CreateContextWithTestCertificate(TLSv1_2_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
-
   bssl::UniquePtr<SSL> client, server;
   ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
                                      server_ctx.get()));
@@ -3865,17 +4824,10 @@
 
 TEST(SSLTest, SealRecordTrailingData) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLSv1_2_method())),
-      server_ctx(SSL_CTX_new(TLSv1_2_method()));
+      server_ctx(CreateContextWithTestCertificate(TLSv1_2_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
-
   bssl::UniquePtr<SSL> client, server;
   ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
                                      server_ctx.get()));
@@ -3904,17 +4856,10 @@
 
 TEST(SSLTest, SealRecordInvalidSpanSize) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLSv1_2_method())),
-      server_ctx(SSL_CTX_new(TLSv1_2_method()));
+      server_ctx(CreateContextWithTestCertificate(TLSv1_2_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
-
   bssl::UniquePtr<SSL> client, server;
   ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
                                      server_ctx.get()));
@@ -4038,18 +4983,11 @@
 
 // Test that post-handshake tickets consumed by |SSL_shutdown| are ignored.
 TEST(SSLTest, ShutdownIgnoresTickets) {
-  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> ctx(CreateContextWithTestCertificate(TLS_method()));
   ASSERT_TRUE(ctx);
   ASSERT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), TLS1_3_VERSION));
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_3_VERSION));
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(ctx.get(), key.get()));
-
   SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_BOTH);
 
   bssl::UniquePtr<SSL> client, server;
@@ -4131,17 +5069,11 @@
 
 TEST(SSLTest, CertCompression) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(
+      CreateContextWithTestCertificate(TLS_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
-
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION));
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION));
   ASSERT_TRUE(SSL_CTX_add_cert_compression_alg(
@@ -4173,7 +5105,8 @@
 TEST(SSLTest, Handoff) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
   bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
-  bssl::UniquePtr<SSL_CTX> handshaker_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> handshaker_ctx(
+      CreateContextWithTestCertificate(TLS_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
   ASSERT_TRUE(handshaker_ctx);
@@ -4185,36 +5118,28 @@
   SSL_CTX_get_tlsext_ticket_keys(server_ctx.get(), &keys, sizeof(keys));
   SSL_CTX_set_tlsext_ticket_keys(handshaker_ctx.get(), &keys, sizeof(keys));
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(handshaker_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(handshaker_ctx.get(), key.get()));
-
   for (bool early_data : {false, true}) {
     SCOPED_TRACE(early_data);
     for (bool is_resume : {false, true}) {
       SCOPED_TRACE(is_resume);
       bssl::UniquePtr<SSL> client, server;
-      auto config = ClientConfig();
-      config.early_data = early_data;
+      ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                        server_ctx.get()));
+      SSL_set_early_data_enabled(client.get(), early_data);
       if (is_resume) {
         ASSERT_TRUE(g_last_session);
-        config.session = g_last_session.get();
+        SSL_set_session(client.get(), g_last_session.get());
+        if (early_data) {
+          EXPECT_GT(g_last_session->ticket_max_early_data, 0u);
+        }
       }
-      if (is_resume && config.early_data) {
-        EXPECT_GT(g_last_session->ticket_max_early_data, 0u);
-      }
-      ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
-                                         server_ctx.get(), config,
-                                         false /* don't handshake */));
+
 
       int client_ret = SSL_do_handshake(client.get());
       int client_err = SSL_get_error(client.get(), client_ret);
 
       uint8_t byte_written;
-      if (config.early_data && is_resume) {
+      if (early_data && is_resume) {
         ASSERT_EQ(client_err, 0);
         EXPECT_TRUE(SSL_in_early_data(client.get()));
         // Attempt to write early data.
@@ -4267,7 +5192,7 @@
       ASSERT_TRUE(CompleteHandshakes(client.get(), server2.get()));
       EXPECT_EQ(is_resume, SSL_session_reused(client.get()));
 
-      if (config.early_data && is_resume) {
+      if (early_data && is_resume) {
         // In this case, one byte of early data has already been written above.
         EXPECT_TRUE(SSL_early_data_accepted(client.get()));
       } else {
@@ -4288,24 +5213,17 @@
 
 TEST(SSLTest, HandoffDeclined) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(
+      CreateContextWithTestCertificate(TLS_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
 
   SSL_CTX_set_handoff_mode(server_ctx.get(), 1);
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_2_VERSION));
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
-
   bssl::UniquePtr<SSL> client, server;
-  ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
-                                     server_ctx.get(), ClientConfig(),
-                                     false /* don't handshake */));
+  ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                    server_ctx.get()));
 
   int client_ret = SSL_do_handshake(client.get());
   int client_err = SSL_get_error(client.get(), client_ret);
@@ -4551,15 +5469,10 @@
 TEST(SSLTest, ZeroSizedWiteFlushesHandshakeMessages) {
   // If there are pending handshake mesages, an |SSL_write| of zero bytes should
   // flush them.
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(
+      CreateContextWithTestCertificate(TLS_method()));
   EXPECT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION));
   EXPECT_TRUE(SSL_CTX_set_min_proto_version(server_ctx.get(), TLS1_3_VERSION));
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
 
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
   EXPECT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION));
@@ -4660,7 +5573,8 @@
     }
   }
 
-  // Hit the maximum session cache size across multiple threads
+  // Hit the maximum session cache size across multiple threads, to test the
+  // size enforcement logic.
   size_t limit = SSL_CTX_sess_number(server_ctx_.get()) + 2;
   SSL_CTX_sess_set_cache_size(server_ctx_.get(), limit);
   {
@@ -4676,6 +5590,59 @@
     }
     EXPECT_EQ(SSL_CTX_sess_number(server_ctx_.get()), limit);
   }
+
+  // Reset the session cache, this time with a mock clock.
+  ASSERT_NO_FATAL_FAILURE(ResetContexts());
+  SSL_CTX_set_options(server_ctx_.get(), SSL_OP_NO_TICKET);
+  SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
+  SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH);
+  SSL_CTX_set_current_time_cb(server_ctx_.get(), CurrentTimeCallback);
+
+  // Make some sessions at an arbitrary start time. Then expire them.
+  g_current_time.tv_sec = 1000;
+  bssl::UniquePtr<SSL_SESSION> expired_session1 =
+      CreateClientSession(client_ctx_.get(), server_ctx_.get());
+  ASSERT_TRUE(expired_session1);
+  bssl::UniquePtr<SSL_SESSION> expired_session2 =
+      CreateClientSession(client_ctx_.get(), server_ctx_.get());
+  ASSERT_TRUE(expired_session2);
+  g_current_time.tv_sec += 100 * SSL_DEFAULT_SESSION_TIMEOUT;
+
+  session1 = CreateClientSession(client_ctx_.get(), server_ctx_.get());
+  ASSERT_TRUE(session1);
+
+  // Every 256 connections, we flush stale sessions from the session cache. Test
+  // this logic is correctly synchronized with other connection attempts.
+  static const int kNumConnections = 256;
+  {
+    std::vector<std::thread> threads;
+    threads.emplace_back([&] {
+      for (int i = 0; i < kNumConnections; i++) {
+        connect_with_session(nullptr);
+      }
+    });
+    threads.emplace_back([&] {
+      for (int i = 0; i < kNumConnections; i++) {
+        connect_with_session(nullptr);
+      }
+    });
+    threads.emplace_back([&] {
+      // Never connect with |expired_session2|. The session cache eagerly
+      // removes expired sessions when it sees them. Leaving |expired_session2|
+      // untouched ensures it is instead cleared by periodic flushing.
+      for (int i = 0; i < kNumConnections; i++) {
+        connect_with_session(expired_session1.get());
+      }
+    });
+    threads.emplace_back([&] {
+      for (int i = 0; i < kNumConnections; i++) {
+        connect_with_session(session1.get());
+      }
+    });
+    for (auto &thread : threads) {
+      thread.join();
+    }
+  }
 }
 
 TEST_P(SSLVersionTest, SessionTicketThreads) {
@@ -5055,17 +6022,10 @@
  protected:
   void SetUp() override {
     client_ctx_.reset(SSL_CTX_new(TLS_method()));
-    server_ctx_.reset(SSL_CTX_new(TLS_method()));
+    server_ctx_ = CreateContextWithTestCertificate(TLS_method());
     ASSERT_TRUE(client_ctx_);
     ASSERT_TRUE(server_ctx_);
 
-    bssl::UniquePtr<X509> cert = GetTestCertificate();
-    bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-    ASSERT_TRUE(cert);
-    ASSERT_TRUE(key);
-    ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx_.get(), cert.get()));
-    ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx_.get(), key.get()));
-
     SSL_CTX_set_min_proto_version(server_ctx_.get(), TLS1_3_VERSION);
     SSL_CTX_set_max_proto_version(server_ctx_.get(), TLS1_3_VERSION);
     SSL_CTX_set_min_proto_version(client_ctx_.get(), TLS1_3_VERSION);
@@ -6377,26 +7337,47 @@
   EXPECT_FALSE(SSL_session_reused(server.get()));
 }
 
+Span<const uint8_t> SessionIDOf(const SSL* ssl) {
+  const SSL_SESSION *session = SSL_get_session(ssl);
+  unsigned len;
+  const uint8_t *data = SSL_SESSION_get_id(session, &len);
+  return MakeConstSpan(data, len);
+}
+
+TEST_P(SSLVersionTest, TicketSessionIDsMatch) {
+  // This checks that the session IDs at client and server match after a ticket
+  // resumption. It's unclear whether this should be true, but Envoy depends
+  // on it in their tests so this will give an early signal if we break it.
+  SSL_CTX_set_session_cache_mode(client_ctx_.get(), SSL_SESS_CACHE_BOTH);
+  SSL_CTX_set_session_cache_mode(server_ctx_.get(), SSL_SESS_CACHE_BOTH);
+
+  bssl::UniquePtr<SSL_SESSION> session =
+      CreateClientSession(client_ctx_.get(), server_ctx_.get());
+
+  bssl::UniquePtr<SSL> client, server;
+  ClientConfig config;
+  config.session = session.get();
+  EXPECT_TRUE(ConnectClientAndServer(&client, &server, client_ctx_.get(),
+                                     server_ctx_.get(), config));
+  EXPECT_TRUE(SSL_session_reused(client.get()));
+  EXPECT_TRUE(SSL_session_reused(server.get()));
+
+  EXPECT_EQ(Bytes(SessionIDOf(client.get())), Bytes(SessionIDOf(server.get())));
+}
+
 TEST(SSLTest, WriteWhileExplicitRenegotiate) {
-  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> ctx(CreateContextWithTestCertificate(TLS_method()));
   ASSERT_TRUE(ctx);
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> pkey = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(pkey);
-  ASSERT_TRUE(SSL_CTX_use_certificate(ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(ctx.get(), pkey.get()));
   ASSERT_TRUE(SSL_CTX_set_min_proto_version(ctx.get(), TLS1_2_VERSION));
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(ctx.get(), TLS1_2_VERSION));
   ASSERT_TRUE(SSL_CTX_set_strict_cipher_list(
       ctx.get(), "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"));
 
   bssl::UniquePtr<SSL> client, server;
-  ASSERT_TRUE(ConnectClientAndServer(&client, &server, ctx.get(), ctx.get(),
-                                     ClientConfig(), true /* do_handshake */,
-                                     false /* don't shed handshake config */));
+  ASSERT_TRUE(CreateClientAndServer(&client, &server, ctx.get(), ctx.get()));
   SSL_set_renegotiate_mode(client.get(), ssl_renegotiate_explicit);
+  ASSERT_TRUE(CompleteHandshakes(client.get(), server.get()));
 
   static const uint8_t kInput[] = {'h', 'e', 'l', 'l', 'o'};
 
@@ -6514,17 +7495,11 @@
 
 TEST(SSLTest, CopyWithoutEarlyData) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(
+      CreateContextWithTestCertificate(TLS_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
-
   SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
   SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
   SSL_CTX_set_early_data_enabled(client_ctx.get(), 1);
@@ -6535,13 +7510,11 @@
   ASSERT_TRUE(session);
 
   // The client should attempt early data with |session|.
-  auto config = ClientConfig();
-  config.early_data = true;
-  config.session = session.get();
   bssl::UniquePtr<SSL> client, server;
-  ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
-                                     server_ctx.get(), config,
-                                     /*do_handshake=*/false));
+  ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                     server_ctx.get()));
+  SSL_set_session(client.get(), session.get());
+  SSL_set_early_data_enabled(client.get(), 1);
   ASSERT_EQ(1, SSL_do_handshake(client.get()));
   EXPECT_TRUE(SSL_in_early_data(client.get()));
 
@@ -6551,9 +7524,11 @@
       SSL_SESSION_copy_without_early_data(session.get()));
   ASSERT_TRUE(session2);
   EXPECT_NE(session.get(), session2.get());
-  config.session = session2.get();
-  ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
-                                     server_ctx.get(), config));
+  ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                    server_ctx.get()));
+  SSL_set_session(client.get(), session2.get());
+  SSL_set_early_data_enabled(client.get(), 1);
+  EXPECT_TRUE(CompleteHandshakes(client.get(), server.get()));
   EXPECT_TRUE(SSL_session_reused(client.get()));
   EXPECT_EQ(ssl_early_data_unsupported_for_session,
             SSL_get_early_data_reason(client.get()));
@@ -6567,18 +7542,15 @@
 
 TEST(SSLTest, ProcessTLS13NewSessionTicket) {
   // Configure client and server to negotiate TLS 1.3 only.
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(
+      CreateContextWithTestCertificate(TLS_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
   ASSERT_TRUE(SSL_CTX_set_min_proto_version(client_ctx.get(), TLS1_3_VERSION));
   ASSERT_TRUE(SSL_CTX_set_min_proto_version(server_ctx.get(), TLS1_3_VERSION));
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(client_ctx.get(), TLS1_3_VERSION));
   ASSERT_TRUE(SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION));
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
 
   bssl::UniquePtr<SSL> client, server;
   ASSERT_TRUE(ConnectClientAndServer(&client, &server, client_ctx.get(),
@@ -6632,17 +7604,11 @@
 
 TEST(SSLTest, BIO) {
   bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
-  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL_CTX> server_ctx(
+      CreateContextWithTestCertificate(TLS_method()));
   ASSERT_TRUE(client_ctx);
   ASSERT_TRUE(server_ctx);
 
-  bssl::UniquePtr<X509> cert = GetTestCertificate();
-  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
-  ASSERT_TRUE(cert);
-  ASSERT_TRUE(key);
-  ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
-  ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
-
   for (bool take_ownership : {true, false}) {
     // For simplicity, get the handshake out of the way first.
     bssl::UniquePtr<SSL> client, server;
@@ -6689,5 +7655,328 @@
   }
 }
 
+TEST(SSLTest, ALPNConfig) {
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(ctx);
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+  ASSERT_TRUE(cert);
+  ASSERT_TRUE(key);
+  ASSERT_TRUE(SSL_CTX_use_certificate(ctx.get(), cert.get()));
+  ASSERT_TRUE(SSL_CTX_use_PrivateKey(ctx.get(), key.get()));
+
+  // Set up some machinery to check the configured ALPN against what is actually
+  // sent over the wire. Note that the ALPN callback is only called when the
+  // client offers ALPN.
+  std::vector<uint8_t> observed_alpn;
+  SSL_CTX_set_alpn_select_cb(
+      ctx.get(),
+      [](SSL *ssl, const uint8_t **out, uint8_t *out_len, const uint8_t *in,
+         unsigned in_len, void *arg) -> int {
+        std::vector<uint8_t> *observed_alpn_ptr =
+            static_cast<std::vector<uint8_t> *>(arg);
+        observed_alpn_ptr->assign(in, in + in_len);
+        return SSL_TLSEXT_ERR_NOACK;
+      },
+      &observed_alpn);
+  auto check_alpn_proto = [&](Span<const uint8_t> expected) {
+    observed_alpn.clear();
+    bssl::UniquePtr<SSL> client, server;
+    EXPECT_TRUE(ConnectClientAndServer(&client, &server, ctx.get(), ctx.get()));
+    EXPECT_EQ(Bytes(expected), Bytes(observed_alpn));
+  };
+
+  // Note that |SSL_CTX_set_alpn_protos|'s return value is reversed.
+  static const uint8_t kValidList[] = {0x03, 'f', 'o', 'o',
+                                       0x03, 'b', 'a', 'r'};
+  EXPECT_EQ(0,
+            SSL_CTX_set_alpn_protos(ctx.get(), kValidList, sizeof(kValidList)));
+  check_alpn_proto(kValidList);
+
+  // Invalid lists are rejected.
+  static const uint8_t kInvalidList[] = {0x04, 'f', 'o', 'o'};
+  EXPECT_EQ(1, SSL_CTX_set_alpn_protos(ctx.get(), kInvalidList,
+                                       sizeof(kInvalidList)));
+
+  // Empty lists are valid and are interpreted as disabling ALPN.
+  EXPECT_EQ(0, SSL_CTX_set_alpn_protos(ctx.get(), nullptr, 0));
+  check_alpn_proto({});
+}
+
+// Test that the key usage checker can correctly handle issuerUID and
+// subjectUID. See https://crbug.com/1199744.
+TEST(SSLTest, KeyUsageWithUIDs) {
+  static const char kGoodKeyUsage[] = R"(
+-----BEGIN CERTIFICATE-----
+MIIB7DCCAZOgAwIBAgIJANlMBNpJfb/rMAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT
+AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
+aXRzIFB0eSBMdGQwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjBFMQsw
+CQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJu
+ZXQgV2lkZ2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp
+4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsW
+Ghz1HX7xlC1Lz3IiwYEEABI0VoIEABI0VqNgMF4wHQYDVR0OBBYEFKuE0qyrlfCC
+ThZ4B1VXX+QmjYLRMB8GA1UdIwQYMBaAFKuE0qyrlfCCThZ4B1VXX+QmjYLRMA4G
+A1UdDwEB/wQEAwIHgDAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0cAMEQCIEWJ
+34EcqW5MHwLIA1hZ2Tj/jV2QjN02KLxis9mFsqDKAiAMlMTkzsM51vVs9Ohqa+Rc
+4Z7qDhjIhiF4dM0uEDYRVA==
+-----END CERTIFICATE-----
+)";
+  static const char kBadKeyUsage[] = R"(
+-----BEGIN CERTIFICATE-----
+MIIB7jCCAZOgAwIBAgIJANlMBNpJfb/rMAoGCCqGSM49BAMCMEUxCzAJBgNVBAYT
+AkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRn
+aXRzIFB0eSBMdGQwHhcNMTQwNDIzMjMyMTU3WhcNMTQwNTIzMjMyMTU3WjBFMQsw
+CQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJu
+ZXQgV2lkZ2l0cyBQdHkgTHRkMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE5itp
+4r9ln5e+Lx4NlIpM1Zdrt6keDUb73ampHp3culoB59aXqAoY+cPEox5W4nyDSNsW
+Ghz1HX7xlC1Lz3IiwYEEABI0VoIEABI0VqNgMF4wHQYDVR0OBBYEFKuE0qyrlfCC
+ThZ4B1VXX+QmjYLRMB8GA1UdIwQYMBaAFKuE0qyrlfCCThZ4B1VXX+QmjYLRMA4G
+A1UdDwEB/wQEAwIDCDAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMCA0kAMEYCIQC6
+taYBUDu2gcZC6EMk79FBHArYI0ucF+kzvETegZCbBAIhANtObFec5gtso/47moPD
+RHrQbWsFUakETXL9QMlegh5t
+-----END CERTIFICATE-----
+)";
+
+  bssl::UniquePtr<X509> good = CertFromPEM(kGoodKeyUsage);
+  ASSERT_TRUE(good);
+  bssl::UniquePtr<X509> bad = CertFromPEM(kBadKeyUsage);
+  ASSERT_TRUE(bad);
+
+  // We check key usage when configuring EC certificates to distinguish ECDSA
+  // and ECDH.
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(ctx);
+  EXPECT_TRUE(SSL_CTX_use_certificate(ctx.get(), good.get()));
+  EXPECT_FALSE(SSL_CTX_use_certificate(ctx.get(), bad.get()));
+}
+
+// Test that |SSL_can_release_private_key| reports true as early as expected.
+// The internal asserts in the library check we do not report true too early.
+TEST(SSLTest, CanReleasePrivateKey) {
+  bssl::UniquePtr<SSL_CTX> client_ctx =
+      CreateContextWithTestCertificate(TLS_method());
+  ASSERT_TRUE(client_ctx);
+  SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
+
+  // Note this assumes the transport buffer is large enough to fit the client
+  // and server first flights. We check this with |SSL_ERROR_WANT_READ|. If the
+  // transport buffer was too small it would return |SSL_ERROR_WANT_WRITE|.
+  auto check_first_server_round_trip = [&](SSL *client, SSL *server) {
+    // Write the ClientHello.
+    ASSERT_EQ(-1, SSL_do_handshake(client));
+    ASSERT_EQ(SSL_ERROR_WANT_READ, SSL_get_error(client, -1));
+
+    // Consume the ClientHello and write the server flight.
+    ASSERT_EQ(-1, SSL_do_handshake(server));
+    ASSERT_EQ(SSL_ERROR_WANT_READ, SSL_get_error(server, -1));
+
+    EXPECT_TRUE(SSL_can_release_private_key(server));
+  };
+
+  {
+    SCOPED_TRACE("TLS 1.2 ECDHE");
+    bssl::UniquePtr<SSL_CTX> server_ctx(
+        CreateContextWithTestCertificate(TLS_method()));
+    ASSERT_TRUE(server_ctx);
+    ASSERT_TRUE(
+        SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_2_VERSION));
+    ASSERT_TRUE(SSL_CTX_set_strict_cipher_list(
+        server_ctx.get(), "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"));
+    // Configure the server to request client certificates, so we can also test
+    // the client half.
+    SSL_CTX_set_custom_verify(
+        server_ctx.get(), SSL_VERIFY_PEER,
+        [](SSL *ssl, uint8_t *out_alert) { return ssl_verify_ok; });
+    bssl::UniquePtr<SSL> client, server;
+    ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                      server_ctx.get()));
+    check_first_server_round_trip(client.get(), server.get());
+
+    // Consume the server flight and write the client response. The client still
+    // has a Finished message to consume but can also release its key early.
+    ASSERT_EQ(-1, SSL_do_handshake(client.get()));
+    ASSERT_EQ(SSL_ERROR_WANT_READ, SSL_get_error(client.get(), -1));
+    EXPECT_TRUE(SSL_can_release_private_key(client.get()));
+
+    // However, a client that has not disabled renegotiation can never release
+    // the key.
+    ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                      server_ctx.get()));
+    SSL_set_renegotiate_mode(client.get(), ssl_renegotiate_freely);
+    check_first_server_round_trip(client.get(), server.get());
+    ASSERT_EQ(-1, SSL_do_handshake(client.get()));
+    ASSERT_EQ(SSL_ERROR_WANT_READ, SSL_get_error(client.get(), -1));
+    EXPECT_FALSE(SSL_can_release_private_key(client.get()));
+  }
+
+  {
+    SCOPED_TRACE("TLS 1.2 resumption");
+    bssl::UniquePtr<SSL_CTX> server_ctx(
+        CreateContextWithTestCertificate(TLS_method()));
+    ASSERT_TRUE(server_ctx);
+    ASSERT_TRUE(
+        SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_2_VERSION));
+    bssl::UniquePtr<SSL_SESSION> session =
+        CreateClientSession(client_ctx.get(), server_ctx.get());
+    ASSERT_TRUE(session);
+    bssl::UniquePtr<SSL> client, server;
+    ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                      server_ctx.get()));
+    SSL_set_session(client.get(), session.get());
+    check_first_server_round_trip(client.get(), server.get());
+  }
+
+  {
+    SCOPED_TRACE("TLS 1.3 1-RTT");
+    bssl::UniquePtr<SSL_CTX> server_ctx(
+        CreateContextWithTestCertificate(TLS_method()));
+    ASSERT_TRUE(server_ctx);
+    ASSERT_TRUE(
+        SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION));
+    bssl::UniquePtr<SSL> client, server;
+    ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                      server_ctx.get()));
+    check_first_server_round_trip(client.get(), server.get());
+  }
+
+  {
+    SCOPED_TRACE("TLS 1.3 resumption");
+    bssl::UniquePtr<SSL_CTX> server_ctx(
+        CreateContextWithTestCertificate(TLS_method()));
+    ASSERT_TRUE(server_ctx);
+    ASSERT_TRUE(
+        SSL_CTX_set_max_proto_version(server_ctx.get(), TLS1_3_VERSION));
+    bssl::UniquePtr<SSL_SESSION> session =
+        CreateClientSession(client_ctx.get(), server_ctx.get());
+    ASSERT_TRUE(session);
+    bssl::UniquePtr<SSL> client, server;
+    ASSERT_TRUE(CreateClientAndServer(&client, &server, client_ctx.get(),
+                                      server_ctx.get()));
+    SSL_set_session(client.get(), session.get());
+    check_first_server_round_trip(client.get(), server.get());
+  }
+}
+
+// GetExtensionOrder sets |*out| to the list of extensions a client attached to
+// |ctx| will send in the ClientHello. If |ech_keys| is non-null, the client
+// will offer ECH with the public component. If |decrypt_ech| is true, |*out|
+// will be set to the ClientHelloInner's extensions, rather than
+// ClientHelloOuter.
+static bool GetExtensionOrder(SSL_CTX *client_ctx, std::vector<uint16_t> *out,
+                              SSL_ECH_KEYS *ech_keys, bool decrypt_ech) {
+  struct AppData {
+    std::vector<uint16_t> *out;
+    bool decrypt_ech;
+    bool callback_done = false;
+  };
+  AppData app_data;
+  app_data.out = out;
+  app_data.decrypt_ech = decrypt_ech;
+
+  bssl::UniquePtr<SSL_CTX> server_ctx =
+      CreateContextWithTestCertificate(TLS_method());
+  if (!server_ctx ||  //
+      !SSL_CTX_set_app_data(server_ctx.get(), &app_data) ||
+      (decrypt_ech && !SSL_CTX_set1_ech_keys(server_ctx.get(), ech_keys))) {
+    return false;
+  }
+
+  // Configure the server to record the ClientHello extension order. We use a
+  // server rather than |GetClientHello| so it can decrypt ClientHelloInner.
+  SSL_CTX_set_select_certificate_cb(
+      server_ctx.get(),
+      [](const SSL_CLIENT_HELLO *client_hello) -> ssl_select_cert_result_t {
+        AppData *app_data_ptr = static_cast<AppData *>(
+            SSL_CTX_get_app_data(SSL_get_SSL_CTX(client_hello->ssl)));
+        EXPECT_EQ(app_data_ptr->decrypt_ech ? 1 : 0,
+                  SSL_ech_accepted(client_hello->ssl));
+
+        app_data_ptr->out->clear();
+        CBS extensions;
+        CBS_init(&extensions, client_hello->extensions,
+                 client_hello->extensions_len);
+        while (CBS_len(&extensions)) {
+          uint16_t type;
+          CBS body;
+          if (!CBS_get_u16(&extensions, &type) ||
+              !CBS_get_u16_length_prefixed(&extensions, &body)) {
+            return ssl_select_cert_error;
+          }
+          app_data_ptr->out->push_back(type);
+        }
+
+        // Don't bother completing the handshake.
+        app_data_ptr->callback_done = true;
+        return ssl_select_cert_error;
+      });
+
+  bssl::UniquePtr<SSL> client, server;
+  if (!CreateClientAndServer(&client, &server, client_ctx, server_ctx.get()) ||
+      (ech_keys != nullptr && !InstallECHConfigList(client.get(), ech_keys))) {
+    return false;
+  }
+
+  // Run the handshake far enough to process the ClientHello.
+  SSL_do_handshake(client.get());
+  SSL_do_handshake(server.get());
+  return app_data.callback_done;
+}
+
+// Test that, when extension permutation is enabled, the ClientHello extension
+// order changes, both with and without ECH, and in both ClientHelloInner and
+// ClientHelloOuter.
+TEST(SSLTest, PermuteExtensions) {
+  bssl::UniquePtr<SSL_ECH_KEYS> keys = MakeTestECHKeys();
+  ASSERT_TRUE(keys);
+  for (bool offer_ech : {false, true}) {
+    SCOPED_TRACE(offer_ech);
+    SSL_ECH_KEYS *maybe_keys = offer_ech ? keys.get() : nullptr;
+    for (bool decrypt_ech : {false, true}) {
+      SCOPED_TRACE(decrypt_ech);
+      if (!offer_ech && decrypt_ech) {
+        continue;
+      }
+
+      // When extension permutation is disabled, the order should be consistent.
+      bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+      ASSERT_TRUE(ctx);
+      std::vector<uint16_t> order1, order2;
+      ASSERT_TRUE(
+          GetExtensionOrder(ctx.get(), &order1, maybe_keys, decrypt_ech));
+      ASSERT_TRUE(
+          GetExtensionOrder(ctx.get(), &order2, maybe_keys, decrypt_ech));
+      EXPECT_EQ(order1, order2);
+
+      ctx.reset(SSL_CTX_new(TLS_method()));
+      ASSERT_TRUE(ctx);
+      SSL_CTX_set_permute_extensions(ctx.get(), 1);
+
+      // When extension permutation is enabled, each ClientHello should have a
+      // different order.
+      //
+      // This test is inherently flaky, so we run it multiple times. We send at
+      // least five extensions by default from TLS 1.3: supported_versions,
+      // key_share, supported_groups, psk_key_exchange_modes, and
+      // signature_algorithms. That means the probability of a false negative is
+      // at most 1/120. Repeating the test 14 times lowers false negative rate
+      // to under 2^-96.
+      ASSERT_TRUE(
+          GetExtensionOrder(ctx.get(), &order1, maybe_keys, decrypt_ech));
+      EXPECT_GE(order1.size(), 5u);
+      static const int kNumIterations = 14;
+      bool passed = false;
+      for (int i = 0; i < kNumIterations; i++) {
+        ASSERT_TRUE(
+            GetExtensionOrder(ctx.get(), &order2, maybe_keys, decrypt_ech));
+        if (order1 != order2) {
+          passed = true;
+          break;
+        }
+      }
+      EXPECT_TRUE(passed) << "Extensions were not permuted";
+    }
+  }
+}
+
 }  // namespace
 BSSL_NAMESPACE_END
diff --git a/deps/boringssl/src/ssl/ssl_transcript.cc b/deps/boringssl/src/ssl/ssl_transcript.cc
index 0bc13b9..58fd21e 100644
--- a/deps/boringssl/src/ssl/ssl_transcript.cc
+++ b/deps/boringssl/src/ssl/ssl_transcript.cc
@@ -158,20 +158,14 @@
   return true;
 }
 
-// InitDigestWithData calls |EVP_DigestInit_ex| on |ctx| with |md| and then
-// writes the data in |buf| to it.
-static bool InitDigestWithData(EVP_MD_CTX *ctx, const EVP_MD *md,
-                               const BUF_MEM *buf) {
-  if (!EVP_DigestInit_ex(ctx, md, NULL)) {
-    return false;
-  }
-  EVP_DigestUpdate(ctx, buf->data, buf->length);
-  return true;
-}
-
 bool SSLTranscript::InitHash(uint16_t version, const SSL_CIPHER *cipher) {
   const EVP_MD *md = ssl_get_handshake_digest(version, cipher);
-  return InitDigestWithData(hash_.get(), md, buffer_.get());
+  if (Digest() == md) {
+    // No need to re-hash the buffer.
+    return true;
+  }
+  return EVP_DigestInit_ex(hash_.get(), md, nullptr) &&
+         EVP_DigestUpdate(hash_.get(), buffer_->data, buffer_->length);
 }
 
 void SSLTranscript::FreeBuffer() {
@@ -206,7 +200,8 @@
   return true;
 }
 
-bool SSLTranscript::CopyToHashContext(EVP_MD_CTX *ctx, const EVP_MD *digest) {
+bool SSLTranscript::CopyToHashContext(EVP_MD_CTX *ctx,
+                                      const EVP_MD *digest) const {
   const EVP_MD *transcript_digest = Digest();
   if (transcript_digest != nullptr &&
       EVP_MD_type(transcript_digest) == EVP_MD_type(digest)) {
@@ -237,7 +232,7 @@
   return true;
 }
 
-bool SSLTranscript::GetHash(uint8_t *out, size_t *out_len) {
+bool SSLTranscript::GetHash(uint8_t *out, size_t *out_len) const {
   ScopedEVP_MD_CTX ctx;
   unsigned len;
   if (!EVP_MD_CTX_copy_ex(ctx.get(), hash_.get()) ||
@@ -250,7 +245,7 @@
 
 bool SSLTranscript::GetFinishedMAC(uint8_t *out, size_t *out_len,
                                    const SSL_SESSION *session,
-                                   bool from_server) {
+                                   bool from_server) const {
   static const char kClientLabel[] = "client finished";
   static const char kServerLabel[] = "server finished";
   auto label = from_server
diff --git a/deps/boringssl/src/ssl/ssl_versions.cc b/deps/boringssl/src/ssl/ssl_versions.cc
index 3bbb4e3..df499c7 100644
--- a/deps/boringssl/src/ssl/ssl_versions.cc
+++ b/deps/boringssl/src/ssl/ssl_versions.cc
@@ -260,8 +260,8 @@
   return version;
 }
 
-bool ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) {
-  SSL *const ssl = hs->ssl;
+bool ssl_supports_version(const SSL_HANDSHAKE *hs, uint16_t version) {
+  const SSL *const ssl = hs->ssl;
   uint16_t protocol_version;
   if (!ssl_method_supports_version(ssl->method, version) ||
       !ssl_protocol_version_from_wire(&protocol_version, version) ||
@@ -273,9 +273,13 @@
   return true;
 }
 
-bool ssl_add_supported_versions(SSL_HANDSHAKE *hs, CBB *cbb) {
+bool ssl_add_supported_versions(const SSL_HANDSHAKE *hs, CBB *cbb,
+                                uint16_t extra_min_version) {
   for (uint16_t version : get_method_versions(hs->ssl->method)) {
+    uint16_t protocol_version;
     if (ssl_supports_version(hs, version) &&
+        ssl_protocol_version_from_wire(&protocol_version, version) &&
+        protocol_version >= extra_min_version &&  //
         !CBB_add_u16(cbb, version)) {
       return false;
     }
diff --git a/deps/boringssl/src/ssl/ssl_x509.cc b/deps/boringssl/src/ssl/ssl_x509.cc
index cda7611..680f253 100644
--- a/deps/boringssl/src/ssl/ssl_x509.cc
+++ b/deps/boringssl/src/ssl/ssl_x509.cc
@@ -368,25 +368,34 @@
     return false;
   }
 
-  SSL_CTX *ssl_ctx = hs->ssl->ctx.get();
+  SSL *const ssl = hs->ssl;
+  SSL_CTX *ssl_ctx = ssl->ctx.get();
   X509_STORE *verify_store = ssl_ctx->cert_store;
   if (hs->config->cert->verify_store != nullptr) {
     verify_store = hs->config->cert->verify_store;
   }
 
   X509 *leaf = sk_X509_value(cert_chain, 0);
-  ScopedX509_STORE_CTX ctx;
-  if (!X509_STORE_CTX_init(ctx.get(), verify_store, leaf, cert_chain) ||
-      !X509_STORE_CTX_set_ex_data(
-          ctx.get(), SSL_get_ex_data_X509_STORE_CTX_idx(), hs->ssl) ||
+  const char *name;
+  size_t name_len;
+  SSL_get0_ech_name_override(ssl, &name, &name_len);
+  UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new());
+  if (!ctx ||
+      !X509_STORE_CTX_init(ctx.get(), verify_store, leaf, cert_chain) ||
+      !X509_STORE_CTX_set_ex_data(ctx.get(),
+                                  SSL_get_ex_data_X509_STORE_CTX_idx(), ssl) ||
       // We need to inherit the verify parameters. These can be determined by
       // the context: if its a server it will verify SSL client certificates or
       // vice versa.
-      !X509_STORE_CTX_set_default(
-          ctx.get(), hs->ssl->server ? "ssl_client" : "ssl_server") ||
+      !X509_STORE_CTX_set_default(ctx.get(),
+                                  ssl->server ? "ssl_client" : "ssl_server") ||
       // Anything non-default in "param" should overwrite anything in the ctx.
       !X509_VERIFY_PARAM_set1(X509_STORE_CTX_get0_param(ctx.get()),
-                              hs->config->param)) {
+                              hs->config->param) ||
+      // ClientHelloOuter connections use a different name.
+      (name_len != 0 &&
+       !X509_VERIFY_PARAM_set1_host(X509_STORE_CTX_get0_param(ctx.get()), name,
+                                    name_len))) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
     return false;
   }
@@ -403,11 +412,11 @@
     verify_ret = X509_verify_cert(ctx.get());
   }
 
-  session->verify_result = ctx->error;
+  session->verify_result = X509_STORE_CTX_get_error(ctx.get());
 
   // If |SSL_VERIFY_NONE|, the error is non-fatal, but we keep the result.
   if (verify_ret <= 0 && hs->config->verify_mode != SSL_VERIFY_NONE) {
-    *out_alert = SSL_alert_from_verify_result(ctx->error);
+    *out_alert = SSL_alert_from_verify_result(session->verify_result);
     return false;
   }
 
@@ -456,9 +465,9 @@
     return false;
   }
 
-  ScopedX509_STORE_CTX ctx;
-  if (!X509_STORE_CTX_init(ctx.get(), hs->ssl->ctx->cert_store, leaf.get(),
-                           NULL)) {
+  UniquePtr<X509_STORE_CTX> ctx(X509_STORE_CTX_new());
+  if (!ctx || !X509_STORE_CTX_init(ctx.get(), hs->ssl->ctx->cert_store,
+                                   leaf.get(), nullptr)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
     return false;
   }
@@ -468,9 +477,13 @@
   ERR_clear_error();
 
   // Remove the leaf from the generated chain.
-  X509_free(sk_X509_shift(ctx->chain));
+  UniquePtr<STACK_OF(X509)> chain(X509_STORE_CTX_get1_chain(ctx.get()));
+  if (!chain) {
+    return false;
+  }
+  X509_free(sk_X509_shift(chain.get()));
 
-  if (!ssl_cert_set_chain(hs->config->cert.get(), ctx->chain)) {
+  if (!ssl_cert_set_chain(hs->config->cert.get(), chain.get())) {
     return false;
   }
 
@@ -698,13 +711,6 @@
   return X509_STORE_load_locations(ctx->cert_store, ca_file, ca_dir);
 }
 
-void SSL_set_verify_result(SSL *ssl, long result) {
-  check_ssl_x509_method(ssl);
-  if (result != X509_V_OK) {
-    abort();
-  }
-}
-
 long SSL_get_verify_result(const SSL *ssl) {
   check_ssl_x509_method(ssl);
   SSL_SESSION *session = SSL_get_session(ssl);
diff --git a/deps/boringssl/src/ssl/test/bssl_shim.cc b/deps/boringssl/src/ssl/test/bssl_shim.cc
index 31c0a01..f4e7fff 100644
--- a/deps/boringssl/src/ssl/test/bssl_shim.cc
+++ b/deps/boringssl/src/ssl/test/bssl_shim.cc
@@ -66,10 +66,6 @@
 #include "test_config.h"
 #include "test_state.h"
 
-#if defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
-#define HANDSHAKER_SUPPORTED
-#endif
-
 
 #if !defined(OPENSSL_WINDOWS)
 static int closesocket(int sock) {
@@ -550,18 +546,6 @@
     }
   }
 
-  if (config->expect_token_binding_param != -1) {
-    if (!SSL_is_token_binding_negotiated(ssl)) {
-      fprintf(stderr, "no Token Binding negotiated\n");
-      return false;
-    }
-    if (SSL_get_negotiated_token_binding_param(ssl) !=
-        static_cast<uint8_t>(config->expect_token_binding_param)) {
-      fprintf(stderr, "Token Binding param mismatch\n");
-      return false;
-    }
-  }
-
   if (config->expect_extended_master_secret && !SSL_get_extms_support(ssl)) {
     fprintf(stderr, "No EMS for connection when expected\n");
     return false;
@@ -675,6 +659,33 @@
             SSL_used_hello_retry_request(ssl) ? "" : "no ");
     return false;
   }
+
+  if (config->expect_ech_accept != !!SSL_ech_accepted(ssl)) {
+    fprintf(stderr, "ECH was %saccepted, but wanted opposite.\n",
+            SSL_ech_accepted(ssl) ? "" : "not ");
+    return false;
+  }
+
+  // Test that handshake hints correctly skipped the expected operations.
+  //
+  // TODO(davidben): Add support for TLS 1.2 hints and remove the version check.
+  // Also add a check for the session cache lookup.
+  if (config->handshake_hints && !config->allow_hint_mismatch &&
+      SSL_version(ssl) == TLS1_3_VERSION) {
+    const TestState *state = GetTestState(ssl);
+    if (!SSL_used_hello_retry_request(ssl) && state->used_private_key) {
+      fprintf(
+          stderr,
+          "Performed private key operation, but hint should have skipped it\n");
+      return false;
+    }
+
+    if (state->ticket_decrypt_done) {
+      fprintf(stderr,
+              "Performed ticket decryption, but hint should have skipped it\n");
+      return false;
+    }
+  }
   return true;
 }
 
@@ -692,7 +703,7 @@
                          const TestConfig *retry_config, bool is_resume,
                          SSL_SESSION *session, SettingsWriter *writer) {
   bssl::UniquePtr<SSL> ssl = config->NewSSL(
-      ssl_ctx, session, is_resume, std::unique_ptr<TestState>(new TestState));
+      ssl_ctx, session, std::unique_ptr<TestState>(new TestState));
   if (!ssl) {
     return false;
   }
@@ -701,6 +712,17 @@
   } else {
     SSL_set_connect_state(ssl.get());
   }
+  if (config->handshake_hints) {
+#if defined(HANDSHAKER_SUPPORTED)
+    GetTestState(ssl.get())->get_handshake_hints_cb =
+        [&](const SSL_CLIENT_HELLO *client_hello) {
+          return GetHandshakeHint(ssl.get(), writer, is_resume, client_hello);
+        };
+#else
+    fprintf(stderr, "The external handshaker can only be used on Linux\n");
+    return false;
+#endif
+  }
 
   int sock = Connect(config->port);
   if (sock == -1) {
@@ -787,9 +809,44 @@
     }
 
     assert(!config->handoff);
+    config = retry_config;
     ret = DoExchange(out_session, &ssl, retry_config, is_resume, true, writer);
   }
 
+  // An ECH rejection appears as a failed connection. Note |ssl| may use a
+  // different config on ECH rejection.
+  if (config->expect_no_ech_retry_configs ||
+      !config->expect_ech_retry_configs.empty()) {
+    bssl::Span<const uint8_t> expected =
+        config->expect_no_ech_retry_configs
+            ? bssl::Span<const uint8_t>()
+            : bssl::MakeConstSpan(reinterpret_cast<const uint8_t *>(
+                                      config->expect_ech_retry_configs.data()),
+                                  config->expect_ech_retry_configs.size());
+    if (ret) {
+      fprintf(stderr, "Expected ECH rejection, but connection succeeded.\n");
+      return false;
+    }
+    uint32_t err = ERR_peek_error();
+    if (SSL_get_error(ssl.get(), -1) != SSL_ERROR_SSL ||
+        ERR_GET_LIB(err) != ERR_LIB_SSL ||
+        ERR_GET_REASON(err) != SSL_R_ECH_REJECTED) {
+      fprintf(stderr, "Expected ECH rejection, but connection succeeded.\n");
+      return false;
+    }
+    const uint8_t *retry_configs;
+    size_t retry_configs_len;
+    SSL_get0_ech_retry_configs(ssl.get(), &retry_configs, &retry_configs_len);
+    if (bssl::MakeConstSpan(retry_configs, retry_configs_len) != expected) {
+      fprintf(stderr, "ECH retry configs did not match expectations.\n");
+      // Clear the error queue. Otherwise |SSL_R_ECH_REJECTED| will be printed
+      // to stderr and the test framework will think the test had the expected
+      // expectations.
+      ERR_clear_error();
+      return false;
+    }
+  }
+
   if (!ret) {
     // Print the |SSL_get_error| code. Otherwise, some failures are silent and
     // hard to debug.
@@ -823,6 +880,7 @@
   int ret;
   SSL *ssl = ssl_uniqueptr->get();
   SSL_CTX *session_ctx = SSL_get_SSL_CTX(ssl);
+  TestState *test_state = GetTestState(ssl);
 
   if (!config->implicit_handshake) {
     if (config->handoff) {
@@ -831,6 +889,7 @@
         return false;
       }
       ssl = ssl_uniqueptr->get();
+      test_state = GetTestState(ssl);
 #else
       fprintf(stderr, "The external handshaker can only be used on Linux\n");
       return false;
@@ -875,9 +934,44 @@
       return false;
     }
 
+    if (config->early_write_after_message != 0) {
+      if (!SSL_in_early_data(ssl) || config->is_server) {
+        fprintf(stderr,
+                "-early-write-after-message only works for 0-RTT connections "
+                "on servers.\n");
+        return false;
+      }
+      if (!config->shim_writes_first || !config->async) {
+        fprintf(stderr,
+                "-early-write-after-message requires -shim-writes-first and "
+                "-async.\n");
+        return false;
+      }
+      // Run the handshake until the specified message. Note that, if a
+      // handshake record contains multiple messages, |SSL_do_handshake| usually
+      // processes both atomically. The test must ensure there is a record
+      // boundary after the desired message. Checking |last_message_received|
+      // confirms this.
+      do {
+        ret = SSL_do_handshake(ssl);
+      } while (test_state->last_message_received !=
+                   config->early_write_after_message &&
+               RetryAsync(ssl, ret));
+      if (ret == 1) {
+        fprintf(stderr, "Handshake unexpectedly succeeded.\n");
+        return false;
+      }
+      if (test_state->last_message_received !=
+          config->early_write_after_message) {
+        // The handshake failed before we saw the target message. The generic
+        // error-handling logic in the caller will print the error.
+        return false;
+      }
+    }
+
     // Reset the state to assert later that the callback isn't called in
     // renegotations.
-    GetTestState(ssl)->got_new_session = false;
+    test_state->got_new_session = false;
   }
 
   if (config->export_keying_material > 0) {
@@ -977,7 +1071,7 @@
       }
 
       // Let only one byte of the record through.
-      AsyncBioAllowWrite(GetTestState(ssl)->async_bio, 1);
+      AsyncBioAllowWrite(test_state->async_bio, 1);
       int write_ret =
           SSL_write(ssl, kInitialWrite, strlen(kInitialWrite));
       if (SSL_get_error(ssl, write_ret) != SSL_ERROR_WANT_WRITE) {
@@ -1032,7 +1126,7 @@
 
         // After a successful read, with or without False Start, the handshake
         // must be complete unless we are doing early data.
-        if (!GetTestState(ssl)->handshake_done &&
+        if (!test_state->handshake_done &&
             !SSL_early_data_accepted(ssl)) {
           fprintf(stderr, "handshake was not completed after SSL_read\n");
           return false;
@@ -1066,7 +1160,7 @@
       !config->implicit_handshake &&
       // Session tickets are sent post-handshake in TLS 1.3.
       GetProtocolVersion(ssl) < TLS1_3_VERSION &&
-      GetTestState(ssl)->got_new_session) {
+      test_state->got_new_session) {
     fprintf(stderr, "new session was established after the handshake\n");
     return false;
   }
@@ -1074,16 +1168,16 @@
   if (GetProtocolVersion(ssl) >= TLS1_3_VERSION && !config->is_server) {
     bool expect_new_session =
         !config->expect_no_session && !config->shim_shuts_down;
-    if (expect_new_session != GetTestState(ssl)->got_new_session) {
+    if (expect_new_session != test_state->got_new_session) {
       fprintf(stderr,
               "new session was%s cached, but we expected the opposite\n",
-              GetTestState(ssl)->got_new_session ? "" : " not");
+              test_state->got_new_session ? "" : " not");
       return false;
     }
 
     if (expect_new_session) {
       bool got_early_data =
-          GetTestState(ssl)->new_session->ticket_max_early_data != 0;
+          test_state->new_session->ticket_max_early_data != 0;
       if (config->expect_ticket_supports_early_data != got_early_data) {
         fprintf(stderr,
                 "new session did%s support early data, but we expected the "
@@ -1095,7 +1189,7 @@
   }
 
   if (out_session) {
-    *out_session = std::move(GetTestState(ssl)->new_session);
+    *out_session = std::move(test_state->new_session);
   }
 
   ret = DoShutdown(ssl);
@@ -1144,10 +1238,10 @@
 
   if (config->renegotiate_explicit &&
       SSL_total_renegotiations(ssl) !=
-          GetTestState(ssl)->explicit_renegotiates) {
+          test_state->explicit_renegotiates) {
     fprintf(stderr, "Performed %d renegotiations, but triggered %d of them\n",
             SSL_total_renegotiations(ssl),
-            GetTestState(ssl)->explicit_renegotiates);
+            test_state->explicit_renegotiates);
     return false;
   }
 
@@ -1184,8 +1278,8 @@
   CRYPTO_library_init();
 
   TestConfig initial_config, resume_config, retry_config;
-  if (!ParseConfig(argc - 1, argv + 1, &initial_config, &resume_config,
-                   &retry_config)) {
+  if (!ParseConfig(argc - 1, argv + 1, /*is_shim=*/true, &initial_config,
+                   &resume_config, &retry_config)) {
     return Usage(argv[0]);
   }
 
diff --git a/deps/boringssl/src/ssl/test/fuzzer.h b/deps/boringssl/src/ssl/test/fuzzer.h
index f10c4a0..509cfdb 100644
--- a/deps/boringssl/src/ssl/test/fuzzer.h
+++ b/deps/boringssl/src/ssl/test/fuzzer.h
@@ -20,17 +20,19 @@
 #include <string.h>
 
 #include <algorithm>
+#include <vector>
 
 #include <openssl/bio.h>
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
+#include <openssl/hpke.h>
 #include <openssl/rand.h>
 #include <openssl/rsa.h>
 #include <openssl/ssl.h>
 #include <openssl/x509.h>
 
-#include "../internal.h"
+#include "../../crypto/internal.h"
 #include "./fuzzer_tags.h"
 
 namespace {
@@ -229,6 +231,22 @@
     0x01, 'a', 0x02, 'a', 'a', 0x03, 'a', 'a', 'a',
 };
 
+const uint8_t kECHConfig[] = {
+    0xfe, 0x0a, 0x00, 0x47, 0x2a, 0x00, 0x20, 0x00, 0x20, 0x6c, 0x55,
+    0x96, 0x41, 0x3d, 0x12, 0x4e, 0x63, 0x3d, 0x39, 0x7a, 0xe9, 0xbc,
+    0xec, 0xb2, 0x55, 0xd0, 0xe6, 0xaa, 0xbd, 0xa9, 0x79, 0xb8, 0x86,
+    0x9a, 0x13, 0x61, 0xc6, 0x69, 0xac, 0xb4, 0x21, 0x00, 0x0c, 0x00,
+    0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03,
+    0x00, 0x10, 0x00, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x2e,
+    0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, 0x00,
+};
+
+const uint8_t kECHKey[] = {
+    0x35, 0x6d, 0x45, 0x06, 0xb3, 0x88, 0x89, 0x2e, 0xd6, 0x87, 0x84,
+    0xd2, 0x2d, 0x6f, 0x83, 0x48, 0xad, 0xf2, 0xfd, 0x08, 0x51, 0x73,
+    0x10, 0xa0, 0xb8, 0xdd, 0xe9, 0x96, 0x6a, 0xde, 0xbc, 0x82,
+};
+
 int ALPNSelectCallback(SSL *ssl, const uint8_t **out, uint8_t *out_len,
                        const uint8_t *in, unsigned in_len, void *arg) {
   static const uint8_t kProtocol[] = {'a', 'a'};
@@ -324,7 +342,7 @@
         MoveBIOs(ssl_handoff.get(), ssl.get());
         // Ordinarily we would call SSL_serialize_handoff(ssl.get().  But for
         // fuzzing, use the serialized handoff that's getting fuzzed.
-        if (!SSL_apply_handoff(ssl_handoff.get(), handoff_)) {
+        if (!bssl::SSL_apply_handoff(ssl_handoff.get(), handoff_)) {
           if (debug_) {
             fprintf(stderr, "Handoff failed.\n");
           }
@@ -334,7 +352,7 @@
       } else if (ret < 0 &&
                  SSL_get_error(ssl_handshake, ret) == SSL_ERROR_HANDBACK) {
         MoveBIOs(ssl_handback.get(), ssl_handoff.get());
-        if (!SSL_apply_handback(ssl_handback.get(), handback_)) {
+        if (!bssl::SSL_apply_handback(ssl_handback.get(), handback_)) {
           if (debug_) {
             fprintf(stderr, "Handback failed.\n");
           }
@@ -437,6 +455,19 @@
     }
     SSL_CTX_set_tls_channel_id_enabled(ctx_.get(), 1);
 
+    if (role_ == kServer) {
+      bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+      bssl::ScopedEVP_HPKE_KEY key;
+      if (!keys ||
+          !EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(), kECHKey,
+                             sizeof(kECHKey)) ||
+          !SSL_ECH_KEYS_add(keys.get(), /*is_retry_config=*/true, kECHConfig,
+                            sizeof(kECHConfig), key.get()) ||
+          !SSL_CTX_set1_ech_keys(ctx_.get(), keys.get())) {
+        return false;
+      }
+    }
+
     return true;
   }
 
@@ -497,7 +528,8 @@
           if (!CBS_get_u24_length_prefixed(cbs, &handoff)) {
             return nullptr;
           }
-          handoff_.CopyFrom(handoff);
+          handoff_.assign(CBS_data(&handoff),
+                          CBS_data(&handoff) + CBS_len(&handoff));
           bssl::SSL_set_handoff_mode(ssl.get(), 1);
           break;
         }
@@ -507,11 +539,21 @@
           if (!CBS_get_u24_length_prefixed(cbs, &handback)) {
             return nullptr;
           }
-          handback_.CopyFrom(handback);
+          handback_.assign(CBS_data(&handback),
+                           CBS_data(&handback) + CBS_len(&handback));
           bssl::SSL_set_handoff_mode(ssl.get(), 1);
           break;
         }
 
+        case kHintsTag: {
+          CBS hints;
+          if (!CBS_get_u24_length_prefixed(cbs, &hints)) {
+            return nullptr;
+          }
+          SSL_set_handshake_hints(ssl.get(), CBS_data(&hints), CBS_len(&hints));
+          break;
+        }
+
         default:
           return nullptr;
       }
@@ -567,7 +609,7 @@
   Protocol protocol_;
   Role role_;
   bssl::UniquePtr<SSL_CTX> ctx_;
-  bssl::Array<uint8_t> handoff_, handback_;
+  std::vector<uint8_t> handoff_, handback_;
 };
 
 const BIO_METHOD TLSFuzzer::kBIOMethod = {
diff --git a/deps/boringssl/src/ssl/test/fuzzer_tags.h b/deps/boringssl/src/ssl/test/fuzzer_tags.h
index eb9991d..b612222 100644
--- a/deps/boringssl/src/ssl/test/fuzzer_tags.h
+++ b/deps/boringssl/src/ssl/test/fuzzer_tags.h
@@ -23,7 +23,7 @@
 // The TLS client and server fuzzers coordinate with bssl_shim on a common
 // format to encode configuration parameters in a fuzzer file. To add a new
 // configuration, define a tag, update |SetupTest| in fuzzer.h to parse it, and
-// update |WriteSettings| in bssl_shim to serialize it. Finally, record
+// update |SettingsWriter| in bssl_shim to serialize it. Finally, record
 // transcripts from a test run, and use the BORINGSSL_FUZZER_DEBUG environment
 // variable to confirm the transcripts are compatible.
 
@@ -45,4 +45,7 @@
 // kHandbackTag is followed by te output of |SSL_serialize_handback|.
 static const uint16_t kHandbackTag = 4;
 
+// kHintsTag is followed by the output of |SSL_serialize_handshake_hints|.
+static const uint16_t kHintsTag = 5;
+
 #endif  // HEADER_SSL_TEST_FUZZER_TAGS
diff --git a/deps/boringssl/src/ssl/test/handshake_util.cc b/deps/boringssl/src/ssl/test/handshake_util.cc
index f3f725d..b999831 100644
--- a/deps/boringssl/src/ssl/test/handshake_util.cc
+++ b/deps/boringssl/src/ssl/test/handshake_util.cc
@@ -15,7 +15,7 @@
 #include "handshake_util.h"
 
 #include <assert.h>
-#if defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
+#if defined(HANDSHAKER_SUPPORTED)
 #include <errno.h>
 #include <fcntl.h>
 #include <spawn.h>
@@ -27,12 +27,15 @@
 #endif
 
 #include <functional>
+#include <map>
+#include <vector>
 
 #include "async_bio.h"
 #include "packeted_bio.h"
 #include "test_config.h"
 #include "test_state.h"
 
+#include <openssl/bytestring.h>
 #include <openssl/ssl.h>
 
 using namespace bssl;
@@ -83,14 +86,6 @@
     case SSL_ERROR_WANT_WRITE:
       AsyncBioAllowWrite(test_state->async_bio, 1);
       return true;
-    case SSL_ERROR_WANT_CHANNEL_ID_LOOKUP: {
-      UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(config->send_channel_id);
-      if (!pkey) {
-        return false;
-      }
-      test_state->channel_id = std::move(pkey);
-      return true;
-    }
     case SSL_ERROR_WANT_X509_LOOKUP:
       test_state->cert_ready = true;
       return true;
@@ -135,7 +130,7 @@
   return ret;
 }
 
-#if defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
+#if defined(HANDSHAKER_SUPPORTED)
 
 // MoveBIOs moves the |BIO|s of |src| to |dst|.  It is used for handoff.
 static void MoveBIOs(SSL *dest, SSL *src) {
@@ -231,7 +226,7 @@
       return false;
     }
     switch (msg) {
-      case kControlMsgHandback:
+      case kControlMsgDone:
         return true;
       case kControlMsgError:
         return false;
@@ -303,17 +298,90 @@
 
 class ScopedFD {
  public:
-  explicit ScopedFD(int fd): fd_(fd) {}
-  ~ScopedFD() { close(fd_); }
+  ScopedFD() : fd_(-1) {}
+  explicit ScopedFD(int fd) : fd_(fd) {}
+  ~ScopedFD() { Reset(); }
+
+  ScopedFD(ScopedFD &&other) { *this = std::move(other); }
+  ScopedFD &operator=(ScopedFD &&other) {
+    Reset(other.fd_);
+    other.fd_ = -1;
+    return *this;
+  }
+
+  int fd() const { return fd_; }
+
+  void Reset(int fd = -1) {
+    if (fd_ >= 0) {
+      close(fd_);
+    }
+    fd_ = fd;
+  }
+
  private:
-  const int fd_;
+  int fd_;
 };
 
-// RunHandshaker forks and execs the handshaker binary, handing off |input|,
-// and, after proxying some amount of handshake traffic, handing back |out|.
-static bool RunHandshaker(BIO *bio, const TestConfig *config, bool is_resume,
-                          const Array<uint8_t> &input,
-                          Array<uint8_t> *out) {
+class ScopedProcess {
+ public:
+  ScopedProcess() : pid_(-1) {}
+  ~ScopedProcess() { Reset(); }
+
+  ScopedProcess(ScopedProcess &&other) { *this = std::move(other); }
+  ScopedProcess &operator=(ScopedProcess &&other) {
+    Reset(other.pid_);
+    other.pid_ = -1;
+    return *this;
+  }
+
+  pid_t pid() const { return pid_; }
+
+  void Reset(pid_t pid = -1) {
+    if (pid_ >= 0) {
+      kill(pid_, SIGTERM);
+      int unused;
+      Wait(&unused);
+    }
+    pid_ = pid;
+  }
+
+  bool Wait(int *out_status) {
+    if (pid_ < 0) {
+      return false;
+    }
+    if (waitpid_eintr(pid_, out_status, 0) != pid_) {
+      return false;
+    }
+    pid_ = -1;
+    return true;
+  }
+
+ private:
+  pid_t pid_;
+};
+
+class FileActionsDestroyer {
+ public:
+  explicit FileActionsDestroyer(posix_spawn_file_actions_t *actions)
+      : actions_(actions) {}
+  ~FileActionsDestroyer() { posix_spawn_file_actions_destroy(actions_); }
+  FileActionsDestroyer(const FileActionsDestroyer &) = delete;
+  FileActionsDestroyer &operator=(const FileActionsDestroyer &) = delete;
+
+ private:
+  posix_spawn_file_actions_t *actions_;
+};
+
+// StartHandshaker starts the handshaker process and, on success, returns a
+// handle to the process in |*out|. It sets |*out_control| to a control pipe to
+// the process. |map_fds| maps from desired fd number in the child process to
+// the source fd in the calling process. |close_fds| is the list of additional
+// fds to close, which may overlap with |map_fds|. Other than stdin, stdout, and
+// stderr, the status of fds not listed in either set is undefined.
+static bool StartHandshaker(ScopedProcess *out, ScopedFD *out_control,
+                            const TestConfig *config, bool is_resume,
+                            std::map<int, int> map_fds,
+                            std::vector<int> close_fds) {
   if (config->handshaker_path.empty()) {
     fprintf(stderr, "no -handshaker-path specified\n");
     return false;
@@ -324,12 +392,97 @@
     return false;
   }
 
+  std::vector<const char *> args;
+  args.push_back(config->handshaker_path.c_str());
+  static const char kResumeFlag[] = "-handshaker-resume";
+  if (is_resume) {
+    args.push_back(kResumeFlag);
+  }
+  // config->argv omits argv[0].
+  for (int j = 0; j < config->argc; ++j) {
+    args.push_back(config->argv[j]);
+  }
+  args.push_back(nullptr);
+
   // A datagram socket guarantees that writes are all-or-nothing.
   int control[2];
   if (socketpair(AF_LOCAL, SOCK_DGRAM, 0, control) != 0) {
     perror("socketpair");
     return false;
   }
+  ScopedFD scoped_control0(control[0]), scoped_control1(control[1]);
+  close_fds.push_back(control[0]);
+  map_fds[kFdControl] = control[1];
+
+  posix_spawn_file_actions_t actions;
+  if (posix_spawn_file_actions_init(&actions) != 0) {
+    return false;
+  }
+  FileActionsDestroyer actions_destroyer(&actions);
+  for (int fd : close_fds) {
+    if (posix_spawn_file_actions_addclose(&actions, fd) != 0) {
+      return false;
+    }
+  }
+  if (!map_fds.empty()) {
+    int max_fd = STDERR_FILENO;
+    for (const auto &pair : map_fds) {
+      max_fd = std::max(max_fd, pair.first);
+      max_fd = std::max(max_fd, pair.second);
+    }
+    // |map_fds| may contain cycles, so make a copy of all the source fds.
+    // |posix_spawn| can only use |dup2|, not |dup|, so we assume |max_fd| is
+    // the last fd we care about inheriting. |temp_fds| maps from fd number in
+    // the parent process to a temporary fd number in the child process.
+    std::map<int, int> temp_fds;
+    int next_fd = max_fd + 1;
+    for (const auto &pair : map_fds) {
+      if (temp_fds.count(pair.second)) {
+        continue;
+      }
+      temp_fds[pair.second] = next_fd;
+      if (posix_spawn_file_actions_adddup2(&actions, pair.second, next_fd) !=
+          0 ||
+          posix_spawn_file_actions_addclose(&actions, pair.second) != 0) {
+        return false;
+      }
+      next_fd++;
+    }
+    for (const auto &pair : map_fds) {
+      if (posix_spawn_file_actions_adddup2(&actions, temp_fds[pair.second],
+                                           pair.first) != 0) {
+        return false;
+      }
+    }
+    // Clean up temporary fds.
+    for (int fd = max_fd + 1; fd < next_fd; fd++) {
+      if (posix_spawn_file_actions_addclose(&actions, fd) != 0) {
+        return false;
+      }
+    }
+  }
+
+  fflush(stdout);
+  fflush(stderr);
+
+  // MSan doesn't know that |posix_spawn| initializes its output, so initialize
+  // it to -1.
+  pid_t pid = -1;
+  if (posix_spawn(&pid, args[0], &actions, nullptr,
+                  const_cast<char *const *>(args.data()), environ) != 0) {
+    return false;
+  }
+
+  out->Reset(pid);
+  *out_control = std::move(scoped_control0);
+  return true;
+}
+
+// RunHandshaker forks and execs the handshaker binary, handing off |input|,
+// and, after proxying some amount of handshake traffic, handing back |out|.
+static bool RunHandshaker(BIO *bio, const TestConfig *config, bool is_resume,
+                          Span<const uint8_t> input,
+                          std::vector<uint8_t> *out) {
   int rfd[2], wfd[2];
   // We use pipes, rather than some other mechanism, for their buffers.  During
   // the handshake, this process acts as a dumb proxy until receiving the
@@ -341,77 +494,37 @@
   // handshaker has not explicitly requested as a result of hitting
   // |SSL_ERROR_WANT_READ|.  Pipes allow the data to sit in a buffer while the
   // two processes synchronize over the |control| channel.
-  if (pipe(rfd) != 0 || pipe(wfd) != 0) {
-    perror("pipe2");
+  if (pipe(rfd) != 0) {
+    perror("pipe");
+    return false;
+  }
+  ScopedFD rfd0_closer(rfd[0]), rfd1_closer(rfd[1]);
+
+  if (pipe(wfd) != 0) {
+    perror("pipe");
+    return false;
+  }
+  ScopedFD wfd0_closer(wfd[0]), wfd1_closer(wfd[1]);
+
+  ScopedProcess handshaker;
+  ScopedFD control;
+  if (!StartHandshaker(
+          &handshaker, &control, config, is_resume,
+          {{kFdProxyToHandshaker, rfd[0]}, {kFdHandshakerToProxy, wfd[1]}},
+          {rfd[1], wfd[0]})) {
     return false;
   }
 
-  fflush(stdout);
-  fflush(stderr);
+  rfd0_closer.Reset();
+  wfd1_closer.Reset();
 
-  std::vector<char *> args;
-  bssl::UniquePtr<char> handshaker_path(
-      OPENSSL_strdup(config->handshaker_path.c_str()));
-  args.push_back(handshaker_path.get());
-  char resume[] = "-handshaker-resume";
-  if (is_resume) {
-    args.push_back(resume);
-  }
-  // config->argv omits argv[0].
-  for (int j = 0; j < config->argc; ++j) {
-    args.push_back(config->argv[j]);
-  }
-  args.push_back(nullptr);
-
-  posix_spawn_file_actions_t actions;
-  if (posix_spawn_file_actions_init(&actions) != 0 ||
-      posix_spawn_file_actions_addclose(&actions, control[0]) ||
-      posix_spawn_file_actions_addclose(&actions, rfd[1]) ||
-      posix_spawn_file_actions_addclose(&actions, wfd[0])) {
-    return false;
-  }
-  assert(kFdControl != rfd[0]);
-  assert(kFdControl != wfd[1]);
-  if (control[1] != kFdControl &&
-      posix_spawn_file_actions_adddup2(&actions, control[1], kFdControl) != 0) {
-    return false;
-  }
-  assert(kFdProxyToHandshaker != wfd[1]);
-  if (rfd[0] != kFdProxyToHandshaker &&
-      posix_spawn_file_actions_adddup2(&actions, rfd[0],
-                                       kFdProxyToHandshaker) != 0) {
-    return false;
-  }
-  if (wfd[1] != kFdHandshakerToProxy &&
-      posix_spawn_file_actions_adddup2(&actions, wfd[1],
-                                       kFdHandshakerToProxy) != 0) {
-      return false;
-  }
-
-  // MSan doesn't know that |posix_spawn| initializes its output, so initialize
-  // it to -1.
-  pid_t handshaker_pid = -1;
-  int ret = posix_spawn(&handshaker_pid, args[0], &actions, nullptr,
-                        args.data(), environ);
-  if (posix_spawn_file_actions_destroy(&actions) != 0 ||
-      ret != 0) {
-    return false;
-  }
-
-  close(control[1]);
-  close(rfd[0]);
-  close(wfd[1]);
-  ScopedFD rfd_closer(rfd[1]);
-  ScopedFD wfd_closer(wfd[0]);
-  ScopedFD control_closer(control[0]);
-
-  if (write_eintr(control[0], input.data(), input.size()) == -1) {
+  if (write_eintr(control.fd(), input.data(), input.size()) == -1) {
     perror("write");
     return false;
   }
-  bool ok = Proxy(bio, config->async, control[0], rfd[1], wfd[0]);
+  bool ok = Proxy(bio, config->async, control.fd(), rfd[1], wfd[0]);
   int wstatus;
-  if (waitpid_eintr(handshaker_pid, &wstatus, 0) != handshaker_pid) {
+  if (!handshaker.Wait(&wstatus)) {
     perror("waitpid");
     return false;
   }
@@ -424,13 +537,69 @@
   }
 
   constexpr size_t kBufSize = 1024 * 1024;
-  bssl::UniquePtr<uint8_t> buf((uint8_t *) OPENSSL_malloc(kBufSize));
-  int len = read_eintr(control[0], buf.get(), kBufSize);
+  std::vector<uint8_t> buf(kBufSize);
+  ssize_t len = read_eintr(control.fd(), buf.data(), buf.size());
   if (len == -1) {
     perror("read");
     return false;
   }
-  out->CopyFrom({buf.get(), (size_t)len});
+  buf.resize(len);
+  *out = std::move(buf);
+  return true;
+}
+
+static bool RequestHandshakeHint(const TestConfig *config, bool is_resume,
+                                 Span<const uint8_t> input, bool *out_has_hints,
+                                 std::vector<uint8_t> *out_hints) {
+  ScopedProcess handshaker;
+  ScopedFD control;
+  if (!StartHandshaker(&handshaker, &control, config, is_resume, {}, {})) {
+    return false;
+  }
+
+  if (write_eintr(control.fd(), input.data(), input.size()) == -1) {
+    perror("write");
+    return false;
+  }
+
+  char msg;
+  if (read_eintr(control.fd(), &msg, 1) != 1) {
+    perror("read");
+    return false;
+  }
+
+  switch (msg) {
+    case kControlMsgDone: {
+      constexpr size_t kBufSize = 1024 * 1024;
+      out_hints->resize(kBufSize);
+      ssize_t len =
+          read_eintr(control.fd(), out_hints->data(), out_hints->size());
+      if (len == -1) {
+        perror("read");
+        return false;
+      }
+      out_hints->resize(len);
+      *out_has_hints = true;
+      break;
+    }
+    case kControlMsgError:
+      *out_has_hints = false;
+      break;
+    default:
+      fprintf(stderr, "Unknown control message from handshaker: %c\n", msg);
+      return false;
+  }
+
+  int wstatus;
+  if (!handshaker.Wait(&wstatus)) {
+    perror("waitpid");
+    return false;
+  }
+  if (wstatus) {
+    fprintf(stderr, "handshaker exited irregularly\n");
+    return false;
+  }
+
   return true;
 }
 
@@ -438,7 +607,7 @@
 // be passed to the handshaker.  The serialized state includes both the SSL
 // handoff, as well test-related state.
 static bool PrepareHandoff(SSL *ssl, SettingsWriter *writer,
-                           Array<uint8_t> *out_handoff) {
+                           std::vector<uint8_t> *out_handoff) {
   SSL_set_handoff_mode(ssl, 1);
 
   const TestConfig *config = GetTestConfig(ssl);
@@ -460,12 +629,14 @@
   if (!CBB_init(cbb.get(), 512) ||
       !SSL_serialize_handoff(ssl, cbb.get(), &hello) ||
       !writer->WriteHandoff({CBB_data(cbb.get()), CBB_len(cbb.get())}) ||
-      !SerializeContextState(ssl->ctx.get(), cbb.get()) ||
+      !SerializeContextState(SSL_get_SSL_CTX(ssl), cbb.get()) ||
       !GetTestState(ssl)->Serialize(cbb.get())) {
     fprintf(stderr, "Handoff serialisation failed.\n");
     return false;
   }
-  return CBBFinishArray(cbb.get(), out_handoff);
+  out_handoff->assign(CBB_data(cbb.get()),
+                      CBB_data(cbb.get()) + CBB_len(cbb.get()));
+  return true;
 }
 
 // DoSplitHandshake delegates the SSL handshake to a separate process, called
@@ -476,11 +647,11 @@
 bool DoSplitHandshake(UniquePtr<SSL> *ssl, SettingsWriter *writer,
                       bool is_resume) {
   assert(SSL_get_rbio(ssl->get()) == SSL_get_wbio(ssl->get()));
-  Array<uint8_t> handshaker_input;
+  std::vector<uint8_t> handshaker_input;
   const TestConfig *config = GetTestConfig(ssl->get());
   // out is the response from the handshaker, which includes a serialized
   // handback message, but also serialized updates to the |TestState|.
-  Array<uint8_t> out;
+  std::vector<uint8_t> out;
   if (!PrepareHandoff(ssl->get(), writer, &handshaker_input) ||
       !RunHandshaker(SSL_get_rbio(ssl->get()), config, is_resume,
                      handshaker_input, &out)) {
@@ -488,19 +659,17 @@
     return false;
   }
 
-  UniquePtr<SSL> ssl_handback =
-      config->NewSSL((*ssl)->ctx.get(), nullptr, false, nullptr);
+  SSL_CTX *ctx = SSL_get_SSL_CTX(ssl->get());
+  UniquePtr<SSL> ssl_handback = config->NewSSL(ctx, nullptr, nullptr);
   if (!ssl_handback) {
     return false;
   }
   CBS output, handback;
   CBS_init(&output, out.data(), out.size());
   if (!CBS_get_u24_length_prefixed(&output, &handback) ||
-      !DeserializeContextState(&output, ssl_handback->ctx.get()) ||
-      !SetTestState(ssl_handback.get(), TestState::Deserialize(
-          &output, ssl_handback->ctx.get())) ||
-      !GetTestState(ssl_handback.get()) ||
-      !writer->WriteHandback(handback) ||
+      !DeserializeContextState(&output, ctx) ||
+      !SetTestState(ssl_handback.get(), TestState::Deserialize(&output, ctx)) ||
+      !GetTestState(ssl_handback.get()) || !writer->WriteHandback(handback) ||
       !SSL_apply_handback(ssl_handback.get(), handback)) {
     fprintf(stderr, "Handback failed.\n");
     return false;
@@ -514,4 +683,35 @@
   return true;
 }
 
-#endif  // defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
+bool GetHandshakeHint(SSL *ssl, SettingsWriter *writer, bool is_resume,
+                      const SSL_CLIENT_HELLO *client_hello) {
+  ScopedCBB input;
+  CBB child;
+  if (!CBB_init(input.get(), client_hello->client_hello_len + 256) ||
+      !CBB_add_u24_length_prefixed(input.get(), &child) ||
+      !CBB_add_bytes(&child, client_hello->client_hello,
+                     client_hello->client_hello_len) ||
+      !CBB_add_u24_length_prefixed(input.get(), &child) ||
+      !SSL_serialize_capabilities(ssl, &child) ||  //
+      !CBB_flush(input.get())) {
+    return false;
+  }
+
+  bool has_hints;
+  std::vector<uint8_t> hints;
+  if (!RequestHandshakeHint(
+          GetTestConfig(ssl), is_resume,
+          MakeConstSpan(CBB_data(input.get()), CBB_len(input.get())),
+          &has_hints, &hints)) {
+    return false;
+  }
+  if (has_hints &&
+      (!writer->WriteHints(hints) ||
+       !SSL_set_handshake_hints(ssl, hints.data(), hints.size()))) {
+    return false;
+  }
+
+  return true;
+}
+
+#endif  // defined(HANDSHAKER_SUPPORTED)
diff --git a/deps/boringssl/src/ssl/test/handshake_util.h b/deps/boringssl/src/ssl/test/handshake_util.h
index 4fb46db..dda9206 100644
--- a/deps/boringssl/src/ssl/test/handshake_util.h
+++ b/deps/boringssl/src/ssl/test/handshake_util.h
@@ -21,6 +21,11 @@
 
 #include "settings_writer.h"
 
+
+#if defined(OPENSSL_LINUX) && !defined(OPENSSL_ANDROID)
+#define HANDSHAKER_SUPPORTED
+#endif
+
 // RetryAsync is called after a failed operation on |ssl| with return code
 // |ret|. If the operation should be retried, it simulates one asynchronous
 // event and returns true. Otherwise it returns false.
@@ -30,6 +35,7 @@
 // errors are idempotent.
 int CheckIdempotentError(const char *name, SSL *ssl, std::function<int()> func);
 
+#if defined(HANDSHAKER_SUPPORTED)
 // DoSplitHandshake delegates the SSL handshake to a separate process, called
 // the handshaker.  This process proxies I/O between the handshaker and the
 // client, using the |BIO| from |ssl|.  After a successful handshake, |ssl| is
@@ -38,16 +44,24 @@
 bool DoSplitHandshake(bssl::UniquePtr<SSL> *ssl, SettingsWriter *writer,
                       bool is_resume);
 
+// GetHandshakeHint requests a handshake hint from the handshaker process and
+// configures the result on |ssl|. It returns true on success and false on
+// error.
+bool GetHandshakeHint(SSL *ssl, SettingsWriter *writer, bool is_resume,
+                      const SSL_CLIENT_HELLO *client_hello);
+
 // The protocol between the proxy and the handshaker is defined by these
-// single-character prefixes.
+// single-character prefixes. |kControlMsgDone| uses 'H' for compatibility with
+// older binaries.
 constexpr char kControlMsgWantRead = 'R';        // Handshaker wants data
 constexpr char kControlMsgWriteCompleted = 'W';  // Proxy has sent data
-constexpr char kControlMsgHandback = 'H';        // Proxy should resume control
+constexpr char kControlMsgDone = 'H';            // Proxy should resume control
 constexpr char kControlMsgError = 'E';           // Handshaker hit an error
 
 // The protocol between the proxy and handshaker uses these file descriptors.
-constexpr int kFdControl = 3;                    // Bi-directional dgram socket.
-constexpr int kFdProxyToHandshaker = 4;          // Uni-directional pipe.
-constexpr int kFdHandshakerToProxy = 5;          // Uni-directional pipe.
+constexpr int kFdControl = 3;            // Bi-directional dgram socket.
+constexpr int kFdProxyToHandshaker = 4;  // Uni-directional pipe.
+constexpr int kFdHandshakerToProxy = 5;  // Uni-directional pipe.
+#endif  // HANDSHAKER_SUPPORTED
 
 #endif  // HEADER_TEST_HANDSHAKE
diff --git a/deps/boringssl/src/ssl/test/handshaker.cc b/deps/boringssl/src/ssl/test/handshaker.cc
index 72d6b2f..ac89063 100644
--- a/deps/boringssl/src/ssl/test/handshaker.cc
+++ b/deps/boringssl/src/ssl/test/handshaker.cc
@@ -12,16 +12,18 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+#include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <unistd.h>
 
+#include <memory>
+
 #include <openssl/bytestring.h>
 #include <openssl/rand.h>
 #include <openssl/ssl.h>
 
-#include "../internal.h"
 #include "handshake_util.h"
 #include "test_config.h"
 #include "test_state.h"
@@ -30,6 +32,22 @@
 
 namespace {
 
+ssize_t read_eintr(int fd, void *out, size_t len) {
+  ssize_t ret;
+  do {
+    ret = read(fd, out, len);
+  } while (ret < 0 && errno == EINTR);
+  return ret;
+}
+
+ssize_t write_eintr(int fd, const void *in, size_t len) {
+  ssize_t ret;
+  do {
+    ret = write(fd, in, len);
+  } while (ret < 0 && errno == EINTR);
+  return ret;
+}
+
 bool HandbackReady(SSL *ssl, int ret) {
   return ret < 0 && SSL_get_error(ssl, ret) == SSL_ERROR_HANDBACK;
 }
@@ -40,7 +58,11 @@
   if (!ctx) {
     return false;
   }
-  UniquePtr<SSL> ssl = config->NewSSL(ctx.get(), nullptr, false, nullptr);
+  UniquePtr<SSL> ssl =
+      config->NewSSL(ctx.get(), /*session=*/nullptr, /*test_state=*/nullptr);
+  if (!ssl) {
+    return false;
+  }
 
   // Set |O_NONBLOCK| in order to break out of the loop when we hit
   // |SSL_ERROR_WANT_READ|, so that we can send |kControlMsgWantRead| to the
@@ -72,8 +94,8 @@
       // Synchronize with the proxy, i.e. don't let the handshake continue until
       // the proxy has sent more data.
       char msg = kControlMsgWantRead;
-      if (write(control, &msg, 1) != 1 ||
-          read(control, &msg, 1) != 1 ||
+      if (write_eintr(control, &msg, 1) != 1 ||
+          read_eintr(control, &msg, 1) != 1 ||
           msg != kControlMsgWriteCompleted) {
         fprintf(stderr, "read via proxy failed\n");
         return false;
@@ -85,46 +107,109 @@
     }
   }
   if (!HandbackReady(ssl.get(), ret)) {
+    fprintf(stderr, "Handshaker: %s\n",
+            SSL_error_description(SSL_get_error(ssl.get(), ret)));
     ERR_print_errors_fp(stderr);
     return false;
   }
 
   ScopedCBB output;
   CBB handback;
-  Array<uint8_t> bytes;
   if (!CBB_init(output.get(), 1024) ||
       !CBB_add_u24_length_prefixed(output.get(), &handback) ||
       !SSL_serialize_handback(ssl.get(), &handback) ||
-      !SerializeContextState(ssl->ctx.get(), output.get()) ||
-      !GetTestState(ssl.get())->Serialize(output.get()) ||
-      !CBBFinishArray(output.get(), &bytes)) {
+      !SerializeContextState(ctx.get(), output.get()) ||
+      !GetTestState(ssl.get())->Serialize(output.get())) {
     fprintf(stderr, "Handback serialisation failed.\n");
     return false;
   }
 
-  char msg = kControlMsgHandback;
-  if (write(control, &msg, 1) == -1 ||
-      write(control, bytes.data(), bytes.size()) == -1) {
+  char msg = kControlMsgDone;
+  if (write_eintr(control, &msg, 1) == -1 ||
+      write_eintr(control, CBB_data(output.get()), CBB_len(output.get())) ==
+          -1) {
     perror("write");
     return false;
   }
   return true;
 }
 
-ssize_t read_eintr(int fd, void *out, size_t len) {
-  ssize_t ret;
-  do {
-    ret = read(fd, out, len);
-  } while (ret < 0 && errno == EINTR);
-  return ret;
-}
+bool GenerateHandshakeHint(const TestConfig *config,
+                           bssl::Span<const uint8_t> request, int control) {
+  // The handshake hint contains the ClientHello and the capabilities string.
+  CBS cbs = request;
+  CBS client_hello, capabilities;
+  if (!CBS_get_u24_length_prefixed(&cbs, &client_hello) ||
+      !CBS_get_u24_length_prefixed(&cbs, &capabilities) ||  //
+      CBS_len(&cbs) != 0) {
+    fprintf(stderr, "Handshaker: Could not parse hint request\n");
+    return false;
+  }
 
-ssize_t write_eintr(int fd, const void *in, size_t len) {
-  ssize_t ret;
+  UniquePtr<SSL_CTX> ctx = config->SetupCtx(/*old_ctx=*/nullptr);
+  if (!ctx) {
+    return false;
+  }
+
+  UniquePtr<SSL> ssl =
+      config->NewSSL(ctx.get(), /*session=*/nullptr,
+                     std::unique_ptr<TestState>(new TestState));
+  if (!ssl) {
+    return false;
+  }
+
+  // TODO(davidben): When split handshakes is replaced, move this into |NewSSL|.
+  assert(config->is_server);
+  SSL_set_accept_state(ssl.get());
+
+  if (!SSL_request_handshake_hints(
+          ssl.get(), CBS_data(&client_hello), CBS_len(&client_hello),
+          CBS_data(&capabilities), CBS_len(&capabilities))) {
+    fprintf(stderr, "Handshaker: SSL_request_handshake_hints failed\n");
+    return false;
+  }
+
+  int ret = 0;
   do {
-    ret = write(fd, in, len);
-  } while (ret < 0 && errno == EINTR);
-  return ret;
+    ret = CheckIdempotentError("SSL_do_handshake", ssl.get(),
+                               [&] { return SSL_do_handshake(ssl.get()); });
+  } while (RetryAsync(ssl.get(), ret));
+
+  if (ret > 0) {
+    fprintf(stderr, "Handshaker: handshake unexpectedly succeeded.\n");
+    return false;
+  }
+
+  if (SSL_get_error(ssl.get(), ret) != SSL_ERROR_HANDSHAKE_HINTS_READY) {
+    // Errors here may be expected if the test is testing a failing case. The
+    // shim should continue executing without a hint, so we report an error
+    // "successfully". This allows the shim to distinguish this from the other
+    // unexpected error cases.
+    //
+    // We intentionally avoid printing the error in this case, to avoid mixing
+    // up test expectations with errors from the shim.
+    char msg = kControlMsgError;
+    if (write_eintr(control, &msg, 1) == -1) {
+      return false;
+    }
+    return true;
+  }
+
+  bssl::ScopedCBB hints;
+  if (!CBB_init(hints.get(), 256) ||
+      !SSL_serialize_handshake_hints(ssl.get(), hints.get())) {
+    fprintf(stderr, "Handshaker: failed to serialize handshake hints\n");
+    return false;
+  }
+
+  char msg = kControlMsgDone;
+  if (write_eintr(control, &msg, 1) == -1 ||
+      write_eintr(control, CBB_data(hints.get()), CBB_len(hints.get())) == -1) {
+    perror("write");
+    return false;
+  }
+
+  return true;
 }
 
 int SignalError() {
@@ -139,12 +224,12 @@
 
 int main(int argc, char **argv) {
   TestConfig initial_config, resume_config, retry_config;
-  if (!ParseConfig(argc - 1, argv + 1, &initial_config, &resume_config,
-                   &retry_config)) {
+  if (!ParseConfig(argc - 1, argv + 1, /*is_shim=*/false, &initial_config,
+                   &resume_config, &retry_config)) {
     return SignalError();
   }
-  const TestConfig *config = initial_config.handshaker_resume
-      ? &resume_config : &initial_config;
+  const TestConfig *config =
+      initial_config.handshaker_resume ? &resume_config : &initial_config;
 #if defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
   if (initial_config.handshaker_resume) {
     // If the PRNG returns exactly the same values when trying to resume then a
@@ -159,16 +244,23 @@
   // read() will return the entire message in one go, because it's a datagram
   // socket.
   constexpr size_t kBufSize = 1024 * 1024;
-  bssl::UniquePtr<uint8_t> buf((uint8_t *) OPENSSL_malloc(kBufSize));
-  ssize_t len = read_eintr(kFdControl, buf.get(), kBufSize);
+  std::vector<uint8_t> request(kBufSize);
+  ssize_t len = read_eintr(kFdControl, request.data(), request.size());
   if (len == -1) {
     perror("read");
     return 2;
   }
-  Span<uint8_t> handoff(buf.get(), len);
-  if (!Handshaker(config, kFdProxyToHandshaker, kFdHandshakerToProxy, handoff,
-                  kFdControl)) {
-    return SignalError();
+  request.resize(static_cast<size_t>(len));
+
+  if (config->handshake_hints) {
+    if (!GenerateHandshakeHint(config, request, kFdControl)) {
+      return SignalError();
+    }
+  } else {
+    if (!Handshaker(config, kFdProxyToHandshaker, kFdHandshakerToProxy,
+                    request, kFdControl)) {
+      return SignalError();
+    }
   }
   return 0;
 }
diff --git a/deps/boringssl/src/ssl/test/mock_quic_transport.cc b/deps/boringssl/src/ssl/test/mock_quic_transport.cc
index 4b8bc30..310b779 100644
--- a/deps/boringssl/src/ssl/test/mock_quic_transport.cc
+++ b/deps/boringssl/src/ssl/test/mock_quic_transport.cc
@@ -271,7 +271,7 @@
   return WriteRecord(level, SSL3_RT_APPLICATION_DATA, in, len);
 }
 
-bool MockQuicTransport::Flush() { return BIO_flush(bio_.get()); }
+bool MockQuicTransport::Flush() { return BIO_flush(bio_.get()) > 0; }
 
 bool MockQuicTransport::SendAlert(enum ssl_encryption_level_t level,
                                   uint8_t alert) {
diff --git a/deps/boringssl/src/ssl/test/settings_writer.cc b/deps/boringssl/src/ssl/test/settings_writer.cc
index fe8d42e..8605222 100644
--- a/deps/boringssl/src/ssl/test/settings_writer.cc
+++ b/deps/boringssl/src/ssl/test/settings_writer.cc
@@ -85,29 +85,26 @@
 }
 
 bool SettingsWriter::WriteHandoff(bssl::Span<const uint8_t> handoff) {
-  if (path_.empty()) {
-    return true;
-  }
-
-  CBB child;
-  if (!CBB_add_u16(cbb_.get(), kHandoffTag) ||
-      !CBB_add_u24_length_prefixed(cbb_.get(), &child) ||
-      !CBB_add_bytes(&child, handoff.data(), handoff.size()) ||
-      !CBB_flush(cbb_.get())) {
-    return false;
-  }
-  return true;
+  return WriteData(kHandoffTag, handoff);
 }
 
 bool SettingsWriter::WriteHandback(bssl::Span<const uint8_t> handback) {
+  return WriteData(kHandbackTag, handback);
+}
+
+bool SettingsWriter::WriteHints(bssl::Span<const uint8_t> hints) {
+  return WriteData(kHintsTag, hints);
+}
+
+bool SettingsWriter::WriteData(uint16_t tag, bssl::Span<const uint8_t> data) {
   if (path_.empty()) {
     return true;
   }
 
   CBB child;
-  if (!CBB_add_u16(cbb_.get(), kHandbackTag) ||
+  if (!CBB_add_u16(cbb_.get(), tag) ||
       !CBB_add_u24_length_prefixed(cbb_.get(), &child) ||
-      !CBB_add_bytes(&child, handback.data(), handback.size()) ||
+      !CBB_add_bytes(&child, data.data(), data.size()) ||
       !CBB_flush(cbb_.get())) {
     return false;
   }
diff --git a/deps/boringssl/src/ssl/test/settings_writer.h b/deps/boringssl/src/ssl/test/settings_writer.h
index 322850d..e1ffdc1 100644
--- a/deps/boringssl/src/ssl/test/settings_writer.h
+++ b/deps/boringssl/src/ssl/test/settings_writer.h
@@ -20,7 +20,6 @@
 #include <openssl/bytestring.h>
 #include <openssl/ssl.h>
 
-#include "../internal.h"
 #include "test_config.h"
 
 struct SettingsWriter {
@@ -35,10 +34,12 @@
   bool Commit();
 
   bool WriteHandoff(bssl::Span<const uint8_t> handoff);
-
   bool WriteHandback(bssl::Span<const uint8_t> handback);
+  bool WriteHints(bssl::Span<const uint8_t> hints);
 
  private:
+  bool WriteData(uint16_t tag, bssl::Span<const uint8_t> data);
+
   std::string path_;
   bssl::ScopedCBB cbb_;
 };
diff --git a/deps/boringssl/src/ssl/test/test_config.cc b/deps/boringssl/src/ssl/test/test_config.cc
index c1d215b..7d1cefa 100644
--- a/deps/boringssl/src/ssl/test/test_config.cc
+++ b/deps/boringssl/src/ssl/test/test_config.cc
@@ -22,11 +22,14 @@
 #include <memory>
 
 #include <openssl/base64.h>
+#include <openssl/hpke.h>
 #include <openssl/rand.h>
 #include <openssl/ssl.h>
 
 #include "../../crypto/internal.h"
 #include "../internal.h"
+#include "handshake_util.h"
+#include "mock_quic_transport.h"
 #include "test_state.h"
 
 namespace {
@@ -56,6 +59,9 @@
     {"-quic", &TestConfig::is_quic},
     {"-fallback-scsv", &TestConfig::fallback_scsv},
     {"-enable-ech-grease", &TestConfig::enable_ech_grease},
+    {"-expect-ech-accept", &TestConfig::expect_ech_accept},
+    {"-expect-no-ech-name-override", &TestConfig::expect_no_ech_name_override},
+    {"-expect-no-ech-retry-configs", &TestConfig::expect_no_ech_retry_configs},
     {"-require-any-client-certificate",
      &TestConfig::require_any_client_certificate},
     {"-false-start", &TestConfig::false_start},
@@ -73,6 +79,7 @@
     {"-shim-writes-first", &TestConfig::shim_writes_first},
     {"-expect-session-miss", &TestConfig::expect_session_miss},
     {"-decline-alpn", &TestConfig::decline_alpn},
+    {"-reject-alpn", &TestConfig::reject_alpn},
     {"-select-empty-alpn", &TestConfig::select_empty_alpn},
     {"-defer-alps", &TestConfig::defer_alps},
     {"-expect-extended-master-secret",
@@ -108,12 +115,12 @@
     {"-renegotiate-explicit", &TestConfig::renegotiate_explicit},
     {"-forbid-renegotiation-after-handshake",
      &TestConfig::forbid_renegotiation_after_handshake},
-    {"-enable-all-curves", &TestConfig::enable_all_curves},
     {"-use-old-client-cert-callback",
      &TestConfig::use_old_client_cert_callback},
     {"-send-alert", &TestConfig::send_alert},
     {"-peek-then-read", &TestConfig::peek_then_read},
     {"-enable-grease", &TestConfig::enable_grease},
+    {"-permute-extensions", &TestConfig::permute_extensions},
     {"-use-exporter-between-reads", &TestConfig::use_exporter_between_reads},
     {"-retain-only-sha256-client-cert",
      &TestConfig::retain_only_sha256_client_cert},
@@ -134,6 +141,8 @@
     {"-allow-false-start-without-alpn",
      &TestConfig::allow_false_start_without_alpn},
     {"-handoff", &TestConfig::handoff},
+    {"-handshake-hints", &TestConfig::handshake_hints},
+    {"-allow-hint-mismatch", &TestConfig::allow_hint_mismatch},
     {"-use-ocsp-callback", &TestConfig::use_ocsp_callback},
     {"-set-ocsp-in-callback", &TestConfig::set_ocsp_in_callback},
     {"-decline-ocsp-callback", &TestConfig::decline_ocsp_callback},
@@ -160,6 +169,7 @@
     {"-key-file", &TestConfig::key_file},
     {"-cert-file", &TestConfig::cert_file},
     {"-expect-server-name", &TestConfig::expect_server_name},
+    {"-expect-ech-name-override", &TestConfig::expect_ech_name_override},
     {"-advertise-npn", &TestConfig::advertise_npn},
     {"-expect-next-proto", &TestConfig::expect_next_proto},
     {"-select-next-proto", &TestConfig::select_next_proto},
@@ -194,9 +204,10 @@
 };
 
 const Flag<std::string> kBase64Flags[] = {
+    {"-expect-ech-retry-configs", &TestConfig::expect_ech_retry_configs},
+    {"-ech-config-list", &TestConfig::ech_config_list},
     {"-expect-certificate-types", &TestConfig::expect_certificate_types},
     {"-expect-channel-id", &TestConfig::expect_channel_id},
-    {"-token-binding-params", &TestConfig::send_token_binding_params},
     {"-expect-ocsp-response", &TestConfig::expect_ocsp_response},
     {"-expect-signed-cert-timestamps",
      &TestConfig::expect_signed_cert_timestamps},
@@ -211,7 +222,6 @@
 const Flag<int> kIntFlags[] = {
     {"-port", &TestConfig::port},
     {"-resume-count", &TestConfig::resume_count},
-    {"-expect-token-binding-param", &TestConfig::expect_token_binding_param},
     {"-min-version", &TestConfig::min_version},
     {"-max-version", &TestConfig::max_version},
     {"-expect-version", &TestConfig::expect_version},
@@ -231,6 +241,9 @@
     {"-read-size", &TestConfig::read_size},
     {"-expect-ticket-age-skew", &TestConfig::expect_ticket_age_skew},
     {"-quic-use-legacy-codepoint", &TestConfig::quic_use_legacy_codepoint},
+    {"-install-one-cert-compression-alg",
+     &TestConfig::install_one_cert_compression_alg},
+    {"-early-write-after-message", &TestConfig::early_write_after_message},
 };
 
 const Flag<std::vector<int>> kIntVectorFlags[] = {
@@ -238,6 +251,12 @@
     {"-verify-prefs", &TestConfig::verify_prefs},
     {"-expect-peer-verify-pref", &TestConfig::expect_peer_verify_prefs},
     {"-curves", &TestConfig::curves},
+    {"-ech-is-retry-config", &TestConfig::ech_is_retry_config},
+};
+
+const Flag<std::vector<std::string>> kBase64VectorFlags[] = {
+    {"-ech-server-config", &TestConfig::ech_server_configs},
+    {"-ech-server-key", &TestConfig::ech_server_keys},
 };
 
 const Flag<std::vector<std::pair<std::string, std::string>>>
@@ -245,7 +264,24 @@
         {"-application-settings", &TestConfig::application_settings},
 };
 
-bool ParseFlag(char *flag, int argc, char **argv, int *i,
+bool DecodeBase64(std::string *out, const std::string &in) {
+  size_t len;
+  if (!EVP_DecodedLength(&len, in.size())) {
+    fprintf(stderr, "Invalid base64: %s.\n", in.c_str());
+    return false;
+  }
+  std::vector<uint8_t> buf(len);
+  if (!EVP_DecodeBase64(buf.data(), &len, buf.size(),
+                        reinterpret_cast<const uint8_t *>(in.data()),
+                        in.size())) {
+    fprintf(stderr, "Invalid base64: %s.\n", in.c_str());
+    return false;
+  }
+  out->assign(reinterpret_cast<const char *>(buf.data()), len);
+  return true;
+}
+
+bool ParseFlag(const char *flag, int argc, char **argv, int *i,
                bool skip, TestConfig *out_config) {
   bool *bool_field = FindField(out_config, kBoolFlags, flag);
   if (bool_field != NULL) {
@@ -289,21 +325,12 @@
       fprintf(stderr, "Missing parameter.\n");
       return false;
     }
-    size_t len;
-    if (!EVP_DecodedLength(&len, strlen(argv[*i]))) {
-      fprintf(stderr, "Invalid base64: %s.\n", argv[*i]);
-      return false;
-    }
-    std::unique_ptr<uint8_t[]> decoded(new uint8_t[len]);
-    if (!EVP_DecodeBase64(decoded.get(), &len, len,
-                          reinterpret_cast<const uint8_t *>(argv[*i]),
-                          strlen(argv[*i]))) {
-      fprintf(stderr, "Invalid base64: %s.\n", argv[*i]);
+    std::string value;
+    if (!DecodeBase64(&value, argv[*i])) {
       return false;
     }
     if (!skip) {
-      base64_field->assign(reinterpret_cast<const char *>(decoded.get()),
-                           len);
+      *base64_field = std::move(value);
     }
     return true;
   }
@@ -337,6 +364,25 @@
     return true;
   }
 
+  std::vector<std::string> *base64_vector_field =
+      FindField(out_config, kBase64VectorFlags, flag);
+  if (base64_vector_field) {
+    *i = *i + 1;
+    if (*i >= argc) {
+      fprintf(stderr, "Missing parameter.\n");
+      return false;
+    }
+    std::string value;
+    if (!DecodeBase64(&value, argv[*i])) {
+      return false;
+    }
+    // Each instance of the flag adds to the list.
+    if (!skip) {
+      base64_vector_field->push_back(std::move(value));
+    }
+    return true;
+  }
+
   std::vector<std::pair<std::string, std::string>> *string_pair_vector_field =
       FindField(out_config, kStringPairVectorFlags, flag);
   if (string_pair_vector_field) {
@@ -347,8 +393,10 @@
     }
     const char *comma = strchr(argv[*i], ',');
     if (!comma) {
-      fprintf(stderr,
-              "Parameter should be a pair of comma-separated strings.\n");
+      fprintf(
+          stderr,
+          "Parameter should be a comma-separated triple composed of two base64 "
+          "strings followed by \"true\" or \"false\".\n");
       return false;
     }
     // Each instance of the flag adds to the list.
@@ -363,13 +411,21 @@
   return false;
 }
 
-const char kInit[] = "-on-initial";
-const char kResume[] = "-on-resume";
-const char kRetry[] = "-on-retry";
+// RemovePrefix checks if |*str| begins with |prefix| + "-". If so, it advances
+// |*str| past |prefix| (but not past the "-") and returns true. Otherwise, it
+// returns false and leaves |*str| unmodified.
+bool RemovePrefix(const char **str, const char *prefix) {
+  size_t prefix_len = strlen(prefix);
+  if (strncmp(*str, prefix, strlen(prefix)) == 0 && (*str)[prefix_len] == '-') {
+    *str += strlen(prefix);
+    return true;
+  }
+  return false;
+}
 
 }  // namespace
 
-bool ParseConfig(int argc, char **argv,
+bool ParseConfig(int argc, char **argv, bool is_shim,
                  TestConfig *out_initial,
                  TestConfig *out_resume,
                  TestConfig *out_retry) {
@@ -377,21 +433,36 @@
   out_initial->argv = out_resume->argv = out_retry->argv = argv;
   for (int i = 0; i < argc; i++) {
     bool skip = false;
-    char *flag = argv[i];
-    if (strncmp(flag, kInit, strlen(kInit)) == 0) {
-      if (!ParseFlag(flag + strlen(kInit), argc, argv, &i, skip, out_initial)) {
+    const char *flag = argv[i];
+
+    // -on-shim and -on-handshaker prefixes enable flags only on the shim or
+    // handshaker.
+    if (RemovePrefix(&flag, "-on-shim")) {
+      if (!is_shim) {
+        skip = true;
+      }
+    } else if (RemovePrefix(&flag, "-on-handshaker")) {
+      if (is_shim) {
+        skip = true;
+      }
+    }
+
+    // The following prefixes allow different configurations for each of the
+    // initial, resumption, and 0-RTT retry handshakes.
+    if (RemovePrefix(&flag, "-on-initial")) {
+      if (!ParseFlag(flag, argc, argv, &i, skip, out_initial)) {
         return false;
       }
-    } else if (strncmp(flag, kResume, strlen(kResume)) == 0) {
-      if (!ParseFlag(flag + strlen(kResume), argc, argv, &i, skip,
-                     out_resume)) {
+    } else if (RemovePrefix(&flag, "-on-resume")) {
+      if (!ParseFlag(flag, argc, argv, &i, skip, out_resume)) {
         return false;
       }
-    } else if (strncmp(flag, kRetry, strlen(kRetry)) == 0) {
-      if (!ParseFlag(flag + strlen(kRetry), argc, argv, &i, skip, out_retry)) {
+    } else if (RemovePrefix(&flag, "-on-retry")) {
+      if (!ParseFlag(flag, argc, argv, &i, skip, out_retry)) {
         return false;
       }
     } else {
+      // Unprefixed flags apply to all three.
       int i_init = i;
       int i_resume = i;
       if (!ParseFlag(flag, argc, argv, &i_init, skip, out_initial) ||
@@ -537,6 +608,9 @@
       char text[16];
       snprintf(text, sizeof(text), "hs %d\n", type);
       state->msg_callback_text += text;
+      if (!is_write) {
+        state->last_message_received = type;
+      }
       return;
     }
 
@@ -628,17 +702,9 @@
       abort();
     }
     GetTestState(ssl)->handshake_done = true;
-
-    // Callbacks may be called again on a new handshake.
-    GetTestState(ssl)->ticket_decrypt_done = false;
-    GetTestState(ssl)->alpn_select_done = false;
   }
 }
 
-static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) {
-  *out_pkey = GetTestState(ssl)->channel_id.release();
-}
-
 static SSL_SESSION *GetSessionCallback(SSL *ssl, const uint8_t *data, int len,
                                        int *copy) {
   TestState *async_state = GetTestState(ssl);
@@ -669,6 +735,9 @@
   if (config->decline_alpn) {
     return SSL_TLSEXT_ERR_NOACK;
   }
+  if (config->reject_alpn) {
+    return SSL_TLSEXT_ERR_ALERT_FATAL;
+  }
 
   if (!config->expect_advertised_alpn.empty() &&
       (config->expect_advertised_alpn.size() != inlen ||
@@ -709,6 +778,20 @@
     }
   }
 
+  const char *name_override;
+  size_t name_override_len;
+  SSL_get0_ech_name_override(ssl, &name_override, &name_override_len);
+  if (config->expect_no_ech_name_override && name_override_len != 0) {
+    fprintf(stderr, "Unexpected ECH name override.\n");
+    return false;
+  }
+  if (!config->expect_ech_name_override.empty() &&
+      config->expect_ech_name_override !=
+          std::string(name_override, name_override_len)) {
+    fprintf(stderr, "ECH name did not match expected value.\n");
+    return false;
+  }
+
   if (GetTestState(ssl)->cert_verified) {
     fprintf(stderr, "Certificate verified twice.\n");
     return false;
@@ -727,7 +810,7 @@
 
   GetTestState(ssl)->cert_verified = true;
   if (config->verify_fail) {
-    store_ctx->error = X509_V_ERR_APPLICATION_VERIFICATION;
+    X509_STORE_CTX_set_error(store_ctx, X509_V_ERR_APPLICATION_VERIFICATION);
     return 0;
   }
 
@@ -1018,10 +1101,15 @@
   return 1;
 }
 
+static ssl_private_key_result_t AsyncPrivateKeyComplete(SSL *ssl, uint8_t *out,
+                                                        size_t *out_len,
+                                                        size_t max_out);
+
 static ssl_private_key_result_t AsyncPrivateKeySign(
     SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
     uint16_t signature_algorithm, const uint8_t *in, size_t in_len) {
   TestState *test_state = GetTestState(ssl);
+  test_state->used_private_key = true;
   if (!test_state->private_key_result.empty()) {
     fprintf(stderr, "AsyncPrivateKeySign called with operation pending.\n");
     abort();
@@ -1062,8 +1150,7 @@
   }
   test_state->private_key_result.resize(len);
 
-  // The signature will be released asynchronously in |AsyncPrivateKeyComplete|.
-  return ssl_private_key_retry;
+  return AsyncPrivateKeyComplete(ssl, out, out_len, max_out);
 }
 
 static ssl_private_key_result_t AsyncPrivateKeyDecrypt(SSL *ssl, uint8_t *out,
@@ -1072,6 +1159,7 @@
                                                        const uint8_t *in,
                                                        size_t in_len) {
   TestState *test_state = GetTestState(ssl);
+  test_state->used_private_key = true;
   if (!test_state->private_key_result.empty()) {
     fprintf(stderr, "AsyncPrivateKeyDecrypt called with operation pending.\n");
     abort();
@@ -1090,8 +1178,7 @@
 
   test_state->private_key_result.resize(*out_len);
 
-  // The decryption will be released asynchronously in |AsyncPrivateComplete|.
-  return ssl_private_key_retry;
+  return AsyncPrivateKeyComplete(ssl, out, out_len, max_out);
 }
 
 static ssl_private_key_result_t AsyncPrivateKeyComplete(SSL *ssl, uint8_t *out,
@@ -1104,9 +1191,9 @@
     abort();
   }
 
-  if (test_state->private_key_retries < 2) {
+  if (GetTestConfig(ssl)->async && test_state->private_key_retries < 2) {
     // Only return the decryption on the second attempt, to test both incomplete
-    // |decrypt| and |decrypt_complete|.
+    // |sign|/|decrypt| and |complete|.
     return ssl_private_key_retry;
   }
 
@@ -1140,7 +1227,10 @@
   if (pkey) {
     TestState *test_state = GetTestState(ssl);
     const TestConfig *config = GetTestConfig(ssl);
-    if (config->async) {
+    if (config->async || config->handshake_hints) {
+      // Install a custom private key if testing asynchronous callbacks, or if
+      // testing handshake hints. In the handshake hints case, we wish to check
+      // that hints only mismatch when allowed.
       test_state->private_key = std::move(pkey);
       SSL_set_private_key_method(ssl, &g_async_private_key_method);
     } else if (!SSL_use_PrivateKey(ssl, pkey.get())) {
@@ -1161,12 +1251,14 @@
 
 static enum ssl_select_cert_result_t SelectCertificateCallback(
     const SSL_CLIENT_HELLO *client_hello) {
-  const TestConfig *config = GetTestConfig(client_hello->ssl);
-  GetTestState(client_hello->ssl)->early_callback_called = true;
+  SSL *ssl = client_hello->ssl;
+  const TestConfig *config = GetTestConfig(ssl);
+  TestState *test_state = GetTestState(ssl);
+  test_state->early_callback_called = true;
 
   if (!config->expect_server_name.empty()) {
     const char *server_name =
-        SSL_get_servername(client_hello->ssl, TLSEXT_NAMETYPE_host_name);
+        SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
     if (server_name == nullptr ||
         std::string(server_name) != config->expect_server_name) {
       fprintf(stderr,
@@ -1180,48 +1272,73 @@
     return ssl_select_cert_error;
   }
 
-  // Install the certificate in the early callback.
-  if (config->use_early_callback) {
-    bool early_callback_ready =
-        GetTestState(client_hello->ssl)->early_callback_ready;
-    if (config->async && !early_callback_ready) {
-      // Install the certificate asynchronously.
-      return ssl_select_cert_retry;
-    }
-    if (!InstallCertificate(client_hello->ssl)) {
-      return ssl_select_cert_error;
-    }
+  // Simulate some asynchronous work in the early callback.
+  if ((config->use_early_callback || test_state->get_handshake_hints_cb) &&
+      config->async && !test_state->early_callback_ready) {
+    return ssl_select_cert_retry;
   }
+
+  if (test_state->get_handshake_hints_cb &&
+      !test_state->get_handshake_hints_cb(client_hello)) {
+    return ssl_select_cert_error;
+  }
+
+  if (config->use_early_callback && !InstallCertificate(ssl)) {
+    return ssl_select_cert_error;
+  }
+
   return ssl_select_cert_success;
 }
 
 static int SetQuicReadSecret(SSL *ssl, enum ssl_encryption_level_t level,
                              const SSL_CIPHER *cipher, const uint8_t *secret,
                              size_t secret_len) {
-  return GetTestState(ssl)->quic_transport->SetReadSecret(level, cipher, secret,
-                                                          secret_len);
+  MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
+  if (quic_transport == nullptr) {
+    fprintf(stderr, "No QUIC transport.\n");
+    return 0;
+  }
+  return quic_transport->SetReadSecret(level, cipher, secret, secret_len);
 }
 
 static int SetQuicWriteSecret(SSL *ssl, enum ssl_encryption_level_t level,
                               const SSL_CIPHER *cipher, const uint8_t *secret,
                               size_t secret_len) {
-  return GetTestState(ssl)->quic_transport->SetWriteSecret(level, cipher,
-                                                           secret, secret_len);
+  MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
+  if (quic_transport == nullptr) {
+    fprintf(stderr, "No QUIC transport.\n");
+    return 0;
+  }
+  return quic_transport->SetWriteSecret(level, cipher, secret, secret_len);
 }
 
 static int AddQuicHandshakeData(SSL *ssl, enum ssl_encryption_level_t level,
                                 const uint8_t *data, size_t len) {
-  return GetTestState(ssl)->quic_transport->WriteHandshakeData(level, data,
-                                                               len);
+  MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
+  if (quic_transport == nullptr) {
+    fprintf(stderr, "No QUIC transport.\n");
+    return 0;
+  }
+  return quic_transport->WriteHandshakeData(level, data, len);
 }
 
 static int FlushQuicFlight(SSL *ssl) {
-  return GetTestState(ssl)->quic_transport->Flush();
+  MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
+  if (quic_transport == nullptr) {
+    fprintf(stderr, "No QUIC transport.\n");
+    return 0;
+  }
+  return quic_transport->Flush();
 }
 
 static int SendQuicAlert(SSL *ssl, enum ssl_encryption_level_t level,
                          uint8_t alert) {
-  return GetTestState(ssl)->quic_transport->SendAlert(level, alert);
+  MockQuicTransport *quic_transport = GetTestState(ssl)->quic_transport.get();
+  if (quic_transport == nullptr) {
+    fprintf(stderr, "No QUIC transport.\n");
+    return 0;
+  }
+  return quic_transport->SendAlert(level, alert);
 }
 
 static const SSL_QUIC_METHOD g_quic_method = {
@@ -1232,6 +1349,17 @@
     SendQuicAlert,
 };
 
+static bool MaybeInstallCertCompressionAlg(
+    const TestConfig *config, SSL_CTX *ssl_ctx, uint16_t alg,
+    ssl_cert_compression_func_t compress,
+    ssl_cert_decompression_func_t decompress) {
+  if (!config->install_cert_compression_algs &&
+      config->install_one_cert_compression_alg != alg) {
+    return true;
+  }
+  return SSL_CTX_add_cert_compression_alg(ssl_ctx, alg, compress, decompress);
+}
+
 bssl::UniquePtr<SSL_CTX> TestConfig::SetupCtx(SSL_CTX *old_ctx) const {
   bssl::UniquePtr<SSL_CTX> ssl_ctx(
       SSL_CTX_new(is_dtls ? DTLS_method() : TLS_method()));
@@ -1274,18 +1402,20 @@
                                      NULL);
   }
 
-  if (!select_alpn.empty() || decline_alpn || select_empty_alpn) {
+  if (!select_alpn.empty() || decline_alpn || reject_alpn ||
+      select_empty_alpn) {
     SSL_CTX_set_alpn_select_cb(ssl_ctx.get(), AlpnSelectCallback, NULL);
   }
 
-  SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
-
   SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
 
   SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
   SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
 
-  if (use_ticket_callback) {
+  if (use_ticket_callback || handshake_hints) {
+    // If using handshake hints, always enable the ticket callback, so we can
+    // check that hints only mismatch when allowed. The ticket callback also
+    // uses a constant key, which simplifies the test.
     SSL_CTX_set_tlsext_ticket_key_cb(ssl_ctx.get(), TicketKeyCallback);
   }
 
@@ -1317,6 +1447,10 @@
     SSL_CTX_set_grease_enabled(ssl_ctx.get(), 1);
   }
 
+  if (permute_extensions) {
+    SSL_CTX_set_permute_extensions(ssl_ctx.get(), 1);
+  }
+
   if (!expect_server_name.empty()) {
     SSL_CTX_set_tlsext_servername_callback(ssl_ctx.get(), ServerNameCallback);
   }
@@ -1360,48 +1494,65 @@
     return nullptr;
   }
 
-  if (install_cert_compression_algs &&
-      (!SSL_CTX_add_cert_compression_alg(
-           ssl_ctx.get(), 0xff02,
-           [](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
-             if (!CBB_add_u8(out, 1) || !CBB_add_u8(out, 2) ||
-                 !CBB_add_u8(out, 3) || !CBB_add_u8(out, 4) ||
-                 !CBB_add_bytes(out, in, in_len)) {
-               return 0;
-             }
-             return 1;
-           },
-           [](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
-              const uint8_t *in, size_t in_len) -> int {
-             if (in_len < 4 || in[0] != 1 || in[1] != 2 || in[2] != 3 ||
-                 in[3] != 4 || uncompressed_len != in_len - 4) {
-               return 0;
-             }
-             const bssl::Span<const uint8_t> uncompressed(in + 4, in_len - 4);
-             *out = CRYPTO_BUFFER_new(uncompressed.data(), uncompressed.size(),
-                                      nullptr);
-             return 1;
-           }) ||
-       !SSL_CTX_add_cert_compression_alg(
-           ssl_ctx.get(), 0xff01,
-           [](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
-             if (in_len < 2 || in[0] != 0 || in[1] != 0) {
-               return 0;
-             }
-             return CBB_add_bytes(out, in + 2, in_len - 2);
-           },
-           [](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
-              const uint8_t *in, size_t in_len) -> int {
-             if (uncompressed_len != 2 + in_len) {
-               return 0;
-             }
-             std::unique_ptr<uint8_t[]> buf(new uint8_t[2 + in_len]);
-             buf[0] = 0;
-             buf[1] = 0;
-             OPENSSL_memcpy(&buf[2], in, in_len);
-             *out = CRYPTO_BUFFER_new(buf.get(), 2 + in_len, nullptr);
-             return 1;
-           }))) {
+  // These mock compression algorithms match the corresponding ones in
+  // |addCertCompressionTests|.
+  if (!MaybeInstallCertCompressionAlg(
+          this, ssl_ctx.get(), 0xff02,
+          [](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
+            if (!CBB_add_u8(out, 1) || !CBB_add_u8(out, 2) ||
+                !CBB_add_u8(out, 3) || !CBB_add_u8(out, 4) ||
+                !CBB_add_bytes(out, in, in_len)) {
+              return 0;
+            }
+            return 1;
+          },
+          [](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
+             const uint8_t *in, size_t in_len) -> int {
+            if (in_len < 4 || in[0] != 1 || in[1] != 2 || in[2] != 3 ||
+                in[3] != 4 || uncompressed_len != in_len - 4) {
+              return 0;
+            }
+            const bssl::Span<const uint8_t> uncompressed(in + 4, in_len - 4);
+            *out = CRYPTO_BUFFER_new(uncompressed.data(), uncompressed.size(),
+                                     nullptr);
+            return *out != nullptr;
+          }) ||
+      !MaybeInstallCertCompressionAlg(
+          this, ssl_ctx.get(), 0xff01,
+          [](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
+            if (in_len < 2 || in[0] != 0 || in[1] != 0) {
+              return 0;
+            }
+            return CBB_add_bytes(out, in + 2, in_len - 2);
+          },
+          [](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
+             const uint8_t *in, size_t in_len) -> int {
+            if (uncompressed_len != 2 + in_len) {
+              return 0;
+            }
+            std::unique_ptr<uint8_t[]> buf(new uint8_t[2 + in_len]);
+            buf[0] = 0;
+            buf[1] = 0;
+            OPENSSL_memcpy(&buf[2], in, in_len);
+            *out = CRYPTO_BUFFER_new(buf.get(), 2 + in_len, nullptr);
+            return *out != nullptr;
+          }) ||
+      !MaybeInstallCertCompressionAlg(
+          this, ssl_ctx.get(), 0xff03,
+          [](SSL *ssl, CBB *out, const uint8_t *in, size_t in_len) -> int {
+            uint8_t byte;
+            return RAND_bytes(&byte, 1) &&   //
+                   CBB_add_u8(out, byte) &&  //
+                   CBB_add_bytes(out, in, in_len);
+          },
+          [](SSL *ssl, CRYPTO_BUFFER **out, size_t uncompressed_len,
+             const uint8_t *in, size_t in_len) -> int {
+            if (uncompressed_len + 1 != in_len) {
+              return 0;
+            }
+            *out = CRYPTO_BUFFER_new(in + 1, in_len - 1, nullptr);
+            return *out != nullptr;
+          })) {
     fprintf(stderr, "SSL_CTX_add_cert_compression_alg failed.\n");
     abort();
   }
@@ -1514,7 +1665,7 @@
 }
 
 bssl::UniquePtr<SSL> TestConfig::NewSSL(
-    SSL_CTX *ssl_ctx, SSL_SESSION *session, bool is_resume,
+    SSL_CTX *ssl_ctx, SSL_SESSION *session,
     std::unique_ptr<TestState> test_state) const {
   bssl::UniquePtr<SSL> ssl(SSL_new(ssl_ctx));
   if (!ssl) {
@@ -1528,7 +1679,6 @@
     if (!SetTestState(ssl.get(), std::move(test_state))) {
       return nullptr;
     }
-    GetTestState(ssl.get())->is_resume = is_resume;
   }
 
   if (fallback_scsv && !SSL_set_mode(ssl.get(), SSL_MODE_SEND_FALLBACK_SCSV)) {
@@ -1596,21 +1746,49 @@
   if (enable_ech_grease) {
     SSL_set_enable_ech_grease(ssl.get(), 1);
   }
-  if (!send_channel_id.empty()) {
-    SSL_set_tls_channel_id_enabled(ssl.get(), 1);
-    if (!async) {
-      // The async case will be supplied by |ChannelIdCallback|.
-      bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(send_channel_id);
-      if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) {
+  if (!ech_config_list.empty() &&
+      !SSL_set1_ech_config_list(
+          ssl.get(), reinterpret_cast<const uint8_t *>(ech_config_list.data()),
+          ech_config_list.size())) {
+    return nullptr;
+  }
+  if (ech_server_configs.size() != ech_server_keys.size() ||
+      ech_server_configs.size() != ech_is_retry_config.size()) {
+    fprintf(stderr,
+            "-ech-server-config, -ech-server-key, and -ech-is-retry-config "
+            "flags must match.\n");
+    return nullptr;
+  }
+  if (!ech_server_configs.empty()) {
+    bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+    if (!keys) {
+      return nullptr;
+    }
+    for (size_t i = 0; i < ech_server_configs.size(); i++) {
+      const std::string &ech_config = ech_server_configs[i];
+      const std::string &ech_private_key = ech_server_keys[i];
+      const int is_retry_config = ech_is_retry_config[i];
+      bssl::ScopedEVP_HPKE_KEY key;
+      if (!EVP_HPKE_KEY_init(
+              key.get(), EVP_hpke_x25519_hkdf_sha256(),
+              reinterpret_cast<const uint8_t *>(ech_private_key.data()),
+              ech_private_key.size()) ||
+          !SSL_ECH_KEYS_add(
+              keys.get(), is_retry_config,
+              reinterpret_cast<const uint8_t *>(ech_config.data()),
+              ech_config.size(), key.get())) {
         return nullptr;
       }
     }
+    if (!SSL_CTX_set1_ech_keys(ssl_ctx, keys.get())) {
+      return nullptr;
+    }
   }
-  if (!send_token_binding_params.empty()) {
-    SSL_set_token_binding_params(
-        ssl.get(),
-        reinterpret_cast<const uint8_t *>(send_token_binding_params.data()),
-        send_token_binding_params.length());
+  if (!send_channel_id.empty()) {
+    bssl::UniquePtr<EVP_PKEY> pkey = LoadPrivateKey(send_channel_id);
+    if (!pkey || !SSL_set1_tls_channel_id(ssl.get(), pkey.get())) {
+      return nullptr;
+    }
   }
   if (!host_name.empty() &&
       !SSL_set_tlsext_host_name(ssl.get(), host_name.c_str())) {
@@ -1716,16 +1894,6 @@
       }
     }
   }
-  if (enable_all_curves) {
-    static const int kAllCurves[] = {
-        NID_secp224r1, NID_X9_62_prime256v1, NID_secp384r1,
-        NID_secp521r1, NID_X25519,           NID_CECPQ2,
-    };
-    if (!SSL_set1_curves(ssl.get(), kAllCurves,
-                         OPENSSL_ARRAY_SIZE(kAllCurves))) {
-      return nullptr;
-    }
-  }
   if (initial_timeout_duration_ms > 0) {
     DTLSv1_set_initial_timeout_duration(ssl.get(), initial_timeout_duration_ms);
   }
diff --git a/deps/boringssl/src/ssl/test/test_config.h b/deps/boringssl/src/ssl/test/test_config.h
index 7d77994..c9f2a25 100644
--- a/deps/boringssl/src/ssl/test/test_config.h
+++ b/deps/boringssl/src/ssl/test/test_config.h
@@ -40,6 +40,15 @@
   std::string cert_file;
   std::string expect_server_name;
   bool enable_ech_grease = false;
+  std::vector<std::string> ech_server_configs;
+  std::vector<std::string> ech_server_keys;
+  std::vector<int> ech_is_retry_config;
+  bool expect_ech_accept = false;
+  std::string expect_ech_name_override;
+  bool expect_no_ech_name_override = false;
+  std::string expect_ech_retry_configs;
+  bool expect_no_ech_retry_configs = false;
+  std::string ech_config_list;
   std::string expect_certificate_types;
   bool require_any_client_certificate = false;
   std::string advertise_npn;
@@ -58,8 +67,6 @@
   std::string expect_channel_id;
   bool enable_channel_id = false;
   std::string send_channel_id;
-  int expect_token_binding_param = -1;
-  std::string send_token_binding_params;
   bool shim_writes_first = false;
   std::string host_name;
   std::string advertise_alpn;
@@ -68,6 +75,7 @@
   std::string expect_advertised_alpn;
   std::string select_alpn;
   bool decline_alpn = false;
+  bool reject_alpn = false;
   bool select_empty_alpn = false;
   bool defer_alps = false;
   std::vector<std::pair<std::string, std::string>> application_settings;
@@ -130,7 +138,6 @@
   bool renegotiate_explicit = false;
   bool forbid_renegotiation_after_handshake = false;
   int expect_peer_signature_algorithm = 0;
-  bool enable_all_curves = false;
   int expect_curve_id = 0;
   bool use_old_client_cert_callback = false;
   int initial_timeout_duration_ms = 0;
@@ -139,6 +146,7 @@
   bool send_alert = false;
   bool peek_then_read = false;
   bool enable_grease = false;
+  bool permute_extensions = false;
   int max_cert_list = 0;
   std::string ticket_key;
   bool use_exporter_between_reads = false;
@@ -164,11 +172,14 @@
   std::string expect_msg_callback;
   bool allow_false_start_without_alpn = false;
   bool handoff = false;
+  bool handshake_hints = false;
+  bool allow_hint_mismatch = false;
   bool use_ocsp_callback = false;
   bool set_ocsp_in_callback = false;
   bool decline_ocsp_callback = false;
   bool fail_ocsp_callback = false;
   bool install_cert_compression_algs = false;
+  int install_one_cert_compression_alg = 0;
   bool reverify_on_resume = false;
   bool enforce_rsa_key_usage = false;
   bool is_handshaker_supported = false;
@@ -185,6 +196,7 @@
   bool expect_no_hrr = false;
   bool wait_for_debugger = false;
   std::string quic_early_data_context;
+  int early_write_after_message = 0;
 
   int argc;
   char **argv;
@@ -192,11 +204,10 @@
   bssl::UniquePtr<SSL_CTX> SetupCtx(SSL_CTX *old_ctx) const;
 
   bssl::UniquePtr<SSL> NewSSL(SSL_CTX *ssl_ctx, SSL_SESSION *session,
-                              bool is_resume,
                               std::unique_ptr<TestState> test_state) const;
 };
 
-bool ParseConfig(int argc, char **argv, TestConfig *out_initial,
+bool ParseConfig(int argc, char **argv, bool is_shim, TestConfig *out_initial,
                  TestConfig *out_resume, TestConfig *out_retry);
 
 bool SetTestConfig(SSL *ssl, const TestConfig *config);
diff --git a/deps/boringssl/src/ssl/test/test_state.h b/deps/boringssl/src/ssl/test/test_state.h
index 745c4c4..4199d4a 100644
--- a/deps/boringssl/src/ssl/test/test_state.h
+++ b/deps/boringssl/src/ssl/test/test_state.h
@@ -17,6 +17,7 @@
 
 #include <openssl/base.h>
 
+#include <functional>
 #include <memory>
 #include <string>
 #include <vector>
@@ -40,7 +41,6 @@
   // packeted_bio is the packeted BIO which simulates read timeouts.
   BIO *packeted_bio = nullptr;
   std::unique_ptr<MockQuicTransport> quic_transport;
-  bssl::UniquePtr<EVP_PKEY> channel_id;
   bool cert_ready = false;
   bssl::UniquePtr<SSL_SESSION> session;
   bssl::UniquePtr<SSL_SESSION> pending_session;
@@ -48,6 +48,8 @@
   bool handshake_done = false;
   // private_key is the underlying private key used when testing custom keys.
   bssl::UniquePtr<EVP_PKEY> private_key;
+  // When private key methods are used, whether the private key was used.
+  bool used_private_key = false;
   std::vector<uint8_t> private_key_result;
   // private_key_retries is the number of times an asynchronous private key
   // operation has been retried.
@@ -56,7 +58,6 @@
   bssl::UniquePtr<SSL_SESSION> new_session;
   bool ticket_decrypt_done = false;
   bool alpn_select_done = false;
-  bool is_resume = false;
   bool early_callback_ready = false;
   bool custom_verify_ready = false;
   std::string msg_callback_text;
@@ -65,6 +66,8 @@
   // completion. This tests that the callback is not called again after this.
   bool cert_verified = false;
   int explicit_renegotiates = 0;
+  std::function<bool(const SSL_CLIENT_HELLO*)> get_handshake_hints_cb;
+  int last_message_received = -1;
 };
 
 bool SetTestState(SSL *ssl, std::unique_ptr<TestState> state);
diff --git a/deps/boringssl/src/ssl/tls13_both.cc b/deps/boringssl/src/ssl/tls13_both.cc
index c6bc2b1..226c67b 100644
--- a/deps/boringssl/src/ssl/tls13_both.cc
+++ b/deps/boringssl/src/ssl/tls13_both.cc
@@ -235,15 +235,14 @@
     }
 
     // Parse out the extensions.
-    bool have_status_request = false, have_sct = false;
-    CBS status_request, sct;
-    const SSL_EXTENSION_TYPE ext_types[] = {
-        {TLSEXT_TYPE_status_request, &have_status_request, &status_request},
-        {TLSEXT_TYPE_certificate_timestamp, &have_sct, &sct},
-    };
-
+    SSLExtension status_request(
+        TLSEXT_TYPE_status_request,
+        !ssl->server && hs->config->ocsp_stapling_enabled);
+    SSLExtension sct(
+        TLSEXT_TYPE_certificate_timestamp,
+        !ssl->server && hs->config->signed_cert_timestamps_enabled);
     uint8_t alert = SSL_AD_DECODE_ERROR;
-    if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+    if (!ssl_parse_extensions(&extensions, &alert, {&status_request, &sct},
                               /*ignore_unknown=*/false)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return false;
@@ -251,20 +250,14 @@
 
     // All Certificate extensions are parsed, but only the leaf extensions are
     // stored.
-    if (have_status_request) {
-      if (ssl->server || !hs->config->ocsp_stapling_enabled) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
-        return false;
-      }
-
+    if (status_request.present) {
       uint8_t status_type;
       CBS ocsp_response;
-      if (!CBS_get_u8(&status_request, &status_type) ||
+      if (!CBS_get_u8(&status_request.data, &status_type) ||
           status_type != TLSEXT_STATUSTYPE_ocsp ||
-          !CBS_get_u24_length_prefixed(&status_request, &ocsp_response) ||
+          !CBS_get_u24_length_prefixed(&status_request.data, &ocsp_response) ||
           CBS_len(&ocsp_response) == 0 ||
-          CBS_len(&status_request) != 0) {
+          CBS_len(&status_request.data) != 0) {
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         return false;
       }
@@ -279,14 +272,8 @@
       }
     }
 
-    if (have_sct) {
-      if (ssl->server || !hs->config->signed_cert_timestamps_enabled) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
-        return false;
-      }
-
-      if (!ssl_is_sct_list_valid(&sct)) {
+    if (sct.present) {
+      if (!ssl_is_sct_list_valid(&sct.data)) {
         OPENSSL_PUT_ERROR(SSL, SSL_R_ERROR_PARSING_EXTENSION);
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
         return false;
@@ -294,7 +281,7 @@
 
       if (sk_CRYPTO_BUFFER_num(certs.get()) == 1) {
         hs->new_session->signed_cert_timestamp_list.reset(
-            CRYPTO_BUFFER_new_from_CBS(&sct, ssl->ctx->pool));
+            CRYPTO_BUFFER_new_from_CBS(&sct.data, ssl->ctx->pool));
         if (hs->new_session->signed_cert_timestamp_list == nullptr) {
           ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
           return false;
@@ -534,9 +521,37 @@
                                  SSL3_MT_COMPRESSED_CERTIFICATE) ||
       !CBB_add_u16(body, hs->cert_compression_alg_id) ||
       !CBB_add_u24(body, msg.size()) ||
-      !CBB_add_u24_length_prefixed(body, &compressed) ||
-      !alg->compress(ssl, &compressed, msg.data(), msg.size()) ||
-      !ssl_add_message_cbb(ssl, cbb.get())) {
+      !CBB_add_u24_length_prefixed(body, &compressed)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+
+  SSL_HANDSHAKE_HINTS *const hints = hs->hints.get();
+  if (hints && !hs->hints_requested &&
+      hints->cert_compression_alg_id == hs->cert_compression_alg_id &&
+      hints->cert_compression_input == MakeConstSpan(msg) &&
+      !hints->cert_compression_output.empty()) {
+    if (!CBB_add_bytes(&compressed, hints->cert_compression_output.data(),
+                       hints->cert_compression_output.size())) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return false;
+    }
+  } else {
+    if (!alg->compress(ssl, &compressed, msg.data(), msg.size())) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return false;
+    }
+    if (hints && hs->hints_requested) {
+      hints->cert_compression_alg_id = hs->cert_compression_alg_id;
+      if (!hints->cert_compression_input.CopyFrom(msg) ||
+          !hints->cert_compression_output.CopyFrom(
+              MakeConstSpan(CBB_data(&compressed), CBB_len(&compressed)))) {
+        return false;
+      }
+    }
+  }
+
+  if (!ssl_add_message_cbb(ssl, cbb.get())) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
   }
@@ -580,10 +595,40 @@
     return ssl_private_key_failure;
   }
 
-  enum ssl_private_key_result_t sign_result = ssl_private_key_sign(
-      hs, sig, &sig_len, max_sig_len, signature_algorithm, msg);
-  if (sign_result != ssl_private_key_success) {
-    return sign_result;
+  SSL_HANDSHAKE_HINTS *const hints = hs->hints.get();
+  Array<uint8_t> spki;
+  if (hints) {
+    ScopedCBB spki_cbb;
+    if (!CBB_init(spki_cbb.get(), 64) ||
+        !EVP_marshal_public_key(spki_cbb.get(), hs->local_pubkey.get()) ||
+        !CBBFinishArray(spki_cbb.get(), &spki)) {
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return ssl_private_key_failure;
+    }
+  }
+
+  if (hints && !hs->hints_requested &&
+      signature_algorithm == hints->signature_algorithm &&
+      MakeConstSpan(msg) == hints->signature_input &&
+      MakeConstSpan(spki) == hints->signature_spki &&
+      !hints->signature.empty() && hints->signature.size() <= max_sig_len) {
+    // Signature algorithm and input both match. Reuse the signature from hints.
+    sig_len = hints->signature.size();
+    OPENSSL_memcpy(sig, hints->signature.data(), sig_len);
+  } else {
+    enum ssl_private_key_result_t sign_result = ssl_private_key_sign(
+        hs, sig, &sig_len, max_sig_len, signature_algorithm, msg);
+    if (sign_result != ssl_private_key_success) {
+      return sign_result;
+    }
+    if (hints && hs->hints_requested) {
+      hints->signature_algorithm = signature_algorithm;
+      hints->signature_input = std::move(msg);
+      hints->signature_spki = std::move(spki);
+      if (!hints->signature.CopyFrom(MakeSpan(sig, sig_len))) {
+        return ssl_private_key_failure;
+      }
+    }
   }
 
   if (!CBB_did_write(&child, sig_len) ||
diff --git a/deps/boringssl/src/ssl/tls13_client.cc b/deps/boringssl/src/ssl/tls13_client.cc
index 496ae01..af2120c 100644
--- a/deps/boringssl/src/ssl/tls13_client.cc
+++ b/deps/boringssl/src/ssl/tls13_client.cc
@@ -101,6 +101,73 @@
   return true;
 }
 
+static bool parse_server_hello_tls13(const SSL_HANDSHAKE *hs,
+                                     ParsedServerHello *out, uint8_t *out_alert,
+                                     const SSLMessage &msg) {
+  if (!ssl_parse_server_hello(out, out_alert, msg)) {
+    return false;
+  }
+  // The RFC8446 version of the structure fixes some legacy values.
+  // Additionally, the session ID must echo the original one.
+  if (out->legacy_version != TLS1_2_VERSION ||
+      out->compression_method != 0 ||
+      !CBS_mem_equal(&out->session_id, hs->session_id, hs->session_id_len) ||
+      CBS_len(&out->extensions) == 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    *out_alert = SSL_AD_DECODE_ERROR;
+    return false;
+  }
+  return true;
+}
+
+static bool is_hello_retry_request(const ParsedServerHello &server_hello) {
+  return Span<const uint8_t>(server_hello.random) == kHelloRetryRequest;
+}
+
+static bool check_ech_confirmation(const SSL_HANDSHAKE *hs, bool *out_accepted,
+                                   uint8_t *out_alert,
+                                   const ParsedServerHello &server_hello) {
+  const bool is_hrr = is_hello_retry_request(server_hello);
+  size_t offset;
+  if (is_hrr) {
+    // We check for an unsolicited extension when parsing all of them.
+    SSLExtension ech(TLSEXT_TYPE_encrypted_client_hello);
+    if (!ssl_parse_extensions(&server_hello.extensions, out_alert, {&ech},
+                              /*ignore_unknown=*/true)) {
+      return false;
+    }
+    if (!ech.present) {
+      *out_accepted = false;
+      return true;
+    }
+    if (CBS_len(&ech.data) != ECH_CONFIRMATION_SIGNAL_LEN) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      *out_alert = SSL_AD_DECODE_ERROR;
+      return false;
+    }
+    offset = CBS_data(&ech.data) - CBS_data(&server_hello.raw);
+  } else {
+    offset = ssl_ech_confirmation_signal_hello_offset(hs->ssl);
+  }
+
+  if (!hs->selected_ech_config) {
+    *out_accepted = false;
+    return true;
+  }
+
+  uint8_t expected[ECH_CONFIRMATION_SIGNAL_LEN];
+  if (!ssl_ech_accept_confirmation(hs, expected, hs->inner_client_random,
+                                   hs->inner_transcript, is_hrr,
+                                   server_hello.raw, offset)) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return false;
+  }
+
+  *out_accepted = CRYPTO_memcmp(CBS_data(&server_hello.raw) + offset, expected,
+                                sizeof(expected)) == 0;
+  return true;
+}
+
 static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   assert(ssl->s3->have_version);
@@ -117,36 +184,17 @@
     return ssl_hs_error;
   }
 
-  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
+  ParsedServerHello server_hello;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!parse_server_hello_tls13(hs, &server_hello, &alert, msg)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
-  CBS body = msg.body, extensions, server_random, session_id;
-  uint16_t server_version, cipher_suite;
-  uint8_t compression_method;
-  if (!CBS_get_u16(&body, &server_version) ||
-      !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&body, &session_id) ||
-      !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
-      !CBS_get_u16(&body, &cipher_suite) ||
-      !CBS_get_u8(&body, &compression_method) ||
-      compression_method != 0 ||
-      !CBS_get_u16_length_prefixed(&body, &extensions) ||
-      CBS_len(&extensions) == 0 ||
-      CBS_len(&body) != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return ssl_hs_error;
-  }
-
-  if (!CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
-    hs->tls13_state = state_read_server_hello;
-    return ssl_hs_ok;
-  }
-
-  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
-  // Check if the cipher is a TLS 1.3 cipher.
-  if (cipher == NULL ||
+  // The cipher suite must be one we offered. We currently offer all supported
+  // TLS 1.3 ciphers, so check the version.
+  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(server_hello.cipher_suite);
+  if (cipher == nullptr ||
       SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
       SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
@@ -156,38 +204,60 @@
 
   hs->new_cipher = cipher;
 
+  const bool is_hrr = is_hello_retry_request(server_hello);
   if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher) ||
-      !hs->transcript.UpdateForHelloRetryRequest()) {
+      (is_hrr && !hs->transcript.UpdateForHelloRetryRequest())) {
     return ssl_hs_error;
   }
+  if (hs->selected_ech_config) {
+    if (!hs->inner_transcript.InitHash(ssl_protocol_version(ssl),
+                                       hs->new_cipher) ||
+        (is_hrr && !hs->inner_transcript.UpdateForHelloRetryRequest())) {
+      return ssl_hs_error;
+    }
+  }
 
+  // Determine which ClientHello the server is responding to. Run
+  // |check_ech_confirmation| unconditionally, so we validate the extension
+  // contents.
+  bool ech_accepted;
+  if (!check_ech_confirmation(hs, &ech_accepted, &alert, server_hello)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return ssl_hs_error;
+  }
+  if (hs->selected_ech_config) {
+    ssl->s3->ech_status = ech_accepted ? ssl_ech_accepted : ssl_ech_rejected;
+  }
 
-  bool have_cookie, have_key_share, have_supported_versions;
-  CBS cookie, key_share, supported_versions;
-  SSL_EXTENSION_TYPE ext_types[] = {
-      {TLSEXT_TYPE_key_share, &have_key_share, &key_share},
-      {TLSEXT_TYPE_cookie, &have_cookie, &cookie},
-      {TLSEXT_TYPE_supported_versions, &have_supported_versions,
-       &supported_versions},
-  };
+  if (!is_hrr) {
+    hs->tls13_state = state_read_server_hello;
+    return ssl_hs_ok;
+  }
 
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
-                            /*ignore_unknown=*/false)) {
+  // The ECH extension, if present, was already parsed by
+  // |check_ech_confirmation|.
+  SSLExtension cookie(TLSEXT_TYPE_cookie), key_share(TLSEXT_TYPE_key_share),
+      supported_versions(TLSEXT_TYPE_supported_versions),
+      ech_unused(TLSEXT_TYPE_encrypted_client_hello,
+                 hs->selected_ech_config || hs->config->ech_grease_enabled);
+  if (!ssl_parse_extensions(
+          &server_hello.extensions, &alert,
+          {&cookie, &key_share, &supported_versions, &ech_unused},
+          /*ignore_unknown=*/false)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
-  if (!have_cookie && !have_key_share) {
+  if (!cookie.present && !key_share.present) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_EMPTY_HELLO_RETRY_REQUEST);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return ssl_hs_error;
   }
-  if (have_cookie) {
+  if (cookie.present) {
     CBS cookie_value;
-    if (!CBS_get_u16_length_prefixed(&cookie, &cookie_value) ||
+    if (!CBS_get_u16_length_prefixed(&cookie.data, &cookie_value) ||
         CBS_len(&cookie_value) == 0 ||
-        CBS_len(&cookie) != 0) {
+        CBS_len(&cookie.data) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
@@ -198,9 +268,10 @@
     }
   }
 
-  if (have_key_share) {
+  if (key_share.present) {
     uint16_t group_id;
-    if (!CBS_get_u16(&key_share, &group_id) || CBS_len(&key_share) != 0) {
+    if (!CBS_get_u16(&key_share.data, &group_id) ||
+        CBS_len(&key_share.data) != 0) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return ssl_hs_error;
@@ -222,14 +293,22 @@
       return ssl_hs_error;
     }
 
-    hs->key_shares[0].reset();
-    hs->key_shares[1].reset();
-    hs->retry_group = group_id;
+    if (!ssl_setup_key_shares(hs, group_id)) {
+      return ssl_hs_error;
+    }
   }
 
+  // Although we now know whether ClientHelloInner was used, we currently
+  // maintain both transcripts up to ServerHello. We could swap transcripts
+  // early, but then ClientHello construction and |check_ech_confirmation|
+  // become more complex.
   if (!ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
+  if (ssl->s3->ech_status == ssl_ech_accepted &&
+      !hs->inner_transcript.Update(msg.raw)) {
+    return ssl_hs_error;
+  }
 
   // HelloRetryRequest should be the end of the flight.
   if (ssl->method->has_unprocessed_handshake_data(ssl)) {
@@ -256,10 +335,18 @@
   // Any 0-RTT keys must have been discarded.
   assert(hs->ssl->s3->write_level == ssl_encryption_initial);
 
-  if (!ssl_write_client_hello(hs)) {
+  // Build the second ClientHelloInner, if applicable. The second ClientHello
+  // uses an empty string for |enc|.
+  if (hs->ssl->s3->ech_status == ssl_ech_accepted &&
+      !ssl_encrypt_client_hello(hs, {})) {
     return ssl_hs_error;
   }
 
+  if (!ssl_add_client_hello(hs)) {
+    return ssl_hs_error;
+  }
+
+  ssl_done_writing_client_hello(hs);
   hs->tls13_state = state_read_server_hello;
   return ssl_hs_flush;
 }
@@ -270,83 +357,70 @@
   if (!ssl->method->get_message(ssl, &msg)) {
     return ssl_hs_read_message;
   }
-  if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) {
-    return ssl_hs_error;
-  }
-
-  CBS body = msg.body, server_random, session_id, extensions;
-  uint16_t server_version;
-  uint16_t cipher_suite;
-  uint8_t compression_method;
-  if (!CBS_get_u16(&body, &server_version) ||
-      !CBS_get_bytes(&body, &server_random, SSL3_RANDOM_SIZE) ||
-      !CBS_get_u8_length_prefixed(&body, &session_id) ||
-      !CBS_mem_equal(&session_id, hs->session_id, hs->session_id_len) ||
-      !CBS_get_u16(&body, &cipher_suite) ||
-      !CBS_get_u8(&body, &compression_method) ||
-      compression_method != 0 ||
-      !CBS_get_u16_length_prefixed(&body, &extensions) ||
-      CBS_len(&body) != 0) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    return ssl_hs_error;
-  }
-
-  if (server_version != TLS1_2_VERSION) {
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
+  ParsedServerHello server_hello;
+  uint8_t alert = SSL_AD_DECODE_ERROR;
+  if (!parse_server_hello_tls13(hs, &server_hello, &alert, msg)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
   // Forbid a second HelloRetryRequest.
-  if (CBS_mem_equal(&server_random, kHelloRetryRequest, SSL3_RANDOM_SIZE)) {
+  if (is_hello_retry_request(server_hello)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
     return ssl_hs_error;
   }
 
-  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_random),
+  // Check the cipher suite, in case this is after HelloRetryRequest.
+  if (SSL_CIPHER_get_value(hs->new_cipher) != server_hello.cipher_suite) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return ssl_hs_error;
+  }
+
+  if (ssl->s3->ech_status == ssl_ech_accepted) {
+    if (ssl->s3->used_hello_retry_request) {
+      // HelloRetryRequest and ServerHello must accept ECH consistently.
+      bool ech_accepted;
+      if (!check_ech_confirmation(hs, &ech_accepted, &alert, server_hello)) {
+        ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+        return ssl_hs_error;
+      }
+      if (!ech_accepted) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_INCONSISTENT_ECH_NEGOTIATION);
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+        return ssl_hs_error;
+      }
+    }
+
+    hs->transcript = std::move(hs->inner_transcript);
+    hs->extensions.sent = hs->inner_extensions_sent;
+    // Report the inner random value through |SSL_get_client_random|.
+    OPENSSL_memcpy(ssl->s3->client_random, hs->inner_client_random,
+                   SSL3_RANDOM_SIZE);
+  }
+
+  OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_hello.random),
                  SSL3_RANDOM_SIZE);
 
-  // Check if the cipher is a TLS 1.3 cipher.
-  const SSL_CIPHER *cipher = SSL_get_cipher_by_value(cipher_suite);
-  if (cipher == nullptr ||
-      SSL_CIPHER_get_min_version(cipher) > ssl_protocol_version(ssl) ||
-      SSL_CIPHER_get_max_version(cipher) < ssl_protocol_version(ssl)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    return ssl_hs_error;
-  }
-
-  // Check that the cipher matches the one in the HelloRetryRequest.
-  if (ssl->s3->used_hello_retry_request && hs->new_cipher != cipher) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CIPHER_RETURNED);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    return ssl_hs_error;
-  }
-
-  // Parse out the extensions.
-  bool have_key_share = false, have_pre_shared_key = false,
-       have_supported_versions = false;
-  CBS key_share, pre_shared_key, supported_versions;
-  SSL_EXTENSION_TYPE ext_types[] = {
-      {TLSEXT_TYPE_key_share, &have_key_share, &key_share},
-      {TLSEXT_TYPE_pre_shared_key, &have_pre_shared_key, &pre_shared_key},
-      {TLSEXT_TYPE_supported_versions, &have_supported_versions,
-       &supported_versions},
-  };
-
-  uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+  // When offering ECH, |ssl->session| is only offered in ClientHelloInner.
+  const bool pre_shared_key_allowed =
+      ssl->session != nullptr && ssl->s3->ech_status != ssl_ech_rejected;
+  SSLExtension key_share(TLSEXT_TYPE_key_share),
+      pre_shared_key(TLSEXT_TYPE_pre_shared_key, pre_shared_key_allowed),
+      supported_versions(TLSEXT_TYPE_supported_versions);
+  if (!ssl_parse_extensions(&server_hello.extensions, &alert,
+                            {&key_share, &pre_shared_key, &supported_versions},
                             /*ignore_unknown=*/false)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
 
-  // Recheck supported_versions, in case this is the second ServerHello.
+  // Recheck supported_versions, in case this is after HelloRetryRequest.
   uint16_t version;
-  if (!have_supported_versions ||
-      !CBS_get_u16(&supported_versions, &version) ||
+  if (!supported_versions.present ||
+      !CBS_get_u16(&supported_versions.data, &version) ||
+      CBS_len(&supported_versions.data) != 0 ||
       version != ssl->version) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_SECOND_SERVERHELLO_VERSION_MISMATCH);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
@@ -354,15 +428,9 @@
   }
 
   alert = SSL_AD_DECODE_ERROR;
-  if (have_pre_shared_key) {
-    if (ssl->session == NULL) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
-      return ssl_hs_error;
-    }
-
+  if (pre_shared_key.present) {
     if (!ssl_ext_pre_shared_key_parse_serverhello(hs, &alert,
-                                                  &pre_shared_key)) {
+                                                  &pre_shared_key.data)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
@@ -373,7 +441,7 @@
       return ssl_hs_error;
     }
 
-    if (ssl->session->cipher->algorithm_prf != cipher->algorithm_prf) {
+    if (ssl->session->cipher->algorithm_prf != hs->new_cipher->algorithm_prf) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_OLD_SESSION_PRF_HASH_MISMATCH);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
@@ -388,6 +456,7 @@
     }
 
     ssl->s3->session_reused = true;
+    hs->can_release_private_key = true;
     // Only authentication information carries over in TLS 1.3.
     hs->new_session =
         SSL_SESSION_dup(ssl->session.get(), SSL_SESSION_DUP_AUTH_ONLY);
@@ -400,29 +469,25 @@
     // Resumption incorporates fresh key material, so refresh the timeout.
     ssl_session_renew_timeout(ssl, hs->new_session.get(),
                               ssl->session_ctx->session_psk_dhe_timeout);
-  } else if (!ssl_get_new_session(hs, 0)) {
+  } else if (!ssl_get_new_session(hs)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
   }
 
-  hs->new_session->cipher = cipher;
-  hs->new_cipher = cipher;
-
-  size_t hash_len =
-      EVP_MD_size(ssl_get_handshake_digest(ssl_protocol_version(ssl), cipher));
+  hs->new_session->cipher = hs->new_cipher;
 
   // Set up the key schedule and incorporate the PSK into the running secret.
-  if (ssl->s3->session_reused) {
-    if (!tls13_init_key_schedule(
-            hs, MakeConstSpan(hs->new_session->secret,
-                              hs->new_session->secret_length))) {
-      return ssl_hs_error;
-    }
-  } else if (!tls13_init_key_schedule(hs, MakeConstSpan(kZeroes, hash_len))) {
+  size_t hash_len = EVP_MD_size(
+      ssl_get_handshake_digest(ssl_protocol_version(ssl), hs->new_cipher));
+  if (!tls13_init_key_schedule(
+          hs, ssl->s3->session_reused
+                  ? MakeConstSpan(hs->new_session->secret,
+                                  hs->new_session->secret_length)
+                  : MakeConstSpan(kZeroes, hash_len))) {
     return ssl_hs_error;
   }
 
-  if (!have_key_share) {
+  if (!key_share.present) {
     // We do not support psk_ke and thus always require a key share.
     OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
@@ -433,7 +498,7 @@
   Array<uint8_t> dhe_secret;
   alert = SSL_AD_DECODE_ERROR;
   if (!ssl_ext_key_share_parse_serverhello(hs, &dhe_secret, &alert,
-                                           &key_share)) {
+                                           &key_share.data)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return ssl_hs_error;
   }
@@ -477,18 +542,27 @@
     return ssl_hs_error;
   }
 
-  CBS body = msg.body;
-  if (!ssl_parse_serverhello_tlsext(hs, &body)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
-    return ssl_hs_error;
-  }
-  if (CBS_len(&body) != 0) {
+  CBS body = msg.body, extensions;
+  if (!CBS_get_u16_length_prefixed(&body, &extensions) ||
+      CBS_len(&body) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
+  if (!ssl_parse_serverhello_tlsext(hs, &extensions)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
+    return ssl_hs_error;
+  }
+
   if (ssl->s3->early_data_accepted) {
+    // The extension parser checks the server resumed the session.
+    assert(ssl->s3->session_reused);
+    // If offering ECH, the server may not accept early data with
+    // ClientHelloOuter. We do not offer sessions with ClientHelloOuter, so this
+    // this should be implied by checking |session_reused|.
+    assert(ssl->s3->ech_status != ssl_ech_rejected);
+
     if (hs->early_session->cipher != hs->new_session->cipher) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_CIPHER_MISMATCH_ON_EARLY_DATA);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
@@ -500,9 +574,9 @@
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return ssl_hs_error;
     }
-    // Channel ID and Token Binding are incompatible with 0-RTT. The ALPS
-    // extension should be negotiated implicitly.
-    if (ssl->s3->channel_id_valid || ssl->s3->token_binding_negotiated ||
+    // Channel ID is incompatible with 0-RTT. The ALPS extension should be
+    // negotiated implicitly.
+    if (hs->channel_id_negotiated ||
         hs->new_session->has_application_settings) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION_ON_EARLY_DATA);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
@@ -564,25 +638,19 @@
   }
 
 
-  bool have_sigalgs = false, have_ca = false;
-  CBS sigalgs, ca;
-  const SSL_EXTENSION_TYPE ext_types[] = {
-    {TLSEXT_TYPE_signature_algorithms, &have_sigalgs, &sigalgs},
-    {TLSEXT_TYPE_certificate_authorities, &have_ca, &ca},
-  };
-
+  SSLExtension sigalgs(TLSEXT_TYPE_signature_algorithms),
+      ca(TLSEXT_TYPE_certificate_authorities);
   CBS body = msg.body, context, extensions, supported_signature_algorithms;
   uint8_t alert = SSL_AD_DECODE_ERROR;
   if (!CBS_get_u8_length_prefixed(&body, &context) ||
       // The request context is always empty during the handshake.
       CBS_len(&context) != 0 ||
-      !CBS_get_u16_length_prefixed(&body, &extensions) ||
+      !CBS_get_u16_length_prefixed(&body, &extensions) ||  //
       CBS_len(&body) != 0 ||
-      !ssl_parse_extensions(&extensions, &alert, ext_types,
+      !ssl_parse_extensions(&extensions, &alert, {&sigalgs, &ca},
                             /*ignore_unknown=*/true) ||
-      (have_ca && CBS_len(&ca) == 0) ||
-      !have_sigalgs ||
-      !CBS_get_u16_length_prefixed(&sigalgs,
+      !sigalgs.present ||
+      !CBS_get_u16_length_prefixed(&sigalgs.data,
                                    &supported_signature_algorithms) ||
       !tls1_parse_peer_sigalgs(hs, &supported_signature_algorithms)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -590,8 +658,8 @@
     return ssl_hs_error;
   }
 
-  if (have_ca) {
-    hs->ca_names = ssl_parse_client_CA_list(ssl, &alert, &ca);
+  if (ca.present) {
+    hs->ca_names = ssl_parse_client_CA_list(ssl, &alert, &ca.data);
     if (!hs->ca_names) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
@@ -712,8 +780,7 @@
   SSL *const ssl = hs->ssl;
 
   if (ssl->s3->early_data_accepted) {
-    // QUIC omits the EndOfEarlyData message. See draft-ietf-quic-tls-22,
-    // section 8.3.
+    // QUIC omits the EndOfEarlyData message. See RFC 9001, section 8.3.
     if (ssl->quic_method == nullptr) {
       ScopedCBB cbb;
       CBB body;
@@ -768,8 +835,12 @@
     return ssl_hs_ok;
   }
 
-  // Call cert_cb to update the certificate.
-  if (hs->config->cert->cert_cb != NULL) {
+  if (ssl->s3->ech_status == ssl_ech_rejected) {
+    // Do not send client certificates on ECH reject. We have not authenticated
+    // the server for the name that can learn the certificate.
+    SSL_certs_clear(ssl);
+  } else if (hs->config->cert->cert_cb != nullptr) {
+    // Call cert_cb to update the certificate.
     int rv = hs->config->cert->cert_cb(ssl, hs->config->cert->cert_cb_arg);
     if (rv == 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
@@ -817,18 +888,10 @@
 
 static enum ssl_hs_wait_t do_complete_second_flight(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+  hs->can_release_private_key = true;
 
   // Send a Channel ID assertion if necessary.
-  if (ssl->s3->channel_id_valid) {
-    if (!ssl_do_channel_id_callback(hs)) {
-      hs->tls13_state = state_complete_second_flight;
-      return ssl_hs_error;
-    }
-
-    if (hs->config->channel_id_private == NULL) {
-      return ssl_hs_channel_id_lookup;
-    }
-
+  if (hs->channel_id_negotiated) {
     ScopedCBB cbb;
     CBB body;
     if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_CHANNEL_ID) ||
@@ -1019,30 +1082,24 @@
     return nullptr;
   }
 
-  // Parse out the extensions.
-  bool have_early_data = false;
-  CBS early_data;
-  const SSL_EXTENSION_TYPE ext_types[] = {
-      {TLSEXT_TYPE_early_data, &have_early_data, &early_data},
-  };
-
+  SSLExtension early_data(TLSEXT_TYPE_early_data);
   uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+  if (!ssl_parse_extensions(&extensions, &alert, {&early_data},
                             /*ignore_unknown=*/true)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
     return nullptr;
   }
 
-  if (have_early_data) {
-    if (!CBS_get_u32(&early_data, &session->ticket_max_early_data) ||
-        CBS_len(&early_data) != 0) {
+  if (early_data.present) {
+    if (!CBS_get_u32(&early_data.data, &session->ticket_max_early_data) ||
+        CBS_len(&early_data.data) != 0) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
       return nullptr;
     }
 
     // QUIC does not use the max_early_data_size parameter and always sets it to
-    // a fixed value. See draft-ietf-quic-tls-22, section 4.5.
+    // a fixed value. See RFC 9001, section 4.6.1.
     if (ssl->quic_method != nullptr &&
         session->ticket_max_early_data != 0xffffffff) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
@@ -1051,8 +1108,8 @@
     }
   }
 
-  // Generate a session ID for this session. Some callers expect all sessions to
-  // have a session ID.
+  // Historically, OpenSSL filled in fake session IDs for ticket-based sessions.
+  // Envoy's tests depend on this, although perhaps they shouldn't.
   SHA256(CBS_data(&ticket), CBS_len(&ticket), session->session_id);
   session->session_id_length = SHA256_DIGEST_LENGTH;
 
diff --git a/deps/boringssl/src/ssl/tls13_enc.cc b/deps/boringssl/src/ssl/tls13_enc.cc
index cda53ec..c7b75a6 100644
--- a/deps/boringssl/src/ssl/tls13_enc.cc
+++ b/deps/boringssl/src/ssl/tls13_enc.cc
@@ -33,24 +33,25 @@
 
 BSSL_NAMESPACE_BEGIN
 
-static bool init_key_schedule(SSL_HANDSHAKE *hs, uint16_t version,
-                              const SSL_CIPHER *cipher) {
-  if (!hs->transcript.InitHash(version, cipher)) {
+static bool init_key_schedule(SSL_HANDSHAKE *hs, SSLTranscript *transcript,
+                              uint16_t version, const SSL_CIPHER *cipher) {
+  if (!transcript->InitHash(version, cipher)) {
     return false;
   }
 
   // Initialize the secret to the zero key.
-  hs->ResizeSecrets(hs->transcript.DigestLen());
+  hs->ResizeSecrets(transcript->DigestLen());
   OPENSSL_memset(hs->secret().data(), 0, hs->secret().size());
 
   return true;
 }
 
-static bool hkdf_extract_to_secret(SSL_HANDSHAKE *hs, Span<const uint8_t> in) {
+static bool hkdf_extract_to_secret(SSL_HANDSHAKE *hs,
+                                   const SSLTranscript &transcript,
+                                   Span<const uint8_t> in) {
   size_t len;
-  if (!HKDF_extract(hs->secret().data(), &len, hs->transcript.Digest(),
-                    in.data(), in.size(), hs->secret().data(),
-                    hs->secret().size())) {
+  if (!HKDF_extract(hs->secret().data(), &len, transcript.Digest(), in.data(),
+                    in.size(), hs->secret().data(), hs->secret().size())) {
     return false;
   }
   assert(len == hs->secret().size());
@@ -58,7 +59,8 @@
 }
 
 bool tls13_init_key_schedule(SSL_HANDSHAKE *hs, Span<const uint8_t> psk) {
-  if (!init_key_schedule(hs, ssl_protocol_version(hs->ssl), hs->new_cipher)) {
+  if (!init_key_schedule(hs, &hs->transcript, ssl_protocol_version(hs->ssl),
+                         hs->new_cipher)) {
     return false;
   }
 
@@ -67,14 +69,22 @@
   if (!hs->handback) {
     hs->transcript.FreeBuffer();
   }
-  return hkdf_extract_to_secret(hs, psk);
+  return hkdf_extract_to_secret(hs, hs->transcript, psk);
 }
 
-bool tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, Span<const uint8_t> psk) {
-  SSL *const ssl = hs->ssl;
-  return init_key_schedule(hs, ssl_session_protocol_version(ssl->session.get()),
-                           ssl->session->cipher) &&
-         hkdf_extract_to_secret(hs, psk);
+bool tls13_init_early_key_schedule(SSL_HANDSHAKE *hs,
+                                   const SSL_SESSION *session) {
+  assert(!hs->ssl->server);
+  // When offering ECH, early data is associated with ClientHelloInner, not
+  // ClientHelloOuter.
+  SSLTranscript *transcript =
+      hs->selected_ech_config ? &hs->inner_transcript : &hs->transcript;
+  return init_key_schedule(hs, transcript,
+                           ssl_session_protocol_version(session),
+                           session->cipher) &&
+         hkdf_extract_to_secret(
+             hs, *transcript,
+             MakeConstSpan(session->secret, session->secret_length));
 }
 
 static Span<const char> label_to_span(const char *label) {
@@ -118,25 +128,31 @@
          hkdf_expand_label(hs->secret(), hs->transcript.Digest(), hs->secret(),
                            label_to_span(kTLS13LabelDerived),
                            MakeConstSpan(derive_context, derive_context_len)) &&
-         hkdf_extract_to_secret(hs, in);
+         hkdf_extract_to_secret(hs, hs->transcript, in);
 }
 
-// derive_secret derives a secret of length |out.size()| and writes the result
-// in |out| with the given label, the current base secret, and the most
-// recently-saved handshake context. It returns true on success and false on
-// error.
-static bool derive_secret(SSL_HANDSHAKE *hs, Span<uint8_t> out,
-                          Span<const char> label) {
+// derive_secret_with_transcript derives a secret of length |out.size()| and
+// writes the result in |out| with the given label, the current base secret, and
+// the state of |transcript|. It returns true on success and false on error.
+static bool derive_secret_with_transcript(const SSL_HANDSHAKE *hs,
+                                          Span<uint8_t> out,
+                                          const SSLTranscript &transcript,
+                                          Span<const char> label) {
   uint8_t context_hash[EVP_MAX_MD_SIZE];
   size_t context_hash_len;
-  if (!hs->transcript.GetHash(context_hash, &context_hash_len)) {
+  if (!transcript.GetHash(context_hash, &context_hash_len)) {
     return false;
   }
 
-  return hkdf_expand_label(out, hs->transcript.Digest(), hs->secret(), label,
+  return hkdf_expand_label(out, transcript.Digest(), hs->secret(), label,
                            MakeConstSpan(context_hash, context_hash_len));
 }
 
+static bool derive_secret(SSL_HANDSHAKE *hs, Span<uint8_t> out,
+                          Span<const char> label) {
+  return derive_secret_with_transcript(hs, out, hs->transcript, label);
+}
+
 bool tls13_set_traffic_key(SSL *ssl, enum ssl_encryption_level_t level,
                            enum evp_aead_direction_t direction,
                            const SSL_SESSION *session,
@@ -228,8 +244,14 @@
 
 bool tls13_derive_early_secret(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!derive_secret(hs, hs->early_traffic_secret(),
-                     label_to_span(kTLS13LabelClientEarlyTraffic)) ||
+  // When offering ECH on the client, early data is associated with
+  // ClientHelloInner, not ClientHelloOuter.
+  const SSLTranscript &transcript = (!ssl->server && hs->selected_ech_config)
+                                        ? hs->inner_transcript
+                                        : hs->transcript;
+  if (!derive_secret_with_transcript(
+          hs, hs->early_traffic_secret(), transcript,
+          label_to_span(kTLS13LabelClientEarlyTraffic)) ||
       !ssl_log_secret(ssl, "CLIENT_EARLY_TRAFFIC_SECRET",
                       hs->early_traffic_secret())) {
     return false;
@@ -395,29 +417,52 @@
 
 static const char kTLS13LabelPSKBinder[] = "res binder";
 
-static bool tls13_psk_binder(uint8_t *out, size_t *out_len, uint16_t version,
-                             const EVP_MD *digest, Span<const uint8_t> psk,
-                             Span<const uint8_t> context) {
+static bool tls13_psk_binder(uint8_t *out, size_t *out_len,
+                             const SSL_SESSION *session,
+                             const SSLTranscript &transcript,
+                             Span<const uint8_t> client_hello,
+                             size_t binders_len) {
+  const EVP_MD *digest = ssl_session_get_digest(session);
+
+  // Compute the binder key.
+  //
+  // TODO(davidben): Ideally we wouldn't recompute early secret and the binder
+  // key each time.
   uint8_t binder_context[EVP_MAX_MD_SIZE];
   unsigned binder_context_len;
-  if (!EVP_Digest(NULL, 0, binder_context, &binder_context_len, digest, NULL)) {
-    return false;
-  }
-
   uint8_t early_secret[EVP_MAX_MD_SIZE] = {0};
   size_t early_secret_len;
-  if (!HKDF_extract(early_secret, &early_secret_len, digest, psk.data(),
-                    psk.size(), NULL, 0)) {
+  uint8_t binder_key_buf[EVP_MAX_MD_SIZE] = {0};
+  auto binder_key = MakeSpan(binder_key_buf, EVP_MD_size(digest));
+  if (!EVP_Digest(nullptr, 0, binder_context, &binder_context_len, digest,
+                  nullptr) ||
+      !HKDF_extract(early_secret, &early_secret_len, digest, session->secret,
+                    session->secret_length, nullptr, 0) ||
+      !hkdf_expand_label(binder_key, digest,
+                         MakeConstSpan(early_secret, early_secret_len),
+                         label_to_span(kTLS13LabelPSKBinder),
+                         MakeConstSpan(binder_context, binder_context_len))) {
     return false;
   }
 
-  uint8_t binder_key_buf[EVP_MAX_MD_SIZE] = {0};
-  auto binder_key = MakeSpan(binder_key_buf, EVP_MD_size(digest));
-  if (!hkdf_expand_label(binder_key, digest,
-                         MakeConstSpan(early_secret, early_secret_len),
-                         label_to_span(kTLS13LabelPSKBinder),
-                         MakeConstSpan(binder_context, binder_context_len)) ||
-      !tls13_verify_data(out, out_len, digest, version, binder_key, context)) {
+  // Hash the transcript and truncated ClientHello.
+  if (client_hello.size() < binders_len) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+  auto truncated = client_hello.subspan(0, client_hello.size() - binders_len);
+  uint8_t context[EVP_MAX_MD_SIZE];
+  unsigned context_len;
+  ScopedEVP_MD_CTX ctx;
+  if (!transcript.CopyToHashContext(ctx.get(), digest) ||
+      !EVP_DigestUpdate(ctx.get(), truncated.data(),
+                        truncated.size()) ||
+      !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) {
+    return false;
+  }
+
+  if (!tls13_verify_data(out, out_len, digest, session->ssl_version, binder_key,
+                         MakeConstSpan(context, context_len))) {
     return false;
   }
 
@@ -425,68 +470,44 @@
   return true;
 }
 
-static bool hash_transcript_and_truncated_client_hello(
-    SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, const EVP_MD *digest,
-    Span<const uint8_t> client_hello, size_t binders_len) {
-  // Truncate the ClientHello.
-  if (binders_len + 2 < binders_len || client_hello.size() < binders_len + 2) {
-    return false;
-  }
-  client_hello = client_hello.subspan(0, client_hello.size() - binders_len - 2);
-
-  ScopedEVP_MD_CTX ctx;
-  unsigned len;
-  if (!hs->transcript.CopyToHashContext(ctx.get(), digest) ||
-      !EVP_DigestUpdate(ctx.get(), client_hello.data(), client_hello.size()) ||
-      !EVP_DigestFinal_ex(ctx.get(), out, &len)) {
-    return false;
-  }
-
-  *out_len = len;
-  return true;
-}
-
-bool tls13_write_psk_binder(SSL_HANDSHAKE *hs, Span<uint8_t> msg) {
-  SSL *const ssl = hs->ssl;
+bool tls13_write_psk_binder(const SSL_HANDSHAKE *hs,
+                            const SSLTranscript &transcript, Span<uint8_t> msg,
+                            size_t *out_binder_len) {
+  const SSL *const ssl = hs->ssl;
   const EVP_MD *digest = ssl_session_get_digest(ssl->session.get());
-  size_t hash_len = EVP_MD_size(digest);
-
-  ScopedEVP_MD_CTX ctx;
-  uint8_t context[EVP_MAX_MD_SIZE];
-  size_t context_len;
+  const size_t hash_len = EVP_MD_size(digest);
+  // We only offer one PSK, so the binders are a u16 and u8 length
+  // prefix, followed by the binder. The caller is assumed to have constructed
+  // |msg| with placeholder binders.
+  const size_t binders_len = 3 + hash_len;
   uint8_t verify_data[EVP_MAX_MD_SIZE];
   size_t verify_data_len;
-  if (!hash_transcript_and_truncated_client_hello(
-          hs, context, &context_len, digest, msg,
-          1 /* length prefix */ + hash_len) ||
-      !tls13_psk_binder(
-          verify_data, &verify_data_len, ssl->session->ssl_version, digest,
-          MakeConstSpan(ssl->session->secret, ssl->session->secret_length),
-          MakeConstSpan(context, context_len)) ||
+  if (!tls13_psk_binder(verify_data, &verify_data_len, ssl->session.get(),
+                        transcript, msg, binders_len) ||
       verify_data_len != hash_len) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
   }
 
-  OPENSSL_memcpy(msg.data() + msg.size() - verify_data_len, verify_data,
-                 verify_data_len);
+  auto msg_binder = msg.last(verify_data_len);
+  OPENSSL_memcpy(msg_binder.data(), verify_data, verify_data_len);
+  if (out_binder_len != nullptr) {
+    *out_binder_len = verify_data_len;
+  }
   return true;
 }
 
-bool tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
-                             const SSLMessage &msg, CBS *binders) {
-  uint8_t context[EVP_MAX_MD_SIZE];
-  size_t context_len;
+bool tls13_verify_psk_binder(const SSL_HANDSHAKE *hs,
+                             const SSL_SESSION *session, const SSLMessage &msg,
+                             CBS *binders) {
   uint8_t verify_data[EVP_MAX_MD_SIZE];
   size_t verify_data_len;
   CBS binder;
-  if (!hash_transcript_and_truncated_client_hello(hs, context, &context_len,
-                                                  hs->transcript.Digest(),
-                                                  msg.raw, CBS_len(binders)) ||
-      !tls13_psk_binder(verify_data, &verify_data_len, hs->ssl->version,
-                        hs->transcript.Digest(),
-                        MakeConstSpan(session->secret, session->secret_length),
-                        MakeConstSpan(context, context_len)) ||
+  // The binders are computed over |msg| with |binders| and its u16 length
+  // prefix removed. The caller is assumed to have parsed |msg|, extracted
+  // |binders|, and verified the PSK extension is last.
+  if (!tls13_psk_binder(verify_data, &verify_data_len, session, hs->transcript,
+                        msg.raw, 2 + CBS_len(binders)) ||
       // We only consider the first PSK, so compare against the first binder.
       !CBS_get_u8_length_prefixed(binders, &binder)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -507,40 +528,55 @@
   return true;
 }
 
-bool tls13_ech_accept_confirmation(
-    SSL_HANDSHAKE *hs, bssl::Span<uint8_t> out,
-    bssl::Span<const uint8_t> server_hello_ech_conf) {
-  // Compute the hash of the transcript concatenated with
-  // |server_hello_ech_conf| without modifying |hs->transcript|.
-  uint8_t context_hash[EVP_MAX_MD_SIZE];
-  unsigned context_hash_len;
-  ScopedEVP_MD_CTX ctx;
-  if (!hs->transcript.CopyToHashContext(ctx.get(), hs->transcript.Digest()) ||
-      !EVP_DigestUpdate(ctx.get(), server_hello_ech_conf.data(),
-                        server_hello_ech_conf.size()) ||
-      !EVP_DigestFinal_ex(ctx.get(), context_hash, &context_hash_len)) {
-    return false;
-  }
+size_t ssl_ech_confirmation_signal_hello_offset(const SSL *ssl) {
+  static_assert(ECH_CONFIRMATION_SIGNAL_LEN < SSL3_RANDOM_SIZE,
+                "the confirmation signal is a suffix of the random");
+  const size_t header_len =
+      SSL_is_dtls(ssl) ? DTLS1_HM_HEADER_LENGTH : SSL3_HM_HEADER_LENGTH;
+  return header_len + 2 /* version */ + SSL3_RANDOM_SIZE -
+         ECH_CONFIRMATION_SIGNAL_LEN;
+}
 
-  // Per draft-ietf-tls-esni-09, accept_confirmation is computed with
-  // Derive-Secret, which derives a secret of size Hash.length. That value is
-  // then truncated to the first 8 bytes. Note this differs from deriving an
-  // 8-byte secret because the target length is included in the derivation.
-  uint8_t accept_confirmation_buf[EVP_MAX_MD_SIZE];
-  bssl::Span<uint8_t> accept_confirmation =
-      MakeSpan(accept_confirmation_buf, hs->transcript.DigestLen());
-  if (!hkdf_expand_label(accept_confirmation, hs->transcript.Digest(),
-                         hs->secret(), label_to_span("ech accept confirmation"),
-                         MakeConstSpan(context_hash, context_hash_len))) {
-    return false;
-  }
+bool ssl_ech_accept_confirmation(const SSL_HANDSHAKE *hs, Span<uint8_t> out,
+                                 Span<const uint8_t> client_random,
+                                 const SSLTranscript &transcript, bool is_hrr,
+                                 Span<const uint8_t> msg, size_t offset) {
+  // See draft-ietf-tls-esni-13, sections 7.2 and 7.2.1.
+  static const uint8_t kZeros[EVP_MAX_MD_SIZE] = {0};
 
-  if (out.size() > accept_confirmation.size()) {
+  // We hash |msg|, with bytes from |offset| zeroed.
+  if (msg.size() < offset + ECH_CONFIRMATION_SIGNAL_LEN) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
   }
-  OPENSSL_memcpy(out.data(), accept_confirmation.data(), out.size());
-  return true;
+
+  auto before_zeros = msg.subspan(0, offset);
+  auto after_zeros = msg.subspan(offset + ECH_CONFIRMATION_SIGNAL_LEN);
+  uint8_t context[EVP_MAX_MD_SIZE];
+  unsigned context_len;
+  ScopedEVP_MD_CTX ctx;
+  if (!transcript.CopyToHashContext(ctx.get(), transcript.Digest()) ||
+      !EVP_DigestUpdate(ctx.get(), before_zeros.data(), before_zeros.size()) ||
+      !EVP_DigestUpdate(ctx.get(), kZeros, ECH_CONFIRMATION_SIGNAL_LEN) ||
+      !EVP_DigestUpdate(ctx.get(), after_zeros.data(), after_zeros.size()) ||
+      !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) {
+    return false;
+  }
+
+  uint8_t secret[EVP_MAX_MD_SIZE];
+  size_t secret_len;
+  if (!HKDF_extract(secret, &secret_len, transcript.Digest(),
+                    client_random.data(), client_random.size(), kZeros,
+                    transcript.DigestLen())) {
+    return false;
+  }
+
+  assert(out.size() == ECH_CONFIRMATION_SIGNAL_LEN);
+  return hkdf_expand_label(out, transcript.Digest(),
+                           MakeConstSpan(secret, secret_len),
+                           is_hrr ? label_to_span("hrr ech accept confirmation")
+                                  : label_to_span("ech accept confirmation"),
+                           MakeConstSpan(context, context_len));
 }
 
 BSSL_NAMESPACE_END
diff --git a/deps/boringssl/src/ssl/tls13_server.cc b/deps/boringssl/src/ssl/tls13_server.cc
index 6e9cd07..2f000e5 100644
--- a/deps/boringssl/src/ssl/tls13_server.cc
+++ b/deps/boringssl/src/ssl/tls13_server.cc
@@ -23,6 +23,7 @@
 #include <openssl/bytestring.h>
 #include <openssl/digest.h>
 #include <openssl/err.h>
+#include <openssl/hpke.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
 #include <openssl/stack.h>
@@ -41,35 +42,57 @@
 // See RFC 8446, section 8.3.
 static const int32_t kMaxTicketAgeSkewSeconds = 60;
 
-static int resolve_ecdhe_secret(SSL_HANDSHAKE *hs, bool *out_need_retry,
-                                SSL_CLIENT_HELLO *client_hello) {
+static bool resolve_ecdhe_secret(SSL_HANDSHAKE *hs,
+                                 const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
-  *out_need_retry = false;
-
-  // We only support connections that include an ECDHE key exchange.
-  CBS key_share;
-  if (!ssl_client_hello_get_extension(client_hello, &key_share,
-                                      TLSEXT_TYPE_key_share)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_KEY_SHARE);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
-    return 0;
-  }
+  const uint16_t group_id = hs->new_session->group_id;
 
   bool found_key_share;
-  Array<uint8_t> dhe_secret;
+  Span<const uint8_t> peer_key;
   uint8_t alert = SSL_AD_DECODE_ERROR;
-  if (!ssl_ext_key_share_parse_clienthello(hs, &found_key_share, &dhe_secret,
-                                           &alert, &key_share)) {
+  if (!ssl_ext_key_share_parse_clienthello(hs, &found_key_share, &peer_key,
+                                           &alert, client_hello)) {
     ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
-    return 0;
+    return false;
   }
 
   if (!found_key_share) {
-    *out_need_retry = true;
-    return 0;
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
+    return false;
   }
 
-  return tls13_advance_key_schedule(hs, dhe_secret);
+  Array<uint8_t> secret;
+  SSL_HANDSHAKE_HINTS *const hints = hs->hints.get();
+  if (hints && !hs->hints_requested && hints->key_share_group_id == group_id &&
+      !hints->key_share_secret.empty()) {
+    // Copy DH secret from hints.
+    if (!hs->ecdh_public_key.CopyFrom(hints->key_share_public_key) ||
+        !secret.CopyFrom(hints->key_share_secret)) {
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return false;
+    }
+  } else {
+    ScopedCBB public_key;
+    UniquePtr<SSLKeyShare> key_share = SSLKeyShare::Create(group_id);
+    if (!key_share ||  //
+        !CBB_init(public_key.get(), 32) ||
+        !key_share->Accept(public_key.get(), &secret, &alert, peer_key) ||
+        !CBBFinishArray(public_key.get(), &hs->ecdh_public_key)) {
+      ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+      return false;
+    }
+    if (hints && hs->hints_requested) {
+      hints->key_share_group_id = group_id;
+      if (!hints->key_share_public_key.CopyFrom(hs->ecdh_public_key) ||
+          !hints->key_share_secret.CopyFrom(secret)) {
+        ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        return false;
+      }
+    }
+  }
+
+  return tls13_advance_key_schedule(hs, secret);
 }
 
 static int ssl_ext_supported_versions_add_serverhello(SSL_HANDSHAKE *hs,
@@ -132,7 +155,7 @@
         (!ssl->quic_method || !ssl->config->quic_early_data_context.empty());
     if (enable_early_data) {
       // QUIC does not use the max_early_data_size parameter and always sets it
-      // to a fixed value. See draft-ietf-quic-tls-22, section 4.5.
+      // to a fixed value. See RFC 9001, section 4.6.1.
       session->ticket_max_early_data =
           ssl->quic_method != nullptr ? 0xffffffff : kMaxEarlyDataAccepted;
     }
@@ -165,7 +188,7 @@
       }
     }
 
-    // Add a fake extension. See draft-davidben-tls-grease-01.
+    // Add a fake extension. See RFC 8701.
     if (!CBB_add_u16(&extensions,
                      ssl_get_grease_value(hs, ssl_grease_ticket_extension)) ||
         !CBB_add_u16(&extensions, 0 /* empty */)) {
@@ -186,13 +209,8 @@
   // the common handshake logic. Resolve the remaining non-PSK parameters.
   SSL *const ssl = hs->ssl;
   SSLMessage msg;
-  if (!ssl->method->get_message(ssl, &msg)) {
-    return ssl_hs_read_message;
-  }
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+  if (!hs->GetClientHello(&msg, &client_hello)) {
     return ssl_hs_error;
   }
 
@@ -228,8 +246,7 @@
     return ssl_hs_error;
   }
 
-  // The PRF hash is now known. Set up the key schedule and hash the
-  // ClientHello.
+  // The PRF hash is now known.
   if (!hs->transcript.InitHash(ssl_protocol_version(ssl), hs->new_cipher)) {
     return ssl_hs_error;
   }
@@ -252,6 +269,16 @@
     return ssl_ticket_aead_ignore_ticket;
   }
 
+  // Per RFC 8446, section 4.2.9, servers MUST abort the handshake if the client
+  // sends pre_shared_key without psk_key_exchange_modes.
+  CBS unused;
+  if (!ssl_client_hello_get_extension(client_hello, &unused,
+                                      TLSEXT_TYPE_psk_key_exchange_modes)) {
+    *out_alert = SSL_AD_MISSING_EXTENSION;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
+    return ssl_ticket_aead_error;
+  }
+
   CBS ticket, binders;
   uint32_t client_ticket_age;
   if (!ssl_ext_pre_shared_key_parse_clienthello(
@@ -337,13 +364,8 @@
 static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   SSLMessage msg;
-  if (!ssl->method->get_message(ssl, &msg)) {
-    return ssl_hs_read_message;
-  }
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
-    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+  if (!hs->GetClientHello(&msg, &client_hello)) {
     return ssl_hs_error;
   }
 
@@ -354,7 +376,7 @@
                          &offered_ticket, msg, &client_hello)) {
     case ssl_ticket_aead_ignore_ticket:
       assert(!session);
-      if (!ssl_get_new_session(hs, 1 /* server */)) {
+      if (!ssl_get_new_session(hs)) {
         ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
         return ssl_hs_error;
       }
@@ -371,6 +393,7 @@
       }
 
       ssl->s3->session_reused = true;
+      hs->can_release_private_key = true;
 
       // Resumption incorporates fresh key material, so refresh the timeout.
       ssl_session_renew_timeout(ssl, hs->new_session.get(),
@@ -393,6 +416,23 @@
     return ssl_hs_error;
   }
 
+  // Record connection properties in the new session.
+  hs->new_session->cipher = hs->new_cipher;
+  if (!tls1_get_shared_group(hs, &hs->new_session->group_id)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SHARED_GROUP);
+    ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+    return ssl_hs_error;
+  }
+
+  // Determine if we need HelloRetryRequest.
+  bool found_key_share;
+  if (!ssl_ext_key_share_parse_clienthello(hs, &found_key_share,
+                                           /*out_key_share=*/nullptr, &alert,
+                                           &client_hello)) {
+    ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+    return ssl_hs_error;
+  }
+
   // Determine if we're negotiating 0-RTT.
   if (!ssl->enable_early_data) {
     ssl->s3->early_data_reason = ssl_early_data_disabled;
@@ -404,12 +444,9 @@
     ssl->s3->early_data_reason = ssl_early_data_unsupported_for_session;
   } else if (!hs->early_data_offered) {
     ssl->s3->early_data_reason = ssl_early_data_peer_declined;
-  } else if (ssl->s3->channel_id_valid) {
+  } else if (hs->channel_id_negotiated) {
     // Channel ID is incompatible with 0-RTT.
     ssl->s3->early_data_reason = ssl_early_data_channel_id;
-  } else if (ssl->s3->token_binding_negotiated) {
-    // Token Binding is incompatible with 0-RTT.
-    ssl->s3->early_data_reason = ssl_early_data_token_binding;
   } else if (MakeConstSpan(ssl->s3->alpn_selected) != session->early_alpn) {
     // The negotiated ALPN must match the one in the ticket.
     ssl->s3->early_data_reason = ssl_early_data_alpn_mismatch;
@@ -423,6 +460,8 @@
     ssl->s3->early_data_reason = ssl_early_data_ticket_age_skew;
   } else if (!quic_ticket_compatible(session.get(), hs->config)) {
     ssl->s3->early_data_reason = ssl_early_data_quic_parameter_mismatch;
+  } else if (!found_key_share) {
+    ssl->s3->early_data_reason = ssl_early_data_hello_retry_request;
   } else {
     // |ssl_session_is_resumable| forbids cross-cipher resumptions even if the
     // PRF hashes match.
@@ -432,9 +471,6 @@
     ssl->s3->early_data_accepted = true;
   }
 
-  // Record connection properties in the new session.
-  hs->new_session->cipher = hs->new_cipher;
-
   // Store the ALPN and ALPS values in the session for 0-RTT. Note the peer
   // applications settings are not generally known until client
   // EncryptedExtensions.
@@ -475,17 +511,12 @@
       ssl_get_handshake_digest(ssl_protocol_version(ssl), hs->new_cipher));
 
   // Set up the key schedule and incorporate the PSK into the running secret.
-  if (ssl->s3->session_reused) {
-    if (!tls13_init_key_schedule(
-            hs, MakeConstSpan(hs->new_session->secret,
-                              hs->new_session->secret_length))) {
-      return ssl_hs_error;
-    }
-  } else if (!tls13_init_key_schedule(hs, MakeConstSpan(kZeroes, hash_len))) {
-    return ssl_hs_error;
-  }
-
-  if (!ssl_hash_message(hs, msg)) {
+  if (!tls13_init_key_schedule(
+          hs, ssl->s3->session_reused
+                  ? MakeConstSpan(hs->new_session->secret,
+                                  hs->new_session->secret_length)
+                  : MakeConstSpan(kZeroes, hash_len)) ||
+      !ssl_hash_message(hs, msg)) {
     return ssl_hs_error;
   }
 
@@ -497,33 +528,30 @@
     ssl->s3->skip_early_data = true;
   }
 
-  // Resolve ECDHE and incorporate it into the secret.
-  bool need_retry;
-  if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
-    if (need_retry) {
-      if (ssl->s3->early_data_accepted) {
-        ssl->s3->early_data_reason = ssl_early_data_hello_retry_request;
-        ssl->s3->early_data_accepted = false;
-      }
-      ssl->s3->skip_early_data = true;
-      ssl->method->next_message(ssl);
-      if (!hs->transcript.UpdateForHelloRetryRequest()) {
-        return ssl_hs_error;
-      }
-      hs->tls13_state = state13_send_hello_retry_request;
-      return ssl_hs_ok;
+  if (!found_key_share) {
+    ssl->method->next_message(ssl);
+    if (!hs->transcript.UpdateForHelloRetryRequest()) {
+      return ssl_hs_error;
     }
+    hs->tls13_state = state13_send_hello_retry_request;
+    return ssl_hs_ok;
+  }
+
+  if (!resolve_ecdhe_secret(hs, &client_hello)) {
     return ssl_hs_error;
   }
 
   ssl->method->next_message(ssl);
+  hs->ech_client_hello_buf.Reset();
   hs->tls13_state = state13_send_server_hello;
   return ssl_hs_ok;
 }
 
 static enum ssl_hs_wait_t do_send_hello_retry_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-
+  if (hs->hints_requested) {
+    return ssl_hs_hints_ready;
+  }
 
   ScopedCBB cbb;
   CBB body, session_id, extensions;
@@ -542,12 +570,34 @@
       !CBB_add_u16(&extensions, ssl->version) ||
       !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16(&extensions, 2 /* length */) ||
-      !CBB_add_u16(&extensions, group_id) ||
-      !ssl_add_message_cbb(ssl, cbb.get())) {
+      !CBB_add_u16(&extensions, group_id)) {
     return ssl_hs_error;
   }
+  if (hs->ech_is_inner) {
+    // Fill a placeholder for the ECH confirmation value.
+    if (!CBB_add_u16(&extensions, TLSEXT_TYPE_encrypted_client_hello) ||
+        !CBB_add_u16(&extensions, ECH_CONFIRMATION_SIGNAL_LEN) ||
+        !CBB_add_zeros(&extensions, ECH_CONFIRMATION_SIGNAL_LEN)) {
+      return ssl_hs_error;
+    }
+  }
+  Array<uint8_t> hrr;
+  if (!ssl->method->finish_message(ssl, cbb.get(), &hrr)) {
+    return ssl_hs_error;
+  }
+  if (hs->ech_is_inner) {
+    // Now that the message is encoded, fill in the whole value.
+    size_t offset = hrr.size() - ECH_CONFIRMATION_SIGNAL_LEN;
+    if (!ssl_ech_accept_confirmation(
+            hs, MakeSpan(hrr).last(ECH_CONFIRMATION_SIGNAL_LEN),
+            ssl->s3->client_random, hs->transcript, /*is_hrr=*/true, hrr,
+            offset)) {
+      return ssl_hs_error;
+    }
+  }
 
-  if (!ssl->method->add_change_cipher_spec(ssl)) {
+  if (!ssl->method->add_message(ssl, std::move(hrr)) ||
+      !ssl->method->add_change_cipher_spec(ssl)) {
     return ssl_hs_error;
   }
 
@@ -566,12 +616,78 @@
     return ssl_hs_error;
   }
   SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, msg)) {
+  if (!ssl_client_hello_init(ssl, &client_hello, msg.body)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
     ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
+  if (ssl->s3->ech_status == ssl_ech_accepted) {
+    // If we previously accepted the ClientHelloInner, the second ClientHello
+    // must contain an outer encrypted_client_hello extension.
+    CBS ech_body;
+    if (!ssl_client_hello_get_extension(&client_hello, &ech_body,
+                                        TLSEXT_TYPE_encrypted_client_hello)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
+      return ssl_hs_error;
+    }
+    uint16_t kdf_id, aead_id;
+    uint8_t type, config_id;
+    CBS enc, payload;
+    if (!CBS_get_u8(&ech_body, &type) ||     //
+        type != ECH_CLIENT_OUTER ||          //
+        !CBS_get_u16(&ech_body, &kdf_id) ||  //
+        !CBS_get_u16(&ech_body, &aead_id) ||
+        !CBS_get_u8(&ech_body, &config_id) ||
+        !CBS_get_u16_length_prefixed(&ech_body, &enc) ||
+        !CBS_get_u16_length_prefixed(&ech_body, &payload) ||
+        CBS_len(&ech_body) != 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      return ssl_hs_error;
+    }
+
+    if (kdf_id != EVP_HPKE_KDF_id(EVP_HPKE_CTX_kdf(hs->ech_hpke_ctx.get())) ||
+        aead_id !=
+            EVP_HPKE_AEAD_id(EVP_HPKE_CTX_aead(hs->ech_hpke_ctx.get())) ||
+        config_id != hs->ech_config_id || CBS_len(&enc) > 0) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+      return ssl_hs_error;
+    }
+
+    // Decrypt the payload with the HPKE context from the first ClientHello.
+    Array<uint8_t> encoded_client_hello_inner;
+    bool unused;
+    if (!ssl_client_hello_decrypt(hs->ech_hpke_ctx.get(),
+                                  &encoded_client_hello_inner, &unused,
+                                  &client_hello, payload)) {
+      // Decryption failure is fatal in the second ClientHello.
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECRYPT_ERROR);
+      return ssl_hs_error;
+    }
+
+    // Recover the ClientHelloInner from the EncodedClientHelloInner.
+    uint8_t alert = SSL_AD_DECODE_ERROR;
+    bssl::Array<uint8_t> client_hello_inner;
+    if (!ssl_decode_client_hello_inner(ssl, &alert, &client_hello_inner,
+                                       encoded_client_hello_inner,
+                                       &client_hello)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
+      return ssl_hs_error;
+    }
+    hs->ech_client_hello_buf = std::move(client_hello_inner);
+
+    // Reparse |client_hello| from the buffer owned by |hs|.
+    if (!hs->GetClientHello(&msg, &client_hello)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      return ssl_hs_error;
+    }
+  }
+
   // We perform all our negotiation based on the first ClientHello (for
   // consistency with what |select_certificate_cb| observed), which is in the
   // transcript, so we can ignore most of this second one.
@@ -607,13 +723,7 @@
     }
   }
 
-  bool need_retry;
-  if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
-    if (need_retry) {
-      // Only send one HelloRetryRequest.
-      ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-      OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-    }
+  if (!resolve_ecdhe_secret(hs, &client_hello)) {
     return ssl_hs_error;
   }
 
@@ -629,6 +739,7 @@
   }
 
   ssl->method->next_message(ssl);
+  hs->ech_client_hello_buf.Reset();
   hs->tls13_state = state13_send_server_hello;
   return ssl_hs_ok;
 }
@@ -637,62 +748,61 @@
   SSL *const ssl = hs->ssl;
 
   Span<uint8_t> random(ssl->s3->server_random);
-  RAND_bytes(random.data(), random.size());
 
-  // If the ClientHello has an ech_is_inner extension, we must be the ECH
-  // backend server. In response to ech_is_inner, we will overwrite part of the
-  // ServerHello.random with the ECH acceptance confirmation.
-  if (hs->ech_is_inner_present) {
-    // Construct the ServerHelloECHConf message, which is the same as
-    // ServerHello, except the last 8 bytes of its random field are zeroed out.
-    Span<uint8_t> random_suffix = random.subspan(24);
-    OPENSSL_memset(random_suffix.data(), 0, random_suffix.size());
-
-    ScopedCBB cbb;
-    CBB body, extensions, session_id;
-    if (!ssl->method->init_message(ssl, cbb.get(), &body,
-                                   SSL3_MT_SERVER_HELLO) ||
-        !CBB_add_u16(&body, TLS1_2_VERSION) ||
-        !CBB_add_bytes(&body, random.data(), random.size()) ||
-        !CBB_add_u8_length_prefixed(&body, &session_id) ||
-        !CBB_add_bytes(&session_id, hs->session_id, hs->session_id_len) ||
-        !CBB_add_u16(&body, SSL_CIPHER_get_protocol_id(hs->new_cipher)) ||
-        !CBB_add_u8(&body, 0) ||
-        !CBB_add_u16_length_prefixed(&body, &extensions) ||
-        !ssl_ext_pre_shared_key_add_serverhello(hs, &extensions) ||
-        !ssl_ext_key_share_add_serverhello(hs, &extensions, /*dry_run=*/true) ||
-        !ssl_ext_supported_versions_add_serverhello(hs, &extensions) ||
-        !CBB_flush(cbb.get())) {
-      return ssl_hs_error;
-    }
-
-    // Note that |cbb| includes the message type and length fields, but not the
-    // record layer header.
-    if (!tls13_ech_accept_confirmation(
-            hs, random_suffix,
-            bssl::MakeConstSpan(CBB_data(cbb.get()), CBB_len(cbb.get())))) {
+  SSL_HANDSHAKE_HINTS *const hints = hs->hints.get();
+  if (hints && !hs->hints_requested &&
+      hints->server_random.size() == random.size()) {
+    OPENSSL_memcpy(random.data(), hints->server_random.data(), random.size());
+  } else {
+    RAND_bytes(random.data(), random.size());
+    if (hints && hs->hints_requested &&
+        !hints->server_random.CopyFrom(random)) {
       return ssl_hs_error;
     }
   }
 
-  // Send a ServerHello.
+  Array<uint8_t> server_hello;
   ScopedCBB cbb;
   CBB body, extensions, session_id;
   if (!ssl->method->init_message(ssl, cbb.get(), &body, SSL3_MT_SERVER_HELLO) ||
       !CBB_add_u16(&body, TLS1_2_VERSION) ||
-      !CBB_add_bytes(&body, random.data(), random.size()) ||
+      !CBB_add_bytes(&body, ssl->s3->server_random,
+                     sizeof(ssl->s3->server_random)) ||
       !CBB_add_u8_length_prefixed(&body, &session_id) ||
       !CBB_add_bytes(&session_id, hs->session_id, hs->session_id_len) ||
       !CBB_add_u16(&body, SSL_CIPHER_get_protocol_id(hs->new_cipher)) ||
       !CBB_add_u8(&body, 0) ||
       !CBB_add_u16_length_prefixed(&body, &extensions) ||
       !ssl_ext_pre_shared_key_add_serverhello(hs, &extensions) ||
-      !ssl_ext_key_share_add_serverhello(hs, &extensions, /*dry_run=*/false) ||
+      !ssl_ext_key_share_add_serverhello(hs, &extensions) ||
       !ssl_ext_supported_versions_add_serverhello(hs, &extensions) ||
-      !ssl_add_message_cbb(ssl, cbb.get())) {
+      !ssl->method->finish_message(ssl, cbb.get(), &server_hello)) {
     return ssl_hs_error;
   }
 
+  assert(ssl->s3->ech_status != ssl_ech_accepted || hs->ech_is_inner);
+  if (hs->ech_is_inner) {
+    // Fill in the ECH confirmation signal.
+    const size_t offset = ssl_ech_confirmation_signal_hello_offset(ssl);
+    Span<uint8_t> random_suffix = random.last(ECH_CONFIRMATION_SIGNAL_LEN);
+    if (!ssl_ech_accept_confirmation(hs, random_suffix, ssl->s3->client_random,
+                                     hs->transcript,
+                                     /*is_hrr=*/false, server_hello, offset)) {
+      return ssl_hs_error;
+    }
+
+    // Update |server_hello|.
+    Span<uint8_t> server_hello_out =
+        MakeSpan(server_hello).subspan(offset, ECH_CONFIRMATION_SIGNAL_LEN);
+    OPENSSL_memcpy(server_hello_out.data(), random_suffix.data(),
+                   ECH_CONFIRMATION_SIGNAL_LEN);
+  }
+
+  if (!ssl->method->add_message(ssl, std::move(server_hello))) {
+    return ssl_hs_error;
+  }
+
+  hs->ecdh_public_key.Reset();  // No longer needed.
   if (!ssl->s3->used_hello_retry_request &&
       !ssl->method->add_change_cipher_spec(ssl)) {
     return ssl_hs_error;
@@ -719,7 +829,7 @@
     hs->cert_request = !!(hs->config->verify_mode & SSL_VERIFY_PEER);
     // Only request a certificate if Channel ID isn't negotiated.
     if ((hs->config->verify_mode & SSL_VERIFY_PEER_IF_NO_OBC) &&
-        ssl->s3->channel_id_valid) {
+        hs->channel_id_negotiated) {
       hs->cert_request = false;
     }
   }
@@ -796,6 +906,11 @@
 
 static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+  if (hs->hints_requested) {
+    return ssl_hs_hints_ready;
+  }
+
+  hs->can_release_private_key = true;
   if (!tls13_add_finished(hs) ||
       // Update the secret to the master secret and derive traffic keys.
       !tls13_advance_key_schedule(
@@ -873,9 +988,8 @@
     hs->in_early_data = true;
   }
 
-  // QUIC doesn't use an EndOfEarlyData message (draft-ietf-quic-tls-22,
-  // section 8.3), so we switch to client_handshake_secret before the early
-  // return.
+  // QUIC doesn't use an EndOfEarlyData message (RFC 9001, section 8.3), so we
+  // switch to client_handshake_secret before the early return.
   if (ssl->quic_method != nullptr) {
     if (!tls13_set_traffic_key(ssl, ssl_encryption_handshake, evp_aead_open,
                                hs->new_session.get(),
@@ -946,20 +1060,15 @@
       return ssl_hs_error;
     }
 
-    // Parse out the extensions.
-    bool have_application_settings = false;
-    CBS application_settings;
-    SSL_EXTENSION_TYPE ext_types[] = {{TLSEXT_TYPE_application_settings,
-                                       &have_application_settings,
-                                       &application_settings}};
+    SSLExtension application_settings(TLSEXT_TYPE_application_settings);
     uint8_t alert = SSL_AD_DECODE_ERROR;
-    if (!ssl_parse_extensions(&extensions, &alert, ext_types,
+    if (!ssl_parse_extensions(&extensions, &alert, {&application_settings},
                               /*ignore_unknown=*/false)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, alert);
       return ssl_hs_error;
     }
 
-    if (!have_application_settings) {
+    if (!application_settings.present) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_MISSING_EXTENSION);
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_MISSING_EXTENSION);
       return ssl_hs_error;
@@ -968,7 +1077,7 @@
     // Note that, if 0-RTT was accepted, these values will already have been
     // initialized earlier.
     if (!hs->new_session->peer_application_settings.CopyFrom(
-            application_settings) ||
+            application_settings.data) ||
         !ssl_hash_message(hs, msg)) {
       ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
       return ssl_hs_error;
@@ -1051,7 +1160,7 @@
 
 static enum ssl_hs_wait_t do_read_channel_id(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl->s3->channel_id_valid) {
+  if (!hs->channel_id_negotiated) {
     hs->tls13_state = state13_read_client_finished;
     return ssl_hs_ok;
   }
diff --git a/deps/boringssl/src/ssl/tls_method.cc b/deps/boringssl/src/ssl/tls_method.cc
index 8165d1c..326cbe7 100644
--- a/deps/boringssl/src/ssl/tls_method.cc
+++ b/deps/boringssl/src/ssl/tls_method.cc
@@ -93,7 +93,8 @@
   }
 
   if (ssl->quic_method != nullptr) {
-    if (!ssl->quic_method->set_read_secret(ssl, level, aead_ctx->cipher(),
+    if ((ssl->s3->hs == nullptr || !ssl->s3->hs->hints_requested) &&
+        !ssl->quic_method->set_read_secret(ssl, level, aead_ctx->cipher(),
                                            secret_for_quic.data(),
                                            secret_for_quic.size())) {
       return false;
@@ -121,7 +122,8 @@
   }
 
   if (ssl->quic_method != nullptr) {
-    if (!ssl->quic_method->set_write_secret(ssl, level, aead_ctx->cipher(),
+    if ((ssl->s3->hs == nullptr || !ssl->s3->hs->hints_requested) &&
+        !ssl->quic_method->set_write_secret(ssl, level, aead_ctx->cipher(),
                                             secret_for_quic.data(),
                                             secret_for_quic.size())) {
       return false;
diff --git a/deps/boringssl/src/tool/CMakeLists.txt b/deps/boringssl/src/tool/CMakeLists.txt
index e9e387b..a591b7d 100644
--- a/deps/boringssl/src/tool/CMakeLists.txt
+++ b/deps/boringssl/src/tool/CMakeLists.txt
@@ -10,6 +10,7 @@
   digest.cc
   fd.cc
   file.cc
+  generate_ech.cc
   generate_ed25519.cc
   genrsa.cc
   pkcs12.cc
diff --git a/deps/boringssl/src/tool/args.cc b/deps/boringssl/src/tool/args.cc
index 9ec18a3..4deb881 100644
--- a/deps/boringssl/src/tool/args.cc
+++ b/deps/boringssl/src/tool/args.cc
@@ -15,6 +15,7 @@
 #include <string>
 #include <vector>
 
+#include <errno.h>
 #include <limits.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -92,13 +93,16 @@
     return false;
   }
 
+  errno = 0;
   char *endptr;
   unsigned long int num = strtoul(value.c_str(), &endptr, 10);
-  if (*endptr ||
-      num > UINT_MAX) {
+  if (num == ULONG_MAX && errno == ERANGE) {
     return false;
   }
+  if (*endptr != 0 || num > UINT_MAX) {
+    return false;
+  }
+  *out = static_cast<unsigned>(num);
 
-  *out = num;
   return true;
 }
diff --git a/deps/boringssl/src/tool/client.cc b/deps/boringssl/src/tool/client.cc
index 31378d6..4301c22 100644
--- a/deps/boringssl/src/tool/client.cc
+++ b/deps/boringssl/src/tool/client.cc
@@ -64,6 +64,13 @@
         "-server-name", kOptionalArgument, "The server name to advertise",
     },
     {
+        "-ech-grease", kBooleanArgument, "Enable ECH GREASE",
+    },
+    {
+        "-ech-config-list", kOptionalArgument,
+        "Path to file containing serialized ECHConfigs",
+    },
+    {
         "-select-next-proto", kOptionalArgument,
         "An NPN protocol to select if the server supports NPN",
     },
@@ -116,6 +123,11 @@
         "-grease", kBooleanArgument, "Enable GREASE",
     },
     {
+        "-permute-extensions",
+        kBooleanArgument,
+        "Permute extensions in handshake messages",
+    },
+    {
         "-test-resumption", kBooleanArgument,
         "Connect to the server twice. The first connection is closed once a "
         "session is established. The second connection offers it.",
@@ -265,6 +277,24 @@
     SSL_set_tlsext_host_name(ssl.get(), args_map["-server-name"].c_str());
   }
 
+  if (args_map.count("-ech-grease") != 0) {
+    SSL_set_enable_ech_grease(ssl.get(), 1);
+  }
+
+  if (args_map.count("-ech-config-list") != 0) {
+    const char *filename = args_map["-ech-config-list"].c_str();
+    ScopedFILE f(fopen(filename, "rb"));
+    std::vector<uint8_t> data;
+    if (f == nullptr || !ReadAll(&data, f.get())) {
+      fprintf(stderr, "Error reading %s.\n", filename);
+      return false;
+    }
+    if (!SSL_set1_ech_config_list(ssl.get(), data.data(), data.size())) {
+      fprintf(stderr, "Error setting ECHConfigList\n");
+      return false;
+    }
+  }
+
   if (args_map.count("-session-in") != 0) {
     bssl::UniquePtr<BIO> in(BIO_new_file(args_map["-session-in"].c_str(),
                                          "rb"));
@@ -313,15 +343,17 @@
       }
       early_data = std::string(data.begin(), data.end());
     }
-    int ed_size = early_data.size();
-    int ssl_ret = SSL_write(ssl.get(), early_data.data(), ed_size);
-    if (ssl_ret <= 0) {
-      int ssl_err = SSL_get_error(ssl.get(), ssl_ret);
-      PrintSSLError(stderr, "Error while writing", ssl_err, ssl_ret);
-      return false;
-    } else if (ssl_ret != ed_size) {
-      fprintf(stderr, "Short write from SSL_write.\n");
-      return false;
+    if (!early_data.empty()) {
+      int ed_size = early_data.size();
+      int ssl_ret = SSL_write(ssl.get(), early_data.data(), ed_size);
+      if (ssl_ret <= 0) {
+        int ssl_err = SSL_get_error(ssl.get(), ssl_ret);
+        PrintSSLError(stderr, "Error while writing", ssl_err, ssl_ret);
+        return false;
+      } else if (ssl_ret != ed_size) {
+        fprintf(stderr, "Short write from SSL_write.\n");
+        return false;
+      }
     }
   }
 
@@ -504,6 +536,10 @@
     SSL_CTX_set_grease_enabled(ctx.get(), 1);
   }
 
+  if (args_map.count("-permute-extensions") != 0) {
+    SSL_CTX_set_permute_extensions(ctx.get(), 1);
+  }
+
   if (args_map.count("-root-certs") != 0) {
     if (!SSL_CTX_load_verify_locations(
             ctx.get(), args_map["-root-certs"].c_str(), nullptr)) {
diff --git a/deps/boringssl/src/tool/file.cc b/deps/boringssl/src/tool/file.cc
index 9b5ff1b..8d74e63 100644
--- a/deps/boringssl/src/tool/file.cc
+++ b/deps/boringssl/src/tool/file.cc
@@ -12,7 +12,11 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+#include <openssl/bytestring.h>
+
+#include <errno.h>
 #include <stdio.h>
+#include <string.h>
 
 #include <algorithm>
 #include <vector>
@@ -48,3 +52,17 @@
     }
   }
 }
+
+bool WriteToFile(const std::string &path, bssl::Span<const uint8_t> in) {
+  ScopedFILE file(fopen(path.c_str(), "wb"));
+  if (!file) {
+    fprintf(stderr, "Failed to open '%s': %s\n", path.c_str(), strerror(errno));
+    return false;
+  }
+  if (fwrite(in.data(), in.size(), 1, file.get()) != 1) {
+    fprintf(stderr, "Failed to write to '%s': %s\n", path.c_str(),
+            strerror(errno));
+    return false;
+  }
+  return true;
+}
diff --git a/deps/boringssl/src/tool/generate_ech.cc b/deps/boringssl/src/tool/generate_ech.cc
new file mode 100644
index 0000000..f1e8cbe
--- /dev/null
+++ b/deps/boringssl/src/tool/generate_ech.cc
@@ -0,0 +1,133 @@
+/* Copyright (c) 2021, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <stdio.h>
+
+#include <limits>
+#include <vector>
+
+#include <openssl/bytestring.h>
+#include <openssl/hpke.h>
+#include <openssl/span.h>
+#include <openssl/ssl.h>
+
+#include "internal.h"
+
+
+static const struct argument kArguments[] = {
+    {
+        "-out-ech-config-list",
+        kRequiredArgument,
+        "The path where the ECHConfigList should be written.",
+    },
+    {
+        "-out-ech-config",
+        kRequiredArgument,
+        "The path where the ECHConfig should be written.",
+    },
+    {
+        "-out-private-key",
+        kRequiredArgument,
+        "The path where the private key should be written.",
+    },
+    {
+        "-public-name",
+        kRequiredArgument,
+        "The public name for the new ECHConfig.",
+    },
+    {
+        "-config-id",
+        kRequiredArgument,
+        "The config ID for the new ECHConfig, from 0 to 255. Config IDs may be "
+        "reused, but should be unique among active configs on a server for "
+        "performance.",
+    },
+    {
+        "-max-name-length",
+        kOptionalArgument,
+        "The length of the longest name in the anonymity set, to guide client "
+        "padding.",
+    },
+    {
+        "",
+        kOptionalArgument,
+        "",
+    },
+};
+
+bool GenerateECH(const std::vector<std::string> &args) {
+  std::map<std::string, std::string> args_map;
+  if (!ParseKeyValueArguments(&args_map, args, kArguments)) {
+    PrintUsage(kArguments);
+    return false;
+  }
+
+  unsigned config_id;
+  if (!GetUnsigned(&config_id, "-config-id", 0, args_map) ||
+      config_id > std::numeric_limits<uint8_t>::max()) {
+    fprintf(stderr, "Error parsing -config-id argument\n");
+    return false;
+  }
+
+  unsigned max_name_len = 0;
+  if (args_map.count("-max-name-length") != 0 &&
+      !GetUnsigned(&max_name_len, "-max-name-length", 0, args_map)) {
+    fprintf(stderr, "Error parsing -max-name-length argument\n");
+    return false;
+  }
+
+  bssl::ScopedEVP_HPKE_KEY key;
+  uint8_t public_key[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH];
+  uint8_t private_key[EVP_HPKE_MAX_PRIVATE_KEY_LENGTH];
+  size_t public_key_len, private_key_len;
+  if (!EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256()) ||
+      !EVP_HPKE_KEY_public_key(key.get(), public_key, &public_key_len,
+                               sizeof(public_key)) ||
+      !EVP_HPKE_KEY_private_key(key.get(), private_key, &private_key_len,
+                                sizeof(private_key))) {
+    fprintf(stderr, "Failed to generate the HPKE keypair\n");
+    return false;
+  }
+
+  uint8_t *ech_config;
+  size_t ech_config_len;
+  if (!SSL_marshal_ech_config(
+          &ech_config, &ech_config_len, static_cast<uint8_t>(config_id),
+          key.get(), args_map["-public-name"].c_str(), size_t{max_name_len})) {
+    fprintf(stderr, "Failed to serialize the ECHConfigList\n");
+    return false;
+  }
+  bssl::UniquePtr<uint8_t> free_ech_config(ech_config);
+
+  bssl::ScopedCBB cbb;
+  CBB body;
+  if (!CBB_init(cbb.get(), ech_config_len + sizeof(uint16_t)) ||
+      !CBB_add_u16_length_prefixed(cbb.get(), &body) ||
+      !CBB_add_bytes(&body, ech_config, ech_config_len) ||
+      !CBB_flush(cbb.get())) {
+    fprintf(stderr, "Failed to serialize the ECHConfigList\n");
+    return false;
+  }
+  if (!WriteToFile(
+          args_map["-out-ech-config-list"],
+          bssl::MakeConstSpan(CBB_data(cbb.get()), CBB_len(cbb.get()))) ||
+      !WriteToFile(args_map["-out-ech-config"],
+                   bssl::MakeConstSpan(ech_config, ech_config_len)) ||
+      !WriteToFile(args_map["-out-private-key"],
+                   bssl::MakeConstSpan(private_key, private_key_len))) {
+    fprintf(stderr, "Failed to write ECHConfig or private key to file\n");
+    return false;
+  }
+  return true;
+}
diff --git a/deps/boringssl/src/tool/generate_ed25519.cc b/deps/boringssl/src/tool/generate_ed25519.cc
index 6499dbe..8920099 100644
--- a/deps/boringssl/src/tool/generate_ed25519.cc
+++ b/deps/boringssl/src/tool/generate_ed25519.cc
@@ -34,21 +34,6 @@
     },
 };
 
-static bool WriteToFile(const std::string &path, const uint8_t *in,
-                        size_t in_len) {
-  ScopedFILE file(fopen(path.c_str(), "wb"));
-  if (!file) {
-    fprintf(stderr, "Failed to open '%s': %s\n", path.c_str(), strerror(errno));
-    return false;
-  }
-  if (fwrite(in, in_len, 1, file.get()) != 1) {
-    fprintf(stderr, "Failed to write to '%s': %s\n", path.c_str(),
-            strerror(errno));
-    return false;
-  }
-  return true;
-}
-
 bool GenerateEd25519Key(const std::vector<std::string> &args) {
   std::map<std::string, std::string> args_map;
 
@@ -60,7 +45,6 @@
   uint8_t public_key[32], private_key[64];
   ED25519_keypair(public_key, private_key);
 
-  return WriteToFile(args_map["-out-public"], public_key, sizeof(public_key)) &&
-         WriteToFile(args_map["-out-private"], private_key,
-                     sizeof(private_key));
+  return WriteToFile(args_map["-out-public"], public_key) &&
+         WriteToFile(args_map["-out-private"], private_key);
 }
diff --git a/deps/boringssl/src/tool/internal.h b/deps/boringssl/src/tool/internal.h
index eb9e4ba..7f3692a 100644
--- a/deps/boringssl/src/tool/internal.h
+++ b/deps/boringssl/src/tool/internal.h
@@ -16,6 +16,7 @@
 #define OPENSSL_HEADER_TOOL_INTERNAL_H
 
 #include <openssl/base.h>
+#include <openssl/span.h>
 
 #include <string>
 #include <utility>
@@ -120,10 +121,12 @@
                  const std::map<std::string, std::string> &args);
 
 bool ReadAll(std::vector<uint8_t> *out, FILE *in);
+bool WriteToFile(const std::string &path, bssl::Span<const uint8_t> in);
 
 bool Ciphers(const std::vector<std::string> &args);
 bool Client(const std::vector<std::string> &args);
 bool DoPKCS12(const std::vector<std::string> &args);
+bool GenerateECH(const std::vector<std::string> &args);
 bool GenerateEd25519Key(const std::vector<std::string> &args);
 bool GenerateRSAKey(const std::vector<std::string> &args);
 bool MD5Sum(const std::vector<std::string> &args);
diff --git a/deps/boringssl/src/tool/server.cc b/deps/boringssl/src/tool/server.cc
index 989d335..18b692d 100644
--- a/deps/boringssl/src/tool/server.cc
+++ b/deps/boringssl/src/tool/server.cc
@@ -17,6 +17,7 @@
 #include <memory>
 
 #include <openssl/err.h>
+#include <openssl/hpke.h>
 #include <openssl/rand.h>
 #include <openssl/ssl.h>
 
@@ -61,6 +62,16 @@
         "-ocsp-response", kOptionalArgument, "OCSP response file to send",
     },
     {
+        "-ech-key",
+        kOptionalArgument,
+        "File containing the private key corresponding to the ECHConfig.",
+    },
+    {
+        "-ech-config",
+        kOptionalArgument,
+        "File containing one ECHConfig.",
+    },
+    {
         "-loop", kBooleanArgument,
         "The server will continue accepting new sequential connections.",
     },
@@ -261,6 +272,47 @@
     }
   }
 
+  if (args_map.count("-ech-key") + args_map.count("-ech-config") == 1) {
+    fprintf(stderr,
+            "-ech-config and -ech-key must be specified together.\n");
+    return false;
+  }
+
+  if (args_map.count("-ech-key") != 0) {
+    // Load the ECH private key.
+    std::string ech_key_path = args_map["-ech-key"];
+    ScopedFILE ech_key_file(fopen(ech_key_path.c_str(), "rb"));
+    std::vector<uint8_t> ech_key;
+    if (ech_key_file == nullptr ||
+        !ReadAll(&ech_key, ech_key_file.get())) {
+      fprintf(stderr, "Error reading %s\n", ech_key_path.c_str());
+      return false;
+    }
+
+    // Load the ECHConfig.
+    std::string ech_config_path = args_map["-ech-config"];
+    ScopedFILE ech_config_file(fopen(ech_config_path.c_str(), "rb"));
+    std::vector<uint8_t> ech_config;
+    if (ech_config_file == nullptr ||
+        !ReadAll(&ech_config, ech_config_file.get())) {
+      fprintf(stderr, "Error reading %s\n", ech_config_path.c_str());
+      return false;
+    }
+
+    bssl::UniquePtr<SSL_ECH_KEYS> keys(SSL_ECH_KEYS_new());
+    bssl::ScopedEVP_HPKE_KEY key;
+    if (!keys ||
+        !EVP_HPKE_KEY_init(key.get(), EVP_hpke_x25519_hkdf_sha256(),
+                           ech_key.data(), ech_key.size()) ||
+        !SSL_ECH_KEYS_add(keys.get(),
+                          /*is_retry_config=*/1, ech_config.data(),
+                          ech_config.size(), key.get()) ||
+        !SSL_CTX_set1_ech_keys(ctx.get(), keys.get())) {
+      fprintf(stderr, "Error setting server's ECHConfig and private key\n");
+      return false;
+    }
+  }
+
   if (args_map.count("-cipher") != 0 &&
       !SSL_CTX_set_strict_cipher_list(ctx.get(), args_map["-cipher"].c_str())) {
     fprintf(stderr, "Failed setting cipher list\n");
diff --git a/deps/boringssl/src/tool/speed.cc b/deps/boringssl/src/tool/speed.cc
index 1b89b42..613e630 100644
--- a/deps/boringssl/src/tool/speed.cc
+++ b/deps/boringssl/src/tool/speed.cc
@@ -270,6 +270,16 @@
       return false;
     }
     results.Print(name + " verify (fresh key)");
+
+    if (!TimeFunction(&results, [&]() -> bool {
+          return bssl::UniquePtr<RSA>(RSA_private_key_from_bytes(
+                     kRSAKeys[i].key, kRSAKeys[i].key_len)) != nullptr;
+        })) {
+      fprintf(stderr, "Failed to parse %s key.\n", name.c_str());
+      ERR_print_errors_fp(stderr);
+      return false;
+    }
+    results.Print(name + " private key parse");
   }
 
   return true;
@@ -342,12 +352,6 @@
   return true;
 }
 
-static uint8_t *align(uint8_t *in, unsigned alignment) {
-  return reinterpret_cast<uint8_t *>(
-      (reinterpret_cast<uintptr_t>(in) + alignment) &
-      ~static_cast<size_t>(alignment - 1));
-}
-
 static std::string ChunkLenSuffix(size_t chunk_len) {
   char buf[32];
   snprintf(buf, sizeof(buf), " (%zu byte%s)", chunk_len,
@@ -384,13 +388,17 @@
       new uint8_t[overhead_len + kAlignment]);
 
 
-  uint8_t *const in = align(in_storage.get(), kAlignment);
+  uint8_t *const in =
+      static_cast<uint8_t *>(align_pointer(in_storage.get(), kAlignment));
   OPENSSL_memset(in, 0, chunk_len);
-  uint8_t *const out = align(out_storage.get(), kAlignment);
+  uint8_t *const out =
+      static_cast<uint8_t *>(align_pointer(out_storage.get(), kAlignment));
   OPENSSL_memset(out, 0, chunk_len + overhead_len);
-  uint8_t *const tag = align(tag_storage.get(), kAlignment);
+  uint8_t *const tag =
+      static_cast<uint8_t *>(align_pointer(tag_storage.get(), kAlignment));
   OPENSSL_memset(tag, 0, overhead_len);
-  uint8_t *const in2 = align(in2_storage.get(), kAlignment);
+  uint8_t *const in2 =
+      static_cast<uint8_t *>(align_pointer(in2_storage.get(), kAlignment));
 
   if (!EVP_AEAD_CTX_init_with_direction(ctx.get(), aead, key.get(), key_len,
                                         EVP_AEAD_DEFAULT_TAG_LENGTH,
@@ -898,13 +906,12 @@
   TimeResults results;
 
   if (!TimeFunction(&results, []() -> bool {
-    struct HRSS_public_key pub;
-    struct HRSS_private_key priv;
-    uint8_t entropy[HRSS_GENERATE_KEY_BYTES];
-    RAND_bytes(entropy, sizeof(entropy));
-    HRSS_generate_key(&pub, &priv, entropy);
-    return true;
-  })) {
+        struct HRSS_public_key pub;
+        struct HRSS_private_key priv;
+        uint8_t entropy[HRSS_GENERATE_KEY_BYTES];
+        RAND_bytes(entropy, sizeof(entropy));
+        return HRSS_generate_key(&pub, &priv, entropy);
+      })) {
     fprintf(stderr, "Failed to time HRSS_generate_key.\n");
     return false;
   }
@@ -915,16 +922,17 @@
   struct HRSS_private_key priv;
   uint8_t key_entropy[HRSS_GENERATE_KEY_BYTES];
   RAND_bytes(key_entropy, sizeof(key_entropy));
-  HRSS_generate_key(&pub, &priv, key_entropy);
+  if (!HRSS_generate_key(&pub, &priv, key_entropy)) {
+    return false;
+  }
 
   uint8_t ciphertext[HRSS_CIPHERTEXT_BYTES];
   if (!TimeFunction(&results, [&pub, &ciphertext]() -> bool {
-    uint8_t entropy[HRSS_ENCAP_BYTES];
-    uint8_t shared_key[HRSS_KEY_BYTES];
-    RAND_bytes(entropy, sizeof(entropy));
-    HRSS_encap(ciphertext, shared_key, &pub, entropy);
-    return true;
-  })) {
+        uint8_t entropy[HRSS_ENCAP_BYTES];
+        uint8_t shared_key[HRSS_KEY_BYTES];
+        RAND_bytes(entropy, sizeof(entropy));
+        return HRSS_encap(ciphertext, shared_key, &pub, entropy);
+      })) {
     fprintf(stderr, "Failed to time HRSS_encap.\n");
     return false;
   }
@@ -932,10 +940,9 @@
   results.Print("HRSS encap");
 
   if (!TimeFunction(&results, [&priv, &ciphertext]() -> bool {
-    uint8_t shared_key[HRSS_KEY_BYTES];
-    HRSS_decap(shared_key, &priv, ciphertext, sizeof(ciphertext));
-    return true;
-  })) {
+        uint8_t shared_key[HRSS_KEY_BYTES];
+        return HRSS_decap(shared_key, &priv, ciphertext, sizeof(ciphertext));
+      })) {
     fprintf(stderr, "Failed to time HRSS_encap.\n");
     return false;
   }
diff --git a/deps/boringssl/src/tool/tool.cc b/deps/boringssl/src/tool/tool.cc
index d278535..7bec7a2 100644
--- a/deps/boringssl/src/tool/tool.cc
+++ b/deps/boringssl/src/tool/tool.cc
@@ -45,6 +45,7 @@
   { "ciphers", Ciphers },
   { "client", Client },
   { "isfips", IsFIPS },
+  { "generate-ech", GenerateECH},
   { "generate-ed25519", GenerateEd25519Key },
   { "genrsa", GenerateRSAKey },
   { "md5sum", MD5Sum },
diff --git a/deps/boringssl/src/tool/transport_common.cc b/deps/boringssl/src/tool/transport_common.cc
index b985221..cba3c7b 100644
--- a/deps/boringssl/src/tool/transport_common.cc
+++ b/deps/boringssl/src/tool/transport_common.cc
@@ -335,6 +335,9 @@
       bio, "  Early data: %s\n",
       (SSL_early_data_accepted(ssl) || SSL_in_early_data(ssl)) ? "yes" : "no");
 
+  BIO_printf(bio, "  Encrypted ClientHello: %s\n",
+             SSL_ech_accepted(ssl) ? "yes" : "no");
+
   // Print the server cert subject and issuer names.
   bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(ssl));
   if (peer != nullptr) {
diff --git a/deps/boringssl/src/util/fipstools/acvp/modulewrapper/CMakeLists.txt b/deps/boringssl/src/util/fipstools/acvp/modulewrapper/CMakeLists.txt
index 8bee5cd..267f82c 100644
--- a/deps/boringssl/src/util/fipstools/acvp/modulewrapper/CMakeLists.txt
+++ b/deps/boringssl/src/util/fipstools/acvp/modulewrapper/CMakeLists.txt
@@ -4,6 +4,7 @@
   add_executable(
     modulewrapper
 
+    main.cc
     modulewrapper.cc
   )
 
diff --git a/deps/boringssl/src/util/fipstools/acvp/modulewrapper/main.cc b/deps/boringssl/src/util/fipstools/acvp/modulewrapper/main.cc
new file mode 100644
index 0000000..a903361
--- /dev/null
+++ b/deps/boringssl/src/util/fipstools/acvp/modulewrapper/main.cc
@@ -0,0 +1,75 @@
+/* Copyright (c) 2021, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <stdio.h>
+#include <string>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/crypto.h>
+#include <openssl/span.h>
+
+#include "modulewrapper.h"
+
+
+int main(int argc, char **argv) {
+  if (argc == 2 && strcmp(argv[1], "--version") == 0) {
+    printf("Built for architecture: ");
+
+#if defined(OPENSSL_X86_64)
+    puts("x86-64 (64-bit)");
+#elif defined(OPENSSL_ARM)
+    puts("ARM (32-bit)");
+#elif defined(OPENSSL_AARCH64)
+    puts("aarch64 (64-bit)");
+#elif defined(OPENSSL_PPC64LE)
+    puts("PPC64LE (64-bit)");
+#else
+#error "FIPS build not supported on this architecture"
+#endif
+
+    printf("Hardware acceleration enabled: %s\n",
+           CRYPTO_has_asm() ? "yes" : "no");
+
+    return 0;
+  } else if (argc != 1) {
+    fprintf(stderr, "Usage: %s [--version]\n", argv[0]);
+    return 4;
+  }
+
+  std::unique_ptr<bssl::acvp::RequestBuffer> buffer =
+      bssl::acvp::RequestBuffer::New();
+  const bssl::acvp::ReplyCallback write_reply = std::bind(
+      bssl::acvp::WriteReplyToFd, STDOUT_FILENO, std::placeholders::_1);
+
+  for (;;) {
+    const bssl::Span<const bssl::Span<const uint8_t>> args =
+        ParseArgsFromFd(STDIN_FILENO, buffer.get());
+    if (args.empty()) {
+      return 1;
+    }
+
+    const bssl::acvp::Handler handler = bssl::acvp::FindHandler(args);
+    if (!handler) {
+      return 2;
+    }
+
+    if (!handler(args.subspan(1).data(), write_reply)) {
+      const std::string name(reinterpret_cast<const char *>(args[0].data()),
+                             args[0].size());
+      fprintf(stderr, "\'%s\' operation failed.\n", name.c_str());
+      return 3;
+    }
+  }
+};
diff --git a/deps/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc b/deps/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc
index 06eac8b..1974dce 100644
--- a/deps/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc
+++ b/deps/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.cc
@@ -45,15 +45,35 @@
 #include "../../../../crypto/fipsmodule/ec/internal.h"
 #include "../../../../crypto/fipsmodule/rand/internal.h"
 #include "../../../../crypto/fipsmodule/tls/internal.h"
+#include "modulewrapper.h"
 
-static constexpr size_t kMaxArgs = 8;
-static constexpr size_t kMaxArgLength = (1 << 20);
-static constexpr size_t kMaxNameLength = 30;
 
-static_assert((kMaxArgs - 1 * kMaxArgLength) + kMaxNameLength > (1 << 30),
-              "Argument limits permit excessive messages");
+namespace bssl {
+namespace acvp {
 
-using namespace bssl;
+#if defined(OPENSSL_TRUSTY)
+#include <trusty_log.h>
+#define LOG_ERROR(...) TLOGE(__VA_ARGS__)
+#else
+#define LOG_ERROR(...) fprintf(stderr, __VA_ARGS__)
+#endif  // OPENSSL_TRUSTY
+
+constexpr size_t kMaxArgLength = (1 << 20);
+
+RequestBuffer::~RequestBuffer() = default;
+
+class RequestBufferImpl : public RequestBuffer {
+ public:
+  ~RequestBufferImpl() = default;
+
+  std::vector<uint8_t> buf;
+  Span<const uint8_t> args[kMaxArgs];
+};
+
+// static
+std::unique_ptr<RequestBuffer> RequestBuffer::New() {
+  return std::unique_ptr<RequestBuffer>(new RequestBufferImpl);
+}
 
 static bool ReadAll(int fd, void *in_data, size_t data_len) {
   uint8_t *data = reinterpret_cast<uint8_t *>(in_data);
@@ -75,9 +95,74 @@
   return true;
 }
 
-template <typename... Args>
-static bool WriteReply(int fd, Args... args) {
-  std::vector<Span<const uint8_t>> spans = {args...};
+Span<const Span<const uint8_t>> ParseArgsFromFd(int fd,
+                                                RequestBuffer *in_buffer) {
+  RequestBufferImpl *buffer = reinterpret_cast<RequestBufferImpl *>(in_buffer);
+  uint32_t nums[1 + kMaxArgs];
+  const Span<const Span<const uint8_t>> empty_span;
+
+  if (!ReadAll(fd, nums, sizeof(uint32_t) * 2)) {
+    return empty_span;
+  }
+
+  const size_t num_args = nums[0];
+  if (num_args == 0) {
+    LOG_ERROR("Invalid, zero-argument operation requested.\n");
+    return empty_span;
+  } else if (num_args > kMaxArgs) {
+    LOG_ERROR("Operation requested with %zu args, but %zu is the limit.\n",
+              num_args, kMaxArgs);
+    return empty_span;
+  }
+
+  if (num_args > 1 &&
+      !ReadAll(fd, &nums[2], sizeof(uint32_t) * (num_args - 1))) {
+    return empty_span;
+  }
+
+  size_t need = 0;
+  for (size_t i = 0; i < num_args; i++) {
+    const size_t arg_length = nums[i + 1];
+    if (i == 0 && arg_length > kMaxNameLength) {
+      LOG_ERROR("Operation with name of length %zu exceeded limit of %zu.\n",
+                arg_length, kMaxNameLength);
+      return empty_span;
+    } else if (arg_length > kMaxArgLength) {
+      LOG_ERROR(
+          "Operation with argument of length %zu exceeded limit of %zu.\n",
+          arg_length, kMaxArgLength);
+      return empty_span;
+    }
+
+    // This static_assert confirms that the following addition doesn't
+    // overflow.
+    static_assert((kMaxArgs - 1 * kMaxArgLength) + kMaxNameLength > (1 << 30),
+                  "Argument limits permit excessive messages");
+    need += arg_length;
+  }
+
+  if (need > buffer->buf.size()) {
+    size_t alloced = need + (need >> 1);
+    if (alloced < need) {
+      abort();
+    }
+    buffer->buf.resize(alloced);
+  }
+
+  if (!ReadAll(fd, buffer->buf.data(), need)) {
+    return empty_span;
+  }
+
+  size_t offset = 0;
+  for (size_t i = 0; i < num_args; i++) {
+    buffer->args[i] = Span<const uint8_t>(&buffer->buf[offset], nums[i + 1]);
+    offset += nums[i + 1];
+  }
+
+  return Span<const Span<const uint8_t>>(buffer->args, num_args);
+}
+
+bool WriteReplyToFd(int fd, const std::vector<Span<const uint8_t>> &spans) {
   if (spans.empty() || spans.size() > kMaxArgs) {
     abort();
   }
@@ -136,7 +221,7 @@
   return true;
 }
 
-static bool GetConfig(const Span<const uint8_t> args[]) {
+static bool GetConfig(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   static constexpr char kConfig[] =
       R"([
       {
@@ -221,6 +306,21 @@
         "ivGen": "external"
       },
       {
+        "algorithm": "ACVP-AES-GMAC",
+        "revision": "1.0",
+        "direction": ["encrypt", "decrypt"],
+        "keyLen": [128, 192, 256],
+        "payloadLen": [{
+          "min": 0, "max": 256, "increment": 8
+        }],
+        "aadLen": [{
+          "min": 0, "max": 320, "increment": 8
+        }],
+        "tagLen": [32, 64, 96, 104, 112, 120, 128],
+        "ivLen": [96],
+        "ivGen": "external"
+      },
+      {
         "algorithm": "ACVP-AES-KW",
         "revision": "1.0",
         "direction": [
@@ -402,6 +502,7 @@
             "P-521"
           ],
           "hashAlg": [
+            "SHA-1",
             "SHA2-224",
             "SHA2-256",
             "SHA2-384",
@@ -676,9 +777,10 @@
       },
       {
         "algorithm": "CMAC-AES",
+        "acvptoolTestOnly": true,
         "revision": "1.0",
         "capabilities": [{
-          "direction": ["gen"],
+          "direction": ["gen", "ver"],
           "msgLen": [{
             "min": 0,
             "max": 65536,
@@ -715,6 +817,12 @@
               "initiator",
               "responder"
             ]
+          },
+          "staticUnified": {
+            "kasRole": [
+              "initiator",
+              "responder"
+            ]
           }
         },
         "domainParameterGenerationMethods": [
@@ -740,35 +848,57 @@
         ]
       }
     ])";
-  return WriteReply(
-      STDOUT_FILENO,
-      Span<const uint8_t>(reinterpret_cast<const uint8_t *>(kConfig),
-                          sizeof(kConfig) - 1));
+  return write_reply({Span<const uint8_t>(
+      reinterpret_cast<const uint8_t *>(kConfig), sizeof(kConfig) - 1)});
 }
 
 template <uint8_t *(*OneShotHash)(const uint8_t *, size_t, uint8_t *),
           size_t DigestLength>
-static bool Hash(const Span<const uint8_t> args[]) {
+static bool Hash(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   uint8_t digest[DigestLength];
   OneShotHash(args[0].data(), args[0].size(), digest);
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(digest));
+  return write_reply({Span<const uint8_t>(digest)});
+}
+
+template <uint8_t *(*OneShotHash)(const uint8_t *, size_t, uint8_t *),
+          size_t DigestLength>
+static bool HashMCT(const Span<const uint8_t> args[],
+                    ReplyCallback write_reply) {
+  if (args[0].size() != DigestLength) {
+    return false;
+  }
+
+  uint8_t buf[DigestLength * 3];
+  memcpy(buf, args[0].data(), DigestLength);
+  memcpy(buf + DigestLength, args[0].data(), DigestLength);
+  memcpy(buf + 2 * DigestLength, args[0].data(), DigestLength);
+
+  for (size_t i = 0; i < 1000; i++) {
+    uint8_t digest[DigestLength];
+    OneShotHash(buf, sizeof(buf), digest);
+    memmove(buf, buf + DigestLength, DigestLength * 2);
+    memcpy(buf + DigestLength * 2, digest, DigestLength);
+  }
+
+  return write_reply(
+      {Span<const uint8_t>(buf + 2 * DigestLength, DigestLength)});
 }
 
 static uint32_t GetIterations(const Span<const uint8_t> iterations_bytes) {
   uint32_t iterations;
   if (iterations_bytes.size() != sizeof(iterations)) {
-    fprintf(stderr,
-            "Expected %u-byte input for number of iterations, but found %u "
-            "bytes.\n",
-            static_cast<unsigned>(sizeof(iterations)),
-            static_cast<unsigned>(iterations_bytes.size()));
+    LOG_ERROR(
+        "Expected %u-byte input for number of iterations, but found %u "
+        "bytes.\n",
+        static_cast<unsigned>(sizeof(iterations)),
+        static_cast<unsigned>(iterations_bytes.size()));
     abort();
   }
 
   memcpy(&iterations, iterations_bytes.data(), sizeof(iterations));
   if (iterations == 0 || iterations == UINT32_MAX) {
-    fprintf(stderr, "Invalid number of iterations: %x.\n",
-            static_cast<unsigned>(iterations));
+    LOG_ERROR("Invalid number of iterations: %x.\n",
+         static_cast<unsigned>(iterations));
     abort();
   }
 
@@ -777,7 +907,7 @@
 
 template <int (*SetKey)(const uint8_t *key, unsigned bits, AES_KEY *out),
           void (*Block)(const uint8_t *in, uint8_t *out, const AES_KEY *key)>
-static bool AES(const Span<const uint8_t> args[]) {
+static bool AES(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   AES_KEY key;
   if (SetKey(args[0].data(), args[0].size() * 8, &key) != 0) {
     return false;
@@ -799,13 +929,13 @@
     }
   }
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(result),
-                    Span<const uint8_t>(prev_result));
+  return write_reply(
+      {Span<const uint8_t>(result), Span<const uint8_t>(prev_result)});
 }
 
 template <int (*SetKey)(const uint8_t *key, unsigned bits, AES_KEY *out),
           int Direction>
-static bool AES_CBC(const Span<const uint8_t> args[]) {
+static bool AES_CBC(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   AES_KEY key;
   if (SetKey(args[0].data(), args[0].size() * 8, &key) != 0) {
     return false;
@@ -848,15 +978,15 @@
     }
   }
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(result),
-                    Span<const uint8_t>(prev_result));
+  return write_reply(
+      {Span<const uint8_t>(result), Span<const uint8_t>(prev_result)});
 }
 
-static bool AES_CTR(const Span<const uint8_t> args[]) {
+static bool AES_CTR(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   static const uint32_t kOneIteration = 1;
   if (args[3].size() != sizeof(kOneIteration) ||
       memcmp(args[3].data(), &kOneIteration, sizeof(kOneIteration))) {
-    fprintf(stderr, "Only a single iteration supported with AES-CTR\n");
+    LOG_ERROR("Only a single iteration supported with AES-CTR\n");
     return false;
   }
 
@@ -870,7 +1000,7 @@
   uint8_t iv[AES_BLOCK_SIZE];
   memcpy(iv, args[2].data(), AES_BLOCK_SIZE);
   if (GetIterations(args[3]) != 1) {
-    fprintf(stderr, "Multiple iterations of AES-CTR is not supported.\n");
+    LOG_ERROR("Multiple iterations of AES-CTR is not supported.\n");
     return false;
   }
 
@@ -880,15 +1010,15 @@
   uint8_t ecount_buf[AES_BLOCK_SIZE];
   AES_ctr128_encrypt(args[1].data(), out.data(), args[1].size(), &key, iv,
                      ecount_buf, &num);
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
+  return write_reply({Span<const uint8_t>(out)});
 }
 
 static bool AESGCMSetup(EVP_AEAD_CTX *ctx, Span<const uint8_t> tag_len_span,
                         Span<const uint8_t> key) {
   uint32_t tag_len_32;
   if (tag_len_span.size() != sizeof(tag_len_32)) {
-    fprintf(stderr, "Tag size value is %u bytes, not an uint32_t\n",
-            static_cast<unsigned>(tag_len_span.size()));
+    LOG_ERROR("Tag size value is %u bytes, not an uint32_t\n",
+              static_cast<unsigned>(tag_len_span.size()));
     return false;
   }
   memcpy(&tag_len_32, tag_len_span.data(), sizeof(tag_len_32));
@@ -905,15 +1035,14 @@
       aead = EVP_aead_aes_256_gcm();
       break;
     default:
-      fprintf(stderr, "Bad AES-GCM key length %u\n",
-              static_cast<unsigned>(key.size()));
+      LOG_ERROR("Bad AES-GCM key length %u\n", static_cast<unsigned>(key.size()));
       return false;
   }
 
   if (!EVP_AEAD_CTX_init(ctx, aead, key.data(), key.size(), tag_len_32,
                          nullptr)) {
-    fprintf(stderr, "Failed to setup AES-GCM with tag length %u\n",
-            static_cast<unsigned>(tag_len_32));
+    LOG_ERROR("Failed to setup AES-GCM with tag length %u\n",
+              static_cast<unsigned>(tag_len_32));
     return false;
   }
 
@@ -924,28 +1053,27 @@
                         Span<const uint8_t> key) {
   uint32_t tag_len_32;
   if (tag_len_span.size() != sizeof(tag_len_32)) {
-    fprintf(stderr, "Tag size value is %u bytes, not an uint32_t\n",
-            static_cast<unsigned>(tag_len_span.size()));
+    LOG_ERROR("Tag size value is %u bytes, not an uint32_t\n",
+              static_cast<unsigned>(tag_len_span.size()));
     return false;
   }
   memcpy(&tag_len_32, tag_len_span.data(), sizeof(tag_len_32));
   if (tag_len_32 != 4) {
-    fprintf(stderr, "AES-CCM only supports 4-byte tags, but %u was requested\n",
-            static_cast<unsigned>(tag_len_32));
+    LOG_ERROR("AES-CCM only supports 4-byte tags, but %u was requested\n",
+              static_cast<unsigned>(tag_len_32));
     return false;
   }
 
   if (key.size() != 16) {
-    fprintf(stderr,
-            "AES-CCM only supports 128-bit keys, but %u bits were given\n",
-            static_cast<unsigned>(key.size() * 8));
+    LOG_ERROR("AES-CCM only supports 128-bit keys, but %u bits were given\n",
+              static_cast<unsigned>(key.size() * 8));
     return false;
   }
 
   if (!EVP_AEAD_CTX_init(ctx, EVP_aead_aes_128_ccm_bluetooth(), key.data(),
                          key.size(), tag_len_32, nullptr)) {
-    fprintf(stderr, "Failed to setup AES-CCM with tag length %u\n",
-            static_cast<unsigned>(tag_len_32));
+    LOG_ERROR("Failed to setup AES-CCM with tag length %u\n",
+              static_cast<unsigned>(tag_len_32));
     return false;
   }
 
@@ -954,7 +1082,7 @@
 
 template <bool (*SetupFunc)(EVP_AEAD_CTX *ctx, Span<const uint8_t> tag_len_span,
                             Span<const uint8_t> key)>
-static bool AEADSeal(const Span<const uint8_t> args[]) {
+static bool AEADSeal(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   Span<const uint8_t> tag_len_span = args[0];
   Span<const uint8_t> key = args[1];
   Span<const uint8_t> plaintext = args[2];
@@ -979,12 +1107,12 @@
   }
 
   out.resize(out_len);
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
+  return write_reply({Span<const uint8_t>(out)});
 }
 
 template <bool (*SetupFunc)(EVP_AEAD_CTX *ctx, Span<const uint8_t> tag_len_span,
                             Span<const uint8_t> key)>
-static bool AEADOpen(const Span<const uint8_t> args[]) {
+static bool AEADOpen(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   Span<const uint8_t> tag_len_span = args[0];
   Span<const uint8_t> key = args[1];
   Span<const uint8_t> ciphertext = args[2];
@@ -1003,22 +1131,22 @@
   if (!EVP_AEAD_CTX_open(ctx.get(), out.data(), &out_len, out.size(),
                          nonce.data(), nonce.size(), ciphertext.data(),
                          ciphertext.size(), ad.data(), ad.size())) {
-    return WriteReply(STDOUT_FILENO, Span<const uint8_t>(success_flag),
-                      Span<const uint8_t>());
+    return write_reply(
+        {Span<const uint8_t>(success_flag), Span<const uint8_t>()});
   }
 
   out.resize(out_len);
   success_flag[0] = 1;
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(success_flag),
-                    Span<const uint8_t>(out));
+  return write_reply(
+      {Span<const uint8_t>(success_flag), Span<const uint8_t>(out)});
 }
 
 static bool AESPaddedKeyWrapSetup(AES_KEY *out, bool decrypt,
                                   Span<const uint8_t> key) {
   if ((decrypt ? AES_set_decrypt_key : AES_set_encrypt_key)(
           key.data(), key.size() * 8, out) != 0) {
-    fprintf(stderr, "Invalid AES key length for AES-KW(P): %u\n",
-            static_cast<unsigned>(key.size()));
+    LOG_ERROR("Invalid AES key length for AES-KW(P): %u\n",
+              static_cast<unsigned>(key.size()));
     return false;
   }
   return true;
@@ -1031,15 +1159,15 @@
   }
 
   if (input.size() % 8) {
-    fprintf(stderr, "Invalid AES-KW input length: %u\n",
-            static_cast<unsigned>(input.size()));
+    LOG_ERROR("Invalid AES-KW input length: %u\n",
+              static_cast<unsigned>(input.size()));
     return false;
   }
 
   return true;
 }
 
-static bool AESKeyWrapSeal(const Span<const uint8_t> args[]) {
+static bool AESKeyWrapSeal(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   Span<const uint8_t> key = args[1];
   Span<const uint8_t> plaintext = args[2];
 
@@ -1052,21 +1180,20 @@
   std::vector<uint8_t> out(plaintext.size() + 8);
   if (AES_wrap_key(&aes, /*iv=*/nullptr, out.data(), plaintext.data(),
                    plaintext.size()) != static_cast<int>(out.size())) {
-    fprintf(stderr, "AES-KW failed\n");
+    LOG_ERROR("AES-KW failed\n");
     return false;
   }
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
+  return write_reply({Span<const uint8_t>(out)});
 }
 
-static bool AESKeyWrapOpen(const Span<const uint8_t> args[]) {
+static bool AESKeyWrapOpen(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   Span<const uint8_t> key = args[1];
   Span<const uint8_t> ciphertext = args[2];
 
   AES_KEY aes;
   if (!AESKeyWrapSetup(&aes, /*decrypt=*/true, key, ciphertext) ||
-      ciphertext.size() < 8 ||
-      ciphertext.size() > INT_MAX) {
+      ciphertext.size() < 8 || ciphertext.size() > INT_MAX) {
     return false;
   }
 
@@ -1074,16 +1201,16 @@
   uint8_t success_flag[1] = {0};
   if (AES_unwrap_key(&aes, /*iv=*/nullptr, out.data(), ciphertext.data(),
                      ciphertext.size()) != static_cast<int>(out.size())) {
-    return WriteReply(STDOUT_FILENO, Span<const uint8_t>(success_flag),
-                      Span<const uint8_t>());
+    return write_reply(
+        {Span<const uint8_t>(success_flag), Span<const uint8_t>()});
   }
 
   success_flag[0] = 1;
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(success_flag),
-                    Span<const uint8_t>(out));
+  return write_reply(
+      {Span<const uint8_t>(success_flag), Span<const uint8_t>(out)});
 }
 
-static bool AESPaddedKeyWrapSeal(const Span<const uint8_t> args[]) {
+static bool AESPaddedKeyWrapSeal(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   Span<const uint8_t> key = args[1];
   Span<const uint8_t> plaintext = args[2];
 
@@ -1097,15 +1224,15 @@
   size_t out_len;
   if (!AES_wrap_key_padded(&aes, out.data(), &out_len, out.size(),
                            plaintext.data(), plaintext.size())) {
-    fprintf(stderr, "AES-KWP failed\n");
+    LOG_ERROR("AES-KWP failed\n");
     return false;
   }
 
   out.resize(out_len);
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
+  return write_reply({Span<const uint8_t>(out)});
 }
 
-static bool AESPaddedKeyWrapOpen(const Span<const uint8_t> args[]) {
+static bool AESPaddedKeyWrapOpen(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   Span<const uint8_t> key = args[1];
   Span<const uint8_t> ciphertext = args[2];
 
@@ -1120,23 +1247,23 @@
   uint8_t success_flag[1] = {0};
   if (!AES_unwrap_key_padded(&aes, out.data(), &out_len, out.size(),
                              ciphertext.data(), ciphertext.size())) {
-    return WriteReply(STDOUT_FILENO, Span<const uint8_t>(success_flag),
-                      Span<const uint8_t>());
+    return write_reply(
+        {Span<const uint8_t>(success_flag), Span<const uint8_t>()});
   }
 
   success_flag[0] = 1;
   out.resize(out_len);
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(success_flag),
-                    Span<const uint8_t>(out));
+  return write_reply(
+      {Span<const uint8_t>(success_flag), Span<const uint8_t>(out)});
 }
 
 template <bool Encrypt>
-static bool TDES(const Span<const uint8_t> args[]) {
+static bool TDES(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   const EVP_CIPHER *cipher = EVP_des_ede3();
 
   if (args[0].size() != 24) {
-    fprintf(stderr, "Bad key length %u for 3DES.\n",
-            static_cast<unsigned>(args[0].size()));
+    LOG_ERROR("Bad key length %u for 3DES.\n",
+              static_cast<unsigned>(args[0].size()));
     return false;
   }
   bssl::ScopedEVP_CIPHER_CTX ctx;
@@ -1147,8 +1274,8 @@
   }
 
   if (args[1].size() % 8) {
-    fprintf(stderr, "Bad input length %u for 3DES.\n",
-            static_cast<unsigned>(args[1].size()));
+    LOG_ERROR("Bad input length %u for 3DES.\n",
+              static_cast<unsigned>(args[1].size()));
     return false;
   }
   std::vector<uint8_t> result(args[1].begin(), args[1].end());
@@ -1171,31 +1298,31 @@
     }
   }
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(result),
-                    Span<const uint8_t>(prev_result),
-                    Span<const uint8_t>(prev_prev_result));
+  return write_reply({Span<const uint8_t>(result),
+                      Span<const uint8_t>(prev_result),
+                      Span<const uint8_t>(prev_prev_result)});
 }
 
 template <bool Encrypt>
-static bool TDES_CBC(const Span<const uint8_t> args[]) {
+static bool TDES_CBC(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   const EVP_CIPHER *cipher = EVP_des_ede3_cbc();
 
   if (args[0].size() != 24) {
-    fprintf(stderr, "Bad key length %u for 3DES.\n",
-            static_cast<unsigned>(args[0].size()));
+    LOG_ERROR("Bad key length %u for 3DES.\n",
+              static_cast<unsigned>(args[0].size()));
     return false;
   }
 
   if (args[1].size() % 8 || args[1].size() == 0) {
-    fprintf(stderr, "Bad input length %u for 3DES.\n",
-            static_cast<unsigned>(args[1].size()));
+    LOG_ERROR("Bad input length %u for 3DES.\n",
+              static_cast<unsigned>(args[1].size()));
     return false;
   }
   std::vector<uint8_t> input(args[1].begin(), args[1].end());
 
   if (args[2].size() != EVP_CIPHER_iv_length(cipher)) {
-    fprintf(stderr, "Bad IV length %u for 3DES.\n",
-            static_cast<unsigned>(args[2].size()));
+    LOG_ERROR("Bad IV length %u for 3DES.\n",
+              static_cast<unsigned>(args[2].size()));
     return false;
   }
   std::vector<uint8_t> iv(args[2].begin(), args[2].end());
@@ -1237,13 +1364,13 @@
     }
   }
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(result),
-                    Span<const uint8_t>(prev_result),
-                    Span<const uint8_t>(prev_prev_result));
+  return write_reply({Span<const uint8_t>(result),
+                     Span<const uint8_t>(prev_result),
+                     Span<const uint8_t>(prev_prev_result)});
 }
 
 template <const EVP_MD *HashFunc()>
-static bool HMAC(const Span<const uint8_t> args[]) {
+static bool HMAC(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   const EVP_MD *const md = HashFunc();
   uint8_t digest[EVP_MAX_MD_SIZE];
   unsigned digest_len;
@@ -1251,10 +1378,10 @@
              digest, &digest_len) == nullptr) {
     return false;
   }
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(digest, digest_len));
+  return write_reply({Span<const uint8_t>(digest, digest_len)});
 }
 
-static bool DRBG(const Span<const uint8_t> args[]) {
+static bool DRBG(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   const auto out_len_bytes = args[0];
   const auto entropy = args[1];
   const auto personalisation = args[2];
@@ -1285,7 +1412,7 @@
     return false;
   }
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(out));
+  return write_reply({Span<const uint8_t>(out)});
 }
 
 static bool StringEq(Span<const uint8_t> a, const char *b) {
@@ -1333,7 +1460,7 @@
   return std::make_pair(std::move(x_bytes), std::move(y_bytes));
 }
 
-static bool ECDSAKeyGen(const Span<const uint8_t> args[]) {
+static bool ECDSAKeyGen(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   bssl::UniquePtr<EC_KEY> key = ECKeyFromName(args[0]);
   if (!key || !EC_KEY_generate_key_fips(key.get())) {
     return false;
@@ -1343,9 +1470,9 @@
   std::vector<uint8_t> d_bytes =
       BIGNUMBytes(EC_KEY_get0_private_key(key.get()));
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(d_bytes),
-                    Span<const uint8_t>(pub_key.first),
-                    Span<const uint8_t>(pub_key.second));
+  return write_reply({Span<const uint8_t>(d_bytes),
+                      Span<const uint8_t>(pub_key.first),
+                      Span<const uint8_t>(pub_key.second)});
 }
 
 static bssl::UniquePtr<BIGNUM> BytesToBIGNUM(Span<const uint8_t> bytes) {
@@ -1354,7 +1481,7 @@
   return bn;
 }
 
-static bool ECDSAKeyVer(const Span<const uint8_t> args[]) {
+static bool ECDSAKeyVer(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   bssl::UniquePtr<EC_KEY> key = ECKeyFromName(args[0]);
   if (!key) {
     return false;
@@ -1375,11 +1502,13 @@
     reply[0] = 1;
   }
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(reply));
+  return write_reply({Span<const uint8_t>(reply)});
 }
 
 static const EVP_MD *HashFromName(Span<const uint8_t> name) {
-  if (StringEq(name, "SHA2-224")) {
+  if (StringEq(name, "SHA-1")) {
+    return EVP_sha1();
+  } else if (StringEq(name, "SHA2-224")) {
     return EVP_sha224();
   } else if (StringEq(name, "SHA2-256")) {
     return EVP_sha256();
@@ -1392,7 +1521,7 @@
   }
 }
 
-static bool ECDSASigGen(const Span<const uint8_t> args[]) {
+static bool ECDSASigGen(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   bssl::UniquePtr<EC_KEY> key = ECKeyFromName(args[0]);
   bssl::UniquePtr<BIGNUM> d = BytesToBIGNUM(args[1]);
   const EVP_MD *hash = HashFromName(args[2]);
@@ -1413,11 +1542,11 @@
   std::vector<uint8_t> r_bytes(BIGNUMBytes(sig->r));
   std::vector<uint8_t> s_bytes(BIGNUMBytes(sig->s));
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(r_bytes),
-                    Span<const uint8_t>(s_bytes));
+  return write_reply(
+      {Span<const uint8_t>(r_bytes), Span<const uint8_t>(s_bytes)});
 }
 
-static bool ECDSASigVer(const Span<const uint8_t> args[]) {
+static bool ECDSASigVer(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   bssl::UniquePtr<EC_KEY> key = ECKeyFromName(args[0]);
   const EVP_MD *hash = HashFromName(args[1]);
   auto msg = args[2];
@@ -1450,10 +1579,10 @@
     reply[0] = 1;
   }
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(reply));
+  return write_reply({Span<const uint8_t>(reply)});
 }
 
-static bool CMAC_AES(const Span<const uint8_t> args[]) {
+static bool CMAC_AES(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   uint8_t mac[16];
   if (!AES_CMAC(mac, args[1].data(), args[1].size(), args[2].data(),
                 args[2].size())) {
@@ -1469,10 +1598,10 @@
     return false;
   }
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(mac, mac_len));
+  return write_reply({Span<const uint8_t>(mac, mac_len)});
 }
 
-static bool CMAC_AESVerify(const Span<const uint8_t> args[]) {
+static bool CMAC_AESVerify(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   // This function is just for testing since libcrypto doesn't do the
   // verification itself. The regcap doesn't advertise "ver" support.
   uint8_t mac[16];
@@ -1482,8 +1611,8 @@
     return false;
   }
 
-  const uint8_t ok = OPENSSL_memcmp(mac, args[2].data(), args[2].size());
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(&ok, sizeof(ok)));
+  const uint8_t ok = (OPENSSL_memcmp(mac, args[2].data(), args[2].size()) == 0);
+  return write_reply({Span<const uint8_t>(&ok, sizeof(ok))});
 }
 
 static std::map<unsigned, bssl::UniquePtr<RSA>>& CachedRSAKeys() {
@@ -1508,7 +1637,7 @@
   return ret;
 }
 
-static bool RSAKeyGen(const Span<const uint8_t> args[]) {
+static bool RSAKeyGen(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   uint32_t bits;
   if (args[0].size() != sizeof(bits)) {
     return false;
@@ -1517,8 +1646,7 @@
 
   bssl::UniquePtr<RSA> key(RSA_new());
   if (!RSA_generate_key_fips(key.get(), bits, nullptr)) {
-    fprintf(stderr, "RSA_generate_key_fips failed for modulus length %u.\n",
-            bits);
+    LOG_ERROR("RSA_generate_key_fips failed for modulus length %u.\n", bits);
     return false;
   }
 
@@ -1526,8 +1654,8 @@
   RSA_get0_key(key.get(), &n, &e, &d);
   RSA_get0_factors(key.get(), &p, &q);
 
-  if (!WriteReply(STDOUT_FILENO, BIGNUMBytes(e), BIGNUMBytes(p), BIGNUMBytes(q),
-                  BIGNUMBytes(n), BIGNUMBytes(d))) {
+  if (!write_reply({BIGNUMBytes(e), BIGNUMBytes(p), BIGNUMBytes(q),
+                    BIGNUMBytes(n), BIGNUMBytes(d)})) {
     return false;
   }
 
@@ -1535,8 +1663,8 @@
   return true;
 }
 
-template<const EVP_MD *(MDFunc)(), bool UsePSS>
-static bool RSASigGen(const Span<const uint8_t> args[]) {
+template <const EVP_MD *(MDFunc)(), bool UsePSS>
+static bool RSASigGen(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   uint32_t bits;
   if (args[0].size() != sizeof(bits)) {
     return false;
@@ -1570,12 +1698,12 @@
 
   sig.resize(sig_len);
 
-  return WriteReply(STDOUT_FILENO, BIGNUMBytes(RSA_get0_n(key)),
-                    BIGNUMBytes(RSA_get0_e(key)), sig);
+  return write_reply(
+      {BIGNUMBytes(RSA_get0_n(key)), BIGNUMBytes(RSA_get0_e(key)), sig});
 }
 
-template<const EVP_MD *(MDFunc)(), bool UsePSS>
-static bool RSASigVer(const Span<const uint8_t> args[]) {
+template <const EVP_MD *(MDFunc)(), bool UsePSS>
+static bool RSASigVer(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   const Span<const uint8_t> n_bytes = args[0];
   const Span<const uint8_t> e_bytes = args[1];
   const Span<const uint8_t> msg = args[2];
@@ -1607,11 +1735,11 @@
   }
   ERR_clear_error();
 
-  return WriteReply(STDOUT_FILENO, Span<const uint8_t>(&ok, 1));
+  return write_reply({Span<const uint8_t>(&ok, 1)});
 }
 
-template<const EVP_MD *(MDFunc)()>
-static bool TLSKDF(const Span<const uint8_t> args[]) {
+template <const EVP_MD *(MDFunc)()>
+static bool TLSKDF(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   const Span<const uint8_t> out_len_bytes = args[0];
   const Span<const uint8_t> secret = args[1];
   const Span<const uint8_t> label = args[2];
@@ -1633,11 +1761,11 @@
     return 0;
   }
 
-  return WriteReply(STDOUT_FILENO, out);
+  return write_reply({out});
 }
 
 template <int Nid>
-static bool ECDH(const Span<const uint8_t> args[]) {
+static bool ECDH(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   bssl::UniquePtr<BIGNUM> their_x(BytesToBIGNUM(args[0]));
   bssl::UniquePtr<BIGNUM> their_y(BytesToBIGNUM(args[1]));
   const Span<const uint8_t> private_key = args[2];
@@ -1649,14 +1777,14 @@
   bssl::UniquePtr<EC_POINT> their_point(EC_POINT_new(group));
   if (!EC_POINT_set_affine_coordinates_GFp(
           group, their_point.get(), their_x.get(), their_y.get(), ctx.get())) {
-    fprintf(stderr, "Invalid peer point for ECDH.\n");
+    LOG_ERROR("Invalid peer point for ECDH.\n");
     return false;
   }
 
   if (!private_key.empty()) {
     bssl::UniquePtr<BIGNUM> our_k(BytesToBIGNUM(private_key));
     if (!EC_KEY_set_private_key(ec_key.get(), our_k.get())) {
-      fprintf(stderr, "EC_KEY_set_private_key failed.\n");
+      LOG_ERROR("EC_KEY_set_private_key failed.\n");
       return false;
     }
 
@@ -1664,11 +1792,11 @@
     if (!EC_POINT_mul(group, our_pub.get(), our_k.get(), nullptr, nullptr,
                       ctx.get()) ||
         !EC_KEY_set_public_key(ec_key.get(), our_pub.get())) {
-      fprintf(stderr, "Calculating public key failed.\n");
+      LOG_ERROR("Calculating public key failed.\n");
       return false;
     }
   } else if (!EC_KEY_generate_key_fips(ec_key.get())) {
-    fprintf(stderr, "EC_KEY_generate_key_fips failed.\n");
+    LOG_ERROR("EC_KEY_generate_key_fips failed.\n");
     return false;
   }
 
@@ -1679,10 +1807,10 @@
       ECDH_compute_key(output.data(), output.size(), their_point.get(),
                        ec_key.get(), /*kdf=*/nullptr);
   if (out_len < 0) {
-    fprintf(stderr, "ECDH_compute_key failed.\n");
+    LOG_ERROR("ECDH_compute_key failed.\n");
     return false;
   } else if (static_cast<size_t>(out_len) == output.size()) {
-    fprintf(stderr, "ECDH_compute_key output may have been truncated.\n");
+    LOG_ERROR("ECDH_compute_key output may have been truncated.\n");
     return false;
   }
   output.resize(static_cast<size_t>(out_len));
@@ -1692,15 +1820,14 @@
   bssl::UniquePtr<BIGNUM> y(BN_new());
   if (!EC_POINT_get_affine_coordinates_GFp(group, pub, x.get(), y.get(),
                                            ctx.get())) {
-    fprintf(stderr, "EC_POINT_get_affine_coordinates_GFp failed.\n");
+    LOG_ERROR("EC_POINT_get_affine_coordinates_GFp failed.\n");
     return false;
   }
 
-  return WriteReply(STDOUT_FILENO, BIGNUMBytes(x.get()), BIGNUMBytes(y.get()),
-                    output);
+  return write_reply({BIGNUMBytes(x.get()), BIGNUMBytes(y.get()), output});
 }
 
-static bool FFDH(const Span<const uint8_t> args[]) {
+static bool FFDH(const Span<const uint8_t> args[], ReplyCallback write_reply) {
   bssl::UniquePtr<BIGNUM> p(BytesToBIGNUM(args[0]));
   bssl::UniquePtr<BIGNUM> q(BytesToBIGNUM(args[1]));
   bssl::UniquePtr<BIGNUM> g(BytesToBIGNUM(args[2]));
@@ -1710,7 +1837,7 @@
 
   bssl::UniquePtr<DH> dh(DH_new());
   if (!DH_set0_pqg(dh.get(), p.get(), q.get(), g.get())) {
-    fprintf(stderr, "DH_set0_pqg failed.\n");
+    LOG_ERROR("DH_set0_pqg failed.\n");
     return 0;
   }
 
@@ -1724,7 +1851,7 @@
     bssl::UniquePtr<BIGNUM> public_key(BytesToBIGNUM(public_key_span));
 
     if (!DH_set0_key(dh.get(), public_key.get(), private_key.get())) {
-      fprintf(stderr, "DH_set0_key failed.\n");
+      LOG_ERROR("DH_set0_key failed.\n");
       return 0;
     }
 
@@ -1732,24 +1859,24 @@
     public_key.release();
     private_key.release();
   } else if (!DH_generate_key(dh.get())) {
-    fprintf(stderr, "DH_generate_key failed.\n");
+    LOG_ERROR("DH_generate_key failed.\n");
     return false;
   }
 
   std::vector<uint8_t> z(DH_size(dh.get()));
   if (DH_compute_key_padded(z.data(), their_pub.get(), dh.get()) !=
       static_cast<int>(z.size())) {
-    fprintf(stderr, "DH_compute_key_hashed failed.\n");
+    LOG_ERROR("DH_compute_key_hashed failed.\n");
     return false;
   }
 
-  return WriteReply(STDOUT_FILENO, BIGNUMBytes(DH_get0_pub_key(dh.get())), z);
+  return write_reply({BIGNUMBytes(DH_get0_pub_key(dh.get())), z});
 }
 
 static constexpr struct {
-  const char name[kMaxNameLength + 1];
-  uint8_t expected_args;
-  bool (*handler)(const Span<const uint8_t>[]);
+  char name[kMaxNameLength + 1];
+  uint8_t num_expected_args;
+  bool (*handler)(const Span<const uint8_t> args[], ReplyCallback write_reply);
 } kFunctions[] = {
     {"getConfig", 0, GetConfig},
     {"SHA-1", 1, Hash<SHA1, SHA_DIGEST_LENGTH>},
@@ -1758,6 +1885,12 @@
     {"SHA2-384", 1, Hash<SHA384, SHA384_DIGEST_LENGTH>},
     {"SHA2-512", 1, Hash<SHA512, SHA512_DIGEST_LENGTH>},
     {"SHA2-512/256", 1, Hash<SHA512_256, SHA512_256_DIGEST_LENGTH>},
+    {"SHA-1/MCT", 1, HashMCT<SHA1, SHA_DIGEST_LENGTH>},
+    {"SHA2-224/MCT", 1, HashMCT<SHA224, SHA224_DIGEST_LENGTH>},
+    {"SHA2-256/MCT", 1, HashMCT<SHA256, SHA256_DIGEST_LENGTH>},
+    {"SHA2-384/MCT", 1, HashMCT<SHA384, SHA384_DIGEST_LENGTH>},
+    {"SHA2-512/MCT", 1, HashMCT<SHA512, SHA512_DIGEST_LENGTH>},
+    {"SHA2-512/256/MCT", 1, HashMCT<SHA512_256, SHA512_256_DIGEST_LENGTH>},
     {"AES/encrypt", 3, AES<AES_set_encrypt_key, AES_encrypt>},
     {"AES/decrypt", 3, AES<AES_set_decrypt_key, AES_decrypt>},
     {"AES-CBC/encrypt", 4, AES_CBC<AES_set_encrypt_key, AES_ENCRYPT>},
@@ -1820,98 +1953,26 @@
     {"FFDH", 6, FFDH},
 };
 
-int main() {
-  uint32_t nums[1 + kMaxArgs];
-  std::unique_ptr<uint8_t[]> buf;
-  size_t buf_len = 0;
-  Span<const uint8_t> args[kMaxArgs];
-
-  for (;;) {
-    if (!ReadAll(STDIN_FILENO, nums, sizeof(uint32_t) * 2)) {
-      return 1;
-    }
-
-    const size_t num_args = nums[0];
-    if (num_args == 0) {
-      fprintf(stderr, "Invalid, zero-argument operation requested.\n");
-      return 2;
-    } else if (num_args > kMaxArgs) {
-      fprintf(stderr,
-              "Operation requested with %zu args, but %zu is the limit.\n",
-              num_args, kMaxArgs);
-      return 2;
-    }
-
-    if (num_args > 1 &&
-        !ReadAll(STDIN_FILENO, &nums[2], sizeof(uint32_t) * (num_args - 1))) {
-      return 1;
-    }
-
-    size_t need = 0;
-    for (size_t i = 0; i < num_args; i++) {
-      const size_t arg_length = nums[i + 1];
-      if (i == 0 && arg_length > kMaxNameLength) {
-        fprintf(stderr,
-                "Operation with name of length %zu exceeded limit of %zu.\n",
-                arg_length, kMaxNameLength);
-        return 2;
-      } else if (arg_length > kMaxArgLength) {
-        fprintf(
-            stderr,
-            "Operation with argument of length %zu exceeded limit of %zu.\n",
-            arg_length, kMaxArgLength);
-        return 2;
+Handler FindHandler(Span<const Span<const uint8_t>> args) {
+  const bssl::Span<const uint8_t> algorithm = args[0];
+  for (const auto &func : kFunctions) {
+    if (algorithm.size() == strlen(func.name) &&
+        memcmp(algorithm.data(), func.name, algorithm.size()) == 0) {
+      if (args.size() - 1 != func.num_expected_args) {
+        LOG_ERROR("\'%s\' operation received %zu arguments but expected %u.\n",
+                  func.name, args.size() - 1, func.num_expected_args);
+        return nullptr;
       }
 
-      // static_assert around kMaxArgs etc enforces that this doesn't overflow.
-      need += arg_length;
-    }
-
-    if (need > buf_len) {
-      size_t alloced = need + (need >> 1);
-      if (alloced < need) {
-        abort();
-      }
-      buf.reset(new uint8_t[alloced]);
-      buf_len = alloced;
-    }
-
-    if (!ReadAll(STDIN_FILENO, buf.get(), need)) {
-      return 1;
-    }
-
-    size_t offset = 0;
-    for (size_t i = 0; i < num_args; i++) {
-      args[i] = Span<const uint8_t>(&buf[offset], nums[i + 1]);
-      offset += nums[i + 1];
-    }
-
-    bool found = false;
-    for (const auto &func : kFunctions) {
-      if (args[0].size() == strlen(func.name) &&
-          memcmp(args[0].data(), func.name, args[0].size()) == 0) {
-        if (num_args - 1 != func.expected_args) {
-          fprintf(stderr,
-                  "\'%s\' operation received %zu arguments but expected %u.\n",
-                  func.name, num_args - 1, func.expected_args);
-          return 2;
-        }
-
-        if (!func.handler(&args[1])) {
-          fprintf(stderr, "\'%s\' operation failed.\n", func.name);
-          return 4;
-        }
-
-        found = true;
-        break;
-      }
-    }
-
-    if (!found) {
-      const std::string name(reinterpret_cast<const char *>(args[0].data()),
-                             args[0].size());
-      fprintf(stderr, "Unknown operation: %s\n", name.c_str());
-      return 3;
+      return func.handler;
     }
   }
+
+  const std::string name(reinterpret_cast<const char *>(algorithm.data()),
+                         algorithm.size());
+  LOG_ERROR("Unknown operation: %s\n", name.c_str());
+  return nullptr;
 }
+
+}  // namespace acvp
+}  // namespace bssl
diff --git a/deps/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.h b/deps/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.h
new file mode 100644
index 0000000..0472800
--- /dev/null
+++ b/deps/boringssl/src/util/fipstools/acvp/modulewrapper/modulewrapper.h
@@ -0,0 +1,69 @@
+/* Copyright (c) 2021, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/base.h>
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <openssl/span.h>
+
+
+namespace bssl {
+namespace acvp {
+
+// kMaxArgs is the maximum number of arguments (including the function name)
+// that an ACVP request can contain.
+constexpr size_t kMaxArgs = 8;
+// kMaxNameLength is the maximum length of a function name in an ACVP request.
+constexpr size_t kMaxNameLength = 30;
+
+// RequestBuffer holds various buffers needed for parsing an ACVP request. It
+// can be reused between requests.
+class RequestBuffer {
+ public:
+  virtual ~RequestBuffer();
+
+  static std::unique_ptr<RequestBuffer> New();
+};
+
+// ParseArgsFromFd returns a span of arguments, the first of which is the name
+// of the requested function, from |fd|. The return values point into |buffer|
+// and so must not be used after |buffer| has been freed or reused for a
+// subsequent call. It returns an empty span on error, because std::optional
+// is still too new.
+Span<const Span<const uint8_t>> ParseArgsFromFd(int fd, RequestBuffer *buffer);
+
+// WriteReplyToFd writes a reply to the given file descriptor.
+bool WriteReplyToFd(int fd, const std::vector<Span<const uint8_t>> &spans);
+
+// ReplyCallback is the type of a callback that writes a reply to an ACVP
+// request.
+typedef std::function<bool(const std::vector<Span<const uint8_t>> &)>
+    ReplyCallback;
+
+// Handler is the type of a function that handles a specific ACVP request. If
+// successful it will call |write_reply| with the response arguments and return
+// |write_reply|'s return value. Otherwise it will return false. The given args
+// must not include the name at the beginning.
+typedef bool (*Handler)(const Span<const uint8_t> args[],
+                        ReplyCallback write_reply);
+
+// FindHandler returns a |Handler| that can process the given arguments, or logs
+// a reason and returns |nullptr| if none is found.
+Handler FindHandler(Span<const Span<const uint8_t>> args);
+
+}  // namespace acvp
+}  // namespace bssl
diff --git a/deps/boringssl/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S b/deps/boringssl/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S
index 75f7b64..7cba4da 100644
--- a/deps/boringssl/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S
+++ b/deps/boringssl/win-aarch64/crypto/fipsmodule/ghashv8-armx64.S
@@ -15,6 +15,7 @@
 #endif
 #include <openssl/arm_arch.h>
 
+#if __ARM_MAX_ARCH__>=7
 .text
 .arch	armv8-a+crypto
 .globl	gcm_init_v8
@@ -67,8 +68,48 @@
 	ext	v17.16b,v22.16b,v22.16b,#8		//Karatsuba pre-processing
 	eor	v17.16b,v17.16b,v22.16b
 	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
-	st1	{v21.2d,v22.2d},[x0]		//store Htable[1..2]
+	st1	{v21.2d,v22.2d},[x0],#32	//store Htable[1..2]
+	//calculate H^3 and H^4
+	pmull	v0.1q,v20.1d, v22.1d
+	pmull	v5.1q,v22.1d,v22.1d
+	pmull2	v2.1q,v20.2d, v22.2d
+	pmull2	v7.1q,v22.2d,v22.2d
+	pmull	v1.1q,v16.1d,v17.1d
+	pmull	v6.1q,v17.1d,v17.1d
 
+	ext	v16.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	ext	v17.16b,v5.16b,v7.16b,#8
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v16.16b
+	eor	v4.16b,v5.16b,v7.16b
+	eor	v6.16b,v6.16b,v17.16b
+	eor	v1.16b,v1.16b,v18.16b
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase
+	eor	v6.16b,v6.16b,v4.16b
+	pmull	v4.1q,v5.1d,v19.1d
+
+	ins	v2.d[0],v1.d[1]
+	ins	v7.d[0],v6.d[1]
+	ins	v1.d[1],v0.d[0]
+	ins	v6.d[1],v5.d[0]
+	eor	v0.16b,v1.16b,v18.16b
+	eor	v5.16b,v6.16b,v4.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase
+	ext	v4.16b,v5.16b,v5.16b,#8
+	pmull	v0.1q,v0.1d,v19.1d
+	pmull	v5.1q,v5.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v4.16b,v4.16b,v7.16b
+	eor	v20.16b, v0.16b,v18.16b		//H^3
+	eor	v22.16b,v5.16b,v4.16b		//H^4
+
+	ext	v16.16b,v20.16b, v20.16b,#8		//Karatsuba pre-processing
+	ext	v17.16b,v22.16b,v22.16b,#8
+	eor	v16.16b,v16.16b,v20.16b
+	eor	v17.16b,v17.16b,v22.16b
+	ext	v21.16b,v16.16b,v17.16b,#8		//pack Karatsuba pre-processed
+	st1	{v20.2d,v21.2d,v22.2d},[x0]		//store Htable[3..5]
 	ret
 
 .globl	gcm_gmult_v8
@@ -124,6 +165,8 @@
 .align	4
 gcm_ghash_v8:
 	AARCH64_VALID_CALL_TARGET
+	cmp	x3,#64
+	b.hs	Lgcm_ghash_v8_4x
 	ld1	{v0.2d},[x0]		//load [rotated] Xi
 						//"[rotated]" means that
 						//loaded value would have
@@ -250,8 +293,291 @@
 
 	ret
 
+.def gcm_ghash_v8_4x
+   .type 32
+.endef
+.align	4
+gcm_ghash_v8_4x:
+Lgcm_ghash_v8_4x:
+	ld1	{v0.2d},[x0]		//load [rotated] Xi
+	ld1	{v20.2d,v21.2d,v22.2d},[x1],#48	//load twisted H, ..., H^2
+	movi	v19.16b,#0xe1
+	ld1	{v26.2d,v27.2d,v28.2d},[x1]	//load twisted H^3, ..., H^4
+	shl	v19.2d,v19.2d,#57		//compose 0xc2.0 constant
+
+	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
+#ifndef __ARMEB__
+	rev64	v0.16b,v0.16b
+	rev64	v5.16b,v5.16b
+	rev64	v6.16b,v6.16b
+	rev64	v7.16b,v7.16b
+	rev64	v4.16b,v4.16b
+#endif
+	ext	v25.16b,v7.16b,v7.16b,#8
+	ext	v24.16b,v6.16b,v6.16b,#8
+	ext	v23.16b,v5.16b,v5.16b,#8
+
+	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
+	eor	v7.16b,v7.16b,v25.16b
+	pmull2	v31.1q,v20.2d,v25.2d
+	pmull	v30.1q,v21.1d,v7.1d
+
+	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
+	eor	v6.16b,v6.16b,v24.16b
+	pmull2	v24.1q,v22.2d,v24.2d
+	pmull2	v6.1q,v21.2d,v6.2d
+
+	eor	v29.16b,v29.16b,v16.16b
+	eor	v31.16b,v31.16b,v24.16b
+	eor	v30.16b,v30.16b,v6.16b
+
+	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+	pmull2	v23.1q,v26.2d,v23.2d
+	pmull	v5.1q,v27.1d,v5.1d
+
+	eor	v29.16b,v29.16b,v7.16b
+	eor	v31.16b,v31.16b,v23.16b
+	eor	v30.16b,v30.16b,v5.16b
+
+	subs	x3,x3,#128
+	b.lo	Ltail4x
+
+	b	Loop4x
+
+.align	4
+Loop4x:
+	eor	v16.16b,v4.16b,v0.16b
+	ld1	{v4.2d,v5.2d,v6.2d,v7.2d},[x2],#64
+	ext	v3.16b,v16.16b,v16.16b,#8
+#ifndef __ARMEB__
+	rev64	v5.16b,v5.16b
+	rev64	v6.16b,v6.16b
+	rev64	v7.16b,v7.16b
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v28.2d,v3.2d
+	ext	v25.16b,v7.16b,v7.16b,#8
+	pmull2	v1.1q,v27.2d,v16.2d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	ext	v24.16b,v6.16b,v6.16b,#8
+	eor	v1.16b,v1.16b,v30.16b
+	ext	v23.16b,v5.16b,v5.16b,#8
+
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	pmull	v29.1q,v20.1d,v25.1d		//H·Ii+3
+	eor	v7.16b,v7.16b,v25.16b
+	eor	v1.16b,v1.16b,v17.16b
+	pmull2	v31.1q,v20.2d,v25.2d
+	eor	v1.16b,v1.16b,v18.16b
+	pmull	v30.1q,v21.1d,v7.1d
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	pmull	v16.1q,v22.1d,v24.1d		//H^2·Ii+2
+	eor	v6.16b,v6.16b,v24.16b
+	pmull2	v24.1q,v22.2d,v24.2d
+	eor	v0.16b,v1.16b,v18.16b
+	pmull2	v6.1q,v21.2d,v6.2d
+
+	eor	v29.16b,v29.16b,v16.16b
+	eor	v31.16b,v31.16b,v24.16b
+	eor	v30.16b,v30.16b,v6.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	pmull	v7.1q,v26.1d,v23.1d		//H^3·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+	eor	v18.16b,v18.16b,v2.16b
+	pmull2	v23.1q,v26.2d,v23.2d
+	pmull	v5.1q,v27.1d,v5.1d
+
+	eor	v0.16b,v0.16b,v18.16b
+	eor	v29.16b,v29.16b,v7.16b
+	eor	v31.16b,v31.16b,v23.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+	eor	v30.16b,v30.16b,v5.16b
+
+	subs	x3,x3,#64
+	b.hs	Loop4x
+
+Ltail4x:
+	eor	v16.16b,v4.16b,v0.16b
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	pmull	v0.1q,v28.1d,v3.1d		//H^4·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v28.2d,v3.2d
+	pmull2	v1.1q,v27.2d,v16.2d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	eor	v1.16b,v1.16b,v30.16b
+
+	adds	x3,x3,#64
+	b.eq	Ldone4x
+
+	cmp	x3,#32
+	b.lo	Lone
+	b.eq	Ltwo
+Lthree:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	ld1	{v4.2d,v5.2d,v6.2d},[x2]
+	eor	v1.16b,v1.16b,v18.16b
+#ifndef	__ARMEB__
+	rev64	v5.16b,v5.16b
+	rev64	v6.16b,v6.16b
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	ext	v24.16b,v6.16b,v6.16b,#8
+	ext	v23.16b,v5.16b,v5.16b,#8
+	eor	v0.16b,v1.16b,v18.16b
+
+	pmull	v29.1q,v20.1d,v24.1d		//H·Ii+2
+	eor	v6.16b,v6.16b,v24.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	pmull2	v31.1q,v20.2d,v24.2d
+	pmull	v30.1q,v21.1d,v6.1d
+	eor	v0.16b,v0.16b,v18.16b
+	pmull	v7.1q,v22.1d,v23.1d		//H^2·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+	pmull2	v23.1q,v22.2d,v23.2d
+	eor	v16.16b,v4.16b,v0.16b
+	pmull2	v5.1q,v21.2d,v5.2d
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	eor	v29.16b,v29.16b,v7.16b
+	eor	v31.16b,v31.16b,v23.16b
+	eor	v30.16b,v30.16b,v5.16b
+
+	pmull	v0.1q,v26.1d,v3.1d		//H^3·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v26.2d,v3.2d
+	pmull	v1.1q,v27.1d,v16.1d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	eor	v1.16b,v1.16b,v30.16b
+	b	Ldone4x
+
+.align	4
+Ltwo:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	ld1	{v4.2d,v5.2d},[x2]
+	eor	v1.16b,v1.16b,v18.16b
+#ifndef	__ARMEB__
+	rev64	v5.16b,v5.16b
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	ext	v23.16b,v5.16b,v5.16b,#8
+	eor	v0.16b,v1.16b,v18.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v0.16b,v0.16b,v18.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+	pmull	v29.1q,v20.1d,v23.1d		//H·Ii+1
+	eor	v5.16b,v5.16b,v23.16b
+
+	eor	v16.16b,v4.16b,v0.16b
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	pmull2	v31.1q,v20.2d,v23.2d
+	pmull	v30.1q,v21.1d,v5.1d
+
+	pmull	v0.1q,v22.1d,v3.1d		//H^2·(Xi+Ii)
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v22.2d,v3.2d
+	pmull2	v1.1q,v21.2d,v16.2d
+
+	eor	v0.16b,v0.16b,v29.16b
+	eor	v2.16b,v2.16b,v31.16b
+	eor	v1.16b,v1.16b,v30.16b
+	b	Ldone4x
+
+.align	4
+Lone:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	ld1	{v4.2d},[x2]
+	eor	v1.16b,v1.16b,v18.16b
+#ifndef	__ARMEB__
+	rev64	v4.16b,v4.16b
+#endif
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	eor	v0.16b,v1.16b,v18.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v0.16b,v0.16b,v18.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+	eor	v16.16b,v4.16b,v0.16b
+	ext	v3.16b,v16.16b,v16.16b,#8
+
+	pmull	v0.1q,v20.1d,v3.1d
+	eor	v16.16b,v16.16b,v3.16b
+	pmull2	v2.1q,v20.2d,v3.2d
+	pmull	v1.1q,v21.1d,v16.1d
+
+Ldone4x:
+	ext	v17.16b,v0.16b,v2.16b,#8		//Karatsuba post-processing
+	eor	v18.16b,v0.16b,v2.16b
+	eor	v1.16b,v1.16b,v17.16b
+	eor	v1.16b,v1.16b,v18.16b
+
+	pmull	v18.1q,v0.1d,v19.1d		//1st phase of reduction
+	ins	v2.d[0],v1.d[1]
+	ins	v1.d[1],v0.d[0]
+	eor	v0.16b,v1.16b,v18.16b
+
+	ext	v18.16b,v0.16b,v0.16b,#8		//2nd phase of reduction
+	pmull	v0.1q,v0.1d,v19.1d
+	eor	v18.16b,v18.16b,v2.16b
+	eor	v0.16b,v0.16b,v18.16b
+	ext	v0.16b,v0.16b,v0.16b,#8
+
+#ifndef __ARMEB__
+	rev64	v0.16b,v0.16b
+#endif
+	st1	{v0.2d},[x0]		//write out Xi
+
+	ret
+
 .byte	71,72,65,83,72,32,102,111,114,32,65,82,77,118,56,44,32,67,82,89,80,84,79,71,65,77,83,32,98,121,32,60,97,112,112,114,111,64,111,112,101,110,115,115,108,46,111,114,103,62,0
 .align	2
 .align	2
 #endif
+#endif
 #endif  // !OPENSSL_NO_ASM
diff --git a/examples/Makefile b/examples/Makefile
index 5660bbd..82db2e4 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -17,7 +17,7 @@
 
 LDFLAGS = -L$(LIBCRYPTO_DIR) -L$(LIBSSL_DIR) -L$(LIB_DIR)
 
-LIBS = $(LIB_DIR)/libquiche.a -lev -ldl -pthread
+LIBS = $(LIB_DIR)/libquiche.a -lev -ldl -pthread -lm
 
 all: client server http3-client http3-server
 
diff --git a/examples/client.c b/examples/client.c
index 0df9665..ebe049b 100644
--- a/examples/client.c
+++ b/examples/client.c
@@ -51,6 +51,9 @@
 
     int sock;
 
+    struct sockaddr_storage local_addr;
+    socklen_t local_addr_len;
+
     quiche_conn *conn;
 };
 
@@ -122,8 +125,10 @@
 
         quiche_recv_info recv_info = {
             (struct sockaddr *) &peer_addr,
-
             peer_addr_len,
+
+            (struct sockaddr *) &conn_io->local_addr,
+            conn_io->local_addr_len,
         };
 
         ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);
@@ -206,11 +211,13 @@
 
     if (quiche_conn_is_closed(conn_io->conn)) {
         quiche_stats stats;
+        quiche_path_stats path_stats;
 
         quiche_conn_stats(conn_io->conn, &stats);
+        quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
 
         fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns\n",
-                stats.recv, stats.sent, stats.lost, stats.rtt);
+                stats.recv, stats.sent, stats.lost, path_stats.rtt);
 
         ev_break(EV_A_ EVBREAK_ONE);
         return;
@@ -282,7 +289,23 @@
         return -1;
     }
 
-    quiche_conn *conn = quiche_connect(host, (const uint8_t*) scid, sizeof(scid),
+    struct conn_io *conn_io = malloc(sizeof(*conn_io));
+    if (conn_io == NULL) {
+        fprintf(stderr, "failed to allocate connection IO\n");
+        return -1;
+    }
+
+    conn_io->local_addr_len = sizeof(conn_io->local_addr);
+    if (getsockname(sock, (struct sockaddr *)&conn_io->local_addr,
+                    &conn_io->local_addr_len) != 0)
+    {
+        perror("failed to get local address of socket");
+        return -1;
+    };
+
+    quiche_conn *conn = quiche_connect(host, (const uint8_t *) scid, sizeof(scid),
+                                       (struct sockaddr *) &conn_io->local_addr,
+                                       conn_io->local_addr_len,
                                        peer->ai_addr, peer->ai_addrlen, config);
 
     if (conn == NULL) {
@@ -290,12 +313,6 @@
         return -1;
     }
 
-    struct conn_io *conn_io = malloc(sizeof(*conn_io));
-    if (conn_io == NULL) {
-        fprintf(stderr, "failed to allocate connection IO\n");
-        return -1;
-    }
-
     conn_io->sock = sock;
     conn_io->conn = conn;
 
diff --git a/examples/client.rs b/examples/client.rs
index 88490aa..2f576fc 100644
--- a/examples/client.rs
+++ b/examples/client.rs
@@ -44,7 +44,7 @@
     let cmd = &args.next().unwrap();
 
     if args.len() != 1 {
-        println!("Usage: {} URL", cmd);
+        println!("Usage: {cmd} URL");
         println!("\nSee tools/apps/ for more complete implementations.");
         return;
     }
@@ -52,7 +52,7 @@
     let url = url::Url::parse(&args.next().unwrap()).unwrap();
 
     // Setup the event loop.
-    let poll = mio::Poll::new().unwrap();
+    let mut poll = mio::Poll::new().unwrap();
     let mut events = mio::Events::with_capacity(1024);
 
     // Resolve server address.
@@ -68,16 +68,11 @@
 
     // Create the UDP socket backing the QUIC connection, and register it with
     // the event loop.
-    let socket = std::net::UdpSocket::bind(bind_addr).unwrap();
-
-    let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
-    poll.register(
-        &socket,
-        mio::Token(0),
-        mio::Ready::readable(),
-        mio::PollOpt::edge(),
-    )
-    .unwrap();
+    let mut socket =
+        mio::net::UdpSocket::bind(bind_addr.parse().unwrap()).unwrap();
+    poll.registry()
+        .register(&mut socket, mio::Token(0), mio::Interest::READABLE)
+        .unwrap();
 
     // Create the configuration for the QUIC connection.
     let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
@@ -86,9 +81,13 @@
     config.verify_peer(false);
 
     config
-        .set_application_protos(
-            b"\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9",
-        )
+        .set_application_protos(&[
+            b"hq-interop",
+            b"hq-29",
+            b"hq-28",
+            b"hq-27",
+            b"http/0.9",
+        ])
         .unwrap();
 
     config.set_max_idle_timeout(5000);
@@ -107,9 +106,13 @@
 
     let scid = quiche::ConnectionId::from_ref(&scid);
 
+    // Get local address.
+    let local_addr = socket.local_addr().unwrap();
+
     // Create a QUIC connection and initiate handshake.
     let mut conn =
-        quiche::connect(url.domain(), &scid, peer_addr, &mut config).unwrap();
+        quiche::connect(url.domain(), &scid, local_addr, peer_addr, &mut config)
+            .unwrap();
 
     info!(
         "connecting to {:} from {:} with scid {}",
@@ -120,7 +123,7 @@
 
     let (write, send_info) = conn.send(&mut out).expect("initial send failed");
 
-    while let Err(e) = socket.send_to(&out[..write], &send_info.to) {
+    while let Err(e) = socket.send_to(&out[..write], send_info.to) {
         if e.kind() == std::io::ErrorKind::WouldBlock {
             debug!("send() would block");
             continue;
@@ -168,7 +171,10 @@
 
             debug!("got {} bytes", len);
 
-            let recv_info = quiche::RecvInfo { from };
+            let recv_info = quiche::RecvInfo {
+                to: socket.local_addr().unwrap(),
+                from,
+            };
 
             // Process potentially coalesced packets.
             let read = match conn.recv(&mut buf[..len], recv_info) {
@@ -216,7 +222,7 @@
                 );
 
                 print!("{}", unsafe {
-                    std::str::from_utf8_unchecked(&stream_buf)
+                    std::str::from_utf8_unchecked(stream_buf)
                 });
 
                 // The server reported that it has no more data to send, which
@@ -251,7 +257,7 @@
                 },
             };
 
-            if let Err(e) = socket.send_to(&out[..write], &send_info.to) {
+            if let Err(e) = socket.send_to(&out[..write], send_info.to) {
                 if e.kind() == std::io::ErrorKind::WouldBlock {
                     debug!("send() would block");
                     break;
@@ -271,7 +277,7 @@
 }
 
 fn hex_dump(buf: &[u8]) -> String {
-    let vec: Vec<String> = buf.iter().map(|b| format!("{:02x}", b)).collect();
+    let vec: Vec<String> = buf.iter().map(|b| format!("{b:02x}")).collect();
 
     vec.join("")
 }
diff --git a/examples/http3-client.c b/examples/http3-client.c
index 6b263ff..03b1133 100644
--- a/examples/http3-client.c
+++ b/examples/http3-client.c
@@ -53,6 +53,9 @@
 
     int sock;
 
+    struct sockaddr_storage local_addr;
+    socklen_t local_addr_len;
+
     quiche_conn *conn;
 
     quiche_h3_conn *http3;
@@ -98,6 +101,14 @@
     ev_timer_again(loop, &conn_io->timer);
 }
 
+static int for_each_setting(uint64_t identifier, uint64_t value,
+                           void *argp) {
+    fprintf(stderr, "got HTTP/3 SETTING: %" PRIu64 "=%" PRIu64 "\n",
+            identifier, value);
+
+    return 0;
+}
+
 static int for_each_header(uint8_t *name, size_t name_len,
                            uint8_t *value, size_t value_len,
                            void *argp) {
@@ -109,6 +120,7 @@
 
 static void recv_cb(EV_P_ ev_io *w, int revents) {
     static bool req_sent = false;
+    static bool settings_received = false;
 
     struct conn_io *conn_io = w->data;
 
@@ -135,8 +147,10 @@
 
         quiche_recv_info recv_info = {
             (struct sockaddr *) &peer_addr,
-
             peer_addr_len,
+
+            (struct sockaddr *) &conn_io->local_addr,
+            conn_io->local_addr_len,
         };
 
         ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);
@@ -244,6 +258,16 @@
                 break;
             }
 
+            if (!settings_received) {
+                int rc = quiche_h3_for_each_setting(conn_io->http3,
+                                                    for_each_setting,
+                                                    NULL);
+
+                if (rc == 0) {
+                    settings_received = true;
+                }
+            }
+
             switch (quiche_h3_event_type(ev)) {
                 case QUICHE_H3_EVENT_HEADERS: {
                     int rc = quiche_h3_event_for_each_header(ev, for_each_header,
@@ -278,6 +302,17 @@
                     }
                     break;
 
+                case QUICHE_H3_EVENT_RESET:
+                    fprintf(stderr, "request was reset\n");
+
+                    if (quiche_conn_close(conn_io->conn, true, 0, NULL, 0) < 0) {
+                        fprintf(stderr, "failed to close connection\n");
+                    }
+                    break;
+
+                case QUICHE_H3_EVENT_PRIORITY_UPDATE:
+                    break;
+
                 case QUICHE_H3_EVENT_DATAGRAM:
                     break;
 
@@ -304,11 +339,13 @@
 
     if (quiche_conn_is_closed(conn_io->conn)) {
         quiche_stats stats;
+        quiche_path_stats path_stats;
 
         quiche_conn_stats(conn_io->conn, &stats);
+        quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
 
         fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns\n",
-                stats.recv, stats.sent, stats.lost, stats.rtt);
+                stats.recv, stats.sent, stats.lost, path_stats.rtt);
 
         ev_break(EV_A_ EVBREAK_ONE);
         return;
@@ -384,7 +421,23 @@
         return -1;
     }
 
-    quiche_conn *conn = quiche_connect(host, (const uint8_t*) scid, sizeof(scid),
+    struct conn_io *conn_io = malloc(sizeof(*conn_io));
+    if (conn_io == NULL) {
+        fprintf(stderr, "failed to allocate connection IO\n");
+        return -1;
+    }
+
+    conn_io->local_addr_len = sizeof(conn_io->local_addr);
+    if (getsockname(sock, (struct sockaddr *)&conn_io->local_addr,
+                    &conn_io->local_addr_len) != 0)
+    {
+        perror("failed to get local address of socket");
+        return -1;
+    };
+
+    quiche_conn *conn = quiche_connect(host, (const uint8_t *) scid, sizeof(scid),
+                                       (struct sockaddr *) &conn_io->local_addr,
+                                       conn_io->local_addr_len,
                                        peer->ai_addr, peer->ai_addrlen, config);
 
     if (conn == NULL) {
@@ -392,12 +445,6 @@
         return -1;
     }
 
-    struct conn_io *conn_io = malloc(sizeof(*conn_io));
-    if (conn_io == NULL) {
-        fprintf(stderr, "failed to allocate connection IO\n");
-        return -1;
-    }
-
     conn_io->sock = sock;
     conn_io->conn = conn;
     conn_io->host = host;
diff --git a/examples/http3-client.rs b/examples/http3-client.rs
index 2acb2ca..6a6bbaa 100644
--- a/examples/http3-client.rs
+++ b/examples/http3-client.rs
@@ -29,6 +29,8 @@
 
 use std::net::ToSocketAddrs;
 
+use quiche::h3::NameValue;
+
 use ring::rand::*;
 
 const MAX_DATAGRAM_SIZE: usize = 1350;
@@ -42,7 +44,7 @@
     let cmd = &args.next().unwrap();
 
     if args.len() != 1 {
-        println!("Usage: {} URL", cmd);
+        println!("Usage: {cmd} URL");
         println!("\nSee tools/apps/ for more complete implementations.");
         return;
     }
@@ -50,7 +52,7 @@
     let url = url::Url::parse(&args.next().unwrap()).unwrap();
 
     // Setup the event loop.
-    let poll = mio::Poll::new().unwrap();
+    let mut poll = mio::Poll::new().unwrap();
     let mut events = mio::Events::with_capacity(1024);
 
     // Resolve server address.
@@ -66,16 +68,11 @@
 
     // Create the UDP socket backing the QUIC connection, and register it with
     // the event loop.
-    let socket = std::net::UdpSocket::bind(bind_addr).unwrap();
-
-    let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
-    poll.register(
-        &socket,
-        mio::Token(0),
-        mio::Ready::readable(),
-        mio::PollOpt::edge(),
-    )
-    .unwrap();
+    let mut socket =
+        mio::net::UdpSocket::bind(bind_addr.parse().unwrap()).unwrap();
+    poll.registry()
+        .register(&mut socket, mio::Token(0), mio::Interest::READABLE)
+        .unwrap();
 
     // Create the configuration for the QUIC connection.
     let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
@@ -106,9 +103,13 @@
 
     let scid = quiche::ConnectionId::from_ref(&scid);
 
+    // Get local address.
+    let local_addr = socket.local_addr().unwrap();
+
     // Create a QUIC connection and initiate handshake.
     let mut conn =
-        quiche::connect(url.domain(), &scid, peer_addr, &mut config).unwrap();
+        quiche::connect(url.domain(), &scid, local_addr, peer_addr, &mut config)
+            .unwrap();
 
     info!(
         "connecting to {:} from {:} with scid {}",
@@ -119,7 +120,7 @@
 
     let (write, send_info) = conn.send(&mut out).expect("initial send failed");
 
-    while let Err(e) = socket.send_to(&out[..write], &send_info.to) {
+    while let Err(e) = socket.send_to(&out[..write], send_info.to) {
         if e.kind() == std::io::ErrorKind::WouldBlock {
             debug!("send() would block");
             continue;
@@ -189,7 +190,10 @@
 
             debug!("got {} bytes", len);
 
-            let recv_info = quiche::RecvInfo { from };
+            let recv_info = quiche::RecvInfo {
+                to: local_addr,
+                from,
+            };
 
             // Process potentially coalesced packets.
             let read = match conn.recv(&mut buf[..len], recv_info) {
@@ -215,7 +219,7 @@
         if conn.is_established() && http3_conn.is_none() {
             http3_conn = Some(
                 quiche::h3::Connection::with_transport(&mut conn, &h3_config)
-                    .unwrap(),
+                .expect("Unable to create HTTP/3 connection, check the server's uni stream limit and window size"),
             );
         }
 
@@ -238,7 +242,8 @@
                     Ok((stream_id, quiche::h3::Event::Headers { list, .. })) => {
                         info!(
                             "got response headers {:?} on stream id {}",
-                            list, stream_id
+                            hdrs_to_strings(&list),
+                            stream_id
                         );
                     },
 
@@ -266,8 +271,19 @@
                         conn.close(true, 0x00, b"kthxbye").unwrap();
                     },
 
+                    Ok((_stream_id, quiche::h3::Event::Reset(e))) => {
+                        error!(
+                            "request was reset by peer with {}, closing...",
+                            e
+                        );
+
+                        conn.close(true, 0x00, b"kthxbye").unwrap();
+                    },
+
                     Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
 
+                    Ok((_, quiche::h3::Event::PriorityUpdate)) => unreachable!(),
+
                     Ok((goaway_id, quiche::h3::Event::GoAway)) => {
                         info!("GOAWAY id={}", goaway_id);
                     },
@@ -304,7 +320,7 @@
                 },
             };
 
-            if let Err(e) = socket.send_to(&out[..write], &send_info.to) {
+            if let Err(e) = socket.send_to(&out[..write], send_info.to) {
                 if e.kind() == std::io::ErrorKind::WouldBlock {
                     debug!("send() would block");
                     break;
@@ -324,7 +340,18 @@
 }
 
 fn hex_dump(buf: &[u8]) -> String {
-    let vec: Vec<String> = buf.iter().map(|b| format!("{:02x}", b)).collect();
+    let vec: Vec<String> = buf.iter().map(|b| format!("{b:02x}")).collect();
 
     vec.join("")
 }
+
+pub fn hdrs_to_strings(hdrs: &[quiche::h3::Header]) -> Vec<(String, String)> {
+    hdrs.iter()
+        .map(|h| {
+            let name = String::from_utf8_lossy(h.name()).to_string();
+            let value = String::from_utf8_lossy(h.value()).to_string();
+
+            (name, value)
+        })
+        .collect()
+}
diff --git a/examples/http3-server.c b/examples/http3-server.c
index 73c29ae..7eb2d22 100644
--- a/examples/http3-server.c
+++ b/examples/http3-server.c
@@ -55,6 +55,9 @@
 struct connections {
     int sock;
 
+    struct sockaddr *local_addr;
+    socklen_t local_addr_len;
+
     struct conn_io *h;
 };
 
@@ -177,8 +180,11 @@
 
 static struct conn_io *create_conn(uint8_t *scid, size_t scid_len,
                                    uint8_t *odcid, size_t odcid_len,
+                                   struct sockaddr *local_addr,
+                                   socklen_t local_addr_len,
                                    struct sockaddr_storage *peer_addr,
-                                   socklen_t peer_addr_len) {
+                                   socklen_t peer_addr_len)
+{
     struct conn_io *conn_io = calloc(1, sizeof(*conn_io));
     if (conn_io == NULL) {
         fprintf(stderr, "failed to allocate connection IO\n");
@@ -193,6 +199,8 @@
 
     quiche_conn *conn = quiche_accept(conn_io->cid, LOCAL_CONN_ID_LEN,
                                       odcid, odcid_len,
+                                      local_addr,
+                                      local_addr_len,
                                       (struct sockaddr *) peer_addr,
                                       peer_addr_len,
                                       config);
@@ -205,7 +213,7 @@
     conn_io->sock = conns->sock;
     conn_io->conn = conn;
 
-    memcpy(&conn_io->peer_addr, &peer_addr, peer_addr_len);
+    memcpy(&conn_io->peer_addr, peer_addr, peer_addr_len);
     conn_io->peer_addr_len = peer_addr_len;
 
     ev_init(&conn_io->timer, timeout_cb);
@@ -347,6 +355,7 @@
             }
 
             conn_io = create_conn(dcid, dcid_len, odcid, odcid_len,
+                                  conns->local_addr, conns->local_addr_len,
                                   &peer_addr, peer_addr_len);
 
             if (conn_io == NULL) {
@@ -355,9 +364,11 @@
         }
 
         quiche_recv_info recv_info = {
-            (struct sockaddr *) &peer_addr,
-
+            (struct sockaddr *)&peer_addr,
             peer_addr_len,
+
+            conns->local_addr,
+            conns->local_addr_len,
         };
 
         ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);
@@ -442,6 +453,12 @@
                     case QUICHE_H3_EVENT_FINISHED:
                         break;
 
+                    case QUICHE_H3_EVENT_RESET:
+                        break;
+
+                    case QUICHE_H3_EVENT_PRIORITY_UPDATE:
+                        break;
+
                     case QUICHE_H3_EVENT_DATAGRAM:
                         break;
 
@@ -461,10 +478,13 @@
 
         if (quiche_conn_is_closed(conn_io->conn)) {
             quiche_stats stats;
+            quiche_path_stats path_stats;
 
             quiche_conn_stats(conn_io->conn, &stats);
+            quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
+
             fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
-                    stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
+                    stats.recv, stats.sent, stats.lost, path_stats.rtt, path_stats.cwnd);
 
             HASH_DELETE(hh, conns->h, conn_io);
 
@@ -486,10 +506,13 @@
 
     if (quiche_conn_is_closed(conn_io->conn)) {
         quiche_stats stats;
+        quiche_path_stats path_stats;
 
         quiche_conn_stats(conn_io->conn, &stats);
+        quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
+
         fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
-                stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
+                stats.recv, stats.sent, stats.lost, path_stats.rtt, path_stats.cwnd);
 
         HASH_DELETE(hh, conns->h, conn_io);
 
@@ -569,6 +592,8 @@
     struct connections c;
     c.sock = sock;
     c.h = NULL;
+    c.local_addr = local->ai_addr;
+    c.local_addr_len = local->ai_addrlen;
 
     conns = &c;
 
diff --git a/examples/http3-server.rs b/examples/http3-server.rs
index e84d1ea..32650cd 100644
--- a/examples/http3-server.rs
+++ b/examples/http3-server.rs
@@ -46,7 +46,7 @@
 }
 
 struct Client {
-    conn: std::pin::Pin<Box<quiche::Connection>>,
+    conn: quiche::Connection,
 
     http3_conn: Option<quiche::h3::Connection>,
 
@@ -64,26 +64,21 @@
     let cmd = &args.next().unwrap();
 
     if args.len() != 0 {
-        println!("Usage: {}", cmd);
+        println!("Usage: {cmd}");
         println!("\nSee tools/apps/ for more complete implementations.");
         return;
     }
 
     // Setup the event loop.
-    let poll = mio::Poll::new().unwrap();
+    let mut poll = mio::Poll::new().unwrap();
     let mut events = mio::Events::with_capacity(1024);
 
     // Create the UDP listening socket, and register it with the event loop.
-    let socket = net::UdpSocket::bind("127.0.0.1:4433").unwrap();
-
-    let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
-    poll.register(
-        &socket,
-        mio::Token(0),
-        mio::Ready::readable(),
-        mio::PollOpt::edge(),
-    )
-    .unwrap();
+    let mut socket =
+        mio::net::UdpSocket::bind("127.0.0.1:4433".parse().unwrap()).unwrap();
+    poll.registry()
+        .register(&mut socket, mio::Token(0), mio::Interest::READABLE)
+        .unwrap();
 
     // Create the configuration for the QUIC connections.
     let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
@@ -119,6 +114,8 @@
 
     let mut clients = ClientMap::new();
 
+    let local_addr = socket.local_addr().unwrap();
+
     loop {
         // Find the shorter timeout from all the active connections.
         //
@@ -198,7 +195,7 @@
 
                     let out = &out[..len];
 
-                    if let Err(e) = socket.send_to(out, &from) {
+                    if let Err(e) = socket.send_to(out, from) {
                         if e.kind() == std::io::ErrorKind::WouldBlock {
                             debug!("send() would block");
                             break;
@@ -235,7 +232,7 @@
 
                     let out = &out[..len];
 
-                    if let Err(e) = socket.send_to(out, &from) {
+                    if let Err(e) = socket.send_to(out, from) {
                         if e.kind() == std::io::ErrorKind::WouldBlock {
                             debug!("send() would block");
                             break;
@@ -266,9 +263,14 @@
 
                 debug!("New connection: dcid={:?} scid={:?}", hdr.dcid, scid);
 
-                let conn =
-                    quiche::accept(&scid, odcid.as_ref(), from, &mut config)
-                        .unwrap();
+                let conn = quiche::accept(
+                    &scid,
+                    odcid.as_ref(),
+                    local_addr,
+                    from,
+                    &mut config,
+                )
+                .unwrap();
 
                 let client = Client {
                     conn,
@@ -287,7 +289,10 @@
                 }
             };
 
-            let recv_info = quiche::RecvInfo { from };
+            let recv_info = quiche::RecvInfo {
+                to: socket.local_addr().unwrap(),
+                from,
+            };
 
             // Process potentially coalesced packets.
             let read = match client.conn.recv(pkt_buf, recv_info) {
@@ -360,8 +365,15 @@
 
                         Ok((_stream_id, quiche::h3::Event::Finished)) => (),
 
+                        Ok((_stream_id, quiche::h3::Event::Reset { .. })) => (),
+
                         Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
 
+                        Ok((
+                            _prioritized_element_id,
+                            quiche::h3::Event::PriorityUpdate,
+                        )) => (),
+
                         Ok((_goaway_id, quiche::h3::Event::GoAway)) => (),
 
                         Err(quiche::h3::Error::Done) => {
@@ -403,7 +415,7 @@
                     },
                 };
 
-                if let Err(e) = socket.send_to(&out[..write], &send_info.to) {
+                if let Err(e) = socket.send_to(&out[..write], send_info.to) {
                     if e.kind() == std::io::ErrorKind::WouldBlock {
                         debug!("send() would block");
                         break;
@@ -500,7 +512,7 @@
     info!(
         "{} got request {:?} on stream id {}",
         conn.trace_id(),
-        headers,
+        hdrs_to_strings(headers),
         stream_id
     );
 
@@ -620,7 +632,7 @@
     let resp = client.partial_responses.get_mut(&stream_id).unwrap();
 
     if let Some(ref headers) = resp.headers {
-        match http3_conn.send_response(conn, stream_id, &headers, false) {
+        match http3_conn.send_response(conn, stream_id, headers, false) {
             Ok(_) => (),
 
             Err(quiche::h3::Error::StreamBlocked) => {
@@ -657,3 +669,14 @@
         client.partial_responses.remove(&stream_id);
     }
 }
+
+pub fn hdrs_to_strings(hdrs: &[quiche::h3::Header]) -> Vec<(String, String)> {
+    hdrs.iter()
+        .map(|h| {
+            let name = String::from_utf8_lossy(h.name()).to_string();
+            let value = String::from_utf8_lossy(h.value()).to_string();
+
+            (name, value)
+        })
+        .collect()
+}
diff --git a/examples/qpack-decode.rs b/examples/qpack-decode.rs
index d2aaaa5..6d1cbf4 100644
--- a/examples/qpack-decode.rs
+++ b/examples/qpack-decode.rs
@@ -43,11 +43,11 @@
     let cmd = &args.next().unwrap();
 
     if args.len() != 1 {
-        println!("Usage: {} FILE", cmd);
+        println!("Usage: {cmd} FILE");
         return;
     }
 
-    let mut file = File::open(&args.next().unwrap()).unwrap();
+    let mut file = File::open(args.next().unwrap()).unwrap();
 
     let mut dec = qpack::Decoder::new();
 
@@ -61,7 +61,7 @@
         let _ = file.read(&mut len).unwrap();
         let len = u32::from_be_bytes(len) as usize;
 
-        let mut data = vec![0; len as usize];
+        let mut data = vec![0; len];
 
         let data_len = file.read(&mut data).unwrap();
 
@@ -76,10 +76,10 @@
             continue;
         }
 
-        for hdr in dec.decode(&data[..len], std::u64::MAX).unwrap() {
+        for hdr in dec.decode(&data[..len], u64::MAX).unwrap() {
             let name = std::str::from_utf8(hdr.name()).unwrap();
             let value = std::str::from_utf8(hdr.value()).unwrap();
-            println!("{}\t{}", name, value);
+            println!("{name}\t{value}");
         }
 
         println!();
diff --git a/examples/qpack-encode.rs b/examples/qpack-encode.rs
index 5215fe4..4d44e84 100644
--- a/examples/qpack-encode.rs
+++ b/examples/qpack-encode.rs
@@ -40,11 +40,11 @@
     let cmd = &args.next().unwrap();
 
     if args.len() != 1 {
-        println!("Usage: {} FILE", cmd);
+        println!("Usage: {cmd} FILE");
         return;
     }
 
-    let file = File::open(&args.next().unwrap()).unwrap();
+    let file = File::open(args.next().unwrap()).unwrap();
     let file = BufReader::new(&file);
 
     let mut enc = h3::qpack::Encoder::new();
diff --git a/examples/server.c b/examples/server.c
index a97250f..73447a3 100644
--- a/examples/server.c
+++ b/examples/server.c
@@ -55,6 +55,9 @@
 struct connections {
     int sock;
 
+    struct sockaddr *local_addr;
+    socklen_t local_addr_len;
+
     struct conn_io *h;
 };
 
@@ -175,8 +178,11 @@
 
 static struct conn_io *create_conn(uint8_t *scid, size_t scid_len,
                                    uint8_t *odcid, size_t odcid_len,
+                                   struct sockaddr *local_addr,
+                                   socklen_t local_addr_len,
                                    struct sockaddr_storage *peer_addr,
-                                   socklen_t peer_addr_len) {
+                                   socklen_t peer_addr_len)
+{
     struct conn_io *conn_io = calloc(1, sizeof(*conn_io));
     if (conn_io == NULL) {
         fprintf(stderr, "failed to allocate connection IO\n");
@@ -191,6 +197,8 @@
 
     quiche_conn *conn = quiche_accept(conn_io->cid, LOCAL_CONN_ID_LEN,
                                       odcid, odcid_len,
+                                      local_addr,
+                                      local_addr_len,
                                       (struct sockaddr *) peer_addr,
                                       peer_addr_len,
                                       config);
@@ -203,7 +211,7 @@
     conn_io->sock = conns->sock;
     conn_io->conn = conn;
 
-    memcpy(&conn_io->peer_addr, &peer_addr, peer_addr_len);
+    memcpy(&conn_io->peer_addr, peer_addr, peer_addr_len);
     conn_io->peer_addr_len = peer_addr_len;
 
     ev_init(&conn_io->timer, timeout_cb);
@@ -336,6 +344,7 @@
             }
 
             conn_io = create_conn(dcid, dcid_len, odcid, odcid_len,
+                                  conns->local_addr, conns->local_addr_len,
                                   &peer_addr, peer_addr_len);
 
             if (conn_io == NULL) {
@@ -344,9 +353,11 @@
         }
 
         quiche_recv_info recv_info = {
-            (struct sockaddr *) &peer_addr,
-
+            (struct sockaddr *)&peer_addr,
             peer_addr_len,
+
+            conns->local_addr,
+            conns->local_addr_len,
         };
 
         ssize_t done = quiche_conn_recv(conn_io->conn, buf, read, &recv_info);
@@ -390,10 +401,13 @@
 
         if (quiche_conn_is_closed(conn_io->conn)) {
             quiche_stats stats;
+            quiche_path_stats path_stats;
 
             quiche_conn_stats(conn_io->conn, &stats);
+            quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
+
             fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
-                    stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
+                    stats.recv, stats.sent, stats.lost, path_stats.rtt, path_stats.cwnd);
 
             HASH_DELETE(hh, conns->h, conn_io);
 
@@ -414,10 +428,13 @@
 
     if (quiche_conn_is_closed(conn_io->conn)) {
         quiche_stats stats;
+        quiche_path_stats path_stats;
 
         quiche_conn_stats(conn_io->conn, &stats);
+        quiche_conn_path_stats(conn_io->conn, 0, &path_stats);
+
         fprintf(stderr, "connection closed, recv=%zu sent=%zu lost=%zu rtt=%" PRIu64 "ns cwnd=%zu\n",
-                stats.recv, stats.sent, stats.lost, stats.rtt, stats.cwnd);
+                stats.recv, stats.sent, stats.lost, path_stats.rtt, path_stats.cwnd);
 
         HASH_DELETE(hh, conns->h, conn_io);
 
@@ -487,6 +504,8 @@
     struct connections c;
     c.sock = sock;
     c.h = NULL;
+    c.local_addr = local->ai_addr;
+    c.local_addr_len = local->ai_addrlen;
 
     conns = &c;
 
diff --git a/examples/server.rs b/examples/server.rs
index 90f0102..496b51c 100644
--- a/examples/server.rs
+++ b/examples/server.rs
@@ -42,7 +42,7 @@
 }
 
 struct Client {
-    conn: std::pin::Pin<Box<quiche::Connection>>,
+    conn: quiche::Connection,
 
     partial_responses: HashMap<u64, PartialResponse>,
 }
@@ -58,26 +58,21 @@
     let cmd = &args.next().unwrap();
 
     if args.len() != 0 {
-        println!("Usage: {}", cmd);
+        println!("Usage: {cmd}");
         println!("\nSee tools/apps/ for more complete implementations.");
         return;
     }
 
     // Setup the event loop.
-    let poll = mio::Poll::new().unwrap();
+    let mut poll = mio::Poll::new().unwrap();
     let mut events = mio::Events::with_capacity(1024);
 
     // Create the UDP listening socket, and register it with the event loop.
-    let socket = net::UdpSocket::bind("127.0.0.1:4433").unwrap();
-
-    let socket = mio::net::UdpSocket::from_socket(socket).unwrap();
-    poll.register(
-        &socket,
-        mio::Token(0),
-        mio::Ready::readable(),
-        mio::PollOpt::edge(),
-    )
-    .unwrap();
+    let mut socket =
+        mio::net::UdpSocket::bind("127.0.0.1:4433".parse().unwrap()).unwrap();
+    poll.registry()
+        .register(&mut socket, mio::Token(0), mio::Interest::READABLE)
+        .unwrap();
 
     // Create the configuration for the QUIC connections.
     let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
@@ -90,9 +85,13 @@
         .unwrap();
 
     config
-        .set_application_protos(
-            b"\x0ahq-interop\x05hq-29\x05hq-28\x05hq-27\x08http/0.9",
-        )
+        .set_application_protos(&[
+            b"hq-interop",
+            b"hq-29",
+            b"hq-28",
+            b"hq-27",
+            b"http/0.9",
+        ])
         .unwrap();
 
     config.set_max_idle_timeout(5000);
@@ -113,6 +112,8 @@
 
     let mut clients = ClientMap::new();
 
+    let local_addr = socket.local_addr().unwrap();
+
     loop {
         // Find the shorter timeout from all the active connections.
         //
@@ -192,7 +193,7 @@
 
                     let out = &out[..len];
 
-                    if let Err(e) = socket.send_to(out, &from) {
+                    if let Err(e) = socket.send_to(out, from) {
                         if e.kind() == std::io::ErrorKind::WouldBlock {
                             debug!("send() would block");
                             break;
@@ -229,7 +230,7 @@
 
                     let out = &out[..len];
 
-                    if let Err(e) = socket.send_to(out, &from) {
+                    if let Err(e) = socket.send_to(out, from) {
                         if e.kind() == std::io::ErrorKind::WouldBlock {
                             debug!("send() would block");
                             break;
@@ -260,9 +261,14 @@
 
                 debug!("New connection: dcid={:?} scid={:?}", hdr.dcid, scid);
 
-                let conn =
-                    quiche::accept(&scid, odcid.as_ref(), from, &mut config)
-                        .unwrap();
+                let conn = quiche::accept(
+                    &scid,
+                    odcid.as_ref(),
+                    local_addr,
+                    from,
+                    &mut config,
+                )
+                .unwrap();
 
                 let client = Client {
                     conn,
@@ -280,7 +286,10 @@
                 }
             };
 
-            let recv_info = quiche::RecvInfo { from };
+            let recv_info = quiche::RecvInfo {
+                to: socket.local_addr().unwrap(),
+                from,
+            };
 
             // Process potentially coalesced packets.
             let read = match client.conn.recv(pkt_buf, recv_info) {
@@ -348,7 +357,7 @@
                     },
                 };
 
-                if let Err(e) = socket.send_to(&out[..write], &send_info.to) {
+                if let Err(e) = socket.send_to(&out[..write], send_info.to) {
                     if e.kind() == std::io::ErrorKind::WouldBlock {
                         debug!("send() would block");
                         break;
@@ -499,7 +508,7 @@
     let resp = client.partial_responses.get_mut(&stream_id).unwrap();
     let body = &resp.body[resp.written..];
 
-    let written = match conn.stream_send(stream_id, &body, true) {
+    let written = match conn.stream_send(stream_id, body, true) {
         Ok(v) => v,
 
         Err(quiche::Error::Done) => 0,
diff --git a/include/quiche.h b/include/quiche.h
index ba0f04a..c8ced63 100644
--- a/include/quiche.h
+++ b/include/quiche.h
@@ -34,6 +34,16 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include <stddef.h>
+
+#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <time.h>
+#else
+#include <sys/socket.h>
+#include <sys/time.h>
+#endif
+
 #ifdef __unix__
 #include <sys/types.h>
 #endif
@@ -97,11 +107,23 @@
     // The specified stream was stopped by the peer.
     QUICHE_ERR_STREAM_STOPPED = -15,
 
+    // The specified stream was reset by the peer.
+    QUICHE_ERR_STREAM_RESET = -16,
+
     // The received data exceeds the stream's final size.
     QUICHE_ERR_FINAL_SIZE = -13,
 
     // Error in congestion control.
     QUICHE_ERR_CONGESTION_CONTROL = -14,
+
+    // Too many identifiers were provided.
+    QUICHE_ERR_ID_LIMIT = -17,
+
+    // Not enough available identifiers.
+    QUICHE_ERR_OUT_OF_IDENTIFIERS = -18,
+
+    // Error in key update.
+    QUICHE_ERR_KEY_UPDATE = -19,
 };
 
 // Returns a human readable string with the quiche version number.
@@ -112,7 +134,7 @@
                                 void *argp);
 
 // Stores configuration shared between multiple connections.
-typedef struct Config quiche_config;
+typedef struct quiche_config quiche_config;
 
 // Creates a config object with the given version.
 quiche_config *quiche_config_new(uint32_t version);
@@ -129,6 +151,10 @@
 int quiche_config_load_verify_locations_from_file(quiche_config *config,
                                                   const char *path);
 
+// Specifies a directory where trusted CA certificates are stored for the purposes of certificate verification.
+int quiche_config_load_verify_locations_from_directory(quiche_config *config,
+                                                       const char *path);
+
 // Configures whether to verify the peer's certificate.
 void quiche_config_verify_peer(quiche_config *config, bool v);
 
@@ -186,6 +212,7 @@
 enum quiche_cc_algorithm {
     QUICHE_CC_RENO = 0,
     QUICHE_CC_CUBIC = 1,
+    QUICHE_CC_BBR = 2,
 };
 
 // Sets the congestion control algorithm used.
@@ -194,11 +221,26 @@
 // Configures whether to use HyStart++.
 void quiche_config_enable_hystart(quiche_config *config, bool v);
 
+// Configures whether to enable pacing (enabled by default).
+void quiche_config_enable_pacing(quiche_config *config, bool v);
+
 // Configures whether to enable receiving DATAGRAM frames.
 void quiche_config_enable_dgram(quiche_config *config, bool enabled,
                                 size_t recv_queue_len,
                                 size_t send_queue_len);
 
+// Sets the maximum connection window.
+void quiche_config_set_max_connection_window(quiche_config *config, uint64_t v);
+
+// Sets the maximum stream window.
+void quiche_config_set_max_stream_window(quiche_config *config, uint64_t v);
+
+// Sets the limit of active connection IDs.
+void quiche_config_set_active_connection_id_limit(quiche_config *config, uint64_t v);
+
+// Sets the initial stateless reset token. |v| must contain 16 bytes, otherwise the behaviour is undefined.
+void quiche_config_set_stateless_reset_token(quiche_config *config, const uint8_t *v);
+
 // Frees the config object.
 void quiche_config_free(quiche_config *config);
 
@@ -211,18 +253,20 @@
                        uint8_t *token, size_t *token_len);
 
 // A QUIC connection.
-typedef struct Connection quiche_conn;
+typedef struct quiche_conn quiche_conn;
 
 // Creates a new server-side connection.
 quiche_conn *quiche_accept(const uint8_t *scid, size_t scid_len,
                            const uint8_t *odcid, size_t odcid_len,
-                           const struct sockaddr *from, size_t from_len,
+                           const struct sockaddr *local, size_t local_len,
+                           const struct sockaddr *peer, size_t peer_len,
                            quiche_config *config);
 
 // Creates a new client-side connection.
 quiche_conn *quiche_connect(const char *server_name,
                             const uint8_t *scid, size_t scid_len,
-                            const struct sockaddr *to, size_t to_len,
+                            const struct sockaddr *local, size_t local_len,
+                            const struct sockaddr *peer, size_t peer_len,
                             quiche_config *config);
 
 // Writes a version negotiation packet.
@@ -242,6 +286,7 @@
 
 quiche_conn *quiche_conn_new_with_tls(const uint8_t *scid, size_t scid_len,
                                       const uint8_t *odcid, size_t odcid_len,
+                                      const struct sockaddr *local, size_t local_len,
                                       const struct sockaddr *peer, size_t peer_len,
                                       quiche_config *config, void *ssl,
                                       bool is_server);
@@ -264,8 +309,13 @@
 int quiche_conn_set_session(quiche_conn *conn, const uint8_t *buf, size_t buf_len);
 
 typedef struct {
+    // The remote address the packet was received from.
     struct sockaddr *from;
     socklen_t from_len;
+
+    // The local address the packet was received on.
+    struct sockaddr *to;
+    socklen_t to_len;
 } quiche_recv_info;
 
 // Processes QUIC packets received from the peer.
@@ -273,7 +323,11 @@
                          const quiche_recv_info *info);
 
 typedef struct {
-    // The address the packet should be sent to.
+    // The local address the packet should be sent from.
+    struct sockaddr_storage from;
+    socklen_t from_len;
+
+    // The remote address the packet should be sent to.
     struct sockaddr_storage to;
     socklen_t to_len;
 
@@ -285,6 +339,9 @@
 ssize_t quiche_conn_send(quiche_conn *conn, uint8_t *out, size_t out_len,
                          quiche_send_info *out_info);
 
+// Returns the size of the send quantum, in bytes.
+size_t quiche_conn_send_quantum(const quiche_conn *conn);
+
 // Reads contiguous data from a stream.
 ssize_t quiche_conn_stream_recv(quiche_conn *conn, uint64_t stream_id,
                                 uint8_t *out, size_t buf_len, bool *fin);
@@ -293,6 +350,7 @@
 ssize_t quiche_conn_stream_send(quiche_conn *conn, uint64_t stream_id,
                                 const uint8_t *buf, size_t buf_len, bool fin);
 
+// The side of the stream to be shut down.
 enum quiche_shutdown {
     QUICHE_SHUTDOWN_READ = 0,
     QUICHE_SHUTDOWN_WRITE = 1,
@@ -306,29 +364,44 @@
 int quiche_conn_stream_shutdown(quiche_conn *conn, uint64_t stream_id,
                                 enum quiche_shutdown direction, uint64_t err);
 
-ssize_t quiche_conn_stream_capacity(quiche_conn *conn, uint64_t stream_id);
+// Returns the stream's send capacity in bytes.
+ssize_t quiche_conn_stream_capacity(const quiche_conn *conn, uint64_t stream_id);
 
-bool quiche_conn_stream_readable(quiche_conn *conn, uint64_t stream_id);
+// Returns true if the stream has data that can be read.
+bool quiche_conn_stream_readable(const quiche_conn *conn, uint64_t stream_id);
+
+// Returns the next stream that has data to read, or -1 if no such stream is
+// available.
+int64_t quiche_conn_stream_readable_next(quiche_conn *conn);
+
+// Returns true if the stream has enough send capacity.
+//
+// On error a value lower than 0 is returned.
+int quiche_conn_stream_writable(quiche_conn *conn, uint64_t stream_id, size_t len);
+
+// Returns the next stream that can be written to, or -1 if no such stream is
+// available.
+int64_t quiche_conn_stream_writable_next(quiche_conn *conn);
 
 // Returns true if all the data has been read from the specified stream.
-bool quiche_conn_stream_finished(quiche_conn *conn, uint64_t stream_id);
+bool quiche_conn_stream_finished(const quiche_conn *conn, uint64_t stream_id);
 
-typedef struct StreamIter quiche_stream_iter;
+typedef struct quiche_stream_iter quiche_stream_iter;
 
 // Returns an iterator over streams that have outstanding data to read.
-quiche_stream_iter *quiche_conn_readable(quiche_conn *conn);
+quiche_stream_iter *quiche_conn_readable(const quiche_conn *conn);
 
 // Returns an iterator over streams that can be written to.
-quiche_stream_iter *quiche_conn_writable(quiche_conn *conn);
+quiche_stream_iter *quiche_conn_writable(const quiche_conn *conn);
 
 // Returns the maximum possible size of egress UDP payloads.
-size_t quiche_conn_max_send_udp_payload_size(quiche_conn *conn);
+size_t quiche_conn_max_send_udp_payload_size(const quiche_conn *conn);
 
 // Returns the amount of time until the next timeout event, in nanoseconds.
-uint64_t quiche_conn_timeout_as_nanos(quiche_conn *conn);
+uint64_t quiche_conn_timeout_as_nanos(const quiche_conn *conn);
 
 // Returns the amount of time until the next timeout event, in milliseconds.
-uint64_t quiche_conn_timeout_as_millis(quiche_conn *conn);
+uint64_t quiche_conn_timeout_as_millis(const quiche_conn *conn);
 
 // Processes a timeout event.
 void quiche_conn_on_timeout(quiche_conn *conn);
@@ -338,53 +411,67 @@
                       const uint8_t *reason, size_t reason_len);
 
 // Returns a string uniquely representing the connection.
-void quiche_conn_trace_id(quiche_conn *conn, const uint8_t **out, size_t *out_len);
+void quiche_conn_trace_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
 
 // Returns the source connection ID.
-void quiche_conn_source_id(quiche_conn *conn, const uint8_t **out, size_t *out_len);
+void quiche_conn_source_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
 
 // Returns the destination connection ID.
-void quiche_conn_destination_id(quiche_conn *conn, const uint8_t **out, size_t *out_len);
+void quiche_conn_destination_id(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
 
 // Returns the negotiated ALPN protocol.
-void quiche_conn_application_proto(quiche_conn *conn, const uint8_t **out,
+void quiche_conn_application_proto(const quiche_conn *conn, const uint8_t **out,
                                    size_t *out_len);
 
+// Returns the peer's leaf certificate (if any) as a DER-encoded buffer.
+void quiche_conn_peer_cert(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
+
 // Returns the serialized cryptographic session for the connection.
-void quiche_conn_session(quiche_conn *conn, const uint8_t **out, size_t *out_len);
+void quiche_conn_session(const quiche_conn *conn, const uint8_t **out, size_t *out_len);
 
 // Returns true if the connection handshake is complete.
-bool quiche_conn_is_established(quiche_conn *conn);
+bool quiche_conn_is_established(const quiche_conn *conn);
 
 // Returns true if the connection has a pending handshake that has progressed
 // enough to send or receive early data.
-bool quiche_conn_is_in_early_data(quiche_conn *conn);
+bool quiche_conn_is_in_early_data(const quiche_conn *conn);
 
 // Returns whether there is stream or DATAGRAM data available to read.
-bool quiche_conn_is_readable(quiche_conn *conn);
+bool quiche_conn_is_readable(const quiche_conn *conn);
 
 // Returns true if the connection is draining.
-bool quiche_conn_is_draining(quiche_conn *conn);
+bool quiche_conn_is_draining(const quiche_conn *conn);
 
 // Returns the number of bidirectional streams that can be created
 // before the peer's stream count limit is reached.
-uint64_t quiche_conn_peer_streams_left_bidi(quiche_conn *conn);
+uint64_t quiche_conn_peer_streams_left_bidi(const quiche_conn *conn);
 
 // Returns the number of unidirectional streams that can be created
 // before the peer's stream count limit is reached.
-uint64_t quiche_conn_peer_streams_left_uni(quiche_conn *conn);
+uint64_t quiche_conn_peer_streams_left_uni(const quiche_conn *conn);
 
 // Returns true if the connection is closed.
-bool quiche_conn_is_closed(quiche_conn *conn);
+bool quiche_conn_is_closed(const quiche_conn *conn);
+
+// Returns true if the connection was closed due to the idle timeout.
+bool quiche_conn_is_timed_out(const quiche_conn *conn);
 
 // Returns true if a connection error was received, and updates the provided
 // parameters accordingly.
-bool quiche_conn_peer_error(quiche_conn *conn,
+bool quiche_conn_peer_error(const quiche_conn *conn,
                             bool *is_app,
                             uint64_t *error_code,
                             const uint8_t **reason,
                             size_t *reason_len);
 
+// Returns true if a connection error was queued or sent, and updates the provided
+// parameters accordingly.
+bool quiche_conn_local_error(const quiche_conn *conn,
+                             bool *is_app,
+                             uint64_t *error_code,
+                             const uint8_t **reason,
+                             size_t *reason_len);
+
 // Initializes the stream's application data.
 //
 // Stream data can only be initialized once. Additional calls to this method
@@ -415,36 +502,143 @@
     // The number of QUIC packets that were lost.
     size_t lost;
 
-    // The estimated round-trip time of the connection (in nanoseconds).
-    uint64_t rtt;
+    // The number of sent QUIC packets with retransmitted data.
+    size_t retrans;
 
-    // The size of the connection's congestion window in bytes.
-    size_t cwnd;
+    // The number of sent bytes.
+    uint64_t sent_bytes;
 
-    // The estimated data delivery rate in bytes/s.
-    uint64_t delivery_rate;
+    // The number of received bytes.
+    uint64_t recv_bytes;
+
+    // The number of bytes lost.
+    uint64_t lost_bytes;
+
+    // The number of stream bytes retransmitted.
+    uint64_t stream_retrans_bytes;
+
+    // The number of known paths for the connection.
+    size_t paths_count;
+
+    // The maximum idle timeout.
+    uint64_t peer_max_idle_timeout;
+
+    // The maximum UDP payload size.
+    uint64_t peer_max_udp_payload_size;
+
+    // The initial flow control maximum data for the connection.
+    uint64_t peer_initial_max_data;
+
+    // The initial flow control maximum data for local bidirectional streams.
+    uint64_t peer_initial_max_stream_data_bidi_local;
+
+    // The initial flow control maximum data for remote bidirectional streams.
+    uint64_t peer_initial_max_stream_data_bidi_remote;
+
+    // The initial flow control maximum data for unidirectional streams.
+    uint64_t peer_initial_max_stream_data_uni;
+
+    // The initial maximum bidirectional streams.
+    uint64_t peer_initial_max_streams_bidi;
+
+    // The initial maximum unidirectional streams.
+    uint64_t peer_initial_max_streams_uni;
+
+    // The ACK delay exponent.
+    uint64_t peer_ack_delay_exponent;
+
+    // The max ACK delay.
+    uint64_t peer_max_ack_delay;
+
+    // Whether active migration is disabled.
+    bool peer_disable_active_migration;
+
+    // The active connection ID limit.
+    uint64_t peer_active_conn_id_limit;
+
+    // DATAGRAM frame extension parameter, if any.
+    ssize_t peer_max_datagram_frame_size;
 } quiche_stats;
 
 // Collects and returns statistics about the connection.
-void quiche_conn_stats(quiche_conn *conn, quiche_stats *out);
+void quiche_conn_stats(const quiche_conn *conn, quiche_stats *out);
+
+typedef struct {
+    // The local address used by this path.
+    struct sockaddr_storage local_addr;
+    socklen_t local_addr_len;
+
+    // The peer address seen by this path.
+    struct sockaddr_storage peer_addr;
+    socklen_t peer_addr_len;
+
+    // The validation state of the path.
+    ssize_t validation_state;
+
+    // Whether this path is active.
+    bool active;
+
+    // The number of QUIC packets received on this path.
+    size_t recv;
+
+    // The number of QUIC packets sent on this path.
+    size_t sent;
+
+    // The number of QUIC packets that were lost on this path.
+    size_t lost;
+
+    // The number of sent QUIC packets with retransmitted data on this path.
+    size_t retrans;
+
+    // The estimated round-trip time of the path (in nanoseconds).
+    uint64_t rtt;
+
+    // The size of the path's congestion window in bytes.
+    size_t cwnd;
+
+    // The number of sent bytes on this path.
+    uint64_t sent_bytes;
+
+    // The number of received bytes on this path.
+    uint64_t recv_bytes;
+
+    // The number of bytes lost on this path.
+    uint64_t lost_bytes;
+
+    // The number of stream bytes retransmitted on this path.
+    uint64_t stream_retrans_bytes;
+
+    // The current PMTU for the path.
+    size_t pmtu;
+
+    // The most recent data delivery rate estimate in bytes/s.
+    uint64_t delivery_rate;
+} quiche_path_stats;
+
+
+// Collects and returns statistics about the specified path for the connection.
+//
+// The `idx` argument represent the path's index (also see the `paths_count`
+// field of `quiche_stats`).
+int quiche_conn_path_stats(const quiche_conn *conn, size_t idx, quiche_path_stats *out);
 
 // Returns the maximum DATAGRAM payload that can be sent.
-ssize_t quiche_conn_dgram_max_writable_len(quiche_conn *conn);
+ssize_t quiche_conn_dgram_max_writable_len(const quiche_conn *conn);
 
 // Returns the length of the first stored DATAGRAM.
-ssize_t quiche_conn_dgram_recv_front_len(quiche_conn *conn);
+ssize_t quiche_conn_dgram_recv_front_len(const quiche_conn *conn);
 
 // Returns the number of items in the DATAGRAM receive queue.
-ssize_t quiche_conn_dgram_recv_queue_len(quiche_conn *conn);
+ssize_t quiche_conn_dgram_recv_queue_len(const quiche_conn *conn);
 
-///Returns the total size of all items in the DATAGRAM receive queue.
-ssize_t quiche_conn_dgram_recv_queue_byte_size(quiche_conn *conn);
+// Returns the total size of all items in the DATAGRAM receive queue.
+ssize_t quiche_conn_dgram_recv_queue_byte_size(const quiche_conn *conn);
 
 // Returns the number of items in the DATAGRAM send queue.
-ssize_t quiche_conn_dgram_send_queue_len(quiche_conn *conn);
+ssize_t quiche_conn_dgram_send_queue_len(const quiche_conn *conn);
 
 // Returns the total size of all items in the DATAGRAM send queue.
-ssize_t quiche_conn_dgram_send_queue_byte_size(quiche_conn *conn);
+ssize_t quiche_conn_dgram_send_queue_byte_size(const quiche_conn *conn);
 
 // Reads the first received DATAGRAM.
 ssize_t quiche_conn_dgram_recv(quiche_conn *conn, uint8_t *buf,
@@ -458,6 +652,14 @@
 void quiche_conn_dgram_purge_outgoing(quiche_conn *conn,
                                       bool (*f)(uint8_t *, size_t));
 
+// Schedule an ack-eliciting packet on the active path.
+ssize_t quiche_conn_send_ack_eliciting(quiche_conn *conn);
+
+// Schedule an ack-eliciting packet on the specified path.
+ssize_t quiche_conn_send_ack_eliciting_on_path(quiche_conn *conn,
+                           const struct sockaddr *local, size_t local_len,
+                           const struct sockaddr *peer, size_t peer_len);
+
 // Frees the connection object.
 void quiche_conn_free(quiche_conn *conn);
 
@@ -469,83 +671,142 @@
 #define QUICHE_H3_APPLICATION_PROTOCOL "\x02h3\x05h3-29\x05h3-28\x05h3-27"
 
 enum quiche_h3_error {
-    /// There is no error or no work to do
+    // There is no error or no work to do
     QUICHE_H3_ERR_DONE = -1,
 
-    /// The provided buffer is too short.
+    // The provided buffer is too short.
     QUICHE_H3_ERR_BUFFER_TOO_SHORT = -2,
 
-    /// Internal error in the HTTP/3 stack.
+    // Internal error in the HTTP/3 stack.
     QUICHE_H3_ERR_INTERNAL_ERROR = -3,
 
-    /// Endpoint detected that the peer is exhibiting behavior that causes.
-    /// excessive load.
+    // Endpoint detected that the peer is exhibiting behavior that causes.
+    // excessive load.
     QUICHE_H3_ERR_EXCESSIVE_LOAD = -4,
 
-    /// Stream ID or Push ID greater that current maximum was
-    /// used incorrectly, such as exceeding a limit, reducing a limit,
-    /// or being reused.
+    // Stream ID or Push ID greater that current maximum was
+    // used incorrectly, such as exceeding a limit, reducing a limit,
+    // or being reused.
     QUICHE_H3_ERR_ID_ERROR= -5,
 
-    /// The endpoint detected that its peer created a stream that it will not
-    /// accept.
+    // The endpoint detected that its peer created a stream that it will not
+    // accept.
     QUICHE_H3_ERR_STREAM_CREATION_ERROR = -6,
 
-    /// A required critical stream was closed.
+    // A required critical stream was closed.
     QUICHE_H3_ERR_CLOSED_CRITICAL_STREAM = -7,
 
-    /// No SETTINGS frame at beginning of control stream.
+    // No SETTINGS frame at beginning of control stream.
     QUICHE_H3_ERR_MISSING_SETTINGS = -8,
 
-    /// A frame was received which is not permitted in the current state.
+    // A frame was received which is not permitted in the current state.
     QUICHE_H3_ERR_FRAME_UNEXPECTED = -9,
 
-    /// Frame violated layout or size rules.
+    // Frame violated layout or size rules.
     QUICHE_H3_ERR_FRAME_ERROR = -10,
 
-    /// QPACK Header block decompression failure.
+    // QPACK Header block decompression failure.
     QUICHE_H3_ERR_QPACK_DECOMPRESSION_FAILED = -11,
 
-    /// Error originated from the transport layer.
-    QUICHE_H3_ERR_TRANSPORT_ERROR = -12,
+    // -12 was previously used for TransportError, skip it
 
-    /// The underlying QUIC stream (or connection) doesn't have enough capacity
-    /// for the operation to complete. The application should retry later on.
+    // The underlying QUIC stream (or connection) doesn't have enough capacity
+    // for the operation to complete. The application should retry later on.
     QUICHE_H3_ERR_STREAM_BLOCKED = -13,
 
-    /// Error in the payload of a SETTINGS frame.
+    // Error in the payload of a SETTINGS frame.
     QUICHE_H3_ERR_SETTINGS_ERROR = -14,
 
-    /// Server rejected request.
+    // Server rejected request.
     QUICHE_H3_ERR_REQUEST_REJECTED = -15,
 
-    /// Request or its response cancelled.
+    // Request or its response cancelled.
     QUICHE_H3_ERR_REQUEST_CANCELLED = -16,
 
-    /// Client's request stream terminated without containing a full-formed
-    /// request.
+    // Client's request stream terminated without containing a full-formed
+    // request.
     QUICHE_H3_ERR_REQUEST_INCOMPLETE = -17,
 
-    /// An HTTP message was malformed and cannot be processed.
+    // An HTTP message was malformed and cannot be processed.
     QUICHE_H3_ERR_MESSAGE_ERROR = -18,
 
     // The TCP connection established in response to a CONNECT request was
-    /// reset or abnormally closed.
+    // reset or abnormally closed.
     QUICHE_H3_ERR_CONNECT_ERROR = -19,
 
-    /// The requested operation cannot be served over HTTP/3. Peer should retry
-    /// over HTTP/1.1.
+    // The requested operation cannot be served over HTTP/3. Peer should retry
+    // over HTTP/1.1.
     QUICHE_H3_ERR_VERSION_FALLBACK = -20,
+
+    // The following QUICHE_H3_TRANSPORT_ERR_* errors are propagated
+    // from the QUIC transport layer.
+
+    // See QUICHE_ERR_DONE.
+    QUICHE_H3_TRANSPORT_ERR_DONE = QUICHE_ERR_DONE - 1000,
+
+    // See QUICHE_ERR_BUFFER_TOO_SHORT.
+    QUICHE_H3_TRANSPORT_ERR_BUFFER_TOO_SHORT = QUICHE_ERR_BUFFER_TOO_SHORT - 1000,
+
+    // See QUICHE_ERR_UNKNOWN_VERSION.
+    QUICHE_H3_TRANSPORT_ERR_UNKNOWN_VERSION = QUICHE_ERR_UNKNOWN_VERSION - 1000,
+
+    // See QUICHE_ERR_INVALID_FRAME.
+    QUICHE_H3_TRANSPORT_ERR_INVALID_FRAME = QUICHE_ERR_INVALID_FRAME - 1000,
+
+    // See QUICHE_ERR_INVALID_PACKET.
+    QUICHE_H3_TRANSPORT_ERR_INVALID_PACKET = QUICHE_ERR_INVALID_PACKET - 1000,
+
+    // See QUICHE_ERR_INVALID_STATE.
+    QUICHE_H3_TRANSPORT_ERR_INVALID_STATE = QUICHE_ERR_INVALID_STATE - 1000,
+
+    // See QUICHE_ERR_INVALID_STREAM_STATE.
+    QUICHE_H3_TRANSPORT_ERR_INVALID_STREAM_STATE = QUICHE_ERR_INVALID_STREAM_STATE - 1000,
+
+    // See QUICHE_ERR_INVALID_TRANSPORT_PARAM.
+    QUICHE_H3_TRANSPORT_ERR_INVALID_TRANSPORT_PARAM = QUICHE_ERR_INVALID_TRANSPORT_PARAM - 1000,
+
+    // See QUICHE_ERR_CRYPTO_FAIL.
+    QUICHE_H3_TRANSPORT_ERR_CRYPTO_FAIL = QUICHE_ERR_CRYPTO_FAIL - 1000,
+
+    // See QUICHE_ERR_TLS_FAIL.
+    QUICHE_H3_TRANSPORT_ERR_TLS_FAIL = QUICHE_ERR_TLS_FAIL - 1000,
+
+    // See QUICHE_ERR_FLOW_CONTROL.
+    QUICHE_H3_TRANSPORT_ERR_FLOW_CONTROL = QUICHE_ERR_FLOW_CONTROL - 1000,
+
+    // See QUICHE_ERR_STREAM_LIMIT.
+    QUICHE_H3_TRANSPORT_ERR_STREAM_LIMIT = QUICHE_ERR_STREAM_LIMIT - 1000,
+
+    // See QUICHE_ERR_STREAM_STOPPED.
+    QUICHE_H3_TRANSPORT_ERR_STREAM_STOPPED = QUICHE_ERR_STREAM_STOPPED - 1000,
+
+    // See QUICHE_ERR_STREAM_RESET.
+    QUICHE_H3_TRANSPORT_ERR_STREAM_RESET = QUICHE_ERR_STREAM_RESET - 1000,
+
+    // See QUICHE_ERR_FINAL_SIZE.
+    QUICHE_H3_TRANSPORT_ERR_FINAL_SIZE = QUICHE_ERR_FINAL_SIZE - 1000,
+
+    // See QUICHE_ERR_CONGESTION_CONTROL.
+    QUICHE_H3_TRANSPORT_ERR_CONGESTION_CONTROL = QUICHE_ERR_CONGESTION_CONTROL - 1000,
+
+    // See QUICHE_ERR_ID_LIMIT.
+    QUICHE_H3_TRANSPORT_ERR_ID_LIMIT = QUICHE_ERR_ID_LIMIT - 1000,
+
+    // See QUICHE_ERR_OUT_OF_IDENTIFIERS.
+    QUICHE_H3_TRANSPORT_ERR_OUT_OF_IDENTIFIERS = QUICHE_ERR_OUT_OF_IDENTIFIERS - 1000,
+
+    // See QUICHE_ERR_KEY_UPDATE.
+    QUICHE_H3_TRANSPORT_ERR_KEY_UPDATE = QUICHE_ERR_KEY_UPDATE - 1000,
 };
 
 // Stores configuration shared between multiple connections.
-typedef struct Http3Config quiche_h3_config;
+typedef struct quiche_h3_config quiche_h3_config;
 
 // Creates an HTTP/3 config object with default settings values.
 quiche_h3_config *quiche_h3_config_new(void);
 
-// Sets the `SETTINGS_MAX_HEADER_LIST_SIZE` setting.
-void quiche_h3_config_set_max_header_list_size(quiche_h3_config *config, uint64_t v);
+// Sets the `SETTINGS_MAX_FIELD_SECTION_SIZE` setting.
+void quiche_h3_config_set_max_field_section_size(quiche_h3_config *config, uint64_t v);
 
 // Sets the `SETTINGS_QPACK_MAX_TABLE_CAPACITY` setting.
 void quiche_h3_config_set_qpack_max_table_capacity(quiche_h3_config *config, uint64_t v);
@@ -553,11 +814,14 @@
 // Sets the `SETTINGS_QPACK_BLOCKED_STREAMS` setting.
 void quiche_h3_config_set_qpack_blocked_streams(quiche_h3_config *config, uint64_t v);
 
+// Sets the `SETTINGS_ENABLE_CONNECT_PROTOCOL` setting.
+void quiche_h3_config_enable_extended_connect(quiche_h3_config *config, bool enabled);
+
 // Frees the HTTP/3 config object.
 void quiche_h3_config_free(quiche_h3_config *config);
 
-// A QUIC connection.
-typedef struct Http3Connection quiche_h3_conn;
+// An HTTP/3 connection.
+typedef struct quiche_h3_conn quiche_h3_conn;
 
 // Creates a new server-side connection.
 quiche_h3_conn *quiche_h3_accept(quiche_conn *quiche_conn,
@@ -573,9 +837,11 @@
     QUICHE_H3_EVENT_FINISHED,
     QUICHE_H3_EVENT_DATAGRAM,
     QUICHE_H3_EVENT_GOAWAY,
+    QUICHE_H3_EVENT_RESET,
+    QUICHE_H3_EVENT_PRIORITY_UPDATE,
 };
 
-typedef struct Http3Event quiche_h3_event;
+typedef struct quiche_h3_event quiche_h3_event;
 
 // Processes HTTP/3 data received from the peer.
 int64_t quiche_h3_conn_poll(quiche_h3_conn *conn, quiche_conn *quic_conn,
@@ -596,9 +862,22 @@
                                               void *argp),
                                     void *argp);
 
+// Iterates over the peer's HTTP/3 settings.
+//
+// The `cb` callback will be called for each setting in `conn`.
+// If `cb` returns any value other than `0`, processing will be interrupted and
+// the value is returned to the caller.
+int quiche_h3_for_each_setting(quiche_h3_conn *conn,
+                               int (*cb)(uint64_t identifier,
+                                         uint64_t value, void *argp),
+                               void *argp);
+
 // Check whether data will follow the headers on the stream.
 bool quiche_h3_event_headers_has_body(quiche_h3_event *ev);
 
+// Check whether or not extended connection is enabled by the peer
+bool quiche_h3_extended_connect_enabled_by_peer(quiche_h3_conn *conn);
+
 // Frees the HTTP/3 event object.
 void quiche_h3_event_free(quiche_h3_event *ev);
 
@@ -610,6 +889,12 @@
     size_t value_len;
 } quiche_h3_header;
 
+// Extensible Priorities parameters.
+typedef struct {
+    uint8_t urgency;
+    bool incremental;
+} quiche_h3_priority;
+
 // Sends an HTTP/3 request.
 int64_t quiche_h3_send_request(quiche_h3_conn *conn, quiche_conn *quic_conn,
                                quiche_h3_header *headers, size_t headers_len,
@@ -624,7 +909,7 @@
 int quiche_h3_send_response_with_priority(quiche_h3_conn *conn,
                             quiche_conn *quic_conn, uint64_t stream_id,
                             quiche_h3_header *headers, size_t headers_len,
-                            const char *priority, bool fin);
+                            quiche_h3_priority *priority, bool fin);
 
 // Sends an HTTP/3 body chunk on the given stream.
 ssize_t quiche_h3_send_body(quiche_h3_conn *conn, quiche_conn *quic_conn,
@@ -635,6 +920,30 @@
 ssize_t quiche_h3_recv_body(quiche_h3_conn *conn, quiche_conn *quic_conn,
                             uint64_t stream_id, uint8_t *out, size_t out_len);
 
+// Try to parse an Extensible Priority field value.
+int quiche_h3_parse_extensible_priority(uint8_t *priority,
+                                        size_t priority_len,
+                                        quiche_h3_priority *parsed);
+
+/// Sends a PRIORITY_UPDATE frame on the control stream with specified
+/// request stream ID and priority.
+int quiche_h3_send_priority_update_for_request(quiche_h3_conn *conn,
+                                               quiche_conn *quic_conn,
+                                               uint64_t stream_id,
+                                               quiche_h3_priority *priority);
+
+// Take the last received PRIORITY_UPDATE frame for a stream.
+//
+// The `cb` callback will be called once. `cb` should check the validity of
+// priority field value contents. If `cb` returns any value other than `0`,
+// processing will be interrupted and the value is returned to the caller.
+int quiche_h3_take_last_priority_update(quiche_h3_conn *conn,
+                                        uint64_t prioritized_element_id,
+                                        int (*cb)(uint8_t  *priority_field_value,
+                                                  uint64_t priority_field_value_len,
+                                                  void *argp),
+                                        void *argp);
+
 // Returns whether the peer enabled HTTP/3 DATAGRAM frame support.
 bool quiche_h3_dgram_enabled_by_peer(quiche_h3_conn *conn,
                                      quiche_conn *quic_conn);
diff --git a/patches/Android.bp.patch b/patches/Android.bp.patch
index 48c6dee..a1b4bf2 100644
--- a/patches/Android.bp.patch
+++ b/patches/Android.bp.patch
@@ -1,8 +1,8 @@
 diff --git a/Android.bp b/Android.bp
-index aced8a6..578cc68 100644
+index 884af0e..ecbb83f 100644
 --- a/Android.bp
 +++ b/Android.bp
-@@ -43,26 +43,39 @@ cc_library_headers {
+@@ -43,8 +43,8 @@ cc_library_headers {
      min_sdk_version: "29",
  }
  
@@ -13,7 +13,7 @@
      stem: "libquiche",
      host_supported: true,
      crate_name: "quiche",
-     cargo_env_compat: true,
+@@ -52,10 +52,11 @@ rust_ffi_shared {
      srcs: ["src/lib.rs"],
      edition: "2018",
      features: [
@@ -27,30 +27,27 @@
          "liblazy_static",
          "liblibc",
          "liblibm",
-         "liblog_rust",
-         "libring",
+@@ -65,41 +66,19 @@ rust_ffi_shared {
+         "libslab",
+         "libsmallvec",
      ],
 -    static_libs: [
+-        "libcrypto",
+-        "libssl",
+-    ],
 +    prefer_rlib: true,
 +    // For DnsResolver (Mainline module introduced in Q).
-+    apex_available: [
-+        "//apex_available:platform",
-+        "com.android.resolv",
-+    ],
-+    min_sdk_version: "29",
-+}
-+
-+rust_ffi {
-+    name: "libquiche_ffi",
-+    defaults: ["libquiche_defaults"],
-+    shared_libs: [
-         "libcrypto",
-         "libssl",
+     apex_available: [
+         "//apex_available:platform",
+         "com.android.resolv",
      ],
-@@ -69,54 +82,20 @@ rust_ffi_shared {
+-    product_available: true,
+-    vendor_available: true,
+     min_sdk_version: "29",
+ }
  
- rust_library {
-     name: "libquiche",
+-rust_library {
+-    name: "libquiche",
 -    host_supported: true,
 -    crate_name: "quiche",
 -    cargo_env_compat: true,
@@ -65,26 +62,30 @@
 -        "liblibc",
 -        "liblibm",
 -        "liblog_rust",
+-        "liboctets",
 -        "libring",
+-        "libslab",
+-        "libsmallvec",
 -    ],
 -    static_libs: [
++rust_ffi {
++    name: "libquiche_ffi",
 +    defaults: ["libquiche_defaults"],
 +    shared_libs: [
          "libcrypto",
          "libssl",
      ],
--    apex_available: [
--        "//apex_available:platform",
--        "com.android.resolv",
--    ],
--    min_sdk_version: "29",
+@@ -107,57 +86,41 @@ rust_library {
+         "//apex_available:platform",
+         "com.android.resolv",
+     ],
+-    product_available: true,
+-    vendor_available: true,
+     min_sdk_version: "29",
  }
  
 -rust_ffi_static {
-+// This target is used by doh_unit_test to prevent compatibility issues
-+// because doh_unit_test needs to be run on the R platform.
-+rust_library_rlib {
-     name: "libquiche_static",
+-    name: "libquiche_static",
 -    stem: "libquiche",
 -    host_supported: true,
 -    crate_name: "quiche",
@@ -100,8 +101,23 @@
 -        "liblibc",
 -        "liblibm",
 -        "liblog_rust",
+-        "liboctets",
 -        "libring",
--    ],
+-        "libslab",
+-        "libsmallvec",
++rust_library {
++    name: "libquiche",
++    defaults: ["libquiche_defaults"],
++    shared_libs: [
++        "libcrypto",
++        "libssl",
+     ],
++}
++
++// This target is used by doh_unit_test to prevent compatibility issues
++// because doh_unit_test needs to be run on the R platform.
++rust_library_rlib {
++    name: "libquiche_static",
 +    defaults: ["libquiche_defaults"],
      static_libs: [
 -        "libcrypto",
@@ -109,7 +125,11 @@
          "libssl",
      ],
      apex_available: [
-@@ -111,17 +90,13 @@ rust_library_rlib {
+         "//apex_available:platform",
+         "com.android.resolv",
+     ],
+-    product_available: true,
+-    vendor_available: true,
      min_sdk_version: "29",
  }
  
@@ -128,9 +148,9 @@
 -    },
      edition: "2018",
      features: [
-         "boringssl",
-@@ -136,10 +132,6 @@ rust_test {
-         "libring",
+         "boringssl-vendored",
+@@ -175,10 +138,6 @@ rust_test {
+         "libsmallvec",
          "liburl",
      ],
 -    static_libs: [
@@ -140,7 +160,7 @@
      data: [
          "examples/cert.crt",
          "examples/cert.key",
-@@ -149,3 +118,26 @@ rust_test {
+@@ -186,3 +145,26 @@ rust_test {
          "examples/rootca.crt",
      ],
  }
diff --git a/patches/lib.rs-app-proto.patch b/patches/lib.rs-app-proto.patch
deleted file mode 100644
index cd02a46..0000000
--- a/patches/lib.rs-app-proto.patch
+++ /dev/null
@@ -1,47 +0,0 @@
---- a/src/lib.rs
-+++ b/src/lib.rs
-@@ -6301,7 +6301,7 @@ mod tests {
-             .load_priv_key_from_pem_file("examples/cert.key")
-             .unwrap();
-         config
--            .set_application_protos(b"\x06proto1\06proto2")
-+            .set_application_protos(b"\x06proto1\x06proto2")
-             .unwrap();
- 
-         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
-@@ -8347,7 +8347,7 @@ mod tests {
-             .load_priv_key_from_pem_file("examples/cert.key")
-             .unwrap();
-         config
--            .set_application_protos(b"\x06proto1\06proto2")
-+            .set_application_protos(b"\x06proto1\x06proto2")
-             .unwrap();
- 
-         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
-@@ -8407,7 +8407,7 @@ mod tests {
-             .load_priv_key_from_pem_file("examples/cert.key")
-             .unwrap();
-         config
--            .set_application_protos(b"\x06proto1\06proto2")
-+            .set_application_protos(b"\x06proto1\x06proto2")
-             .unwrap();
- 
-         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
-@@ -8465,7 +8465,7 @@ mod tests {
-             .load_priv_key_from_pem_file("examples/cert.key")
-             .unwrap();
-         config
--            .set_application_protos(b"\x06proto1\06proto2")
-+            .set_application_protos(b"\x06proto1\x06proto2")
-             .unwrap();
- 
-         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
-@@ -9509,7 +9509,7 @@ mod tests {
-             .load_priv_key_from_pem_file("examples/cert.key")
-             .unwrap();
-         config
--            .set_application_protos(b"\x06proto1\06proto2")
-+            .set_application_protos(b"\x06proto1\x06proto2")
-             .unwrap();
- 
-         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
diff --git a/rustfmt.toml b/rustfmt.toml
deleted file mode 100644
index f0d9d93..0000000
--- a/rustfmt.toml
+++ /dev/null
@@ -1,62 +0,0 @@
-max_width = 82
-hard_tabs = false
-tab_spaces = 4
-newline_style = "Auto"
-use_small_heuristics = "Default"
-indent_style = "Block"
-wrap_comments = true
-format_code_in_doc_comments = true
-comment_width = 80
-normalize_comments = true
-normalize_doc_attributes = true
-format_strings = false
-format_macro_matchers = false
-format_macro_bodies = true
-empty_item_single_line = true
-struct_lit_single_line = true
-fn_single_line = false
-where_single_line = false
-imports_indent = "Block"
-imports_layout = "Vertical"
-imports_granularity = "Item"
-reorder_imports = true
-reorder_modules = true
-reorder_impl_items = true
-type_punctuation_density = "Wide"
-space_before_colon = false
-space_after_colon = true
-spaces_around_ranges = false
-binop_separator = "Back"
-remove_nested_parens = true
-combine_control_expr = true
-overflow_delimited_expr = true
-struct_field_align_threshold = 0
-enum_discrim_align_threshold = 20
-match_arm_blocks = false
-force_multiline_blocks = false
-fn_args_layout = "Compressed"
-brace_style = "SameLineWhere"
-control_brace_style = "AlwaysSameLine"
-trailing_semicolon = true
-trailing_comma = "Vertical"
-match_block_trailing_comma = true
-blank_lines_upper_bound = 1
-blank_lines_lower_bound = 0
-edition = "2018"
-merge_derives = true
-use_try_shorthand = true
-use_field_init_shorthand = true
-force_explicit_abi = false
-condense_wildcard_suffixes = true
-color = "Auto"
-unstable_features = true
-disable_all_formatting = false
-skip_children = false
-hide_parse_errors = false
-error_on_line_overflow = false
-error_on_unformatted = false
-report_todo = "Never"
-report_fixme = "Never"
-ignore = []
-emit_mode = "Files"
-make_backup = false
diff --git a/src/build.rs b/src/build.rs
index 875f556..739422f 100644
--- a/src/build.rs
+++ b/src/build.rs
@@ -1,26 +1,6 @@
 // Additional parameters for Android build of BoringSSL.
 //
-// Android NDK < 18 with GCC.
-const CMAKE_PARAMS_ANDROID_NDK_OLD_GCC: &[(&str, &[(&str, &str)])] = &[
-    ("aarch64", &[(
-        "ANDROID_TOOLCHAIN_NAME",
-        "aarch64-linux-android-4.9",
-    )]),
-    ("arm", &[(
-        "ANDROID_TOOLCHAIN_NAME",
-        "arm-linux-androideabi-4.9",
-    )]),
-    ("x86", &[(
-        "ANDROID_TOOLCHAIN_NAME",
-        "x86-linux-android-4.9",
-    )]),
-    ("x86_64", &[(
-        "ANDROID_TOOLCHAIN_NAME",
-        "x86_64-linux-android-4.9",
-    )]),
-];
-
-// Android NDK >= 19.
+// Requires Android NDK >= 19.
 const CMAKE_PARAMS_ANDROID_NDK: &[(&str, &[(&str, &str)])] = &[
     ("aarch64", &[("ANDROID_ABI", "arm64-v8a")]),
     ("arm", &[("ANDROID_ABI", "armeabi-v7a")]),
@@ -52,7 +32,7 @@
 /// so adjust library location based on platform and build target.
 /// See issue: https://github.com/alexcrichton/cmake-rs/issues/18
 fn get_boringssl_platform_output_path() -> String {
-    if cfg!(windows) {
+    if cfg!(target_env = "msvc") {
         // Code under this branch should match the logic in cmake-rs
         let debug_env_var =
             std::env::var("DEBUG").expect("DEBUG variable not defined in env");
@@ -97,17 +77,11 @@
     // Add platform-specific parameters.
     match os.as_ref() {
         "android" => {
-            let cmake_params_android = if cfg!(feature = "ndk-old-gcc") {
-                CMAKE_PARAMS_ANDROID_NDK_OLD_GCC
-            } else {
-                CMAKE_PARAMS_ANDROID_NDK
-            };
-
             // We need ANDROID_NDK_HOME to be set properly.
             let android_ndk_home = std::env::var("ANDROID_NDK_HOME")
                 .expect("Please set ANDROID_NDK_HOME for Android build");
             let android_ndk_home = std::path::Path::new(&android_ndk_home);
-            for (android_arch, params) in cmake_params_android {
+            for (android_arch, params) in CMAKE_PARAMS_ANDROID_NDK {
                 if *android_arch == arch {
                     for (name, value) in *params {
                         boringssl_cmake.define(name, value);
@@ -145,7 +119,7 @@
                 ""
             };
 
-            let cflag = format!("{} {}", bitcode_cflag, target_cflag);
+            let cflag = format!("{bitcode_cflag} {target_cflag}");
 
             boringssl_cmake.define("CMAKE_ASM_FLAGS", &cflag);
             boringssl_cmake.cflag(&cflag);
@@ -199,37 +173,52 @@
 fn write_pkg_config() {
     use std::io::prelude::*;
 
-    let profile = std::env::var("PROFILE").unwrap();
     let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
-    let target_dir = format!("{}/target/{}", manifest_dir, profile);
+    let target_dir = target_dir_path();
 
-    let out_path = std::path::Path::new(&target_dir).join("quiche.pc");
-    let mut out_file = std::fs::File::create(&out_path).unwrap();
+    let out_path = target_dir.as_path().join("quiche.pc");
+    let mut out_file = std::fs::File::create(out_path).unwrap();
 
-    let include_dir = format!("{}/include", manifest_dir);
+    let include_dir = format!("{manifest_dir}/include");
+
     let version = std::env::var("CARGO_PKG_VERSION").unwrap();
 
     let output = format!(
         "# quiche
 
-includedir={}
+includedir={include_dir}
 libdir={}
 
 Name: quiche
 Description: quiche library
 URL: https://github.com/cloudflare/quiche
-Version: {}
+Version: {version}
 Libs: -Wl,-rpath,${{libdir}} -L${{libdir}} -lquiche
 Cflags: -I${{includedir}}
 ",
-        include_dir, target_dir, version
+        target_dir.to_str().unwrap(),
     );
 
     out_file.write_all(output.as_bytes()).unwrap();
 }
 
+fn target_dir_path() -> std::path::PathBuf {
+    let out_dir = std::env::var("OUT_DIR").unwrap();
+    let out_dir = std::path::Path::new(&out_dir);
+
+    for p in out_dir.ancestors() {
+        if p.ends_with("build") {
+            return p.parent().unwrap().to_path_buf();
+        }
+    }
+
+    unreachable!();
+}
+
 fn main() {
-    if cfg!(feature = "boringssl-vendored") && !cfg!(feature = "boring-sys") {
+    if cfg!(feature = "boringssl-vendored") &&
+        !cfg!(feature = "boringssl-boring-crate")
+    {
         let bssl_dir = std::env::var("QUICHE_BSSL_PATH").unwrap_or_else(|_| {
             let mut cfg = get_boringssl_cmake_config();
 
@@ -238,24 +227,34 @@
                     .cxxflag("-DBORINGSSL_UNSAFE_FUZZER_MODE");
             }
 
-            cfg.build_target("bssl").build().display().to_string()
+            cfg.build_target("ssl").build();
+            cfg.build_target("crypto").build().display().to_string()
         });
 
         let build_path = get_boringssl_platform_output_path();
-        let build_dir = format!("{}/build/{}", bssl_dir, build_path);
-        println!("cargo:rustc-link-search=native={}", build_dir);
+        let mut build_dir = format!("{bssl_dir}/build/{build_path}");
 
-        println!("cargo:rustc-link-lib=static=crypto");
-        println!("cargo:rustc-link-lib=static=ssl");
+        // If build directory doesn't exist, use the specified path as is.
+        if !std::path::Path::new(&build_dir).is_dir() {
+            build_dir = bssl_dir;
+        }
+
+        println!("cargo:rustc-link-search=native={build_dir}");
+
+        let bssl_link_kind = std::env::var("QUICHE_BSSL_LINK_KIND")
+            .unwrap_or("static".to_string());
+        println!("cargo:rustc-link-lib={bssl_link_kind}=ssl");
+        println!("cargo:rustc-link-lib={bssl_link_kind}=crypto");
     }
 
-    if cfg!(feature = "boring-sys") {
-        println!("cargo:rustc-link-lib=static=crypto");
+    if cfg!(feature = "boringssl-boring-crate") {
         println!("cargo:rustc-link-lib=static=ssl");
+        println!("cargo:rustc-link-lib=static=crypto");
     }
 
     // MacOS: Allow cdylib to link with undefined symbols
-    if cfg!(target_os = "macos") {
+    let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
+    if target_os == "macos" {
         println!("cargo:rustc-cdylib-link-arg=-Wl,-undefined,dynamic_lookup");
     }
 
diff --git a/src/cid.rs b/src/cid.rs
new file mode 100644
index 0000000..2584635
--- /dev/null
+++ b/src/cid.rs
@@ -0,0 +1,964 @@
+// Copyright (C) 2022, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use crate::Error;
+use crate::Result;
+
+use crate::frame;
+use crate::packet::ConnectionId;
+
+use std::collections::VecDeque;
+
+/// A structure holding a `ConnectionId` and all its related metadata.
+#[derive(Debug, Default)]
+pub struct ConnectionIdEntry {
+    /// The Connection ID.
+    pub cid: ConnectionId<'static>,
+
+    /// Its associated sequence number.
+    pub seq: u64,
+
+    /// Its associated reset token. Initial CIDs may not have any reset token.
+    pub reset_token: Option<u128>,
+
+    /// The path identifier using this CID, if any.
+    pub path_id: Option<usize>,
+}
+
+#[derive(Default)]
+struct BoundedNonEmptyConnectionIdVecDeque {
+    /// The inner `VecDeque`.
+    inner: VecDeque<ConnectionIdEntry>,
+
+    /// The maximum number of elements that the `VecDeque` can have.
+    capacity: usize,
+}
+
+impl BoundedNonEmptyConnectionIdVecDeque {
+    /// Creates a `VecDeque` bounded by `capacity` and inserts
+    /// `initial_entry` in it.
+    fn new(capacity: usize, initial_entry: ConnectionIdEntry) -> Self {
+        let mut inner = VecDeque::with_capacity(1);
+        inner.push_back(initial_entry);
+        Self { inner, capacity }
+    }
+
+    /// Updates the maximum capacity of the inner `VecDeque` to `new_capacity`.
+    /// Does nothing if `new_capacity` is lower or equal to the current
+    /// `capacity`.
+    fn resize(&mut self, new_capacity: usize) {
+        if new_capacity > self.capacity {
+            self.capacity = new_capacity;
+        }
+    }
+
+    /// Returns the oldest inserted entry still present in the `VecDeque`.
+    fn get_oldest(&self) -> &ConnectionIdEntry {
+        self.inner.front().expect("vecdeque is empty")
+    }
+
+    /// Gets a immutable reference to the entry having the provided `seq`.
+    fn get(&self, seq: u64) -> Option<&ConnectionIdEntry> {
+        // We need to iterate over the whole map to find the key.
+        self.inner.iter().find(|e| e.seq == seq)
+    }
+
+    /// Gets a mutable reference to the entry having the provided `seq`.
+    fn get_mut(&mut self, seq: u64) -> Option<&mut ConnectionIdEntry> {
+        // We need to iterate over the whole map to find the key.
+        self.inner.iter_mut().find(|e| e.seq == seq)
+    }
+
+    /// Returns an iterator over the entries in the `VecDeque`.
+    fn iter(&self) -> impl Iterator<Item = &ConnectionIdEntry> {
+        self.inner.iter()
+    }
+
+    /// Returns the number of elements in the `VecDeque`.
+    fn len(&self) -> usize {
+        self.inner.len()
+    }
+
+    /// Inserts the provided entry in the `VecDeque`.
+    ///
+    /// This method ensures the unicity of the `seq` associated to an entry. If
+    /// an entry has the same `seq` than `e`, this method updates the entry in
+    /// the `VecDeque` and the number of stored elements remains unchanged.
+    ///
+    /// If inserting a new element would exceed the collection's capacity, this
+    /// method raises an [`IdLimit`].
+    ///
+    /// [`IdLimit`]: enum.Error.html#IdLimit
+    fn insert(&mut self, e: ConnectionIdEntry) -> Result<()> {
+        // Ensure we don't have duplicates.
+        match self.get_mut(e.seq) {
+            Some(oe) => *oe = e,
+            None => {
+                if self.inner.len() == self.capacity {
+                    return Err(Error::IdLimit);
+                }
+                self.inner.push_back(e);
+            },
+        };
+        Ok(())
+    }
+
+    /// Removes all the elements in the collection and inserts the provided one.
+    fn clear_and_insert(&mut self, e: ConnectionIdEntry) {
+        self.inner.clear();
+        self.inner.push_back(e);
+    }
+
+    /// Removes the element in the collection having the provided `seq`.
+    ///
+    /// If this method is called when there remains a single element in the
+    /// collection, this method raises an [`OutOfIdentifiers`].
+    ///
+    /// Returns `Some` if the element was in the collection and removed, or
+    /// `None` if it was not and nothing was modified.
+    ///
+    /// [`OutOfIdentifiers`]: enum.Error.html#OutOfIdentifiers
+    fn remove(&mut self, seq: u64) -> Result<Option<ConnectionIdEntry>> {
+        if self.inner.len() <= 1 {
+            return Err(Error::OutOfIdentifiers);
+        }
+
+        Ok(self
+            .inner
+            .iter()
+            .position(|e| e.seq == seq)
+            .and_then(|index| self.inner.remove(index)))
+    }
+
+    /// Removes all the elements in the collection whose `seq` is lower than the
+    /// provided one, and inserts `e` in the collection.
+    ///
+    /// For each removed element `re`, `f(re)` is called.
+    ///
+    /// If the inserted element has a `seq` lower than the one used to remove
+    /// elements, it raises an [`OutOfIdentifiers`].
+    ///
+    /// [`OutOfIdentifiers`]: enum.Error.html#OutOfIdentifiers
+    fn remove_lower_than_and_insert<F>(
+        &mut self, seq: u64, e: ConnectionIdEntry, mut f: F,
+    ) -> Result<()>
+    where
+        F: FnMut(&ConnectionIdEntry),
+    {
+        // The insert entry MUST have a sequence higher or equal to the ones
+        // being retired.
+        if e.seq < seq {
+            return Err(Error::OutOfIdentifiers);
+        }
+
+        // To avoid exceeding the capacity of the inner `VecDeque`, we first
+        // remove the elements and then insert the new one.
+        self.inner.retain(|e| {
+            if e.seq < seq {
+                f(e);
+                false
+            } else {
+                true
+            }
+        });
+
+        // Note that if no element has been retired and the `VecDeque` reaches
+        // its capacity limit, this will raise an `IdLimit`.
+        self.insert(e)
+    }
+}
+
+/// An iterator over QUIC Connection IDs.
+pub struct ConnectionIdIter {
+    cids: VecDeque<ConnectionId<'static>>,
+}
+
+impl Iterator for ConnectionIdIter {
+    type Item = ConnectionId<'static>;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        self.cids.pop_front()
+    }
+}
+
+impl ExactSizeIterator for ConnectionIdIter {
+    #[inline]
+    fn len(&self) -> usize {
+        self.cids.len()
+    }
+}
+
+#[derive(Default)]
+pub struct ConnectionIdentifiers {
+    /// All the Destination Connection IDs provided by our peer.
+    dcids: BoundedNonEmptyConnectionIdVecDeque,
+
+    /// All the Source Connection IDs we provide to our peer.
+    scids: BoundedNonEmptyConnectionIdVecDeque,
+
+    /// Source Connection IDs that should be announced to the peer.
+    advertise_new_scid_seqs: VecDeque<u64>,
+
+    /// Retired Destination Connection IDs that should be announced to the peer.
+    retire_dcid_seqs: VecDeque<u64>,
+
+    /// Retired Source Connection IDs that should be notified to the
+    /// application.
+    retired_scids: VecDeque<ConnectionId<'static>>,
+
+    /// Largest "Retire Prior To" we received from the peer.
+    largest_peer_retire_prior_to: u64,
+
+    /// Largest sequence number we received from the peer.
+    largest_destination_seq: u64,
+
+    /// Next sequence number to use.
+    next_scid_seq: u64,
+
+    /// "Retire Prior To" value to advertise to the peer.
+    retire_prior_to: u64,
+
+    /// The maximum number of source Connection IDs our peer allows us.
+    source_conn_id_limit: usize,
+
+    /// Does the host use zero-length source Connection ID.
+    zero_length_scid: bool,
+
+    /// Does the host use zero-length destination Connection ID.
+    zero_length_dcid: bool,
+}
+
+impl ConnectionIdentifiers {
+    /// Creates a new `ConnectionIdentifiers` with the specified destination
+    /// connection ID limit and initial source Connection ID. The destination
+    /// Connection ID is set to the empty one.
+    pub fn new(
+        mut destination_conn_id_limit: usize, initial_scid: &ConnectionId,
+        initial_path_id: usize, reset_token: Option<u128>,
+    ) -> ConnectionIdentifiers {
+        // It must be at least 2.
+        if destination_conn_id_limit < 2 {
+            destination_conn_id_limit = 2;
+        }
+
+        // Initially, the limit of active source connection IDs is 2.
+        let source_conn_id_limit = 2;
+
+        // Record the zero-length SCID status.
+        let zero_length_scid = initial_scid.is_empty();
+
+        let initial_scid =
+            ConnectionId::from_ref(initial_scid.as_ref()).into_owned();
+
+        // We need to track up to (2 * source_conn_id_limit - 1) source
+        // Connection IDs when the host wants to force their renewal.
+        let scids = BoundedNonEmptyConnectionIdVecDeque::new(
+            2 * source_conn_id_limit - 1,
+            ConnectionIdEntry {
+                cid: initial_scid,
+                seq: 0,
+                reset_token,
+                path_id: Some(initial_path_id),
+            },
+        );
+
+        let dcids = BoundedNonEmptyConnectionIdVecDeque::new(
+            destination_conn_id_limit,
+            ConnectionIdEntry {
+                cid: ConnectionId::default(),
+                seq: 0,
+                reset_token: None,
+                path_id: Some(initial_path_id),
+            },
+        );
+
+        // Because we already inserted the initial SCID.
+        let next_scid_seq = 1;
+        ConnectionIdentifiers {
+            scids,
+            dcids,
+            retired_scids: VecDeque::new(),
+            next_scid_seq,
+            source_conn_id_limit,
+            zero_length_scid,
+            ..Default::default()
+        }
+    }
+
+    /// Sets the maximum number of source connection IDs our peer allows us.
+    pub fn set_source_conn_id_limit(&mut self, v: u64) {
+        // Bound conn id limit so our scids queue sizing is valid.
+        let v = std::cmp::min(v, (usize::MAX / 2) as u64) as usize;
+
+        // It must be at least 2.
+        if v >= 2 {
+            self.source_conn_id_limit = v;
+            // We need to track up to (2 * source_conn_id_limit - 1) source
+            // Connection IDs when the host wants to force their renewal.
+            self.scids.resize(2 * v - 1);
+        }
+    }
+
+    /// Gets the destination Connection ID associated with the provided sequence
+    /// number.
+    #[inline]
+    pub fn get_dcid(&self, seq_num: u64) -> Result<&ConnectionIdEntry> {
+        self.dcids.get(seq_num).ok_or(Error::InvalidState)
+    }
+
+    /// Gets the source Connection ID associated with the provided sequence
+    /// number.
+    #[inline]
+    pub fn get_scid(&self, seq_num: u64) -> Result<&ConnectionIdEntry> {
+        self.scids.get(seq_num).ok_or(Error::InvalidState)
+    }
+
+    /// Adds a new source identifier, and indicates whether it should be
+    /// advertised through a `NEW_CONNECTION_ID` frame or not.
+    ///
+    /// At any time, the peer cannot have more Destination Connection IDs than
+    /// the maximum number of active Connection IDs it negotiated. In such case
+    /// (i.e., when [`active_source_cids()`] - `peer_active_conn_id_limit` = 0,
+    /// if the caller agrees to request the removal of previous connection IDs,
+    /// it sets the `retire_if_needed` parameter. Otherwise, an [`IdLimit`] is
+    /// returned.
+    ///
+    /// Note that setting `retire_if_needed` does not prevent this function from
+    /// returning an [`IdLimit`] in the case the caller wants to retire still
+    /// unannounced Connection IDs.
+    ///
+    /// When setting the initial Source Connection ID, the `reset_token` may be
+    /// `None`. However, other Source CIDs must have an associated
+    /// `reset_token`. Providing `None` as the `reset_token` for non-initial
+    /// SCIDs raises an [`InvalidState`].
+    ///
+    /// In the case the provided `cid` is already present, it does not add it.
+    /// If the provided `reset_token` differs from the one already registered,
+    /// returns an `InvalidState`.
+    ///
+    /// Returns the sequence number associated to that new source identifier.
+    ///
+    /// [`active_source_cids()`]:  struct.ConnectionIdentifiers.html#method.active_source_cids
+    /// [`InvalidState`]: enum.Error.html#InvalidState
+    /// [`IdLimit`]: enum.Error.html#IdLimit
+    pub fn new_scid(
+        &mut self, cid: ConnectionId<'static>, reset_token: Option<u128>,
+        advertise: bool, path_id: Option<usize>, retire_if_needed: bool,
+    ) -> Result<u64> {
+        if self.zero_length_scid {
+            return Err(Error::InvalidState);
+        }
+
+        // Check whether the number of source Connection IDs does not exceed the
+        // limit. If the host agrees to retire old CIDs, it can store up to
+        // (2 * source_active_conn_id - 1) source CIDs. This limit is enforced
+        // when calling `self.scids.insert()`.
+        if self.scids.len() >= self.source_conn_id_limit {
+            if !retire_if_needed {
+                return Err(Error::IdLimit);
+            }
+
+            // We need to retire the lowest one.
+            self.retire_prior_to = self.lowest_usable_scid_seq()? + 1;
+        }
+
+        let seq = self.next_scid_seq;
+
+        if reset_token.is_none() && seq != 0 {
+            return Err(Error::InvalidState);
+        }
+
+        // Check first that the SCID has not been inserted before.
+        if let Some(e) = self.scids.iter().find(|e| e.cid == cid) {
+            if e.reset_token != reset_token {
+                return Err(Error::InvalidState);
+            }
+            return Ok(e.seq);
+        }
+
+        self.scids.insert(ConnectionIdEntry {
+            cid,
+            seq,
+            reset_token,
+            path_id,
+        })?;
+        self.next_scid_seq += 1;
+
+        self.mark_advertise_new_scid_seq(seq, advertise);
+
+        Ok(seq)
+    }
+
+    /// Sets the initial destination identifier.
+    pub fn set_initial_dcid(
+        &mut self, cid: ConnectionId<'static>, reset_token: Option<u128>,
+        path_id: Option<usize>,
+    ) {
+        // Record the zero-length DCID status.
+        self.zero_length_dcid = cid.is_empty();
+        self.dcids.clear_and_insert(ConnectionIdEntry {
+            cid,
+            seq: 0,
+            reset_token,
+            path_id,
+        });
+    }
+
+    /// Adds a new Destination Connection ID (originating from a
+    /// NEW_CONNECTION_ID frame) and process all its related metadata.
+    ///
+    /// Returns an error if the provided Connection ID or its metadata are
+    /// invalid.
+    ///
+    /// Returns a list of tuples (DCID sequence number, Path ID), containing the
+    /// sequence number of retired DCIDs that were linked to their respective
+    /// Path ID.
+    pub fn new_dcid(
+        &mut self, cid: ConnectionId<'static>, seq: u64, reset_token: u128,
+        retire_prior_to: u64,
+    ) -> Result<Vec<(u64, usize)>> {
+        if self.zero_length_dcid {
+            return Err(Error::InvalidState);
+        }
+
+        let mut retired_path_ids = Vec::new();
+        // If an endpoint receives a NEW_CONNECTION_ID frame that repeats a
+        // previously issued connection ID with a different Stateless Reset
+        // Token field value or a different Sequence Number field value, or if a
+        // sequence number is used for different connection IDs, the endpoint
+        // MAY treat that receipt as a connection error of type
+        // PROTOCOL_VIOLATION.
+        if let Some(e) = self.dcids.iter().find(|e| e.cid == cid || e.seq == seq)
+        {
+            if e.cid != cid || e.seq != seq || e.reset_token != Some(reset_token)
+            {
+                return Err(Error::InvalidFrame);
+            }
+            // The identifier is already there, nothing to do.
+            return Ok(retired_path_ids);
+        }
+
+        // The value in the Retire Prior To field MUST be less than or equal to
+        // the value in the Sequence Number field. Receiving a value in the
+        // Retire Prior To field that is greater than that in the Sequence
+        // Number field MUST be treated as a connection error of type
+        // FRAME_ENCODING_ERROR.
+        if retire_prior_to > seq {
+            return Err(Error::InvalidFrame);
+        }
+
+        // An endpoint that receives a NEW_CONNECTION_ID frame with a sequence
+        // number smaller than the Retire Prior To field of a previously
+        // received NEW_CONNECTION_ID frame MUST send a corresponding
+        // RETIRE_CONNECTION_ID frame that retires the newly received connection
+        // ID, unless it has already done so for that sequence number.
+        if seq < self.largest_peer_retire_prior_to &&
+            !self.retire_dcid_seqs.contains(&seq)
+        {
+            self.retire_dcid_seqs.push_back(seq);
+            return Ok(retired_path_ids);
+        }
+
+        if seq > self.largest_destination_seq {
+            self.largest_destination_seq = seq;
+        }
+
+        let new_entry = ConnectionIdEntry {
+            cid: cid.clone(),
+            seq,
+            reset_token: Some(reset_token),
+            path_id: None,
+        };
+
+        // A receiver MUST ignore any Retire Prior To fields that do not
+        // increase the largest received Retire Prior To value.
+        //
+        // After processing a NEW_CONNECTION_ID frame and adding and retiring
+        // active connection IDs, if the number of active connection IDs exceeds
+        // the value advertised in its active_connection_id_limit transport
+        // parameter, an endpoint MUST close the connection with an error of type
+        // CONNECTION_ID_LIMIT_ERROR.
+        if retire_prior_to > self.largest_peer_retire_prior_to {
+            let retired = &mut self.retire_dcid_seqs;
+            self.dcids.remove_lower_than_and_insert(
+                retire_prior_to,
+                new_entry,
+                |e| {
+                    retired.push_back(e.seq);
+
+                    if let Some(pid) = e.path_id {
+                        retired_path_ids.push((e.seq, pid));
+                    }
+                },
+            )?;
+            self.largest_peer_retire_prior_to = retire_prior_to;
+        } else {
+            self.dcids.insert(new_entry)?;
+        }
+
+        Ok(retired_path_ids)
+    }
+
+    /// Retires the Source Connection ID having the provided sequence number.
+    ///
+    /// In case the retired Connection ID is the same as the one used by the
+    /// packet requesting the retiring, or if the retired sequence number is
+    /// greater than any previously advertised sequence numbers, it returns an
+    /// [`InvalidState`].
+    ///
+    /// Returns the path ID that was associated to the retired CID, if any.
+    ///
+    /// [`InvalidState`]: enum.Error.html#InvalidState
+    pub fn retire_scid(
+        &mut self, seq: u64, pkt_dcid: &ConnectionId,
+    ) -> Result<Option<usize>> {
+        if seq >= self.next_scid_seq {
+            return Err(Error::InvalidState);
+        }
+
+        let pid = if let Some(e) = self.scids.remove(seq)? {
+            if e.cid == *pkt_dcid {
+                return Err(Error::InvalidState);
+            }
+
+            // Notifies the application.
+            self.retired_scids.push_back(e.cid);
+
+            // Retiring this SCID may increase the retire prior to.
+            let lowest_scid_seq = self.lowest_usable_scid_seq()?;
+            self.retire_prior_to = lowest_scid_seq;
+
+            e.path_id
+        } else {
+            None
+        };
+
+        Ok(pid)
+    }
+
+    /// Retires the Destination Connection ID having the provided sequence
+    /// number.
+    ///
+    /// If the caller tries to retire the last destination Connection ID, this
+    /// method triggers an [`OutOfIdentifiers`].
+    ///
+    /// If the caller tries to retire a non-existing Destination Connection
+    /// ID sequence number, this method returns an [`InvalidState`].
+    ///
+    /// Returns the path ID that was associated to the retired CID, if any.
+    ///
+    /// [`OutOfIdentifiers`]: enum.Error.html#OutOfIdentifiers
+    /// [`InvalidState`]: enum.Error.html#InvalidState
+    pub fn retire_dcid(&mut self, seq: u64) -> Result<Option<usize>> {
+        if self.zero_length_dcid {
+            return Err(Error::InvalidState);
+        }
+
+        let e = self.dcids.remove(seq)?.ok_or(Error::InvalidState)?;
+
+        self.retire_dcid_seqs.push_back(seq);
+
+        Ok(e.path_id)
+    }
+
+    /// Updates the Source Connection ID entry with the provided sequence number
+    /// to indicate that it is now linked to the provided path ID.
+    pub fn link_scid_to_path_id(
+        &mut self, dcid_seq: u64, path_id: usize,
+    ) -> Result<()> {
+        let e = self.scids.get_mut(dcid_seq).ok_or(Error::InvalidState)?;
+        e.path_id = Some(path_id);
+        Ok(())
+    }
+
+    /// Updates the Destination Connection ID entry with the provided sequence
+    /// number to indicate that it is now linked to the provided path ID.
+    pub fn link_dcid_to_path_id(
+        &mut self, dcid_seq: u64, path_id: usize,
+    ) -> Result<()> {
+        let e = self.dcids.get_mut(dcid_seq).ok_or(Error::InvalidState)?;
+        e.path_id = Some(path_id);
+        Ok(())
+    }
+
+    /// Gets the minimum Source Connection ID sequence number whose removal has
+    /// not been requested yet.
+    #[inline]
+    pub fn lowest_usable_scid_seq(&self) -> Result<u64> {
+        self.scids
+            .iter()
+            .filter_map(|e| {
+                if e.seq >= self.retire_prior_to {
+                    Some(e.seq)
+                } else {
+                    None
+                }
+            })
+            .min()
+            .ok_or(Error::InvalidState)
+    }
+
+    /// Gets the lowest Destination Connection ID sequence number that is not
+    /// associated to a path.
+    #[inline]
+    pub fn lowest_available_dcid_seq(&self) -> Option<u64> {
+        self.dcids
+            .iter()
+            .filter_map(|e| {
+                if e.path_id.is_none() {
+                    Some(e.seq)
+                } else {
+                    None
+                }
+            })
+            .min()
+    }
+
+    /// Finds the sequence number of the Source Connection ID having the
+    /// provided value and the identifier of the path using it, if any.
+    #[inline]
+    pub fn find_scid_seq(
+        &self, scid: &ConnectionId,
+    ) -> Option<(u64, Option<usize>)> {
+        self.scids.iter().find_map(|e| {
+            if e.cid == *scid {
+                Some((e.seq, e.path_id))
+            } else {
+                None
+            }
+        })
+    }
+
+    /// Returns the number of Source Connection IDs that have not been
+    /// assigned to a path yet.
+    ///
+    /// Note that this function is only meaningful if the host uses non-zero
+    /// length Source Connection IDs.
+    #[inline]
+    pub fn available_scids(&self) -> usize {
+        self.scids.iter().filter(|e| e.path_id.is_none()).count()
+    }
+
+    /// Returns the number of Destination Connection IDs that have not been
+    /// assigned to a path yet.
+    ///
+    /// Note that this function returns 0 if the host uses zero length
+    /// Destination Connection IDs.
+    #[inline]
+    pub fn available_dcids(&self) -> usize {
+        if self.zero_length_dcid() {
+            return 0;
+        }
+        self.dcids.iter().filter(|e| e.path_id.is_none()).count()
+    }
+
+    /// Returns the oldest active source Connection ID of this connection.
+    #[inline]
+    pub fn oldest_scid(&self) -> &ConnectionIdEntry {
+        self.scids.get_oldest()
+    }
+
+    /// Returns the oldest known active destination Connection ID of this
+    /// connection.
+    ///
+    /// Note that due to e.g., reordering at reception side, the oldest known
+    /// active destination Connection ID is not necessarily the one having the
+    /// lowest sequence.
+    #[inline]
+    pub fn oldest_dcid(&self) -> &ConnectionIdEntry {
+        self.dcids.get_oldest()
+    }
+
+    /// Adds or remove the source Connection ID sequence number from the
+    /// source Connection ID set that need to be advertised to the peer through
+    /// NEW_CONNECTION_ID frames.
+    #[inline]
+    pub fn mark_advertise_new_scid_seq(
+        &mut self, scid_seq: u64, advertise: bool,
+    ) {
+        if advertise {
+            self.advertise_new_scid_seqs.push_back(scid_seq);
+        } else if let Some(index) = self
+            .advertise_new_scid_seqs
+            .iter()
+            .position(|s| *s == scid_seq)
+        {
+            self.advertise_new_scid_seqs.remove(index);
+        }
+    }
+
+    /// Adds or remove the destination Connection ID sequence number from the
+    /// retired destination Connection ID set that need to be advertised to the
+    /// peer through RETIRE_CONNECTION_ID frames.
+    #[inline]
+    pub fn mark_retire_dcid_seq(&mut self, dcid_seq: u64, retire: bool) {
+        if retire {
+            self.retire_dcid_seqs.push_back(dcid_seq);
+        } else if let Some(index) =
+            self.retire_dcid_seqs.iter().position(|s| *s == dcid_seq)
+        {
+            self.retire_dcid_seqs.remove(index);
+        }
+    }
+
+    /// Gets a source Connection ID's sequence number requiring advertising it
+    /// to the peer through NEW_CONNECTION_ID frame, if any.
+    ///
+    /// If `Some`, it always returns the same value until it has been removed
+    /// using `mark_advertise_new_scid_seq`.
+    #[inline]
+    pub fn next_advertise_new_scid_seq(&self) -> Option<u64> {
+        self.advertise_new_scid_seqs.front().copied()
+    }
+
+    /// Gets a destination Connection IDs's sequence number that need to send
+    /// RETIRE_CONNECTION_ID frames.
+    ///
+    /// If `Some`, it always returns the same value until it has been removed
+    /// using `mark_retire_dcid_seq`.
+    #[inline]
+    pub fn next_retire_dcid_seq(&self) -> Option<u64> {
+        self.retire_dcid_seqs.front().copied()
+    }
+
+    /// Returns true if there are new source Connection IDs to advertise.
+    #[inline]
+    pub fn has_new_scids(&self) -> bool {
+        !self.advertise_new_scid_seqs.is_empty()
+    }
+
+    /// Returns true if there are retired destination Connection IDs to\
+    /// advertise.
+    #[inline]
+    pub fn has_retire_dcids(&self) -> bool {
+        !self.retire_dcid_seqs.is_empty()
+    }
+
+    /// Returns whether zero-length source CIDs are used.
+    #[inline]
+    pub fn zero_length_scid(&self) -> bool {
+        self.zero_length_scid
+    }
+
+    /// Returns whether zero-length destination CIDs are used.
+    #[inline]
+    pub fn zero_length_dcid(&self) -> bool {
+        self.zero_length_dcid
+    }
+
+    /// Gets the NEW_CONNECTION_ID frame related to the source connection ID
+    /// with sequence `seq_num`.
+    pub fn get_new_connection_id_frame_for(
+        &self, seq_num: u64,
+    ) -> Result<frame::Frame> {
+        let e = self.scids.get(seq_num).ok_or(Error::InvalidState)?;
+        Ok(frame::Frame::NewConnectionId {
+            seq_num,
+            retire_prior_to: self.retire_prior_to,
+            conn_id: e.cid.to_vec(),
+            reset_token: e.reset_token.ok_or(Error::InvalidState)?.to_be_bytes(),
+        })
+    }
+
+    /// Returns the number of source Connection IDs that are active. This is
+    /// only meaningful if the host uses non-zero length Source Connection IDs.
+    #[inline]
+    pub fn active_source_cids(&self) -> usize {
+        self.scids.len()
+    }
+
+    pub fn pop_retired_scid(&mut self) -> Option<ConnectionId<'static>> {
+        self.retired_scids.pop_front()
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::testing::create_cid_and_reset_token;
+
+    #[test]
+    fn ids_new_scids() {
+        let (scid, _) = create_cid_and_reset_token(16);
+        let (dcid, _) = create_cid_and_reset_token(16);
+
+        let mut ids = ConnectionIdentifiers::new(2, &scid, 0, None);
+        ids.set_source_conn_id_limit(3);
+        ids.set_initial_dcid(dcid, None, Some(0));
+
+        assert_eq!(ids.available_dcids(), 0);
+        assert_eq!(ids.available_scids(), 0);
+        assert_eq!(ids.has_new_scids(), false);
+        assert_eq!(ids.next_advertise_new_scid_seq(), None);
+
+        let (scid2, rt2) = create_cid_and_reset_token(16);
+
+        assert_eq!(ids.new_scid(scid2, Some(rt2), true, None, false), Ok(1));
+        assert_eq!(ids.available_dcids(), 0);
+        assert_eq!(ids.available_scids(), 1);
+        assert_eq!(ids.has_new_scids(), true);
+        assert_eq!(ids.next_advertise_new_scid_seq(), Some(1));
+
+        let (scid3, rt3) = create_cid_and_reset_token(16);
+
+        assert_eq!(ids.new_scid(scid3, Some(rt3), true, None, false), Ok(2));
+        assert_eq!(ids.available_dcids(), 0);
+        assert_eq!(ids.available_scids(), 2);
+        assert_eq!(ids.has_new_scids(), true);
+        assert_eq!(ids.next_advertise_new_scid_seq(), Some(1));
+
+        // If now we give another CID, it reports an error since it exceeds the
+        // limit of active CIDs.
+        let (scid4, rt4) = create_cid_and_reset_token(16);
+
+        assert_eq!(
+            ids.new_scid(scid4, Some(rt4), true, None, false),
+            Err(Error::IdLimit),
+        );
+        assert_eq!(ids.available_dcids(), 0);
+        assert_eq!(ids.available_scids(), 2);
+        assert_eq!(ids.has_new_scids(), true);
+        assert_eq!(ids.next_advertise_new_scid_seq(), Some(1));
+
+        // Assume we sent one of them.
+        ids.mark_advertise_new_scid_seq(1, false);
+        assert_eq!(ids.available_dcids(), 0);
+        assert_eq!(ids.available_scids(), 2);
+        assert_eq!(ids.has_new_scids(), true);
+        assert_eq!(ids.next_advertise_new_scid_seq(), Some(2));
+
+        // Send the other.
+        ids.mark_advertise_new_scid_seq(2, false);
+
+        assert_eq!(ids.available_dcids(), 0);
+        assert_eq!(ids.available_scids(), 2);
+        assert_eq!(ids.has_new_scids(), false);
+        assert_eq!(ids.next_advertise_new_scid_seq(), None);
+    }
+
+    #[test]
+    fn new_dcid_event() {
+        let (scid, _) = create_cid_and_reset_token(16);
+        let (dcid, _) = create_cid_and_reset_token(16);
+
+        let mut ids = ConnectionIdentifiers::new(2, &scid, 0, None);
+        ids.set_initial_dcid(dcid, None, Some(0));
+
+        assert_eq!(ids.available_dcids(), 0);
+        assert_eq!(ids.dcids.len(), 1);
+
+        let (dcid2, rt2) = create_cid_and_reset_token(16);
+
+        assert_eq!(
+            ids.new_dcid(dcid2.clone(), 1, rt2, 0),
+            Ok(Vec::<(u64, usize)>::new()),
+        );
+        assert_eq!(ids.available_dcids(), 1);
+        assert_eq!(ids.dcids.len(), 2);
+
+        // Now we assume that the client wants to advertise more source
+        // Connection IDs than the advertised limit. This is valid if it
+        // requests its peer to retire enough Connection IDs to fit within the
+        // limits.
+        let (dcid3, rt3) = create_cid_and_reset_token(16);
+        assert_eq!(ids.new_dcid(dcid3.clone(), 2, rt3, 1), Ok(vec![(0, 0)]));
+        // The CID module does not handle path replacing. Fake it now.
+        ids.link_dcid_to_path_id(1, 0).unwrap();
+        assert_eq!(ids.available_dcids(), 1);
+        assert_eq!(ids.dcids.len(), 2);
+        assert_eq!(ids.has_retire_dcids(), true);
+        assert_eq!(ids.next_retire_dcid_seq(), Some(0));
+
+        // Fake RETIRE_CONNECTION_ID sending.
+        ids.mark_retire_dcid_seq(0, false);
+        assert_eq!(ids.has_retire_dcids(), false);
+        assert_eq!(ids.next_retire_dcid_seq(), None);
+
+        // Now tries to experience CID retirement. If the server tries to remove
+        // non-existing DCIDs, it fails.
+        assert_eq!(ids.retire_dcid(0), Err(Error::InvalidState));
+        assert_eq!(ids.retire_dcid(3), Err(Error::InvalidState));
+        assert_eq!(ids.has_retire_dcids(), false);
+        assert_eq!(ids.dcids.len(), 2);
+
+        // Now it removes DCID with sequence 1.
+        assert_eq!(ids.retire_dcid(1), Ok(Some(0)));
+        // The CID module does not handle path replacing. Fake it now.
+        ids.link_dcid_to_path_id(2, 0).unwrap();
+        assert_eq!(ids.available_dcids(), 0);
+        assert_eq!(ids.has_retire_dcids(), true);
+        assert_eq!(ids.next_retire_dcid_seq(), Some(1));
+        assert_eq!(ids.dcids.len(), 1);
+
+        // Fake RETIRE_CONNECTION_ID sending.
+        ids.mark_retire_dcid_seq(1, false);
+        assert_eq!(ids.has_retire_dcids(), false);
+        assert_eq!(ids.next_retire_dcid_seq(), None);
+
+        // Trying to remove the last DCID triggers an error.
+        assert_eq!(ids.retire_dcid(2), Err(Error::OutOfIdentifiers));
+        assert_eq!(ids.available_dcids(), 0);
+        assert_eq!(ids.has_retire_dcids(), false);
+        assert_eq!(ids.dcids.len(), 1);
+    }
+
+    #[test]
+    fn retire_scids() {
+        let (scid, _) = create_cid_and_reset_token(16);
+        let (dcid, _) = create_cid_and_reset_token(16);
+
+        let mut ids = ConnectionIdentifiers::new(3, &scid, 0, None);
+        ids.set_initial_dcid(dcid, None, Some(0));
+        ids.set_source_conn_id_limit(3);
+
+        let (scid2, rt2) = create_cid_and_reset_token(16);
+        let (scid3, rt3) = create_cid_and_reset_token(16);
+
+        assert_eq!(
+            ids.new_scid(scid2.clone(), Some(rt2), true, None, false),
+            Ok(1),
+        );
+        assert_eq!(ids.scids.len(), 2);
+        assert_eq!(
+            ids.new_scid(scid3.clone(), Some(rt3), true, None, false),
+            Ok(2),
+        );
+        assert_eq!(ids.scids.len(), 3);
+
+        assert_eq!(ids.pop_retired_scid(), None);
+
+        assert_eq!(ids.retire_scid(0, &scid2), Ok(Some(0)));
+
+        assert_eq!(ids.pop_retired_scid(), Some(scid));
+        assert_eq!(ids.pop_retired_scid(), None);
+
+        assert_eq!(ids.retire_scid(1, &scid3), Ok(None));
+
+        assert_eq!(ids.pop_retired_scid(), Some(scid2));
+        assert_eq!(ids.pop_retired_scid(), None);
+    }
+}
diff --git a/src/crypto.rs b/src/crypto.rs
index 6e33957..b873757 100644
--- a/src/crypto.rs
+++ b/src/crypto.rs
@@ -24,16 +24,21 @@
 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+use std::mem::MaybeUninit;
+
 use ring::aead;
 use ring::hkdf;
 
+use libc::c_int;
+use libc::c_void;
+
 use crate::Error;
 use crate::Result;
 
 use crate::packet;
 
 #[repr(C)]
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum Level {
     Initial   = 0,
     ZeroRTT   = 1,
@@ -44,18 +49,16 @@
 impl Level {
     pub fn from_epoch(e: packet::Epoch) -> Level {
         match e {
-            packet::EPOCH_INITIAL => Level::Initial,
+            packet::Epoch::Initial => Level::Initial,
 
-            packet::EPOCH_HANDSHAKE => Level::Handshake,
+            packet::Epoch::Handshake => Level::Handshake,
 
-            packet::EPOCH_APPLICATION => Level::OneRTT,
-
-            _ => unreachable!(),
+            packet::Epoch::Application => Level::OneRTT,
         }
     }
 }
 
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum Algorithm {
     #[allow(non_camel_case_types)]
     AES128_GCM,
@@ -68,11 +71,13 @@
 }
 
 impl Algorithm {
-    fn get_ring_aead(self) -> &'static aead::Algorithm {
+    fn get_evp_aead(self) -> *const EVP_AEAD {
         match self {
-            Algorithm::AES128_GCM => &aead::AES_128_GCM,
-            Algorithm::AES256_GCM => &aead::AES_256_GCM,
-            Algorithm::ChaCha20_Poly1305 => &aead::CHACHA20_POLY1305,
+            Algorithm::AES128_GCM => unsafe { EVP_aead_aes_128_gcm() },
+            Algorithm::AES256_GCM => unsafe { EVP_aead_aes_256_gcm() },
+            Algorithm::ChaCha20_Poly1305 => unsafe {
+                EVP_aead_chacha20_poly1305()
+            },
         }
     }
 
@@ -93,7 +98,11 @@
     }
 
     pub fn key_len(self) -> usize {
-        self.get_ring_aead().key_len()
+        match self {
+            Algorithm::AES128_GCM => 16,
+            Algorithm::AES256_GCM => 32,
+            Algorithm::ChaCha20_Poly1305 => 32,
+        }
     }
 
     pub fn tag_len(self) -> usize {
@@ -101,59 +110,57 @@
             return 0;
         }
 
-        self.get_ring_aead().tag_len()
+        match self {
+            Algorithm::AES128_GCM => 16,
+            Algorithm::AES256_GCM => 16,
+            Algorithm::ChaCha20_Poly1305 => 16,
+        }
     }
 
     pub fn nonce_len(self) -> usize {
-        self.get_ring_aead().nonce_len()
+        match self {
+            Algorithm::AES128_GCM => 12,
+            Algorithm::AES256_GCM => 12,
+            Algorithm::ChaCha20_Poly1305 => 12,
+        }
     }
 }
 
 pub struct Open {
     alg: Algorithm,
 
-    hp_key: aead::quic::HeaderProtectionKey,
+    secret: Vec<u8>,
 
-    key: aead::LessSafeKey,
+    header: HeaderProtectionKey,
 
-    nonce: Vec<u8>,
+    packet: PacketKey,
 }
 
 impl Open {
     pub fn new(
-        alg: Algorithm, key: &[u8], iv: &[u8], hp_key: &[u8],
+        alg: Algorithm, key: &[u8], iv: &[u8], hp_key: &[u8], secret: &[u8],
     ) -> Result<Open> {
         Ok(Open {
-            hp_key: aead::quic::HeaderProtectionKey::new(
-                alg.get_ring_hp(),
-                hp_key,
-            )
-            .map_err(|_| Error::CryptoFail)?,
-
-            key: aead::LessSafeKey::new(
-                aead::UnboundKey::new(alg.get_ring_aead(), key)
-                    .map_err(|_| Error::CryptoFail)?,
-            ),
-
-            nonce: Vec::from(iv),
-
             alg,
+
+            secret: Vec::from(secret),
+
+            header: HeaderProtectionKey::new(alg, hp_key)?,
+
+            packet: PacketKey::new(alg, key, iv)?,
         })
     }
 
     pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result<Open> {
-        let key_len = aead.key_len();
-        let nonce_len = aead.nonce_len();
+        Ok(Open {
+            alg: aead,
 
-        let mut key = vec![0; key_len];
-        let mut iv = vec![0; nonce_len];
-        let mut pn_key = vec![0; key_len];
+            secret: Vec::from(secret),
 
-        derive_pkt_key(aead, &secret, &mut key)?;
-        derive_pkt_iv(aead, &secret, &mut iv)?;
-        derive_hdr_key(aead, &secret, &mut pn_key)?;
+            header: HeaderProtectionKey::from_secret(aead, secret)?,
 
-        Open::new(aead, &key, &iv, &pn_key)
+            packet: PacketKey::from_secret(aead, secret)?,
+        })
     }
 
     pub fn open_with_u64_counter(
@@ -163,16 +170,37 @@
             return Ok(buf.len());
         }
 
-        let nonce = make_nonce(&self.nonce, counter);
+        let tag_len = self.alg().tag_len();
 
-        let ad = aead::Aad::from(ad);
+        let mut out_len = match buf.len().checked_sub(tag_len) {
+            Some(n) => n,
+            None => return Err(Error::CryptoFail),
+        };
 
-        let plain = self
-            .key
-            .open_in_place(nonce, ad, buf)
-            .map_err(|_| Error::CryptoFail)?;
+        let max_out_len = out_len;
 
-        Ok(plain.len())
+        let nonce = make_nonce(&self.packet.nonce, counter);
+
+        let rc = unsafe {
+            EVP_AEAD_CTX_open(
+                &self.packet.ctx,   // ctx
+                buf.as_mut_ptr(),   // out
+                &mut out_len,       // out_len
+                max_out_len,        // max_out_len
+                nonce[..].as_ptr(), // nonce
+                nonce.len(),        // nonce_len
+                buf.as_ptr(),       // inp
+                buf.len(),          // in_len
+                ad.as_ptr(),        // ad
+                ad.len(),           // ad_len
+            )
+        };
+
+        if rc != 1 {
+            return Err(Error::CryptoFail);
+        }
+
+        Ok(out_len)
     }
 
     pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> {
@@ -181,7 +209,8 @@
         }
 
         let mask = self
-            .hp_key
+            .header
+            .hpk
             .new_mask(sample)
             .map_err(|_| Error::CryptoFail)?;
 
@@ -191,82 +220,114 @@
     pub fn alg(&self) -> Algorithm {
         self.alg
     }
+
+    pub fn derive_next_packet_key(&self) -> Result<Open> {
+        let next_secret = derive_next_secret(self.alg, &self.secret)?;
+
+        let next_packet_key = PacketKey::from_secret(self.alg, &next_secret)?;
+
+        Ok(Open {
+            alg: self.alg,
+
+            secret: next_secret,
+
+            header: HeaderProtectionKey::new(self.alg, &self.header.hp_key)?,
+
+            packet: next_packet_key,
+        })
+    }
 }
 
 pub struct Seal {
     alg: Algorithm,
 
-    hp_key: aead::quic::HeaderProtectionKey,
+    secret: Vec<u8>,
 
-    key: aead::LessSafeKey,
+    header: HeaderProtectionKey,
 
-    nonce: Vec<u8>,
+    packet: PacketKey,
 }
 
 impl Seal {
     pub fn new(
-        alg: Algorithm, key: &[u8], iv: &[u8], hp_key: &[u8],
+        alg: Algorithm, key: &[u8], iv: &[u8], hp_key: &[u8], secret: &[u8],
     ) -> Result<Seal> {
         Ok(Seal {
-            hp_key: aead::quic::HeaderProtectionKey::new(
-                alg.get_ring_hp(),
-                hp_key,
-            )
-            .map_err(|_| Error::CryptoFail)?,
-
-            key: aead::LessSafeKey::new(
-                aead::UnboundKey::new(alg.get_ring_aead(), key)
-                    .map_err(|_| Error::CryptoFail)?,
-            ),
-
-            nonce: Vec::from(iv),
-
             alg,
+
+            secret: Vec::from(secret),
+
+            header: HeaderProtectionKey::new(alg, hp_key)?,
+
+            packet: PacketKey::new(alg, key, iv)?,
         })
     }
 
     pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result<Seal> {
-        let key_len = aead.key_len();
-        let nonce_len = aead.nonce_len();
+        Ok(Seal {
+            alg: aead,
 
-        let mut key = vec![0; key_len];
-        let mut iv = vec![0; nonce_len];
-        let mut pn_key = vec![0; key_len];
+            secret: Vec::from(secret),
 
-        derive_pkt_key(aead, &secret, &mut key)?;
-        derive_pkt_iv(aead, &secret, &mut iv)?;
-        derive_hdr_key(aead, &secret, &mut pn_key)?;
+            header: HeaderProtectionKey::from_secret(aead, secret)?,
 
-        Seal::new(aead, &key, &iv, &pn_key)
+            packet: PacketKey::from_secret(aead, secret)?,
+        })
     }
 
     pub fn seal_with_u64_counter(
-        &self, counter: u64, ad: &[u8], buf: &mut [u8],
-    ) -> Result<()> {
+        &self, counter: u64, ad: &[u8], buf: &mut [u8], in_len: usize,
+        extra_in: Option<&[u8]>,
+    ) -> Result<usize> {
         if cfg!(feature = "fuzzing") {
-            return Ok(());
+            if let Some(extra) = extra_in {
+                buf[in_len..in_len + extra.len()].copy_from_slice(extra);
+                return Ok(in_len + extra.len());
+            }
+
+            return Ok(in_len);
         }
 
-        let nonce = make_nonce(&self.nonce, counter);
-
-        let ad = aead::Aad::from(ad);
-
         let tag_len = self.alg().tag_len();
 
-        let in_out_len =
-            buf.len().checked_sub(tag_len).ok_or(Error::CryptoFail)?;
+        let mut out_tag_len = tag_len;
 
-        let (in_out, tag_out) = buf.split_at_mut(in_out_len);
+        let (extra_in_ptr, extra_in_len) = match extra_in {
+            Some(v) => (v.as_ptr(), v.len()),
 
-        let tag = self
-            .key
-            .seal_in_place_separate_tag(nonce, ad, in_out)
-            .map_err(|_| Error::CryptoFail)?;
+            None => (std::ptr::null(), 0),
+        };
 
-        // Append the AEAD tag to the end of the sealed buffer.
-        tag_out.copy_from_slice(tag.as_ref());
+        // Make sure all the outputs combined fit in the buffer.
+        if in_len + tag_len + extra_in_len > buf.len() {
+            return Err(Error::CryptoFail);
+        }
 
-        Ok(())
+        let nonce = make_nonce(&self.packet.nonce, counter);
+
+        let rc = unsafe {
+            EVP_AEAD_CTX_seal_scatter(
+                &self.packet.ctx,           // ctx
+                buf.as_mut_ptr(),           // out
+                buf[in_len..].as_mut_ptr(), // out_tag
+                &mut out_tag_len,           // out_tag_len
+                tag_len + extra_in_len,     // max_out_tag_len
+                nonce[..].as_ptr(),         // nonce
+                nonce.len(),                // nonce_len
+                buf.as_ptr(),               // inp
+                in_len,                     // in_len
+                extra_in_ptr,               // extra_in
+                extra_in_len,               // extra_in_len
+                ad.as_ptr(),                // ad
+                ad.len(),                   // ad_len
+            )
+        };
+
+        if rc != 1 {
+            return Err(Error::CryptoFail);
+        }
+
+        Ok(in_len + out_tag_len)
     }
 
     pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> {
@@ -275,7 +336,8 @@
         }
 
         let mask = self
-            .hp_key
+            .header
+            .hpk
             .new_mask(sample)
             .map_err(|_| Error::CryptoFail)?;
 
@@ -285,49 +347,146 @@
     pub fn alg(&self) -> Algorithm {
         self.alg
     }
+
+    pub fn derive_next_packet_key(&self) -> Result<Seal> {
+        let next_secret = derive_next_secret(self.alg, &self.secret)?;
+
+        let next_packet_key = PacketKey::from_secret(self.alg, &next_secret)?;
+
+        Ok(Seal {
+            alg: self.alg,
+
+            secret: next_secret,
+
+            header: HeaderProtectionKey::new(self.alg, &self.header.hp_key)?,
+
+            packet: next_packet_key,
+        })
+    }
+}
+
+pub struct HeaderProtectionKey {
+    hpk: aead::quic::HeaderProtectionKey,
+
+    hp_key: Vec<u8>,
+}
+
+impl HeaderProtectionKey {
+    pub fn new(alg: Algorithm, hp_key: &[u8]) -> Result<Self> {
+        aead::quic::HeaderProtectionKey::new(alg.get_ring_hp(), hp_key)
+            .map(|hpk| Self {
+                hpk,
+                hp_key: Vec::from(hp_key),
+            })
+            .map_err(|_| Error::CryptoFail)
+    }
+
+    pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result<Self> {
+        let key_len = aead.key_len();
+
+        let mut hp_key = vec![0; key_len];
+
+        derive_hdr_key(aead, secret, &mut hp_key)?;
+
+        Self::new(aead, &hp_key)
+    }
+}
+
+pub struct PacketKey {
+    ctx: EVP_AEAD_CTX,
+
+    nonce: Vec<u8>,
+}
+
+impl PacketKey {
+    pub fn new(alg: Algorithm, key: &[u8], iv: &[u8]) -> Result<Self> {
+        Ok(Self {
+            ctx: make_aead_ctx(alg, key)?,
+
+            nonce: Vec::from(iv),
+        })
+    }
+
+    pub fn from_secret(aead: Algorithm, secret: &[u8]) -> Result<Self> {
+        let key_len = aead.key_len();
+        let nonce_len = aead.nonce_len();
+
+        let mut key = vec![0; key_len];
+        let mut iv = vec![0; nonce_len];
+
+        derive_pkt_key(aead, secret, &mut key)?;
+        derive_pkt_iv(aead, secret, &mut iv)?;
+
+        Self::new(aead, &key, &iv)
+    }
 }
 
 pub fn derive_initial_key_material(
     cid: &[u8], version: u32, is_server: bool,
 ) -> Result<(Open, Seal)> {
-    let mut secret = [0; 32];
+    let mut client_secret = [0; 32];
+    let mut server_secret = [0; 32];
 
     let aead = Algorithm::AES128_GCM;
 
     let key_len = aead.key_len();
     let nonce_len = aead.nonce_len();
 
-    let initial_secret = derive_initial_secret(&cid, version);
+    let initial_secret = derive_initial_secret(cid, version);
 
     // Client.
     let mut client_key = vec![0; key_len];
     let mut client_iv = vec![0; nonce_len];
     let mut client_hp_key = vec![0; key_len];
 
-    derive_client_initial_secret(&initial_secret, &mut secret)?;
-    derive_pkt_key(aead, &secret, &mut client_key)?;
-    derive_pkt_iv(aead, &secret, &mut client_iv)?;
-    derive_hdr_key(aead, &secret, &mut client_hp_key)?;
+    derive_client_initial_secret(&initial_secret, &mut client_secret)?;
+    derive_pkt_key(aead, &client_secret, &mut client_key)?;
+    derive_pkt_iv(aead, &client_secret, &mut client_iv)?;
+    derive_hdr_key(aead, &client_secret, &mut client_hp_key)?;
 
     // Server.
     let mut server_key = vec![0; key_len];
     let mut server_iv = vec![0; nonce_len];
     let mut server_hp_key = vec![0; key_len];
 
-    derive_server_initial_secret(&initial_secret, &mut secret)?;
-    derive_pkt_key(aead, &secret, &mut server_key)?;
-    derive_pkt_iv(aead, &secret, &mut server_iv)?;
-    derive_hdr_key(aead, &secret, &mut server_hp_key)?;
+    derive_server_initial_secret(&initial_secret, &mut server_secret)?;
+    derive_pkt_key(aead, &server_secret, &mut server_key)?;
+    derive_pkt_iv(aead, &server_secret, &mut server_iv)?;
+    derive_hdr_key(aead, &server_secret, &mut server_hp_key)?;
 
     let (open, seal) = if is_server {
         (
-            Open::new(aead, &client_key, &client_iv, &client_hp_key)?,
-            Seal::new(aead, &server_key, &server_iv, &server_hp_key)?,
+            Open::new(
+                aead,
+                &client_key,
+                &client_iv,
+                &client_hp_key,
+                &client_secret,
+            )?,
+            Seal::new(
+                aead,
+                &server_key,
+                &server_iv,
+                &server_hp_key,
+                &server_secret,
+            )?,
         )
     } else {
         (
-            Open::new(aead, &server_key, &server_iv, &server_hp_key)?,
-            Seal::new(aead, &client_key, &client_iv, &client_hp_key)?,
+            Open::new(
+                aead,
+                &server_key,
+                &server_iv,
+                &server_hp_key,
+                &server_secret,
+            )?,
+            Seal::new(
+                aead,
+                &client_key,
+                &client_iv,
+                &client_hp_key,
+                &client_secret,
+            )?,
         )
     };
 
@@ -373,6 +532,17 @@
     hkdf_expand_label(prk, LABEL, out)
 }
 
+fn derive_next_secret(aead: Algorithm, secret: &[u8]) -> Result<Vec<u8>> {
+    const LABEL: &[u8] = b"quic ku";
+
+    let mut next_secret = vec![0; secret.len()];
+
+    let secret_prk = hkdf::Prk::new_less_safe(aead.get_ring_digest(), secret);
+    hkdf_expand_label(&secret_prk, LABEL, &mut next_secret)?;
+
+    Ok(next_secret)
+}
+
 pub fn derive_hdr_key(
     aead: Algorithm, secret: &[u8], out: &mut [u8],
 ) -> Result<()> {
@@ -418,6 +588,31 @@
     hkdf_expand_label(&secret, LABEL, &mut out[..nonce_len])
 }
 
+fn make_aead_ctx(alg: Algorithm, key: &[u8]) -> Result<EVP_AEAD_CTX> {
+    let mut ctx = MaybeUninit::uninit();
+
+    let ctx = unsafe {
+        let aead = alg.get_evp_aead();
+
+        let rc = EVP_AEAD_CTX_init(
+            ctx.as_mut_ptr(),
+            aead,
+            key.as_ptr(),
+            alg.key_len(),
+            alg.tag_len(),
+            std::ptr::null_mut(),
+        );
+
+        if rc != 1 {
+            return Err(Error::CryptoFail);
+        }
+
+        ctx.assume_init()
+    };
+
+    Ok(ctx)
+}
+
 fn hkdf_expand_label(
     prk: &hkdf::Prk, label: &[u8], out: &mut [u8],
 ) -> Result<()> {
@@ -436,9 +631,9 @@
     Ok(())
 }
 
-fn make_nonce(iv: &[u8], counter: u64) -> aead::Nonce {
+fn make_nonce(iv: &[u8], counter: u64) -> [u8; aead::NONCE_LEN] {
     let mut nonce = [0; aead::NONCE_LEN];
-    nonce.copy_from_slice(&iv);
+    nonce.copy_from_slice(iv);
 
     // XOR the last bytes of the IV with the counter. This is equivalent to
     // left-padding the counter with zero bytes.
@@ -446,7 +641,7 @@
         *a ^= b;
     }
 
-    aead::Nonce::assume_unique_for_key(nonce)
+    nonce
 }
 
 // The ring HKDF expand() API does not accept an arbitrary output length, so we
@@ -460,6 +655,49 @@
     }
 }
 
+#[allow(non_camel_case_types)]
+#[repr(transparent)]
+struct EVP_AEAD(c_void);
+
+// NOTE: This structure is copied from <openssl/aead.h> in order to be able to
+// statically allocate it. While it is not often modified upstream, it needs to
+// be kept in sync.
+#[repr(C)]
+struct EVP_AEAD_CTX {
+    aead: libc::uintptr_t,
+    opaque: [u8; 580],
+    alignment: u64,
+    tag_len: u8,
+}
+
+extern {
+    // EVP_AEAD
+    fn EVP_aead_aes_128_gcm() -> *const EVP_AEAD;
+
+    fn EVP_aead_aes_256_gcm() -> *const EVP_AEAD;
+
+    fn EVP_aead_chacha20_poly1305() -> *const EVP_AEAD;
+
+    // EVP_AEAD_CTX
+    fn EVP_AEAD_CTX_init(
+        ctx: *mut EVP_AEAD_CTX, aead: *const EVP_AEAD, key: *const u8,
+        key_len: usize, tag_len: usize, engine: *mut c_void,
+    ) -> c_int;
+
+    fn EVP_AEAD_CTX_open(
+        ctx: *const EVP_AEAD_CTX, out: *mut u8, out_len: *mut usize,
+        max_out_len: usize, nonce: *const u8, nonce_len: usize, inp: *const u8,
+        in_len: usize, ad: *const u8, ad_len: usize,
+    ) -> c_int;
+
+    fn EVP_AEAD_CTX_seal_scatter(
+        ctx: *const EVP_AEAD_CTX, out: *mut u8, out_tag: *mut u8,
+        out_tag_len: *mut usize, max_out_tag_len: usize, nonce: *const u8,
+        nonce_len: usize, inp: *const u8, in_len: usize, extra_in: *const u8,
+        extra_in_len: usize, ad: *const u8, ad_len: usize,
+    ) -> c_int;
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/src/dgram.rs b/src/dgram.rs
index 023df5f..a6b9b5b 100644
--- a/src/dgram.rs
+++ b/src/dgram.rs
@@ -32,7 +32,7 @@
 /// Keeps track of DATAGRAM frames.
 #[derive(Default)]
 pub struct DatagramQueue {
-    queue: VecDeque<Vec<u8>>,
+    queue: Option<VecDeque<Vec<u8>>>,
     queue_max_len: usize,
     queue_bytes_size: usize,
 }
@@ -40,28 +40,31 @@
 impl DatagramQueue {
     pub fn new(queue_max_len: usize) -> Self {
         DatagramQueue {
-            queue: VecDeque::with_capacity(queue_max_len),
+            queue: None,
             queue_bytes_size: 0,
             queue_max_len,
         }
     }
 
-    pub fn push(&mut self, data: &[u8]) -> Result<()> {
+    pub fn push(&mut self, data: Vec<u8>) -> Result<()> {
         if self.is_full() {
             return Err(Error::Done);
         }
 
-        self.queue.push_back(data.to_vec());
         self.queue_bytes_size += data.len();
+        self.queue
+            .get_or_insert_with(Default::default)
+            .push_back(data);
+
         Ok(())
     }
 
     pub fn peek_front_len(&self) -> Option<usize> {
-        self.queue.front().map(|d| d.len())
+        self.queue.as_ref().and_then(|q| q.front().map(|d| d.len()))
     }
 
     pub fn peek_front_bytes(&self, buf: &mut [u8], len: usize) -> Result<usize> {
-        match self.queue.front() {
+        match self.queue.as_ref().and_then(|q| q.front()) {
             Some(d) => {
                 let len = std::cmp::min(len, d.len());
                 if buf.len() < len {
@@ -77,7 +80,7 @@
     }
 
     pub fn pop(&mut self) -> Option<Vec<u8>> {
-        if let Some(d) = self.queue.pop_front() {
+        if let Some(d) = self.queue.as_mut().and_then(|q| q.pop_front()) {
             self.queue_bytes_size = self.queue_bytes_size.saturating_sub(d.len());
             return Some(d);
         }
@@ -86,21 +89,22 @@
     }
 
     pub fn has_pending(&self) -> bool {
-        !self.queue.is_empty()
+        !self.queue.as_ref().map(|q| q.is_empty()).unwrap_or(true)
     }
 
     pub fn purge<F: Fn(&[u8]) -> bool>(&mut self, f: F) {
-        self.queue.retain(|d| !f(d));
-        self.queue_bytes_size =
-            self.queue.iter().fold(0, |total, d| total + d.len());
+        if let Some(q) = self.queue.as_mut() {
+            q.retain(|d| !f(d));
+            self.queue_bytes_size = q.iter().fold(0, |total, d| total + d.len());
+        }
     }
 
     pub fn is_full(&self) -> bool {
-        self.queue.len() == self.queue_max_len
+        self.len() == self.queue_max_len
     }
 
     pub fn len(&self) -> usize {
-        self.queue.len()
+        self.queue.as_ref().map(|q| q.len()).unwrap_or(0)
     }
 
     pub fn byte_size(&self) -> usize {
diff --git a/src/ffi.rs b/src/ffi.rs
index 3290964..38e82c4 100644
--- a/src/ffi.rs
+++ b/src/ffi.rs
@@ -29,7 +29,11 @@
 use std::slice;
 use std::sync::atomic;
 
+use std::net::Ipv4Addr;
+use std::net::Ipv6Addr;
 use std::net::SocketAddr;
+use std::net::SocketAddrV4;
+use std::net::SocketAddrV6;
 
 #[cfg(unix)]
 use std::os::unix::io::FromRawFd;
@@ -43,6 +47,31 @@
 use libc::timespec;
 
 #[cfg(not(windows))]
+use libc::AF_INET;
+#[cfg(windows)]
+use winapi::shared::ws2def::AF_INET;
+
+#[cfg(not(windows))]
+use libc::AF_INET6;
+#[cfg(windows)]
+use winapi::shared::ws2def::AF_INET6;
+
+#[cfg(not(windows))]
+use libc::in_addr;
+#[cfg(windows)]
+use winapi::shared::inaddr::IN_ADDR as in_addr;
+
+#[cfg(not(windows))]
+use libc::in6_addr;
+#[cfg(windows)]
+use winapi::shared::in6addr::IN6_ADDR as in6_addr;
+
+#[cfg(not(windows))]
+use libc::sa_family_t;
+#[cfg(windows)]
+use winapi::shared::ws2def::ADDRESS_FAMILY as sa_family_t;
+
+#[cfg(not(windows))]
 use libc::sockaddr_in;
 #[cfg(windows)]
 use winapi::shared::ws2def::SOCKADDR_IN as sockaddr_in;
@@ -62,21 +91,18 @@
 #[cfg(not(windows))]
 use libc::socklen_t;
 
-#[cfg(not(windows))]
-use libc::AF_INET;
 #[cfg(windows)]
-use winapi::shared::ws2def::AF_INET;
-
-#[cfg(not(windows))]
-use libc::AF_INET6;
+use winapi::shared::in6addr::in6_addr_u;
 #[cfg(windows)]
-use winapi::shared::ws2def::AF_INET6;
+use winapi::shared::inaddr::in_addr_S_un;
+#[cfg(windows)]
+use winapi::shared::ws2ipdef::SOCKADDR_IN6_LH_u;
 
 use crate::*;
 
 #[no_mangle]
 pub extern fn quiche_version() -> *const u8 {
-    //static VERSION: &str = concat!("0.9.0", "\0");
+    //static VERSION: &str = concat!("0.17.1", "\0");
     // ANDROID's build system doesn't support environment variables
     // so we hardcode the package version here.
     static VERSION: &str = concat!("0.6.0", "\0");
@@ -166,6 +192,19 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_config_load_verify_locations_from_directory(
+    config: &mut Config, path: *const c_char,
+) -> c_int {
+    let path = unsafe { ffi::CStr::from_ptr(path).to_str().unwrap() };
+
+    match config.load_verify_locations_from_directory(path) {
+        Ok(_) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
 pub extern fn quiche_config_verify_peer(config: &mut Config, v: bool) {
     config.verify_peer(v);
 }
@@ -186,12 +225,14 @@
 }
 
 #[no_mangle]
+/// Corresponds to the `Config::set_application_protos_wire_format` Rust
+/// function.
 pub extern fn quiche_config_set_application_protos(
     config: &mut Config, protos: *const u8, protos_len: size_t,
 ) -> c_int {
     let protos = unsafe { slice::from_raw_parts(protos, protos_len) };
 
-    match config.set_application_protos(protos) {
+    match config.set_application_protos_wire_format(protos) {
         Ok(_) => 0,
 
         Err(e) => e.to_c() as c_int,
@@ -292,6 +333,11 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_config_enable_pacing(config: &mut Config, v: bool) {
+    config.enable_pacing(v);
+}
+
+#[no_mangle]
 pub extern fn quiche_config_enable_dgram(
     config: &mut Config, enabled: bool, recv_queue_len: size_t,
     send_queue_len: size_t,
@@ -307,6 +353,38 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_config_set_max_connection_window(
+    config: &mut Config, v: u64,
+) {
+    config.set_max_connection_window(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_max_stream_window(config: &mut Config, v: u64) {
+    config.set_max_stream_window(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_active_connection_id_limit(
+    config: &mut Config, v: u64,
+) {
+    config.set_active_connection_id_limit(v);
+}
+
+#[no_mangle]
+pub extern fn quiche_config_set_stateless_reset_token(
+    config: &mut Config, v: *const u8,
+) {
+    let reset_token = unsafe { slice::from_raw_parts(v, 16) };
+    let reset_token = match reset_token.try_into() {
+        Ok(rt) => rt,
+        Err(_) => unreachable!(),
+    };
+    let reset_token = u128::from_be_bytes(reset_token);
+    config.set_stateless_reset_token(Some(reset_token));
+}
+
+#[no_mangle]
 pub extern fn quiche_config_free(config: *mut Config) {
     unsafe { Box::from_raw(config) };
 }
@@ -379,7 +457,8 @@
 #[no_mangle]
 pub extern fn quiche_accept(
     scid: *const u8, scid_len: size_t, odcid: *const u8, odcid_len: size_t,
-    from: &sockaddr, from_len: socklen_t, config: &mut Config,
+    local: &sockaddr, local_len: socklen_t, peer: &sockaddr, peer_len: socklen_t,
+    config: &mut Config,
 ) -> *mut Connection {
     let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
     let scid = ConnectionId::from_ref(scid);
@@ -392,10 +471,11 @@
         None
     };
 
-    let from = std_addr_from_c(from, from_len);
+    let local = std_addr_from_c(local, local_len);
+    let peer = std_addr_from_c(peer, peer_len);
 
-    match accept(&scid, odcid.as_ref(), from, config) {
-        Ok(c) => Box::into_raw(Pin::into_inner(c)),
+    match accept(&scid, odcid.as_ref(), local, peer, config) {
+        Ok(c) => Box::into_raw(Box::new(c)),
 
         Err(_) => ptr::null_mut(),
     }
@@ -403,8 +483,9 @@
 
 #[no_mangle]
 pub extern fn quiche_connect(
-    server_name: *const c_char, scid: *const u8, scid_len: size_t, to: &sockaddr,
-    to_len: socklen_t, config: &mut Config,
+    server_name: *const c_char, scid: *const u8, scid_len: size_t,
+    local: &sockaddr, local_len: socklen_t, peer: &sockaddr, peer_len: socklen_t,
+    config: &mut Config,
 ) -> *mut Connection {
     let server_name = if server_name.is_null() {
         None
@@ -415,10 +496,11 @@
     let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
     let scid = ConnectionId::from_ref(scid);
 
-    let to = std_addr_from_c(to, to_len);
+    let local = std_addr_from_c(local, local_len);
+    let peer = std_addr_from_c(peer, peer_len);
 
-    match connect(server_name, &scid, to, config) {
-        Ok(c) => Box::into_raw(Pin::into_inner(c)),
+    match connect(server_name, &scid, local, peer, config) {
+        Ok(c) => Box::into_raw(Box::new(c)),
 
         Err(_) => ptr::null_mut(),
     }
@@ -477,8 +559,8 @@
 #[no_mangle]
 pub extern fn quiche_conn_new_with_tls(
     scid: *const u8, scid_len: size_t, odcid: *const u8, odcid_len: size_t,
-    peer: &sockaddr, peer_len: socklen_t, config: &mut Config, ssl: *mut c_void,
-    is_server: bool,
+    local: &sockaddr, local_len: socklen_t, peer: &sockaddr, peer_len: socklen_t,
+    config: &mut Config, ssl: *mut c_void, is_server: bool,
 ) -> *mut Connection {
     let scid = unsafe { slice::from_raw_parts(scid, scid_len) };
     let scid = ConnectionId::from_ref(scid);
@@ -491,6 +573,7 @@
         None
     };
 
+    let local = std_addr_from_c(local, local_len);
     let peer = std_addr_from_c(peer, peer_len);
 
     let tls = unsafe { tls::Handshake::from_ptr(ssl) };
@@ -498,12 +581,13 @@
     match Connection::with_tls(
         &scid,
         odcid.as_ref(),
+        local,
         peer,
         config,
         tls,
         is_server,
     ) {
-        Ok(c) => Box::into_raw(Pin::into_inner(c)),
+        Ok(c) => Box::into_raw(Box::new(c)),
 
         Err(_) => ptr::null_mut(),
     }
@@ -607,12 +691,15 @@
 pub struct RecvInfo<'a> {
     from: &'a sockaddr,
     from_len: socklen_t,
+    to: &'a sockaddr,
+    to_len: socklen_t,
 }
 
 impl<'a> From<&RecvInfo<'a>> for crate::RecvInfo {
     fn from(info: &RecvInfo) -> crate::RecvInfo {
         crate::RecvInfo {
             from: std_addr_from_c(info.from, info.from_len),
+            to: std_addr_from_c(info.to, info.to_len),
         }
     }
 }
@@ -636,6 +723,8 @@
 
 #[repr(C)]
 pub struct SendInfo {
+    from: sockaddr_storage,
+    from_len: socklen_t,
     to: sockaddr_storage,
     to_len: socklen_t,
 
@@ -654,6 +743,7 @@
 
     match conn.send(out) {
         Ok((v, info)) => {
+            out_info.from_len = std_addr_to_c(&info.from, &mut out_info.from);
             out_info.to_len = std_addr_to_c(&info.to, &mut out_info.to);
 
             std_time_to_c(&info.at, &mut out_info.at);
@@ -729,7 +819,7 @@
 
 #[no_mangle]
 pub extern fn quiche_conn_stream_capacity(
-    conn: &mut Connection, stream_id: u64,
+    conn: &Connection, stream_id: u64,
 ) -> ssize_t {
     match conn.stream_capacity(stream_id) {
         Ok(v) => v as ssize_t,
@@ -740,14 +830,37 @@
 
 #[no_mangle]
 pub extern fn quiche_conn_stream_readable(
-    conn: &mut Connection, stream_id: u64,
+    conn: &Connection, stream_id: u64,
 ) -> bool {
     conn.stream_readable(stream_id)
 }
 
 #[no_mangle]
+pub extern fn quiche_conn_stream_readable_next(conn: &mut Connection) -> i64 {
+    conn.stream_readable_next().map(|v| v as i64).unwrap_or(-1)
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_writable(
+    conn: &mut Connection, stream_id: u64, len: usize,
+) -> c_int {
+    match conn.stream_writable(stream_id, len) {
+        Ok(true) => 1,
+
+        Ok(false) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_stream_writable_next(conn: &mut Connection) -> i64 {
+    conn.stream_writable_next().map(|v| v as i64).unwrap_or(-1)
+}
+
+#[no_mangle]
 pub extern fn quiche_conn_stream_finished(
-    conn: &mut Connection, stream_id: u64,
+    conn: &Connection, stream_id: u64,
 ) -> bool {
     conn.stream_finished(stream_id)
 }
@@ -813,20 +926,20 @@
 }
 
 #[no_mangle]
-pub extern fn quiche_conn_timeout_as_nanos(conn: &mut Connection) -> u64 {
+pub extern fn quiche_conn_timeout_as_nanos(conn: &Connection) -> u64 {
     match conn.timeout() {
         Some(timeout) => timeout.as_nanos() as u64,
 
-        None => std::u64::MAX,
+        None => u64::MAX,
     }
 }
 
 #[no_mangle]
-pub extern fn quiche_conn_timeout_as_millis(conn: &mut Connection) -> u64 {
+pub extern fn quiche_conn_timeout_as_millis(conn: &Connection) -> u64 {
     match conn.timeout() {
         Some(timeout) => timeout.as_millis() as u64,
 
-        None => std::u64::MAX,
+        None => u64::MAX,
     }
 }
 
@@ -837,7 +950,7 @@
 
 #[no_mangle]
 pub extern fn quiche_conn_trace_id(
-    conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t,
+    conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
 ) {
     let trace_id = conn.trace_id();
 
@@ -847,7 +960,7 @@
 
 #[no_mangle]
 pub extern fn quiche_conn_source_id(
-    conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t,
+    conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
 ) {
     let conn_id = conn.source_id();
     let id = conn_id.as_ref();
@@ -857,7 +970,7 @@
 
 #[no_mangle]
 pub extern fn quiche_conn_destination_id(
-    conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t,
+    conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
 ) {
     let conn_id = conn.destination_id();
     let id = conn_id.as_ref();
@@ -868,7 +981,7 @@
 
 #[no_mangle]
 pub extern fn quiche_conn_application_proto(
-    conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t,
+    conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
 ) {
     let proto = conn.application_proto();
 
@@ -877,8 +990,22 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_conn_peer_cert(
+    conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
+) {
+    match conn.peer_cert() {
+        Some(peer_cert) => {
+            *out = peer_cert.as_ptr();
+            *out_len = peer_cert.len();
+        },
+
+        None => *out_len = 0,
+    }
+}
+
+#[no_mangle]
 pub extern fn quiche_conn_session(
-    conn: &mut Connection, out: &mut *const u8, out_len: &mut size_t,
+    conn: &Connection, out: &mut *const u8, out_len: &mut size_t,
 ) {
     match conn.session() {
         Some(session) => {
@@ -891,28 +1018,33 @@
 }
 
 #[no_mangle]
-pub extern fn quiche_conn_is_established(conn: &mut Connection) -> bool {
+pub extern fn quiche_conn_is_established(conn: &Connection) -> bool {
     conn.is_established()
 }
 
 #[no_mangle]
-pub extern fn quiche_conn_is_in_early_data(conn: &mut Connection) -> bool {
+pub extern fn quiche_conn_is_in_early_data(conn: &Connection) -> bool {
     conn.is_in_early_data()
 }
 
 #[no_mangle]
-pub extern fn quiche_conn_is_draining(conn: &mut Connection) -> bool {
+pub extern fn quiche_conn_is_draining(conn: &Connection) -> bool {
     conn.is_draining()
 }
 
 #[no_mangle]
-pub extern fn quiche_conn_is_closed(conn: &mut Connection) -> bool {
+pub extern fn quiche_conn_is_closed(conn: &Connection) -> bool {
     conn.is_closed()
 }
 
 #[no_mangle]
+pub extern fn quiche_conn_is_timed_out(conn: &Connection) -> bool {
+    conn.is_timed_out()
+}
+
+#[no_mangle]
 pub extern fn quiche_conn_peer_error(
-    conn: &mut Connection, is_app: *mut bool, error_code: *mut u64,
+    conn: &Connection, is_app: *mut bool, error_code: *mut u64,
     reason: &mut *const u8, reason_len: &mut size_t,
 ) -> bool {
     match &conn.peer_error {
@@ -930,6 +1062,25 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_conn_local_error(
+    conn: &Connection, is_app: *mut bool, error_code: *mut u64,
+    reason: &mut *const u8, reason_len: &mut size_t,
+) -> bool {
+    match &conn.local_error {
+        Some(conn_err) => unsafe {
+            *is_app = conn_err.is_app;
+            *error_code = conn_err.error_code;
+            *reason = conn_err.reason.as_ptr();
+            *reason_len = conn_err.reason.len();
+
+            true
+        },
+
+        None => false,
+    }
+}
+
+#[no_mangle]
 pub extern fn quiche_stream_iter_next(
     iter: &mut StreamIter, stream_id: *mut u64,
 ) -> bool {
@@ -951,9 +1102,26 @@
     recv: usize,
     sent: usize,
     lost: usize,
-    rtt: u64,
-    cwnd: usize,
-    delivery_rate: u64,
+    retrans: usize,
+    sent_bytes: u64,
+    recv_bytes: u64,
+    lost_bytes: u64,
+    stream_retrans_bytes: u64,
+    paths_count: usize,
+    peer_max_idle_timeout: u64,
+    peer_max_udp_payload_size: u64,
+    peer_initial_max_data: u64,
+    peer_initial_max_stream_data_bidi_local: u64,
+    peer_initial_max_stream_data_bidi_remote: u64,
+    peer_initial_max_stream_data_uni: u64,
+    peer_initial_max_streams_bidi: u64,
+    peer_initial_max_streams_uni: u64,
+    peer_ack_delay_exponent: u64,
+    peer_max_ack_delay: u64,
+    peer_disable_active_migration: bool,
+    peer_active_conn_id_limit: u64,
+    peer_max_datagram_frame_size: ssize_t,
+    paths: [PathStats; 8],
 }
 
 #[no_mangle]
@@ -963,9 +1131,82 @@
     out.recv = stats.recv;
     out.sent = stats.sent;
     out.lost = stats.lost;
+    out.retrans = stats.retrans;
+    out.sent_bytes = stats.sent_bytes;
+    out.recv_bytes = stats.recv_bytes;
+    out.lost_bytes = stats.lost_bytes;
+    out.stream_retrans_bytes = stats.stream_retrans_bytes;
+    out.paths_count = stats.paths_count;
+    out.peer_max_idle_timeout = stats.peer_max_idle_timeout;
+    out.peer_max_udp_payload_size = stats.peer_max_udp_payload_size;
+    out.peer_initial_max_data = stats.peer_initial_max_data;
+    out.peer_initial_max_stream_data_bidi_local =
+        stats.peer_initial_max_stream_data_bidi_local;
+    out.peer_initial_max_stream_data_bidi_remote =
+        stats.peer_initial_max_stream_data_bidi_remote;
+    out.peer_initial_max_stream_data_uni = stats.peer_initial_max_stream_data_uni;
+    out.peer_initial_max_streams_bidi = stats.peer_initial_max_streams_bidi;
+    out.peer_initial_max_streams_uni = stats.peer_initial_max_streams_uni;
+    out.peer_ack_delay_exponent = stats.peer_ack_delay_exponent;
+    out.peer_max_ack_delay = stats.peer_max_ack_delay;
+    out.peer_disable_active_migration = stats.peer_disable_active_migration;
+    out.peer_active_conn_id_limit = stats.peer_active_conn_id_limit;
+    out.peer_max_datagram_frame_size = match stats.peer_max_datagram_frame_size {
+        None => Error::Done.to_c(),
+
+        Some(v) => v as ssize_t,
+    };
+}
+
+#[repr(C)]
+pub struct PathStats {
+    local_addr: sockaddr_storage,
+    local_addr_len: socklen_t,
+    peer_addr: sockaddr_storage,
+    peer_addr_len: socklen_t,
+    validation_state: ssize_t,
+    active: bool,
+    recv: usize,
+    sent: usize,
+    lost: usize,
+    retrans: usize,
+    rtt: u64,
+    cwnd: usize,
+    sent_bytes: u64,
+    recv_bytes: u64,
+    lost_bytes: u64,
+    stream_retrans_bytes: u64,
+    pmtu: usize,
+    delivery_rate: u64,
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_path_stats(
+    conn: &Connection, idx: usize, out: &mut PathStats,
+) -> c_int {
+    let stats = match conn.path_stats().nth(idx) {
+        Some(p) => p,
+        None => return Error::Done.to_c() as c_int,
+    };
+
+    out.local_addr_len = std_addr_to_c(&stats.local_addr, &mut out.local_addr);
+    out.peer_addr_len = std_addr_to_c(&stats.peer_addr, &mut out.peer_addr);
+    out.validation_state = stats.validation_state.to_c();
+    out.active = stats.active;
+    out.recv = stats.recv;
+    out.sent = stats.sent;
+    out.lost = stats.lost;
+    out.retrans = stats.retrans;
     out.rtt = stats.rtt.as_nanos() as u64;
     out.cwnd = stats.cwnd;
+    out.sent_bytes = stats.sent_bytes;
+    out.recv_bytes = stats.recv_bytes;
+    out.lost_bytes = stats.lost_bytes;
+    out.stream_retrans_bytes = stats.stream_retrans_bytes;
+    out.pmtu = stats.pmtu;
     out.delivery_rate = stats.delivery_rate;
+
+    0
 }
 
 #[no_mangle]
@@ -1059,69 +1300,196 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_conn_send_ack_eliciting(conn: &mut Connection) -> ssize_t {
+    match conn.send_ack_eliciting() {
+        Ok(()) => 0,
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_conn_send_ack_eliciting_on_path(
+    conn: &mut Connection, local: &sockaddr, local_len: socklen_t,
+    peer: &sockaddr, peer_len: socklen_t,
+) -> ssize_t {
+    let local = std_addr_from_c(local, local_len);
+    let peer = std_addr_from_c(peer, peer_len);
+    match conn.send_ack_eliciting_on_path(local, peer) {
+        Ok(()) => 0,
+        Err(e) => e.to_c(),
+    }
+}
+
+#[no_mangle]
 pub extern fn quiche_conn_free(conn: *mut Connection) {
     unsafe { Box::from_raw(conn) };
 }
 
 #[no_mangle]
-pub extern fn quiche_conn_peer_streams_left_bidi(conn: &mut Connection) -> u64 {
+pub extern fn quiche_conn_peer_streams_left_bidi(conn: &Connection) -> u64 {
     conn.peer_streams_left_bidi()
 }
 
 #[no_mangle]
-pub extern fn quiche_conn_peer_streams_left_uni(conn: &mut Connection) -> u64 {
+pub extern fn quiche_conn_peer_streams_left_uni(conn: &Connection) -> u64 {
     conn.peer_streams_left_uni()
 }
 
+#[no_mangle]
+pub extern fn quiche_conn_send_quantum(conn: &Connection) -> size_t {
+    conn.send_quantum() as size_t
+}
+
 fn std_addr_from_c(addr: &sockaddr, addr_len: socklen_t) -> SocketAddr {
-    unsafe {
-        match addr.sa_family as i32 {
-            AF_INET => {
-                assert!(addr_len as usize == std::mem::size_of::<sockaddr_in>());
+    match addr.sa_family as i32 {
+        AF_INET => {
+            assert!(addr_len as usize == std::mem::size_of::<sockaddr_in>());
 
-                SocketAddr::V4(
-                    *(addr as *const _ as *const sockaddr_in as *const _),
-                )
-            },
+            let in4 = unsafe { *(addr as *const _ as *const sockaddr_in) };
 
-            AF_INET6 => {
-                assert!(addr_len as usize == std::mem::size_of::<sockaddr_in6>());
+            #[cfg(not(windows))]
+            let ip_addr = Ipv4Addr::from(u32::from_be(in4.sin_addr.s_addr));
+            #[cfg(windows)]
+            let ip_addr = {
+                let ip_bytes = unsafe { in4.sin_addr.S_un.S_un_b() };
 
-                SocketAddr::V6(
-                    *(addr as *const _ as *const sockaddr_in6 as *const _),
-                )
-            },
+                Ipv4Addr::from([
+                    ip_bytes.s_b1,
+                    ip_bytes.s_b2,
+                    ip_bytes.s_b3,
+                    ip_bytes.s_b4,
+                ])
+            };
 
-            _ => unimplemented!("unsupported address type"),
-        }
+            let port = u16::from_be(in4.sin_port);
+
+            let out = SocketAddrV4::new(ip_addr, port);
+
+            out.into()
+        },
+
+        AF_INET6 => {
+            assert!(addr_len as usize == std::mem::size_of::<sockaddr_in6>());
+
+            let in6 = unsafe { *(addr as *const _ as *const sockaddr_in6) };
+
+            let ip_addr = Ipv6Addr::from(
+                #[cfg(not(windows))]
+                in6.sin6_addr.s6_addr,
+                #[cfg(windows)]
+                *unsafe { in6.sin6_addr.u.Byte() },
+            );
+
+            let port = u16::from_be(in6.sin6_port);
+
+            #[cfg(not(windows))]
+            let scope_id = in6.sin6_scope_id;
+            #[cfg(windows)]
+            let scope_id = unsafe { *in6.u.sin6_scope_id() };
+
+            let out =
+                SocketAddrV6::new(ip_addr, port, in6.sin6_flowinfo, scope_id);
+
+            out.into()
+        },
+
+        _ => unimplemented!("unsupported address type"),
     }
 }
 
 fn std_addr_to_c(addr: &SocketAddr, out: &mut sockaddr_storage) -> socklen_t {
-    unsafe {
-        match addr {
-            SocketAddr::V4(addr) => {
-                let sa_len = std::mem::size_of::<sockaddr_in>();
+    let sin_port = addr.port().to_be();
 
-                let src = addr as *const _ as *const u8;
-                let dst = out as *mut _ as *mut u8;
+    match addr {
+        SocketAddr::V4(addr) => unsafe {
+            let sa_len = std::mem::size_of::<sockaddr_in>();
+            let out_in = out as *mut _ as *mut sockaddr_in;
 
-                std::ptr::copy_nonoverlapping(src, dst, sa_len);
+            let s_addr = u32::from_ne_bytes(addr.ip().octets());
 
-                sa_len as socklen_t
-            },
+            #[cfg(not(windows))]
+            let sin_addr = in_addr { s_addr };
+            #[cfg(windows)]
+            let sin_addr = {
+                let mut s_un = std::mem::zeroed::<in_addr_S_un>();
+                *s_un.S_addr_mut() = s_addr;
+                in_addr { S_un: s_un }
+            };
 
-            SocketAddr::V6(addr) => {
-                let sa_len = std::mem::size_of::<sockaddr_in6>();
+            *out_in = sockaddr_in {
+                sin_family: AF_INET as sa_family_t,
 
-                let src = addr as *const _ as *const u8;
-                let dst = out as *mut _ as *mut u8;
+                sin_addr,
 
-                std::ptr::copy_nonoverlapping(src, dst, sa_len);
+                #[cfg(any(
+                    target_os = "macos",
+                    target_os = "ios",
+                    target_os = "watchos",
+                    target_os = "freebsd",
+                    target_os = "dragonfly",
+                    target_os = "openbsd",
+                    target_os = "netbsd"
+                ))]
+                sin_len: sa_len as u8,
 
-                sa_len as socklen_t
-            },
-        }
+                sin_port,
+
+                sin_zero: std::mem::zeroed(),
+            };
+
+            sa_len as socklen_t
+        },
+
+        SocketAddr::V6(addr) => unsafe {
+            let sa_len = std::mem::size_of::<sockaddr_in6>();
+            let out_in6 = out as *mut _ as *mut sockaddr_in6;
+
+            #[cfg(not(windows))]
+            let sin6_addr = in6_addr {
+                s6_addr: addr.ip().octets(),
+            };
+            #[cfg(windows)]
+            let sin6_addr = {
+                let mut u = std::mem::zeroed::<in6_addr_u>();
+                *u.Byte_mut() = addr.ip().octets();
+                in6_addr { u }
+            };
+
+            #[cfg(windows)]
+            let u = {
+                let mut u = std::mem::zeroed::<SOCKADDR_IN6_LH_u>();
+                *u.sin6_scope_id_mut() = addr.scope_id();
+                u
+            };
+
+            *out_in6 = sockaddr_in6 {
+                sin6_family: AF_INET6 as sa_family_t,
+
+                sin6_addr,
+
+                #[cfg(any(
+                    target_os = "macos",
+                    target_os = "ios",
+                    target_os = "watchos",
+                    target_os = "freebsd",
+                    target_os = "dragonfly",
+                    target_os = "openbsd",
+                    target_os = "netbsd"
+                ))]
+                sin6_len: sa_len as u8,
+
+                sin6_port: sin_port,
+
+                sin6_flowinfo: addr.flowinfo(),
+
+                #[cfg(not(windows))]
+                sin6_scope_id: addr.scope_id(),
+                #[cfg(windows)]
+                u,
+            };
+
+            sa_len as socklen_t
+        },
     }
 }
 
@@ -1138,3 +1506,108 @@
     out.tv_sec = 0;
     out.tv_nsec = 0;
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[cfg(windows)]
+    use winapi::um::ws2tcpip::inet_ntop;
+
+    #[test]
+    fn addr_v4() {
+        let addr = "127.0.0.1:8080".parse().unwrap();
+
+        let mut out: sockaddr_storage = unsafe { std::mem::zeroed() };
+
+        assert_eq!(
+            std_addr_to_c(&addr, &mut out),
+            std::mem::size_of::<sockaddr_in>() as socklen_t
+        );
+
+        let s = std::ffi::CString::new("ddd.ddd.ddd.ddd").unwrap();
+
+        let s = unsafe {
+            let in_addr = &out as *const _ as *const sockaddr_in;
+            assert_eq!(u16::from_be((*in_addr).sin_port), addr.port());
+
+            let dst = s.into_raw();
+
+            inet_ntop(
+                AF_INET,
+                &((*in_addr).sin_addr) as *const _ as *const c_void,
+                dst,
+                16,
+            );
+
+            std::ffi::CString::from_raw(dst).into_string().unwrap()
+        };
+
+        assert_eq!(s, "127.0.0.1");
+
+        let addr = unsafe {
+            std_addr_from_c(
+                &*(&out as *const _ as *const sockaddr),
+                std::mem::size_of::<sockaddr_in>() as socklen_t,
+            )
+        };
+
+        assert_eq!(addr, "127.0.0.1:8080".parse().unwrap());
+    }
+
+    #[test]
+    fn addr_v6() {
+        let addr = "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080"
+            .parse()
+            .unwrap();
+
+        let mut out: sockaddr_storage = unsafe { std::mem::zeroed() };
+
+        assert_eq!(
+            std_addr_to_c(&addr, &mut out),
+            std::mem::size_of::<sockaddr_in6>() as socklen_t
+        );
+
+        let s = std::ffi::CString::new("dddd:dddd:dddd:dddd:dddd:dddd:dddd:dddd")
+            .unwrap();
+
+        let s = unsafe {
+            let in6_addr = &out as *const _ as *const sockaddr_in6;
+            assert_eq!(u16::from_be((*in6_addr).sin6_port), addr.port());
+
+            let dst = s.into_raw();
+
+            inet_ntop(
+                AF_INET6,
+                &((*in6_addr).sin6_addr) as *const _ as *const c_void,
+                dst,
+                45,
+            );
+
+            std::ffi::CString::from_raw(dst).into_string().unwrap()
+        };
+
+        assert_eq!(s, "2001:db8:85a3::8a2e:370:7334");
+
+        let addr = unsafe {
+            std_addr_from_c(
+                &*(&out as *const _ as *const sockaddr),
+                std::mem::size_of::<sockaddr_in6>() as socklen_t,
+            )
+        };
+
+        assert_eq!(
+            addr,
+            "[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080"
+                .parse()
+                .unwrap()
+        );
+    }
+
+    #[cfg(not(windows))]
+    extern {
+        fn inet_ntop(
+            af: c_int, src: *const c_void, dst: *mut c_char, size: socklen_t,
+        ) -> *mut c_char;
+    }
+}
diff --git a/src/flowcontrol.rs b/src/flowcontrol.rs
new file mode 100644
index 0000000..6731c26
--- /dev/null
+++ b/src/flowcontrol.rs
@@ -0,0 +1,220 @@
+// Copyright (C) 2021, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use std::time::Duration;
+use std::time::Instant;
+
+// When autotuning the receiver window, decide how much
+// we increase the window.
+const WINDOW_INCREASE_FACTOR: u64 = 2;
+
+// When autotuning the receiver window, check if the last
+// update is within RTT * this constant.
+const WINDOW_TRIGGER_FACTOR: u32 = 2;
+
+#[derive(Default, Debug)]
+pub struct FlowControl {
+    /// Total consumed bytes by the receiver.
+    consumed: u64,
+
+    /// Flow control limit.
+    max_data: u64,
+
+    /// The receive window. This value is used for updating
+    /// flow control limit.
+    window: u64,
+
+    /// The maximum receive window.
+    max_window: u64,
+
+    /// Last update time of max_data for autotuning the window.
+    last_update: Option<Instant>,
+}
+
+impl FlowControl {
+    pub fn new(max_data: u64, window: u64, max_window: u64) -> Self {
+        Self {
+            max_data,
+
+            window,
+
+            max_window,
+
+            ..Default::default()
+        }
+    }
+
+    /// Returns the current window size.
+    pub fn window(&self) -> u64 {
+        self.window
+    }
+
+    /// Returns the current flow limit.
+    pub fn max_data(&self) -> u64 {
+        self.max_data
+    }
+
+    /// Update consumed bytes.
+    pub fn add_consumed(&mut self, consumed: u64) {
+        self.consumed += consumed;
+    }
+
+    /// Returns true if the flow control needs to update max_data.
+    ///
+    /// This happens when the available window is smaller than the half
+    /// of the current window.
+    pub fn should_update_max_data(&self) -> bool {
+        let available_window = self.max_data - self.consumed;
+
+        available_window < (self.window / 2)
+    }
+
+    /// Returns the new max_data limit.
+    pub fn max_data_next(&self) -> u64 {
+        self.consumed + self.window
+    }
+
+    /// Commits the new max_data limit.
+    pub fn update_max_data(&mut self, now: Instant) {
+        self.max_data = self.max_data_next();
+        self.last_update = Some(now);
+    }
+
+    /// Autotune the window size. When there is an another update
+    /// within RTT x 2, bump the window x 1.5, capped by
+    /// max_window.
+    pub fn autotune_window(&mut self, now: Instant, rtt: Duration) {
+        if let Some(last_update) = self.last_update {
+            if now - last_update < rtt * WINDOW_TRIGGER_FACTOR {
+                self.window = std::cmp::min(
+                    self.window * WINDOW_INCREASE_FACTOR,
+                    self.max_window,
+                );
+            }
+        }
+    }
+
+    /// Make sure the lower bound of the window is same to
+    /// the current window.
+    pub fn ensure_window_lower_bound(&mut self, min_window: u64) {
+        if min_window > self.window {
+            self.window = min_window;
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn max_data() {
+        let fc = FlowControl::new(100, 20, 100);
+
+        assert_eq!(fc.max_data(), 100);
+    }
+
+    #[test]
+    fn should_update_max_data() {
+        let mut fc = FlowControl::new(100, 20, 100);
+
+        fc.add_consumed(85);
+        assert_eq!(fc.should_update_max_data(), false);
+
+        fc.add_consumed(10);
+        assert_eq!(fc.should_update_max_data(), true);
+    }
+
+    #[test]
+    fn max_data_next() {
+        let mut fc = FlowControl::new(100, 20, 100);
+
+        let consumed = 95;
+
+        fc.add_consumed(consumed);
+        assert_eq!(fc.should_update_max_data(), true);
+        assert_eq!(fc.max_data_next(), consumed + 20);
+    }
+
+    #[test]
+    fn update_max_data() {
+        let mut fc = FlowControl::new(100, 20, 100);
+
+        let consumed = 95;
+
+        fc.add_consumed(consumed);
+        assert_eq!(fc.should_update_max_data(), true);
+
+        let max_data_next = fc.max_data_next();
+        assert_eq!(fc.max_data_next(), consumed + 20);
+
+        fc.update_max_data(Instant::now());
+        assert_eq!(fc.max_data(), max_data_next);
+    }
+
+    #[test]
+    fn autotune_window() {
+        let w = 20;
+        let mut fc = FlowControl::new(100, w, 100);
+
+        let consumed = 95;
+
+        fc.add_consumed(consumed);
+        assert_eq!(fc.should_update_max_data(), true);
+
+        let max_data_next = fc.max_data_next();
+        assert_eq!(max_data_next, consumed + w);
+
+        fc.update_max_data(Instant::now());
+        assert_eq!(fc.max_data(), max_data_next);
+
+        // Window size should be doubled.
+        fc.autotune_window(Instant::now(), Duration::from_millis(100));
+
+        let w = w * 2;
+        let consumed_inc = 15;
+
+        fc.add_consumed(consumed_inc);
+        assert_eq!(fc.should_update_max_data(), true);
+
+        let max_data_next = fc.max_data_next();
+        assert_eq!(max_data_next, consumed + consumed_inc + w);
+    }
+
+    #[test]
+    fn ensure_window_lower_bound() {
+        let w = 20;
+        let mut fc = FlowControl::new(100, w, 100);
+
+        // Window doesn't change.
+        fc.ensure_window_lower_bound(w);
+        assert_eq!(fc.window(), 20);
+
+        // Window changed to the new value.
+        fc.ensure_window_lower_bound(w * 2);
+        assert_eq!(fc.window(), 40);
+    }
+}
diff --git a/src/frame.rs b/src/frame.rs
index c0cba7e..2addb8d 100644
--- a/src/frame.rs
+++ b/src/frame.rs
@@ -24,20 +24,37 @@
 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+use std::convert::TryInto;
+
 use crate::Error;
 use crate::Result;
 
-use crate::octets;
 use crate::packet;
 use crate::ranges;
 use crate::stream;
 
+#[cfg(feature = "qlog")]
+use qlog::events::quic::AckedRanges;
+#[cfg(feature = "qlog")]
+use qlog::events::quic::ErrorSpace;
+#[cfg(feature = "qlog")]
+use qlog::events::quic::QuicFrame;
+#[cfg(feature = "qlog")]
+use qlog::events::quic::StreamType;
+
 pub const MAX_CRYPTO_OVERHEAD: usize = 8;
 pub const MAX_DGRAM_OVERHEAD: usize = 2;
 pub const MAX_STREAM_OVERHEAD: usize = 12;
 pub const MAX_STREAM_SIZE: u64 = 1 << 62;
 
-#[derive(Clone, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct EcnCounts {
+    ect0_count: u64,
+    ect1_count: u64,
+    ecn_ce_count: u64,
+}
+
+#[derive(Clone, PartialEq, Eq)]
 pub enum Frame {
     Padding {
         len: usize,
@@ -48,6 +65,7 @@
     ACK {
         ack_delay: u64,
         ranges: ranges::RangeSet,
+        ecn_counts: Option<EcnCounts>,
     },
 
     ResetStream {
@@ -124,7 +142,7 @@
         seq_num: u64,
         retire_prior_to: u64,
         conn_id: Vec<u8>,
-        reset_token: Vec<u8>,
+        reset_token: [u8; 16],
     },
 
     RetireConnectionId {
@@ -132,11 +150,11 @@
     },
 
     PathChallenge {
-        data: Vec<u8>,
+        data: [u8; 8],
     },
 
     PathResponse {
-        data: Vec<u8>,
+        data: [u8; 8],
     },
 
     ConnectionClose {
@@ -155,6 +173,10 @@
     Datagram {
         data: Vec<u8>,
     },
+
+    DatagramHeader {
+        length: usize,
+    },
 }
 
 impl Frame {
@@ -163,8 +185,6 @@
     ) -> Result<Frame> {
         let frame_type = b.get_varint()?;
 
-        // println!("GOT FRAME {:x}", frame_type);
-
         let frame = match frame_type {
             0x00 => {
                 let mut len = 1;
@@ -180,7 +200,7 @@
 
             0x01 => Frame::Ping,
 
-            0x02 => parse_ack_frame(frame_type, b)?,
+            0x02..=0x03 => parse_ack_frame(frame_type, b)?,
 
             0x04 => Frame::ResetStream {
                 stream_id: b.get_varint()?,
@@ -245,7 +265,11 @@
                 seq_num: b.get_varint()?,
                 retire_prior_to: b.get_varint()?,
                 conn_id: b.get_bytes_with_u8_length()?.to_vec(),
-                reset_token: b.get_bytes(16)?.to_vec(),
+                reset_token: b
+                    .get_bytes(16)?
+                    .buf()
+                    .try_into()
+                    .map_err(|_| Error::BufferTooShort)?,
             },
 
             0x19 => Frame::RetireConnectionId {
@@ -253,11 +277,19 @@
             },
 
             0x1a => Frame::PathChallenge {
-                data: b.get_bytes(8)?.to_vec(),
+                data: b
+                    .get_bytes(8)?
+                    .buf()
+                    .try_into()
+                    .map_err(|_| Error::BufferTooShort)?,
             },
 
             0x1b => Frame::PathResponse {
-                data: b.get_bytes(8)?.to_vec(),
+                data: b
+                    .get_bytes(8)?
+                    .buf()
+                    .try_into()
+                    .map_err(|_| Error::BufferTooShort)?,
             },
 
             0x1c => Frame::ConnectionClose {
@@ -331,8 +363,16 @@
                 b.put_varint(0x01)?;
             },
 
-            Frame::ACK { ack_delay, ranges } => {
-                b.put_varint(0x02)?;
+            Frame::ACK {
+                ack_delay,
+                ranges,
+                ecn_counts,
+            } => {
+                if ecn_counts.is_none() {
+                    b.put_varint(0x02)?;
+                } else {
+                    b.put_varint(0x03)?;
+                }
 
                 let mut it = ranges.iter().rev();
 
@@ -355,6 +395,12 @@
 
                     smallest_ack = block.start;
                 }
+
+                if let Some(ecn) = ecn_counts {
+                    b.put_varint(ecn.ect0_count)?;
+                    b.put_varint(ecn.ect1_count)?;
+                    b.put_varint(ecn.ecn_ce_count)?;
+                }
             },
 
             Frame::ResetStream {
@@ -380,9 +426,9 @@
             },
 
             Frame::Crypto { data } => {
-                encode_crypto_header(data.off() as u64, data.len() as u64, b)?;
+                encode_crypto_header(data.off(), data.len() as u64, b)?;
 
-                b.put_bytes(&data)?;
+                b.put_bytes(data)?;
             },
 
             Frame::CryptoHeader { .. } => (),
@@ -391,19 +437,19 @@
                 b.put_varint(0x07)?;
 
                 b.put_varint(token.len() as u64)?;
-                b.put_bytes(&token)?;
+                b.put_bytes(token)?;
             },
 
             Frame::Stream { stream_id, data } => {
                 encode_stream_header(
                     *stream_id,
-                    data.off() as u64,
+                    data.off(),
                     data.len() as u64,
                     data.fin(),
                     b,
                 )?;
 
-                b.put_bytes(&data)?;
+                b.put_bytes(data)?;
             },
 
             Frame::StreamHeader { .. } => (),
@@ -517,16 +563,12 @@
             },
 
             Frame::Datagram { data } => {
-                let mut ty: u8 = 0x30;
+                encode_dgram_header(data.len() as u64, b)?;
 
-                // Always encode length
-                ty |= 0x01;
-
-                b.put_varint(u64::from(ty))?;
-
-                b.put_varint(data.len() as u64)?;
                 b.put_bytes(data.as_ref())?;
             },
+
+            Frame::DatagramHeader { .. } => (),
         }
 
         Ok(before - b.cap())
@@ -538,7 +580,11 @@
 
             Frame::Ping => 1,
 
-            Frame::ACK { ack_delay, ranges } => {
+            Frame::ACK {
+                ack_delay,
+                ranges,
+                ecn_counts,
+            } => {
                 let mut it = ranges.iter().rev();
 
                 let first = it.next().unwrap();
@@ -562,6 +608,12 @@
                     smallest_ack = block.start;
                 }
 
+                if let Some(ecn) = ecn_counts {
+                    len += octets::varint_len(ecn.ect0_count) +
+                        octets::varint_len(ecn.ect1_count) +
+                        octets::varint_len(ecn.ecn_ce_count);
+                }
+
                 len
             },
 
@@ -587,7 +639,7 @@
 
             Frame::Crypto { data } => {
                 1 + // frame type
-                octets::varint_len(data.off() as u64) + // offset
+                octets::varint_len(data.off()) + // offset
                 2 + // length, always encode as 2-byte varint
                 data.len() // data
             },
@@ -608,7 +660,7 @@
             Frame::Stream { stream_id, data } => {
                 1 + // frame type
                 octets::varint_len(*stream_id) + // stream_id
-                octets::varint_len(data.off() as u64) + // offset
+                octets::varint_len(data.off()) + // offset
                 2 + // length, always encode as 2-byte varint
                 data.len() // data
             },
@@ -723,9 +775,15 @@
 
             Frame::Datagram { data } => {
                 1 + // frame type
-                octets::varint_len(data.len() as u64) + // length
+                2 + // length, always encode as 2-byte varint
                 data.len() // data
             },
+
+            Frame::DatagramHeader { length } => {
+                1 + // frame type
+                2 + // length, always encode as 2-byte varint
+                *length // data
+            },
         }
     }
 
@@ -740,176 +798,204 @@
         )
     }
 
-    pub fn shrink_for_retransmission(&mut self) {
-        if let Frame::Datagram { data } = self {
-            *data = Vec::new();
-        }
+    pub fn probing(&self) -> bool {
+        matches!(
+            self,
+            Frame::Padding { .. } |
+                Frame::NewConnectionId { .. } |
+                Frame::PathChallenge { .. } |
+                Frame::PathResponse { .. }
+        )
     }
 
     #[cfg(feature = "qlog")]
-    pub fn to_qlog(&self) -> qlog::QuicFrame {
+    pub fn to_qlog(&self) -> QuicFrame {
         match self {
-            Frame::Padding { .. } => qlog::QuicFrame::padding(),
+            Frame::Padding { .. } => QuicFrame::Padding,
 
-            Frame::Ping { .. } => qlog::QuicFrame::ping(),
+            Frame::Ping { .. } => QuicFrame::Ping,
 
-            Frame::ACK { ack_delay, ranges } => {
-                let ack_ranges =
-                    ranges.iter().map(|r| (r.start, r.end - 1)).collect();
-                qlog::QuicFrame::ack(
-                    Some(ack_delay.to_string()),
-                    Some(ack_ranges),
-                    None,
-                    None,
-                    None,
-                )
+            Frame::ACK {
+                ack_delay,
+                ranges,
+                ecn_counts,
+            } => {
+                let ack_ranges = AckedRanges::Double(
+                    ranges.iter().map(|r| (r.start, r.end - 1)).collect(),
+                );
+
+                let (ect0, ect1, ce) = match ecn_counts {
+                    Some(ecn) => (
+                        Some(ecn.ect0_count),
+                        Some(ecn.ect1_count),
+                        Some(ecn.ecn_ce_count),
+                    ),
+
+                    None => (None, None, None),
+                };
+
+                QuicFrame::Ack {
+                    ack_delay: Some(*ack_delay as f32 / 1000.0),
+                    acked_ranges: Some(ack_ranges),
+                    ect1,
+                    ect0,
+                    ce,
+                }
             },
 
             Frame::ResetStream {
                 stream_id,
                 error_code,
                 final_size,
-            } => qlog::QuicFrame::reset_stream(
-                stream_id.to_string(),
-                *error_code,
-                final_size.to_string(),
-            ),
+            } => QuicFrame::ResetStream {
+                stream_id: *stream_id,
+                error_code: *error_code,
+                final_size: *final_size,
+            },
 
             Frame::StopSending {
                 stream_id,
                 error_code,
-            } =>
-                qlog::QuicFrame::stop_sending(stream_id.to_string(), *error_code),
+            } => QuicFrame::StopSending {
+                stream_id: *stream_id,
+                error_code: *error_code,
+            },
 
-            Frame::Crypto { data } => qlog::QuicFrame::crypto(
-                data.off().to_string(),
-                data.len().to_string(),
-            ),
+            Frame::Crypto { data } => QuicFrame::Crypto {
+                offset: data.off(),
+                length: data.len() as u64,
+            },
 
-            Frame::CryptoHeader { offset, length } =>
-                qlog::QuicFrame::crypto(offset.to_string(), length.to_string()),
+            Frame::CryptoHeader { offset, length } => QuicFrame::Crypto {
+                offset: *offset,
+                length: *length as u64,
+            },
 
-            Frame::NewToken { token } => qlog::QuicFrame::new_token(
-                token.len().to_string(),
-                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
-                    .to_string(),
-            ),
+            Frame::NewToken { token } => QuicFrame::NewToken {
+                token: qlog::Token {
+                    // TODO: pick the token type some how
+                    ty: Some(qlog::TokenType::Retry),
+                    raw: Some(qlog::events::RawInfo {
+                        data: qlog::HexSlice::maybe_string(Some(token)),
+                        length: Some(token.len() as u64),
+                        payload_length: None,
+                    }),
+                    details: None,
+                },
+            },
 
-            Frame::Stream { stream_id, data } => qlog::QuicFrame::stream(
-                stream_id.to_string(),
-                data.off().to_string(),
-                data.len().to_string(),
-                data.fin(),
-                None,
-            ),
+            Frame::Stream { stream_id, data } => QuicFrame::Stream {
+                stream_id: *stream_id,
+                offset: data.off(),
+                length: data.len() as u64,
+                fin: data.fin().then_some(true),
+                raw: None,
+            },
 
             Frame::StreamHeader {
                 stream_id,
                 offset,
                 length,
                 fin,
-            } => qlog::QuicFrame::stream(
-                stream_id.to_string(),
-                offset.to_string(),
-                length.to_string(),
-                *fin,
-                None,
-            ),
+            } => QuicFrame::Stream {
+                stream_id: *stream_id,
+                offset: *offset,
+                length: *length as u64,
+                fin: fin.then(|| true),
+                raw: None,
+            },
 
-            Frame::MaxData { max } => qlog::QuicFrame::max_data(max.to_string()),
+            Frame::MaxData { max } => QuicFrame::MaxData { maximum: *max },
 
-            Frame::MaxStreamData { stream_id, max } =>
-                qlog::QuicFrame::max_stream_data(
-                    stream_id.to_string(),
-                    max.to_string(),
-                ),
+            Frame::MaxStreamData { stream_id, max } => QuicFrame::MaxStreamData {
+                stream_id: *stream_id,
+                maximum: *max,
+            },
 
-            Frame::MaxStreamsBidi { max } => qlog::QuicFrame::max_streams(
-                qlog::StreamType::Bidirectional,
-                max.to_string(),
-            ),
+            Frame::MaxStreamsBidi { max } => QuicFrame::MaxStreams {
+                stream_type: StreamType::Bidirectional,
+                maximum: *max,
+            },
 
-            Frame::MaxStreamsUni { max } => qlog::QuicFrame::max_streams(
-                qlog::StreamType::Unidirectional,
-                max.to_string(),
-            ),
+            Frame::MaxStreamsUni { max } => QuicFrame::MaxStreams {
+                stream_type: StreamType::Unidirectional,
+                maximum: *max,
+            },
 
             Frame::DataBlocked { limit } =>
-                qlog::QuicFrame::data_blocked(limit.to_string()),
+                QuicFrame::DataBlocked { limit: *limit },
 
             Frame::StreamDataBlocked { stream_id, limit } =>
-                qlog::QuicFrame::stream_data_blocked(
-                    stream_id.to_string(),
-                    limit.to_string(),
-                ),
+                QuicFrame::StreamDataBlocked {
+                    stream_id: *stream_id,
+                    limit: *limit,
+                },
 
-            Frame::StreamsBlockedBidi { limit } =>
-                qlog::QuicFrame::streams_blocked(
-                    qlog::StreamType::Bidirectional,
-                    limit.to_string(),
-                ),
+            Frame::StreamsBlockedBidi { limit } => QuicFrame::StreamsBlocked {
+                stream_type: StreamType::Bidirectional,
+                limit: *limit,
+            },
 
-            Frame::StreamsBlockedUni { limit } =>
-                qlog::QuicFrame::streams_blocked(
-                    qlog::StreamType::Unidirectional,
-                    limit.to_string(),
-                ),
+            Frame::StreamsBlockedUni { limit } => QuicFrame::StreamsBlocked {
+                stream_type: StreamType::Unidirectional,
+                limit: *limit,
+            },
 
             Frame::NewConnectionId {
                 seq_num,
                 retire_prior_to,
                 conn_id,
-                ..
-            } => qlog::QuicFrame::new_connection_id(
-                seq_num.to_string(),
-                retire_prior_to.to_string(),
-                conn_id.len() as u64,
-                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
-                    .to_string(),
-                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
-                    .to_string(),
-            ),
+                reset_token,
+            } => QuicFrame::NewConnectionId {
+                sequence_number: *seq_num as u32,
+                retire_prior_to: *retire_prior_to as u32,
+                connection_id_length: Some(conn_id.len() as u8),
+                connection_id: format!("{}", qlog::HexSlice::new(conn_id)),
+                stateless_reset_token: qlog::HexSlice::maybe_string(Some(
+                    reset_token,
+                )),
+            },
 
             Frame::RetireConnectionId { seq_num } =>
-                qlog::QuicFrame::retire_connection_id(seq_num.to_string()),
+                QuicFrame::RetireConnectionId {
+                    sequence_number: *seq_num as u32,
+                },
 
-            Frame::PathChallenge { .. } => qlog::QuicFrame::path_challenge(Some(
-                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
-                    .to_string(),
-            )),
+            Frame::PathChallenge { .. } =>
+                QuicFrame::PathChallenge { data: None },
 
-            Frame::PathResponse { .. } => qlog::QuicFrame::path_response(Some(
-                "TODO: https://github.com/quiclog/internet-drafts/issues/36"
-                    .to_string(),
-            )),
+            Frame::PathResponse { .. } => QuicFrame::PathResponse { data: None },
 
             Frame::ConnectionClose {
                 error_code, reason, ..
-            } => qlog::QuicFrame::connection_close(
-                qlog::ErrorSpace::TransportError,
-                *error_code,
-                *error_code,
-                String::from_utf8(reason.clone()).unwrap(),
-                Some(
-                    "TODO: https://github.com/quiclog/internet-drafts/issues/36"
-                        .to_string(),
-                ),
-            ),
+            } => QuicFrame::ConnectionClose {
+                error_space: Some(ErrorSpace::TransportError),
+                error_code: Some(*error_code),
+                error_code_value: None, // raw error is no different for us
+                reason: Some(String::from_utf8_lossy(reason).into_owned()),
+                trigger_frame_type: None, // don't know trigger type
+            },
 
             Frame::ApplicationClose { error_code, reason } =>
-                qlog::QuicFrame::connection_close(
-                    qlog::ErrorSpace::ApplicationError,
-                    *error_code,
-                    *error_code,
-                    String::from_utf8(reason.clone()).unwrap(),
-                    None, /* Application variant of the frame has no trigger
-                           * frame type */
-                ),
+                QuicFrame::ConnectionClose {
+                    error_space: Some(ErrorSpace::ApplicationError),
+                    error_code: Some(*error_code),
+                    error_code_value: None, // raw error is no different for us
+                    reason: Some(String::from_utf8_lossy(reason).into_owned()),
+                    trigger_frame_type: None, // don't know trigger type
+                },
 
-            Frame::HandshakeDone => qlog::QuicFrame::handshake_done(),
+            Frame::HandshakeDone => QuicFrame::HandshakeDone,
 
-            Frame::Datagram { data } =>
-                qlog::QuicFrame::datagram(data.len().to_string(), None),
+            Frame::Datagram { data } => QuicFrame::Datagram {
+                length: data.len() as u64,
+                raw: None,
+            },
+
+            Frame::DatagramHeader { length } => QuicFrame::Datagram {
+                length: *length as u64,
+                raw: None,
+            },
         }
     }
 }
@@ -918,15 +1004,22 @@
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         match self {
             Frame::Padding { len } => {
-                write!(f, "PADDING len={}", len)?;
+                write!(f, "PADDING len={len}")?;
             },
 
             Frame::Ping => {
                 write!(f, "PING")?;
             },
 
-            Frame::ACK { ack_delay, ranges } => {
-                write!(f, "ACK delay={} blocks={:?}", ack_delay, ranges)?;
+            Frame::ACK {
+                ack_delay,
+                ranges,
+                ecn_counts,
+            } => {
+                write!(
+                    f,
+                    "ACK delay={ack_delay} blocks={ranges:?} ecn_counts={ecn_counts:?}"
+                )?;
             },
 
             Frame::ResetStream {
@@ -936,8 +1029,7 @@
             } => {
                 write!(
                     f,
-                    "RESET_STREAM stream={} err={:x} size={}",
-                    stream_id, error_code, final_size
+                    "RESET_STREAM stream={stream_id} err={error_code:x} size={final_size}"
                 )?;
             },
 
@@ -945,11 +1037,7 @@
                 stream_id,
                 error_code,
             } => {
-                write!(
-                    f,
-                    "STOP_SENDING stream={} err={:x}",
-                    stream_id, error_code
-                )?;
+                write!(f, "STOP_SENDING stream={stream_id} err={error_code:x}")?;
             },
 
             Frame::Crypto { data } => {
@@ -957,7 +1045,7 @@
             },
 
             Frame::CryptoHeader { offset, length } => {
-                write!(f, "CRYPTO off={} len={}", offset, length)?;
+                write!(f, "CRYPTO off={offset} len={length}")?;
             },
 
             Frame::NewToken { .. } => {
@@ -983,61 +1071,67 @@
             } => {
                 write!(
                     f,
-                    "STREAM id={} off={} len={} fin={}",
-                    stream_id, offset, length, fin
+                    "STREAM id={stream_id} off={offset} len={length} fin={fin}"
                 )?;
             },
 
             Frame::MaxData { max } => {
-                write!(f, "MAX_DATA max={}", max)?;
+                write!(f, "MAX_DATA max={max}")?;
             },
 
             Frame::MaxStreamData { stream_id, max } => {
-                write!(f, "MAX_STREAM_DATA stream={} max={}", stream_id, max)?;
+                write!(f, "MAX_STREAM_DATA stream={stream_id} max={max}")?;
             },
 
             Frame::MaxStreamsBidi { max } => {
-                write!(f, "MAX_STREAMS type=bidi max={}", max)?;
+                write!(f, "MAX_STREAMS type=bidi max={max}")?;
             },
 
             Frame::MaxStreamsUni { max } => {
-                write!(f, "MAX_STREAMS type=uni max={}", max)?;
+                write!(f, "MAX_STREAMS type=uni max={max}")?;
             },
 
             Frame::DataBlocked { limit } => {
-                write!(f, "DATA_BLOCKED limit={}", limit)?;
+                write!(f, "DATA_BLOCKED limit={limit}")?;
             },
 
             Frame::StreamDataBlocked { stream_id, limit } => {
                 write!(
                     f,
-                    "STREAM_DATA_BLOCKED stream={} limit={}",
-                    stream_id, limit
+                    "STREAM_DATA_BLOCKED stream={stream_id} limit={limit}"
                 )?;
             },
 
             Frame::StreamsBlockedBidi { limit } => {
-                write!(f, "STREAMS_BLOCKED type=bidi limit={}", limit)?;
+                write!(f, "STREAMS_BLOCKED type=bidi limit={limit}")?;
             },
 
             Frame::StreamsBlockedUni { limit } => {
-                write!(f, "STREAMS_BLOCKED type=uni limit={}", limit)?;
+                write!(f, "STREAMS_BLOCKED type=uni limit={limit}")?;
             },
 
-            Frame::NewConnectionId { .. } => {
-                write!(f, "NEW_CONNECTION_ID (TODO)")?;
+            Frame::NewConnectionId {
+                seq_num,
+                retire_prior_to,
+                conn_id,
+                reset_token,
+            } => {
+                write!(
+                    f,
+                    "NEW_CONNECTION_ID seq_num={seq_num} retire_prior_to={retire_prior_to} conn_id={conn_id:02x?} reset_token={reset_token:02x?}",
+                )?;
             },
 
-            Frame::RetireConnectionId { .. } => {
-                write!(f, "RETIRE_CONNECTION_ID (TODO)")?;
+            Frame::RetireConnectionId { seq_num } => {
+                write!(f, "RETIRE_CONNECTION_ID seq_num={seq_num}")?;
             },
 
             Frame::PathChallenge { data } => {
-                write!(f, "PATH_CHALLENGE data={:02x?}", data)?;
+                write!(f, "PATH_CHALLENGE data={data:02x?}")?;
             },
 
             Frame::PathResponse { data } => {
-                write!(f, "PATH_RESPONSE data={:02x?}", data)?;
+                write!(f, "PATH_RESPONSE data={data:02x?}")?;
             },
 
             Frame::ConnectionClose {
@@ -1047,16 +1141,14 @@
             } => {
                 write!(
                     f,
-                    "CONNECTION_CLOSE err={:x} frame={:x} reason={:x?}",
-                    error_code, frame_type, reason
+                    "CONNECTION_CLOSE err={error_code:x} frame={frame_type:x} reason={reason:x?}"
                 )?;
             },
 
             Frame::ApplicationClose { error_code, reason } => {
                 write!(
                     f,
-                    "APPLICATION_CLOSE err={:x} reason={:x?}",
-                    error_code, reason
+                    "APPLICATION_CLOSE err={error_code:x} reason={reason:x?}"
                 )?;
             },
 
@@ -1065,7 +1157,11 @@
             },
 
             Frame::Datagram { data } => {
-                write!(f, "DATAGRAM len={}", data.len(),)?;
+                write!(f, "DATAGRAM len={}", data.len())?;
+            },
+
+            Frame::DatagramHeader { length } => {
+                write!(f, "DATAGRAM len={length}")?;
             },
         }
 
@@ -1073,7 +1169,9 @@
     }
 }
 
-fn parse_ack_frame(_ty: u64, b: &mut octets::Octets) -> Result<Frame> {
+fn parse_ack_frame(ty: u64, b: &mut octets::Octets) -> Result<Frame> {
+    let first = ty as u8;
+
     let largest_ack = b.get_varint()?;
     let ack_delay = b.get_varint()?;
     let block_count = b.get_varint()?;
@@ -1087,7 +1185,6 @@
 
     let mut ranges = ranges::RangeSet::default();
 
-    #[allow(clippy::range_plus_one)]
     ranges.insert(smallest_ack..largest_ack + 1);
 
     for _i in 0..block_count {
@@ -1106,11 +1203,26 @@
 
         smallest_ack = largest_ack - ack_block;
 
-        #[allow(clippy::range_plus_one)]
         ranges.insert(smallest_ack..largest_ack + 1);
     }
 
-    Ok(Frame::ACK { ack_delay, ranges })
+    let ecn_counts = if first & 0x01 != 0 {
+        let ecn = EcnCounts {
+            ect0_count: b.get_varint()?,
+            ect1_count: b.get_varint()?,
+            ecn_ce_count: b.get_varint()?,
+        };
+
+        Some(ecn)
+    } else {
+        None
+    };
+
+    Ok(Frame::ACK {
+        ack_delay,
+        ranges,
+        ecn_counts,
+    })
 }
 
 pub fn encode_crypto_header(
@@ -1153,6 +1265,20 @@
     Ok(())
 }
 
+pub fn encode_dgram_header(length: u64, b: &mut octets::OctetsMut) -> Result<()> {
+    let mut ty: u8 = 0x30;
+
+    // Always encode length
+    ty |= 0x01;
+
+    b.put_varint(u64::from(ty))?;
+
+    // Always encode length field as 2-byte varint.
+    b.put_varint_with_len(length, 2)?;
+
+    Ok(())
+}
+
 fn parse_stream_frame(ty: u64, b: &mut octets::Octets) -> Result<Frame> {
     let first = ty as u8;
 
@@ -1240,7 +1366,7 @@
         };
 
         assert_eq!(wire_len, 1);
-        assert_eq!(&d[..wire_len], [0x01 as u8]);
+        assert_eq!(&d[..wire_len], [0x01_u8]);
 
         let mut b = octets::Octets::with_slice(&d);
         assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
@@ -1268,6 +1394,7 @@
         let frame = Frame::ACK {
             ack_delay: 874_656_534,
             ranges,
+            ecn_counts: None,
         };
 
         let wire_len = {
@@ -1291,6 +1418,48 @@
     }
 
     #[test]
+    fn ack_ecn() {
+        let mut d = [42; 128];
+
+        let mut ranges = ranges::RangeSet::default();
+        ranges.insert(4..7);
+        ranges.insert(9..12);
+        ranges.insert(15..19);
+        ranges.insert(3000..5000);
+
+        let ecn_counts = Some(EcnCounts {
+            ect0_count: 100,
+            ect1_count: 200,
+            ecn_ce_count: 300,
+        });
+
+        let frame = Frame::ACK {
+            ack_delay: 874_656_534,
+            ranges,
+            ecn_counts,
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, 23);
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert_eq!(Frame::from_bytes(&mut b, packet::Type::Short), Ok(frame));
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Initial).is_ok());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::ZeroRTT).is_err());
+
+        let mut b = octets::Octets::with_slice(&d);
+        assert!(Frame::from_bytes(&mut b, packet::Type::Handshake).is_ok());
+    }
+
+    #[test]
     fn reset_stream() {
         let mut d = [42; 128];
 
@@ -1685,7 +1854,7 @@
             seq_num: 123_213,
             retire_prior_to: 122_211,
             conn_id: vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
-            reset_token: vec![0x42; 16],
+            reset_token: [0x42; 16],
         };
 
         let wire_len = {
@@ -1739,7 +1908,7 @@
         let mut d = [42; 128];
 
         let frame = Frame::PathChallenge {
-            data: vec![1, 2, 3, 4, 5, 6, 7, 8],
+            data: [1, 2, 3, 4, 5, 6, 7, 8],
         };
 
         let wire_len = {
@@ -1767,7 +1936,7 @@
         let mut d = [42; 128];
 
         let frame = Frame::PathResponse {
-            data: vec![1, 2, 3, 4, 5, 6, 7, 8],
+            data: [1, 2, 3, 4, 5, 6, 7, 8],
         };
 
         let wire_len = {
@@ -1881,14 +2050,14 @@
 
         let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
 
-        let mut frame = Frame::Datagram { data: data.clone() };
+        let frame = Frame::Datagram { data: data.clone() };
 
         let wire_len = {
             let mut b = octets::OctetsMut::with_slice(&mut d);
             frame.to_bytes(&mut b).unwrap()
         };
 
-        assert_eq!(wire_len, 14);
+        assert_eq!(wire_len, 15);
 
         let mut b = octets::Octets::with_slice(&mut d);
         assert_eq!(
@@ -1912,15 +2081,5 @@
         };
 
         assert_eq!(frame_data, data);
-
-        frame.shrink_for_retransmission();
-
-        let frame_data = match &frame {
-            Frame::Datagram { data } => data.clone(),
-
-            _ => unreachable!(),
-        };
-
-        assert_eq!(frame_data.len(), 0);
     }
 }
diff --git a/src/h3/ffi.rs b/src/h3/ffi.rs
index fc254b0..f42e667 100644
--- a/src/h3/ffi.rs
+++ b/src/h3/ffi.rs
@@ -24,11 +24,12 @@
 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-use std::ffi;
+#[cfg(feature = "sfv")]
+use std::convert::TryFrom;
+
 use std::ptr;
 use std::slice;
 
-use libc::c_char;
 use libc::c_int;
 use libc::c_void;
 use libc::size_t;
@@ -37,6 +38,7 @@
 use crate::*;
 
 use crate::h3::NameValue;
+use crate::h3::Priority;
 
 #[no_mangle]
 pub extern fn quiche_h3_config_new() -> *mut h3::Config {
@@ -48,10 +50,10 @@
 }
 
 #[no_mangle]
-pub extern fn quiche_h3_config_set_max_header_list_size(
+pub extern fn quiche_h3_config_set_max_field_section_size(
     config: &mut h3::Config, v: u64,
 ) {
-    config.set_max_header_list_size(v);
+    config.set_max_field_section_size(v);
 }
 
 #[no_mangle]
@@ -69,6 +71,13 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_h3_config_enable_extended_connect(
+    config: &mut h3::Config, enabled: bool,
+) {
+    config.enable_extended_connect(enabled);
+}
+
+#[no_mangle]
 pub extern fn quiche_h3_config_free(config: *mut h3::Config) {
     unsafe { Box::from_raw(config) };
 }
@@ -85,6 +94,29 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_h3_for_each_setting(
+    conn: &h3::Connection,
+    cb: extern fn(identifier: u64, value: u64, argp: *mut c_void) -> c_int,
+    argp: *mut c_void,
+) -> c_int {
+    match conn.peer_settings_raw() {
+        Some(raw) => {
+            for setting in raw {
+                let rc = cb(setting.0, setting.1, argp);
+
+                if rc != 0 {
+                    return rc;
+                }
+            }
+
+            0
+        },
+
+        None => -1,
+    }
+}
+
+#[no_mangle]
 pub extern fn quiche_h3_conn_poll(
     conn: &mut h3::Connection, quic_conn: &mut Connection,
     ev: *mut *const h3::Event,
@@ -114,6 +146,10 @@
         h3::Event::Datagram { .. } => 3,
 
         h3::Event::GoAway { .. } => 4,
+
+        h3::Event::Reset { .. } => 5,
+
+        h3::Event::PriorityUpdate { .. } => 6,
     }
 }
 
@@ -163,6 +199,13 @@
 }
 
 #[no_mangle]
+pub extern fn quiche_h3_extended_connect_enabled_by_peer(
+    conn: &h3::Connection,
+) -> bool {
+    conn.extended_connect_enabled_by_peer()
+}
+
+#[no_mangle]
 pub extern fn quiche_h3_event_free(ev: *mut h3::Event) {
     unsafe { Box::from_raw(ev) };
 }
@@ -207,17 +250,15 @@
 #[no_mangle]
 pub extern fn quiche_h3_send_response_with_priority(
     conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
-    headers: *const Header, headers_len: size_t, priority: *const c_char,
-    fin: bool,
+    headers: *const Header, headers_len: size_t, priority: &Priority, fin: bool,
 ) -> c_int {
     let resp_headers = headers_from_ptr(headers, headers_len);
-    let priority = unsafe { ffi::CStr::from_ptr(priority).to_str().unwrap() };
 
     match conn.send_response_with_priority(
         quic_conn,
         stream_id,
         &resp_headers,
-        &priority,
+        priority,
         fin,
     ) {
         Ok(_) => 0,
@@ -263,6 +304,61 @@
 }
 
 #[no_mangle]
+#[cfg(feature = "sfv")]
+pub extern fn quiche_h3_parse_extensible_priority(
+    priority: *const u8, priority_len: size_t, parsed: &mut Priority,
+) -> c_int {
+    let priority = unsafe { slice::from_raw_parts(priority, priority_len) };
+
+    match h3::Priority::try_from(priority) {
+        Ok(v) => {
+            parsed.urgency = v.urgency;
+            parsed.incremental = v.incremental;
+            0
+        },
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_send_priority_update_for_request(
+    conn: &mut h3::Connection, quic_conn: &mut Connection, stream_id: u64,
+    priority: &Priority,
+) -> c_int {
+    match conn.send_priority_update_for_request(quic_conn, stream_id, priority) {
+        Ok(()) => 0,
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
+pub extern fn quiche_h3_take_last_priority_update(
+    conn: &mut h3::Connection, prioritized_element_id: u64,
+    cb: extern fn(
+        priority_field_value: *const u8,
+        priority_field_value_len: size_t,
+        argp: *mut c_void,
+    ) -> c_int,
+    argp: *mut c_void,
+) -> c_int {
+    match conn.take_last_priority_update(prioritized_element_id) {
+        Ok(priority) => {
+            let rc = cb(priority.as_ptr(), priority.len(), argp);
+
+            if rc != 0 {
+                return rc;
+            }
+
+            0
+        },
+
+        Err(e) => e.to_c() as c_int,
+    }
+}
+
+#[no_mangle]
 pub extern fn quiche_h3_dgram_enabled_by_peer(
     conn: &h3::Connection, quic_conn: &Connection,
 ) -> bool {
diff --git a/src/h3/frame.rs b/src/h3/frame.rs
index 085524b..76160fe 100644
--- a/src/h3/frame.rs
+++ b/src/h3/frame.rs
@@ -26,7 +26,8 @@
 
 use super::Result;
 
-use crate::octets;
+#[cfg(feature = "qlog")]
+use qlog::events::h3::Http3Frame;
 
 pub const DATA_FRAME_TYPE_ID: u64 = 0x0;
 pub const HEADERS_FRAME_TYPE_ID: u64 = 0x1;
@@ -35,16 +36,19 @@
 pub const PUSH_PROMISE_FRAME_TYPE_ID: u64 = 0x5;
 pub const GOAWAY_FRAME_TYPE_ID: u64 = 0x6;
 pub const MAX_PUSH_FRAME_TYPE_ID: u64 = 0xD;
+pub const PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID: u64 = 0xF0700;
+pub const PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID: u64 = 0xF0701;
 
-const SETTINGS_QPACK_MAX_TABLE_CAPACITY: u64 = 0x1;
-const SETTINGS_MAX_HEADER_LIST_SIZE: u64 = 0x6;
-const SETTINGS_QPACK_BLOCKED_STREAMS: u64 = 0x7;
-const SETTINGS_H3_DATAGRAM: u64 = 0x276;
+pub const SETTINGS_QPACK_MAX_TABLE_CAPACITY: u64 = 0x1;
+pub const SETTINGS_MAX_FIELD_SECTION_SIZE: u64 = 0x6;
+pub const SETTINGS_QPACK_BLOCKED_STREAMS: u64 = 0x7;
+pub const SETTINGS_ENABLE_CONNECT_PROTOCOL: u64 = 0x8;
+pub const SETTINGS_H3_DATAGRAM: u64 = 0x276;
 
 // Permit between 16 maximally-encoded and 128 minimally-encoded SETTINGS.
 const MAX_SETTINGS_PAYLOAD_SIZE: usize = 256;
 
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
 pub enum Frame {
     Data {
         payload: Vec<u8>,
@@ -59,11 +63,13 @@
     },
 
     Settings {
-        max_header_list_size: Option<u64>,
+        max_field_section_size: Option<u64>,
         qpack_max_table_capacity: Option<u64>,
         qpack_blocked_streams: Option<u64>,
+        connect_protocol_enabled: Option<u64>,
         h3_datagram: Option<u64>,
         grease: Option<(u64, u64)>,
+        raw: Option<Vec<(u64, u64)>>,
     },
 
     PushPromise {
@@ -79,7 +85,20 @@
         push_id: u64,
     },
 
-    Unknown,
+    PriorityUpdateRequest {
+        prioritized_element_id: u64,
+        priority_field_value: Vec<u8>,
+    },
+
+    PriorityUpdatePush {
+        prioritized_element_id: u64,
+        priority_field_value: Vec<u8>,
+    },
+
+    Unknown {
+        raw_type: u64,
+        payload_length: u64,
+    },
 }
 
 impl Frame {
@@ -116,7 +135,14 @@
                 push_id: b.get_varint()?,
             },
 
-            _ => Frame::Unknown,
+            PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID |
+            PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID =>
+                parse_priority_update(frame_type, payload_length, &mut b)?,
+
+            _ => Frame::Unknown {
+                raw_type: frame_type,
+                payload_length,
+            },
         };
 
         Ok(frame)
@@ -148,16 +174,18 @@
             },
 
             Frame::Settings {
-                max_header_list_size,
+                max_field_section_size,
                 qpack_max_table_capacity,
                 qpack_blocked_streams,
+                connect_protocol_enabled,
                 h3_datagram,
                 grease,
+                ..
             } => {
                 let mut len = 0;
 
-                if let Some(val) = max_header_list_size {
-                    len += octets::varint_len(SETTINGS_MAX_HEADER_LIST_SIZE);
+                if let Some(val) = max_field_section_size {
+                    len += octets::varint_len(SETTINGS_MAX_FIELD_SECTION_SIZE);
                     len += octets::varint_len(*val);
                 }
 
@@ -171,6 +199,11 @@
                     len += octets::varint_len(*val);
                 }
 
+                if let Some(val) = connect_protocol_enabled {
+                    len += octets::varint_len(SETTINGS_ENABLE_CONNECT_PROTOCOL);
+                    len += octets::varint_len(*val);
+                }
+
                 if let Some(val) = h3_datagram {
                     len += octets::varint_len(SETTINGS_H3_DATAGRAM);
                     len += octets::varint_len(*val);
@@ -184,24 +217,29 @@
                 b.put_varint(SETTINGS_FRAME_TYPE_ID)?;
                 b.put_varint(len as u64)?;
 
-                if let Some(val) = max_header_list_size {
-                    b.put_varint(SETTINGS_MAX_HEADER_LIST_SIZE)?;
-                    b.put_varint(*val as u64)?;
+                if let Some(val) = max_field_section_size {
+                    b.put_varint(SETTINGS_MAX_FIELD_SECTION_SIZE)?;
+                    b.put_varint(*val)?;
                 }
 
                 if let Some(val) = qpack_max_table_capacity {
                     b.put_varint(SETTINGS_QPACK_MAX_TABLE_CAPACITY)?;
-                    b.put_varint(*val as u64)?;
+                    b.put_varint(*val)?;
                 }
 
                 if let Some(val) = qpack_blocked_streams {
                     b.put_varint(SETTINGS_QPACK_BLOCKED_STREAMS)?;
-                    b.put_varint(*val as u64)?;
+                    b.put_varint(*val)?;
+                }
+
+                if let Some(val) = connect_protocol_enabled {
+                    b.put_varint(SETTINGS_ENABLE_CONNECT_PROTOCOL)?;
+                    b.put_varint(*val)?;
                 }
 
                 if let Some(val) = h3_datagram {
                     b.put_varint(SETTINGS_H3_DATAGRAM)?;
-                    b.put_varint(*val as u64)?;
+                    b.put_varint(*val)?;
                 }
 
                 if let Some(val) = grease {
@@ -236,35 +274,188 @@
                 b.put_varint(*push_id)?;
             },
 
-            Frame::Unknown => unreachable!(),
+            Frame::PriorityUpdateRequest {
+                prioritized_element_id,
+                priority_field_value,
+            } => {
+                let len = octets::varint_len(*prioritized_element_id) +
+                    priority_field_value.len();
+
+                b.put_varint(PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID)?;
+                b.put_varint(len as u64)?;
+
+                b.put_varint(*prioritized_element_id)?;
+                b.put_bytes(priority_field_value)?;
+            },
+
+            Frame::PriorityUpdatePush {
+                prioritized_element_id,
+                priority_field_value,
+            } => {
+                let len = octets::varint_len(*prioritized_element_id) +
+                    priority_field_value.len();
+
+                b.put_varint(PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID)?;
+                b.put_varint(len as u64)?;
+
+                b.put_varint(*prioritized_element_id)?;
+                b.put_bytes(priority_field_value)?;
+            },
+
+            Frame::Unknown { .. } => unreachable!(),
         }
 
         Ok(before - b.cap())
     }
+
+    #[cfg(feature = "qlog")]
+    pub fn to_qlog(&self) -> Http3Frame {
+        use qlog::events::RawInfo;
+
+        match self {
+            Frame::Data { .. } => Http3Frame::Data { raw: None },
+
+            // Qlog expects the `headers` to be represented as an array of
+            // name:value pairs. At this stage, we only have the qpack block, so
+            // populate the field with an empty vec.
+            Frame::Headers { .. } => Http3Frame::Headers { headers: vec![] },
+
+            Frame::CancelPush { push_id } =>
+                Http3Frame::CancelPush { push_id: *push_id },
+
+            Frame::Settings {
+                max_field_section_size,
+                qpack_max_table_capacity,
+                qpack_blocked_streams,
+                connect_protocol_enabled,
+                h3_datagram,
+                grease,
+                ..
+            } => {
+                let mut settings = vec![];
+
+                if let Some(v) = max_field_section_size {
+                    settings.push(qlog::events::h3::Setting {
+                        name: "MAX_FIELD_SECTION_SIZE".to_string(),
+                        value: *v,
+                    });
+                }
+
+                if let Some(v) = qpack_max_table_capacity {
+                    settings.push(qlog::events::h3::Setting {
+                        name: "QPACK_MAX_TABLE_CAPACITY".to_string(),
+                        value: *v,
+                    });
+                }
+
+                if let Some(v) = qpack_blocked_streams {
+                    settings.push(qlog::events::h3::Setting {
+                        name: "QPACK_BLOCKED_STREAMS".to_string(),
+                        value: *v,
+                    });
+                }
+
+                if let Some(v) = connect_protocol_enabled {
+                    settings.push(qlog::events::h3::Setting {
+                        name: "SETTINGS_ENABLE_CONNECT_PROTOCOL".to_string(),
+                        value: *v,
+                    });
+                }
+
+                if let Some(v) = h3_datagram {
+                    settings.push(qlog::events::h3::Setting {
+                        name: "H3_DATAGRAM".to_string(),
+                        value: *v,
+                    });
+                }
+
+                if let Some((k, v)) = grease {
+                    settings.push(qlog::events::h3::Setting {
+                        name: k.to_string(),
+                        value: *v,
+                    });
+                }
+
+                qlog::events::h3::Http3Frame::Settings { settings }
+            },
+
+            // Qlog expects the `headers` to be represented as an array of
+            // name:value pairs. At this stage, we only have the qpack block, so
+            // populate the field with an empty vec.
+            Frame::PushPromise { push_id, .. } => Http3Frame::PushPromise {
+                push_id: *push_id,
+                headers: vec![],
+            },
+
+            Frame::GoAway { id } => Http3Frame::Goaway { id: *id },
+
+            Frame::MaxPushId { push_id } =>
+                Http3Frame::MaxPushId { push_id: *push_id },
+
+            Frame::PriorityUpdateRequest {
+                prioritized_element_id,
+                priority_field_value,
+            } => Http3Frame::PriorityUpdate {
+                target_stream_type:
+                    qlog::events::h3::H3PriorityTargetStreamType::Request,
+                prioritized_element_id: *prioritized_element_id,
+                priority_field_value: String::from_utf8_lossy(
+                    priority_field_value,
+                )
+                .into_owned(),
+            },
+
+            Frame::PriorityUpdatePush {
+                prioritized_element_id,
+                priority_field_value,
+            } => Http3Frame::PriorityUpdate {
+                target_stream_type:
+                    qlog::events::h3::H3PriorityTargetStreamType::Request,
+                prioritized_element_id: *prioritized_element_id,
+                priority_field_value: String::from_utf8_lossy(
+                    priority_field_value,
+                )
+                .into_owned(),
+            },
+
+            Frame::Unknown {
+                raw_type,
+                payload_length,
+            } => Http3Frame::Unknown {
+                frame_type_value: *raw_type,
+                raw: Some(RawInfo {
+                    data: None,
+                    payload_length: Some(*payload_length),
+                    length: None,
+                }),
+            },
+        }
+    }
 }
 
 impl std::fmt::Debug for Frame {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         match self {
-            Frame::Data { payload } => {
-                write!(f, "DATA len={}", payload.len())?;
+            Frame::Data { .. } => {
+                write!(f, "DATA")?;
             },
 
-            Frame::Headers { header_block } => {
-                write!(f, "HEADERS len={}", header_block.len())?;
+            Frame::Headers { .. } => {
+                write!(f, "HEADERS")?;
             },
 
             Frame::CancelPush { push_id } => {
-                write!(f, "CANCEL_PUSH push_id={}", push_id)?;
+                write!(f, "CANCEL_PUSH push_id={push_id}")?;
             },
 
             Frame::Settings {
-                max_header_list_size,
+                max_field_section_size,
                 qpack_max_table_capacity,
                 qpack_blocked_streams,
+                raw,
                 ..
             } => {
-                write!(f, "SETTINGS max_headers={:?}, qpack_max_table={:?}, qpack_blocked={:?} ", max_header_list_size, qpack_max_table_capacity, qpack_blocked_streams)?;
+                write!(f, "SETTINGS max_field_section={max_field_section_size:?}, qpack_max_table={qpack_max_table_capacity:?}, qpack_blocked={qpack_blocked_streams:?} raw={raw:?}")?;
             },
 
             Frame::PushPromise {
@@ -280,15 +471,39 @@
             },
 
             Frame::GoAway { id } => {
-                write!(f, "GOAWAY id={}", id)?;
+                write!(f, "GOAWAY id={id}")?;
             },
 
             Frame::MaxPushId { push_id } => {
-                write!(f, "MAX_PUSH_ID push_id={}", push_id)?;
+                write!(f, "MAX_PUSH_ID push_id={push_id}")?;
             },
 
-            Frame::Unknown => {
-                write!(f, "UNKNOWN")?;
+            Frame::PriorityUpdateRequest {
+                prioritized_element_id,
+                priority_field_value,
+            } => {
+                write!(
+                    f,
+                    "PRIORITY_UPDATE request_stream_id={}, priority_field_len={}",
+                    prioritized_element_id,
+                    priority_field_value.len()
+                )?;
+            },
+
+            Frame::PriorityUpdatePush {
+                prioritized_element_id,
+                priority_field_value,
+            } => {
+                write!(
+                    f,
+                    "PRIORITY_UPDATE push_id={}, priority_field_len={}",
+                    prioritized_element_id,
+                    priority_field_value.len()
+                )?;
+            },
+
+            Frame::Unknown { raw_type, .. } => {
+                write!(f, "UNKNOWN raw_type={raw_type}",)?;
             },
         }
 
@@ -299,10 +514,12 @@
 fn parse_settings_frame(
     b: &mut octets::Octets, settings_length: usize,
 ) -> Result<Frame> {
-    let mut max_header_list_size = None;
+    let mut max_field_section_size = None;
     let mut qpack_max_table_capacity = None;
     let mut qpack_blocked_streams = None;
+    let mut connect_protocol_enabled = None;
     let mut h3_datagram = None;
+    let mut raw = Vec::new();
 
     // Reject SETTINGS frames that are too long.
     if settings_length > MAX_SETTINGS_PAYLOAD_SIZE {
@@ -310,28 +527,40 @@
     }
 
     while b.off() < settings_length {
-        let setting_ty = b.get_varint()?;
-        let settings_val = b.get_varint()?;
+        let identifier = b.get_varint()?;
+        let value = b.get_varint()?;
 
-        match setting_ty {
+        // MAX_SETTINGS_PAYLOAD_SIZE protects us from storing too many raw
+        // settings.
+        raw.push((identifier, value));
+
+        match identifier {
             SETTINGS_QPACK_MAX_TABLE_CAPACITY => {
-                qpack_max_table_capacity = Some(settings_val);
+                qpack_max_table_capacity = Some(value);
             },
 
-            SETTINGS_MAX_HEADER_LIST_SIZE => {
-                max_header_list_size = Some(settings_val);
+            SETTINGS_MAX_FIELD_SECTION_SIZE => {
+                max_field_section_size = Some(value);
             },
 
             SETTINGS_QPACK_BLOCKED_STREAMS => {
-                qpack_blocked_streams = Some(settings_val);
+                qpack_blocked_streams = Some(value);
             },
 
-            SETTINGS_H3_DATAGRAM => {
-                if settings_val > 1 {
+            SETTINGS_ENABLE_CONNECT_PROTOCOL => {
+                if value > 1 {
                     return Err(super::Error::SettingsError);
                 }
 
-                h3_datagram = Some(settings_val);
+                connect_protocol_enabled = Some(value);
+            },
+
+            SETTINGS_H3_DATAGRAM => {
+                if value > 1 {
+                    return Err(super::Error::SettingsError);
+                }
+
+                h3_datagram = Some(value);
             },
 
             // Reserved values overlap with HTTP/2 and MUST be rejected
@@ -344,11 +573,13 @@
     }
 
     Ok(Frame::Settings {
-        max_header_list_size,
+        max_field_section_size,
         qpack_max_table_capacity,
         qpack_blocked_streams,
+        connect_protocol_enabled,
         h3_datagram,
         grease: None,
+        raw: Some(raw),
     })
 }
 
@@ -365,6 +596,31 @@
     })
 }
 
+fn parse_priority_update(
+    frame_type: u64, payload_length: u64, b: &mut octets::Octets,
+) -> Result<Frame> {
+    let prioritized_element_id = b.get_varint()?;
+    let priority_field_value_length =
+        payload_length - octets::varint_len(prioritized_element_id) as u64;
+    let priority_field_value =
+        b.get_bytes(priority_field_value_length as usize)?.to_vec();
+
+    match frame_type {
+        PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID =>
+            Ok(Frame::PriorityUpdateRequest {
+                prioritized_element_id,
+                priority_field_value,
+            }),
+
+        PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID => Ok(Frame::PriorityUpdatePush {
+            prioritized_element_id,
+            priority_field_value,
+        }),
+
+        _ => unreachable!(),
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -456,15 +712,25 @@
     fn settings_all_no_grease() {
         let mut d = [42; 128];
 
+        let raw_settings = vec![
+            (SETTINGS_MAX_FIELD_SECTION_SIZE, 0),
+            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
+            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
+            (SETTINGS_ENABLE_CONNECT_PROTOCOL, 0),
+            (SETTINGS_H3_DATAGRAM, 0),
+        ];
+
         let frame = Frame::Settings {
-            max_header_list_size: Some(0),
+            max_field_section_size: Some(0),
             qpack_max_table_capacity: Some(0),
             qpack_blocked_streams: Some(0),
+            connect_protocol_enabled: Some(0),
             h3_datagram: Some(0),
             grease: None,
+            raw: Some(raw_settings),
         };
 
-        let frame_payload_len = 9;
+        let frame_payload_len = 11;
         let frame_header_len = 2;
 
         let wire_len = {
@@ -490,23 +756,37 @@
         let mut d = [42; 128];
 
         let frame = Frame::Settings {
-            max_header_list_size: Some(0),
+            max_field_section_size: Some(0),
             qpack_max_table_capacity: Some(0),
             qpack_blocked_streams: Some(0),
+            connect_protocol_enabled: Some(0),
             h3_datagram: Some(0),
             grease: Some((33, 33)),
+            raw: Default::default(),
         };
 
-        // Frame parsing will always ignore GREASE values.
+        let raw_settings = vec![
+            (SETTINGS_MAX_FIELD_SECTION_SIZE, 0),
+            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
+            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
+            (SETTINGS_ENABLE_CONNECT_PROTOCOL, 0),
+            (SETTINGS_H3_DATAGRAM, 0),
+            (33, 33),
+        ];
+
+        // Frame parsing will not populate GREASE property but will be in the
+        // raw info.
         let frame_parsed = Frame::Settings {
-            max_header_list_size: Some(0),
+            max_field_section_size: Some(0),
             qpack_max_table_capacity: Some(0),
             qpack_blocked_streams: Some(0),
+            connect_protocol_enabled: Some(0),
             h3_datagram: Some(0),
             grease: None,
+            raw: Some(raw_settings),
         };
 
-        let frame_payload_len = 11;
+        let frame_payload_len = 13;
         let frame_header_len = 2;
 
         let wire_len = {
@@ -531,12 +811,16 @@
     fn settings_h3_only() {
         let mut d = [42; 128];
 
+        let raw_settings = vec![(SETTINGS_MAX_FIELD_SECTION_SIZE, 1024)];
+
         let frame = Frame::Settings {
-            max_header_list_size: Some(1024),
+            max_field_section_size: Some(1024),
             qpack_max_table_capacity: None,
             qpack_blocked_streams: None,
+            connect_protocol_enabled: None,
             h3_datagram: None,
             grease: None,
+            raw: Some(raw_settings),
         };
 
         let frame_payload_len = 3;
@@ -561,15 +845,92 @@
     }
 
     #[test]
+    fn settings_h3_connect_protocol_enabled() {
+        let mut d = [42; 128];
+
+        let raw_settings = vec![(SETTINGS_ENABLE_CONNECT_PROTOCOL, 1)];
+
+        let frame = Frame::Settings {
+            max_field_section_size: None,
+            qpack_max_table_capacity: None,
+            qpack_blocked_streams: None,
+            connect_protocol_enabled: Some(1),
+            h3_datagram: None,
+            grease: None,
+            raw: Some(raw_settings),
+        };
+
+        let frame_payload_len = 2;
+        let frame_header_len = 2;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, frame_header_len + frame_payload_len);
+
+        assert_eq!(
+            Frame::from_bytes(
+                SETTINGS_FRAME_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn settings_h3_connect_protocol_enabled_bad() {
+        let mut d = [42; 128];
+
+        let raw_settings = vec![(SETTINGS_ENABLE_CONNECT_PROTOCOL, 9)];
+
+        let frame = Frame::Settings {
+            max_field_section_size: None,
+            qpack_max_table_capacity: None,
+            qpack_blocked_streams: None,
+            connect_protocol_enabled: Some(9),
+            h3_datagram: None,
+            grease: None,
+            raw: Some(raw_settings),
+        };
+
+        let frame_payload_len = 2;
+        let frame_header_len = 2;
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, frame_header_len + frame_payload_len);
+
+        assert_eq!(
+            Frame::from_bytes(
+                SETTINGS_FRAME_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            ),
+            Err(crate::h3::Error::SettingsError)
+        );
+    }
+
+    #[test]
     fn settings_h3_dgram_only() {
         let mut d = [42; 128];
 
+        let raw_settings = vec![(SETTINGS_H3_DATAGRAM, 1)];
+
         let frame = Frame::Settings {
-            max_header_list_size: None,
+            max_field_section_size: None,
             qpack_max_table_capacity: None,
             qpack_blocked_streams: None,
+            connect_protocol_enabled: None,
             h3_datagram: Some(1),
             grease: None,
+            raw: Some(raw_settings),
         };
 
         let frame_payload_len = 3;
@@ -598,11 +959,13 @@
         let mut d = [42; 128];
 
         let frame = Frame::Settings {
-            max_header_list_size: None,
+            max_field_section_size: None,
             qpack_max_table_capacity: None,
             qpack_blocked_streams: None,
+            connect_protocol_enabled: None,
             h3_datagram: Some(5),
             grease: None,
+            raw: Default::default(),
         };
 
         let frame_payload_len = 3;
@@ -629,12 +992,19 @@
     fn settings_qpack_only() {
         let mut d = [42; 128];
 
+        let raw_settings = vec![
+            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
+            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
+        ];
+
         let frame = Frame::Settings {
-            max_header_list_size: None,
+            max_field_section_size: None,
             qpack_max_table_capacity: Some(0),
             qpack_blocked_streams: Some(0),
+            connect_protocol_enabled: None,
             h3_datagram: None,
             grease: None,
+            raw: Some(raw_settings),
         };
 
         let frame_payload_len = 4;
@@ -837,9 +1207,79 @@
     }
 
     #[test]
+    fn priority_update_request() {
+        let mut d = [42; 128];
+
+        let prioritized_element_id = 4;
+        let priority_field_value = b"abcdefghijklm".to_vec();
+        let frame_payload_len = 1 + priority_field_value.len();
+        let frame_header_len = 5;
+
+        let frame = Frame::PriorityUpdateRequest {
+            prioritized_element_id,
+            priority_field_value,
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, frame_header_len + frame_payload_len);
+
+        assert_eq!(
+            Frame::from_bytes(
+                PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
+    fn priority_update_push() {
+        let mut d = [42; 128];
+
+        let prioritized_element_id = 6;
+        let priority_field_value = b"abcdefghijklm".to_vec();
+        let frame_payload_len = 1 + priority_field_value.len();
+        let frame_header_len = 5;
+
+        let frame = Frame::PriorityUpdatePush {
+            prioritized_element_id,
+            priority_field_value,
+        };
+
+        let wire_len = {
+            let mut b = octets::OctetsMut::with_slice(&mut d);
+            frame.to_bytes(&mut b).unwrap()
+        };
+
+        assert_eq!(wire_len, frame_header_len + frame_payload_len);
+
+        assert_eq!(
+            Frame::from_bytes(
+                PRIORITY_UPDATE_FRAME_PUSH_TYPE_ID,
+                frame_payload_len as u64,
+                &d[frame_header_len..]
+            )
+            .unwrap(),
+            frame
+        );
+    }
+
+    #[test]
     fn unknown_type() {
         let d = [42; 12];
 
-        assert_eq!(Frame::from_bytes(255, 12345, &d[..]), Ok(Frame::Unknown));
+        assert_eq!(
+            Frame::from_bytes(255, 12345, &d[..]),
+            Ok(Frame::Unknown {
+                raw_type: 255,
+                payload_length: 12345
+            })
+        );
     }
 }
diff --git a/src/h3/mod.rs b/src/h3/mod.rs
index dd2a51a..f5203d1 100644
--- a/src/h3/mod.rs
+++ b/src/h3/mod.rs
@@ -60,8 +60,9 @@
 //! ```no_run
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let from = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::accept(&scid, None, from, &mut config).unwrap();
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! # let mut conn = quiche::accept(&scid, None, local, peer, &mut config).unwrap();
 //! # let h3_config = quiche::h3::Config::new()?;
 //! let h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
 //! # Ok::<(), quiche::h3::Error>(())
@@ -76,8 +77,9 @@
 //! ```no_run
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let to = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::connect(None, &scid, to, &mut config).unwrap();
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! # let mut conn = quiche::connect(None, &scid, local, peer, &mut config).unwrap();
 //! # let h3_config = quiche::h3::Config::new()?;
 //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
 //! let req = vec![
@@ -98,8 +100,9 @@
 //! ```no_run
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let to = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::connect(None, &scid, to, &mut config).unwrap();
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! # let mut conn = quiche::connect(None, &scid, local, peer, &mut config).unwrap();
 //! # let h3_config = quiche::h3::Config::new()?;
 //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
 //! let req = vec![
@@ -129,8 +132,9 @@
 //!
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let from = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::accept(&scid, None, from, &mut config).unwrap();
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:1234".parse().unwrap();
+//! # let mut conn = quiche::accept(&scid, None, local, peer, &mut config).unwrap();
 //! # let h3_config = quiche::h3::Config::new()?;
 //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
 //! loop {
@@ -164,8 +168,14 @@
 //!             // Peer terminated stream, handle it.
 //!         },
 //!
+//!         Ok((stream_id, quiche::h3::Event::Reset(err))) => {
+//!             // Peer reset the stream, handle it.
+//!         },
+//!
 //!         Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
 //!
+//!         Ok((_flow_id, quiche::h3::Event::PriorityUpdate)) => (),
+//!
 //!         Ok((goaway_id, quiche::h3::Event::GoAway)) => {
 //!              // Peer signalled it is going away, handle it.
 //!         },
@@ -191,8 +201,9 @@
 //!
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION).unwrap();
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let to = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::connect(None, &scid, to, &mut config).unwrap();
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:1234".parse().unwrap();
+//! # let mut conn = quiche::connect(None, &scid, local, peer, &mut config).unwrap();
 //! # let h3_config = quiche::h3::Config::new()?;
 //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?;
 //! loop {
@@ -220,8 +231,14 @@
 //!             // Peer terminated stream, handle it.
 //!         },
 //!
+//!         Ok((stream_id, quiche::h3::Event::Reset(err))) => {
+//!             // Peer reset the stream, handle it.
+//!         },
+//!
 //!         Ok((_flow_id, quiche::h3::Event::Datagram)) => (),
 //!
+//!         Ok((_prioritized_element_id, quiche::h3::Event::PriorityUpdate)) => (),
+//!
 //!         Ok((goaway_id, quiche::h3::Event::GoAway)) => {
 //!              // Peer signalled it is going away, handle it.
 //!         },
@@ -269,10 +286,35 @@
 //! [`send_response()`]: struct.Connection.html#method.send_response
 //! [`send_body()`]: struct.Connection.html#method.send_body
 
-use std::collections::HashMap;
 use std::collections::VecDeque;
 
-use crate::octets;
+#[cfg(feature = "sfv")]
+use std::convert::TryFrom;
+use std::fmt;
+use std::fmt::Write;
+
+#[cfg(feature = "qlog")]
+use qlog::events::h3::H3FrameCreated;
+#[cfg(feature = "qlog")]
+use qlog::events::h3::H3FrameParsed;
+#[cfg(feature = "qlog")]
+use qlog::events::h3::H3Owner;
+#[cfg(feature = "qlog")]
+use qlog::events::h3::H3PriorityTargetStreamType;
+#[cfg(feature = "qlog")]
+use qlog::events::h3::H3StreamType;
+#[cfg(feature = "qlog")]
+use qlog::events::h3::H3StreamTypeSet;
+#[cfg(feature = "qlog")]
+use qlog::events::h3::Http3EventType;
+#[cfg(feature = "qlog")]
+use qlog::events::h3::Http3Frame;
+#[cfg(feature = "qlog")]
+use qlog::events::EventData;
+#[cfg(feature = "qlog")]
+use qlog::events::EventImportance;
+#[cfg(feature = "qlog")]
+use qlog::events::EventType;
 
 /// List of ALPN tokens of supported HTTP/3 versions.
 ///
@@ -281,11 +323,29 @@
 ///
 /// [`Config::set_application_protos()`]:
 /// ../struct.Config.html#method.set_application_protos
-pub const APPLICATION_PROTOCOL: &[u8] = b"\x02h3\x05h3-29\x05h3-28\x05h3-27";
+pub const APPLICATION_PROTOCOL: &[&[u8]] = &[b"h3", b"h3-29", b"h3-28", b"h3-27"];
 
 // The offset used when converting HTTP/3 urgency to quiche urgency.
 const PRIORITY_URGENCY_OFFSET: u8 = 124;
 
+// Parameter values as specified in [Extensible Priorities].
+//
+// [Extensible Priorities]: https://www.rfc-editor.org/rfc/rfc9218.html#section-4.
+const PRIORITY_URGENCY_LOWER_BOUND: u8 = 0;
+const PRIORITY_URGENCY_UPPER_BOUND: u8 = 7;
+const PRIORITY_URGENCY_DEFAULT: u8 = 3;
+const PRIORITY_INCREMENTAL_DEFAULT: bool = false;
+
+#[cfg(feature = "qlog")]
+const QLOG_FRAME_CREATED: EventType =
+    EventType::Http3EventType(Http3EventType::FrameCreated);
+#[cfg(feature = "qlog")]
+const QLOG_FRAME_PARSED: EventType =
+    EventType::Http3EventType(Http3EventType::FrameParsed);
+#[cfg(feature = "qlog")]
+const QLOG_STREAM_TYPE_SET: EventType =
+    EventType::Http3EventType(Http3EventType::StreamTypeSet);
+
 /// A specialized [`Result`] type for quiche HTTP/3 operations.
 ///
 /// This type is used throughout quiche's HTTP/3 public API for any operation
@@ -295,7 +355,7 @@
 pub type Result<T> = std::result::Result<T, Error>;
 
 /// An HTTP/3 error.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum Error {
     /// There is no error or no work to do
     Done,
@@ -406,7 +466,7 @@
             Error::FrameUnexpected => -9,
             Error::FrameError => -10,
             Error::QpackDecompressionFailed => -11,
-            Error::TransportError { .. } => -12,
+            // -12 was previously used for TransportError, skip it
             Error::StreamBlocked => -13,
             Error::SettingsError => -14,
             Error::RequestRejected => -15,
@@ -415,13 +475,15 @@
             Error::MessageError => -18,
             Error::ConnectError => -19,
             Error::VersionFallback => -20,
+
+            Error::TransportError(quic_error) => quic_error.to_c() - 1000,
         }
     }
 }
 
 impl std::fmt::Display for Error {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        write!(f, "{:?}", self)
+        write!(f, "{self:?}")
     }
 }
 
@@ -449,22 +511,24 @@
 
 /// An HTTP/3 configuration.
 pub struct Config {
-    max_header_list_size: Option<u64>,
+    max_field_section_size: Option<u64>,
     qpack_max_table_capacity: Option<u64>,
     qpack_blocked_streams: Option<u64>,
+    connect_protocol_enabled: Option<u64>,
 }
 
 impl Config {
     /// Creates a new configuration object with default settings.
-    pub fn new() -> Result<Config> {
+    pub const fn new() -> Result<Config> {
         Ok(Config {
-            max_header_list_size: None,
+            max_field_section_size: None,
             qpack_max_table_capacity: None,
             qpack_blocked_streams: None,
+            connect_protocol_enabled: None,
         })
     }
 
-    /// Sets the `SETTINGS_MAX_HEADER_LIST_SIZE` setting.
+    /// Sets the `SETTINGS_MAX_FIELD_SECTION_SIZE` setting.
     ///
     /// By default no limit is enforced. When a request whose headers exceed
     /// the limit set by the application is received, the call to the [`poll()`]
@@ -473,8 +537,8 @@
     ///
     /// [`poll()`]: struct.Connection.html#method.poll
     /// [`Error::ExcessiveLoad`]: enum.Error.html#variant.ExcessiveLoad
-    pub fn set_max_header_list_size(&mut self, v: u64) {
-        self.max_header_list_size = Some(v);
+    pub fn set_max_field_section_size(&mut self, v: u64) {
+        self.max_field_section_size = Some(v);
     }
 
     /// Sets the `SETTINGS_QPACK_MAX_TABLE_CAPACITY` setting.
@@ -490,6 +554,17 @@
     pub fn set_qpack_blocked_streams(&mut self, v: u64) {
         self.qpack_blocked_streams = Some(v);
     }
+
+    /// Sets or omits the `SETTINGS_ENABLE_CONNECT_PROTOCOL` setting.
+    ///
+    /// The default value is `false`.
+    pub fn enable_extended_connect(&mut self, enabled: bool) {
+        if enabled {
+            self.connect_protocol_enabled = Some(1);
+        } else {
+            self.connect_protocol_enabled = None;
+        }
+    }
 }
 
 /// A trait for types with associated string name and value.
@@ -501,10 +576,37 @@
     fn value(&self) -> &[u8];
 }
 
+impl NameValue for (&[u8], &[u8]) {
+    fn name(&self) -> &[u8] {
+        self.0
+    }
+
+    fn value(&self) -> &[u8] {
+        self.1
+    }
+}
+
 /// An owned name-value pair representing a raw HTTP header.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
 pub struct Header(Vec<u8>, Vec<u8>);
 
+fn try_print_as_readable(hdr: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
+    match std::str::from_utf8(hdr) {
+        Ok(s) => f.write_str(&s.escape_default().to_string()),
+        Err(_) => write!(f, "{hdr:?}"),
+    }
+}
+
+impl fmt::Debug for Header {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_char('"')?;
+        try_print_as_readable(&self.0, f)?;
+        f.write_str(": ")?;
+        try_print_as_readable(&self.1, f)?;
+        f.write_char('"')
+    }
+}
+
 impl Header {
     /// Creates a new header.
     ///
@@ -525,12 +627,12 @@
 }
 
 /// A non-owned name-value pair representing a raw HTTP header.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub struct HeaderRef<'a>(&'a [u8], &'a [u8]);
 
 impl<'a> HeaderRef<'a> {
     /// Creates a new header.
-    pub fn new(name: &'a [u8], value: &'a [u8]) -> Self {
+    pub const fn new(name: &'a [u8], value: &'a [u8]) -> Self {
         Self(name, value)
     }
 }
@@ -546,7 +648,7 @@
 }
 
 /// An HTTP/3 connection event.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum Event {
     /// Request/response headers were received.
     Headers {
@@ -574,6 +676,11 @@
     /// Stream was closed,
     Finished,
 
+    /// Stream was reset.
+    ///
+    /// The associated data represents the error code sent by the peer.
+    Reset(u64),
+
     /// DATAGRAM was received.
     ///
     /// This indicates that the application can use the [`recv_dgram()`] method
@@ -587,15 +694,130 @@
     /// [`Done`]: enum.Error.html#variant.Done
     Datagram,
 
+    /// PRIORITY_UPDATE was received.
+    ///
+    /// This indicates that the application can use the
+    /// [`take_last_priority_update()`] method to take the last received
+    /// PRIORITY_UPDATE for a specified stream.
+    ///
+    /// This event is triggered once per stream until the last PRIORITY_UPDATE
+    /// is taken. It is recommended that applications defer taking the
+    /// PRIORITY_UPDATE until after [`poll()`] returns [`Done`].
+    ///
+    /// [`take_last_priority_update()`]: struct.Connection.html#method.take_last_priority_update
+    /// [`poll()`]: struct.Connection.html#method.poll
+    /// [`Done`]: enum.Error.html#variant.Done
+    PriorityUpdate,
+
     /// GOAWAY was received.
     GoAway,
 }
 
+/// Extensible Priorities parameters.
+///
+/// The `TryFrom` trait supports constructing this object from the serialized
+/// Structured Fields Dictionary field value. I.e, use `TryFrom` to parse the
+/// value of a Priority header field or a PRIORITY_UPDATE frame. Using this
+/// trait requires the `sfv` feature to be enabled.
+#[derive(Debug, PartialEq, Eq)]
+#[repr(C)]
+pub struct Priority {
+    urgency: u8,
+    incremental: bool,
+}
+
+impl Default for Priority {
+    fn default() -> Self {
+        Priority {
+            urgency: PRIORITY_URGENCY_DEFAULT,
+            incremental: PRIORITY_INCREMENTAL_DEFAULT,
+        }
+    }
+}
+
+impl Priority {
+    /// Creates a new Priority.
+    pub const fn new(urgency: u8, incremental: bool) -> Self {
+        Priority {
+            urgency,
+            incremental,
+        }
+    }
+}
+
+#[cfg(feature = "sfv")]
+#[cfg_attr(docsrs, doc(cfg(feature = "sfv")))]
+impl TryFrom<&[u8]> for Priority {
+    type Error = crate::h3::Error;
+
+    /// Try to parse an Extensible Priority field value.
+    ///
+    /// The field value is expected to be a Structured Fields Dictionary; see
+    /// [Extensible Priorities].
+    ///
+    /// If the `u` or `i` fields are contained with correct types, a constructed
+    /// Priority object is returned. Note that urgency values outside of valid
+    /// range (0 through 7) are clamped to 7.
+    ///
+    /// If the `u` or `i` fields are contained with the wrong types,
+    /// Error::Done is returned.
+    ///
+    /// Omitted parameters will yield default values.
+    ///
+    /// [Extensible Priorities]: https://www.rfc-editor.org/rfc/rfc9218.html#section-4.
+    fn try_from(value: &[u8]) -> std::result::Result<Self, Self::Error> {
+        let dict = match sfv::Parser::parse_dictionary(value) {
+            Ok(v) => v,
+
+            Err(_) => return Err(Error::Done),
+        };
+
+        let urgency = match dict.get("u") {
+            // If there is a u parameter, try to read it as an Item of type
+            // Integer. If the value out of the spec's allowed range
+            // (0 through 7), that's an error so set it to the upper
+            // bound (lowest priority) to avoid interference with
+            // other streams.
+            Some(sfv::ListEntry::Item(item)) => match item.bare_item.as_int() {
+                Some(v) => {
+                    if !(PRIORITY_URGENCY_LOWER_BOUND as i64..=
+                        PRIORITY_URGENCY_UPPER_BOUND as i64)
+                        .contains(&v)
+                    {
+                        PRIORITY_URGENCY_UPPER_BOUND
+                    } else {
+                        v as u8
+                    }
+                },
+
+                None => return Err(Error::Done),
+            },
+
+            Some(sfv::ListEntry::InnerList(_)) => return Err(Error::Done),
+
+            // Omitted so use default value.
+            None => PRIORITY_URGENCY_DEFAULT,
+        };
+
+        let incremental = match dict.get("i") {
+            Some(sfv::ListEntry::Item(item)) =>
+                item.bare_item.as_bool().ok_or(Error::Done)?,
+
+            // Omitted so use default value.
+            _ => false,
+        };
+
+        Ok(Priority::new(urgency, incremental))
+    }
+}
+
 struct ConnectionSettings {
-    pub max_header_list_size: Option<u64>,
+    pub max_field_section_size: Option<u64>,
     pub qpack_max_table_capacity: Option<u64>,
     pub qpack_blocked_streams: Option<u64>,
+    pub connect_protocol_enabled: Option<u64>,
     pub h3_datagram: Option<u64>,
+    pub raw: Option<Vec<(u64, u64)>>,
 }
 
 struct QpackStreams {
@@ -610,7 +832,7 @@
     next_request_stream_id: u64,
     next_uni_stream_id: u64,
 
-    streams: HashMap<u64, stream::Stream>,
+    streams: crate::stream::StreamIdHashMap<stream::Stream>,
 
     local_settings: ConnectionSettings,
     peer_settings: ConnectionSettings,
@@ -621,7 +843,6 @@
     qpack_encoder: qpack::Encoder,
     qpack_decoder: qpack::Decoder,
 
-    #[allow(dead_code)]
     local_qpack_streams: QpackStreams,
     peer_qpack_streams: QpackStreams,
 
@@ -638,7 +859,6 @@
 }
 
 impl Connection {
-    #[allow(clippy::unnecessary_wraps)]
     fn new(
         config: &Config, is_server: bool, enable_dgram: bool,
     ) -> Result<Connection> {
@@ -652,20 +872,24 @@
 
             next_uni_stream_id: initial_uni_stream_id,
 
-            streams: HashMap::new(),
+            streams: Default::default(),
 
             local_settings: ConnectionSettings {
-                max_header_list_size: config.max_header_list_size,
+                max_field_section_size: config.max_field_section_size,
                 qpack_max_table_capacity: config.qpack_max_table_capacity,
                 qpack_blocked_streams: config.qpack_blocked_streams,
+                connect_protocol_enabled: config.connect_protocol_enabled,
                 h3_datagram,
+                raw: Default::default(),
             },
 
             peer_settings: ConnectionSettings {
-                max_header_list_size: None,
+                max_field_section_size: None,
                 qpack_max_table_capacity: None,
                 qpack_blocked_streams: None,
                 h3_datagram: None,
+                connect_protocol_enabled: None,
+                raw: Default::default(),
             },
 
             control_stream_id: None,
@@ -705,12 +929,23 @@
     /// On success the new connection is returned.
     ///
     /// The [`StreamLimit`] error is returned when the HTTP/3 control stream
-    /// cannot be created.
+    /// cannot be created due to stream limits.
     ///
-    /// [`StreamLimit`]: ../enum.Error.html#variant.InvalidState
+    /// The [`InternalError`] error is returned when either the underlying QUIC
+    /// connection is not in a suitable state, or the HTTP/3 control stream
+    /// cannot be created due to flow control limits.
+    ///
+    /// [`StreamLimit`]: ../enum.Error.html#variant.StreamLimit
+    /// [`InternalError`]: ../enum.Error.html#variant.InternalError
     pub fn with_transport(
         conn: &mut super::Connection, config: &Config,
     ) -> Result<Connection> {
+        let is_client = !conn.is_server;
+        if is_client && !(conn.is_established() || conn.is_in_early_data()) {
+            trace!("{} QUIC connection must be established or in early data before creating an HTTP/3 connection", conn.trace_id());
+            return Err(Error::InternalError);
+        }
+
         let mut http3_conn =
             Connection::new(config, conn.is_server, conn.dgram_enabled())?;
 
@@ -774,6 +1009,10 @@
         if let Err(e) = conn.stream_send(stream_id, b"", false) {
             self.streams.remove(&stream_id);
 
+            if e == super::Error::Done {
+                return Err(Error::StreamBlocked);
+            }
+
             return Err(e.into());
         };
 
@@ -806,10 +1045,10 @@
         &mut self, conn: &mut super::Connection, stream_id: u64, headers: &[T],
         fin: bool,
     ) -> Result<()> {
-        let priority = "u=3";
+        let priority = Default::default();
 
         self.send_response_with_priority(
-            conn, stream_id, headers, priority, fin,
+            conn, stream_id, headers, &priority, fin,
         )?;
 
         Ok(())
@@ -818,56 +1057,32 @@
     /// Sends an HTTP/3 response on the specified stream with specified
     /// priority.
     ///
+    /// The `priority` parameter represents [Extensible Priority]
+    /// parameters. If the urgency is outside the range 0-7, it will be clamped
+    /// to 7.
+    ///
     /// The [`StreamBlocked`] error is returned when the underlying QUIC stream
     /// doesn't have enough capacity for the operation to complete. When this
     /// happens the application should retry the operation once the stream is
     /// reported as writable again.
     ///
     /// [`StreamBlocked`]: enum.Error.html#variant.StreamBlocked
+    /// [Extensible Priority]: https://www.rfc-editor.org/rfc/rfc9218.html#section-4.
     pub fn send_response_with_priority<T: NameValue>(
         &mut self, conn: &mut super::Connection, stream_id: u64, headers: &[T],
-        priority: &str, fin: bool,
+        priority: &Priority, fin: bool,
     ) -> Result<()> {
         if !self.streams.contains_key(&stream_id) {
             return Err(Error::FrameUnexpected);
         }
 
-        let mut urgency = 3u8.saturating_add(PRIORITY_URGENCY_OFFSET);
-        let mut incremental = false;
+        // Clamp and shift urgency into quiche-priority space
+        let urgency = priority
+            .urgency
+            .clamp(PRIORITY_URGENCY_LOWER_BOUND, PRIORITY_URGENCY_UPPER_BOUND) +
+            PRIORITY_URGENCY_OFFSET;
 
-        for param in priority.split(',') {
-            if param.trim() == "i" {
-                incremental = true;
-                continue;
-            }
-
-            if param.trim().starts_with("u=") {
-                // u is an sh-integer (an i64) but it has a constrained range of
-                // 0-7. So detect anything outside that range and clamp it to
-                // the lowest urgency in order to avoid it interfering with
-                // valid items.
-                //
-                // TODO: this also detects when u is not an sh-integer and
-                // clamps it in the same way. A real structured header parser
-                // would actually fail to parse.
-                let mut u = param
-                    .rsplit('=')
-                    .next()
-                    .unwrap()
-                    .parse::<i64>()
-                    .unwrap_or(7);
-
-                if !(0..=7).contains(&u) {
-                    u = 7;
-                }
-
-                // The HTTP/3 urgency needs to be shifted into the quiche
-                // urgency range.
-                urgency = (u as u8).saturating_add(PRIORITY_URGENCY_OFFSET);
-            }
-        }
-
-        conn.stream_priority(stream_id, urgency, incremental)?;
+        conn.stream_priority(stream_id, urgency, priority.incremental)?;
 
         self.send_headers(conn, stream_id, headers, fin)?;
 
@@ -884,7 +1099,7 @@
         let mut header_block = vec![0; headers_len];
         let len = self
             .qpack_encoder
-            .encode(&headers, &mut header_block)
+            .encode(headers, &mut header_block)
             .map_err(|_| Error::InternalError)?;
 
         header_block.truncate(len);
@@ -904,8 +1119,17 @@
             self.frames_greased = true;
         }
 
-        let stream_cap = match conn.stream_capacity(stream_id) {
-            Ok(v) => v,
+        let header_block = self.encode_header_block(headers)?;
+
+        let overhead = octets::varint_len(frame::HEADERS_FRAME_TYPE_ID) +
+            octets::varint_len(header_block.len() as u64);
+
+        // Headers need to be sent atomically, so make sure the stream has
+        // enough capacity.
+        match conn.stream_writable(stream_id, overhead + header_block.len()) {
+            Ok(true) => (),
+
+            Ok(false) => return Err(Error::StreamBlocked),
 
             Err(e) => {
                 if conn.stream_finished(stream_id) {
@@ -916,14 +1140,13 @@
             },
         };
 
-        let header_block = self.encode_header_block(headers)?;
+        b.put_varint(frame::HEADERS_FRAME_TYPE_ID)?;
+        b.put_varint(header_block.len() as u64)?;
+        let off = b.off();
+        conn.stream_send(stream_id, &d[..off], false)?;
 
-        let overhead = octets::varint_len(frame::HEADERS_FRAME_TYPE_ID) +
-            octets::varint_len(header_block.len() as u64);
-
-        if stream_cap < overhead + header_block.len() {
-            return Err(Error::StreamBlocked);
-        }
+        // Sending header block separately avoids unnecessary copy.
+        conn.stream_send(stream_id, &header_block, fin)?;
 
         trace!(
             "{} tx frm HEADERS stream={} len={} fin={}",
@@ -933,13 +1156,27 @@
             fin
         );
 
-        b.put_varint(frame::HEADERS_FRAME_TYPE_ID)?;
-        b.put_varint(header_block.len() as u64)?;
-        let off = b.off();
-        conn.stream_send(stream_id, &d[..off], false)?;
+        qlog_with_type!(QLOG_FRAME_CREATED, conn.qlog, q, {
+            let qlog_headers = headers
+                .iter()
+                .map(|h| qlog::events::h3::HttpHeader {
+                    name: String::from_utf8_lossy(h.name()).into_owned(),
+                    value: String::from_utf8_lossy(h.value()).into_owned(),
+                })
+                .collect();
 
-        // Sending header block separately avoids unnecessary copy.
-        conn.stream_send(stream_id, &header_block, fin)?;
+            let frame = Http3Frame::Headers {
+                headers: qlog_headers,
+            };
+            let ev_data = EventData::H3FrameCreated(H3FrameCreated {
+                stream_id,
+                length: Some(header_block.len() as u64),
+                frame,
+                raw: None,
+            });
+
+            q.add_event_data_now(ev_data).ok();
+        });
 
         if let Some(s) = self.streams.get_mut(&stream_id) {
             s.initialize_local();
@@ -1011,6 +1248,7 @@
 
         // Make sure there is enough capacity to send the DATA frame header.
         if stream_cap < overhead {
+            let _ = conn.stream_writable(stream_id, overhead + 1);
             return Err(Error::Done);
         }
 
@@ -1023,17 +1261,10 @@
 
         // Again, avoid sending 0-length DATA frames when the fin flag is false.
         if body_len == 0 && !fin {
+            let _ = conn.stream_writable(stream_id, overhead + 1);
             return Err(Error::Done);
         }
 
-        trace!(
-            "{} tx frm DATA stream={} len={} fin={}",
-            conn.trace_id(),
-            stream_id,
-            body_len,
-            fin
-        );
-
         b.put_varint(frame::DATA_FRAME_TYPE_ID)?;
         b.put_varint(body_len as u64)?;
         let off = b.off();
@@ -1043,6 +1274,36 @@
         // Sending body separately avoids unnecessary copy.
         let written = conn.stream_send(stream_id, &body[..body_len], fin)?;
 
+        trace!(
+            "{} tx frm DATA stream={} len={} fin={}",
+            conn.trace_id(),
+            stream_id,
+            written,
+            fin
+        );
+
+        qlog_with_type!(QLOG_FRAME_CREATED, conn.qlog, q, {
+            let frame = Http3Frame::Data { raw: None };
+            let ev_data = EventData::H3FrameCreated(H3FrameCreated {
+                stream_id,
+                length: Some(written as u64),
+                frame,
+                raw: None,
+            });
+
+            q.add_event_data_now(ev_data).ok();
+        });
+
+        if written < body.len() {
+            // Ensure the peer is notified that the connection or stream is
+            // blocked when the stream's capacity is limited by flow control.
+            //
+            // We only need enough capacity to send a few bytes, to make sure
+            // the stream doesn't hang due to congestion window not growing
+            // enough.
+            let _ = conn.stream_writable(stream_id, overhead + 1);
+        }
+
         if fin && written == body.len() && conn.stream_finished(stream_id) {
             self.streams.remove(&stream_id);
         }
@@ -1062,18 +1323,29 @@
             conn.dgram_max_writable_len().is_some()
     }
 
+    /// Returns whether the peer enabled extended CONNECT support.
+    ///
+    /// Support is signalled by the peer's SETTINGS, so this method always
+    /// returns false until they have been processed using the [`poll()`]
+    /// method.
+    ///
+    /// [`poll()`]: struct.Connection.html#method.poll
+    pub fn extended_connect_enabled_by_peer(&self) -> bool {
+        self.peer_settings.connect_protocol_enabled == Some(1)
+    }
+
     /// Sends an HTTP/3 DATAGRAM with the specified flow ID.
     pub fn send_dgram(
         &mut self, conn: &mut super::Connection, flow_id: u64, buf: &[u8],
     ) -> Result<()> {
         let len = octets::varint_len(flow_id) + buf.len();
-        let mut d = vec![0; len as usize];
+        let mut d = vec![0; len];
         let mut b = octets::OctetsMut::with_slice(&mut d);
 
         b.put_varint(flow_id)?;
         b.put_bytes(buf)?;
 
-        conn.dgram_send(&d)?;
+        conn.dgram_send_vec(d)?;
 
         Ok(())
     }
@@ -1119,29 +1391,19 @@
     fn process_dgrams(
         &mut self, conn: &mut super::Connection,
     ) -> Result<(u64, Event)> {
-        let mut d = [0; 8];
+        if conn.dgram_recv_queue_len() > 0 {
+            if self.dgram_event_triggered {
+                return Err(Error::Done);
+            }
 
-        match conn.dgram_recv_peek(&mut d, 8) {
-            Ok(_) => {
-                if self.dgram_event_triggered {
-                    return Err(Error::Done);
-                }
+            self.dgram_event_triggered = true;
 
-                self.dgram_event_triggered = true;
-
-                Ok((0, Event::Datagram))
-            },
-
-            Err(crate::Error::Done) => {
-                // The dgram recv queue is empty, so re-arm the Datagram event
-                // so it is issued next time a DATAGRAM is received.
-                self.dgram_event_triggered = false;
-
-                Err(Error::Done)
-            },
-
-            Err(e) => Err(Error::TransportError(e)),
+            return Ok((0, Event::Datagram));
         }
+
+        self.dgram_event_triggered = false;
+
+        Err(Error::Done)
     }
 
     /// Reads request or response body data into the provided buffer.
@@ -1215,6 +1477,132 @@
         Ok(total)
     }
 
+    /// Sends a PRIORITY_UPDATE frame on the control stream with specified
+    /// request stream ID and priority.
+    ///
+    /// The `priority` parameter represents [Extensible Priority]
+    /// parameters. If the urgency is outside the range 0-7, it will be clamped
+    /// to 7.
+    ///
+    /// The [`StreamBlocked`] error is returned when the underlying QUIC stream
+    /// doesn't have enough capacity for the operation to complete. When this
+    /// happens the application should retry the operation once the stream is
+    /// reported as writable again.
+    ///
+    /// [`StreamBlocked`]: enum.Error.html#variant.StreamBlocked
+    /// [Extensible Priority]: https://www.rfc-editor.org/rfc/rfc9218.html#section-4.
+    pub fn send_priority_update_for_request(
+        &mut self, conn: &mut super::Connection, stream_id: u64,
+        priority: &Priority,
+    ) -> Result<()> {
+        let mut d = [42; 20];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        // Validate that it is sane to send PRIORITY_UPDATE.
+        if self.is_server {
+            return Err(Error::FrameUnexpected);
+        }
+
+        if stream_id % 4 != 0 {
+            return Err(Error::FrameUnexpected);
+        }
+
+        let control_stream_id =
+            self.control_stream_id.ok_or(Error::FrameUnexpected)?;
+
+        let urgency = priority
+            .urgency
+            .clamp(PRIORITY_URGENCY_LOWER_BOUND, PRIORITY_URGENCY_UPPER_BOUND);
+
+        let mut field_value = format!("u={urgency}");
+
+        if priority.incremental {
+            field_value.push_str(",i");
+        }
+
+        let priority_field_value = field_value.as_bytes();
+        let frame_payload_len =
+            octets::varint_len(stream_id) + priority_field_value.len();
+
+        let overhead =
+            octets::varint_len(frame::PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID) +
+                octets::varint_len(stream_id) +
+                octets::varint_len(frame_payload_len as u64);
+
+        // Make sure the control stream has enough capacity.
+        match conn.stream_writable(
+            control_stream_id,
+            overhead + priority_field_value.len(),
+        ) {
+            Ok(true) => (),
+
+            Ok(false) => return Err(Error::StreamBlocked),
+
+            Err(e) => {
+                return Err(e.into());
+            },
+        }
+
+        b.put_varint(frame::PRIORITY_UPDATE_FRAME_REQUEST_TYPE_ID)?;
+        b.put_varint(frame_payload_len as u64)?;
+        b.put_varint(stream_id)?;
+        let off = b.off();
+        conn.stream_send(control_stream_id, &d[..off], false)?;
+
+        // Sending field value separately avoids unnecessary copy.
+        conn.stream_send(control_stream_id, priority_field_value, false)?;
+
+        trace!(
+            "{} tx frm PRIORITY_UPDATE request_stream={} priority_field_value={}",
+            conn.trace_id(),
+            stream_id,
+            field_value,
+        );
+
+        qlog_with_type!(QLOG_FRAME_CREATED, conn.qlog, q, {
+            let frame = Http3Frame::PriorityUpdate {
+                target_stream_type: H3PriorityTargetStreamType::Request,
+                prioritized_element_id: stream_id,
+                priority_field_value: field_value.clone(),
+            };
+
+            let ev_data = EventData::H3FrameCreated(H3FrameCreated {
+                stream_id,
+                length: Some(priority_field_value.len() as u64),
+                frame,
+                raw: None,
+            });
+
+            q.add_event_data_now(ev_data).ok();
+        });
+
+        Ok(())
+    }
+
+    /// Take the last PRIORITY_UPDATE for a prioritized element ID.
+    ///
+    /// When the [`poll()`] method returns a [`PriorityUpdate`] event for a
+    /// prioritized element, the event has triggered and will not rearm until
+    /// applications call this method. It is recommended that applications defer
+    /// taking the PRIORITY_UPDATE until after [`poll()`] returns [`Done`].
+    ///
+    /// On success the Priority Field Value is returned, or [`Done`] if there is
+    /// no PRIORITY_UPDATE to read (either because there is no value to take, or
+    /// because the prioritized element does not exist).
+    ///
+    /// [`poll()`]: struct.Connection.html#method.poll
+    /// [`PriorityUpdate`]: enum.Event.html#variant.PriorityUpdate
+    /// [`Done`]: enum.Error.html#variant.Done
+    pub fn take_last_priority_update(
+        &mut self, prioritized_element_id: u64,
+    ) -> Result<Vec<u8>> {
+        if let Some(stream) = self.streams.get_mut(&prioritized_element_id) {
+            return stream.take_last_priority_update().ok_or(Error::Done);
+        }
+
+        Err(Error::Done)
+    }
+
     /// Processes HTTP/3 data received from the peer.
     ///
     /// On success it returns an [`Event`] and an ID, or [`Done`] when there are
@@ -1235,6 +1623,10 @@
     /// A client receives the largest processed stream ID. A server receives the
     /// the largest permitted push ID.
     ///
+    /// The event [`PriorityUpdate`] only occurs at servers. It returns a
+    /// prioritized element ID that is used in the method
+    /// [`take_last_priority_update()`], which rearms the event for that ID.
+    ///
     /// If an error occurs while processing data, the connection is closed with
     /// the appropriate error code, using the transport's [`close()`] method.
     ///
@@ -1245,10 +1637,12 @@
     /// [`Finished`]: enum.Event.html#variant.Finished
     /// [`Datagram`]: enum.Event.html#variant.Datagram
     /// [`GoAway`]: enum.Event.html#variant.GoAWay
+    /// [`PriorityUpdate`]: enum.Event.html#variant.PriorityUpdate
     /// [`recv_body()`]: struct.Connection.html#method.recv_body
     /// [`send_response()`]: struct.Connection.html#method.send_response
     /// [`send_body()`]: struct.Connection.html#method.send_body
     /// [`recv_dgram()`]: struct.Connection.html#method.recv_dgram
+    /// [`take_last_priority_update()`]: struct.Connection.html#method.take_last_priority_update
     /// [`close()`]: ../struct.Connection.html#method.close
     pub fn poll(&mut self, conn: &mut super::Connection) -> Result<(u64, Event)> {
         // When connection close is initiated by the local application (e.g. due
@@ -1312,6 +1706,11 @@
 
                 Err(Error::Done) => None,
 
+                // Return early if the stream was reset, to avoid returning
+                // a Finished event later as well.
+                Err(Error::TransportError(crate::Error::StreamReset(e))) =>
+                    return Ok((s, Event::Reset(e))),
+
                 Err(e) => return Err(e),
             };
 
@@ -1383,6 +1782,17 @@
 
             trace!("{} tx frm {:?}", conn.trace_id(), frame);
 
+            qlog_with_type!(QLOG_FRAME_CREATED, conn.qlog, q, {
+                let ev_data = EventData::H3FrameCreated(H3FrameCreated {
+                    stream_id,
+                    length: Some(octets::varint_len(id) as u64),
+                    frame: frame.to_qlog(),
+                    raw: None,
+                });
+
+                q.add_event_data_now(ev_data).ok();
+            });
+
             let off = b.off();
             conn.stream_send(stream_id, &d[..off], false)?;
 
@@ -1392,6 +1802,13 @@
         Ok(())
     }
 
+    /// Gets the raw settings from peer including unknown and reserved types.
+    ///
+    /// The order of settings is the same as received in the SETTINGS frame.
+    pub fn peer_settings_raw(&self) -> Option<&[(u64, u64)]> {
+        self.peer_settings.raw.as_deref()
+    }
+
     fn open_uni_stream(
         &mut self, conn: &mut super::Connection, ty: u64,
     ) -> Result<u64> {
@@ -1432,9 +1849,21 @@
     fn open_qpack_encoder_stream(
         &mut self, conn: &mut super::Connection,
     ) -> Result<()> {
-        self.local_qpack_streams.encoder_stream_id = Some(
-            self.open_uni_stream(conn, stream::QPACK_ENCODER_STREAM_TYPE_ID)?,
-        );
+        let stream_id =
+            self.open_uni_stream(conn, stream::QPACK_ENCODER_STREAM_TYPE_ID)?;
+
+        self.local_qpack_streams.encoder_stream_id = Some(stream_id);
+
+        qlog_with_type!(QLOG_STREAM_TYPE_SET, conn.qlog, q, {
+            let ev_data = EventData::H3StreamTypeSet(H3StreamTypeSet {
+                stream_id,
+                owner: Some(H3Owner::Local),
+                stream_type: H3StreamType::QpackEncode,
+                associated_push_id: None,
+            });
+
+            q.add_event_data_now(ev_data).ok();
+        });
 
         Ok(())
     }
@@ -1442,9 +1871,21 @@
     fn open_qpack_decoder_stream(
         &mut self, conn: &mut super::Connection,
     ) -> Result<()> {
-        self.local_qpack_streams.decoder_stream_id = Some(
-            self.open_uni_stream(conn, stream::QPACK_DECODER_STREAM_TYPE_ID)?,
-        );
+        let stream_id =
+            self.open_uni_stream(conn, stream::QPACK_DECODER_STREAM_TYPE_ID)?;
+
+        self.local_qpack_streams.decoder_stream_id = Some(stream_id);
+
+        qlog_with_type!(QLOG_STREAM_TYPE_SET, conn.qlog, q, {
+            let ev_data = EventData::H3StreamTypeSet(H3StreamTypeSet {
+                stream_id,
+                owner: Some(H3Owner::Local),
+                stream_type: H3StreamType::QpackDecode,
+                associated_push_id: None,
+            });
+
+            q.add_event_data_now(ev_data).ok();
+        });
 
         Ok(())
     }
@@ -1483,8 +1924,6 @@
             return Ok(());
         }
 
-        trace!("{} tx frm GREASE stream={}", conn.trace_id(), stream_id);
-
         // Empty GREASE frame.
         let mut b = octets::OctetsMut::with_slice(&mut d);
         conn.stream_send(stream_id, b.put_varint(grease_frame1)?, false)?;
@@ -1492,6 +1931,24 @@
         let mut b = octets::OctetsMut::with_slice(&mut d);
         conn.stream_send(stream_id, b.put_varint(0)?, false)?;
 
+        trace!(
+            "{} tx frm GREASE stream={} len=0",
+            conn.trace_id(),
+            stream_id
+        );
+
+        qlog_with_type!(QLOG_FRAME_CREATED, conn.qlog, q, {
+            let frame = Http3Frame::Reserved { length: Some(0) };
+            let ev_data = EventData::H3FrameCreated(H3FrameCreated {
+                stream_id,
+                length: Some(0),
+                frame,
+                raw: None,
+            });
+
+            q.add_event_data_now(ev_data).ok();
+        });
+
         // GREASE frame with payload.
         let mut b = octets::OctetsMut::with_slice(&mut d);
         conn.stream_send(stream_id, b.put_varint(grease_frame2)?, false)?;
@@ -1501,6 +1958,27 @@
 
         conn.stream_send(stream_id, grease_payload, false)?;
 
+        trace!(
+            "{} tx frm GREASE stream={} len={}",
+            conn.trace_id(),
+            stream_id,
+            grease_payload.len()
+        );
+
+        qlog_with_type!(QLOG_FRAME_CREATED, conn.qlog, q, {
+            let frame = Http3Frame::Reserved {
+                length: Some(grease_payload.len() as u64),
+            };
+            let ev_data = EventData::H3FrameCreated(H3FrameCreated {
+                stream_id,
+                length: Some(grease_payload.len() as u64),
+                frame,
+                raw: None,
+            });
+
+            q.add_event_data_now(ev_data).ok();
+        });
+
         Ok(())
     }
 
@@ -1509,9 +1987,20 @@
     fn open_grease_stream(&mut self, conn: &mut super::Connection) -> Result<()> {
         match self.open_uni_stream(conn, grease_value()) {
             Ok(stream_id) => {
+                conn.stream_send(stream_id, b"GREASE is the word", true)?;
+
                 trace!("{} open GREASE stream {}", conn.trace_id(), stream_id);
 
-                conn.stream_send(stream_id, b"GREASE is the word", true)?;
+                qlog_with_type!(QLOG_STREAM_TYPE_SET, conn.qlog, q, {
+                    let ev_data = EventData::H3StreamTypeSet(H3StreamTypeSet {
+                        stream_id,
+                        owner: Some(H3Owner::Local),
+                        stream_type: H3StreamType::Unknown,
+                        associated_push_id: None,
+                    });
+
+                    q.add_event_data_now(ev_data).ok();
+                });
             },
 
             Err(Error::IdError) => {
@@ -1528,9 +2017,34 @@
 
     /// Sends SETTINGS frame based on HTTP/3 configuration.
     fn send_settings(&mut self, conn: &mut super::Connection) -> Result<()> {
-        self.control_stream_id = Some(
-            self.open_uni_stream(conn, stream::HTTP3_CONTROL_STREAM_TYPE_ID)?,
-        );
+        let stream_id = match self
+            .open_uni_stream(conn, stream::HTTP3_CONTROL_STREAM_TYPE_ID)
+        {
+            Ok(v) => v,
+
+            Err(e) => {
+                trace!("{} Control stream blocked", conn.trace_id(),);
+
+                if e == Error::Done {
+                    return Err(Error::InternalError);
+                }
+
+                return Err(e);
+            },
+        };
+
+        self.control_stream_id = Some(stream_id);
+
+        qlog_with_type!(QLOG_STREAM_TYPE_SET, conn.qlog, q, {
+            let ev_data = EventData::H3StreamTypeSet(H3StreamTypeSet {
+                stream_id,
+                owner: Some(H3Owner::Local),
+                stream_type: H3StreamType::Control,
+                associated_push_id: None,
+            });
+
+            q.add_event_data_now(ev_data).ok();
+        });
 
         let grease = if conn.grease {
             Some((grease_value(), grease_value()))
@@ -1539,13 +2053,17 @@
         };
 
         let frame = frame::Frame::Settings {
-            max_header_list_size: self.local_settings.max_header_list_size,
+            max_field_section_size: self.local_settings.max_field_section_size,
             qpack_max_table_capacity: self
                 .local_settings
                 .qpack_max_table_capacity,
             qpack_blocked_streams: self.local_settings.qpack_blocked_streams,
+            connect_protocol_enabled: self
+                .local_settings
+                .connect_protocol_enabled,
             h3_datagram: self.local_settings.h3_datagram,
             grease,
+            raw: Default::default(),
         };
 
         let mut d = [42; 128];
@@ -1557,6 +2075,25 @@
 
         if let Some(id) = self.control_stream_id {
             conn.stream_send(id, &d[..off], false)?;
+
+            trace!(
+                "{} tx frm SETTINGS stream={} len={}",
+                conn.trace_id(),
+                id,
+                off
+            );
+
+            qlog_with_type!(QLOG_FRAME_CREATED, conn.qlog, q, {
+                let frame = frame.to_qlog();
+                let ev_data = EventData::H3FrameCreated(H3FrameCreated {
+                    stream_id: id,
+                    length: Some(off as u64),
+                    frame,
+                    raw: None,
+                });
+
+                q.add_event_data_now(ev_data).ok();
+            });
         }
 
         Ok(())
@@ -1625,6 +2162,18 @@
                         return Err(e);
                     }
 
+                    qlog_with_type!(QLOG_STREAM_TYPE_SET, conn.qlog, q, {
+                        let ev_data =
+                            EventData::H3StreamTypeSet(H3StreamTypeSet {
+                                stream_id,
+                                owner: Some(H3Owner::Remote),
+                                stream_type: ty.to_qlog(),
+                                associated_push_id: None,
+                            });
+
+                        q.add_event_data_now(ev_data).ok();
+                    });
+
                     match &ty {
                         stream::Type::Control => {
                             // Only one control stream allowed.
@@ -1729,7 +2278,7 @@
 
                     match stream.set_frame_type(varint) {
                         Err(Error::FrameUnexpected) => {
-                            let msg = format!("Unexpected frame type {}", varint);
+                            let msg = format!("Unexpected frame type {varint}");
 
                             conn.close(
                                 true,
@@ -1757,13 +2306,38 @@
                 stream::State::FramePayloadLen => {
                     stream.try_fill_buffer(conn)?;
 
-                    let varint = match stream.try_consume_varint() {
+                    let payload_len = match stream.try_consume_varint() {
                         Ok(v) => v,
 
                         Err(_) => continue,
                     };
 
-                    if let Err(e) = stream.set_frame_payload_len(varint) {
+                    // DATA frames are handled uniquely. After this point we lose
+                    // visibility of DATA framing, so just log here.
+                    if Some(frame::DATA_FRAME_TYPE_ID) == stream.frame_type() {
+                        trace!(
+                            "{} rx frm DATA stream={} wire_payload_len={}",
+                            conn.trace_id(),
+                            stream_id,
+                            payload_len
+                        );
+
+                        qlog_with_type!(QLOG_FRAME_PARSED, conn.qlog, q, {
+                            let frame = Http3Frame::Data { raw: None };
+
+                            let ev_data =
+                                EventData::H3FrameParsed(H3FrameParsed {
+                                    stream_id,
+                                    length: Some(payload_len),
+                                    frame,
+                                    raw: None,
+                                });
+
+                            q.add_event_data_now(ev_data).ok();
+                        });
+                    }
+
+                    if let Err(e) = stream.set_frame_payload_len(payload_len) {
                         conn.close(true, e.to_wire(), b"")?;
                         return Err(e);
                     }
@@ -1777,7 +2351,7 @@
 
                     stream.try_fill_buffer(conn)?;
 
-                    let frame = match stream.try_consume_frame() {
+                    let (frame, payload_len) = match stream.try_consume_frame() {
                         Ok(frame) => frame,
 
                         Err(Error::Done) => return Err(Error::Done),
@@ -1793,10 +2367,19 @@
                         },
                     };
 
-                    match self.process_frame(conn, stream_id, frame) {
+                    match self.process_frame(conn, stream_id, frame, payload_len)
+                    {
                         Ok(ev) => return Ok(ev),
 
-                        Err(Error::Done) => (),
+                        Err(Error::Done) => {
+                            // This might be a frame that is processed internally
+                            // without needing to bubble up to the user as an
+                            // event. Check whether the frame has FIN'd by QUIC
+                            // to prevent trying to read again on a closed stream.
+                            if conn.stream_finished(stream_id) {
+                                break;
+                            }
+                        },
 
                         Err(e) => return Err(e),
                     };
@@ -1866,28 +2449,48 @@
 
     fn process_frame(
         &mut self, conn: &mut super::Connection, stream_id: u64,
-        frame: frame::Frame,
+        frame: frame::Frame, payload_len: u64,
     ) -> Result<(u64, Event)> {
         trace!(
-            "{} rx frm {:?} stream={}",
+            "{} rx frm {:?} stream={} payload_len={}",
             conn.trace_id(),
             frame,
-            stream_id
+            stream_id,
+            payload_len
         );
 
+        qlog_with_type!(QLOG_FRAME_PARSED, conn.qlog, q, {
+            // HEADERS frames are special case and will be logged below.
+            if !matches!(frame, frame::Frame::Headers { .. }) {
+                let frame = frame.to_qlog();
+                let ev_data = EventData::H3FrameParsed(H3FrameParsed {
+                    stream_id,
+                    length: Some(payload_len),
+                    frame,
+                    raw: None,
+                });
+
+                q.add_event_data_now(ev_data).ok();
+            }
+        });
+
         match frame {
             frame::Frame::Settings {
-                max_header_list_size,
+                max_field_section_size,
                 qpack_max_table_capacity,
                 qpack_blocked_streams,
+                connect_protocol_enabled,
                 h3_datagram,
+                raw,
                 ..
             } => {
                 self.peer_settings = ConnectionSettings {
-                    max_header_list_size,
+                    max_field_section_size,
                     qpack_max_table_capacity,
                     qpack_blocked_streams,
+                    connect_protocol_enabled,
                     h3_datagram,
+                    raw,
                 };
 
                 if let Some(1) = h3_datagram {
@@ -1915,12 +2518,12 @@
                     return Err(Error::FrameUnexpected);
                 }
 
-                // Use "infinite" as default value for max_header_list_size if
+                // Use "infinite" as default value for max_field_section_size if
                 // it is not configured by the application.
                 let max_size = self
                     .local_settings
-                    .max_header_list_size
-                    .unwrap_or(std::u64::MAX);
+                    .max_field_section_size
+                    .unwrap_or(u64::MAX);
 
                 let headers = match self
                     .qpack_decoder
@@ -1942,6 +2545,30 @@
                     },
                 };
 
+                qlog_with_type!(QLOG_FRAME_PARSED, conn.qlog, q, {
+                    let qlog_headers = headers
+                        .iter()
+                        .map(|h| qlog::events::h3::HttpHeader {
+                            name: String::from_utf8_lossy(h.name()).into_owned(),
+                            value: String::from_utf8_lossy(h.value())
+                                .into_owned(),
+                        })
+                        .collect();
+
+                    let frame = Http3Frame::Headers {
+                        headers: qlog_headers,
+                    };
+
+                    let ev_data = EventData::H3FrameParsed(H3FrameParsed {
+                        stream_id,
+                        length: Some(payload_len),
+                        frame,
+                        raw: None,
+                    });
+
+                    q.add_event_data_now(ev_data).ok();
+                });
+
                 let has_body = !conn.stream_finished(stream_id);
 
                 return Ok((stream_id, Event::Headers {
@@ -2074,7 +2701,116 @@
                 // TODO: implement CANCEL_PUSH frame
             },
 
-            frame::Frame::Unknown => (),
+            frame::Frame::PriorityUpdateRequest {
+                prioritized_element_id,
+                priority_field_value,
+            } => {
+                if !self.is_server {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"PRIORITY_UPDATE received by client",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                if Some(stream_id) != self.peer_control_stream_id {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"PRIORITY_UPDATE received on non-control stream",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                if prioritized_element_id % 4 != 0 {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"PRIORITY_UPDATE for request stream type with wrong ID",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                if prioritized_element_id > conn.streams.max_streams_bidi() * 4 {
+                    conn.close(
+                        true,
+                        Error::IdError.to_wire(),
+                        b"PRIORITY_UPDATE for request stream beyond max streams limit",
+                    )?;
+
+                    return Err(Error::IdError);
+                }
+
+                // If the PRIORITY_UPDATE is valid, consider storing the latest
+                // contents. Due to reordering, it is possible that we might
+                // receive frames that reference streams that have not yet to
+                // been opened and that's OK because it's within our concurrency
+                // limit. However, we discard PRIORITY_UPDATE that refers to
+                // streams that we know have been collected.
+                if conn.streams.is_collected(prioritized_element_id) {
+                    return Err(Error::Done);
+                }
+
+                // If the stream did not yet exist, create it and store.
+                let stream =
+                    self.streams.entry(prioritized_element_id).or_insert_with(
+                        || stream::Stream::new(prioritized_element_id, false),
+                    );
+
+                let had_priority_update = stream.has_last_priority_update();
+                stream.set_last_priority_update(Some(priority_field_value));
+
+                // Only trigger the event when there wasn't already a stored
+                // PRIORITY_UPDATE.
+                if !had_priority_update {
+                    return Ok((prioritized_element_id, Event::PriorityUpdate));
+                } else {
+                    return Err(Error::Done);
+                }
+            },
+
+            frame::Frame::PriorityUpdatePush {
+                prioritized_element_id,
+                ..
+            } => {
+                if !self.is_server {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"PRIORITY_UPDATE received by client",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                if Some(stream_id) != self.peer_control_stream_id {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"PRIORITY_UPDATE received on non-control stream",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                if prioritized_element_id % 3 != 0 {
+                    conn.close(
+                        true,
+                        Error::FrameUnexpected.to_wire(),
+                        b"PRIORITY_UPDATE for push stream type with wrong ID",
+                    )?;
+
+                    return Err(Error::FrameUnexpected);
+                }
+
+                // TODO: we only implement this if we implement server push
+            },
+
+            frame::Frame::Unknown { .. } => (),
         }
 
         Err(Error::Done)
@@ -2114,11 +2850,11 @@
     }
 
     impl Session {
-        pub fn default() -> Result<Session> {
+        pub fn new() -> Result<Session> {
             let mut config = crate::Config::new(crate::PROTOCOL_VERSION)?;
             config.load_cert_chain_from_pem_file("examples/cert.crt")?;
             config.load_priv_key_from_pem_file("examples/cert.key")?;
-            config.set_application_protos(b"\x02h3")?;
+            config.set_application_protos(&[b"h3"])?;
             config.set_initial_max_data(1500);
             config.set_initial_max_stream_data_bidi_local(150);
             config.set_initial_max_stream_data_bidi_remote(150);
@@ -2127,6 +2863,7 @@
             config.set_initial_max_streams_uni(5);
             config.verify_peer(false);
             config.enable_dgram(true, 3, 3);
+            config.set_ack_delay_exponent(8);
 
             let h3_config = Config::new()?;
             Session::with_configs(&mut config, &h3_config)
@@ -2140,8 +2877,8 @@
             let server_dgram = pipe.server.dgram_enabled();
             Ok(Session {
                 pipe,
-                client: Connection::new(&h3_config, false, client_dgram)?,
-                server: Connection::new(&h3_config, true, server_dgram)?,
+                client: Connection::new(h3_config, false, client_dgram)?,
+                server: Connection::new(h3_config, true, server_dgram)?,
             })
         }
 
@@ -2425,9 +3162,87 @@
     }
 
     #[test]
+    fn h3_handshake_0rtt() {
+        let mut buf = [0; 65535];
+
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(15);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(3);
+        config.enable_early_data();
+        config.verify_peer(false);
+
+        let h3_config = Config::new().unwrap();
+
+        // Perform initial handshake.
+        let mut pipe = crate::testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Extract session,
+        let session = pipe.client.session().unwrap();
+
+        // Configure session on new connection.
+        let mut pipe = crate::testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.client.set_session(session), Ok(()));
+
+        // Can't create an H3 connection until the QUIC connection is determined
+        // to have made sufficient early data progress.
+        assert!(matches!(
+            Connection::with_transport(&mut pipe.client, &h3_config),
+            Err(Error::InternalError)
+        ));
+
+        // Client sends initial flight.
+        let (len, _) = pipe.client.send(&mut buf).unwrap();
+
+        // Now an H3 connection can be created.
+        assert!(matches!(
+            Connection::with_transport(&mut pipe.client, &h3_config),
+            Ok(_)
+        ));
+        assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len));
+
+        // Client sends 0-RTT packet.
+        let pkt_type = crate::packet::Type::ZeroRTT;
+
+        let frames = [crate::frame::Frame::Stream {
+            stream_id: 6,
+            data: crate::stream::RangeBuf::from(b"aaaaa", 0, true),
+        }];
+
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Ok(1200)
+        );
+
+        assert_eq!(pipe.server.undecryptable_pkts.len(), 0);
+
+        // 0-RTT stream data is readable.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(6));
+        assert_eq!(r.next(), None);
+
+        let mut b = [0; 15];
+        assert_eq!(pipe.server.stream_recv(6, &mut b), Ok((5, true)));
+        assert_eq!(&b[..5], b"aaaaa");
+    }
+
+    #[test]
     /// Send a request with no body, get a response with no body.
     fn request_no_body_response_no_body() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(true).unwrap();
@@ -2457,7 +3272,7 @@
     #[test]
     /// Send a request with no body, get a response with one DATA frame.
     fn request_no_body_response_one_chunk() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(true).unwrap();
@@ -2495,7 +3310,7 @@
     #[test]
     /// Send a request with no body, get a response with multiple DATA frames.
     fn request_no_body_response_many_chunks() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(true).unwrap();
@@ -2540,7 +3355,7 @@
     #[test]
     /// Send a request with one DATA frame, get a response with no body.
     fn request_one_chunk_response_no_body() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(false).unwrap();
@@ -2575,7 +3390,7 @@
     #[test]
     /// Send a request with multiple DATA frames, get a response with no body.
     fn request_many_chunks_response_no_body() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(false).unwrap();
@@ -2620,7 +3435,7 @@
     /// Send a request with multiple DATA frames, get a response with one DATA
     /// frame.
     fn many_requests_many_chunks_response_one_chunk() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let mut reqs = Vec::new();
@@ -2694,7 +3509,7 @@
     /// Send a request with no body, get a response with one DATA frame and an
     /// empty FIN after reception from the client.
     fn request_no_body_response_one_chunk_empty_fin() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(true).unwrap();
@@ -2731,9 +3546,57 @@
     }
 
     #[test]
+    /// Send a request with no body, get a response with no body followed by
+    /// GREASE that is STREAM frame with a FIN.
+    fn request_no_body_response_no_body_with_grease() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(true).unwrap();
+
+        assert_eq!(stream, 0);
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        let resp = s.send_response(stream, false).unwrap();
+
+        // Note that "has_body" is a misnomer, there will never be a body in
+        // this test. There's other work that will fix this, once it lands
+        // remove this comment.
+        let ev_headers = Event::Headers {
+            list: resp,
+            has_body: true,
+        };
+
+        // Inject a GREASE frame
+        let mut d = [42; 10];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame_type = b.put_varint(148_764_065_110_560_899).unwrap();
+        s.pipe.server.stream_send(0, frame_type, false).unwrap();
+
+        let frame_len = b.put_varint(10).unwrap();
+        s.pipe.server.stream_send(0, frame_len, false).unwrap();
+
+        s.pipe.server.stream_send(0, &d, true).unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_client(), Err(Error::Done));
+    }
+
+    #[test]
     /// Try to send DATA frames before HEADERS.
     fn body_response_before_headers() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(true).unwrap();
@@ -2760,7 +3623,7 @@
     /// Try to send DATA frames on wrong streams, ensure the API returns an
     /// error before anything hits the transport layer.
     fn send_body_invalid_client_stream() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         assert_eq!(s.send_body_client(0, true), Err(Error::FrameUnexpected));
@@ -2812,7 +3675,7 @@
     /// Try to send DATA frames on wrong streams, ensure the API returns an
     /// error before anything hits the transport layer.
     fn send_body_invalid_server_stream() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         assert_eq!(s.send_body_server(0, true), Err(Error::FrameUnexpected));
@@ -2863,7 +3726,7 @@
     #[test]
     /// Send a MAX_PUSH_ID frame from the client on a valid stream.
     fn max_push_id_from_client_good() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.send_frame_client(
@@ -2879,7 +3742,7 @@
     #[test]
     /// Send a MAX_PUSH_ID frame from the client on an invalid stream.
     fn max_push_id_from_client_bad_stream() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(false).unwrap();
@@ -2904,7 +3767,7 @@
     /// Send a sequence of MAX_PUSH_ID frames from the client that attempt to
     /// reduce the limit.
     fn max_push_id_from_client_limit_reduction() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.send_frame_client(
@@ -2927,7 +3790,7 @@
     #[test]
     /// Send a MAX_PUSH_ID frame from the server, which is forbidden.
     fn max_push_id_from_server() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.send_frame_server(
@@ -2943,7 +3806,7 @@
     #[test]
     /// Send a PUSH_PROMISE frame from the client, which is forbidden.
     fn push_promise_from_client() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(false).unwrap();
@@ -2972,7 +3835,7 @@
     #[test]
     /// Send a CANCEL_PUSH frame from the client.
     fn cancel_push_from_client() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.send_frame_client(
@@ -2988,7 +3851,7 @@
     #[test]
     /// Send a CANCEL_PUSH frame from the client on an invalid stream.
     fn cancel_push_from_client_bad_stream() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(false).unwrap();
@@ -3012,7 +3875,7 @@
     #[test]
     /// Send a CANCEL_PUSH frame from the client.
     fn cancel_push_from_server() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.send_frame_server(
@@ -3028,7 +3891,7 @@
     #[test]
     /// Send a GOAWAY frame from the client.
     fn goaway_from_client_good() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.client.send_goaway(&mut s.pipe.client, 100).unwrap();
@@ -3042,7 +3905,7 @@
     #[test]
     /// Send a GOAWAY frame from the server.
     fn goaway_from_server_good() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.server.send_goaway(&mut s.pipe.server, 4000).unwrap();
@@ -3055,7 +3918,7 @@
     #[test]
     /// A client MUST NOT send a request after it receives GOAWAY.
     fn client_request_after_goaway() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.server.send_goaway(&mut s.pipe.server, 4000).unwrap();
@@ -3070,7 +3933,7 @@
     #[test]
     /// Send a GOAWAY frame from the server, using an invalid goaway ID.
     fn goaway_from_server_invalid_id() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.send_frame_server(
@@ -3087,7 +3950,7 @@
     /// Send multiple GOAWAY frames from the server, that increase the goaway
     /// ID.
     fn goaway_from_server_increase_id() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         s.send_frame_server(
@@ -3110,6 +3973,460 @@
     }
 
     #[test]
+    #[cfg(feature = "sfv")]
+    fn parse_priority_field_value() {
+        // Legal dicts
+        assert_eq!(
+            Ok(Priority::new(0, false)),
+            Priority::try_from(b"u=0".as_slice())
+        );
+        assert_eq!(
+            Ok(Priority::new(3, false)),
+            Priority::try_from(b"u=3".as_slice())
+        );
+        assert_eq!(
+            Ok(Priority::new(7, false)),
+            Priority::try_from(b"u=7".as_slice())
+        );
+
+        assert_eq!(
+            Ok(Priority::new(0, true)),
+            Priority::try_from(b"u=0, i".as_slice())
+        );
+        assert_eq!(
+            Ok(Priority::new(3, true)),
+            Priority::try_from(b"u=3, i".as_slice())
+        );
+        assert_eq!(
+            Ok(Priority::new(7, true)),
+            Priority::try_from(b"u=7, i".as_slice())
+        );
+
+        assert_eq!(
+            Ok(Priority::new(0, true)),
+            Priority::try_from(b"u=0, i=?1".as_slice())
+        );
+        assert_eq!(
+            Ok(Priority::new(3, true)),
+            Priority::try_from(b"u=3, i=?1".as_slice())
+        );
+        assert_eq!(
+            Ok(Priority::new(7, true)),
+            Priority::try_from(b"u=7, i=?1".as_slice())
+        );
+
+        assert_eq!(
+            Ok(Priority::new(3, false)),
+            Priority::try_from(b"".as_slice())
+        );
+
+        assert_eq!(
+            Ok(Priority::new(0, true)),
+            Priority::try_from(b"u=0;foo, i;bar".as_slice())
+        );
+        assert_eq!(
+            Ok(Priority::new(3, true)),
+            Priority::try_from(b"u=3;hello, i;world".as_slice())
+        );
+        assert_eq!(
+            Ok(Priority::new(7, true)),
+            Priority::try_from(b"u=7;croeso, i;gymru".as_slice())
+        );
+
+        assert_eq!(
+            Ok(Priority::new(0, true)),
+            Priority::try_from(b"u=0, i, spinaltap=11".as_slice())
+        );
+
+        // Illegal formats
+        assert_eq!(Err(Error::Done), Priority::try_from(b"0".as_slice()));
+        assert_eq!(
+            Ok(Priority::new(7, false)),
+            Priority::try_from(b"u=-1".as_slice())
+        );
+        assert_eq!(Err(Error::Done), Priority::try_from(b"u=0.2".as_slice()));
+        assert_eq!(
+            Ok(Priority::new(7, false)),
+            Priority::try_from(b"u=100".as_slice())
+        );
+        assert_eq!(
+            Err(Error::Done),
+            Priority::try_from(b"u=3, i=true".as_slice())
+        );
+
+        // Trailing comma in dict is malformed
+        assert_eq!(Err(Error::Done), Priority::try_from(b"u=7, ".as_slice()));
+    }
+
+    #[test]
+    /// Send a PRIORITY_UPDATE for request stream from the client.
+    fn priority_update_request() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 0, &Priority {
+                urgency: 3,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Ok((0, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a PRIORITY_UPDATE for request stream from the client.
+    fn priority_update_single_stream_rearm() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 0, &Priority {
+                urgency: 3,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Ok((0, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 0, &Priority {
+                urgency: 5,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        // There is only one PRIORITY_UPDATE frame to read. Once read, the event
+        // will rearm ready for more.
+        assert_eq!(s.server.take_last_priority_update(0), Ok(b"u=5".to_vec()));
+        assert_eq!(s.server.take_last_priority_update(0), Err(Error::Done));
+
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 0, &Priority {
+                urgency: 7,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Ok((0, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        assert_eq!(s.server.take_last_priority_update(0), Ok(b"u=7".to_vec()));
+        assert_eq!(s.server.take_last_priority_update(0), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send multiple PRIORITY_UPDATE frames for different streams from the
+    /// client across multiple flights of exchange.
+    fn priority_update_request_multiple_stream_arm_multiple_flights() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 0, &Priority {
+                urgency: 3,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Ok((0, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 4, &Priority {
+                urgency: 1,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Ok((4, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 8, &Priority {
+                urgency: 2,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Ok((8, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        assert_eq!(s.server.take_last_priority_update(0), Ok(b"u=3".to_vec()));
+        assert_eq!(s.server.take_last_priority_update(4), Ok(b"u=1".to_vec()));
+        assert_eq!(s.server.take_last_priority_update(8), Ok(b"u=2".to_vec()));
+        assert_eq!(s.server.take_last_priority_update(0), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send multiple PRIORITY_UPDATE frames for different streams from the
+    /// client across a single flight.
+    fn priority_update_request_multiple_stream_arm_single_flight() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        let mut d = [42; 65535];
+
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let p1 = frame::Frame::PriorityUpdateRequest {
+            prioritized_element_id: 0,
+            priority_field_value: b"u=3".to_vec(),
+        };
+
+        let p2 = frame::Frame::PriorityUpdateRequest {
+            prioritized_element_id: 4,
+            priority_field_value: b"u=3".to_vec(),
+        };
+
+        let p3 = frame::Frame::PriorityUpdateRequest {
+            prioritized_element_id: 8,
+            priority_field_value: b"u=3".to_vec(),
+        };
+
+        p1.to_bytes(&mut b).unwrap();
+        p2.to_bytes(&mut b).unwrap();
+        p3.to_bytes(&mut b).unwrap();
+
+        let off = b.off();
+        s.pipe
+            .client
+            .stream_send(s.client.control_stream_id.unwrap(), &d[..off], false)
+            .unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Ok((0, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Ok((4, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Ok((8, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        assert_eq!(s.server.take_last_priority_update(0), Ok(b"u=3".to_vec()));
+        assert_eq!(s.server.take_last_priority_update(4), Ok(b"u=3".to_vec()));
+        assert_eq!(s.server.take_last_priority_update(8), Ok(b"u=3".to_vec()));
+
+        assert_eq!(s.server.take_last_priority_update(0), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a PRIORITY_UPDATE for a request stream, before and after the stream
+    /// has been completed.
+    fn priority_update_request_collected_completed() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 0, &Priority {
+                urgency: 3,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        let (stream, req) = s.send_request(true).unwrap();
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        // Priority event is generated before request headers.
+        assert_eq!(s.poll_server(), Ok((0, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        assert_eq!(s.server.take_last_priority_update(0), Ok(b"u=3".to_vec()));
+        assert_eq!(s.server.take_last_priority_update(0), Err(Error::Done));
+
+        let resp = s.send_response(stream, true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: resp,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_client(), Err(Error::Done));
+
+        // Now send a PRIORITY_UPDATE for the completed request stream.
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 0, &Priority {
+                urgency: 3,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        // No event generated at server
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a PRIORITY_UPDATE for a request stream, before and after the stream
+    /// has been stopped.
+    fn priority_update_request_collected_stopped() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 0, &Priority {
+                urgency: 3,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        let (stream, req) = s.send_request(false).unwrap();
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: true,
+        };
+
+        // Priority event is generated before request headers.
+        assert_eq!(s.poll_server(), Ok((0, Event::PriorityUpdate)));
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        assert_eq!(s.server.take_last_priority_update(0), Ok(b"u=3".to_vec()));
+        assert_eq!(s.server.take_last_priority_update(0), Err(Error::Done));
+
+        s.pipe
+            .client
+            .stream_shutdown(stream, crate::Shutdown::Write, 0x100)
+            .unwrap();
+        s.pipe
+            .client
+            .stream_shutdown(stream, crate::Shutdown::Read, 0x100)
+            .unwrap();
+
+        s.advance().ok();
+
+        assert_eq!(s.poll_server(), Ok((0, Event::Reset(0x100))));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        // Now send a PRIORITY_UPDATE for the closed request stream.
+        s.client
+            .send_priority_update_for_request(&mut s.pipe.client, 0, &Priority {
+                urgency: 3,
+                incremental: false,
+            })
+            .unwrap();
+        s.advance().ok();
+
+        // No event generated at server
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a PRIORITY_UPDATE for push stream from the client.
+    fn priority_update_push() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_client(
+            frame::Frame::PriorityUpdatePush {
+                prioritized_element_id: 3,
+                priority_field_value: b"u=3".to_vec(),
+            },
+            s.client.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    /// Send a PRIORITY_UPDATE for request stream from the client but for an
+    /// incorrect stream type.
+    fn priority_update_request_bad_stream() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_client(
+            frame::Frame::PriorityUpdateRequest {
+                prioritized_element_id: 5,
+                priority_field_value: b"u=3".to_vec(),
+            },
+            s.client.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_server(), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    /// Send a PRIORITY_UPDATE for push stream from the client but for an
+    /// incorrect stream type.
+    fn priority_update_push_bad_stream() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_client(
+            frame::Frame::PriorityUpdatePush {
+                prioritized_element_id: 5,
+                priority_field_value: b"u=3".to_vec(),
+            },
+            s.client.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_server(), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    /// Send a PRIORITY_UPDATE for request stream from the server.
+    fn priority_update_request_from_server() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_server(
+            frame::Frame::PriorityUpdateRequest {
+                prioritized_element_id: 0,
+                priority_field_value: b"u=3".to_vec(),
+            },
+            s.server.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_client(), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
+    /// Send a PRIORITY_UPDATE for request stream from the server.
+    fn priority_update_push_from_server() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        s.send_frame_server(
+            frame::Frame::PriorityUpdatePush {
+                prioritized_element_id: 0,
+                priority_field_value: b"u=3".to_vec(),
+            },
+            s.server.control_stream_id.unwrap(),
+            false,
+        )
+        .unwrap();
+
+        assert_eq!(s.poll_client(), Err(Error::FrameUnexpected));
+    }
+
+    #[test]
     /// Ensure quiche allocates streams for client and server roles as expected.
     fn uni_stream_local_counting() {
         let config = Config::new().unwrap();
@@ -3124,7 +4441,7 @@
     #[test]
     /// Client opens multiple control streams, which is forbidden.
     fn open_multiple_control_streams() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let stream_id = s.client.next_uni_stream_id;
@@ -3149,7 +4466,7 @@
     #[test]
     /// Client closes the control stream, which is forbidden.
     fn close_control_stream() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let mut control_stream_closed = false;
@@ -3184,7 +4501,7 @@
     #[test]
     /// Client closes QPACK stream, which is forbidden.
     fn close_qpack_stream() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let mut qpack_stream_closed = false;
@@ -3222,7 +4539,7 @@
     fn qpack_data() {
         // TODO: QPACK instructions are ignored until dynamic table support is
         // added so we just test that the data is safely ignored.
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let e_stream_id = s.client.local_qpack_streams.encoder_stream_id.unwrap();
@@ -3253,7 +4570,7 @@
     #[test]
     /// Tests limits for the stream state buffer maximum size.
     fn max_state_buf_size() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let req = vec![
@@ -3295,7 +4612,7 @@
         assert_eq!(s.server.poll(&mut s.pipe.server), Ok((0, Event::Data)));
 
         // GREASE frames consume the state buffer, so need to be limited.
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let mut d = [42; 128];
@@ -3311,7 +4628,7 @@
 
         s.advance().ok();
 
-        assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::InternalError));
+        assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::ExcessiveLoad));
     }
 
     #[test]
@@ -3320,7 +4637,7 @@
     fn stream_backpressure() {
         let bytes = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(false).unwrap();
@@ -3382,7 +4699,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(1500);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -3392,7 +4709,7 @@
         config.verify_peer(false);
 
         let mut h3_config = Config::new().unwrap();
-        h3_config.set_max_header_list_size(65);
+        h3_config.set_max_field_section_size(65);
 
         let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap();
 
@@ -3426,7 +4743,7 @@
     #[test]
     /// Tests that Error::TransportError contains a transport error.
     fn transport_error() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let req = vec![
@@ -3462,7 +4779,7 @@
     #[test]
     /// Tests that sending DATA before HEADERS causes an error.
     fn data_before_headers() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let mut d = [42; 128];
@@ -3485,9 +4802,9 @@
     }
 
     #[test]
-    /// Tests that calling poll() after an error occured does nothing.
+    /// Tests that calling poll() after an error occurred does nothing.
     fn poll_after_error() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let mut d = [42; 128];
@@ -3503,7 +4820,7 @@
 
         s.advance().ok();
 
-        assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::InternalError));
+        assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::ExcessiveLoad));
 
         // Try to call poll() again after an error occurred.
         assert_eq!(s.server.poll(&mut s.pipe.server), Err(Error::Done));
@@ -3519,7 +4836,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(70);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -3548,17 +4865,368 @@
             Err(Error::StreamBlocked)
         );
 
+        // Clear the writable stream queue.
+        assert_eq!(s.pipe.client.stream_writable_next(), Some(10));
+        assert_eq!(s.pipe.client.stream_writable_next(), Some(2));
+        assert_eq!(s.pipe.client.stream_writable_next(), Some(6));
+        assert_eq!(s.pipe.client.stream_writable_next(), None);
+
         s.advance().ok();
 
         // Once the server gives flow control credits back, we can send the
         // request.
+        assert_eq!(s.pipe.client.stream_writable_next(), Some(4));
         assert_eq!(s.client.send_request(&mut s.pipe.client, &req, true), Ok(4));
     }
 
     #[test]
+    /// Ensure StreamBlocked when connection flow control prevents headers.
+    fn headers_blocked_on_conn() {
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
+        config.set_initial_max_data(70);
+        config.set_initial_max_stream_data_bidi_local(150);
+        config.set_initial_max_stream_data_bidi_remote(150);
+        config.set_initial_max_stream_data_uni(150);
+        config.set_initial_max_streams_bidi(100);
+        config.set_initial_max_streams_uni(5);
+        config.verify_peer(false);
+
+        let mut h3_config = Config::new().unwrap();
+
+        let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap();
+
+        s.handshake().unwrap();
+
+        // After the HTTP handshake, some bytes of connection flow control have
+        // been consumed. Fill the connection with more grease data on the control
+        // stream.
+        let d = [42; 28];
+        assert_eq!(s.pipe.client.stream_send(2, &d, false), Ok(23));
+
+        let req = vec![
+            Header::new(b":method", b"GET"),
+            Header::new(b":scheme", b"https"),
+            Header::new(b":authority", b"quic.tech"),
+            Header::new(b":path", b"/test"),
+        ];
+
+        // There is 0 connection-level flow control, so sending a request is
+        // blocked.
+        assert_eq!(
+            s.client.send_request(&mut s.pipe.client, &req, true),
+            Err(Error::StreamBlocked)
+        );
+        assert_eq!(s.pipe.client.stream_writable_next(), None);
+
+        // Emit the control stream data and drain it at the server via poll() to
+        // consumes it via poll() and gives back flow control.
+        s.advance().ok();
+        assert_eq!(s.poll_server(), Err(Error::Done));
+        s.advance().ok();
+
+        // Now we can send the request.
+        assert_eq!(s.pipe.client.stream_writable_next(), Some(0));
+        assert_eq!(s.client.send_request(&mut s.pipe.client, &req, true), Ok(0));
+    }
+
+    #[test]
+    /// Ensure STREAM_DATA_BLOCKED is not emitted multiple times with the same
+    /// offset when trying to send large bodies.
+    fn send_body_truncation_stream_blocked() {
+        use crate::testing::decode_pkt;
+
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
+        config.set_initial_max_data(10000); // large connection-level flow control
+        config.set_initial_max_stream_data_bidi_local(80);
+        config.set_initial_max_stream_data_bidi_remote(80);
+        config.set_initial_max_stream_data_uni(150);
+        config.set_initial_max_streams_bidi(100);
+        config.set_initial_max_streams_uni(5);
+        config.verify_peer(false);
+
+        let mut h3_config = Config::new().unwrap();
+
+        let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap();
+
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        let _ = s.send_response(stream, false).unwrap();
+
+        assert_eq!(s.pipe.server.streams.blocked().len(), 0);
+
+        // The body must be larger than the stream window would allow
+        let d = [42; 500];
+        let mut off = 0;
+
+        let sent = s
+            .server
+            .send_body(&mut s.pipe.server, stream, &d, true)
+            .unwrap();
+        assert_eq!(sent, 25);
+        off += sent;
+
+        // send_body wrote as much as it could (sent < size of buff).
+        assert_eq!(s.pipe.server.streams.blocked().len(), 1);
+        assert_eq!(
+            s.server
+                .send_body(&mut s.pipe.server, stream, &d[off..], true),
+            Err(Error::Done)
+        );
+        assert_eq!(s.pipe.server.streams.blocked().len(), 1);
+
+        // Now read raw frames to see what the QUIC layer did
+        let mut buf = [0; 65535];
+        let (len, _) = s.pipe.server.send(&mut buf).unwrap();
+
+        let frames = decode_pkt(&mut s.pipe.client, &mut buf, len).unwrap();
+
+        let mut iter = frames.iter();
+
+        assert_eq!(
+            iter.next(),
+            Some(&crate::frame::Frame::StreamDataBlocked {
+                stream_id: 0,
+                limit: 80,
+            })
+        );
+
+        // At the server, after sending the STREAM_DATA_BLOCKED frame, we clear
+        // the mark.
+        assert_eq!(s.pipe.server.streams.blocked().len(), 0);
+
+        // Don't read any data from the client, so stream flow control is never
+        // given back in the form of changing the stream's max offset.
+        // Subsequent body send operations will still fail but no more
+        // STREAM_DATA_BLOCKED frames should be submitted since the limit didn't
+        // change. No frames means no packet to send.
+        assert_eq!(
+            s.server
+                .send_body(&mut s.pipe.server, stream, &d[off..], true),
+            Err(Error::Done)
+        );
+        assert_eq!(s.pipe.server.streams.blocked().len(), 0);
+        assert_eq!(s.pipe.server.send(&mut buf), Err(crate::Error::Done));
+
+        // Now update the client's max offset manually.
+        let frames = [crate::frame::Frame::MaxStreamData {
+            stream_id: 0,
+            max: 100,
+        }];
+
+        let pkt_type = crate::packet::Type::Short;
+        assert_eq!(
+            s.pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Ok(39),
+        );
+
+        let sent = s
+            .server
+            .send_body(&mut s.pipe.server, stream, &d[off..], true)
+            .unwrap();
+        assert_eq!(sent, 18);
+
+        // Same thing here...
+        assert_eq!(s.pipe.server.streams.blocked().len(), 1);
+        assert_eq!(
+            s.server
+                .send_body(&mut s.pipe.server, stream, &d[off..], true),
+            Err(Error::Done)
+        );
+        assert_eq!(s.pipe.server.streams.blocked().len(), 1);
+
+        let (len, _) = s.pipe.server.send(&mut buf).unwrap();
+
+        let frames = decode_pkt(&mut s.pipe.client, &mut buf, len).unwrap();
+
+        let mut iter = frames.iter();
+
+        assert_eq!(
+            iter.next(),
+            Some(&crate::frame::Frame::StreamDataBlocked {
+                stream_id: 0,
+                limit: 100,
+            })
+        );
+    }
+
+    #[test]
+    /// Ensure stream doesn't hang due to small cwnd.
+    fn send_body_stream_blocked_by_small_cwnd() {
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
+        config.set_initial_max_data(100000); // large connection-level flow control
+        config.set_initial_max_stream_data_bidi_local(100000);
+        config.set_initial_max_stream_data_bidi_remote(50000);
+        config.set_initial_max_stream_data_uni(150);
+        config.set_initial_max_streams_bidi(100);
+        config.set_initial_max_streams_uni(5);
+        config.verify_peer(false);
+
+        let mut h3_config = Config::new().unwrap();
+
+        let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap();
+
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        let _ = s.send_response(stream, false).unwrap();
+
+        // Clear the writable stream queue.
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(stream));
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(11));
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(3));
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(7));
+        assert_eq!(s.pipe.server.stream_writable_next(), None);
+
+        // The body must be larger than the cwnd would allow.
+        let send_buf = [42; 80000];
+
+        let sent = s
+            .server
+            .send_body(&mut s.pipe.server, stream, &send_buf, true)
+            .unwrap();
+
+        // send_body wrote as much as it could (sent < size of buff).
+        assert_eq!(sent, 11995);
+
+        s.advance().ok();
+
+        // Client reads received headers and body.
+        let mut recv_buf = [42; 80000];
+        assert!(s.poll_client().is_ok());
+        assert_eq!(s.poll_client(), Ok((stream, Event::Data)));
+        assert_eq!(s.recv_body_client(stream, &mut recv_buf), Ok(11995));
+
+        s.advance().ok();
+
+        // Server send cap is smaller than remaining body buffer.
+        assert!(s.pipe.server.tx_cap < send_buf.len() - sent);
+
+        // Once the server cwnd opens up, we can send more body.
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(0));
+    }
+
+    #[test]
+    /// Ensure stream doesn't hang due to small cwnd.
+    fn send_body_stream_blocked_zero_length() {
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
+        config.set_initial_max_data(100000); // large connection-level flow control
+        config.set_initial_max_stream_data_bidi_local(100000);
+        config.set_initial_max_stream_data_bidi_remote(50000);
+        config.set_initial_max_stream_data_uni(150);
+        config.set_initial_max_streams_bidi(100);
+        config.set_initial_max_streams_uni(5);
+        config.verify_peer(false);
+
+        let mut h3_config = Config::new().unwrap();
+
+        let mut s = Session::with_configs(&mut config, &mut h3_config).unwrap();
+
+        s.handshake().unwrap();
+
+        let (stream, req) = s.send_request(true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+
+        let _ = s.send_response(stream, false).unwrap();
+
+        // Clear the writable stream queue.
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(stream));
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(11));
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(3));
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(7));
+        assert_eq!(s.pipe.server.stream_writable_next(), None);
+
+        // The body is large enough to fill the cwnd, except for enough bytes
+        // for another DATA frame header (but no payload).
+        let send_buf = [42; 11994];
+
+        let sent = s
+            .server
+            .send_body(&mut s.pipe.server, stream, &send_buf, false)
+            .unwrap();
+
+        assert_eq!(sent, 11994);
+
+        // There is only enough capacity left for the DATA frame header, but
+        // no payload.
+        assert_eq!(s.pipe.server.stream_capacity(stream).unwrap(), 3);
+        assert_eq!(
+            s.server
+                .send_body(&mut s.pipe.server, stream, &send_buf, false),
+            Err(Error::Done)
+        );
+
+        s.advance().ok();
+
+        // Client reads received headers and body.
+        let mut recv_buf = [42; 80000];
+        assert!(s.poll_client().is_ok());
+        assert_eq!(s.poll_client(), Ok((stream, Event::Data)));
+        assert_eq!(s.recv_body_client(stream, &mut recv_buf), Ok(11994));
+
+        s.advance().ok();
+
+        // Once the server cwnd opens up, we can send more body.
+        assert_eq!(s.pipe.server.stream_writable_next(), Some(0));
+    }
+
+    #[test]
     /// Test handling of 0-length DATA writes with and without fin.
     fn zero_length_data() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(false).unwrap();
@@ -3620,7 +5288,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(69);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -3652,13 +5320,50 @@
             Err(Error::Done)
         );
 
+        // Clear the writable stream queue.
+        assert_eq!(s.pipe.client.stream_writable_next(), Some(10));
+        assert_eq!(s.pipe.client.stream_writable_next(), Some(2));
+        assert_eq!(s.pipe.client.stream_writable_next(), Some(6));
+        assert_eq!(s.pipe.client.stream_writable_next(), None);
+
         s.advance().ok();
 
         // Once the server gives flow control credits back, we can send the body.
+        assert_eq!(s.pipe.client.stream_writable_next(), Some(0));
         assert_eq!(s.client.send_body(&mut s.pipe.client, 0, b"", true), Ok(0));
     }
 
     #[test]
+    /// Tests that receiving an empty SETTINGS frame is handled and reported.
+    fn empty_settings() {
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
+        config.set_initial_max_data(1500);
+        config.set_initial_max_stream_data_bidi_local(150);
+        config.set_initial_max_stream_data_bidi_remote(150);
+        config.set_initial_max_stream_data_uni(150);
+        config.set_initial_max_streams_bidi(5);
+        config.set_initial_max_streams_uni(5);
+        config.verify_peer(false);
+        config.set_ack_delay_exponent(8);
+        config.grease(false);
+
+        let h3_config = Config::new().unwrap();
+        let mut s = Session::with_configs(&mut config, &h3_config).unwrap();
+
+        s.handshake().unwrap();
+
+        assert!(s.client.peer_settings_raw().is_some());
+        assert!(s.server.peer_settings_raw().is_some());
+    }
+
+    #[test]
     /// Tests that receiving a H3_DATAGRAM setting is ok.
     fn dgram_setting() {
         let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
@@ -3668,7 +5373,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(70);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -3713,7 +5418,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(70);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -3737,11 +5442,13 @@
         );
 
         let settings = frame::Frame::Settings {
-            max_header_list_size: None,
+            max_field_section_size: None,
             qpack_max_table_capacity: None,
             qpack_blocked_streams: None,
+            connect_protocol_enabled: None,
             h3_datagram: Some(1),
             grease: None,
+            raw: Default::default(),
         };
 
         s.send_frame_client(settings, s.client.control_stream_id.unwrap(), false)
@@ -3762,7 +5469,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(70);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -3827,7 +5534,7 @@
     /// Send a single DATAGRAM.
     fn single_dgram() {
         let mut buf = [0; 65535];
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         // We'll send default data of 10 bytes on flow ID 0.
@@ -3848,7 +5555,7 @@
     /// Send multiple DATAGRAMs.
     fn multiple_dgram() {
         let mut buf = [0; 65535];
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         // We'll send default data of 10 bytes on flow ID 0.
@@ -3888,7 +5595,7 @@
     /// Send more DATAGRAMs than the send queue allows.
     fn multiple_dgram_overflow() {
         let mut buf = [0; 65535];
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         // We'll send default data of 10 bytes on flow ID 0.
@@ -3924,7 +5631,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(1500);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -3972,7 +5679,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(1500);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -4064,7 +5771,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(1500);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -4206,7 +5913,7 @@
     /// Tests that the Finished event is not issued for streams of unknown type
     /// (e.g. GREASE).
     fn finished_is_for_requests() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         assert_eq!(s.poll_client(), Err(Error::Done));
@@ -4222,7 +5929,7 @@
     #[test]
     /// Tests that streams are marked as finished only once.
     fn finished_once() {
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(false).unwrap();
@@ -4250,7 +5957,7 @@
     fn data_event_rearm() {
         let bytes = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
-        let mut s = Session::default().unwrap();
+        let mut s = Session::new().unwrap();
         s.handshake().unwrap();
 
         let (stream, req) = s.send_request(false).unwrap();
@@ -4416,7 +6123,7 @@
         config
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
-        config.set_application_protos(b"\x02h3").unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
         config.set_initial_max_data(1500);
         config.set_initial_max_stream_data_bidi_local(150);
         config.set_initial_max_stream_data_bidi_remote(150);
@@ -4485,6 +6192,183 @@
         assert_eq!(s.recv_body_server(stream, &mut recv_buf), Ok(body.len()));
         assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
     }
+
+    #[test]
+    fn reset_stream() {
+        let mut buf = [0; 65535];
+
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        // Client sends request.
+        let (stream, req) = s.send_request(false).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: true,
+        };
+
+        // Server sends response and closes stream.
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        let resp = s.send_response(stream, true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: resp,
+            has_body: false,
+        };
+
+        assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_client(), Err(Error::Done));
+
+        // Client sends RESET_STREAM, closing stream.
+        let frames = [crate::frame::Frame::ResetStream {
+            stream_id: stream,
+            error_code: 42,
+            final_size: 68,
+        }];
+
+        let pkt_type = crate::packet::Type::Short;
+        assert_eq!(
+            s.pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Ok(39)
+        );
+
+        // Server issues Reset event for the stream.
+        assert_eq!(s.poll_server(), Ok((stream, Event::Reset(42))));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        // Sending RESET_STREAM again shouldn't trigger another Reset event.
+        assert_eq!(
+            s.pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Ok(39)
+        );
+
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    fn reset_finished_at_server() {
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        // Client sends HEADERS and doesn't fin
+        let (stream, _req) = s.send_request(false).unwrap();
+
+        // ..then Client sends RESET_STREAM
+        assert_eq!(
+            s.pipe.client.stream_shutdown(0, crate::Shutdown::Write, 0),
+            Ok(())
+        );
+
+        assert_eq!(s.pipe.advance(), Ok(()));
+
+        // Server receives just a reset
+        assert_eq!(s.poll_server(), Ok((stream, Event::Reset(0))));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        // Client sends HEADERS and fin
+        let (stream, req) = s.send_request(true).unwrap();
+
+        // ..then Client sends RESET_STREAM
+        assert_eq!(
+            s.pipe.client.stream_shutdown(4, crate::Shutdown::Write, 0),
+            Ok(())
+        );
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        // Server receives headers and fin.
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+    }
+
+    #[test]
+    fn reset_finished_at_client() {
+        let mut buf = [0; 65535];
+        let mut s = Session::new().unwrap();
+        s.handshake().unwrap();
+
+        // Client sends HEADERS and doesn't fin
+        let (stream, req) = s.send_request(false).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: true,
+        };
+
+        // Server receives headers.
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        // Server sends response and doesn't fin
+        s.send_response(stream, false).unwrap();
+
+        assert_eq!(s.pipe.advance(), Ok(()));
+
+        // .. then Server sends RESET_STREAM
+        assert_eq!(
+            s.pipe
+                .server
+                .stream_shutdown(stream, crate::Shutdown::Write, 0),
+            Ok(())
+        );
+
+        assert_eq!(s.pipe.advance(), Ok(()));
+
+        // Client receives Reset only
+        assert_eq!(s.poll_client(), Ok((stream, Event::Reset(0))));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        // Client sends headers and fin.
+        let (stream, req) = s.send_request(true).unwrap();
+
+        let ev_headers = Event::Headers {
+            list: req,
+            has_body: false,
+        };
+
+        // Server receives headers and fin.
+        assert_eq!(s.poll_server(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_server(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_server(), Err(Error::Done));
+
+        // Server sends response and fin
+        let resp = s.send_response(stream, true).unwrap();
+
+        assert_eq!(s.pipe.advance(), Ok(()));
+
+        // ..then Server sends RESET_STREAM
+        let frames = [crate::frame::Frame::ResetStream {
+            stream_id: stream,
+            error_code: 42,
+            final_size: 68,
+        }];
+
+        let pkt_type = crate::packet::Type::Short;
+        assert_eq!(
+            s.pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Ok(39)
+        );
+
+        assert_eq!(s.pipe.advance(), Ok(()));
+
+        let ev_headers = Event::Headers {
+            list: resp,
+            has_body: false,
+        };
+
+        // Client receives headers and fin.
+        assert_eq!(s.poll_client(), Ok((stream, ev_headers)));
+        assert_eq!(s.poll_client(), Ok((stream, Event::Finished)));
+        assert_eq!(s.poll_client(), Err(Error::Done));
+    }
 }
 
 #[cfg(feature = "ffi")]
diff --git a/src/h3/qpack/decoder.rs b/src/h3/qpack/decoder.rs
index 1bc5755..e8c7dc7 100644
--- a/src/h3/qpack/decoder.rs
+++ b/src/h3/qpack/decoder.rs
@@ -24,8 +24,6 @@
 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-use crate::octets;
-
 use super::Error;
 use super::Result;
 
@@ -68,14 +66,9 @@
 }
 
 /// A QPACK decoder.
+#[derive(Default)]
 pub struct Decoder {}
 
-impl Default for Decoder {
-    fn default() -> Decoder {
-        Decoder {}
-    }
-}
-
 impl Decoder {
     /// Creates a new QPACK decoder.
     pub fn new() -> Decoder {
@@ -274,8 +267,6 @@
 mod tests {
     use super::*;
 
-    use crate::octets;
-
     #[test]
     fn decode_int1() {
         let mut encoded = [0b01010, 0x02];
diff --git a/src/h3/qpack/encoder.rs b/src/h3/qpack/encoder.rs
index 09c8b08..85c8637 100644
--- a/src/h3/qpack/encoder.rs
+++ b/src/h3/qpack/encoder.rs
@@ -26,8 +26,6 @@
 
 use super::Result;
 
-use crate::octets;
-
 use crate::h3::NameValue;
 
 use super::INDEXED;
@@ -35,14 +33,9 @@
 use super::LITERAL_WITH_NAME_REF;
 
 /// A QPACK encoder.
+#[derive(Default)]
 pub struct Encoder {}
 
-impl Default for Encoder {
-    fn default() -> Encoder {
-        Encoder {}
-    }
-}
-
 impl Encoder {
     /// Creates a new QPACK encoder.
     pub fn new() -> Encoder {
@@ -80,12 +73,27 @@
 
                 None => {
                     // Encode as fully literal.
-                    let name_len =
-                        super::huffman::encode_output_length(h.name(), true)?;
 
-                    encode_int(name_len as u64, LITERAL | 0x08, 3, &mut b)?;
+                    // Huffman-encoding generally saves space but in some cases
+                    // it doesn't, for those just encode the literal string.
+                    match super::huffman::encode_output_length(h.name(), true) {
+                        Ok(len) => {
+                            encode_int(len as u64, LITERAL | 0x08, 3, &mut b)?;
+                            super::huffman::encode(h.name(), &mut b, true)?;
+                        },
 
-                    super::huffman::encode(h.name(), &mut b, true)?;
+                        Err(super::Error::InflatedHuffmanEncoding) => {
+                            encode_int(
+                                h.name().len() as u64,
+                                LITERAL,
+                                3,
+                                &mut b,
+                            )?;
+                            b.put_bytes(&h.name().to_ascii_lowercase())?;
+                        },
+
+                        Err(e) => return Err(e),
+                    }
 
                     encode_str(h.value(), 7, &mut b)?;
                 },
@@ -150,11 +158,21 @@
 }
 
 fn encode_str(v: &[u8], prefix: usize, b: &mut octets::OctetsMut) -> Result<()> {
-    let len = super::huffman::encode_output_length(v, false)?;
+    // Huffman-encoding generally saves space but in some cases it doesn't, for
+    // those just encode the literal string.
+    match super::huffman::encode_output_length(v, false) {
+        Ok(len) => {
+            encode_int(len as u64, 0x80, prefix, b)?;
+            super::huffman::encode(v, b, false)?;
+        },
 
-    encode_int(len as u64, 0x80, prefix, b)?;
+        Err(super::Error::InflatedHuffmanEncoding) => {
+            encode_int(v.len() as u64, 0, prefix, b)?;
+            b.put_bytes(v)?;
+        },
 
-    super::huffman::encode(v, b, false)?;
+        Err(e) => return Err(e),
+    }
 
     Ok(())
 }
@@ -163,8 +181,6 @@
 mod tests {
     use super::*;
 
-    use crate::octets;
-
     #[test]
     fn encode_int1() {
         let expected = [0b01010];
diff --git a/src/h3/qpack/huffman/mod.rs b/src/h3/qpack/huffman/mod.rs
index 3a6ff06..a222c6d 100644
--- a/src/h3/qpack/huffman/mod.rs
+++ b/src/h3/qpack/huffman/mod.rs
@@ -24,8 +24,6 @@
 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-use crate::octets;
-
 use super::Error;
 use super::Result;
 
@@ -103,6 +101,10 @@
         len += 1;
     }
 
+    if len > src.len() {
+        return Err(Error::InflatedHuffmanEncoding);
+    }
+
     Ok(len)
 }
 
diff --git a/src/h3/qpack/huffman/table.rs b/src/h3/qpack/huffman/table.rs
index 7162aff..011272c 100644
--- a/src/h3/qpack/huffman/table.rs
+++ b/src/h3/qpack/huffman/table.rs
@@ -1,5 +1,3 @@
-#[allow(clippy::unreadable_literal)]
-
 // (num-bits, bits)
 pub const ENCODE_TABLE: [(usize, u64); 257] = [
     (13, 0x1ff8),
diff --git a/src/h3/qpack/mod.rs b/src/h3/qpack/mod.rs
index 0a23306..4ce54a9 100644
--- a/src/h3/qpack/mod.rs
+++ b/src/h3/qpack/mod.rs
@@ -40,11 +40,14 @@
 pub type Result<T> = std::result::Result<T, Error>;
 
 /// A QPACK error.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum Error {
     /// The provided buffer is too short.
     BufferTooShort,
 
+    /// The provided string would be larger after huffman encoding.
+    InflatedHuffmanEncoding,
+
     /// The QPACK header block's huffman encoding is invalid.
     InvalidHuffmanEncoding,
 
@@ -60,7 +63,7 @@
 
 impl std::fmt::Display for Error {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        write!(f, "{:?}", self)
+        write!(f, "{self:?}")
     }
 }
 
@@ -70,8 +73,8 @@
     }
 }
 
-impl std::convert::From<crate::octets::BufferTooShortError> for Error {
-    fn from(_err: crate::octets::BufferTooShortError) -> Self {
+impl std::convert::From<octets::BufferTooShortError> for Error {
+    fn from(_err: octets::BufferTooShortError) -> Self {
         Error::BufferTooShort
     }
 }
@@ -102,7 +105,7 @@
         assert_eq!(enc.encode(&headers, &mut encoded), Ok(240));
 
         let mut dec = Decoder::new();
-        assert_eq!(dec.decode(&mut encoded, std::u64::MAX), Ok(headers));
+        assert_eq!(dec.decode(&mut encoded, u64::MAX), Ok(headers));
     }
 
     #[test]
@@ -130,7 +133,7 @@
         assert_eq!(enc.encode(&headers_in, &mut encoded), Ok(35));
 
         let mut dec = Decoder::new();
-        let headers_out = dec.decode(&mut encoded, std::u64::MAX).unwrap();
+        let headers_out = dec.decode(&mut encoded, u64::MAX).unwrap();
 
         assert_eq!(headers_expected, headers_out);
 
@@ -147,10 +150,48 @@
         assert_eq!(enc.encode(&headers_in, &mut encoded), Ok(35));
 
         let mut dec = Decoder::new();
-        let headers_out = dec.decode(&mut encoded, std::u64::MAX).unwrap();
+        let headers_out = dec.decode(&mut encoded, u64::MAX).unwrap();
 
         assert_eq!(headers_expected, headers_out);
     }
+
+    #[test]
+    fn lower_ascii_range() {
+        let mut encoded = [0u8; 50];
+        let mut enc = Encoder::new();
+
+        // Indexed name with literal value
+        let headers1 = vec![crate::h3::Header::new(b"location", b"															")];
+        assert_eq!(enc.encode(&headers1, &mut encoded), Ok(19));
+
+        // Literal name and value
+        let headers2 = vec![crate::h3::Header::new(b"a", b"")];
+        assert_eq!(enc.encode(&headers2, &mut encoded), Ok(20));
+
+        let headers3 = vec![crate::h3::Header::new(b"															", b"hello")];
+        assert_eq!(enc.encode(&headers3, &mut encoded), Ok(24));
+    }
+
+    #[test]
+    fn extended_ascii_range() {
+        let mut encoded = [0u8; 50];
+        let mut enc = Encoder::new();
+
+        let name = b"location";
+        let value = "£££££££££££££££";
+
+        // Indexed name with literal value
+        let headers1 = vec![crate::h3::Header::new(name, value.as_bytes())];
+        assert_eq!(enc.encode(&headers1, &mut encoded), Ok(34));
+
+        // Literal name and value
+        let value = "ððððððððððððððð";
+        let headers2 = vec![crate::h3::Header::new(b"a", value.as_bytes())];
+        assert_eq!(enc.encode(&headers2, &mut encoded), Ok(35));
+
+        let headers3 = vec![crate::h3::Header::new(value.as_bytes(), b"hello")];
+        assert_eq!(enc.encode(&headers3, &mut encoded), Ok(39));
+    }
 }
 
 pub use decoder::Decoder;
diff --git a/src/h3/stream.rs b/src/h3/stream.rs
index 58e2873..f23bf34 100644
--- a/src/h3/stream.rs
+++ b/src/h3/stream.rs
@@ -27,8 +27,6 @@
 use super::Error;
 use super::Result;
 
-use crate::octets;
-
 use super::frame;
 
 pub const HTTP3_CONTROL_STREAM_TYPE_ID: u64 = 0x0;
@@ -38,7 +36,7 @@
 
 const MAX_STATE_BUF_SIZE: usize = (1 << 24) - 1;
 
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum Type {
     Control,
     Request,
@@ -48,7 +46,21 @@
     Unknown,
 }
 
-#[derive(Clone, Copy, Debug, PartialEq)]
+impl Type {
+    #[cfg(feature = "qlog")]
+    pub fn to_qlog(self) -> qlog::events::h3::H3StreamType {
+        match self {
+            Type::Control => qlog::events::h3::H3StreamType::Control,
+            Type::Request => qlog::events::h3::H3StreamType::Request,
+            Type::Push => qlog::events::h3::H3StreamType::Push,
+            Type::QpackEncoder => qlog::events::h3::H3StreamType::QpackEncode,
+            Type::QpackDecoder => qlog::events::h3::H3StreamType::QpackDecode,
+            Type::Unknown => qlog::events::h3::H3StreamType::Unknown,
+        }
+    }
+}
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum State {
     /// Reading the stream's type.
     StreamType,
@@ -141,6 +153,9 @@
 
     /// Whether a `Data` event has been triggered for this stream.
     data_event_triggered: bool,
+
+    /// The last `PRIORITY_UPDATE` frame encoded field value, if any.
+    last_priority_update: Option<Vec<u8>>,
 }
 
 impl Stream {
@@ -179,6 +194,8 @@
             local_initialized: false,
 
             data_event_triggered: false,
+
+            last_priority_update: None,
         }
     }
 
@@ -327,18 +344,35 @@
         Ok(())
     }
 
+    // Returns the stream's current frame type, if any
+    pub fn frame_type(&self) -> Option<u64> {
+        self.frame_type
+    }
+
     /// Sets the frame's payload length and transitions to the next state.
     pub fn set_frame_payload_len(&mut self, len: u64) -> Result<()> {
         assert_eq!(self.state, State::FramePayloadLen);
 
         // Only expect frames on Control, Request and Push streams.
-        if self.ty == Some(Type::Control) ||
-            self.ty == Some(Type::Request) ||
-            self.ty == Some(Type::Push)
-        {
+        if matches!(self.ty, Some(Type::Control | Type::Request | Type::Push)) {
             let (state, resize) = match self.frame_type {
                 Some(frame::DATA_FRAME_TYPE_ID) => (State::Data, false),
 
+                // These frame types can never have 0 payload length because
+                // they always have fields that must be populated.
+                Some(
+                    frame::GOAWAY_FRAME_TYPE_ID |
+                    frame::PUSH_PROMISE_FRAME_TYPE_ID |
+                    frame::CANCEL_PUSH_FRAME_TYPE_ID |
+                    frame::MAX_PUSH_FRAME_TYPE_ID,
+                ) => {
+                    if len == 0 {
+                        return Err(Error::FrameError);
+                    }
+
+                    (State::FramePayload, true)
+                },
+
                 _ => (State::FramePayload, true),
             };
 
@@ -358,6 +392,11 @@
     pub fn try_fill_buffer(
         &mut self, conn: &mut crate::Connection,
     ) -> Result<()> {
+        // If no bytes are required to be read, return early.
+        if self.state_buffer_complete() {
+            return Ok(());
+        }
+
         let buf = &mut self.state_buf[self.state_off..self.state_len];
 
         let read = match conn.stream_recv(self.id, buf) {
@@ -409,6 +448,11 @@
     fn try_fill_buffer_for_tests(
         &mut self, stream: &mut std::io::Cursor<Vec<u8>>,
     ) -> Result<()> {
+        // If no bytes are required to be read, return early
+        if self.state_buffer_complete() {
+            return Ok(());
+        }
+
         let buf = &mut self.state_buf[self.state_off..self.state_len];
 
         let read = std::io::Read::read(stream, buf).unwrap();
@@ -441,20 +485,24 @@
     }
 
     /// Tries to parse a frame from the state buffer.
-    pub fn try_consume_frame(&mut self) -> Result<frame::Frame> {
+    ///
+    /// If successful, returns the `frame::Frame` and the payload length.
+    pub fn try_consume_frame(&mut self) -> Result<(frame::Frame, u64)> {
         // Processing a frame other than DATA, so re-arm the Data event.
         self.reset_data_event();
 
+        let payload_len = self.state_len as u64;
+
         // TODO: properly propagate frame parsing errors.
         let frame = frame::Frame::from_bytes(
             self.frame_type.unwrap(),
-            self.state_len as u64,
+            payload_len,
             &self.state_buf,
         )?;
 
         self.state_transition(State::FrameType, 1, true)?;
 
-        Ok(frame)
+        Ok((frame, payload_len))
     }
 
     /// Tries to read DATA payload from the transport stream.
@@ -535,6 +583,21 @@
         self.data_event_triggered = false;
     }
 
+    /// Set the last priority update for the stream.
+    pub fn set_last_priority_update(&mut self, priority_update: Option<Vec<u8>>) {
+        self.last_priority_update = priority_update;
+    }
+
+    /// Take the last priority update and leave `None` in its place.
+    pub fn take_last_priority_update(&mut self) -> Option<Vec<u8>> {
+        self.last_priority_update.take()
+    }
+
+    /// Returns `true` if there is a priority update.
+    pub fn has_last_priority_update(&self) -> bool {
+        self.last_priority_update.is_some()
+    }
+
     /// Returns true if the state buffer has enough data to complete the state.
     fn state_buffer_complete(&self) -> bool {
         self.state_off == self.state_len
@@ -545,60 +608,109 @@
     fn state_transition(
         &mut self, new_state: State, expected_len: usize, resize: bool,
     ) -> Result<()> {
-        self.state = new_state;
-        self.state_off = 0;
-        self.state_len = expected_len;
-
         // Some states don't need the state buffer, so don't resize it if not
         // necessary.
         if resize {
             // A peer can influence the size of the state buffer (e.g. with the
             // payload size of a GREASE frame), so we need to limit the maximum
             // size to avoid DoS.
-            if self.state_len > MAX_STATE_BUF_SIZE {
-                return Err(Error::InternalError);
+            if expected_len > MAX_STATE_BUF_SIZE {
+                return Err(Error::ExcessiveLoad);
             }
 
-            self.state_buf.resize(self.state_len, 0);
+            self.state_buf.resize(expected_len, 0);
         }
 
+        self.state = new_state;
+        self.state_off = 0;
+        self.state_len = expected_len;
+
         Ok(())
     }
 }
 
 #[cfg(test)]
 mod tests {
+    use crate::h3::frame::*;
+
     use super::*;
 
+    fn open_uni(b: &mut octets::OctetsMut, ty: u64) -> Result<Stream> {
+        let stream = Stream::new(2, false);
+        assert_eq!(stream.state, State::StreamType);
+
+        b.put_varint(ty)?;
+
+        Ok(stream)
+    }
+
+    fn parse_uni(
+        stream: &mut Stream, ty: u64, cursor: &mut std::io::Cursor<Vec<u8>>,
+    ) -> Result<()> {
+        stream.try_fill_buffer_for_tests(cursor)?;
+
+        let stream_ty = stream.try_consume_varint()?;
+        assert_eq!(stream_ty, ty);
+        stream.set_ty(Type::deserialize(stream_ty).unwrap())?;
+
+        Ok(())
+    }
+
+    fn parse_skip_frame(
+        stream: &mut Stream, cursor: &mut std::io::Cursor<Vec<u8>>,
+    ) -> Result<()> {
+        // Parse the frame type.
+        stream.try_fill_buffer_for_tests(cursor)?;
+
+        let frame_ty = stream.try_consume_varint()?;
+
+        stream.set_frame_type(frame_ty)?;
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse the frame payload length.
+        stream.try_fill_buffer_for_tests(cursor)?;
+
+        let frame_payload_len = stream.try_consume_varint()?;
+        stream.set_frame_payload_len(frame_payload_len)?;
+        assert_eq!(stream.state, State::FramePayload);
+
+        // Parse the frame payload.
+        stream.try_fill_buffer_for_tests(cursor)?;
+
+        stream.try_consume_frame()?;
+        assert_eq!(stream.state, State::FrameType);
+
+        Ok(())
+    }
+
     #[test]
     /// Process incoming SETTINGS frame on control stream.
     fn control_good() {
-        let mut stream = Stream::new(3, false);
-        assert_eq!(stream.state, State::StreamType);
-
         let mut d = vec![42; 40];
         let mut b = octets::OctetsMut::with_slice(&mut d);
 
-        let frame = frame::Frame::Settings {
-            max_header_list_size: Some(0),
+        let raw_settings = vec![
+            (SETTINGS_MAX_FIELD_SECTION_SIZE, 0),
+            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
+            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
+        ];
+
+        let frame = Frame::Settings {
+            max_field_section_size: Some(0),
             qpack_max_table_capacity: Some(0),
             qpack_blocked_streams: Some(0),
+            connect_protocol_enabled: None,
             h3_datagram: None,
             grease: None,
+            raw: Some(raw_settings),
         };
 
-        b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+        let mut stream = open_uni(&mut b, HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
         frame.to_bytes(&mut b).unwrap();
 
         let mut cursor = std::io::Cursor::new(d);
 
-        // Parse stream type.
-        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
-
-        let stream_ty = stream.try_consume_varint().unwrap();
-        assert_eq!(stream_ty, HTTP3_CONTROL_STREAM_TYPE_ID);
-        stream
-            .set_ty(Type::deserialize(stream_ty).unwrap())
+        parse_uni(&mut stream, HTTP3_CONTROL_STREAM_TYPE_ID, &mut cursor)
             .unwrap();
         assert_eq!(stream.state, State::FrameType);
 
@@ -622,40 +734,88 @@
         // Parse the SETTINGS frame payload.
         stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
 
-        assert_eq!(stream.try_consume_frame(), Ok(frame));
+        assert_eq!(stream.try_consume_frame(), Ok((frame, 6)));
+        assert_eq!(stream.state, State::FrameType);
+    }
+
+    #[test]
+    /// Process incoming empty SETTINGS frame on control stream.
+    fn control_empty_settings() {
+        let mut d = vec![42; 40];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame = Frame::Settings {
+            max_field_section_size: None,
+            qpack_max_table_capacity: None,
+            qpack_blocked_streams: None,
+            connect_protocol_enabled: None,
+            h3_datagram: None,
+            grease: None,
+            raw: Some(vec![]),
+        };
+
+        let mut stream = open_uni(&mut b, HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+        frame.to_bytes(&mut b).unwrap();
+
+        let mut cursor = std::io::Cursor::new(d);
+
+        parse_uni(&mut stream, HTTP3_CONTROL_STREAM_TYPE_ID, &mut cursor)
+            .unwrap();
+        assert_eq!(stream.state, State::FrameType);
+
+        // Parse the SETTINGS frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::SETTINGS_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse the SETTINGS frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_payload_len, 0);
+        stream.set_frame_payload_len(frame_payload_len).unwrap();
+        assert_eq!(stream.state, State::FramePayload);
+
+        // Parse the SETTINGS frame payload.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+
+        assert_eq!(stream.try_consume_frame(), Ok((frame, 0)));
         assert_eq!(stream.state, State::FrameType);
     }
 
     #[test]
     /// Process duplicate SETTINGS frame on control stream.
     fn control_bad_multiple_settings() {
-        let mut stream = Stream::new(3, false);
-        assert_eq!(stream.state, State::StreamType);
-
         let mut d = vec![42; 40];
         let mut b = octets::OctetsMut::with_slice(&mut d);
 
+        let raw_settings = vec![
+            (SETTINGS_MAX_FIELD_SECTION_SIZE, 0),
+            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
+            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
+        ];
+
         let frame = frame::Frame::Settings {
-            max_header_list_size: Some(0),
+            max_field_section_size: Some(0),
             qpack_max_table_capacity: Some(0),
             qpack_blocked_streams: Some(0),
+            connect_protocol_enabled: None,
             h3_datagram: None,
             grease: None,
+            raw: Some(raw_settings),
         };
 
-        b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+        let mut stream = open_uni(&mut b, HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
         frame.to_bytes(&mut b).unwrap();
         frame.to_bytes(&mut b).unwrap();
 
         let mut cursor = std::io::Cursor::new(d);
 
-        // Parse stream type.
-        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
-
-        let stream_ty = stream.try_consume_varint().unwrap();
-        assert_eq!(stream_ty, HTTP3_CONTROL_STREAM_TYPE_ID);
-        stream
-            .set_ty(Type::deserialize(stream_ty).unwrap())
+        parse_uni(&mut stream, HTTP3_CONTROL_STREAM_TYPE_ID, &mut cursor)
             .unwrap();
         assert_eq!(stream.state, State::FrameType);
 
@@ -679,7 +839,7 @@
         // Parse the SETTINGS frame payload.
         stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
 
-        assert_eq!(stream.try_consume_frame(), Ok(frame));
+        assert_eq!(stream.try_consume_frame(), Ok((frame, 6)));
         assert_eq!(stream.state, State::FrameType);
 
         // Parse the second SETTINGS frame type.
@@ -692,35 +852,34 @@
     #[test]
     /// Process other frame before SETTINGS frame on control stream.
     fn control_bad_late_settings() {
-        let mut stream = Stream::new(3, false);
-        assert_eq!(stream.state, State::StreamType);
-
         let mut d = vec![42; 40];
         let mut b = octets::OctetsMut::with_slice(&mut d);
 
         let goaway = frame::Frame::GoAway { id: 0 };
 
+        let raw_settings = vec![
+            (SETTINGS_MAX_FIELD_SECTION_SIZE, 0),
+            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
+            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
+        ];
+
         let settings = frame::Frame::Settings {
-            max_header_list_size: Some(0),
+            max_field_section_size: Some(0),
             qpack_max_table_capacity: Some(0),
             qpack_blocked_streams: Some(0),
+            connect_protocol_enabled: None,
             h3_datagram: None,
             grease: None,
+            raw: Some(raw_settings),
         };
 
-        b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+        let mut stream = open_uni(&mut b, HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
         goaway.to_bytes(&mut b).unwrap();
         settings.to_bytes(&mut b).unwrap();
 
         let mut cursor = std::io::Cursor::new(d);
 
-        // Parse stream type.
-        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
-
-        let stream_ty = stream.try_consume_varint().unwrap();
-        assert_eq!(stream_ty, HTTP3_CONTROL_STREAM_TYPE_ID);
-        stream
-            .set_ty(Type::deserialize(stream_ty).unwrap())
+        parse_uni(&mut stream, HTTP3_CONTROL_STREAM_TYPE_ID, &mut cursor)
             .unwrap();
         assert_eq!(stream.state, State::FrameType);
 
@@ -734,36 +893,38 @@
     #[test]
     /// Process not-allowed frame on control stream.
     fn control_bad_frame() {
-        let mut stream = Stream::new(3, false);
-        assert_eq!(stream.state, State::StreamType);
-
         let mut d = vec![42; 40];
         let mut b = octets::OctetsMut::with_slice(&mut d);
 
         let header_block = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
         let hdrs = frame::Frame::Headers { header_block };
 
+        let raw_settings = vec![
+            (SETTINGS_MAX_FIELD_SECTION_SIZE, 0),
+            (SETTINGS_QPACK_MAX_TABLE_CAPACITY, 0),
+            (SETTINGS_QPACK_BLOCKED_STREAMS, 0),
+            (33, 33),
+        ];
+
         let settings = frame::Frame::Settings {
-            max_header_list_size: Some(0),
+            max_field_section_size: Some(0),
             qpack_max_table_capacity: Some(0),
             qpack_blocked_streams: Some(0),
+            connect_protocol_enabled: None,
             h3_datagram: None,
             grease: None,
+            raw: Some(raw_settings),
         };
 
-        b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+        let mut stream = open_uni(&mut b, HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
         settings.to_bytes(&mut b).unwrap();
         hdrs.to_bytes(&mut b).unwrap();
 
         let mut cursor = std::io::Cursor::new(d);
 
-        // Parse stream type.
-        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
-
-        let stream_ty = stream.try_consume_varint().unwrap();
-        stream
-            .set_ty(Type::deserialize(stream_ty).unwrap())
+        parse_uni(&mut stream, HTTP3_CONTROL_STREAM_TYPE_ID, &mut cursor)
             .unwrap();
+        assert_eq!(stream.state, State::FrameType);
 
         // Parse first SETTINGS frame.
         stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
@@ -837,7 +998,7 @@
         // Parse the HEADERS frame.
         stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
 
-        assert_eq!(stream.try_consume_frame(), Ok(hdrs));
+        assert_eq!(stream.try_consume_frame(), Ok((hdrs, 12)));
         assert_eq!(stream.state, State::FrameType);
 
         // Parse the DATA frame type.
@@ -871,8 +1032,6 @@
 
     #[test]
     fn push_good() {
-        let mut stream = Stream::new(2, false);
-
         let mut d = vec![42; 128];
         let mut b = octets::OctetsMut::with_slice(&mut d);
 
@@ -883,21 +1042,14 @@
             payload: payload.clone(),
         };
 
-        b.put_varint(HTTP3_PUSH_STREAM_TYPE_ID).unwrap();
+        let mut stream = open_uni(&mut b, HTTP3_PUSH_STREAM_TYPE_ID).unwrap();
         b.put_varint(1).unwrap();
         hdrs.to_bytes(&mut b).unwrap();
         data.to_bytes(&mut b).unwrap();
 
         let mut cursor = std::io::Cursor::new(d);
 
-        // Parse stream type.
-        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
-
-        let stream_ty = stream.try_consume_varint().unwrap();
-        assert_eq!(stream_ty, HTTP3_PUSH_STREAM_TYPE_ID);
-        stream
-            .set_ty(Type::deserialize(stream_ty).unwrap())
-            .unwrap();
+        parse_uni(&mut stream, HTTP3_PUSH_STREAM_TYPE_ID, &mut cursor).unwrap();
         assert_eq!(stream.state, State::PushId);
 
         // Parse push ID.
@@ -930,7 +1082,7 @@
         // Parse the HEADERS frame.
         stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
 
-        assert_eq!(stream.try_consume_frame(), Ok(hdrs));
+        assert_eq!(stream.try_consume_frame(), Ok((hdrs, 12)));
         assert_eq!(stream.state, State::FrameType);
 
         // Parse the DATA frame type.
@@ -964,12 +1116,10 @@
 
     #[test]
     fn grease() {
-        let mut stream = Stream::new(2, false);
-
         let mut d = vec![42; 20];
         let mut b = octets::OctetsMut::with_slice(&mut d);
 
-        b.put_varint(33).unwrap();
+        let mut stream = open_uni(&mut b, 33).unwrap();
 
         let mut cursor = std::io::Cursor::new(d);
 
@@ -1007,4 +1157,178 @@
 
         assert_eq!(stream.set_frame_type(frame_ty), Err(Error::FrameUnexpected));
     }
+
+    #[test]
+    fn zero_length_goaway() {
+        let mut d = vec![42; 128];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame = Frame::Settings {
+            max_field_section_size: None,
+            qpack_max_table_capacity: None,
+            qpack_blocked_streams: None,
+            connect_protocol_enabled: None,
+            h3_datagram: None,
+            grease: None,
+            raw: Some(vec![]),
+        };
+
+        let mut stream = open_uni(&mut b, HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+        frame.to_bytes(&mut b).unwrap();
+
+        // Write a 0-length payload frame.
+        b.put_varint(frame::GOAWAY_FRAME_TYPE_ID).unwrap();
+        b.put_varint(0).unwrap();
+
+        let mut cursor = std::io::Cursor::new(d);
+
+        parse_uni(&mut stream, HTTP3_CONTROL_STREAM_TYPE_ID, &mut cursor)
+            .unwrap();
+
+        // Skip SETTINGS frame type.
+        parse_skip_frame(&mut stream, &mut cursor).unwrap();
+
+        // Parse frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::GOAWAY_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(
+            Err(Error::FrameError),
+            stream.set_frame_payload_len(frame_payload_len)
+        );
+    }
+
+    #[test]
+    fn zero_length_push_promise() {
+        let mut d = vec![42; 128];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let mut stream = Stream::new(0, false);
+
+        assert_eq!(stream.ty, Some(Type::Request));
+        assert_eq!(stream.state, State::FrameType);
+
+        // Write a 0-length payload frame.
+        b.put_varint(frame::PUSH_PROMISE_FRAME_TYPE_ID).unwrap();
+        b.put_varint(0).unwrap();
+
+        let mut cursor = std::io::Cursor::new(d);
+
+        // Parse frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::PUSH_PROMISE_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(
+            Err(Error::FrameError),
+            stream.set_frame_payload_len(frame_payload_len)
+        );
+    }
+
+    #[test]
+    fn zero_length_cancel_push() {
+        let mut d = vec![42; 128];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame = Frame::Settings {
+            max_field_section_size: None,
+            qpack_max_table_capacity: None,
+            qpack_blocked_streams: None,
+            connect_protocol_enabled: None,
+            h3_datagram: None,
+            grease: None,
+            raw: Some(vec![]),
+        };
+
+        let mut stream = open_uni(&mut b, HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+        frame.to_bytes(&mut b).unwrap();
+
+        // Write a 0-length payload frame.
+        b.put_varint(frame::CANCEL_PUSH_FRAME_TYPE_ID).unwrap();
+        b.put_varint(0).unwrap();
+
+        let mut cursor = std::io::Cursor::new(d);
+
+        parse_uni(&mut stream, HTTP3_CONTROL_STREAM_TYPE_ID, &mut cursor)
+            .unwrap();
+
+        // Skip SETTINGS frame type.
+        parse_skip_frame(&mut stream, &mut cursor).unwrap();
+
+        // Parse frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::CANCEL_PUSH_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(
+            Err(Error::FrameError),
+            stream.set_frame_payload_len(frame_payload_len)
+        );
+    }
+
+    #[test]
+    fn zero_length_max_push_id() {
+        let mut d = vec![42; 128];
+        let mut b = octets::OctetsMut::with_slice(&mut d);
+
+        let frame = Frame::Settings {
+            max_field_section_size: None,
+            qpack_max_table_capacity: None,
+            qpack_blocked_streams: None,
+            connect_protocol_enabled: None,
+            h3_datagram: None,
+            grease: None,
+            raw: Some(vec![]),
+        };
+
+        let mut stream = open_uni(&mut b, HTTP3_CONTROL_STREAM_TYPE_ID).unwrap();
+        frame.to_bytes(&mut b).unwrap();
+
+        // Write a 0-length payload frame.
+        b.put_varint(frame::MAX_PUSH_FRAME_TYPE_ID).unwrap();
+        b.put_varint(0).unwrap();
+
+        let mut cursor = std::io::Cursor::new(d);
+
+        parse_uni(&mut stream, HTTP3_CONTROL_STREAM_TYPE_ID, &mut cursor)
+            .unwrap();
+
+        // Skip SETTINGS frame type.
+        parse_skip_frame(&mut stream, &mut cursor).unwrap();
+
+        // Parse frame type.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+        let frame_ty = stream.try_consume_varint().unwrap();
+        assert_eq!(frame_ty, frame::MAX_PUSH_FRAME_TYPE_ID);
+
+        stream.set_frame_type(frame_ty).unwrap();
+        assert_eq!(stream.state, State::FramePayloadLen);
+
+        // Parse frame payload length.
+        stream.try_fill_buffer_for_tests(&mut cursor).unwrap();
+        let frame_payload_len = stream.try_consume_varint().unwrap();
+        assert_eq!(
+            Err(Error::FrameError),
+            stream.set_frame_payload_len(frame_payload_len)
+        );
+    }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 0087b52..97ad10e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -35,18 +35,46 @@
 //! [quiche]: https://github.com/cloudflare/quiche/
 //! [ietf]: https://quicwg.org/
 //!
-//! ## Connection setup
+//! ## Configuring connections
 //!
 //! The first step in establishing a QUIC connection using quiche is creating a
-//! configuration object:
+//! [`Config`] object:
 //!
 //! ```
-//! let config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+//! config.set_application_protos(&[b"example-proto"]);
+//!
+//! // Additional configuration specific to application and use case...
 //! # Ok::<(), quiche::Error>(())
 //! ```
 //!
-//! This is shared among multiple connections and can be used to configure a
-//! QUIC endpoint.
+//! The [`Config`] object controls important aspects of the QUIC connection such
+//! as QUIC version, ALPN IDs, flow control, congestion control, idle timeout
+//! and other properties or features.
+//!
+//! QUIC is a general-purpose transport protocol and there are several
+//! configuration properties where there is no reasonable default value. For
+//! example, the permitted number of concurrent streams of any particular type
+//! is dependent on the application running over QUIC, and other use-case
+//! specific concerns.
+//!
+//! quiche defaults several properties to zero, applications most likely need
+//! to set these to something else to satisfy their needs using the following:
+//!
+//! - [`set_initial_max_streams_bidi()`]
+//! - [`set_initial_max_streams_uni()`]
+//! - [`set_initial_max_data()`]
+//! - [`set_initial_max_stream_data_bidi_local()`]
+//! - [`set_initial_max_stream_data_bidi_remote()`]
+//! - [`set_initial_max_stream_data_uni()`]
+//!
+//! [`Config`] also holds TLS configuration. This can be changed by mutators on
+//! the an existing object, or by constructing a TLS context manually and
+//! creating a configuration using [`with_boring_ssl_ctx()`].
+//!
+//! A configuration object can be shared among multiple connections.
+//!
+//! ### Connection setup
 //!
 //! On the client-side the [`connect()`] utility function can be used to create
 //! a new connection, while [`accept()`] is for servers:
@@ -55,13 +83,16 @@
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
 //! # let server_name = "quic.tech";
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let to = "127.0.0.1:1234".parse().unwrap();
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
 //! // Client connection.
-//! let conn = quiche::connect(Some(&server_name), &scid, to, &mut config)?;
+//! let conn =
+//!     quiche::connect(Some(&server_name), &scid, local, peer, &mut config)?;
 //!
 //! // Server connection.
-//! # let from = "127.0.0.1:1234".parse().unwrap();
-//! let conn = quiche::accept(&scid, None, from, &mut config)?;
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! let conn = quiche::accept(&scid, None, local, peer, &mut config)?;
 //! # Ok::<(), quiche::Error>(())
 //! ```
 //!
@@ -83,12 +114,15 @@
 //! # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let from = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
+//! let to = socket.local_addr().unwrap();
+//!
 //! loop {
 //!     let (read, from) = socket.recv_from(&mut buf).unwrap();
 //!
-//!     let recv_info = quiche::RecvInfo { from };
+//!     let recv_info = quiche::RecvInfo { from, to };
 //!
 //!     let read = match conn.recv(&mut buf[..read], recv_info) {
 //!         Ok(v) => v,
@@ -121,8 +155,9 @@
 //! # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let from = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
 //! loop {
 //!     let (write, send_info) = match conn.send(&mut out) {
 //!         Ok(v) => v,
@@ -154,8 +189,9 @@
 //! ```
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let from = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
 //! let timeout = conn.timeout();
 //! # Ok::<(), quiche::Error>(())
 //! ```
@@ -170,8 +206,9 @@
 //! # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let from = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
 //! // Timeout expired, handle it.
 //! conn.on_timeout();
 //!
@@ -196,6 +233,25 @@
 //! # Ok::<(), quiche::Error>(())
 //! ```
 //!
+//! ### Pacing
+//!
+//! It is recommended that applications [pace] sending of outgoing packets to
+//! avoid creating packet bursts that could cause short-term congestion and
+//! losses in the network.
+//!
+//! quiche exposes pacing hints for outgoing packets through the [`at`] field
+//! of the [`SendInfo`] structure that is returned by the [`send()`] method.
+//! This field represents the time when a specific packet should be sent into
+//! the network.
+//!
+//! Applications can use these hints by artificially delaying the sending of
+//! packets through platform-specific mechanisms (such as the [`SO_TXTIME`]
+//! socket option on Linux), or custom methods (for example by using user-space
+//! timers).
+//!
+//! [pace]: https://datatracker.ietf.org/doc/html/rfc9002#section-7.7
+//! [`SO_TXTIME`]: https://man7.org/linux/man-pages/man8/tc-etf.8.html
+//!
 //! ## Sending and receiving stream data
 //!
 //! After some back and forth, the connection will complete its handshake and
@@ -206,8 +262,9 @@
 //! ```no_run
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let from = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
 //! if conn.is_established() {
 //!     // Handshake completed, send some data on stream 0.
 //!     conn.stream_send(0, b"hello", true)?;
@@ -226,8 +283,9 @@
 //! # let mut buf = [0; 512];
 //! # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
 //! # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-//! # let from = "127.0.0.1:1234".parse().unwrap();
-//! # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+//! # let peer = "127.0.0.1:1234".parse().unwrap();
+//! # let local = "127.0.0.1:4321".parse().unwrap();
+//! # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
 //! if conn.is_established() {
 //!     // Iterate over readable streams.
 //!     for stream_id in conn.readable() {
@@ -245,12 +303,21 @@
 //! The quiche [HTTP/3 module] provides a high level API for sending and
 //! receiving HTTP requests and responses on top of the QUIC transport protocol.
 //!
+//! [`Config`]: https://docs.quic.tech/quiche/struct.Config.html
+//! [`set_initial_max_streams_bidi()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_streams_bidi
+//! [`set_initial_max_streams_uni()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_streams_uni
+//! [`set_initial_max_data()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_data
+//! [`set_initial_max_stream_data_bidi_local()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_local
+//! [`set_initial_max_stream_data_bidi_remote()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_stream_data_bidi_remote
+//! [`set_initial_max_stream_data_uni()`]: https://docs.rs/quiche/latest/quiche/struct.Config.html#method.set_initial_max_stream_data_uni
+//! [`with_boring_ssl_ctx()`]: https://docs.quic.tech/quiche/struct.Config.html#method.with_boring_ssl_ctx
 //! [`connect()`]: fn.connect.html
 //! [`accept()`]: fn.accept.html
 //! [`recv()`]: struct.Connection.html#method.recv
 //! [`RecvInfo`]: struct.RecvInfo.html
 //! [`send()`]: struct.Connection.html#method.send
 //! [`SendInfo`]: struct.SendInfo.html
+//! [`at`]: struct.SendInfo.html#structfield.at
 //! [`timeout()`]: struct.Connection.html#method.timeout
 //! [`on_timeout()`]: struct.Connection.html#method.on_timeout
 //! [`stream_send()`]: struct.Connection.html#method.stream_send
@@ -286,27 +353,67 @@
 //! or [`accept()`]. Otherwise the connection will use a default CC algorithm.
 //!
 //! [`CongestionControlAlgorithm`]: enum.CongestionControlAlgorithm.html
+//!
+//! ## Feature flags
+//!
+//! quiche defines a number of [feature flags] to reduce the amount of compiled
+//! code and dependencies:
+//!
+//! * `boringssl-vendored` (default): Build the vendored BoringSSL library.
+//!
+//! * `boringssl-boring-crate`: Use the BoringSSL library provided by the
+//!   [boring] crate. It takes precedence over `boringssl-vendored` if both
+//!   features are enabled.
+//!
+//! * `pkg-config-meta`: Generate pkg-config metadata file for libquiche.
+//!
+//! * `ffi`: Build and expose the FFI API.
+//!
+//! * `qlog`: Enable support for the [qlog] logging format.
+//!
+//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
+//! [boring]: https://crates.io/crates/boring
+//! [qlog]: https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-main-schema
 
-#![allow(improper_ctypes)]
-#![allow(clippy::suspicious_operation_groupings)]
 #![allow(clippy::upper_case_acronyms)]
 #![warn(missing_docs)]
+#![cfg_attr(docsrs, feature(doc_cfg))]
 
 #[macro_use]
 extern crate log;
 
+#[cfg(feature = "qlog")]
+use qlog::events::connectivity::TransportOwner;
+#[cfg(feature = "qlog")]
+use qlog::events::quic::RecoveryEventType;
+#[cfg(feature = "qlog")]
+use qlog::events::quic::TransportEventType;
+#[cfg(feature = "qlog")]
+use qlog::events::DataRecipient;
+#[cfg(feature = "qlog")]
+use qlog::events::Event;
+#[cfg(feature = "qlog")]
+use qlog::events::EventData;
+#[cfg(feature = "qlog")]
+use qlog::events::EventImportance;
+#[cfg(feature = "qlog")]
+use qlog::events::EventType;
+#[cfg(feature = "qlog")]
+use qlog::events::RawInfo;
+
 use std::cmp;
+use std::convert::TryInto;
 use std::time;
 
 use std::net::SocketAddr;
 
-use std::pin::Pin;
 use std::str::FromStr;
 
-use std::sync::Mutex;
-
+use std::collections::HashSet;
 use std::collections::VecDeque;
 
+use smallvec::SmallVec;
+
 /// The current QUIC wire version.
 pub const PROTOCOL_VERSION: u32 = PROTOCOL_VERSION_V1;
 
@@ -333,6 +440,9 @@
 // account for that.
 const PAYLOAD_MIN_LEN: usize = 20;
 
+// PATH_CHALLENGE (9 bytes) + AEAD tag (16 bytes).
+const MIN_PROBING_SIZE: usize = 25;
+
 const MAX_AMPLIFICATION_FACTOR: usize = 3;
 
 // The maximum number of tracked packet number ranges that need to be acked.
@@ -361,6 +471,20 @@
 
 const RESERVED_VERSION_MASK: u32 = 0xfafafafa;
 
+// The default size of the receiver connection flow control window.
+const DEFAULT_CONNECTION_WINDOW: u64 = 48 * 1024;
+
+// The maximum size of the receiver connection flow control window.
+const MAX_CONNECTION_WINDOW: u64 = 24 * 1024 * 1024;
+
+// How much larger the connection flow control window need to be larger than
+// the stream flow control window.
+const CONNECTION_WINDOW_FACTOR: f64 = 1.5;
+
+// How many probing packet timeouts do we tolerate before considering the path
+// validation as failed.
+const MAX_PROBING_TIMEOUTS: usize = 3;
+
 /// A specialized [`Result`] type for quiche operations.
 ///
 /// This type is used throughout quiche's public API for any operation that
@@ -370,7 +494,7 @@
 pub type Result<T> = std::result::Result<T, Error>;
 
 /// A QUIC error.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum Error {
     /// There is no more work to do.
     Done,
@@ -419,11 +543,26 @@
     /// associated data.
     StreamStopped(u64),
 
+    /// The specified stream was reset by the peer.
+    ///
+    /// The error code sent as part of the `RESET_STREAM` frame is provided as
+    /// associated data.
+    StreamReset(u64),
+
     /// The received data exceeds the stream's final size.
     FinalSize,
 
     /// Error in congestion control.
     CongestionControl,
+
+    /// Too many identifiers were provided.
+    IdLimit,
+
+    /// Not enough available identifiers.
+    OutOfIdentifiers,
+
+    /// Error in key update.
+    KeyUpdate,
 }
 
 impl Error {
@@ -436,6 +575,7 @@
             Error::FlowControl => 0x3,
             Error::StreamLimit => 0x4,
             Error::FinalSize => 0x6,
+            Error::KeyUpdate => 0xe,
             _ => 0xa,
         }
     }
@@ -458,13 +598,17 @@
             Error::FinalSize => -13,
             Error::CongestionControl => -14,
             Error::StreamStopped { .. } => -15,
+            Error::StreamReset { .. } => -16,
+            Error::IdLimit => -17,
+            Error::OutOfIdentifiers => -18,
+            Error::KeyUpdate => -19,
         }
     }
 }
 
 impl std::fmt::Display for Error {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        write!(f, "{:?}", self)
+        write!(f, "{self:?}")
     }
 }
 
@@ -481,24 +625,34 @@
 }
 
 /// Ancillary information about incoming packets.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub struct RecvInfo {
-    /// The address the packet was received from.
+    /// The remote address the packet was received from.
     pub from: SocketAddr,
+
+    /// The local address the packet was received on.
+    pub to: SocketAddr,
 }
 
 /// Ancillary information about outgoing packets.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub struct SendInfo {
-    /// The address the packet should be sent to.
+    /// The local address the packet should be sent from.
+    pub from: SocketAddr,
+
+    /// The remote address the packet should be sent to.
     pub to: SocketAddr,
 
     /// The time to send the packet out.
+    ///
+    /// See [Pacing] for more details.
+    ///
+    /// [Pacing]: index.html#pacing
     pub at: time::Instant,
 }
 
 /// Represents information carried by `CONNECTION_CLOSE` frames.
-#[derive(Clone, Debug, PartialEq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub struct ConnectionError {
     /// Whether the error came from the application or the transport layer.
     pub is_app: bool,
@@ -510,12 +664,13 @@
     pub reason: Vec<u8>,
 }
 
-/// The stream's side to shutdown.
+/// The side of the stream to be shut down.
 ///
 /// This should be used when calling [`stream_shutdown()`].
 ///
 /// [`stream_shutdown()`]: struct.Connection.html#method.stream_shutdown
 #[repr(C)]
+#[derive(PartialEq, Eq)]
 pub enum Shutdown {
     /// Stop receiving stream data.
     Read  = 0,
@@ -524,17 +679,28 @@
     Write = 1,
 }
 
+/// Qlog logging level.
+#[repr(C)]
+#[cfg(feature = "qlog")]
+#[cfg_attr(docsrs, doc(cfg(feature = "qlog")))]
+pub enum QlogLevel {
+    /// Logs any events of Core importance.
+    Core  = 0,
+
+    /// Logs any events of Core and Base importance.
+    Base  = 1,
+
+    /// Logs any events of Core, Base and Extra importance
+    Extra = 2,
+}
+
 /// Stores configuration shared between multiple connections.
 pub struct Config {
     local_transport_params: TransportParams,
 
     version: u32,
 
-    // BoringSSL's SSL_CTX structure is technically safe to share across threads
-    // but once shared, functions that modify it can't be used any more. We can't
-    // encode that in Rust, so just make it Send+Sync with a mutex to fulfill
-    // the Sync constraint.
-    tls_ctx: Mutex<tls::Context>,
+    tls_ctx: tls::Context,
 
     application_protos: Vec<Vec<u8>>,
 
@@ -544,10 +710,17 @@
 
     hystart: bool,
 
+    pacing: bool,
+
     dgram_recv_max_queue_len: usize,
     dgram_send_max_queue_len: usize,
 
     max_send_udp_payload_size: usize,
+
+    max_connection_window: u64,
+    max_stream_window: u64,
+
+    disable_dcid_reuse: bool,
 }
 
 // See https://quicwg.org/base-drafts/rfc9000.html#section-15
@@ -565,12 +738,28 @@
     /// # Ok::<(), quiche::Error>(())
     /// ```
     pub fn new(version: u32) -> Result<Config> {
+        Self::with_tls_ctx(version, tls::Context::new()?)
+    }
+
+    /// Creates a config object with the given version and [`SslContext`].
+    ///
+    /// This is useful for applications that wish to manually configure
+    /// [`SslContext`].
+    ///
+    /// [`SslContext`]: https://docs.rs/boring/latest/boring/ssl/struct.SslContext.html
+    #[cfg(feature = "boringssl-boring-crate")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "boringssl-boring-crate")))]
+    pub fn with_boring_ssl_ctx(
+        version: u32, tls_ctx: boring::ssl::SslContext,
+    ) -> Result<Config> {
+        Self::with_tls_ctx(version, tls::Context::from_boring(tls_ctx))
+    }
+
+    fn with_tls_ctx(version: u32, tls_ctx: tls::Context) -> Result<Config> {
         if !is_reserved_version(version) && !version_is_supported(version) {
             return Err(Error::UnknownVersion);
         }
 
-        let tls_ctx = Mutex::new(tls::Context::new()?);
-
         Ok(Config {
             local_transport_params: TransportParams::default(),
             version,
@@ -579,11 +768,17 @@
             grease: true,
             cc_algorithm: CongestionControlAlgorithm::CUBIC,
             hystart: true,
+            pacing: true,
 
             dgram_recv_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN,
             dgram_send_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN,
 
             max_send_udp_payload_size: MAX_SEND_UDP_PAYLOAD_SIZE,
+
+            max_connection_window: MAX_CONNECTION_WINDOW,
+            max_stream_window: stream::MAX_STREAM_WINDOW,
+
+            disable_dcid_reuse: false,
         })
     }
 
@@ -600,10 +795,7 @@
     /// # Ok::<(), quiche::Error>(())
     /// ```
     pub fn load_cert_chain_from_pem_file(&mut self, file: &str) -> Result<()> {
-        self.tls_ctx
-            .lock()
-            .unwrap()
-            .use_certificate_chain_file(file)
+        self.tls_ctx.use_certificate_chain_file(file)
     }
 
     /// Configures the given private key.
@@ -618,7 +810,7 @@
     /// # Ok::<(), quiche::Error>(())
     /// ```
     pub fn load_priv_key_from_pem_file(&mut self, file: &str) -> Result<()> {
-        self.tls_ctx.lock().unwrap().use_privkey_file(file)
+        self.tls_ctx.use_privkey_file(file)
     }
 
     /// Specifies a file where trusted CA certificates are stored for the
@@ -634,10 +826,7 @@
     /// # Ok::<(), quiche::Error>(())
     /// ```
     pub fn load_verify_locations_from_file(&mut self, file: &str) -> Result<()> {
-        self.tls_ctx
-            .lock()
-            .unwrap()
-            .load_verify_locations_from_file(file)
+        self.tls_ctx.load_verify_locations_from_file(file)
     }
 
     /// Specifies a directory where trusted CA certificates are stored for the
@@ -655,10 +844,7 @@
     pub fn load_verify_locations_from_directory(
         &mut self, dir: &str,
     ) -> Result<()> {
-        self.tls_ctx
-            .lock()
-            .unwrap()
-            .load_verify_locations_from_directory(dir)
+        self.tls_ctx.load_verify_locations_from_directory(dir)
     }
 
     /// Configures whether to verify the peer's certificate.
@@ -666,7 +852,7 @@
     /// The default value is `true` for client connections, and `false` for
     /// server ones.
     pub fn verify_peer(&mut self, verify: bool) {
-        self.tls_ctx.lock().unwrap().set_verify(verify);
+        self.tls_ctx.set_verify(verify);
     }
 
     /// Configures whether to send GREASE values.
@@ -685,7 +871,7 @@
     /// [`set_keylog()`]: struct.Connection.html#method.set_keylog
     /// [keylog]: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Key_Log_Format
     pub fn log_keys(&mut self) {
-        self.tls_ctx.lock().unwrap().enable_keylog();
+        self.tls_ctx.enable_keylog();
     }
 
     /// Configures the session ticket key material.
@@ -699,19 +885,16 @@
     /// servers), in which case the application is also responsible for
     /// rotating the key to provide forward secrecy.
     pub fn set_ticket_key(&mut self, key: &[u8]) -> Result<()> {
-        self.tls_ctx.lock().unwrap().set_ticket_key(key)
+        self.tls_ctx.set_ticket_key(key)
     }
 
     /// Enables sending or receiving early data.
     pub fn enable_early_data(&mut self) {
-        self.tls_ctx.lock().unwrap().set_early_data_enabled(true);
+        self.tls_ctx.set_early_data_enabled(true);
     }
 
     /// Configures the list of supported application protocols.
     ///
-    /// The list of protocols `protos` must be in wire-format (i.e. a series
-    /// of non-empty, 8-bit length-prefixed strings).
-    ///
     /// On the client this configures the list of protocols to send to the
     /// server as part of the ALPN extension.
     ///
@@ -724,24 +907,46 @@
     ///
     /// ```
     /// # let mut config = quiche::Config::new(0xbabababa)?;
-    /// config.set_application_protos(b"\x08http/1.1\x08http/0.9")?;
+    /// config.set_application_protos(&[b"http/1.1", b"http/0.9"]);
     /// # Ok::<(), quiche::Error>(())
     /// ```
-    pub fn set_application_protos(&mut self, protos: &[u8]) -> Result<()> {
-        let mut b = octets::Octets::with_slice(&protos);
+    pub fn set_application_protos(
+        &mut self, protos_list: &[&[u8]],
+    ) -> Result<()> {
+        self.application_protos =
+            protos_list.iter().map(|s| s.to_vec()).collect();
+
+        self.tls_ctx.set_alpn(protos_list)
+    }
+
+    /// Configures the list of supported application protocols using wire
+    /// format.
+    ///
+    /// The list of protocols `protos` must be a series of non-empty, 8-bit
+    /// length-prefixed strings.
+    ///
+    /// See [`set_application_protos`](Self::set_application_protos) for more
+    /// background about application protocols.
+    ///
+    /// ## Examples:
+    ///
+    /// ```
+    /// # let mut config = quiche::Config::new(0xbabababa)?;
+    /// config.set_application_protos_wire_format(b"\x08http/1.1\x08http/0.9")?;
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn set_application_protos_wire_format(
+        &mut self, protos: &[u8],
+    ) -> Result<()> {
+        let mut b = octets::Octets::with_slice(protos);
 
         let mut protos_list = Vec::new();
 
         while let Ok(proto) = b.get_bytes_with_u8_length() {
-            protos_list.push(proto.to_vec());
+            protos_list.push(proto.buf());
         }
 
-        self.application_protos = protos_list;
-
-        self.tls_ctx
-            .lock()
-            .unwrap()
-            .set_alpn(&self.application_protos)
+        self.set_application_protos(&protos_list)
     }
 
     /// Sets the `max_idle_timeout` transport parameter, in milliseconds.
@@ -767,10 +972,14 @@
 
     /// Sets the `initial_max_data` transport parameter.
     ///
-    /// When set to a non-zero value quiche will only allow at most `v` bytes
-    /// of incoming stream data to be buffered for the whole connection (that
-    /// is, data that is not yet read by the application) and will allow more
-    /// data to be received as the buffer is consumed by the application.
+    /// When set to a non-zero value quiche will only allow at most `v` bytes of
+    /// incoming stream data to be buffered for the whole connection (that is,
+    /// data that is not yet read by the application) and will allow more data
+    /// to be received as the buffer is consumed by the application.
+    ///
+    /// When set to zero, either explicitly or via the default, quiche will not
+    /// give any flow control to the peer, preventing it from sending any stream
+    /// data.
     ///
     /// The default value is `0`.
     pub fn set_initial_max_data(&mut self, v: u64) {
@@ -785,6 +994,10 @@
     /// application) and will allow more data to be received as the buffer is
     /// consumed by the application.
     ///
+    /// When set to zero, either explicitly or via the default, quiche will not
+    /// give any flow control to the peer, preventing it from sending any stream
+    /// data.
+    ///
     /// The default value is `0`.
     pub fn set_initial_max_stream_data_bidi_local(&mut self, v: u64) {
         self.local_transport_params
@@ -799,6 +1012,10 @@
     /// application) and will allow more data to be received as the buffer is
     /// consumed by the application.
     ///
+    /// When set to zero, either explicitly or via the default, quiche will not
+    /// give any flow control to the peer, preventing it from sending any stream
+    /// data.
+    ///
     /// The default value is `0`.
     pub fn set_initial_max_stream_data_bidi_remote(&mut self, v: u64) {
         self.local_transport_params
@@ -812,6 +1029,10 @@
     /// (that is, data that is not yet read by the application) and will allow
     /// more data to be received as the buffer is consumed by the application.
     ///
+    /// When set to zero, either explicitly or via the default, quiche will not
+    /// give any flow control to the peer, preventing it from sending any stream
+    /// data.
+    ///
     /// The default value is `0`.
     pub fn set_initial_max_stream_data_uni(&mut self, v: u64) {
         self.local_transport_params.initial_max_stream_data_uni = v;
@@ -824,6 +1045,9 @@
     /// given time and will increase the limit automatically as streams are
     /// completed.
     ///
+    /// When set to zero, either explicitly or via the default, quiche will not
+    /// not allow the peer to open any bidirectional streams.
+    ///
     /// A bidirectional stream is considered completed when all incoming data
     /// has been read by the application (up to the `fin` offset) or the
     /// stream's read direction has been shutdown, and all outgoing data has
@@ -842,6 +1066,9 @@
     /// given time and will increase the limit automatically as streams are
     /// completed.
     ///
+    /// When set to zero, either explicitly or via the default, quiche will not
+    /// not allow the peer to open any unidirectional streams.
+    ///
     /// A unidirectional stream is considered completed when all incoming data
     /// has been read by the application (up to the `fin` offset) or the
     /// stream's read direction has been shutdown.
@@ -865,6 +1092,15 @@
         self.local_transport_params.max_ack_delay = v;
     }
 
+    /// Sets the `active_connection_id_limit` transport parameter.
+    ///
+    /// The default value is `2`. Lower values will be ignored.
+    pub fn set_active_connection_id_limit(&mut self, v: u64) {
+        if v >= 2 {
+            self.local_transport_params.active_conn_id_limit = v;
+        }
+    }
+
     /// Sets the `disable_active_migration` transport parameter.
     ///
     /// The default value is `false`.
@@ -904,6 +1140,13 @@
         self.hystart = v;
     }
 
+    /// Configures whether to enable pacing.
+    ///
+    /// The default value is `true`.
+    pub fn enable_pacing(&mut self, v: bool) {
+        self.pacing = v;
+    }
+
     /// Configures whether to enable receiving DATAGRAM frames.
     ///
     /// When enabled, the `max_datagram_frame_size` transport parameter is set
@@ -921,6 +1164,44 @@
         self.dgram_recv_max_queue_len = recv_queue_len;
         self.dgram_send_max_queue_len = send_queue_len;
     }
+
+    /// Sets the maximum size of the connection window.
+    ///
+    /// The default value is MAX_CONNECTION_WINDOW (24MBytes).
+    pub fn set_max_connection_window(&mut self, v: u64) {
+        self.max_connection_window = v;
+    }
+
+    /// Sets the maximum size of the stream window.
+    ///
+    /// The default value is MAX_STREAM_WINDOW (16MBytes).
+    pub fn set_max_stream_window(&mut self, v: u64) {
+        self.max_stream_window = v;
+    }
+
+    /// Sets the initial stateless reset token.
+    ///
+    /// This value is only advertised by servers. Setting a stateless retry
+    /// token as a client has no effect on the connection.
+    ///
+    /// The default value is `None`.
+    pub fn set_stateless_reset_token(&mut self, v: Option<u128>) {
+        self.local_transport_params.stateless_reset_token = v;
+    }
+
+    /// Sets whether the QUIC connection should avoid reusing DCIDs over
+    /// different paths.
+    ///
+    /// When set to `true`, it ensures that a destination Connection ID is never
+    /// reused on different paths. Such behaviour may lead to connection stall
+    /// if the peer performs a non-voluntary migration (e.g., NAT rebinding) and
+    /// does not provide additional destination Connection IDs to handle such
+    /// event.
+    ///
+    /// The default value is `false`.
+    pub fn set_disable_dcid_reuse(&mut self, v: bool) {
+        self.disable_dcid_reuse = v;
+    }
 }
 
 /// A QUIC connection.
@@ -928,17 +1209,14 @@
     /// QUIC wire version used for the connection.
     version: u32,
 
-    /// Peer's connection ID.
-    dcid: ConnectionId<'static>,
-
-    /// Local connection ID.
-    scid: ConnectionId<'static>,
+    /// Connection Identifiers.
+    ids: cid::ConnectionIdentifiers,
 
     /// Unique opaque ID for the connection that can be used for logging.
     trace_id: String,
 
     /// Packet number spaces.
-    pkt_num_spaces: [packet::PktNumSpace; packet::EPOCH_COUNT],
+    pkt_num_spaces: [packet::PktNumSpace; packet::Epoch::count()],
 
     /// Peer's transport parameters.
     peer_transport_params: TransportParams,
@@ -947,11 +1225,7 @@
     local_transport_params: TransportParams,
 
     /// TLS handshake state.
-    ///
-    /// Due to the requirement for `Connection` to be Send+Sync, and the fact
-    /// that BoringSSL's SSL structure is not thread safe, we need to wrap the
-    /// handshake object in a mutex.
-    handshake: Mutex<tls::Handshake>,
+    handshake: tls::Handshake,
 
     /// Serialized TLS session buffer.
     ///
@@ -959,10 +1233,11 @@
     /// client. On the server this is empty.
     session: Option<Vec<u8>>,
 
-    /// Loss recovery and congestion control state.
-    recovery: recovery::Recovery,
+    /// The configuration for recovery.
+    recovery_config: recovery::RecoveryConfig,
 
-    peer_addr: SocketAddr,
+    /// The path manager.
+    paths: path::PathMap,
 
     /// List of supported application protocols.
     application_protos: Vec<Vec<u8>>,
@@ -973,15 +1248,17 @@
     /// Total number of sent packets.
     sent_count: usize,
 
+    /// Total number of lost packets.
+    lost_count: usize,
+
+    /// Total number of packets sent with data retransmitted.
+    retrans_count: usize,
+
     /// Total number of bytes received from the peer.
     rx_data: u64,
 
-    /// Local flow control limit for the connection.
-    max_rx_data: u64,
-
-    /// Updated local flow control limit for the connection. This is used to
-    /// trigger sending MAX_DATA frames after a certain threshold.
-    max_rx_data_next: u64,
+    /// Receiver flow controller.
+    flow_control: flowcontrol::FlowControl,
 
     /// Whether we send MAX_DATA frame.
     almost_full: bool,
@@ -989,15 +1266,30 @@
     /// Number of stream data bytes that can be buffered.
     tx_cap: usize,
 
+    // Number of bytes buffered in the send buffer.
+    tx_buffered: usize,
+
     /// Total number of bytes sent to the peer.
     tx_data: u64,
 
     /// Peer's flow control limit for the connection.
     max_tx_data: u64,
 
-    /// Total number of bytes the server can send before the peer's address
-    /// is verified.
-    max_send_bytes: usize,
+    /// Last tx_data before running a full send() loop.
+    last_tx_data: u64,
+
+    /// Total number of bytes retransmitted over the connection.
+    /// This counts only STREAM and CRYPTO data.
+    stream_retrans_bytes: u64,
+
+    /// Total number of bytes sent over the connection.
+    sent_bytes: u64,
+
+    /// Total number of bytes received over the connection.
+    recv_bytes: u64,
+
+    /// Total number of bytes sent lost over the connection.
+    lost_bytes: u64,
 
     /// Streams map, indexed by stream ID.
     streams: stream::StreamMap,
@@ -1021,9 +1313,6 @@
     /// frame.
     peer_error: Option<ConnectionError>,
 
-    /// Received path challenge.
-    challenge: Option<Vec<u8>>,
-
     /// The connection-level limit at which send blocking occurred.
     blocked_limit: Option<u64>,
 
@@ -1055,11 +1344,8 @@
     /// Whether the peer already updated its connection ID.
     got_peer_conn_id: bool,
 
-    /// Whether the peer's address has been verified.
-    verified_peer_address: bool,
-
-    /// Whether the peer has verified our address.
-    peer_verified_address: bool,
+    /// Whether the peer verified our initial address.
+    peer_verified_initial_address: bool,
 
     /// Whether the peer's transport parameters were parsed.
     parsed_peer_transport_params: bool,
@@ -1067,12 +1353,18 @@
     /// Whether the connection handshake has been completed.
     handshake_completed: bool,
 
-    /// Whether the HANDSHAKE_DONE has been sent.
+    /// Whether the HANDSHAKE_DONE frame has been sent.
     handshake_done_sent: bool,
 
+    /// Whether the HANDSHAKE_DONE frame has been acked.
+    handshake_done_acked: bool,
+
     /// Whether the connection handshake has been confirmed.
     handshake_confirmed: bool,
 
+    /// Key phase bit used for outgoing protected packets.
+    key_phase: bool,
+
     /// Whether an ack-eliciting packet has been sent since last receiving a
     /// packet.
     ack_eliciting_sent: bool,
@@ -1080,19 +1372,17 @@
     /// Whether the connection is closed.
     closed: bool,
 
+    // Whether the connection was timed out
+    timed_out: bool,
+
     /// Whether to send GREASE.
     grease: bool,
 
     /// TLS keylog writer.
     keylog: Option<Box<dyn std::io::Write + Send + Sync>>,
 
-    /// Qlog streaming output.
     #[cfg(feature = "qlog")]
-    qlog_streamer: Option<qlog::QlogStreamer>,
-
-    /// Whether peer transport parameters were qlogged.
-    #[cfg(feature = "qlog")]
-    qlogged_peer_params: bool,
+    qlog: QlogInfo,
 
     /// DATAGRAM queues.
     dgram_recv_queue: dgram::DatagramQueue,
@@ -1100,6 +1390,10 @@
 
     /// Whether to emit DATAGRAM frames in the next packet.
     emit_dgram: bool,
+
+    /// Whether the connection should prevent from reusing destination
+    /// Connection IDs when the peer migrates.
+    disable_dcid_reuse: bool,
 }
 
 /// Creates a new server-side connection.
@@ -1116,16 +1410,17 @@
 /// ```no_run
 /// # let mut config = quiche::Config::new(0xbabababa)?;
 /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-/// # let from = "127.0.0.1:1234".parse().unwrap();
-/// let conn = quiche::accept(&scid, None, from, &mut config)?;
+/// # let local = "127.0.0.1:0".parse().unwrap();
+/// # let peer = "127.0.0.1:1234".parse().unwrap();
+/// let conn = quiche::accept(&scid, None, local, peer, &mut config)?;
 /// # Ok::<(), quiche::Error>(())
 /// ```
 #[inline]
 pub fn accept(
-    scid: &ConnectionId, odcid: Option<&ConnectionId>, from: SocketAddr,
-    config: &mut Config,
-) -> Result<Pin<Box<Connection>>> {
-    let conn = Connection::new(scid, odcid, from, config, true)?;
+    scid: &ConnectionId, odcid: Option<&ConnectionId>, local: SocketAddr,
+    peer: SocketAddr, config: &mut Config,
+) -> Result<Connection> {
+    let conn = Connection::new(scid, odcid, local, peer, config, true)?;
 
     Ok(conn)
 }
@@ -1142,19 +1437,21 @@
 /// # let mut config = quiche::Config::new(0xbabababa)?;
 /// # let server_name = "quic.tech";
 /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-/// # let to = "127.0.0.1:1234".parse().unwrap();
-/// let conn = quiche::connect(Some(&server_name), &scid, to, &mut config)?;
+/// # let local = "127.0.0.1:4321".parse().unwrap();
+/// # let peer = "127.0.0.1:1234".parse().unwrap();
+/// let conn =
+///     quiche::connect(Some(&server_name), &scid, local, peer, &mut config)?;
 /// # Ok::<(), quiche::Error>(())
 /// ```
 #[inline]
 pub fn connect(
-    server_name: Option<&str>, scid: &ConnectionId, to: SocketAddr,
-    config: &mut Config,
-) -> Result<Pin<Box<Connection>>> {
-    let conn = Connection::new(scid, None, to, config, false)?;
+    server_name: Option<&str>, scid: &ConnectionId, local: SocketAddr,
+    peer: SocketAddr, config: &mut Config,
+) -> Result<Connection> {
+    let mut conn = Connection::new(scid, None, local, peer, config, false)?;
 
     if let Some(server_name) = server_name {
-        conn.handshake.lock().unwrap().set_host_name(server_name)?;
+        conn.handshake.set_host_name(server_name)?;
     }
 
     Ok(conn)
@@ -1213,13 +1510,14 @@
 /// # let mut out = [0; 512];
 /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
 /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+/// # let local = socket.local_addr().unwrap();
 /// # fn mint_token(hdr: &quiche::Header, src: &std::net::SocketAddr) -> Vec<u8> {
 /// #     vec![]
 /// # }
 /// # fn validate_token<'a>(src: &std::net::SocketAddr, token: &'a [u8]) -> Option<quiche::ConnectionId<'a>> {
 /// #     None
 /// # }
-/// let (len, src) = socket.recv_from(&mut buf).unwrap();
+/// let (len, peer) = socket.recv_from(&mut buf).unwrap();
 ///
 /// let hdr = quiche::Header::from_slice(&mut buf[..len], quiche::MAX_CONN_ID_LEN)?;
 ///
@@ -1227,25 +1525,25 @@
 ///
 /// // No token sent by client, create a new one.
 /// if token.is_empty() {
-///     let new_token = mint_token(&hdr, &src);
+///     let new_token = mint_token(&hdr, &peer);
 ///
 ///     let len = quiche::retry(
 ///         &hdr.scid, &hdr.dcid, &scid, &new_token, hdr.version, &mut out,
 ///     )?;
 ///
-///     socket.send_to(&out[..len], &src).unwrap();
+///     socket.send_to(&out[..len], &peer).unwrap();
 ///     return Ok(());
 /// }
 ///
 /// // Client sent token, validate it.
-/// let odcid = validate_token(&src, token);
+/// let odcid = validate_token(&peer, token);
 ///
 /// if odcid.is_none() {
 ///     // Invalid address validation token.
 ///     return Ok(());
 /// }
 ///
-/// let conn = quiche::accept(&scid, odcid.as_ref(), src, &mut config)?;
+/// let conn = quiche::accept(&scid, odcid.as_ref(), local, peer, &mut config)?;
 /// # Ok::<(), quiche::Error>(())
 /// ```
 #[inline]
@@ -1289,44 +1587,128 @@
     }};
 }
 
-/// Conditional qlog action.
+/// Conditional qlog actions.
 ///
 /// Executes the provided body if the qlog feature is enabled and quiche
-/// has been condifigured with a log writer.
+/// has been configured with a log writer.
 macro_rules! qlog_with {
-    ($qlog_streamer:expr, $qlog_streamer_ref:ident, $body:block) => {{
+    ($qlog:expr, $qlog_streamer_ref:ident, $body:block) => {{
         #[cfg(feature = "qlog")]
         {
-            if let Some($qlog_streamer_ref) = &mut $qlog_streamer {
+            if let Some($qlog_streamer_ref) = &mut $qlog.streamer {
                 $body
             }
         }
     }};
 }
 
+/// Executes the provided body if the qlog feature is enabled, quiche has been
+/// configured with a log writer, the event's importance is within the
+/// configured level.
+macro_rules! qlog_with_type {
+    ($ty:expr, $qlog:expr, $qlog_streamer_ref:ident, $body:block) => {{
+        #[cfg(feature = "qlog")]
+        {
+            if EventImportance::from($ty).is_contained_in(&$qlog.level) {
+                if let Some($qlog_streamer_ref) = &mut $qlog.streamer {
+                    $body
+                }
+            }
+        }
+    }};
+}
+
+#[cfg(feature = "qlog")]
+const QLOG_PARAMS_SET: EventType =
+    EventType::TransportEventType(TransportEventType::ParametersSet);
+
+#[cfg(feature = "qlog")]
+const QLOG_PACKET_RX: EventType =
+    EventType::TransportEventType(TransportEventType::PacketReceived);
+
+#[cfg(feature = "qlog")]
+const QLOG_PACKET_TX: EventType =
+    EventType::TransportEventType(TransportEventType::PacketSent);
+
+#[cfg(feature = "qlog")]
+const QLOG_DATA_MV: EventType =
+    EventType::TransportEventType(TransportEventType::DataMoved);
+
+#[cfg(feature = "qlog")]
+const QLOG_METRICS: EventType =
+    EventType::RecoveryEventType(RecoveryEventType::MetricsUpdated);
+
+#[cfg(feature = "qlog")]
+struct QlogInfo {
+    streamer: Option<qlog::streamer::QlogStreamer>,
+    logged_peer_params: bool,
+    level: EventImportance,
+}
+
+#[cfg(feature = "qlog")]
+impl Default for QlogInfo {
+    fn default() -> Self {
+        QlogInfo {
+            streamer: None,
+            logged_peer_params: false,
+            level: EventImportance::Base,
+        }
+    }
+}
+
 impl Connection {
     fn new(
-        scid: &ConnectionId, odcid: Option<&ConnectionId>, peer: SocketAddr,
-        config: &mut Config, is_server: bool,
-    ) -> Result<Pin<Box<Connection>>> {
-        let tls = config.tls_ctx.lock().unwrap().new_handshake()?;
-        Connection::with_tls(scid, odcid, peer, config, tls, is_server)
+        scid: &ConnectionId, odcid: Option<&ConnectionId>, local: SocketAddr,
+        peer: SocketAddr, config: &mut Config, is_server: bool,
+    ) -> Result<Connection> {
+        let tls = config.tls_ctx.new_handshake()?;
+        Connection::with_tls(scid, odcid, local, peer, config, tls, is_server)
     }
 
     fn with_tls(
-        scid: &ConnectionId, odcid: Option<&ConnectionId>, peer: SocketAddr,
-        config: &mut Config, tls: tls::Handshake, is_server: bool,
-    ) -> Result<Pin<Box<Connection>>> {
+        scid: &ConnectionId, odcid: Option<&ConnectionId>, local: SocketAddr,
+        peer: SocketAddr, config: &mut Config, tls: tls::Handshake,
+        is_server: bool,
+    ) -> Result<Connection> {
         let max_rx_data = config.local_transport_params.initial_max_data;
 
         let scid_as_hex: Vec<String> =
-            scid.iter().map(|b| format!("{:02x}", b)).collect();
+            scid.iter().map(|b| format!("{b:02x}")).collect();
 
-        let mut conn = Box::pin(Connection {
+        let reset_token = if is_server {
+            config.local_transport_params.stateless_reset_token
+        } else {
+            None
+        };
+
+        let recovery_config = recovery::RecoveryConfig::from_config(config);
+
+        let mut path = path::Path::new(local, peer, &recovery_config, true);
+        // If we did stateless retry assume the peer's address is verified.
+        path.verified_peer_address = odcid.is_some();
+        // Assume clients validate the server's address implicitly.
+        path.peer_verified_local_address = is_server;
+
+        // Do not allocate more than the number of active CIDs.
+        let paths = path::PathMap::new(
+            path,
+            config.local_transport_params.active_conn_id_limit as usize,
+            is_server,
+        );
+
+        let active_path_id = paths.get_active_path_id()?;
+
+        let ids = cid::ConnectionIdentifiers::new(
+            config.local_transport_params.active_conn_id_limit as usize,
+            scid,
+            active_path_id,
+            reset_token,
+        );
+
+        let mut conn = Connection {
             version: config.version,
 
-            dcid: ConnectionId::default(),
-            scid: scid.to_vec().into(),
+            ids,
 
             trace_id: scid_as_hex.join(""),
 
@@ -1340,34 +1722,46 @@
 
             local_transport_params: config.local_transport_params.clone(),
 
-            handshake: Mutex::new(tls),
+            handshake: tls,
 
             session: None,
 
-            recovery: recovery::Recovery::new(&config),
+            recovery_config,
 
-            peer_addr: peer,
+            paths,
 
             application_protos: config.application_protos.clone(),
 
             recv_count: 0,
             sent_count: 0,
+            lost_count: 0,
+            retrans_count: 0,
+            sent_bytes: 0,
+            recv_bytes: 0,
+            lost_bytes: 0,
 
             rx_data: 0,
-            max_rx_data,
-            max_rx_data_next: max_rx_data,
+            flow_control: flowcontrol::FlowControl::new(
+                max_rx_data,
+                cmp::min(max_rx_data / 2 * 3, DEFAULT_CONNECTION_WINDOW),
+                config.max_connection_window,
+            ),
             almost_full: false,
 
             tx_cap: 0,
 
+            tx_buffered: 0,
+
             tx_data: 0,
             max_tx_data: 0,
+            last_tx_data: 0,
 
-            max_send_bytes: 0,
+            stream_retrans_bytes: 0,
 
             streams: stream::StreamMap::new(
                 config.local_transport_params.initial_max_streams_bidi,
                 config.local_transport_params.initial_max_streams_uni,
+                config.max_stream_window,
             ),
 
             odcid: None,
@@ -1380,8 +1774,6 @@
 
             peer_error: None,
 
-            challenge: None,
-
             blocked_limit: None,
 
             idle_timer: None,
@@ -1402,33 +1794,32 @@
 
             got_peer_conn_id: false,
 
-            // If we did stateless retry assume the peer's address is verified.
-            verified_peer_address: odcid.is_some(),
-
             // Assume clients validate the server's address implicitly.
-            peer_verified_address: is_server,
+            peer_verified_initial_address: is_server,
 
             parsed_peer_transport_params: false,
 
             handshake_completed: false,
 
             handshake_done_sent: false,
+            handshake_done_acked: false,
 
             handshake_confirmed: false,
 
+            key_phase: false,
+
             ack_eliciting_sent: false,
 
             closed: false,
 
+            timed_out: false,
+
             grease: config.grease,
 
             keylog: None,
 
             #[cfg(feature = "qlog")]
-            qlog_streamer: None,
-
-            #[cfg(feature = "qlog")]
-            qlogged_peer_params: false,
+            qlog: Default::default(),
 
             dgram_recv_queue: dgram::DatagramQueue::new(
                 config.dgram_recv_max_queue_len,
@@ -1439,26 +1830,26 @@
             ),
 
             emit_dgram: true,
-        });
+
+            disable_dcid_reuse: config.disable_dcid_reuse,
+        };
 
         if let Some(odcid) = odcid {
             conn.local_transport_params
                 .original_destination_connection_id = Some(odcid.to_vec().into());
 
             conn.local_transport_params.retry_source_connection_id =
-                Some(scid.to_vec().into());
+                Some(conn.ids.get_scid(0)?.cid.to_vec().into());
 
             conn.did_retry = true;
         }
 
         conn.local_transport_params.initial_source_connection_id =
-            Some(scid.to_vec().into());
+            Some(conn.ids.get_scid(0)?.cid.to_vec().into());
 
-        conn.handshake.lock().unwrap().init(&conn)?;
+        conn.handshake.init(is_server)?;
 
         conn.handshake
-            .lock()
-            .unwrap()
             .use_legacy_codepoint(config.version != PROTOCOL_VERSION_V1);
 
         conn.encode_transport_params()?;
@@ -1475,16 +1866,23 @@
                 conn.is_server,
             )?;
 
-            conn.dcid = dcid.to_vec().into();
+            let reset_token = conn.peer_transport_params.stateless_reset_token;
+            conn.set_initial_dcid(
+                dcid.to_vec().into(),
+                reset_token,
+                active_path_id,
+            )?;
 
-            conn.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open =
+            conn.pkt_num_spaces[packet::Epoch::Initial].crypto_open =
                 Some(aead_open);
-            conn.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_seal =
+            conn.pkt_num_spaces[packet::Epoch::Initial].crypto_seal =
                 Some(aead_seal);
 
             conn.derived_initial_secrets = true;
         }
 
+        conn.paths.get_mut(active_path_id)?.recovery.on_init();
+
         Ok(conn)
     }
 
@@ -1501,22 +1899,54 @@
 
     /// Sets qlog output to the designated [`Writer`].
     ///
+    /// Only events included in `QlogLevel::Base` are written. The serialization
+    /// format is JSON-SEQ.
+    ///
     /// This needs to be called as soon as the connection is created, to avoid
     /// missing some early logs.
     ///
     /// [`Writer`]: https://doc.rust-lang.org/std/io/trait.Write.html
     #[cfg(feature = "qlog")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "qlog")))]
     pub fn set_qlog(
         &mut self, writer: Box<dyn std::io::Write + Send + Sync>, title: String,
         description: String,
     ) {
+        self.set_qlog_with_level(writer, title, description, QlogLevel::Base)
+    }
+
+    /// Sets qlog output to the designated [`Writer`].
+    ///
+    /// Only qlog events included in the specified `QlogLevel` are written. The
+    /// serialization format is JSON-SEQ.
+    ///
+    /// This needs to be called as soon as the connection is created, to avoid
+    /// missing some early logs.
+    ///
+    /// [`Writer`]: https://doc.rust-lang.org/std/io/trait.Write.html
+    #[cfg(feature = "qlog")]
+    #[cfg_attr(docsrs, doc(cfg(feature = "qlog")))]
+    pub fn set_qlog_with_level(
+        &mut self, writer: Box<dyn std::io::Write + Send + Sync>, title: String,
+        description: String, qlog_level: QlogLevel,
+    ) {
         let vp = if self.is_server {
             qlog::VantagePointType::Server
         } else {
             qlog::VantagePointType::Client
         };
 
-        let trace = qlog::Trace::new(
+        let level = match qlog_level {
+            QlogLevel::Core => EventImportance::Core,
+
+            QlogLevel::Base => EventImportance::Base,
+
+            QlogLevel::Extra => EventImportance::Extra,
+        };
+
+        self.qlog.level = level;
+
+        let trace = qlog::TraceSeq::new(
             qlog::VantagePoint {
                 name: None,
                 ty: vp,
@@ -1525,37 +1955,33 @@
             Some(title.to_string()),
             Some(description.to_string()),
             Some(qlog::Configuration {
-                time_offset: Some("0".to_string()),
-                time_units: Some(qlog::TimeUnits::Ms),
+                time_offset: Some(0.0),
                 original_uris: None,
             }),
             None,
         );
 
-        let mut streamer = qlog::QlogStreamer::new(
+        let mut streamer = qlog::streamer::QlogStreamer::new(
             qlog::QLOG_VERSION.to_string(),
             Some(title),
             Some(description),
             None,
-            std::time::Instant::now(),
+            time::Instant::now(),
             trace,
+            self.qlog.level.clone(),
             writer,
         );
 
         streamer.start_log().ok();
 
-        let handshake = self.handshake.lock().unwrap();
+        let ev_data = self
+            .local_transport_params
+            .to_qlog(TransportOwner::Local, self.handshake.cipher());
 
-        let ev = self.local_transport_params.to_qlog(
-            qlog::TransportOwner::Local,
-            self.version,
-            handshake.alpn_protocol(),
-            handshake.cipher(),
-        );
+        // This event occurs very early, so just mark the relative time as 0.0.
+        streamer.add_event(Event::with_time(0.0, ev_data)).ok();
 
-        streamer.add_event(ev).ok();
-
-        self.qlog_streamer = Some(streamer);
+        self.qlog.streamer = Some(streamer);
     }
 
     /// Configures the given session for resumption.
@@ -1574,10 +2000,7 @@
         let session_len = b.get_u64()? as usize;
         let session_bytes = b.get_bytes(session_len)?;
 
-        self.handshake
-            .lock()
-            .unwrap()
-            .set_session(session_bytes.as_ref())?;
+        self.handshake.set_session(session_bytes.as_ref())?;
 
         let raw_params_len = b.get_u64()? as usize;
         let raw_params_bytes = b.get_bytes(raw_params_len)?;
@@ -1585,7 +2008,7 @@
         let peer_params =
             TransportParams::decode(raw_params_bytes.as_ref(), self.is_server)?;
 
-        self.process_peer_transport_params(peer_params);
+        self.process_peer_transport_params(peer_params)?;
 
         Ok(())
     }
@@ -1610,12 +2033,16 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = socket.local_addr().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// loop {
     ///     let (read, from) = socket.recv_from(&mut buf).unwrap();
     ///
-    ///     let recv_info = quiche::RecvInfo { from };
+    ///     let recv_info = quiche::RecvInfo {
+    ///         from,
+    ///         to: local,
+    ///     };
     ///
     ///     let read = match conn.recv(&mut buf[..read], recv_info) {
     ///         Ok(v) => v,
@@ -1635,15 +2062,35 @@
             return Err(Error::BufferTooShort);
         }
 
-        // Keep track of how many bytes we received from the client, so we
-        // can limit bytes sent back before address validation, to a multiple
-        // of this. The limit needs to be increased early on, so that if there
-        // is an error there is enough credit to send a CONNECTION_CLOSE.
-        //
-        // It doesn't matter if the packets received were valid or not, we only
-        // need to track the total amount of bytes received.
-        if !self.verified_peer_address {
-            self.max_send_bytes += len * MAX_AMPLIFICATION_FACTOR;
+        let recv_pid = self.paths.path_id_from_addrs(&(info.to, info.from));
+
+        if let Some(recv_pid) = recv_pid {
+            let recv_path = self.paths.get_mut(recv_pid)?;
+
+            // Keep track of how many bytes we received from the client, so we
+            // can limit bytes sent back before address validation, to a
+            // multiple of this. The limit needs to be increased early on, so
+            // that if there is an error there is enough credit to send a
+            // CONNECTION_CLOSE.
+            //
+            // It doesn't matter if the packets received were valid or not, we
+            // only need to track the total amount of bytes received.
+            //
+            // Note that we also need to limit the number of bytes we sent on a
+            // path if we are not the host that initiated its usage.
+            if self.is_server && !recv_path.verified_peer_address {
+                recv_path.max_send_bytes += len * MAX_AMPLIFICATION_FACTOR;
+            }
+        } else if !self.is_server {
+            // If a client receives packets from an unknown server address,
+            // the client MUST discard these packets.
+            trace!(
+                "{} client received packet from unknown address {:?}, dropping",
+                self.trace_id,
+                info,
+            );
+
+            return Ok(len);
         }
 
         let mut done = 0;
@@ -1651,10 +2098,24 @@
 
         // Process coalesced packets.
         while left > 0 {
-            let read = match self.recv_single(&mut buf[len - left..len], &info) {
+            let read = match self.recv_single(
+                &mut buf[len - left..len],
+                &info,
+                recv_pid,
+            ) {
                 Ok(v) => v,
 
-                Err(Error::Done) => left,
+                Err(Error::Done) => {
+                    // If the packet can't be processed or decrypted, check if
+                    // it's a stateless reset.
+                    if self.is_stateless_reset(&buf[len - left..len]) {
+                        trace!("{} packet is a stateless reset", self.trace_id);
+
+                        self.closed = true;
+                    }
+
+                    left
+                },
 
                 Err(e) => {
                     // In case of error processing the incoming packet, close
@@ -1668,9 +2129,18 @@
             left -= read;
         }
 
+        // Even though the packet was previously "accepted", it
+        // should be safe to forward the error, as it also comes
+        // from the `recv()` method.
+        self.process_undecrypted_0rtt_packets()?;
+
+        Ok(done)
+    }
+
+    fn process_undecrypted_0rtt_packets(&mut self) -> Result<()> {
         // Process previously undecryptable 0-RTT packets if the decryption key
         // is now available.
-        if self.pkt_num_spaces[packet::EPOCH_APPLICATION]
+        if self.pkt_num_spaces[packet::Epoch::Application]
             .crypto_0rtt_open
             .is_some()
         {
@@ -1679,15 +2149,35 @@
                 if let Err(e) = self.recv(&mut pkt, info) {
                     self.undecryptable_pkts.clear();
 
-                    // Even though the packet was previously "accepted", it
-                    // should be safe to forward the error, as it also comes
-                    // from the `recv()` method.
                     return Err(e);
                 }
             }
         }
+        Ok(())
+    }
 
-        Ok(done)
+    /// Returns true if a QUIC packet is a stateless reset.
+    fn is_stateless_reset(&self, buf: &[u8]) -> bool {
+        // If the packet is too small, then we just throw it away.
+        let buf_len = buf.len();
+        if buf_len < 21 {
+            return false;
+        }
+
+        // TODO: we should iterate over all active destination connection IDs
+        // and check against their reset token.
+        match self.peer_transport_params.stateless_reset_token {
+            Some(token) => {
+                let token_len = 16;
+                ring::constant_time::verify_slices_are_equal(
+                    &token.to_be_bytes(),
+                    &buf[buf_len - token_len..buf_len],
+                )
+                .is_ok()
+            },
+
+            None => false,
+        }
     }
 
     /// Processes a single QUIC packet received from the peer.
@@ -1696,10 +2186,17 @@
     /// returned. When the [`Done`] error is returned, processing of the
     /// remainder of the incoming UDP datagram should be interrupted.
     ///
+    /// Note that a server might observe a new 4-tuple, preventing to
+    /// know in advance to which path the incoming packet belongs to (`recv_pid`
+    /// is `None`). As a client, packets from unknown 4-tuple are dropped
+    /// beforehand (see `recv()`).
+    ///
     /// On error, an error other than [`Done`] is returned.
     ///
     /// [`Done`]: enum.Error.html#variant.Done
-    fn recv_single(&mut self, buf: &mut [u8], info: &RecvInfo) -> Result<usize> {
+    fn recv_single(
+        &mut self, buf: &mut [u8], info: &RecvInfo, recv_pid: Option<usize>,
+    ) -> Result<usize> {
         let now = time::Instant::now();
 
         if buf.is_empty() {
@@ -1716,10 +2213,12 @@
             return Err(Error::Done);
         }
 
+        let buf_len = buf.len();
+
         let mut b = octets::OctetsMut::with_slice(buf);
 
-        let mut hdr =
-            Header::from_bytes(&mut b, self.scid.len()).map_err(|e| {
+        let mut hdr = Header::from_bytes(&mut b, self.source_id().len())
+            .map_err(|e| {
                 drop_pkt_on_err(
                     e,
                     self.recv_count,
@@ -1745,11 +2244,11 @@
                 return Err(Error::Done);
             }
 
-            if hdr.dcid != self.scid {
+            if hdr.dcid != self.source_id() {
                 return Err(Error::Done);
             }
 
-            if hdr.scid != self.dcid {
+            if hdr.scid != self.destination_id() {
                 return Err(Error::Done);
             }
 
@@ -1795,24 +2294,22 @@
 
             // Derive Initial secrets based on the new version.
             let (aead_open, aead_seal) = crypto::derive_initial_key_material(
-                &self.dcid,
+                &self.destination_id(),
                 self.version,
                 self.is_server,
             )?;
 
             // Reset connection state to force sending another Initial packet.
-            self.drop_epoch_state(packet::EPOCH_INITIAL, now);
+            self.drop_epoch_state(packet::Epoch::Initial, now);
             self.got_peer_conn_id = false;
-            self.handshake.lock().unwrap().clear()?;
+            self.handshake.clear()?;
 
-            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open =
+            self.pkt_num_spaces[packet::Epoch::Initial].crypto_open =
                 Some(aead_open);
-            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_seal =
+            self.pkt_num_spaces[packet::Epoch::Initial].crypto_seal =
                 Some(aead_seal);
 
             self.handshake
-                .lock()
-                .unwrap()
                 .use_legacy_codepoint(self.version != PROTOCOL_VERSION_V1);
 
             // Encode transport parameters again, as the new version might be
@@ -1834,8 +2331,12 @@
             }
 
             // Check if Retry packet is valid.
-            if packet::verify_retry_integrity(&b, &self.dcid, self.version)
-                .is_err()
+            if packet::verify_retry_integrity(
+                &b,
+                &self.destination_id(),
+                self.version,
+            )
+            .is_err()
             {
                 return Err(Error::Done);
             }
@@ -1846,11 +2347,15 @@
             self.did_retry = true;
 
             // Remember peer's new connection ID.
-            self.odcid = Some(self.dcid.clone());
+            self.odcid = Some(self.destination_id().into_owned());
 
-            self.dcid = hdr.scid.clone();
+            self.set_initial_dcid(
+                hdr.scid.clone(),
+                None,
+                self.paths.get_active_path_id()?,
+            )?;
 
-            self.rscid = Some(self.dcid.clone());
+            self.rscid = Some(self.destination_id().into_owned());
 
             // Derive Initial secrets using the new connection ID.
             let (aead_open, aead_seal) = crypto::derive_initial_key_material(
@@ -1860,13 +2365,13 @@
             )?;
 
             // Reset connection state to force sending another Initial packet.
-            self.drop_epoch_state(packet::EPOCH_INITIAL, now);
+            self.drop_epoch_state(packet::Epoch::Initial, now);
             self.got_peer_conn_id = false;
-            self.handshake.lock().unwrap().clear()?;
+            self.handshake.clear()?;
 
-            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open =
+            self.pkt_num_spaces[packet::Epoch::Initial].crypto_open =
                 Some(aead_open);
-            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_seal =
+            self.pkt_num_spaces[packet::Epoch::Initial].crypto_seal =
                 Some(aead_seal);
 
             return Err(Error::Done);
@@ -1881,8 +2386,6 @@
             self.did_version_negotiation = true;
 
             self.handshake
-                .lock()
-                .unwrap()
                 .use_legacy_codepoint(self.version != PROTOCOL_VERSION_V1);
 
             // Encode transport parameters again, as the new version might be
@@ -1930,9 +2433,9 @@
                 self.is_server,
             )?;
 
-            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_open =
+            self.pkt_num_spaces[packet::Epoch::Initial].crypto_open =
                 Some(aead_open);
-            self.pkt_num_spaces[packet::EPOCH_INITIAL].crypto_seal =
+            self.pkt_num_spaces[packet::Epoch::Initial].crypto_seal =
                 Some(aead_seal);
 
             self.derived_initial_secrets = true;
@@ -1951,7 +2454,7 @@
         };
 
         // Finally, discard packet if no usable key is available.
-        let aead = match aead {
+        let mut aead = match aead {
             Some(v) => v,
 
             None => {
@@ -1984,7 +2487,7 @@
 
         let aead_tag_len = aead.alg().tag_len();
 
-        packet::decrypt_hdr(&mut b, &mut hdr, &aead).map_err(|e| {
+        packet::decrypt_hdr(&mut b, &mut hdr, aead).map_err(|e| {
             drop_pkt_on_err(e, self.recv_count, self.is_server, &self.trace_id)
         })?;
 
@@ -1997,43 +2500,61 @@
         let pn_len = hdr.pkt_num_len;
 
         trace!(
-            "{} rx pkt {:?} len={} pn={}",
+            "{} rx pkt {:?} len={} pn={} {}",
             self.trace_id,
             hdr,
             payload_len,
-            pn
+            pn,
+            AddrTupleFmt(info.from, info.to)
         );
 
-        qlog_with!(self.qlog_streamer, q, {
-            let packet_size = b.len();
+        #[cfg(feature = "qlog")]
+        let mut qlog_frames = vec![];
 
-            let qlog_pkt_hdr = qlog::PacketHeader::with_type(
-                hdr.ty.to_qlog(),
-                pn,
-                Some(packet_size as u64),
-                Some(payload_len as u64),
-                Some(hdr.version),
-                Some(&hdr.scid),
-                Some(&hdr.dcid),
-            );
+        // Check for key update.
+        let mut aead_next = None;
 
-            q.add_event(qlog::event::Event::packet_received(
-                hdr.ty.to_qlog(),
-                qlog_pkt_hdr,
-                Some(Vec::new()),
-                None,
-                None,
-                None,
-            ))
-            .ok();
-        });
+        if self.handshake_confirmed &&
+            hdr.ty != Type::ZeroRTT &&
+            hdr.key_phase != self.key_phase
+        {
+            // Check if this packet arrived before key update.
+            if let Some(key_update) = self.pkt_num_spaces[epoch]
+                .key_update
+                .as_ref()
+                .and_then(|key_update| {
+                    (pn < key_update.pn_on_update).then_some(key_update)
+                })
+            {
+                aead = &key_update.crypto_open;
+            } else {
+                trace!("{} peer-initiated key update", self.trace_id);
+
+                aead_next = Some((
+                    self.pkt_num_spaces[epoch]
+                        .crypto_open
+                        .as_ref()
+                        .unwrap()
+                        .derive_next_packet_key()?,
+                    self.pkt_num_spaces[epoch]
+                        .crypto_seal
+                        .as_ref()
+                        .unwrap()
+                        .derive_next_packet_key()?,
+                ));
+
+                // `aead_next` is always `Some()` at this point, so the `unwrap()`
+                // will never fail.
+                aead = &aead_next.as_ref().unwrap().0;
+            }
+        }
 
         let mut payload = packet::decrypt_pkt(
             &mut b,
             pn,
             pn_len,
             payload_len,
-            &aead,
+            aead,
         )
         .map_err(|e| {
             drop_pkt_on_err(e, self.recv_count, self.is_server, &self.trace_id)
@@ -2049,20 +2570,97 @@
             return Err(Error::InvalidPacket);
         }
 
+        // Now that we decrypted the packet, let's see if we can map it to an
+        // existing path.
+        let recv_pid = if hdr.ty == packet::Type::Short && self.got_peer_conn_id {
+            let pkt_dcid = ConnectionId::from_ref(&hdr.dcid);
+            self.get_or_create_recv_path_id(recv_pid, &pkt_dcid, buf_len, info)?
+        } else {
+            // During handshake, we are on the initial path.
+            self.paths.get_active_path_id()?
+        };
+
+        // The key update is verified once a packet is successfully decrypted
+        // using the new keys.
+        if let Some((open_next, seal_next)) = aead_next {
+            if !self.pkt_num_spaces[epoch]
+                .key_update
+                .as_ref()
+                .map_or(true, |prev| prev.update_acked)
+            {
+                // Peer has updated keys twice without awaiting confirmation.
+                return Err(Error::KeyUpdate);
+            }
+
+            trace!("{} key update verified", self.trace_id);
+
+            let _ = self.pkt_num_spaces[epoch].crypto_seal.replace(seal_next);
+
+            let open_prev = self.pkt_num_spaces[epoch]
+                .crypto_open
+                .replace(open_next)
+                .unwrap();
+
+            let recv_path = self.paths.get_mut(recv_pid)?;
+
+            self.pkt_num_spaces[epoch].key_update = Some(packet::KeyUpdate {
+                crypto_open: open_prev,
+                pn_on_update: pn,
+                update_acked: false,
+                timer: now + (recv_path.recovery.pto() * 3),
+            });
+
+            self.key_phase = !self.key_phase;
+
+            qlog_with_type!(QLOG_PACKET_RX, self.qlog, q, {
+                let trigger = Some(
+                    qlog::events::security::KeyUpdateOrRetiredTrigger::RemoteUpdate,
+                );
+
+                let ev_data_client =
+                    EventData::KeyUpdated(qlog::events::security::KeyUpdated {
+                        key_type:
+                            qlog::events::security::KeyType::Client1RttSecret,
+                        old: None,
+                        new: String::new(),
+                        generation: None,
+                        trigger: trigger.clone(),
+                    });
+
+                q.add_event_data_with_instant(ev_data_client, now).ok();
+
+                let ev_data_server =
+                    EventData::KeyUpdated(qlog::events::security::KeyUpdated {
+                        key_type:
+                            qlog::events::security::KeyType::Server1RttSecret,
+                        old: None,
+                        new: String::new(),
+                        generation: None,
+                        trigger,
+                    });
+
+                q.add_event_data_with_instant(ev_data_server, now).ok();
+            });
+        }
+
         if !self.is_server && !self.got_peer_conn_id {
             if self.odcid.is_none() {
-                self.odcid = Some(self.dcid.clone());
+                self.odcid = Some(self.destination_id().into_owned());
             }
 
             // Replace the randomly generated destination connection ID with
             // the one supplied by the server.
-            self.dcid = hdr.scid.clone();
+            self.set_initial_dcid(
+                hdr.scid.clone(),
+                self.peer_transport_params.stateless_reset_token,
+                recv_pid,
+            )?;
 
             self.got_peer_conn_id = true;
         }
 
         if self.is_server && !self.got_peer_conn_id {
-            self.dcid = hdr.scid.clone();
+            self.set_initial_dcid(hdr.scid.clone(), None, recv_pid)?;
 
             if !self.did_retry &&
                 (self.version >= PROTOCOL_VERSION_DRAFT28 ||
@@ -2083,124 +2681,215 @@
         // ACK and PADDING.
         let mut ack_elicited = false;
 
+        // Process packet payload. If a frame cannot be processed, store the
+        // error and stop further packet processing.
+        let mut frame_processing_err = None;
+
+        // To know if the peer migrated the connection, we need to keep track
+        // whether this is a non-probing packet.
+        let mut probing = true;
+
         // Process packet payload.
         while payload.cap() > 0 {
             let frame = frame::Frame::from_bytes(&mut payload, hdr.ty)?;
 
-            qlog_with!(self.qlog_streamer, q, {
-                q.add_frame(frame.to_qlog(), false).ok();
+            qlog_with_type!(QLOG_PACKET_RX, self.qlog, _q, {
+                qlog_frames.push(frame.to_qlog());
             });
 
             if frame.ack_eliciting() {
                 ack_elicited = true;
             }
 
-            if let Err(e) = self.process_frame(frame, epoch, now) {
-                qlog_with!(self.qlog_streamer, q, {
-                    // Always conclude frame writing on error.
-                    q.finish_frames().ok();
-                });
+            if !frame.probing() {
+                probing = false;
+            }
 
-                return Err(e);
+            if let Err(e) = self.process_frame(frame, &hdr, recv_pid, epoch, now)
+            {
+                frame_processing_err = Some(e);
+                break;
             }
         }
 
-        qlog_with!(self.qlog_streamer, q, {
-            // Always conclude frame writing.
-            q.finish_frames().ok();
+        qlog_with_type!(QLOG_PACKET_RX, self.qlog, q, {
+            let packet_size = b.len();
+
+            let qlog_pkt_hdr = qlog::events::quic::PacketHeader::with_type(
+                hdr.ty.to_qlog(),
+                pn,
+                Some(hdr.version),
+                Some(&hdr.scid),
+                Some(&hdr.dcid),
+            );
+
+            let qlog_raw_info = RawInfo {
+                length: Some(packet_size as u64),
+                payload_length: Some(payload_len as u64),
+                data: None,
+            };
+
+            let ev_data =
+                EventData::PacketReceived(qlog::events::quic::PacketReceived {
+                    header: qlog_pkt_hdr,
+                    frames: Some(qlog_frames),
+                    is_coalesced: None,
+                    retry_token: None,
+                    stateless_reset_token: None,
+                    supported_versions: None,
+                    raw: Some(qlog_raw_info),
+                    datagram_id: None,
+                    trigger: None,
+                });
+
+            q.add_event_data_with_instant(ev_data, now).ok();
         });
 
-        qlog_with!(self.qlog_streamer, q, {
-            let ev = self.recovery.to_qlog();
-            q.add_event(ev).ok();
+        qlog_with_type!(QLOG_PACKET_RX, self.qlog, q, {
+            let recv_path = self.paths.get_mut(recv_pid)?;
+            if let Some(ev_data) = recv_path.recovery.maybe_qlog() {
+                q.add_event_data_with_instant(ev_data, now).ok();
+            }
         });
 
+        if let Some(e) = frame_processing_err {
+            // Any frame error is terminal, so now just return.
+            return Err(e);
+        }
+
         // Only log the remote transport parameters once the connection is
         // established (i.e. after frames have been fully parsed) and only
         // once per connection.
         if self.is_established() {
-            qlog_with!(self.qlog_streamer, q, {
-                if !self.qlogged_peer_params {
-                    let handshake = self.handshake.lock().unwrap();
+            qlog_with_type!(QLOG_PARAMS_SET, self.qlog, q, {
+                if !self.qlog.logged_peer_params {
+                    let ev_data = self
+                        .peer_transport_params
+                        .to_qlog(TransportOwner::Remote, self.handshake.cipher());
 
-                    let ev = self.peer_transport_params.to_qlog(
-                        qlog::TransportOwner::Remote,
-                        self.version,
-                        handshake.alpn_protocol(),
-                        handshake.cipher(),
-                    );
+                    q.add_event_data_with_instant(ev_data, now).ok();
 
-                    q.add_event(ev).ok();
-
-                    self.qlogged_peer_params = true;
+                    self.qlog.logged_peer_params = true;
                 }
             });
         }
 
-        // Process acked frames.
-        for acked in self.recovery.acked[epoch].drain(..) {
-            match acked {
-                frame::Frame::ACK { ranges, .. } => {
-                    // Stop acknowledging packets less than or equal to the
-                    // largest acknowledged in the sent ACK frame that, in
-                    // turn, got acked.
-                    if let Some(largest_acked) = ranges.last() {
+        // Process acked frames. Note that several packets from several paths
+        // might have been acked by the received packet.
+        for (_, p) in self.paths.iter_mut() {
+            for acked in p.recovery.acked[epoch].drain(..) {
+                match acked {
+                    frame::Frame::ACK { ranges, .. } => {
+                        // Stop acknowledging packets less than or equal to the
+                        // largest acknowledged in the sent ACK frame that, in
+                        // turn, got acked.
+                        if let Some(largest_acked) = ranges.last() {
+                            self.pkt_num_spaces[epoch]
+                                .recv_pkt_need_ack
+                                .remove_until(largest_acked);
+                        }
+                    },
+
+                    frame::Frame::CryptoHeader { offset, length } => {
                         self.pkt_num_spaces[epoch]
-                            .recv_pkt_need_ack
-                            .remove_until(largest_acked);
-                    }
-                },
+                            .crypto_stream
+                            .send
+                            .ack_and_drop(offset, length);
+                    },
 
-                frame::Frame::CryptoHeader { offset, length } => {
-                    self.pkt_num_spaces[epoch]
-                        .crypto_stream
-                        .send
-                        .ack_and_drop(offset, length);
-                },
+                    frame::Frame::StreamHeader {
+                        stream_id,
+                        offset,
+                        length,
+                        ..
+                    } => {
+                        let stream = match self.streams.get_mut(stream_id) {
+                            Some(v) => v,
 
-                frame::Frame::StreamHeader {
-                    stream_id,
-                    offset,
-                    length,
-                    ..
-                } => {
-                    let stream = match self.streams.get_mut(stream_id) {
-                        Some(v) => v,
+                            None => continue,
+                        };
 
-                        None => continue,
-                    };
+                        stream.send.ack_and_drop(offset, length);
 
-                    stream.send.ack_and_drop(offset, length);
+                        self.tx_buffered =
+                            self.tx_buffered.saturating_sub(length);
 
-                    // Only collect the stream if it is complete and not
-                    // readable. If it is readable, it will get collected when
-                    // stream_recv() is used.
-                    if stream.is_complete() && !stream.is_readable() {
-                        let local = stream.local;
-                        self.streams.collect(stream_id, local);
-                    }
-                },
+                        qlog_with_type!(QLOG_DATA_MV, self.qlog, q, {
+                            let ev_data = EventData::DataMoved(
+                                qlog::events::quic::DataMoved {
+                                    stream_id: Some(stream_id),
+                                    offset: Some(offset),
+                                    length: Some(length as u64),
+                                    from: Some(DataRecipient::Transport),
+                                    to: Some(DataRecipient::Dropped),
+                                    raw: None,
+                                },
+                            );
 
-                frame::Frame::ResetStream { stream_id, .. } => {
-                    let stream = match self.streams.get_mut(stream_id) {
-                        Some(v) => v,
+                            q.add_event_data_with_instant(ev_data, now).ok();
+                        });
 
-                        None => continue,
-                    };
+                        // Only collect the stream if it is complete and not
+                        // readable. If it is readable, it will get collected when
+                        // stream_recv() is used.
+                        if stream.is_complete() && !stream.is_readable() {
+                            let local = stream.local;
+                            self.streams.collect(stream_id, local);
+                        }
+                    },
 
-                    // Only collect the stream if it is complete and not
-                    // readable. If it is readable, it will get collected when
-                    // stream_recv() is used.
-                    if stream.is_complete() && !stream.is_readable() {
-                        let local = stream.local;
-                        self.streams.collect(stream_id, local);
-                    }
-                },
+                    frame::Frame::HandshakeDone => {
+                        // Explicitly set this to true, so that if the frame was
+                        // already scheduled for retransmission, it is aborted.
+                        self.handshake_done_sent = true;
 
-                _ => (),
+                        self.handshake_done_acked = true;
+                    },
+
+                    frame::Frame::ResetStream { stream_id, .. } => {
+                        let stream = match self.streams.get_mut(stream_id) {
+                            Some(v) => v,
+
+                            None => continue,
+                        };
+
+                        // Only collect the stream if it is complete and not
+                        // readable. If it is readable, it will get collected when
+                        // stream_recv() is used.
+                        if stream.is_complete() && !stream.is_readable() {
+                            let local = stream.local;
+                            self.streams.collect(stream_id, local);
+                        }
+                    },
+
+                    _ => (),
+                }
             }
         }
 
+        // Now that we processed all the frames, if there is a path that has no
+        // Destination CID, try to allocate one.
+        let no_dcid = self
+            .paths
+            .iter_mut()
+            .filter(|(_, p)| p.active_dcid_seq.is_none());
+
+        for (pid, p) in no_dcid {
+            if self.ids.zero_length_dcid() {
+                p.active_dcid_seq = Some(0);
+                continue;
+            }
+
+            let dcid_seq = match self.ids.lowest_available_dcid_seq() {
+                Some(seq) => seq,
+                None => break,
+            };
+
+            self.ids.link_dcid_to_path_id(dcid_seq, pid)?;
+
+            p.active_dcid_seq = Some(dcid_seq);
+        }
+
         // We only record the time of arrival of the largest packet number
         // that still needs to be acked, to be used for ACK delay calculation.
         if self.pkt_num_spaces[epoch].recv_pkt_need_ack.last() < Some(pn) {
@@ -2217,31 +2906,54 @@
         self.pkt_num_spaces[epoch].largest_rx_pkt_num =
             cmp::max(self.pkt_num_spaces[epoch].largest_rx_pkt_num, pn);
 
+        if !probing {
+            self.pkt_num_spaces[epoch].largest_rx_non_probing_pkt_num = cmp::max(
+                self.pkt_num_spaces[epoch].largest_rx_non_probing_pkt_num,
+                pn,
+            );
+
+            // Did the peer migrated to another path?
+            let active_path_id = self.paths.get_active_path_id()?;
+
+            if self.is_server &&
+                recv_pid != active_path_id &&
+                self.pkt_num_spaces[epoch].largest_rx_non_probing_pkt_num == pn
+            {
+                self.paths
+                    .on_peer_migrated(recv_pid, self.disable_dcid_reuse)?;
+            }
+        }
+
         if let Some(idle_timeout) = self.idle_timeout() {
             self.idle_timer = Some(now + idle_timeout);
         }
 
         // Update send capacity.
-        self.tx_cap = cmp::min(
-            self.recovery.cwnd_available() as u64,
-            self.max_tx_data - self.tx_data,
-        ) as usize;
+        self.update_tx_cap();
 
         self.recv_count += 1;
+        self.paths.get_mut(recv_pid)?.recv_count += 1;
 
         let read = b.off() + aead_tag_len;
 
+        self.recv_bytes += read as u64;
+        self.paths.get_mut(recv_pid)?.recv_bytes += read as u64;
+
         // An Handshake packet has been received from the client and has been
         // successfully processed, so we can drop the initial state and consider
         // the client's address to be verified.
         if self.is_server && hdr.ty == packet::Type::Handshake {
-            self.drop_epoch_state(packet::EPOCH_INITIAL, now);
+            self.drop_epoch_state(packet::Epoch::Initial, now);
 
-            self.verified_peer_address = true;
+            self.paths.get_mut(recv_pid)?.verified_peer_address = true;
         }
 
         self.ack_eliciting_sent = false;
 
+        // Reset pacer and start a new burst when a valid
+        // packet is received.
+        self.paths.get_mut(recv_pid)?.recovery.pacer.reset(now);
+
         Ok(read)
     }
 
@@ -2260,14 +2972,22 @@
     ///  * When the connection timer expires (that is, any time [`on_timeout()`]
     ///    is also called).
     ///
-    ///  * When the application sends data to the peer (for examples, any time
+    ///  * When the application sends data to the peer (for example, any time
     ///    [`stream_send()`] or [`stream_shutdown()`] are called).
     ///
+    ///  * When the application receives data from the peer (for example any
+    ///    time [`stream_recv()`] is called).
+    ///
+    /// Once [`is_draining()`] returns `true`, it is no longer necessary to call
+    /// `send()` and all calls will return [`Done`].
+    ///
     /// [`Done`]: enum.Error.html#variant.Done
     /// [`recv()`]: struct.Connection.html#method.recv
     /// [`on_timeout()`]: struct.Connection.html#method.on_timeout
     /// [`stream_send()`]: struct.Connection.html#method.stream_send
     /// [`stream_shutdown()`]: struct.Connection.html#method.stream_shutdown
+    /// [`stream_recv()`]: struct.Connection.html#method.stream_recv
+    /// [`is_draining()`]: struct.Connection.html#method.is_draining
     ///
     /// ## Examples:
     ///
@@ -2276,8 +2996,9 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = socket.local_addr().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// loop {
     ///     let (write, send_info) = match conn.send(&mut out) {
     ///         Ok(v) => v,
@@ -2298,6 +3019,96 @@
     /// # Ok::<(), quiche::Error>(())
     /// ```
     pub fn send(&mut self, out: &mut [u8]) -> Result<(usize, SendInfo)> {
+        self.send_on_path(out, None, None)
+    }
+
+    /// Writes a single QUIC packet to be sent to the peer from the specified
+    /// local address `from` to the destination address `to`.
+    ///
+    /// The behavior of this method differs depending on the value of the `from`
+    /// and `to` parameters:
+    ///
+    ///  * If both are `Some`, then the method only consider the 4-tuple
+    ///    (`from`, `to`). Application can monitor the 4-tuple availability,
+    ///    either by monitoring [`path_event_next()`] events or by relying on
+    ///    the [`paths_iter()`] method. If the provided 4-tuple does not exist
+    ///    on the connection (anymore), it returns an [`InvalidState`].
+    ///
+    ///  * If `from` is `Some` and `to` is `None`, then the method only
+    ///    considers sending packets on paths having `from` as local address.
+    ///
+    ///  * If `to` is `Some` and `from` is `None`, then the method only
+    ///    considers sending packets on paths having `to` as peer address.
+    ///
+    ///  * If both are `None`, all available paths are considered.
+    ///
+    /// On success the number of bytes written to the output buffer is
+    /// returned, or [`Done`] if there was nothing to write.
+    ///
+    /// The application should call `send_on_path()` multiple times until
+    /// [`Done`] is returned, indicating that there are no more packets to
+    /// send. It is recommended that `send_on_path()` be called in the
+    /// following cases:
+    ///
+    ///  * When the application receives QUIC packets from the peer (that is,
+    ///    any time [`recv()`] is also called).
+    ///
+    ///  * When the connection timer expires (that is, any time [`on_timeout()`]
+    ///    is also called).
+    ///
+    ///  * When the application sends data to the peer (for examples, any time
+    ///    [`stream_send()`] or [`stream_shutdown()`] are called).
+    ///
+    ///  * When the application receives data from the peer (for example any
+    ///    time [`stream_recv()`] is called).
+    ///
+    /// Once [`is_draining()`] returns `true`, it is no longer necessary to call
+    /// `send_on_path()` and all calls will return [`Done`].
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    /// [`InvalidState`]: enum.Error.html#InvalidState
+    /// [`recv()`]: struct.Connection.html#method.recv
+    /// [`on_timeout()`]: struct.Connection.html#method.on_timeout
+    /// [`stream_send()`]: struct.Connection.html#method.stream_send
+    /// [`stream_shutdown()`]: struct.Connection.html#method.stream_shutdown
+    /// [`stream_recv()`]: struct.Connection.html#method.stream_recv
+    /// [`path_event_next()`]: struct.Connection.html#method.path_event_next
+    /// [`paths_iter()`]: struct.Connection.html#method.paths_iter
+    /// [`is_draining()`]: struct.Connection.html#method.is_draining
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut out = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = socket.local_addr().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
+    /// loop {
+    ///     let (write, send_info) = match conn.send_on_path(&mut out, Some(local), Some(peer)) {
+    ///         Ok(v) => v,
+    ///
+    ///         Err(quiche::Error::Done) => {
+    ///             // Done writing.
+    ///             break;
+    ///         },
+    ///
+    ///         Err(e) => {
+    ///             // An error occurred, handle it.
+    ///             break;
+    ///         },
+    ///     };
+    ///
+    ///     socket.send_to(&out[..write], &send_info.to).unwrap();
+    /// }
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    pub fn send_on_path(
+        &mut self, out: &mut [u8], from: Option<SocketAddr>,
+        to: Option<SocketAddr>,
+    ) -> Result<(usize, SendInfo)> {
         if out.is_empty() {
             return Err(Error::BufferTooShort);
         }
@@ -2307,30 +3118,16 @@
         }
 
         if self.local_error.is_none() {
-            self.do_handshake()?;
+            self.do_handshake(time::Instant::now())?;
         }
 
-        // Process previously undecryptable 0-RTT packets if the decryption key
-        // is now available.
-        if self.pkt_num_spaces[packet::EPOCH_APPLICATION]
-            .crypto_0rtt_open
-            .is_some()
-        {
-            while let Some((mut pkt, info)) = self.undecryptable_pkts.pop_front()
-            {
-                if self.recv(&mut pkt, info).is_err() {
-                    self.undecryptable_pkts.clear();
-
-                    // Forwarding the error value here could confuse
-                    // applications, as they may not expect getting a `recv()`
-                    // error when calling `send()`.
-                    //
-                    // We simply fall-through to sending packets, which should
-                    // take care of terminating the connection as needed.
-                    break;
-                }
-            }
-        }
+        // Forwarding the error value here could confuse
+        // applications, as they may not expect getting a `recv()`
+        // error when calling `send()`.
+        //
+        // We simply fall-through to sending packets, which should
+        // take care of terminating the connection as needed.
+        let _ = self.process_undecrypted_0rtt_packets();
 
         // There's no point in trying to send a packet if the Initial secrets
         // have not been derived yet, so return early.
@@ -2346,17 +3143,30 @@
         // maximum UDP payload size limit.
         let mut left = cmp::min(out.len(), self.max_send_udp_payload_size());
 
+        let send_pid = match (from, to) {
+            (Some(f), Some(t)) => self
+                .paths
+                .path_id_from_addrs(&(f, t))
+                .ok_or(Error::InvalidState)?,
+
+            _ => self.get_send_path_id(from, to)?,
+        };
+
+        let send_path = self.paths.get_mut(send_pid)?;
+
         // Limit data sent by the server based on the amount of data received
         // from the client before its address is validated.
-        if !self.verified_peer_address && self.is_server {
-            left = cmp::min(left, self.max_send_bytes);
+        if !send_path.verified_peer_address && self.is_server {
+            left = cmp::min(left, send_path.max_send_bytes);
         }
 
         // Generate coalesced packets.
         while left > 0 {
-            let (ty, written) = match self
-                .send_single(&mut out[done..done + left], has_initial)
-            {
+            let (ty, written) = match self.send_single(
+                &mut out[done..done + left],
+                send_pid,
+                has_initial,
+            ) {
                 Ok(v) => v,
 
                 Err(Error::BufferTooShort) | Err(Error::Done) => break,
@@ -2379,13 +3189,22 @@
             // When sending multiple PTO probes, don't coalesce them together,
             // so they are sent on separate UDP datagrams.
             if let Ok(epoch) = ty.to_epoch() {
-                if self.recovery.loss_probes[epoch] > 0 {
+                if self.paths.get_mut(send_pid)?.recovery.loss_probes[epoch] > 0 {
                     break;
                 }
             }
+
+            // Don't coalesce packets that must go on different paths.
+            if !(from.is_some() && to.is_some()) &&
+                self.get_send_path_id(from, to)? != send_pid
+            {
+                break;
+            }
         }
 
         if done == 0 {
+            self.last_tx_data = self.tx_data;
+
             return Err(Error::Done);
         }
 
@@ -2400,20 +3219,20 @@
             done += pad_len;
         }
 
-        let info = SendInfo {
-            to: self.peer_addr,
+        let send_path = self.paths.get(send_pid)?;
 
-            at: self
-                .recovery
-                .get_packet_send_time()
-                .unwrap_or_else(time::Instant::now),
+        let info = SendInfo {
+            from: send_path.local_addr(),
+            to: send_path.peer_addr(),
+
+            at: send_path.recovery.get_packet_send_time(),
         };
 
         Ok((done, info))
     }
 
     fn send_single(
-        &mut self, out: &mut [u8], has_initial: bool,
+        &mut self, out: &mut [u8], send_pid: usize, has_initial: bool,
     ) -> Result<(packet::Type, usize)> {
         let now = time::Instant::now();
 
@@ -2429,106 +3248,148 @@
 
         let mut b = octets::OctetsMut::with_slice(out);
 
-        let pkt_type = self.write_pkt_type()?;
+        let pkt_type = self.write_pkt_type(send_pid)?;
+
+        let max_dgram_len = self.dgram_max_writable_len();
 
         let epoch = pkt_type.to_epoch()?;
+        let pkt_space = &mut self.pkt_num_spaces[epoch];
 
-        // Process lost frames.
-        for lost in self.recovery.lost[epoch].drain(..) {
-            match lost {
-                frame::Frame::CryptoHeader { offset, length } => {
-                    self.pkt_num_spaces[epoch]
-                        .crypto_stream
-                        .send
-                        .retransmit(offset, length);
-                },
+        // Process lost frames. There might be several paths having lost frames.
+        for (_, p) in self.paths.iter_mut() {
+            for lost in p.recovery.lost[epoch].drain(..) {
+                match lost {
+                    frame::Frame::CryptoHeader { offset, length } => {
+                        pkt_space.crypto_stream.send.retransmit(offset, length);
 
-                frame::Frame::StreamHeader {
-                    stream_id,
-                    offset,
-                    length,
-                    fin,
-                } => {
-                    let stream = match self.streams.get_mut(stream_id) {
-                        Some(v) => v,
+                        self.stream_retrans_bytes += length as u64;
+                        p.stream_retrans_bytes += length as u64;
 
-                        None => continue,
-                    };
-
-                    let was_flushable = stream.is_flushable();
-
-                    let empty_fin = length == 0 && fin;
-
-                    stream.send.retransmit(offset, length);
-
-                    // If the stream is now flushable push it to the flushable
-                    // queue, but only if it wasn't already queued.
-                    //
-                    // Consider the stream flushable also when we are sending a
-                    // zero-length frame that has the fin flag set.
-                    if (stream.is_flushable() || empty_fin) && !was_flushable {
-                        let urgency = stream.urgency;
-                        let incremental = stream.incremental;
-                        self.streams.push_flushable(
-                            stream_id,
-                            urgency,
-                            incremental,
-                        );
-                    }
-                },
-
-                frame::Frame::ACK { .. } => {
-                    self.pkt_num_spaces[epoch].ack_elicited = true;
-                },
-
-                frame::Frame::ResetStream {
-                    stream_id,
-                    error_code,
-                    final_size,
-                } =>
-                    if self.streams.get(stream_id).is_some() {
-                        self.streams
-                            .mark_reset(stream_id, true, error_code, final_size);
+                        self.retrans_count += 1;
+                        p.retrans_count += 1;
                     },
 
-                frame::Frame::HandshakeDone => {
-                    self.handshake_done_sent = false;
-                },
+                    frame::Frame::StreamHeader {
+                        stream_id,
+                        offset,
+                        length,
+                        fin,
+                    } => {
+                        let stream = match self.streams.get_mut(stream_id) {
+                            Some(v) => v,
 
-                frame::Frame::MaxStreamData { stream_id, .. } => {
-                    if self.streams.get(stream_id).is_some() {
-                        self.streams.mark_almost_full(stream_id, true);
-                    }
-                },
+                            None => continue,
+                        };
 
-                frame::Frame::MaxData { .. } => {
-                    self.almost_full = true;
-                },
+                        let was_flushable = stream.is_flushable();
 
-                _ => (),
+                        let empty_fin = length == 0 && fin;
+
+                        stream.send.retransmit(offset, length);
+
+                        // If the stream is now flushable push it to the
+                        // flushable queue, but only if it wasn't already
+                        // queued.
+                        //
+                        // Consider the stream flushable also when we are
+                        // sending a zero-length frame that has the fin flag
+                        // set.
+                        if (stream.is_flushable() || empty_fin) && !was_flushable
+                        {
+                            let urgency = stream.urgency;
+                            let incremental = stream.incremental;
+                            self.streams.push_flushable(
+                                stream_id,
+                                urgency,
+                                incremental,
+                            );
+                        }
+
+                        self.stream_retrans_bytes += length as u64;
+                        p.stream_retrans_bytes += length as u64;
+
+                        self.retrans_count += 1;
+                        p.retrans_count += 1;
+                    },
+
+                    frame::Frame::ACK { .. } => {
+                        pkt_space.ack_elicited = true;
+                    },
+
+                    frame::Frame::ResetStream {
+                        stream_id,
+                        error_code,
+                        final_size,
+                    } =>
+                        if self.streams.get(stream_id).is_some() {
+                            self.streams.mark_reset(
+                                stream_id, true, error_code, final_size,
+                            );
+                        },
+
+                    // Retransmit HANDSHAKE_DONE only if it hasn't been acked at
+                    // least once already.
+                    frame::Frame::HandshakeDone if !self.handshake_done_acked => {
+                        self.handshake_done_sent = false;
+                    },
+
+                    frame::Frame::MaxStreamData { stream_id, .. } => {
+                        if self.streams.get(stream_id).is_some() {
+                            self.streams.mark_almost_full(stream_id, true);
+                        }
+                    },
+
+                    frame::Frame::MaxData { .. } => {
+                        self.almost_full = true;
+                    },
+
+                    frame::Frame::NewConnectionId { seq_num, .. } => {
+                        self.ids.mark_advertise_new_scid_seq(seq_num, true);
+                    },
+
+                    frame::Frame::RetireConnectionId { seq_num } => {
+                        self.ids.mark_retire_dcid_seq(seq_num, true);
+                    },
+
+                    _ => (),
+                }
             }
         }
 
+        let is_app_limited = self.delivery_rate_check_if_app_limited();
+        let n_paths = self.paths.len();
+        let path = self.paths.get_mut(send_pid)?;
+        let flow_control = &mut self.flow_control;
+        let pkt_space = &mut self.pkt_num_spaces[epoch];
+
         let mut left = b.cap();
 
-        // Limit output packet size by congestion window size.
-        left = cmp::min(left, self.recovery.cwnd_available());
-
-        let pn = self.pkt_num_spaces[epoch].next_pkt_num;
+        let pn = pkt_space.next_pkt_num;
         let pn_len = packet::pkt_num_len(pn)?;
 
         // The AEAD overhead at the current encryption level.
-        let crypto_overhead = self.pkt_num_spaces[epoch]
-            .crypto_overhead()
-            .ok_or(Error::Done)?;
+        let crypto_overhead = pkt_space.crypto_overhead().ok_or(Error::Done)?;
+
+        let dcid_seq = path.active_dcid_seq.ok_or(Error::OutOfIdentifiers)?;
+
+        let dcid =
+            ConnectionId::from_ref(self.ids.get_dcid(dcid_seq)?.cid.as_ref());
+
+        let scid = if let Some(scid_seq) = path.active_scid_seq {
+            ConnectionId::from_ref(self.ids.get_scid(scid_seq)?.cid.as_ref())
+        } else if pkt_type == packet::Type::Short {
+            ConnectionId::default()
+        } else {
+            return Err(Error::InvalidState);
+        };
 
         let hdr = Header {
             ty: pkt_type,
 
             version: self.version,
 
-            dcid: ConnectionId::from_ref(&self.dcid),
-            scid: ConnectionId::from_ref(&self.scid),
+            dcid,
+            scid,
 
             pkt_num: 0,
             pkt_num_len: pn_len,
@@ -2543,11 +3404,30 @@
             },
 
             versions: None,
-            key_phase: false,
+            key_phase: self.key_phase,
         };
 
         hdr.to_bytes(&mut b)?;
 
+        let hdr_trace = if log::max_level() == log::LevelFilter::Trace {
+            Some(format!("{hdr:?}"))
+        } else {
+            None
+        };
+
+        let hdr_ty = hdr.ty;
+
+        #[cfg(feature = "qlog")]
+        let qlog_pkt_hdr = self.qlog.streamer.as_ref().map(|_q| {
+            qlog::events::quic::PacketHeader::with_type(
+                hdr.ty.to_qlog(),
+                pn,
+                Some(hdr.version),
+                Some(&hdr.scid),
+                Some(&hdr.dcid),
+            )
+        });
+
         // Calculate the space required for the packet, including the header
         // the payload length, the packet number and the AEAD overhead.
         let mut overhead = b.off() + pn_len + crypto_overhead;
@@ -2569,23 +3449,27 @@
                 // This usually happens when we try to send a new packet but
                 // failed because cwnd is almost full. In such case app_limited
                 // is set to false here to make cwnd grow when ACK is received.
-                self.recovery.update_app_limited(false);
+                path.recovery.update_app_limited(false);
                 return Err(Error::Done);
             },
         }
 
         // Make sure there is enough space for the minimum payload length.
         if left < PAYLOAD_MIN_LEN {
-            self.recovery.update_app_limited(false);
+            path.recovery.update_app_limited(false);
             return Err(Error::Done);
         }
 
-        let mut frames: Vec<frame::Frame> = Vec::new();
+        let mut frames: SmallVec<[frame::Frame; 1]> = SmallVec::new();
 
         let mut ack_eliciting = false;
         let mut in_flight = false;
         let mut has_data = false;
 
+        // Whether or not we should explicitly elicit an ACK via PING frame if we
+        // implicitly elicit one otherwise.
+        let ack_elicit_required = path.recovery.should_elicit_ack(epoch);
+
         let header_offset = b.off();
 
         // Reserve space for payload length in advance. Since we don't yet know
@@ -2600,14 +3484,27 @@
 
         let payload_offset = b.off();
 
+        let cwnd_available =
+            path.recovery.cwnd_available().saturating_sub(overhead);
+
+        let left_before_packing_ack_frame = left;
+
         // Create ACK frame.
-        if self.pkt_num_spaces[epoch].recv_pkt_need_ack.len() > 0 &&
-            (self.pkt_num_spaces[epoch].ack_elicited ||
-                self.recovery.loss_probes[epoch] > 0) &&
-            !is_closing
+        //
+        // When we need to explicitly elicit an ACK via PING later, go ahead and
+        // generate an ACK (if there's anything to ACK) since we're going to
+        // send a packet with PING anyways, even if we haven't received anything
+        // ACK eliciting.
+        if pkt_space.recv_pkt_need_ack.len() > 0 &&
+            (pkt_space.ack_elicited || ack_elicit_required) &&
+            (!is_closing ||
+                (pkt_type == Type::Handshake &&
+                    self.local_error
+                        .as_ref()
+                        .map_or(false, |le| le.is_app))) &&
+            path.active()
         {
-            let ack_delay =
-                self.pkt_num_spaces[epoch].largest_rx_pkt_time.elapsed();
+            let ack_delay = pkt_space.largest_rx_pkt_time.elapsed();
 
             let ack_delay = ack_delay.as_micros() as u64 /
                 2_u64
@@ -2615,17 +3512,90 @@
 
             let frame = frame::Frame::ACK {
                 ack_delay,
-                ranges: self.pkt_num_spaces[epoch].recv_pkt_need_ack.clone(),
+                ranges: pkt_space.recv_pkt_need_ack.clone(),
+                ecn_counts: None, // sending ECN is not supported at this time
             };
 
-            if push_frame_to_pkt!(b, frames, frame, left) {
-                self.pkt_num_spaces[epoch].ack_elicited = false;
+            // When a PING frame needs to be sent, avoid sending the ACK if
+            // there is not enough cwnd available for both (note that PING
+            // frames are always 1 byte, so we just need to check that the
+            // ACK's length is lower than cwnd).
+            if pkt_space.ack_elicited || frame.wire_len() < cwnd_available {
+                // ACK-only packets are not congestion controlled so ACKs must
+                // be bundled considering the buffer capacity only, and not the
+                // available cwnd.
+                if push_frame_to_pkt!(b, frames, frame, left) {
+                    pkt_space.ack_elicited = false;
+                }
+            }
+        }
+
+        // Limit output packet size by congestion window size.
+        left = cmp::min(
+            left,
+            // Bytes consumed by ACK frames.
+            cwnd_available.saturating_sub(left_before_packing_ack_frame - left),
+        );
+
+        let mut challenge_data = None;
+
+        if pkt_type == packet::Type::Short {
+            // Create PATH_RESPONSE frame if needed.
+            // We do not try to ensure that these are really sent.
+            while let Some(challenge) = path.pop_received_challenge() {
+                let frame = frame::Frame::PathResponse { data: challenge };
+
+                if push_frame_to_pkt!(b, frames, frame, left) {
+                    ack_eliciting = true;
+                    in_flight = true;
+                } else {
+                    // If there are other pending PATH_RESPONSE, don't lose them
+                    // now.
+                    break;
+                }
+            }
+
+            // Create PATH_CHALLENGE frame if needed.
+            if path.validation_requested() {
+                // TODO: ensure that data is unique over paths.
+                let data = rand::rand_u64().to_be_bytes();
+
+                let frame = frame::Frame::PathChallenge { data };
+
+                if push_frame_to_pkt!(b, frames, frame, left) {
+                    // Let's notify the path once we know the packet size.
+                    challenge_data = Some(data);
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                }
+            }
+
+            if let Some(key_update) = pkt_space.key_update.as_mut() {
+                key_update.update_acked = true;
             }
         }
 
         if pkt_type == packet::Type::Short && !is_closing {
+            // Create NEW_CONNECTION_ID frames as needed.
+            while let Some(seq_num) = self.ids.next_advertise_new_scid_seq() {
+                let frame = self.ids.get_new_connection_id_frame_for(seq_num)?;
+
+                if push_frame_to_pkt!(b, frames, frame, left) {
+                    self.ids.mark_advertise_new_scid_seq(seq_num, false);
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                } else {
+                    break;
+                }
+            }
+        }
+
+        if pkt_type == packet::Type::Short && !is_closing && path.active() {
             // Create HANDSHAKE_DONE frame.
-            if self.is_established() &&
+            // self.should_send_handshake_done() but without the need to borrow
+            if self.handshake_completed &&
                 !self.handshake_done_sent &&
                 self.is_server
             {
@@ -2692,19 +3662,30 @@
                     },
                 };
 
+                // Autotune the stream window size.
+                stream.recv.autotune_window(now, path.recovery.rtt());
+
                 let frame = frame::Frame::MaxStreamData {
                     stream_id,
                     max: stream.recv.max_data_next(),
                 };
 
                 if push_frame_to_pkt!(b, frames, frame, left) {
-                    stream.recv.update_max_data();
+                    let recv_win = stream.recv.window();
+
+                    stream.recv.update_max_data(now);
 
                     self.streams.mark_almost_full(stream_id, false);
 
                     ack_eliciting = true;
                     in_flight = true;
 
+                    // Make sure the connection window always has some
+                    // room compared to the stream window.
+                    flow_control.ensure_window_lower_bound(
+                        (recv_win as f64 * CONNECTION_WINDOW_FACTOR) as u64,
+                    );
+
                     // Also send MAX_DATA when MAX_STREAM_DATA is sent, to avoid a
                     // potential race condition.
                     self.almost_full = true;
@@ -2712,16 +3693,21 @@
             }
 
             // Create MAX_DATA frame as needed.
-            if self.almost_full && self.max_rx_data < self.max_rx_data_next {
+            if self.almost_full &&
+                flow_control.max_data() < flow_control.max_data_next()
+            {
+                // Autotune the connection window size.
+                flow_control.autotune_window(now, path.recovery.rtt());
+
                 let frame = frame::Frame::MaxData {
-                    max: self.max_rx_data_next,
+                    max: flow_control.max_data_next(),
                 };
 
                 if push_frame_to_pkt!(b, frames, frame, left) {
                     self.almost_full = false;
 
                     // Commits the new max_rx_data limit.
-                    self.max_rx_data = self.max_rx_data_next;
+                    flow_control.update_max_data(now);
 
                     ack_eliciting = true;
                     in_flight = true;
@@ -2785,64 +3771,77 @@
                     in_flight = true;
                 }
             }
+
+            // Create RETIRE_CONNECTION_ID frames as needed.
+            while let Some(seq_num) = self.ids.next_retire_dcid_seq() {
+                // The sequence number specified in a RETIRE_CONNECTION_ID frame
+                // MUST NOT refer to the Destination Connection ID field of the
+                // packet in which the frame is contained.
+                let dcid_seq = path.active_dcid_seq.ok_or(Error::InvalidState)?;
+
+                if seq_num == dcid_seq {
+                    continue;
+                }
+
+                let frame = frame::Frame::RetireConnectionId { seq_num };
+
+                if push_frame_to_pkt!(b, frames, frame, left) {
+                    self.ids.mark_retire_dcid_seq(seq_num, false);
+
+                    ack_eliciting = true;
+                    in_flight = true;
+                } else {
+                    break;
+                }
+            }
         }
 
-        // Create CONNECTION_CLOSE frame.
-        if let Some(conn_err) = self.local_error.as_ref() {
-            if conn_err.is_app {
-                // Create ApplicationClose frame.
-                if pkt_type == packet::Type::Short {
-                    let frame = frame::Frame::ApplicationClose {
+        // Create CONNECTION_CLOSE frame. Try to send this only on the active
+        // path, unless it is the last one available.
+        if path.active() || n_paths == 1 {
+            if let Some(conn_err) = self.local_error.as_ref() {
+                if conn_err.is_app {
+                    // Create ApplicationClose frame.
+                    if pkt_type == packet::Type::Short {
+                        let frame = frame::Frame::ApplicationClose {
+                            error_code: conn_err.error_code,
+                            reason: conn_err.reason.clone(),
+                        };
+
+                        if push_frame_to_pkt!(b, frames, frame, left) {
+                            let pto = path.recovery.pto();
+                            self.draining_timer = Some(now + (pto * 3));
+
+                            ack_eliciting = true;
+                            in_flight = true;
+                        }
+                    }
+                } else {
+                    // Create ConnectionClose frame.
+                    let frame = frame::Frame::ConnectionClose {
                         error_code: conn_err.error_code,
+                        frame_type: 0,
                         reason: conn_err.reason.clone(),
                     };
 
                     if push_frame_to_pkt!(b, frames, frame, left) {
-                        self.draining_timer =
-                            Some(now + (self.recovery.pto() * 3));
+                        let pto = path.recovery.pto();
+                        self.draining_timer = Some(now + (pto * 3));
 
                         ack_eliciting = true;
                         in_flight = true;
                     }
                 }
-            } else {
-                // Create ConnectionClose frame.
-                let frame = frame::Frame::ConnectionClose {
-                    error_code: conn_err.error_code,
-                    frame_type: 0,
-                    reason: conn_err.reason.clone(),
-                };
-
-                if push_frame_to_pkt!(b, frames, frame, left) {
-                    self.draining_timer = Some(now + (self.recovery.pto() * 3));
-
-                    ack_eliciting = true;
-                    in_flight = true;
-                }
-            }
-        }
-
-        // Create PATH_RESPONSE frame.
-        if let Some(ref challenge) = self.challenge {
-            let frame = frame::Frame::PathResponse {
-                data: challenge.clone(),
-            };
-
-            if push_frame_to_pkt!(b, frames, frame, left) {
-                self.challenge = None;
-
-                ack_eliciting = true;
-                in_flight = true;
             }
         }
 
         // Create CRYPTO frame.
-        if self.pkt_num_spaces[epoch].crypto_stream.is_flushable() &&
+        if pkt_space.crypto_stream.is_flushable() &&
             left > frame::MAX_CRYPTO_OVERHEAD &&
-            !is_closing
+            !is_closing &&
+            path.active()
         {
-            let crypto_off =
-                self.pkt_num_spaces[epoch].crypto_stream.send.off_front();
+            let crypto_off = pkt_space.crypto_stream.send.off_front();
 
             // Encode the frame.
             //
@@ -2867,7 +3866,7 @@
                     b.split_at(hdr_off + hdr_len)?;
 
                 // Write stream data into the packet buffer.
-                let (len, _) = self.pkt_num_spaces[epoch]
+                let (len, _) = pkt_space
                     .crypto_stream
                     .send
                     .emit(&mut crypto_payload.as_mut()[..max_len])?;
@@ -2908,7 +3907,7 @@
         // where one type is preferred but its buffer is empty, fall back
         // to the other type in order not to waste this function call.
         let mut dgram_emitted = false;
-        let dgrams_to_emit = self.dgram_max_writable_len().is_some();
+        let dgrams_to_emit = max_dgram_len.is_some();
         let stream_to_emit = self.streams.has_flushable();
 
         let mut do_dgram = self.emit_dgram && dgrams_to_emit;
@@ -2922,15 +3921,60 @@
         if (pkt_type == packet::Type::Short || pkt_type == packet::Type::ZeroRTT) &&
             left > frame::MAX_DGRAM_OVERHEAD &&
             !is_closing &&
+            path.active() &&
             do_dgram
         {
-            if let Some(max_dgram_payload) = self.dgram_max_writable_len() {
+            if let Some(max_dgram_payload) = max_dgram_len {
                 while let Some(len) = self.dgram_send_queue.peek_front_len() {
-                    if (len + frame::MAX_DGRAM_OVERHEAD) <= left {
-                        // Front of the queue fits this packet, send it
+                    let hdr_off = b.off();
+                    let hdr_len = 1 + // frame type
+                        2; // length, always encode as 2-byte varint
+
+                    if (hdr_len + len) <= left {
+                        // Front of the queue fits this packet, send it.
                         match self.dgram_send_queue.pop() {
                             Some(data) => {
-                                let frame = frame::Frame::Datagram { data };
+                                // Encode the frame.
+                                //
+                                // Instead of creating a `frame::Frame` object,
+                                // encode the frame directly into the packet
+                                // buffer.
+                                //
+                                // First we reserve some space in the output
+                                // buffer for writing the frame header (we
+                                // assume the length field is always a 2-byte
+                                // varint as we don't know the value yet).
+                                //
+                                // Then we emit the data from the DATAGRAM's
+                                // buffer.
+                                //
+                                // Finally we go back and encode the frame
+                                // header with the now available information.
+                                let (mut dgram_hdr, mut dgram_payload) =
+                                    b.split_at(hdr_off + hdr_len)?;
+
+                                dgram_payload.as_mut()[..len]
+                                    .copy_from_slice(&data);
+
+                                // Encode the frame's header.
+                                //
+                                // Due to how `OctetsMut::split_at()` works,
+                                // `dgram_hdr` starts from the initial offset
+                                // of `b` (rather than the current offset), so
+                                // it needs to be advanced to the initial frame
+                                // offset.
+                                dgram_hdr.skip(hdr_off)?;
+
+                                frame::encode_dgram_header(
+                                    len as u64,
+                                    &mut dgram_hdr,
+                                )?;
+
+                                // Advance the packet buffer's offset.
+                                b.skip(hdr_len + len)?;
+
+                                let frame =
+                                    frame::Frame::DatagramHeader { length: len };
 
                                 if push_frame_to_pkt!(b, frames, frame, left) {
                                     ack_eliciting = true;
@@ -2955,23 +3999,22 @@
         if (pkt_type == packet::Type::Short || pkt_type == packet::Type::ZeroRTT) &&
             left > frame::MAX_STREAM_OVERHEAD &&
             !is_closing &&
+            path.active() &&
             !dgram_emitted
         {
-            while let Some(stream_id) = self.streams.pop_flushable() {
+            while let Some(stream_id) = self.streams.peek_flushable() {
                 let stream = match self.streams.get_mut(stream_id) {
-                    Some(v) => v,
-
-                    None => continue,
+                    // Avoid sending frames for streams that were already stopped.
+                    //
+                    // This might happen if stream data was buffered but not yet
+                    // flushed on the wire when a STOP_SENDING frame is received.
+                    Some(v) if !v.send.is_stopped() => v,
+                    _ => {
+                        self.streams.remove_flushable();
+                        continue;
+                    },
                 };
 
-                // Avoid sending frames for streams that were already stopped.
-                //
-                // This might happen if stream data was buffered but not yet
-                // flushed on the wire when a STOP_SENDING frame is received.
-                if stream.send.is_stopped() {
-                    continue;
-                }
-
                 let stream_off = stream.send.off_front();
 
                 // Encode the frame.
@@ -2980,9 +4023,8 @@
                 // directly into the packet buffer.
                 //
                 // First we reserve some space in the output buffer for writing
-                // the frame header (we assume the length field is
-                // always a 2-byte varint as we don't know the
-                // value yet).
+                // the frame header (we assume the length field is always a
+                // 2-byte varint as we don't know the value yet).
                 //
                 // Then we emit the data from the stream's send buffer.
                 //
@@ -2996,8 +4038,10 @@
 
                 let max_len = match left.checked_sub(hdr_len) {
                     Some(v) => v,
-
-                    None => continue,
+                    None => {
+                        self.streams.remove_flushable();
+                        continue;
+                    },
                 };
 
                 let (mut stream_hdr, mut stream_payload) =
@@ -3039,19 +4083,9 @@
                     has_data = true;
                 }
 
-                // If the stream is still flushable, push it to the back of the
-                // queue again.
-                if stream.is_flushable() {
-                    let urgency = stream.urgency;
-                    let incremental = stream.incremental;
-                    self.streams.push_flushable(stream_id, urgency, incremental);
-                }
-
-                // When fuzzing, try to coalesce multiple STREAM frames in the
-                // same packet, so it's easier to generate fuzz corpora.
-                if cfg!(feature = "fuzzing") && left > frame::MAX_STREAM_OVERHEAD
-                {
-                    continue;
+                // If the stream is no longer flushable, remove it from the queue
+                if !stream.is_flushable() {
+                    self.streams.remove_flushable();
                 }
 
                 break;
@@ -3061,8 +4095,12 @@
         // Alternate trying to send DATAGRAMs next time.
         self.emit_dgram = !dgram_emitted;
 
-        // Create PING for PTO probe if no other ack-elicitng frame is sent.
-        if self.recovery.loss_probes[epoch] > 0 &&
+        // If no other ack-eliciting frame is sent, include a PING frame
+        // - if PTO probe needed; OR
+        // - if we've sent too many non ack-eliciting packets without having
+        // sent an ACK eliciting one; OR
+        // - the application requested an ack-eliciting frame be sent.
+        if (ack_elicit_required || path.needs_ack_eliciting) &&
             !ack_eliciting &&
             left >= 1 &&
             !is_closing
@@ -3076,23 +4114,30 @@
         }
 
         if ack_eliciting {
-            self.recovery.loss_probes[epoch] =
-                self.recovery.loss_probes[epoch].saturating_sub(1);
+            path.needs_ack_eliciting = false;
+            path.recovery.loss_probes[epoch] =
+                path.recovery.loss_probes[epoch].saturating_sub(1);
         }
 
         if frames.is_empty() {
             // When we reach this point we are not able to write more, so set
             // app_limited to false.
-            self.recovery.update_app_limited(false);
+            path.recovery.update_app_limited(false);
             return Err(Error::Done);
         }
 
         // When coalescing a 1-RTT packet, we can't add padding in the UDP
         // datagram, so use PADDING frames instead.
         //
-        // This is only needed if an Initial packet has already been written to
-        // the UDP datagram, as Initial always requires padding.
-        if has_initial && pkt_type == packet::Type::Short && left >= 1 {
+        // This is only needed if
+        // 1) an Initial packet has already been written to the UDP datagram,
+        // as Initial always requires padding.
+        //
+        // 2) this is a probing packet towards an unvalidated peer address.
+        if (has_initial || !path.validated()) &&
+            pkt_type == packet::Type::Short &&
+            left >= 1
+        {
             let frame = frame::Frame::Padding { len: left };
 
             if push_frame_to_pkt!(b, frames, frame, left) {
@@ -3115,11 +4160,10 @@
         }
 
         let payload_len = b.off() - payload_offset;
-        let payload_len = payload_len + crypto_overhead;
 
         // Fill in payload length.
         if pkt_type != packet::Type::Short {
-            let len = pn_len + payload_len;
+            let len = pn_len + payload_len + crypto_overhead;
 
             let (_, mut payload_with_len) = b.split_at(header_offset)?;
             payload_with_len
@@ -3127,51 +4171,62 @@
         }
 
         trace!(
-            "{} tx pkt {:?} len={} pn={}",
+            "{} tx pkt {} len={} pn={} {}",
             self.trace_id,
-            hdr,
+            hdr_trace.unwrap_or_default(),
             payload_len,
-            pn
+            pn,
+            AddrTupleFmt(path.local_addr(), path.peer_addr())
         );
 
-        qlog_with!(self.qlog_streamer, q, {
-            let qlog_pkt_hdr = qlog::PacketHeader::with_type(
-                hdr.ty.to_qlog(),
-                pn,
-                Some(payload_len as u64 + payload_offset as u64),
-                Some(payload_len as u64),
-                Some(hdr.version),
-                Some(&hdr.scid),
-                Some(&hdr.dcid),
-            );
-
-            let packet_sent_ev = qlog::event::Event::packet_sent_min(
-                hdr.ty.to_qlog(),
-                qlog_pkt_hdr,
-                Some(Vec::new()),
-            );
-
-            q.add_event(packet_sent_ev).ok();
-        });
+        #[cfg(feature = "qlog")]
+        let mut qlog_frames: SmallVec<
+            [qlog::events::quic::QuicFrame; 1],
+        > = SmallVec::with_capacity(frames.len());
 
         for frame in &mut frames {
             trace!("{} tx frm {:?}", self.trace_id, frame);
 
-            qlog_with!(self.qlog_streamer, q, {
-                q.add_frame(frame.to_qlog(), false).ok();
+            qlog_with_type!(QLOG_PACKET_TX, self.qlog, _q, {
+                qlog_frames.push(frame.to_qlog());
             });
-
-            // Once frames have been serialized they are passed to the Recovery
-            // module which manages retransmission. However, some frames do not
-            // contain retransmittable data, so drop it here.
-            frame.shrink_for_retransmission();
         }
 
-        qlog_with!(self.qlog_streamer, q, {
-            q.finish_frames().ok();
+        qlog_with_type!(QLOG_PACKET_TX, self.qlog, q, {
+            if let Some(header) = qlog_pkt_hdr {
+                // Qlog packet raw info described at
+                // https://datatracker.ietf.org/doc/html/draft-ietf-quic-qlog-main-schema-00#section-5.1
+                //
+                // `length` includes packet headers and trailers (AEAD tag).
+                let length = payload_len + payload_offset + crypto_overhead;
+                let qlog_raw_info = RawInfo {
+                    length: Some(length as u64),
+                    payload_length: Some(payload_len as u64),
+                    data: None,
+                };
+
+                let send_at_time =
+                    now.duration_since(q.start_time()).as_secs_f32() * 1000.0;
+
+                let ev_data =
+                    EventData::PacketSent(qlog::events::quic::PacketSent {
+                        header,
+                        frames: Some(qlog_frames),
+                        is_coalesced: None,
+                        retry_token: None,
+                        stateless_reset_token: None,
+                        supported_versions: None,
+                        raw: Some(qlog_raw_info),
+                        datagram_id: None,
+                        send_at_time: Some(send_at_time),
+                        trigger: None,
+                    });
+
+                q.add_event_data_with_instant(ev_data, now).ok();
+            }
         });
 
-        let aead = match self.pkt_num_spaces[epoch].crypto_seal {
+        let aead = match pkt_space.crypto_seal {
             Some(ref v) => v,
             None => return Err(Error::InvalidState),
         };
@@ -3182,6 +4237,7 @@
             pn_len,
             payload_len,
             payload_offset,
+            None,
             aead,
         )?;
 
@@ -3196,39 +4252,59 @@
             in_flight,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data,
         };
 
-        self.recovery.on_packet_sent(
+        if in_flight && is_app_limited {
+            path.recovery.delivery_rate_update_app_limited(true);
+        }
+
+        pkt_space.next_pkt_num += 1;
+
+        let handshake_status = recovery::HandshakeStatus {
+            has_handshake_keys: self.pkt_num_spaces[packet::Epoch::Handshake]
+                .has_keys(),
+            peer_verified_address: self.peer_verified_initial_address,
+            completed: self.handshake_completed,
+        };
+
+        path.recovery.on_packet_sent(
             sent_pkt,
             epoch,
-            self.handshake_status(),
+            handshake_status,
             now,
             &self.trace_id,
         );
 
-        qlog_with!(self.qlog_streamer, q, {
-            let ev = self.recovery.to_qlog();
-            q.add_event(ev).ok();
+        qlog_with_type!(QLOG_METRICS, self.qlog, q, {
+            if let Some(ev_data) = path.recovery.maybe_qlog() {
+                q.add_event_data_with_instant(ev_data, now).ok();
+            }
         });
 
-        self.pkt_num_spaces[epoch].next_pkt_num += 1;
+        // Record sent packet size if we probe the path.
+        if let Some(data) = challenge_data {
+            path.add_challenge_sent(data, written, now);
+        }
 
         self.sent_count += 1;
+        self.sent_bytes += written as u64;
+        path.sent_count += 1;
+        path.sent_bytes += written as u64;
 
-        if self.dgram_send_queue.byte_size() > self.recovery.cwnd_available() {
-            self.recovery.update_app_limited(false);
+        if self.dgram_send_queue.byte_size() > path.recovery.cwnd_available() {
+            path.recovery.update_app_limited(false);
         }
 
+        path.max_send_bytes = path.max_send_bytes.saturating_sub(written);
+
         // On the client, drop initial state after sending an Handshake packet.
-        if !self.is_server && hdr.ty == packet::Type::Handshake {
-            self.drop_epoch_state(packet::EPOCH_INITIAL, now);
+        if !self.is_server && hdr_ty == packet::Type::Handshake {
+            self.drop_epoch_state(packet::Epoch::Initial, now);
         }
 
-        self.max_send_bytes = self.max_send_bytes.saturating_sub(written);
-
         // (Re)start the idle timer if we are sending the first ack-eliciting
         // packet since last receiving a packet.
         if ack_eliciting && !self.ack_eliciting_sent {
@@ -3244,6 +4320,43 @@
         Ok((pkt_type, written))
     }
 
+    /// Returns the size of the send quantum, in bytes.
+    ///
+    /// This represents the maximum size of a packet burst as determined by the
+    /// congestion control algorithm in use.
+    ///
+    /// Applications can, for example, use it in conjunction with segmentation
+    /// offloading mechanisms as the maximum limit for outgoing aggregates of
+    /// multiple packets.
+    #[inline]
+    pub fn send_quantum(&self) -> usize {
+        match self.paths.get_active() {
+            Ok(p) => p.recovery.send_quantum(),
+            _ => 0,
+        }
+    }
+
+    /// Returns the size of the send quantum over the given 4-tuple, in bytes.
+    ///
+    /// This represents the maximum size of a packet burst as determined by the
+    /// congestion control algorithm in use.
+    ///
+    /// Applications can, for example, use it in conjunction with segmentation
+    /// offloading mechanisms as the maximum limit for outgoing aggregates of
+    /// multiple packets.
+    ///
+    /// If the (`local_addr`, peer_addr`) 4-tuple relates to a non-existing
+    /// path, this method returns 0.
+    pub fn send_quantum_on_path(
+        &self, local_addr: SocketAddr, peer_addr: SocketAddr,
+    ) -> usize {
+        self.paths
+            .path_id_from_addrs(&(local_addr, peer_addr))
+            .and_then(|pid| self.paths.get(pid).ok())
+            .map(|path| path.recovery.send_quantum())
+            .unwrap_or(0)
+    }
+
     /// Reads contiguous data from a stream into the provided slice.
     ///
     /// The slice must be sized by the caller and will be populated up to its
@@ -3252,7 +4365,11 @@
     /// On success the amount of bytes read and a flag indicating the fin state
     /// is returned as a tuple, or [`Done`] if there is no data to read.
     ///
+    /// Reading data from a stream may trigger queueing of control messages
+    /// (e.g. MAX_STREAM_DATA). [`send()`] should be called after reading.
+    ///
     /// [`Done`]: enum.Error.html#variant.Done
+    /// [`send()`]: struct.Connection.html#method.send
     ///
     /// ## Examples:
     ///
@@ -3261,8 +4378,9 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = socket.local_addr().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// # let stream_id = 0;
     /// while let Ok((read, fin)) = conn.stream_recv(stream_id, &mut buf) {
     ///     println!("Got {} bytes on stream {}", read, stream_id);
@@ -3288,19 +4406,34 @@
             return Err(Error::Done);
         }
 
+        let local = stream.local;
+
         #[cfg(feature = "qlog")]
         let offset = stream.recv.off_front();
 
-        let (read, fin) = stream.recv.emit(out)?;
+        let (read, fin) = match stream.recv.emit(out) {
+            Ok(v) => v,
 
-        self.max_rx_data_next = self.max_rx_data_next.saturating_add(read as u64);
+            Err(e) => {
+                // Collect the stream if it is now complete. This can happen if
+                // we got a `StreamReset` error which will now be propagated to
+                // the application, so we don't need to keep the stream's state
+                // anymore.
+                if stream.is_complete() {
+                    self.streams.collect(stream_id, local);
+                }
+
+                self.streams.mark_readable(stream_id, false);
+                return Err(e);
+            },
+        };
+
+        self.flow_control.add_consumed(read as u64);
 
         let readable = stream.is_readable();
 
         let complete = stream.is_complete();
 
-        let local = stream.local;
-
         if stream.recv.almost_full() {
             self.streams.mark_almost_full(stream_id, true);
         }
@@ -3313,16 +4446,18 @@
             self.streams.collect(stream_id, local);
         }
 
-        qlog_with!(self.qlog_streamer, q, {
-            let ev = qlog::event::Event::h3_data_moved(
-                stream_id.to_string(),
-                Some(offset.to_string()),
-                Some(read as u64),
-                Some(qlog::H3DataRecipient::Transport),
-                None,
-                None,
-            );
-            q.add_event(ev).ok();
+        qlog_with_type!(QLOG_DATA_MV, self.qlog, q, {
+            let ev_data = EventData::DataMoved(qlog::events::quic::DataMoved {
+                stream_id: Some(stream_id),
+                offset: Some(offset),
+                length: Some(read as u64),
+                from: Some(DataRecipient::Transport),
+                to: Some(DataRecipient::Application),
+                raw: None,
+            });
+
+            let now = time::Instant::now();
+            q.add_event_data_with_instant(ev_data, now).ok();
         });
 
         if self.should_update_max_data() {
@@ -3337,6 +4472,11 @@
     /// On success the number of bytes written is returned, or [`Done`] if no
     /// data was written (e.g. because the stream has no capacity).
     ///
+    /// Applications can provide a 0-length buffer with the fin flag set to
+    /// true. This will lead to a 0-length FIN STREAM frame being sent at the
+    /// latest offset. The `Ok(0)` value is only returned when the application
+    /// provided a 0-length buffer.
+    ///
     /// In addition, if the peer has signalled that it doesn't want to receive
     /// any more data from this stream by sending the `STOP_SENDING` frame, the
     /// [`StreamStopped`] error will be returned instead of any data.
@@ -3367,8 +4507,9 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = "127.0.0.1:4321".parse().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// # let stream_id = 0;
     /// conn.stream_send(stream_id, b"hello", true)?;
     /// # Ok::<(), quiche::Error>(())
@@ -3392,24 +4533,43 @@
             self.blocked_limit = Some(self.max_tx_data);
         }
 
-        // Truncate the input buffer based on the connection's send capacity if
-        // necessary.
         let cap = self.tx_cap;
 
-        let (buf, fin) = if cap < buf.len() {
-            (&buf[..cap], false)
-        } else {
-            (buf, fin)
-        };
-
         // Get existing stream or create a new one.
         let stream = self.get_or_create_stream(stream_id, true)?;
 
         #[cfg(feature = "qlog")]
         let offset = stream.send.off_back();
 
+        let was_writable = stream.is_writable();
+
         let was_flushable = stream.is_flushable();
 
+        // Truncate the input buffer based on the connection's send capacity if
+        // necessary.
+        //
+        // When the cap is zero, the method returns Ok(0) *only* when the passed
+        // buffer is empty. We return Error::Done otherwise.
+        if cap == 0 && !buf.is_empty() {
+            if was_writable {
+                // When `stream_writable_next()` returns a stream, the writable
+                // mark is removed, but because the stream is blocked by the
+                // connection-level send capacity it won't be marked as writable
+                // again once the capacity increases.
+                //
+                // Since the stream is writable already, mark it here instead.
+                self.streams.mark_writable(stream_id, true);
+            }
+
+            return Err(Error::Done);
+        }
+
+        let (buf, fin, blocked_by_cap) = if cap < buf.len() {
+            (&buf[..cap], false, true)
+        } else {
+            (buf, fin, false)
+        };
+
         let sent = match stream.send.write(buf, fin) {
             Ok(v) => v,
 
@@ -3431,8 +4591,12 @@
         if sent < buf.len() {
             let max_off = stream.send.max_off();
 
-            self.streams.mark_blocked(stream_id, true, max_off);
+            if stream.send.blocked_at() != Some(max_off) {
+                stream.send.update_blocked_at(Some(max_off));
+                self.streams.mark_blocked(stream_id, true, max_off);
+            }
         } else {
+            stream.send.update_blocked_at(None);
             self.streams.mark_blocked(stream_id, false, 0);
         }
 
@@ -3447,26 +4611,40 @@
 
         if !writable {
             self.streams.mark_writable(stream_id, false);
+        } else if was_writable && blocked_by_cap {
+            // When `stream_writable_next()` returns a stream, the writable
+            // mark is removed, but because the stream is blocked by the
+            // connection-level send capacity it won't be marked as writable
+            // again once the capacity increases.
+            //
+            // Since the stream is writable already, mark it here instead.
+            self.streams.mark_writable(stream_id, true);
         }
 
         self.tx_cap -= sent;
 
         self.tx_data += sent as u64;
 
-        self.recovery.rate_check_app_limited();
+        self.tx_buffered += sent;
 
-        qlog_with!(self.qlog_streamer, q, {
-            let ev = qlog::event::Event::h3_data_moved(
-                stream_id.to_string(),
-                Some(offset.to_string()),
-                Some(sent as u64),
-                None,
-                Some(qlog::H3DataRecipient::Transport),
-                None,
-            );
-            q.add_event(ev).ok();
+        qlog_with_type!(QLOG_DATA_MV, self.qlog, q, {
+            let ev_data = EventData::DataMoved(qlog::events::quic::DataMoved {
+                stream_id: Some(stream_id),
+                offset: Some(offset),
+                length: Some(sent as u64),
+                from: Some(DataRecipient::Application),
+                to: Some(DataRecipient::Transport),
+                raw: None,
+            });
+
+            let now = time::Instant::now();
+            q.add_event_data_with_instant(ev_data, now).ok();
         });
 
+        if sent == 0 && !buf.is_empty() {
+            return Err(Error::Done);
+        }
+
         Ok(sent)
     }
 
@@ -3513,17 +4691,40 @@
     /// be sent to the peer to signal it to stop sending data.
     ///
     /// When the `direction` argument is set to [`Shutdown::Write`], outstanding
-    /// data in the stream's send buffer is dropped, and no additional data
-    /// is added to it. Data passed to [`stream_send()`] after calling this
-    /// method will be ignored.
+    /// data in the stream's send buffer is dropped, and no additional data is
+    /// added to it. Data passed to [`stream_send()`] after calling this method
+    /// will be ignored. In addition, a `RESET_STREAM` frame will be sent to the
+    /// peer to signal the reset.
+    ///
+    /// Locally-initiated unidirectional streams can only be closed in the
+    /// [`Shutdown::Write`] direction. Remotely-initiated unidirectional streams
+    /// can only be closed in the [`Shutdown::Read`] direction. Using an
+    /// incorrect direction will return [`InvalidStreamState`].
     ///
     /// [`Shutdown::Read`]: enum.Shutdown.html#variant.Read
     /// [`Shutdown::Write`]: enum.Shutdown.html#variant.Write
     /// [`stream_recv()`]: struct.Connection.html#method.stream_recv
     /// [`stream_send()`]: struct.Connection.html#method.stream_send
+    /// [`InvalidStreamState`]: enum.Error.html#variant.InvalidStreamState
     pub fn stream_shutdown(
         &mut self, stream_id: u64, direction: Shutdown, err: u64,
     ) -> Result<()> {
+        // Don't try to stop a local unidirectional stream.
+        if direction == Shutdown::Read &&
+            stream::is_local(stream_id, self.is_server) &&
+            !stream::is_bidi(stream_id)
+        {
+            return Err(Error::InvalidStreamState(stream_id));
+        }
+
+        // Dont' try to reset a remote unidirectional stream.
+        if direction == Shutdown::Write &&
+            !stream::is_local(stream_id, self.is_server) &&
+            !stream::is_bidi(stream_id)
+        {
+            return Err(Error::InvalidStreamState(stream_id));
+        }
+
         // Get existing stream.
         let stream = self.streams.get_mut(stream_id).ok_or(Error::Done)?;
 
@@ -3540,7 +4741,17 @@
             },
 
             Shutdown::Write => {
-                let final_size = stream.send.shutdown()?;
+                let (final_size, unsent) = stream.send.shutdown()?;
+
+                // Claw back some flow control allowance from data that was
+                // buffered but not actually sent before the stream was reset.
+                self.tx_data = self.tx_data.saturating_sub(unsent);
+
+                self.tx_buffered =
+                    self.tx_buffered.saturating_sub(unsent as usize);
+
+                // Update send capacity.
+                self.update_tx_cap();
 
                 self.streams.mark_reset(stream_id, true, err, final_size);
 
@@ -3574,6 +4785,26 @@
         Err(Error::InvalidStreamState(stream_id))
     }
 
+    /// Returns the next stream that has data to read.
+    ///
+    /// Note that once returned by this method, a stream ID will not be returned
+    /// again until it is "re-armed".
+    ///
+    /// The application will need to read all of the pending data on the stream,
+    /// and new data has to be received before the stream is reported again.
+    ///
+    /// This is unlike the [`readable()`] method, that returns the same list of
+    /// readable streams when called multiple times in succession.
+    ///
+    /// [`readable()`]: struct.Connection.html#method.readable
+    pub fn stream_readable_next(&mut self) -> Option<u64> {
+        let &stream_id = self.streams.readable.iter().next()?;
+
+        self.streams.mark_readable(stream_id, false);
+
+        Some(stream_id)
+    }
+
     /// Returns true if the stream has data that can be read.
     pub fn stream_readable(&self, stream_id: u64) -> bool {
         let stream = match self.streams.get(stream_id) {
@@ -3585,6 +4816,115 @@
         stream.is_readable()
     }
 
+    /// Returns the next stream that can be written to.
+    ///
+    /// Note that once returned by this method, a stream ID will not be returned
+    /// again until it is "re-armed".
+    ///
+    /// This is unlike the [`writable()`] method, that returns the same list of
+    /// writable streams when called multiple times in succession. It is not
+    /// advised to use both `stream_writable_next()` and [`writable()`] on the
+    /// same connection, as it may lead to unexpected results.
+    ///
+    /// The [`stream_writable()`] method can also be used to fine-tune when a
+    /// stream is reported as writable again.
+    ///
+    /// [`stream_writable()`]: struct.Connection.html#method.stream_writable
+    /// [`writable()`]: struct.Connection.html#method.writable
+    pub fn stream_writable_next(&mut self) -> Option<u64> {
+        // If there is not enough connection-level send capacity, none of the
+        // streams are writable.
+        if self.tx_cap == 0 {
+            return None;
+        }
+
+        for &stream_id in &self.streams.writable {
+            if let Some(stream) = self.streams.get(stream_id) {
+                let cap = match stream.send.cap() {
+                    Ok(v) => v,
+
+                    // Return the stream to the application immediately if it's
+                    // stopped.
+                    Err(_) =>
+                        return {
+                            self.streams.mark_writable(stream_id, false);
+                            Some(stream_id)
+                        },
+                };
+
+                if cmp::min(self.tx_cap, cap) >= stream.send_lowat {
+                    self.streams.mark_writable(stream_id, false);
+                    return Some(stream_id);
+                }
+            }
+        }
+
+        None
+    }
+
+    /// Returns true if the stream has enough send capacity.
+    ///
+    /// When `len` more bytes can be buffered into the given stream's send
+    /// buffer, `true` will be returned, `false` otherwise.
+    ///
+    /// In the latter case, if the additional data can't be buffered due to
+    /// flow control limits, the peer will also be notified, and a "low send
+    /// watermark" will be set for the stream, such that it is not going to be
+    /// reported as writable again by [`stream_writable_next()`] until its send
+    /// capacity reaches `len`.
+    ///
+    /// If the specified stream doesn't exist (including when it has already
+    /// been completed and closed), the [`InvalidStreamState`] error will be
+    /// returned.
+    ///
+    /// In addition, if the peer has signalled that it doesn't want to receive
+    /// any more data from this stream by sending the `STOP_SENDING` frame, the
+    /// [`StreamStopped`] error will be returned.
+    ///
+    /// [`stream_writable_next()`]: struct.Connection.html#method.stream_writable_next
+    /// [`InvalidStreamState`]: enum.Error.html#variant.InvalidStreamState
+    /// [`StreamStopped`]: enum.Error.html#variant.StreamStopped
+    #[inline]
+    pub fn stream_writable(
+        &mut self, stream_id: u64, len: usize,
+    ) -> Result<bool> {
+        if self.stream_capacity(stream_id)? >= len {
+            return Ok(true);
+        }
+
+        let stream = match self.streams.get_mut(stream_id) {
+            Some(v) => v,
+
+            None => return Err(Error::InvalidStreamState(stream_id)),
+        };
+
+        stream.send_lowat = cmp::max(1, len);
+
+        let is_writable = stream.is_writable();
+
+        if self.max_tx_data - self.tx_data < len as u64 {
+            self.blocked_limit = Some(self.max_tx_data);
+        }
+
+        if stream.send.cap()? < len {
+            let max_off = stream.send.max_off();
+            if stream.send.blocked_at() != Some(max_off) {
+                stream.send.update_blocked_at(Some(max_off));
+                self.streams.mark_blocked(stream_id, true, max_off);
+            }
+        } else if is_writable {
+            // When `stream_writable_next()` returns a stream, the writable
+            // mark is removed, but because the stream is blocked by the
+            // connection-level send capacity it won't be marked as writable
+            // again once the capacity increases.
+            //
+            // Since the stream is writable already, mark it here instead.
+            self.streams.mark_writable(stream_id, true);
+        }
+
+        Ok(false)
+    }
+
     /// Returns true if all the data has been read from the specified stream.
     ///
     /// This instructs the application that all the data received from the
@@ -3685,8 +5025,9 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = socket.local_addr().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// // Iterate over readable streams.
     /// for stream_id in conn.readable() {
     ///     // Stream is readable, read until there's no more data.
@@ -3720,8 +5061,9 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let local = socket.local_addr().unwrap();
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// // Iterate over writable streams.
     /// for stream_id in conn.writable() {
     ///     // Stream is writable, write some data.
@@ -3756,15 +5098,63 @@
     ///     struct.Config.html#method.set_max_send_udp_payload_size
     /// [`send()`]: struct.Connection.html#method.send
     pub fn max_send_udp_payload_size(&self) -> usize {
-        if self.is_established() {
-            // We cap the maximum packet size to 16KB or so, so that it can be
-            // always encoded with a 2-byte varint.
-            cmp::min(16383, self.recovery.max_datagram_size())
-        } else {
-            // Allow for 1200 bytes (minimum QUIC packet size) during the
-            // handshake.
-            MIN_CLIENT_INITIAL_LEN
+        let max_datagram_size = self
+            .paths
+            .get_active()
+            .ok()
+            .map(|p| p.recovery.max_datagram_size());
+
+        if let Some(max_datagram_size) = max_datagram_size {
+            if self.is_established() {
+                // We cap the maximum packet size to 16KB or so, so that it can be
+                // always encoded with a 2-byte varint.
+                return cmp::min(16383, max_datagram_size);
+            }
         }
+
+        // Allow for 1200 bytes (minimum QUIC packet size) during the
+        // handshake.
+        MIN_CLIENT_INITIAL_LEN
+    }
+
+    /// Schedule an ack-eliciting packet on the active path.
+    ///
+    /// QUIC packets might not contain ack-eliciting frames during normal
+    /// operating conditions. If the packet would already contain
+    /// ack-eliciting frames, this method does not change any behavior.
+    /// However, if the packet would not ordinarily contain ack-eliciting
+    /// frames, this method ensures that a PING frame sent.
+    ///
+    /// Calling this method multiple times before [`send()`] has no effect.
+    ///
+    /// [`send()`]: struct.Connection.html#method.send
+    pub fn send_ack_eliciting(&mut self) -> Result<()> {
+        if self.is_closed() || self.is_draining() {
+            return Ok(());
+        }
+        self.paths.get_active_mut()?.needs_ack_eliciting = true;
+        Ok(())
+    }
+
+    /// Schedule an ack-eliciting packet on the specified path.
+    ///
+    /// See [`send_ack_eliciting()`] for more detail. [`InvalidState`] is
+    /// returned if there is no record of the path.
+    ///
+    /// [`send_ack_eliciting()`]: struct.Connection.html#method.send_ack_eliciting
+    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
+    pub fn send_ack_eliciting_on_path(
+        &mut self, local: SocketAddr, peer: SocketAddr,
+    ) -> Result<()> {
+        if self.is_closed() || self.is_draining() {
+            return Ok(());
+        }
+        let path_id = self
+            .paths
+            .path_id_from_addrs(&(local, peer))
+            .ok_or(Error::InvalidState)?;
+        self.paths.get_mut(path_id)?.needs_ack_eliciting = true;
+        Ok(())
     }
 
     /// Reads the first received DATAGRAM.
@@ -3786,8 +5176,9 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = socket.local_addr().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// let mut dgram_buf = [0; 512];
     /// while let Ok((len)) = conn.dgram_recv(&mut dgram_buf) {
     ///     println!("Got {} bytes of DATAGRAM", len);
@@ -3810,6 +5201,21 @@
         }
     }
 
+    /// Reads the first received DATAGRAM.
+    ///
+    /// This is the same as [`dgram_recv()`] but returns the DATAGRAM as a
+    /// `Vec<u8>` instead of copying into the provided buffer.
+    ///
+    /// [`dgram_recv()`]: struct.Connection.html#method.dgram_recv
+    #[inline]
+    pub fn dgram_recv_vec(&mut self) -> Result<Vec<u8>> {
+        match self.dgram_recv_queue.pop() {
+            Some(d) => Ok(d),
+
+            None => Err(Error::Done),
+        }
+    }
+
     /// Reads the first received DATAGRAM without removing it from the queue.
     ///
     /// On success the DATAGRAM's data is returned along with the actual number
@@ -3858,6 +5264,18 @@
         self.dgram_send_queue.byte_size()
     }
 
+    /// Returns whether or not the DATAGRAM send queue is full.
+    #[inline]
+    pub fn is_dgram_send_queue_full(&self) -> bool {
+        self.dgram_send_queue.is_full()
+    }
+
+    /// Returns whether or not the DATAGRAM recv queue is full.
+    #[inline]
+    pub fn is_dgram_recv_queue_full(&self) -> bool {
+        self.dgram_recv_queue.is_full()
+    }
+
     /// Sends data in a DATAGRAM frame.
     ///
     /// [`Done`] is returned if no data was written.
@@ -3884,17 +5302,47 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = socket.local_addr().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// conn.dgram_send(b"hello")?;
     /// # Ok::<(), quiche::Error>(())
     /// ```
     pub fn dgram_send(&mut self, buf: &[u8]) -> Result<()> {
         let max_payload_len = match self.dgram_max_writable_len() {
-            Some(v) => v as usize,
-            None => {
-                return Err(Error::InvalidState);
-            },
+            Some(v) => v,
+
+            None => return Err(Error::InvalidState),
+        };
+
+        if buf.len() > max_payload_len {
+            return Err(Error::BufferTooShort);
+        }
+
+        self.dgram_send_queue.push(buf.to_vec())?;
+
+        let active_path = self.paths.get_active_mut()?;
+
+        if self.dgram_send_queue.byte_size() >
+            active_path.recovery.cwnd_available()
+        {
+            active_path.recovery.update_app_limited(false);
+        }
+
+        Ok(())
+    }
+
+    /// Sends data in a DATAGRAM frame.
+    ///
+    /// This is the same as [`dgram_send()`] but takes a `Vec<u8>` instead of
+    /// a slice.
+    ///
+    /// [`dgram_send()`]: struct.Connection.html#method.dgram_send
+    pub fn dgram_send_vec(&mut self, buf: Vec<u8>) -> Result<()> {
+        let max_payload_len = match self.dgram_max_writable_len() {
+            Some(v) => v,
+
+            None => return Err(Error::InvalidState),
         };
 
         if buf.len() > max_payload_len {
@@ -3903,8 +5351,12 @@
 
         self.dgram_send_queue.push(buf)?;
 
-        if self.dgram_send_queue.byte_size() > self.recovery.cwnd_available() {
-            self.recovery.update_app_limited(false);
+        let active_path = self.paths.get_active_mut()?;
+
+        if self.dgram_send_queue.byte_size() >
+            active_path.recovery.cwnd_available()
+        {
+            active_path.recovery.update_app_limited(false);
         }
 
         Ok(())
@@ -3919,8 +5371,9 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = socket.local_addr().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// conn.dgram_send(b"hello")?;
     /// conn.dgram_purge_outgoing(&|d: &[u8]| -> bool { d[0] == 0 });
     /// # Ok::<(), quiche::Error>(())
@@ -3942,8 +5395,9 @@
     /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
     /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
     /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
-    /// # let from = "127.0.0.1:1234".parse().unwrap();
-    /// # let mut conn = quiche::accept(&scid, None, from, &mut config)?;
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let local = socket.local_addr().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
     /// if let Some(payload_size) = conn.dgram_max_writable_len() {
     ///     if payload_size > 5 {
     ///         conn.dgram_send(b"hello")?;
@@ -3956,16 +5410,17 @@
         match self.peer_transport_params.max_datagram_frame_size {
             None => None,
             Some(peer_frame_len) => {
+                let dcid = self.destination_id();
                 // Start from the maximum packet size...
                 let mut max_len = self.max_send_udp_payload_size();
                 // ...subtract the Short packet header overhead...
                 // (1 byte of pkt_len + len of dcid)
-                max_len = max_len.saturating_sub(1 + self.dcid.len());
+                max_len = max_len.saturating_sub(1 + dcid.len());
                 // ...subtract the packet number (max len)...
                 max_len = max_len.saturating_sub(packet::MAX_PKT_NUM_LEN);
                 // ...subtract the crypto overhead...
                 max_len = max_len.saturating_sub(
-                    self.pkt_num_spaces[packet::EPOCH_APPLICATION]
+                    self.pkt_num_spaces[packet::Epoch::Application]
                         .crypto_overhead()?,
                 );
                 // ...clamp to what peer can support...
@@ -3983,18 +5438,19 @@
             .is_some()
     }
 
-    /// Returns the amount of time until the next timeout event.
+    /// Returns when the next timeout event will occur.
     ///
-    /// Once the given duration has elapsed, the [`on_timeout()`] method should
-    /// be called. A timeout of `None` means that the timer should be disarmed.
+    /// Once the timeout Instant has been reached, the [`on_timeout()`] method
+    /// should be called. A timeout of `None` means that the timer should be
+    /// disarmed.
     ///
     /// [`on_timeout()`]: struct.Connection.html#method.on_timeout
-    pub fn timeout(&self) -> Option<time::Duration> {
+    pub fn timeout_instant(&self) -> Option<time::Instant> {
         if self.is_closed() {
             return None;
         }
 
-        let timeout = if self.is_draining() {
+        if self.is_draining() {
             // Draining timer takes precedence over all other timers. If it is
             // set it means the connection is closing so there's no point in
             // processing the other timers.
@@ -4004,22 +5460,40 @@
             // detection timers. If they are both unset (i.e. `None`) then the
             // result is `None`, but if at least one of them is set then a
             // `Some(...)` value is returned.
-            let timers = [self.idle_timer, self.recovery.loss_detection_timer()];
+            let path_timer = self
+                .paths
+                .iter()
+                .filter_map(|(_, p)| p.recovery.loss_detection_timer())
+                .min();
+
+            let key_update_timer = self.pkt_num_spaces
+                [packet::Epoch::Application]
+                .key_update
+                .as_ref()
+                .map(|key_update| key_update.timer);
+
+            let timers = [self.idle_timer, path_timer, key_update_timer];
 
             timers.iter().filter_map(|&x| x).min()
-        };
+        }
+    }
 
-        if let Some(timeout) = timeout {
+    /// Returns the amount of time until the next timeout event.
+    ///
+    /// Once the given duration has elapsed, the [`on_timeout()`] method should
+    /// be called. A timeout of `None` means that the timer should be disarmed.
+    ///
+    /// [`on_timeout()`]: struct.Connection.html#method.on_timeout
+    pub fn timeout(&self) -> Option<time::Duration> {
+        self.timeout_instant().map(|timeout| {
             let now = time::Instant::now();
 
             if timeout <= now {
-                return Some(time::Duration::new(0, 0));
+                time::Duration::ZERO
+            } else {
+                timeout.duration_since(now)
             }
-
-            return Some(timeout.duration_since(now));
-        }
-
-        None
+        })
     }
 
     /// Processes a timeout event.
@@ -4032,7 +5506,7 @@
             if draining_timer <= now {
                 trace!("{} draining timeout expired", self.trace_id);
 
-                qlog_with!(self.qlog_streamer, q, {
+                qlog_with!(self.qlog, q, {
                     q.finish_log().ok();
                 });
 
@@ -4049,33 +5523,426 @@
             if timer <= now {
                 trace!("{} idle timeout expired", self.trace_id);
 
-                qlog_with!(self.qlog_streamer, q, {
+                qlog_with!(self.qlog, q, {
                     q.finish_log().ok();
                 });
 
                 self.closed = true;
+                self.timed_out = true;
                 return;
             }
         }
 
-        if let Some(timer) = self.recovery.loss_detection_timer() {
+        if let Some(timer) = self.pkt_num_spaces[packet::Epoch::Application]
+            .key_update
+            .as_ref()
+            .map(|key_update| key_update.timer)
+        {
             if timer <= now {
-                trace!("{} loss detection timeout expired", self.trace_id);
-
-                self.recovery.on_loss_detection_timeout(
-                    self.handshake_status(),
-                    now,
-                    &self.trace_id,
-                );
-
-                qlog_with!(self.qlog_streamer, q, {
-                    let ev = self.recovery.to_qlog();
-                    q.add_event(ev).ok();
-                });
-
-                return;
+                // Discard previous key once key update timer expired.
+                let _ = self.pkt_num_spaces[packet::Epoch::Application]
+                    .key_update
+                    .take();
             }
         }
+
+        let handshake_status = self.handshake_status();
+
+        for (_, p) in self.paths.iter_mut() {
+            if let Some(timer) = p.recovery.loss_detection_timer() {
+                if timer <= now {
+                    trace!("{} loss detection timeout expired", self.trace_id);
+
+                    let (lost_packets, lost_bytes) = p.on_loss_detection_timeout(
+                        handshake_status,
+                        now,
+                        self.is_server,
+                        &self.trace_id,
+                    );
+
+                    self.lost_count += lost_packets;
+                    self.lost_bytes += lost_bytes as u64;
+
+                    qlog_with_type!(QLOG_METRICS, self.qlog, q, {
+                        if let Some(ev_data) = p.recovery.maybe_qlog() {
+                            q.add_event_data_with_instant(ev_data, now).ok();
+                        }
+                    });
+                }
+            }
+        }
+
+        // Notify timeout events to the application.
+        self.paths.notify_failed_validations();
+
+        // If the active path failed, try to find a new candidate.
+        if self.paths.get_active_path_id().is_err() {
+            match self.paths.find_candidate_path() {
+                Some(pid) =>
+                    if self.paths.set_active_path(pid).is_err() {
+                        // The connection cannot continue.
+                        self.closed = true;
+                    },
+
+                // The connection cannot continue.
+                None => self.closed = true,
+            }
+        }
+    }
+
+    /// Requests the stack to perform path validation of the proposed 4-tuple.
+    ///
+    /// Probing new paths requires spare Connection IDs at both the host and the
+    /// peer sides. If it is not the case, it raises an [`OutOfIdentifiers`].
+    ///
+    /// The probing of new addresses can only be done by the client. The server
+    /// can only probe network paths that were previously advertised by
+    /// [`NewPath`]. If the server tries to probe such an unseen network path,
+    /// this call raises an [`InvalidState`].
+    ///
+    /// The caller might also want to probe an existing path. In such case, it
+    /// triggers a PATH_CHALLENGE frame, but it does not require spare CIDs.
+    ///
+    /// A server always probes a new path it observes. Calling this method is
+    /// hence not required to validate a new path. However, a server can still
+    /// request an additional path validation of the proposed 4-tuple.
+    ///
+    /// Calling this method several times before calling [`send()`] or
+    /// [`send_on_path()`] results in a single probe being generated. An
+    /// application wanting to send multiple in-flight probes must call this
+    /// method again after having sent packets.
+    ///
+    /// Returns the Destination Connection ID sequence number associated to that
+    /// path.
+    ///
+    /// [`NewPath`]: enum.QuicEvent.html#NewPath
+    /// [`OutOfIdentifiers`]: enum.Error.html#OutOfIdentifiers
+    /// [`InvalidState`]: enum.Error.html#InvalidState
+    /// [`send()`]: struct.Connection.html#method.send
+    /// [`send_on_path()`]: struct.Connection.html#method.send_on_path
+    pub fn probe_path(
+        &mut self, local_addr: SocketAddr, peer_addr: SocketAddr,
+    ) -> Result<u64> {
+        // We may want to probe an existing path.
+        let pid = match self.paths.path_id_from_addrs(&(local_addr, peer_addr)) {
+            Some(pid) => pid,
+            None => self.create_path_on_client(local_addr, peer_addr)?,
+        };
+
+        let path = self.paths.get_mut(pid)?;
+        path.request_validation();
+
+        path.active_dcid_seq.ok_or(Error::InvalidState)
+    }
+
+    /// Migrates the connection to a new local address `local_addr`.
+    ///
+    /// The behavior is similar to [`migrate()`], with the nuance that the
+    /// connection only changes the local address, but not the peer one.
+    ///
+    /// See [`migrate()`] for the full specification of this method.
+    ///
+    /// [`migrate()`]: struct.Connection.html#method.migrate
+    pub fn migrate_source(&mut self, local_addr: SocketAddr) -> Result<u64> {
+        let peer_addr = self.paths.get_active()?.peer_addr();
+        self.migrate(local_addr, peer_addr)
+    }
+
+    /// Migrates the connection over the given network path between `local_addr`
+    /// and `peer_addr`.
+    ///
+    /// Connection migration can only be initiated by the client. Calling this
+    /// method as a server returns [`InvalidState`].
+    ///
+    /// To initiate voluntary migration, there should be enough Connection IDs
+    /// at both sides. If this requirement is not satisfied, this call returns
+    /// [`OutOfIdentifiers`].
+    ///
+    /// Returns the Destination Connection ID associated to that migrated path.
+    ///
+    /// [`OutOfIdentifiers`]: enum.Error.html#OutOfIdentifiers
+    /// [`InvalidState`]: enum.Error.html#InvalidState
+    pub fn migrate(
+        &mut self, local_addr: SocketAddr, peer_addr: SocketAddr,
+    ) -> Result<u64> {
+        if self.is_server {
+            return Err(Error::InvalidState);
+        }
+
+        // If the path already exists, mark it as the active one.
+        let (pid, dcid_seq) = if let Some(pid) =
+            self.paths.path_id_from_addrs(&(local_addr, peer_addr))
+        {
+            let path = self.paths.get_mut(pid)?;
+
+            // If it is already active, do nothing.
+            if path.active() {
+                return path.active_dcid_seq.ok_or(Error::OutOfIdentifiers);
+            }
+
+            // Ensures that a Source Connection ID has been dedicated to this
+            // path, or a free one is available. This is only required if the
+            // host uses non-zero length Source Connection IDs.
+            if !self.ids.zero_length_scid() &&
+                path.active_scid_seq.is_none() &&
+                self.ids.available_scids() == 0
+            {
+                return Err(Error::OutOfIdentifiers);
+            }
+
+            // Ensures that the migrated path has a Destination Connection ID.
+            let dcid_seq = if let Some(dcid_seq) = path.active_dcid_seq {
+                dcid_seq
+            } else {
+                let dcid_seq = self
+                    .ids
+                    .lowest_available_dcid_seq()
+                    .ok_or(Error::OutOfIdentifiers)?;
+
+                self.ids.link_dcid_to_path_id(dcid_seq, pid)?;
+                path.active_dcid_seq = Some(dcid_seq);
+
+                dcid_seq
+            };
+
+            (pid, dcid_seq)
+        } else {
+            let pid = self.create_path_on_client(local_addr, peer_addr)?;
+
+            let dcid_seq = self
+                .paths
+                .get(pid)?
+                .active_dcid_seq
+                .ok_or(Error::InvalidState)?;
+
+            (pid, dcid_seq)
+        };
+
+        // Change the active path.
+        self.paths.set_active_path(pid)?;
+
+        Ok(dcid_seq)
+    }
+
+    /// Provides additional source Connection IDs that the peer can use to reach
+    /// this host.
+    ///
+    /// This triggers sending NEW_CONNECTION_ID frames if the provided Source
+    /// Connection ID is not already present. In the case the caller tries to
+    /// reuse a Connection ID with a different reset token, this raises an
+    /// `InvalidState`.
+    ///
+    /// At any time, the peer cannot have more Destination Connection IDs than
+    /// the maximum number of active Connection IDs it negotiated. In such case
+    /// (i.e., when [`source_cids_left()`] returns 0), if the host agrees to
+    /// request the removal of previous connection IDs, it sets the
+    /// `retire_if_needed` parameter. Otherwise, an [`IdLimit`] is returned.
+    ///
+    /// Note that setting `retire_if_needed` does not prevent this function from
+    /// returning an [`IdLimit`] in the case the caller wants to retire still
+    /// unannounced Connection IDs.
+    ///
+    /// The caller is responsible from ensuring that the provided `scid` is not
+    /// repeated several times over the connection. quiche ensures that as long
+    /// as the provided Connection ID is still in use (i.e., not retired), it
+    /// does not assign a different sequence number.
+    ///
+    /// Note that if the host uses zero-length Source Connection IDs, it cannot
+    /// advertise Source Connection IDs and calling this method returns an
+    /// [`InvalidState`].
+    ///
+    /// Returns the sequence number associated to the provided Connection ID.
+    ///
+    /// [`source_cids_left()`]: struct.Connection.html#method.source_cids_left
+    /// [`IdLimit`]: enum.Error.html#IdLimit
+    /// [`InvalidState`]: enum.Error.html#InvalidState
+    pub fn new_source_cid(
+        &mut self, scid: &ConnectionId, reset_token: u128, retire_if_needed: bool,
+    ) -> Result<u64> {
+        self.ids.new_scid(
+            scid.to_vec().into(),
+            Some(reset_token),
+            true,
+            None,
+            retire_if_needed,
+        )
+    }
+
+    /// Returns the number of source Connection IDs that are active. This is
+    /// only meaningful if the host uses non-zero length Source Connection IDs.
+    pub fn active_source_cids(&self) -> usize {
+        self.ids.active_source_cids()
+    }
+
+    /// Returns the maximum number of concurrently active source Connection IDs
+    /// that can be provided to the peer.
+    pub fn max_active_source_cids(&self) -> usize {
+        self.peer_transport_params.active_conn_id_limit as usize
+    }
+
+    /// Returns the number of source Connection IDs that can still be provided
+    /// to the peer without exceeding the limit it advertised.
+    ///
+    /// The application should not issue the maximum number of permitted source
+    /// Connection IDs, but instead treat this as an untrusted upper bound.
+    /// Applications should limit how many outstanding source ConnectionIDs
+    /// are simultaneously issued to prevent issuing more than they can handle.
+    #[inline]
+    pub fn source_cids_left(&self) -> usize {
+        self.max_active_source_cids() - self.active_source_cids()
+    }
+
+    /// Requests the retirement of the destination Connection ID used by the
+    /// host to reach its peer.
+    ///
+    /// This triggers sending RETIRE_CONNECTION_ID frames.
+    ///
+    /// If the application tries to retire a non-existing Destination Connection
+    /// ID sequence number, or if it uses zero-length Destination Connection ID,
+    /// this method returns an [`InvalidState`].
+    ///
+    /// At any time, the host must have at least one Destination ID. If the
+    /// application tries to retire the last one, or if the caller tries to
+    /// retire the destination Connection ID used by the current active path
+    /// while having neither spare Destination Connection IDs nor validated
+    /// network paths, this method returns an [`OutOfIdentifiers`]. This
+    /// behavior prevents the caller from stalling the connection due to the
+    /// lack of validated path to send non-probing packets.
+    ///
+    /// [`InvalidState`]: enum.Error.html#InvalidState
+    /// [`OutOfIdentifiers`]: enum.Error.html#OutOfIdentifiers
+    pub fn retire_destination_cid(&mut self, dcid_seq: u64) -> Result<()> {
+        if self.ids.zero_length_dcid() {
+            return Err(Error::InvalidState);
+        }
+
+        let active_path_dcid_seq = self
+            .paths
+            .get_active()?
+            .active_dcid_seq
+            .ok_or(Error::InvalidState)?;
+
+        let active_path_id = self.paths.get_active_path_id()?;
+
+        if active_path_dcid_seq == dcid_seq &&
+            self.ids.lowest_available_dcid_seq().is_none() &&
+            !self
+                .paths
+                .iter()
+                .any(|(pid, p)| pid != active_path_id && p.usable())
+        {
+            return Err(Error::OutOfIdentifiers);
+        }
+
+        if let Some(pid) = self.ids.retire_dcid(dcid_seq)? {
+            // The retired Destination CID was associated to a given path. Let's
+            // find an available DCID to associate to that path.
+            let path = self.paths.get_mut(pid)?;
+            let dcid_seq = self.ids.lowest_available_dcid_seq();
+
+            if let Some(dcid_seq) = dcid_seq {
+                self.ids.link_dcid_to_path_id(dcid_seq, pid)?;
+            }
+
+            path.active_dcid_seq = dcid_seq;
+        }
+
+        Ok(())
+    }
+
+    /// Processes path-specific events.
+    ///
+    /// On success it returns a [`PathEvent`], or `None` when there are no
+    /// events to report. Please refer to [`PathEvent`] for the exhaustive event
+    /// list.
+    ///
+    /// Note that all events are edge-triggered, meaning that once reported they
+    /// will not be reported again by calling this method again, until the event
+    /// is re-armed.
+    ///
+    /// [`PathEvent`]: enum.PathEvent.html
+    pub fn path_event_next(&mut self) -> Option<PathEvent> {
+        self.paths.pop_event()
+    }
+
+    /// Returns a source `ConnectionId` that has been retired.
+    ///
+    /// On success it returns a [`ConnectionId`], or `None` when there are no
+    /// more retired connection IDs.
+    ///
+    /// [`ConnectionId`]: struct.ConnectionId.html
+    pub fn retired_scid_next(&mut self) -> Option<ConnectionId<'static>> {
+        self.ids.pop_retired_scid()
+    }
+
+    /// Returns the number of spare Destination Connection IDs, i.e.,
+    /// Destination Connection IDs that are still unused.
+    ///
+    /// Note that this function returns 0 if the host uses zero length
+    /// Destination Connection IDs.
+    pub fn available_dcids(&self) -> usize {
+        self.ids.available_dcids()
+    }
+
+    /// Returns an iterator over destination `SockAddr`s whose association
+    /// with `from` forms a known QUIC path on which packets can be sent to.
+    ///
+    /// This function is typically used in combination with [`send_on_path()`].
+    ///
+    /// Note that the iterator includes all the possible combination of
+    /// destination `SockAddr`s, even those whose sending is not required now.
+    /// In other words, this is another way for the application to recall from
+    /// past [`NewPath`] events.
+    ///
+    /// [`NewPath`]: enum.QuicEvent.html#NewPath
+    /// [`send_on_path()`]: struct.Connection.html#method.send_on_path
+    ///
+    /// ## Examples:
+    ///
+    /// ```no_run
+    /// # let mut out = [0; 512];
+    /// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
+    /// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
+    /// # let scid = quiche::ConnectionId::from_ref(&[0xba; 16]);
+    /// # let local = socket.local_addr().unwrap();
+    /// # let peer = "127.0.0.1:1234".parse().unwrap();
+    /// # let mut conn = quiche::accept(&scid, None, local, peer, &mut config)?;
+    /// // Iterate over possible destinations for the given local `SockAddr`.
+    /// for dest in conn.paths_iter(local) {
+    ///     loop {
+    ///         let (write, send_info) =
+    ///             match conn.send_on_path(&mut out, Some(local), Some(dest)) {
+    ///                 Ok(v) => v,
+    ///
+    ///                 Err(quiche::Error::Done) => {
+    ///                     // Done writing for this destination.
+    ///                     break;
+    ///                 },
+    ///
+    ///                 Err(e) => {
+    ///                     // An error occurred, handle it.
+    ///                     break;
+    ///                 },
+    ///             };
+    ///
+    ///         socket.send_to(&out[..write], &send_info.to).unwrap();
+    ///     }
+    /// }
+    /// # Ok::<(), quiche::Error>(())
+    /// ```
+    #[inline]
+    pub fn paths_iter(&self, from: SocketAddr) -> SocketAddrIter {
+        // Instead of trying to identify whether packets will be sent on the
+        // given 4-tuple, simply filter paths that cannot be used.
+        SocketAddrIter {
+            sockaddrs: self
+                .paths
+                .iter()
+                .filter(|(_, p)| p.usable() || p.probing_required())
+                .filter(|(_, p)| p.local_addr() == from)
+                .map(|(_, p)| p.peer_addr())
+                .collect(),
+        }
     }
 
     /// Closes the connection with the given error and reason.
@@ -4083,6 +5950,13 @@
     /// The `app` parameter specifies whether an application close should be
     /// sent to the peer. Otherwise a normal connection close is sent.
     ///
+    /// If `app` is true but the connection is not in a state that is safe to
+    /// send an application error (not established nor in early data), in
+    /// accordance with [RFC
+    /// 9000](https://www.rfc-editor.org/rfc/rfc9000.html#section-10.2.3-3), the
+    /// error code is changed to APPLICATION_ERROR and the reason phrase is
+    /// cleared.
+    ///
     /// Returns [`Done`] if the connection had already been closed.
     ///
     /// Note that the connection will not be closed immediately. An application
@@ -4105,11 +5979,23 @@
             return Err(Error::Done);
         }
 
-        self.local_error = Some(ConnectionError {
-            is_app: app,
-            error_code: err,
-            reason: reason.to_vec(),
-        });
+        let is_safe_to_send_app_data =
+            self.is_established() || self.is_in_early_data();
+
+        if app && !is_safe_to_send_app_data {
+            // Clear error information.
+            self.local_error = Some(ConnectionError {
+                is_app: false,
+                error_code: 0x0c,
+                reason: vec![],
+            });
+        } else {
+            self.local_error = Some(ConnectionError {
+                is_app: app,
+                error_code: err,
+                reason: reason.to_vec(),
+            });
+        }
 
         // When no packet was successfully processed close connection immediately.
         if self.recv_count == 0 {
@@ -4136,10 +6022,27 @@
         self.alpn.as_ref()
     }
 
+    /// Returns the server name requested by the client.
+    #[inline]
+    pub fn server_name(&self) -> Option<&str> {
+        self.handshake.server_name()
+    }
+
     /// Returns the peer's leaf certificate (if any) as a DER-encoded buffer.
     #[inline]
-    pub fn peer_cert(&self) -> Option<Vec<u8>> {
-        self.handshake.lock().unwrap().peer_cert()
+    pub fn peer_cert(&self) -> Option<&[u8]> {
+        self.handshake.peer_cert()
+    }
+
+    /// Returns the peer's certificate chain (if any) as a vector of DER-encoded
+    /// buffers.
+    ///
+    /// The certificate at index 0 is the peer's leaf certificate, the other
+    /// certificates (if any) are the chain certificate authorities used to
+    /// sign the leaf certificate.
+    #[inline]
+    pub fn peer_cert_chain(&self) -> Option<Vec<&[u8]>> {
+        self.handshake.peer_cert_chain()
     }
 
     /// Returns the serialized cryptographic session for the connection.
@@ -4149,8 +6052,8 @@
     ///
     /// [`set_session()`]: struct.Connection.html#method.set_session
     #[inline]
-    pub fn session(&self) -> Option<Vec<u8>> {
-        self.session.clone()
+    pub fn session(&self) -> Option<&[u8]> {
+        self.session.as_deref()
     }
 
     /// Returns the source connection ID.
@@ -4159,7 +6062,16 @@
     /// lifetime.
     #[inline]
     pub fn source_id(&self) -> ConnectionId {
-        ConnectionId::from_ref(self.scid.as_ref())
+        if let Ok(path) = self.paths.get_active() {
+            if let Some(active_scid_seq) = path.active_scid_seq {
+                if let Ok(e) = self.ids.get_scid(active_scid_seq) {
+                    return ConnectionId::from_ref(e.cid.as_ref());
+                }
+            }
+        }
+
+        let e = self.ids.oldest_scid();
+        ConnectionId::from_ref(e.cid.as_ref())
     }
 
     /// Returns the destination connection ID.
@@ -4168,7 +6080,16 @@
     /// lifetime.
     #[inline]
     pub fn destination_id(&self) -> ConnectionId {
-        ConnectionId::from_ref(self.dcid.as_ref())
+        if let Ok(path) = self.paths.get_active() {
+            if let Some(active_dcid_seq) = path.active_dcid_seq {
+                if let Ok(e) = self.ids.get_dcid(active_dcid_seq) {
+                    return ConnectionId::from_ref(e.cid.as_ref());
+                }
+            }
+        }
+
+        let e = self.ids.oldest_dcid();
+        ConnectionId::from_ref(e.cid.as_ref())
     }
 
     /// Returns true if the connection handshake is complete.
@@ -4180,14 +6101,14 @@
     /// Returns true if the connection is resumed.
     #[inline]
     pub fn is_resumed(&self) -> bool {
-        self.handshake.lock().unwrap().is_resumed()
+        self.handshake.is_resumed()
     }
 
     /// Returns true if the connection has a pending handshake that has
     /// progressed enough to send or receive early data.
     #[inline]
     pub fn is_in_early_data(&self) -> bool {
-        self.handshake.lock().unwrap().is_in_early_data()
+        self.handshake.is_in_early_data()
     }
 
     /// Returns whether there is stream or DATAGRAM data available to read.
@@ -4196,13 +6117,33 @@
         self.streams.has_readable() || self.dgram_recv_front_len().is_some()
     }
 
+    /// Returns whether the network path with local address `from` and remote
+    /// address `peer` has been validated.
+    ///
+    /// If the 4-tuple does not exist over the connection, returns an
+    /// [`InvalidState`].
+    ///
+    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
+    pub fn is_path_validated(
+        &self, from: SocketAddr, to: SocketAddr,
+    ) -> Result<bool> {
+        let pid = self
+            .paths
+            .path_id_from_addrs(&(from, to))
+            .ok_or(Error::InvalidState)?;
+
+        Ok(self.paths.get(pid)?.validated())
+    }
+
     /// Returns true if the connection is draining.
     ///
-    /// If this returns true, the connection object cannot yet be dropped, but
+    /// If this returns `true`, the connection object cannot yet be dropped, but
     /// no new application data can be sent or received. An application should
-    /// continue calling the [`recv()`], [`send()`], [`timeout()`], and
-    /// [`on_timeout()`] methods as normal, until the [`is_closed()`] method
-    /// returns `true`.
+    /// continue calling the [`recv()`], [`timeout()`], and [`on_timeout()`]
+    /// methods as normal, until the [`is_closed()`] method returns `true`.
+    ///
+    /// In contrast, once `is_draining()` returns `true`, calling [`send()`]
+    /// is not required because no new outgoing packets will be generated.
     ///
     /// [`recv()`]: struct.Connection.html#method.recv
     /// [`send()`]: struct.Connection.html#method.send
@@ -4222,34 +6163,93 @@
         self.closed
     }
 
+    /// Returns true if the connection was closed due to the idle timeout.
+    #[inline]
+    pub fn is_timed_out(&self) -> bool {
+        self.timed_out
+    }
+
     /// Returns the error received from the peer, if any.
     ///
-    /// The values contained in the tuple are symmetric with the [`close()`]
-    /// method.
-    ///
     /// Note that a `Some` return value does not necessarily imply
     /// [`is_closed()`] or any other connection state.
     ///
-    /// [`close()`]: struct.Connection.html#method.close
     /// [`is_closed()`]: struct.Connection.html#method.is_closed
     #[inline]
     pub fn peer_error(&self) -> Option<&ConnectionError> {
         self.peer_error.as_ref()
     }
 
+    /// Returns the error [`close()`] was called with, or internally
+    /// created quiche errors, if any.
+    ///
+    /// Note that a `Some` return value does not necessarily imply
+    /// [`is_closed()`] or any other connection state.
+    /// `Some` also does not guarantee that the error has been sent to
+    /// or received by the peer.
+    ///
+    /// [`close()`]: struct.Connection.html#method.close
+    /// [`is_closed()`]: struct.Connection.html#method.is_closed
+    #[inline]
+    pub fn local_error(&self) -> Option<&ConnectionError> {
+        self.local_error.as_ref()
+    }
+
     /// Collects and returns statistics about the connection.
     #[inline]
     pub fn stats(&self) -> Stats {
         Stats {
             recv: self.recv_count,
             sent: self.sent_count,
-            lost: self.recovery.lost_count,
-            cwnd: self.recovery.cwnd(),
-            rtt: self.recovery.rtt(),
-            delivery_rate: self.recovery.delivery_rate(),
+            lost: self.lost_count,
+            retrans: self.retrans_count,
+            sent_bytes: self.sent_bytes,
+            recv_bytes: self.recv_bytes,
+            lost_bytes: self.lost_bytes,
+            stream_retrans_bytes: self.stream_retrans_bytes,
+            paths_count: self.paths.len(),
+            peer_max_idle_timeout: self.peer_transport_params.max_idle_timeout,
+            peer_max_udp_payload_size: self
+                .peer_transport_params
+                .max_udp_payload_size,
+            peer_initial_max_data: self.peer_transport_params.initial_max_data,
+            peer_initial_max_stream_data_bidi_local: self
+                .peer_transport_params
+                .initial_max_stream_data_bidi_local,
+            peer_initial_max_stream_data_bidi_remote: self
+                .peer_transport_params
+                .initial_max_stream_data_bidi_remote,
+            peer_initial_max_stream_data_uni: self
+                .peer_transport_params
+                .initial_max_stream_data_uni,
+            peer_initial_max_streams_bidi: self
+                .peer_transport_params
+                .initial_max_streams_bidi,
+            peer_initial_max_streams_uni: self
+                .peer_transport_params
+                .initial_max_streams_uni,
+            peer_ack_delay_exponent: self
+                .peer_transport_params
+                .ack_delay_exponent,
+            peer_max_ack_delay: self.peer_transport_params.max_ack_delay,
+            peer_disable_active_migration: self
+                .peer_transport_params
+                .disable_active_migration,
+            peer_active_conn_id_limit: self
+                .peer_transport_params
+                .active_conn_id_limit,
+            peer_max_datagram_frame_size: self
+                .peer_transport_params
+                .max_datagram_frame_size,
         }
     }
 
+    /// Collects and returns statistics about each known path for the
+    /// connection.
+    pub fn path_stats(&self) -> impl Iterator<Item = PathStats> + '_ {
+        self.paths.iter().map(|(_, p)| p.stats())
+    }
+
     fn encode_transport_params(&mut self) -> Result<()> {
         let mut raw_params = [0; 128];
 
@@ -4259,10 +6259,7 @@
             &mut raw_params,
         )?;
 
-        self.handshake
-            .lock()
-            .unwrap()
-            .set_quic_transport_params(raw_params)?;
+        self.handshake.set_quic_transport_params(raw_params)?;
 
         Ok(())
     }
@@ -4275,7 +6272,7 @@
         {
             // Validate initial_source_connection_id.
             match &peer_params.initial_source_connection_id {
-                Some(v) if v != &self.dcid =>
+                Some(v) if v != &self.destination_id() =>
                     return Err(Error::InvalidTransportParam),
 
                 Some(_) => (),
@@ -4325,48 +6322,73 @@
             }
         }
 
-        self.process_peer_transport_params(peer_params);
+        self.process_peer_transport_params(peer_params)?;
 
         self.parsed_peer_transport_params = true;
 
         Ok(())
     }
 
-    fn process_peer_transport_params(&mut self, peer_params: TransportParams) {
+    fn process_peer_transport_params(
+        &mut self, peer_params: TransportParams,
+    ) -> Result<()> {
         self.max_tx_data = peer_params.initial_max_data;
 
         // Update send capacity.
-        self.tx_cap = cmp::min(
-            self.recovery.cwnd_available() as u64,
-            self.max_tx_data - self.tx_data,
-        ) as usize;
+        self.update_tx_cap();
 
         self.streams
             .update_peer_max_streams_bidi(peer_params.initial_max_streams_bidi);
         self.streams
             .update_peer_max_streams_uni(peer_params.initial_max_streams_uni);
 
-        self.recovery.max_ack_delay =
+        let max_ack_delay =
             time::Duration::from_millis(peer_params.max_ack_delay);
 
-        self.recovery
+        self.recovery_config.max_ack_delay = max_ack_delay;
+
+        let active_path = self.paths.get_active_mut()?;
+
+        active_path.recovery.max_ack_delay = max_ack_delay;
+
+        active_path
+            .recovery
             .update_max_datagram_size(peer_params.max_udp_payload_size as usize);
 
+        // Record the max_active_conn_id parameter advertised by the peer.
+        self.ids
+            .set_source_conn_id_limit(peer_params.active_conn_id_limit);
+
         self.peer_transport_params = peer_params;
+
+        Ok(())
     }
 
     /// Continues the handshake.
     ///
     /// If the connection is already established, it does nothing.
-    fn do_handshake(&mut self) -> Result<()> {
-        let handshake = self.handshake.lock().unwrap();
+    fn do_handshake(&mut self, now: time::Instant) -> Result<()> {
+        let mut ex_data = tls::ExData {
+            application_protos: &self.application_protos,
 
-        // Handshake is already complete, nothing more to do.
-        if handshake.is_completed() {
-            return Ok(());
+            pkt_num_spaces: &mut self.pkt_num_spaces,
+
+            session: &mut self.session,
+
+            local_error: &mut self.local_error,
+
+            keylog: self.keylog.as_mut(),
+
+            trace_id: &self.trace_id,
+
+            is_server: self.is_server,
+        };
+
+        if self.handshake_completed {
+            return self.handshake.process_post_handshake(&mut ex_data);
         }
 
-        match handshake.do_handshake() {
+        match self.handshake.do_handshake(&mut ex_data) {
             Ok(_) => (),
 
             Err(Error::Done) => {
@@ -4376,14 +6398,11 @@
                 // This is potentially dangerous as the handshake hasn't been
                 // completed yet, though it's required to be able to send data
                 // in 0.5 RTT.
-                let raw_params = handshake.quic_transport_params();
+                let raw_params = self.handshake.quic_transport_params();
 
                 if !self.parsed_peer_transport_params && !raw_params.is_empty() {
                     let peer_params =
-                        TransportParams::decode(&raw_params, self.is_server)?;
-
-                    // Unlock handshake object.
-                    drop(handshake);
+                        TransportParams::decode(raw_params, self.is_server)?;
 
                     self.parse_peer_transport_params(peer_params)?;
                 }
@@ -4394,42 +6413,48 @@
             Err(e) => return Err(e),
         };
 
-        self.handshake_completed = handshake.is_completed();
+        self.handshake_completed = self.handshake.is_completed();
 
-        self.alpn = handshake.alpn_protocol().to_vec();
+        self.alpn = self.handshake.alpn_protocol().to_vec();
 
-        let cipher = handshake.cipher();
-        let curve = handshake.curve();
-        let sigalg = handshake.sigalg();
-        let is_resumed = handshake.is_resumed();
-
-        let raw_params = handshake.quic_transport_params();
+        let raw_params = self.handshake.quic_transport_params();
 
         if !self.parsed_peer_transport_params && !raw_params.is_empty() {
             let peer_params =
-                TransportParams::decode(&raw_params, self.is_server)?;
-
-            // Unlock handshake object.
-            drop(handshake);
+                TransportParams::decode(raw_params, self.is_server)?;
 
             self.parse_peer_transport_params(peer_params)?;
         }
 
-        // Once the handshake is completed there's no point in processing 0-RTT
-        // packets anymore, so clear the buffer now.
         if self.handshake_completed {
-            self.undecryptable_pkts.clear();
-        }
+            // The handshake is considered confirmed at the server when the
+            // handshake completes, at which point we can also drop the
+            // handshake epoch.
+            if self.is_server {
+                self.handshake_confirmed = true;
 
-        trace!("{} connection established: proto={:?} cipher={:?} curve={:?} sigalg={:?} resumed={} {:?}",
-               &self.trace_id, std::str::from_utf8(self.application_proto()),
-               cipher, curve, sigalg, is_resumed, self.peer_transport_params);
+                self.drop_epoch_state(packet::Epoch::Handshake, now);
+            }
+
+            // Once the handshake is completed there's no point in processing
+            // 0-RTT packets anymore, so clear the buffer now.
+            self.undecryptable_pkts.clear();
+
+            trace!("{} connection established: proto={:?} cipher={:?} curve={:?} sigalg={:?} resumed={} {:?}",
+                   &self.trace_id,
+                   std::str::from_utf8(self.application_proto()),
+                   self.handshake.cipher(),
+                   self.handshake.curve(),
+                   self.handshake.sigalg(),
+                   self.handshake.is_resumed(),
+                   self.peer_transport_params);
+        }
 
         Ok(())
     }
 
     /// Selects the packet type for the next outgoing packet.
-    fn write_pkt_type(&self) -> Result<packet::Type> {
+    fn write_pkt_type(&self, send_pid: usize) -> Result<packet::Type> {
         // On error send packet in the latest epoch available, but only send
         // 1-RTT ones when the handshake is completed.
         if self
@@ -4437,23 +6462,37 @@
             .as_ref()
             .map_or(false, |conn_err| !conn_err.is_app)
         {
-            let epoch = match self.handshake.lock().unwrap().write_level() {
-                crypto::Level::Initial => packet::EPOCH_INITIAL,
+            let epoch = match self.handshake.write_level() {
+                crypto::Level::Initial => packet::Epoch::Initial,
                 crypto::Level::ZeroRTT => unreachable!(),
-                crypto::Level::Handshake => packet::EPOCH_HANDSHAKE,
-                crypto::Level::OneRTT => packet::EPOCH_APPLICATION,
+                crypto::Level::Handshake => packet::Epoch::Handshake,
+                crypto::Level::OneRTT => packet::Epoch::Application,
             };
 
-            if epoch == packet::EPOCH_APPLICATION && !self.is_established() {
-                // Downgrade the epoch to handshake as the handshake is not
-                // completed yet.
-                return Ok(packet::Type::Handshake);
+            if !self.is_established() {
+                match epoch {
+                    // Downgrade the epoch to Handshake as the handshake is not
+                    // completed yet.
+                    packet::Epoch::Application =>
+                        return Ok(packet::Type::Handshake),
+
+                    // Downgrade the epoch to Initial as the remote peer might
+                    // not be able to decrypt handshake packets yet.
+                    packet::Epoch::Handshake
+                        if self.pkt_num_spaces[packet::Epoch::Initial]
+                            .has_keys() =>
+                        return Ok(packet::Type::Initial),
+
+                    _ => (),
+                };
             }
 
             return Ok(packet::Type::from_epoch(epoch));
         }
 
-        for epoch in packet::EPOCH_INITIAL..packet::EPOCH_COUNT {
+        for &epoch in packet::Epoch::epochs(
+            packet::Epoch::Initial..=packet::Epoch::Application,
+        ) {
             // Only send packets in a space when we have the send keys for it.
             if self.pkt_num_spaces[epoch].crypto_seal.is_none() {
                 continue;
@@ -4465,20 +6504,23 @@
             }
 
             // There are lost frames in this packet number space.
-            if !self.recovery.lost[epoch].is_empty() {
-                return Ok(packet::Type::from_epoch(epoch));
-            }
+            for (_, p) in self.paths.iter() {
+                if !p.recovery.lost[epoch].is_empty() {
+                    return Ok(packet::Type::from_epoch(epoch));
+                }
 
-            // We need to send PTO probe packets.
-            if self.recovery.loss_probes[epoch] > 0 {
-                return Ok(packet::Type::from_epoch(epoch));
+                // We need to send PTO probe packets.
+                if p.recovery.loss_probes[epoch] > 0 {
+                    return Ok(packet::Type::from_epoch(epoch));
+                }
             }
         }
 
         // If there are flushable, almost full or blocked streams, use the
         // Application epoch.
+        let send_path = self.paths.get(send_pid)?;
         if (self.is_established() || self.is_in_early_data()) &&
-            ((self.is_server && !self.handshake_done_sent) ||
+            (self.should_send_handshake_done() ||
                 self.almost_full ||
                 self.blocked_limit.is_some() ||
                 self.dgram_send_queue.has_pending() ||
@@ -4491,9 +6533,14 @@
                 self.streams.has_almost_full() ||
                 self.streams.has_blocked() ||
                 self.streams.has_reset() ||
-                self.streams.has_stopped())
+                self.streams.has_stopped() ||
+                self.ids.has_new_scids() ||
+                self.ids.has_retire_dcids() ||
+                send_path.needs_ack_eliciting ||
+                send_path.probing_required())
         {
-            if self.is_in_early_data() && !self.is_server {
+            // Only clients can send 0-RTT packets.
+            if !self.is_server && self.is_in_early_data() {
                 return Ok(packet::Type::ZeroRTT);
             }
 
@@ -4519,7 +6566,8 @@
 
     /// Processes an incoming frame.
     fn process_frame(
-        &mut self, frame: frame::Frame, epoch: packet::Epoch, now: time::Instant,
+        &mut self, frame: frame::Frame, hdr: &packet::Header,
+        recv_path_id: usize, epoch: packet::Epoch, now: time::Instant,
     ) -> Result<()> {
         trace!("{} rx frm {:?}", self.trace_id, frame);
 
@@ -4528,44 +6576,49 @@
 
             frame::Frame::Ping => (),
 
-            frame::Frame::ACK { ranges, ack_delay } => {
+            frame::Frame::ACK {
+                ranges, ack_delay, ..
+            } => {
                 let ack_delay = ack_delay
                     .checked_mul(2_u64.pow(
                         self.peer_transport_params.ack_delay_exponent as u32,
                     ))
                     .ok_or(Error::InvalidFrame)?;
 
-                if epoch == packet::EPOCH_HANDSHAKE {
-                    self.peer_verified_address = true;
+                if epoch == packet::Epoch::Handshake ||
+                    (epoch == packet::Epoch::Application &&
+                        self.is_established())
+                {
+                    self.peer_verified_initial_address = true;
                 }
 
-                // When we receive an ACK for a 1-RTT packet after handshake
-                // completion, it means the handshake has been confirmed.
-                if epoch == packet::EPOCH_APPLICATION && self.is_established() {
-                    self.peer_verified_address = true;
+                let handshake_status = self.handshake_status();
 
-                    self.handshake_confirmed = true;
-                }
+                let is_app_limited = self.delivery_rate_check_if_app_limited();
 
-                self.recovery.on_ack_received(
-                    &ranges,
-                    ack_delay,
-                    epoch,
-                    self.handshake_status(),
-                    now,
-                    &self.trace_id,
-                )?;
+                for (_, p) in self.paths.iter_mut() {
+                    if is_app_limited {
+                        p.recovery.delivery_rate_update_app_limited(true);
+                    }
 
-                // Once the handshake is confirmed, we can drop Handshake keys.
-                if self.handshake_confirmed {
-                    self.drop_epoch_state(packet::EPOCH_HANDSHAKE, now);
+                    let (lost_packets, lost_bytes) = p.recovery.on_ack_received(
+                        &ranges,
+                        ack_delay,
+                        epoch,
+                        handshake_status,
+                        now,
+                        &self.trace_id,
+                    )?;
+
+                    self.lost_count += lost_packets;
+                    self.lost_bytes += lost_bytes as u64;
                 }
             },
 
             frame::Frame::ResetStream {
                 stream_id,
+                error_code,
                 final_size,
-                ..
             } => {
                 // Peer can't send on our unidirectional streams.
                 if !stream::is_bidi(stream_id) &&
@@ -4574,6 +6627,8 @@
                     return Err(Error::InvalidStreamState(stream_id));
                 }
 
+                let max_rx_data_left = self.max_rx_data() - self.rx_data;
+
                 // Get existing stream or create a new one, but if the stream
                 // has already been closed and collected, ignore the frame.
                 //
@@ -4592,11 +6647,20 @@
                     Err(e) => return Err(e),
                 };
 
-                self.rx_data += stream.recv.reset(final_size)? as u64;
+                let was_readable = stream.is_readable();
 
-                if self.rx_data > self.max_rx_data {
+                let max_off_delta =
+                    stream.recv.reset(error_code, final_size)? as u64;
+
+                if max_off_delta > max_rx_data_left {
                     return Err(Error::FlowControl);
                 }
+
+                if !was_readable && stream.is_readable() {
+                    self.streams.mark_readable(stream_id, true);
+                }
+
+                self.rx_data += max_off_delta;
             },
 
             frame::Frame::StopSending {
@@ -4631,7 +6695,18 @@
                 let was_writable = stream.is_writable();
 
                 // Try stopping the stream.
-                if let Ok(final_size) = stream.send.stop(error_code) {
+                if let Ok((final_size, unsent)) = stream.send.stop(error_code) {
+                    // Claw back some flow control allowance from data that was
+                    // buffered but not actually sent before the stream was
+                    // reset.
+                    //
+                    // Note that `tx_cap` will be updated later on, so no need
+                    // to touch it here.
+                    self.tx_data = self.tx_data.saturating_sub(unsent);
+
+                    self.tx_buffered =
+                        self.tx_buffered.saturating_sub(unsent as usize);
+
                     self.streams
                         .mark_reset(stream_id, true, error_code, final_size);
 
@@ -4655,17 +6730,10 @@
 
                 while let Ok((read, _)) = stream.recv.emit(&mut crypto_buf) {
                     let recv_buf = &crypto_buf[..read];
-                    self.handshake
-                        .lock()
-                        .unwrap()
-                        .provide_data(level, &recv_buf)?;
+                    self.handshake.provide_data(level, recv_buf)?;
                 }
 
-                if self.is_established() {
-                    self.handshake.lock().unwrap().process_post_handshake()?;
-                } else {
-                    self.do_handshake()?;
-                }
+                self.do_handshake(now)?;
             },
 
             frame::Frame::CryptoHeader { .. } => unreachable!(),
@@ -4681,7 +6749,7 @@
                     return Err(Error::InvalidStreamState(stream_id));
                 }
 
-                let max_rx_data_left = self.max_rx_data - self.rx_data;
+                let max_rx_data_left = self.max_rx_data() - self.rx_data;
 
                 // Get existing stream or create a new one, but if the stream
                 // has already been closed and collected, ignore the frame.
@@ -4709,13 +6777,29 @@
                     return Err(Error::FlowControl);
                 }
 
+                let was_readable = stream.is_readable();
+
+                let was_draining = stream.is_draining();
+
                 stream.recv.write(data)?;
 
-                if stream.is_readable() {
+                if !was_readable && stream.is_readable() {
                     self.streams.mark_readable(stream_id, true);
                 }
 
                 self.rx_data += max_off_delta;
+
+                if was_draining {
+                    // When a stream is in draining state it will not queue
+                    // incoming data for the application to read, so consider
+                    // the received data as consumed, which might trigger a flow
+                    // control update.
+                    self.flow_control.add_consumed(max_off_delta);
+
+                    if self.should_update_max_data() {
+                        self.almost_full = true;
+                    }
+                }
             },
 
             frame::Frame::StreamHeader { .. } => unreachable!(),
@@ -4799,17 +6883,81 @@
                     return Err(Error::InvalidFrame);
                 },
 
-            // TODO: implement connection migration
-            frame::Frame::NewConnectionId { .. } => (),
+            frame::Frame::NewConnectionId {
+                seq_num,
+                retire_prior_to,
+                conn_id,
+                reset_token,
+            } => {
+                if self.ids.zero_length_dcid() {
+                    return Err(Error::InvalidState);
+                }
 
-            // TODO: implement connection migration
-            frame::Frame::RetireConnectionId { .. } => (),
+                let retired_path_ids = self.ids.new_dcid(
+                    conn_id.into(),
+                    seq_num,
+                    u128::from_be_bytes(reset_token),
+                    retire_prior_to,
+                )?;
 
-            frame::Frame::PathChallenge { data } => {
-                self.challenge = Some(data);
+                for (dcid_seq, pid) in retired_path_ids {
+                    let path = self.paths.get_mut(pid)?;
+
+                    // Maybe the path already switched to another DCID.
+                    if path.active_dcid_seq != Some(dcid_seq) {
+                        continue;
+                    }
+
+                    if let Some(new_dcid_seq) =
+                        self.ids.lowest_available_dcid_seq()
+                    {
+                        path.active_dcid_seq = Some(new_dcid_seq);
+
+                        self.ids.link_dcid_to_path_id(new_dcid_seq, pid)?;
+
+                        trace!(
+                            "{} path ID {} changed DCID: old seq num {} new seq num {}",
+                            self.trace_id, pid, dcid_seq, new_dcid_seq,
+                        );
+                    } else {
+                        // We cannot use this path anymore for now.
+                        path.active_dcid_seq = None;
+
+                        trace!(
+                            "{} path ID {} cannot be used; DCID seq num {} has been retired",
+                            self.trace_id, pid, dcid_seq,
+                        );
+                    }
+                }
             },
 
-            frame::Frame::PathResponse { .. } => (),
+            frame::Frame::RetireConnectionId { seq_num } => {
+                if self.ids.zero_length_scid() {
+                    return Err(Error::InvalidState);
+                }
+
+                if let Some(pid) = self.ids.retire_scid(seq_num, &hdr.dcid)? {
+                    let path = self.paths.get_mut(pid)?;
+
+                    // Maybe we already linked a new SCID to that path.
+                    if path.active_scid_seq == Some(seq_num) {
+                        // XXX: We do not remove unused paths now, we instead
+                        // wait until we need to maintain more paths than the
+                        // host is willing to.
+                        path.active_scid_seq = None;
+                    }
+                }
+            },
+
+            frame::Frame::PathChallenge { data } => {
+                self.paths
+                    .get_mut(recv_path_id)?
+                    .on_challenge_received(data);
+            },
+
+            frame::Frame::PathResponse { data } => {
+                self.paths.on_response_received(data)?;
+            },
 
             frame::Frame::ConnectionClose {
                 error_code, reason, ..
@@ -4819,7 +6967,9 @@
                     error_code,
                     reason,
                 });
-                self.draining_timer = Some(now + (self.recovery.pto() * 3));
+
+                let path = self.paths.get_active()?;
+                self.draining_timer = Some(now + (path.recovery.pto() * 3));
             },
 
             frame::Frame::ApplicationClose { error_code, reason } => {
@@ -4828,7 +6978,9 @@
                     error_code,
                     reason,
                 });
-                self.draining_timer = Some(now + (self.recovery.pto() * 3));
+
+                let path = self.paths.get_active()?;
+                self.draining_timer = Some(now + (path.recovery.pto() * 3));
             },
 
             frame::Frame::HandshakeDone => {
@@ -4836,12 +6988,12 @@
                     return Err(Error::InvalidPacket);
                 }
 
-                self.peer_verified_address = true;
+                self.peer_verified_initial_address = true;
 
                 self.handshake_confirmed = true;
 
                 // Once the handshake is confirmed, we can drop Handshake keys.
-                self.drop_epoch_state(packet::EPOCH_HANDSHAKE, now);
+                self.drop_epoch_state(packet::Epoch::Handshake, now);
             },
 
             frame::Frame::Datagram { data } => {
@@ -4858,8 +7010,10 @@
                     self.dgram_recv_queue.pop();
                 }
 
-                self.dgram_recv_queue.push(&data)?;
+                self.dgram_recv_queue.push(data)?;
             },
+
+            frame::Frame::DatagramHeader { .. } => unreachable!(),
         }
 
         Ok(())
@@ -4875,11 +7029,11 @@
         self.pkt_num_spaces[epoch].crypto_seal = None;
         self.pkt_num_spaces[epoch].clear();
 
-        self.recovery.on_pkt_num_space_discarded(
-            epoch,
-            self.handshake_status(),
-            now,
-        );
+        let handshake_status = self.handshake_status();
+        for (_, p) in self.paths.iter_mut() {
+            p.recovery
+                .on_pkt_num_space_discarded(epoch, handshake_status, now);
+        }
 
         trace!("{} dropped epoch {} state", self.trace_id, epoch);
     }
@@ -4889,8 +7043,17 @@
     /// This happens when the new max data limit is at least double the amount
     /// of data that can be received before blocking.
     fn should_update_max_data(&self) -> bool {
-        self.max_rx_data_next != self.max_rx_data &&
-            self.max_rx_data_next / 2 > self.max_rx_data - self.rx_data
+        self.flow_control.should_update_max_data()
+    }
+
+    /// Returns the connection level flow control limit.
+    fn max_rx_data(&self) -> u64 {
+        self.flow_control.max_data()
+    }
+
+    /// Returns true if the HANDSHAKE_DONE frame needs to be sent.
+    fn should_send_handshake_done(&self) -> bool {
+        self.is_established() && !self.handshake_done_sent && self.is_server
     }
 
     /// Returns the idle timeout value.
@@ -4919,8 +7082,13 @@
             )
         };
 
+        let path_pto = match self.paths.get_active() {
+            Ok(p) => p.recovery.pto(),
+            Err(_) => time::Duration::ZERO,
+        };
+
         let idle_timeout = time::Duration::from_millis(idle_timeout);
-        let idle_timeout = cmp::max(idle_timeout, 3 * self.recovery.pto());
+        let idle_timeout = cmp::max(idle_timeout, 3 * path_pto);
 
         Some(idle_timeout)
     }
@@ -4928,14 +7096,243 @@
     /// Returns the connection's handshake status for use in loss recovery.
     fn handshake_status(&self) -> recovery::HandshakeStatus {
         recovery::HandshakeStatus {
-            has_handshake_keys: self.pkt_num_spaces[packet::EPOCH_HANDSHAKE]
+            has_handshake_keys: self.pkt_num_spaces[packet::Epoch::Handshake]
                 .has_keys(),
 
-            peer_verified_address: self.peer_verified_address,
+            peer_verified_address: self.peer_verified_initial_address,
 
             completed: self.is_established(),
         }
     }
+
+    /// Updates send capacity.
+    fn update_tx_cap(&mut self) {
+        let cwin_available = match self.paths.get_active() {
+            Ok(p) => p.recovery.cwnd_available() as u64,
+            Err(_) => 0,
+        };
+
+        self.tx_cap =
+            cmp::min(cwin_available, self.max_tx_data - self.tx_data) as usize;
+    }
+
+    fn delivery_rate_check_if_app_limited(&self) -> bool {
+        // Enter the app-limited phase of delivery rate when these conditions
+        // are met:
+        //
+        // - The remaining capacity is higher than available bytes in cwnd (there
+        //   is more room to send).
+        // - New data since the last send() is smaller than available bytes in
+        //   cwnd (we queued less than what we can send).
+        // - There is room to send more data in cwnd.
+        //
+        // In application-limited phases the transmission rate is limited by the
+        // application rather than the congestion control algorithm.
+        //
+        // Note that this is equivalent to CheckIfApplicationLimited() from the
+        // delivery rate draft. This is also separate from `recovery.app_limited`
+        // and only applies to delivery rate calculation.
+        let cwin_available = self
+            .paths
+            .iter()
+            .filter_map(|(_, p)| p.active().then(|| p.recovery.cwnd_available()))
+            .sum();
+
+        ((self.tx_buffered + self.dgram_send_queue_len()) < cwin_available) &&
+            (self.tx_data.saturating_sub(self.last_tx_data)) <
+                cwin_available as u64 &&
+            cwin_available > 0
+    }
+
+    fn set_initial_dcid(
+        &mut self, cid: ConnectionId<'static>, reset_token: Option<u128>,
+        path_id: usize,
+    ) -> Result<()> {
+        self.ids.set_initial_dcid(cid, reset_token, Some(path_id));
+        self.paths.get_mut(path_id)?.active_dcid_seq = Some(0);
+
+        Ok(())
+    }
+
+    /// Selects the path that the incoming packet belongs to, or creates a new
+    /// one if no existing path matches.
+    fn get_or_create_recv_path_id(
+        &mut self, recv_pid: Option<usize>, dcid: &ConnectionId, buf_len: usize,
+        info: &RecvInfo,
+    ) -> Result<usize> {
+        let ids = &mut self.ids;
+
+        let (in_scid_seq, mut in_scid_pid) =
+            ids.find_scid_seq(dcid).ok_or(Error::InvalidState)?;
+
+        if let Some(recv_pid) = recv_pid {
+            // If the path observes a change of SCID used, note it.
+            let recv_path = self.paths.get_mut(recv_pid)?;
+
+            let cid_entry =
+                recv_path.active_scid_seq.and_then(|v| ids.get_scid(v).ok());
+
+            if cid_entry.map(|e| &e.cid) != Some(dcid) {
+                let incoming_cid_entry = ids.get_scid(in_scid_seq)?;
+
+                let prev_recv_pid =
+                    incoming_cid_entry.path_id.unwrap_or(recv_pid);
+
+                if prev_recv_pid != recv_pid {
+                    trace!(
+                        "{} peer reused CID {:?} from path {} on path {}",
+                        self.trace_id,
+                        dcid,
+                        prev_recv_pid,
+                        recv_pid
+                    );
+
+                    // TODO: reset congestion control.
+                }
+
+                trace!(
+                    "{} path ID {} now see SCID with seq num {}",
+                    self.trace_id,
+                    recv_pid,
+                    in_scid_seq
+                );
+
+                recv_path.active_scid_seq = Some(in_scid_seq);
+                ids.link_scid_to_path_id(in_scid_seq, recv_pid)?;
+            }
+
+            return Ok(recv_pid);
+        }
+
+        // This is a new 4-tuple. See if the CID has not been assigned on
+        // another path.
+
+        // Ignore this step if are using zero-length SCID.
+        if ids.zero_length_scid() {
+            in_scid_pid = None;
+        }
+
+        if let Some(in_scid_pid) = in_scid_pid {
+            // This CID has been used by another path. If we have the
+            // room to do so, create a new `Path` structure holding this
+            // new 4-tuple. Otherwise, drop the packet.
+            let old_path = self.paths.get_mut(in_scid_pid)?;
+            let old_local_addr = old_path.local_addr();
+            let old_peer_addr = old_path.peer_addr();
+
+            trace!(
+                "{} reused CID seq {} of ({},{}) (path {}) on ({},{})",
+                self.trace_id,
+                in_scid_seq,
+                old_local_addr,
+                old_peer_addr,
+                in_scid_pid,
+                info.to,
+                info.from
+            );
+
+            // Notify the application.
+            self.paths
+                .notify_event(path::PathEvent::ReusedSourceConnectionId(
+                    in_scid_seq,
+                    (old_local_addr, old_peer_addr),
+                    (info.to, info.from),
+                ));
+        }
+
+        // This is a new path using an unassigned CID; create it!
+        let mut path =
+            path::Path::new(info.to, info.from, &self.recovery_config, false);
+
+        path.max_send_bytes = buf_len * MAX_AMPLIFICATION_FACTOR;
+        path.active_scid_seq = Some(in_scid_seq);
+
+        // Automatically probes the new path.
+        path.request_validation();
+
+        let pid = self.paths.insert_path(path, self.is_server)?;
+
+        // Do not record path reuse.
+        if in_scid_pid.is_none() {
+            ids.link_scid_to_path_id(in_scid_seq, pid)?;
+        }
+
+        Ok(pid)
+    }
+
+    /// Selects the path on which the next packet must be sent.
+    fn get_send_path_id(
+        &self, from: Option<SocketAddr>, to: Option<SocketAddr>,
+    ) -> Result<usize> {
+        // A probing packet must be sent, but only if the connection is fully
+        // established.
+        if self.is_established() {
+            let mut probing = self
+                .paths
+                .iter()
+                .filter(|(_, p)| from.is_none() || Some(p.local_addr()) == from)
+                .filter(|(_, p)| to.is_none() || Some(p.peer_addr()) == to)
+                .filter(|(_, p)| p.active_dcid_seq.is_some())
+                .filter(|(_, p)| p.probing_required())
+                .map(|(pid, _)| pid);
+
+            if let Some(pid) = probing.next() {
+                return Ok(pid);
+            }
+        }
+
+        if let Some((pid, p)) = self.paths.get_active_with_pid() {
+            if from.is_some() && Some(p.local_addr()) != from {
+                return Err(Error::Done);
+            }
+
+            if to.is_some() && Some(p.peer_addr()) != to {
+                return Err(Error::Done);
+            }
+
+            return Ok(pid);
+        };
+
+        Err(Error::InvalidState)
+    }
+
+    /// Creates a new client-side path.
+    fn create_path_on_client(
+        &mut self, local_addr: SocketAddr, peer_addr: SocketAddr,
+    ) -> Result<usize> {
+        if self.is_server {
+            return Err(Error::InvalidState);
+        }
+
+        // If we use zero-length SCID and go over our local active CID limit,
+        // the `insert_path()` call will raise an error.
+        if !self.ids.zero_length_scid() && self.ids.available_scids() == 0 {
+            return Err(Error::OutOfIdentifiers);
+        }
+
+        // Do we have a spare DCID? If we are using zero-length DCID, just use
+        // the default having sequence 0 (note that if we exceed our local CID
+        // limit, the `insert_path()` call will raise an error.
+        let dcid_seq = if self.ids.zero_length_dcid() {
+            0
+        } else {
+            self.ids
+                .lowest_available_dcid_seq()
+                .ok_or(Error::OutOfIdentifiers)?
+        };
+
+        let mut path =
+            path::Path::new(local_addr, peer_addr, &self.recovery_config, false);
+        path.active_dcid_seq = Some(dcid_seq);
+
+        let pid = self
+            .paths
+            .insert_path(path, false)
+            .map_err(|_| Error::OutOfIdentifiers)?;
+        self.ids.link_dcid_to_path_id(dcid_seq, pid)?;
+
+        Ok(pid)
+    }
 }
 
 /// Maps an `Error` to `Error::Done`, or itself.
@@ -4959,7 +7356,7 @@
 fn drop_pkt_on_err(
     e: Error, recv_count: usize, is_server: bool, trace_id: &str,
 ) -> Error {
-    // On the server, if no other packet has been successflully processed, abort
+    // On the server, if no other packet has been successfully processed, abort
     // the connection to avoid keeping the connection open when only junk is
     // received.
     if is_server && recv_count == 0 {
@@ -4973,12 +7370,26 @@
     Error::Done
 }
 
+struct AddrTupleFmt(SocketAddr, SocketAddr);
+
+impl std::fmt::Display for AddrTupleFmt {
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        let AddrTupleFmt(src, dst) = &self;
+
+        if src.ip().is_unspecified() || dst.ip().is_unspecified() {
+            return Ok(());
+        }
+
+        f.write_fmt(format_args!("src:{src} dst:{dst}"))
+    }
+}
+
 /// Statistics about the connection.
 ///
 /// A connection's statistics can be collected using the [`stats()`] method.
 ///
 /// [`stats()`]: struct.Connection.html#method.stats
-#[derive(Clone)]
+#[derive(Clone, Default)]
 pub struct Stats {
     /// The number of QUIC packets received.
     pub recv: usize,
@@ -4989,14 +7400,62 @@
     /// The number of QUIC packets that were lost.
     pub lost: usize,
 
-    /// The estimated round-trip time of the connection.
-    pub rtt: time::Duration,
+    /// The number of sent QUIC packets with retransmitted data.
+    pub retrans: usize,
 
-    /// The size of the connection's congestion window in bytes.
-    pub cwnd: usize,
+    /// The number of sent bytes.
+    pub sent_bytes: u64,
 
-    /// The most recent data delivery rate estimate in bytes/s.
-    pub delivery_rate: u64,
+    /// The number of received bytes.
+    pub recv_bytes: u64,
+
+    /// The number of bytes sent lost.
+    pub lost_bytes: u64,
+
+    /// The number of stream bytes retransmitted.
+    pub stream_retrans_bytes: u64,
+
+    /// The number of known paths for the connection.
+    pub paths_count: usize,
+
+    /// The maximum idle timeout.
+    pub peer_max_idle_timeout: u64,
+
+    /// The maximum UDP payload size.
+    pub peer_max_udp_payload_size: u64,
+
+    /// The initial flow control maximum data for the connection.
+    pub peer_initial_max_data: u64,
+
+    /// The initial flow control maximum data for local bidirectional streams.
+    pub peer_initial_max_stream_data_bidi_local: u64,
+
+    /// The initial flow control maximum data for remote bidirectional streams.
+    pub peer_initial_max_stream_data_bidi_remote: u64,
+
+    /// The initial flow control maximum data for unidirectional streams.
+    pub peer_initial_max_stream_data_uni: u64,
+
+    /// The initial maximum bidirectional streams.
+    pub peer_initial_max_streams_bidi: u64,
+
+    /// The initial maximum unidirectional streams.
+    pub peer_initial_max_streams_uni: u64,
+
+    /// The ACK delay exponent.
+    pub peer_ack_delay_exponent: u64,
+
+    /// The max ACK delay.
+    pub peer_max_ack_delay: u64,
+
+    /// Whether active migration is disabled.
+    pub peer_disable_active_migration: bool,
+
+    /// The active connection ID limit.
+    pub peer_active_conn_id_limit: u64,
+
+    /// DATAGRAM frame extension parameter, if any.
+    pub peer_max_datagram_frame_size: Option<u64>,
 }
 
 impl std::fmt::Debug for Stats {
@@ -5004,9 +7463,81 @@
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         write!(
             f,
-            "recv={} sent={} lost={} rtt={:?} cwnd={}",
-            self.recv, self.sent, self.lost, self.rtt, self.cwnd,
-        )
+            "recv={} sent={} lost={} retrans={}",
+            self.recv, self.sent, self.lost, self.retrans,
+        )?;
+
+        write!(
+            f,
+            " sent_bytes={} recv_bytes={} lost_bytes={}",
+            self.sent_bytes, self.recv_bytes, self.lost_bytes,
+        )?;
+
+        write!(f, " peer_tps={{")?;
+
+        write!(f, " max_idle_timeout={},", self.peer_max_idle_timeout)?;
+
+        write!(
+            f,
+            " max_udp_payload_size={},",
+            self.peer_max_udp_payload_size,
+        )?;
+
+        write!(f, " initial_max_data={},", self.peer_initial_max_data)?;
+
+        write!(
+            f,
+            " initial_max_stream_data_bidi_local={},",
+            self.peer_initial_max_stream_data_bidi_local,
+        )?;
+
+        write!(
+            f,
+            " initial_max_stream_data_bidi_remote={},",
+            self.peer_initial_max_stream_data_bidi_remote,
+        )?;
+
+        write!(
+            f,
+            " initial_max_stream_data_uni={},",
+            self.peer_initial_max_stream_data_uni,
+        )?;
+
+        write!(
+            f,
+            " initial_max_streams_bidi={},",
+            self.peer_initial_max_streams_bidi,
+        )?;
+
+        write!(
+            f,
+            " initial_max_streams_uni={},",
+            self.peer_initial_max_streams_uni,
+        )?;
+
+        write!(f, " ack_delay_exponent={},", self.peer_ack_delay_exponent)?;
+
+        write!(f, " max_ack_delay={},", self.peer_max_ack_delay)?;
+
+        write!(
+            f,
+            " disable_active_migration={},",
+            self.peer_disable_active_migration,
+        )?;
+
+        write!(
+            f,
+            " active_conn_id_limit={},",
+            self.peer_active_conn_id_limit,
+        )?;
+
+        write!(
+            f,
+            " max_datagram_frame_size={:?}",
+            self.peer_max_datagram_frame_size,
+        )?;
+
+        write!(f, "}}")
     }
 }
 
@@ -5014,7 +7545,7 @@
 struct TransportParams {
     pub original_destination_connection_id: Option<ConnectionId<'static>>,
     pub max_idle_timeout: u64,
-    pub stateless_reset_token: Option<Vec<u8>>,
+    pub stateless_reset_token: Option<u128>,
     pub max_udp_payload_size: u64,
     pub initial_max_data: u64,
     pub initial_max_stream_data_bidi_local: u64,
@@ -5059,15 +7590,19 @@
 impl TransportParams {
     fn decode(buf: &[u8], is_server: bool) -> Result<TransportParams> {
         let mut params = octets::Octets::with_slice(buf);
+        let mut seen_params = HashSet::new();
 
         let mut tp = TransportParams::default();
 
         while params.cap() > 0 {
             let id = params.get_varint()?;
 
-            let mut val = params.get_bytes_with_varint_length()?;
+            if seen_params.contains(&id) {
+                return Err(Error::InvalidTransportParam);
+            }
+            seen_params.insert(id);
 
-            // TODO: forbid duplicated param
+            let mut val = params.get_bytes_with_varint_length()?;
 
             match id {
                 0x0000 => {
@@ -5088,7 +7623,12 @@
                         return Err(Error::InvalidTransportParam);
                     }
 
-                    tp.stateless_reset_token = Some(val.get_bytes(16)?.to_vec());
+                    tp.stateless_reset_token = Some(u128::from_be_bytes(
+                        val.get_bytes(16)?
+                            .to_vec()
+                            .try_into()
+                            .map_err(|_| Error::BufferTooShort)?,
+                    ));
                 },
 
                 0x0003 => {
@@ -5218,7 +7758,7 @@
         if is_server {
             if let Some(ref odcid) = tp.original_destination_connection_id {
                 TransportParams::encode_param(&mut b, 0x0000, odcid.len())?;
-                b.put_bytes(&odcid)?;
+                b.put_bytes(odcid)?;
             }
         };
 
@@ -5233,8 +7773,8 @@
 
         if is_server {
             if let Some(ref token) = tp.stateless_reset_token {
-                TransportParams::encode_param(&mut b, 0x0002, token.len())?;
-                b.put_bytes(&token)?;
+                TransportParams::encode_param(&mut b, 0x0002, 16)?;
+                b.put_bytes(&token.to_be_bytes())?;
             }
         }
 
@@ -5336,13 +7876,13 @@
 
         if let Some(scid) = &tp.initial_source_connection_id {
             TransportParams::encode_param(&mut b, 0x000f, scid.len())?;
-            b.put_bytes(&scid)?;
+            b.put_bytes(scid)?;
         }
 
         if is_server {
             if let Some(scid) = &tp.retry_source_connection_id {
                 TransportParams::encode_param(&mut b, 0x0010, scid.len())?;
-                b.put_bytes(&scid)?;
+                b.put_bytes(scid)?;
             }
         }
 
@@ -5363,37 +7903,51 @@
     /// Creates a qlog event for connection transport parameters and TLS fields
     #[cfg(feature = "qlog")]
     pub fn to_qlog(
-        &self, owner: qlog::TransportOwner, version: u32, alpn: &[u8],
-        cipher: Option<crypto::Algorithm>,
-    ) -> qlog::event::Event {
-        let ocid = qlog::HexSlice::maybe_string(
+        &self, owner: TransportOwner, cipher: Option<crypto::Algorithm>,
+    ) -> EventData {
+        let original_destination_connection_id = qlog::HexSlice::maybe_string(
             self.original_destination_connection_id.as_ref(),
         );
-        let stateless_reset_token =
-            qlog::HexSlice::maybe_string(self.stateless_reset_token.as_ref());
 
-        qlog::event::Event::transport_parameters_set(
-            Some(owner),
-            None, // resumption
-            None, // early data
-            String::from_utf8(alpn.to_vec()).ok(),
-            Some(format!("{:x?}", version)),
-            Some(format!("{:?}", cipher)),
-            ocid,
-            stateless_reset_token,
-            Some(self.disable_active_migration),
-            Some(self.max_idle_timeout),
-            Some(self.max_udp_payload_size),
-            Some(self.ack_delay_exponent),
-            Some(self.max_ack_delay),
-            Some(self.active_conn_id_limit),
-            Some(self.initial_max_data.to_string()),
-            Some(self.initial_max_stream_data_bidi_local.to_string()),
-            Some(self.initial_max_stream_data_bidi_remote.to_string()),
-            Some(self.initial_max_stream_data_uni.to_string()),
-            Some(self.initial_max_streams_bidi.to_string()),
-            Some(self.initial_max_streams_uni.to_string()),
-            None, // preferred address
+        let stateless_reset_token = qlog::HexSlice::maybe_string(
+            self.stateless_reset_token.map(|s| s.to_be_bytes()).as_ref(),
+        );
+
+        EventData::TransportParametersSet(
+            qlog::events::quic::TransportParametersSet {
+                owner: Some(owner),
+                resumption_allowed: None,
+                early_data_enabled: None,
+                tls_cipher: Some(format!("{cipher:?}")),
+                aead_tag_length: None,
+                original_destination_connection_id,
+                initial_source_connection_id: None,
+                retry_source_connection_id: None,
+                stateless_reset_token,
+                disable_active_migration: Some(self.disable_active_migration),
+                max_idle_timeout: Some(self.max_idle_timeout),
+                max_udp_payload_size: Some(self.max_udp_payload_size as u32),
+                ack_delay_exponent: Some(self.ack_delay_exponent as u16),
+                max_ack_delay: Some(self.max_ack_delay as u16),
+                active_connection_id_limit: Some(
+                    self.active_conn_id_limit as u32,
+                ),
+
+                initial_max_data: Some(self.initial_max_data),
+                initial_max_stream_data_bidi_local: Some(
+                    self.initial_max_stream_data_bidi_local,
+                ),
+                initial_max_stream_data_bidi_remote: Some(
+                    self.initial_max_stream_data_bidi_remote,
+                ),
+                initial_max_stream_data_uni: Some(
+                    self.initial_max_stream_data_uni,
+                ),
+                initial_max_streams_bidi: Some(self.initial_max_streams_bidi),
+                initial_max_streams_uni: Some(self.initial_max_streams_uni),
+
+                preferred_address: None,
+            },
         )
     }
 }
@@ -5403,16 +7957,16 @@
     use super::*;
 
     pub struct Pipe {
-        pub client: Pin<Box<Connection>>,
-        pub server: Pin<Box<Connection>>,
+        pub client: Connection,
+        pub server: Connection,
     }
 
     impl Pipe {
-        pub fn default() -> Result<Pipe> {
+        pub fn new() -> Result<Pipe> {
             let mut config = Config::new(crate::PROTOCOL_VERSION)?;
             config.load_cert_chain_from_pem_file("examples/cert.crt")?;
             config.load_priv_key_from_pem_file("examples/cert.key")?;
-            config.set_application_protos(b"\x06proto1\x06proto2")?;
+            config.set_application_protos(&[b"proto1", b"proto2"])?;
             config.set_initial_max_data(30);
             config.set_initial_max_stream_data_bidi_local(15);
             config.set_initial_max_stream_data_bidi_remote(15);
@@ -5421,30 +7975,76 @@
             config.set_initial_max_streams_uni(3);
             config.set_max_idle_timeout(180_000);
             config.verify_peer(false);
-            config.set_ack_delay_exponent(5);
+            config.set_ack_delay_exponent(8);
 
             Pipe::with_config(&mut config)
         }
 
+        pub fn client_addr() -> SocketAddr {
+            "127.0.0.1:1234".parse().unwrap()
+        }
+
+        pub fn server_addr() -> SocketAddr {
+            "127.0.0.1:4321".parse().unwrap()
+        }
+
         pub fn with_config(config: &mut Config) -> Result<Pipe> {
             let mut client_scid = [0; 16];
             rand::rand_bytes(&mut client_scid[..]);
             let client_scid = ConnectionId::from_ref(&client_scid);
-            let client_addr = "127.0.0.1:1234".parse().unwrap();
+            let client_addr = Pipe::client_addr();
 
             let mut server_scid = [0; 16];
             rand::rand_bytes(&mut server_scid[..]);
             let server_scid = ConnectionId::from_ref(&server_scid);
-            let server_addr = "127.0.0.1:4321".parse().unwrap();
+            let server_addr = Pipe::server_addr();
 
             Ok(Pipe {
                 client: connect(
                     Some("quic.tech"),
                     &client_scid,
                     client_addr,
+                    server_addr,
                     config,
                 )?,
-                server: accept(&server_scid, None, server_addr, config)?,
+                server: accept(
+                    &server_scid,
+                    None,
+                    server_addr,
+                    client_addr,
+                    config,
+                )?,
+            })
+        }
+
+        pub fn with_config_and_scid_lengths(
+            config: &mut Config, client_scid_len: usize, server_scid_len: usize,
+        ) -> Result<Pipe> {
+            let mut client_scid = vec![0; client_scid_len];
+            rand::rand_bytes(&mut client_scid[..]);
+            let client_scid = ConnectionId::from_ref(&client_scid);
+            let client_addr = Pipe::client_addr();
+
+            let mut server_scid = vec![0; server_scid_len];
+            rand::rand_bytes(&mut server_scid[..]);
+            let server_scid = ConnectionId::from_ref(&server_scid);
+            let server_addr = Pipe::server_addr();
+
+            Ok(Pipe {
+                client: connect(
+                    Some("quic.tech"),
+                    &client_scid,
+                    client_addr,
+                    server_addr,
+                    config,
+                )?,
+                server: accept(
+                    &server_scid,
+                    None,
+                    server_addr,
+                    client_addr,
+                    config,
+                )?,
             })
         }
 
@@ -5452,31 +8052,39 @@
             let mut client_scid = [0; 16];
             rand::rand_bytes(&mut client_scid[..]);
             let client_scid = ConnectionId::from_ref(&client_scid);
-            let client_addr = "127.0.0.1:1234".parse().unwrap();
+            let client_addr = Pipe::client_addr();
 
             let mut server_scid = [0; 16];
             rand::rand_bytes(&mut server_scid[..]);
             let server_scid = ConnectionId::from_ref(&server_scid);
-            let server_addr = "127.0.0.1:4321".parse().unwrap();
+            let server_addr = Pipe::server_addr();
 
             let mut config = Config::new(crate::PROTOCOL_VERSION)?;
             config.load_cert_chain_from_pem_file("examples/cert.crt")?;
             config.load_priv_key_from_pem_file("examples/cert.key")?;
-            config.set_application_protos(b"\x06proto1\x06proto2")?;
+            config.set_application_protos(&[b"proto1", b"proto2"])?;
             config.set_initial_max_data(30);
             config.set_initial_max_stream_data_bidi_local(15);
             config.set_initial_max_stream_data_bidi_remote(15);
             config.set_initial_max_streams_bidi(3);
             config.set_initial_max_streams_uni(3);
+            config.set_ack_delay_exponent(8);
 
             Ok(Pipe {
                 client: connect(
                     Some("quic.tech"),
                     &client_scid,
                     client_addr,
+                    server_addr,
                     client_config,
                 )?,
-                server: accept(&server_scid, None, server_addr, &mut config)?,
+                server: accept(
+                    &server_scid,
+                    None,
+                    server_addr,
+                    client_addr,
+                    &mut config,
+                )?,
             })
         }
 
@@ -5484,29 +8092,37 @@
             let mut client_scid = [0; 16];
             rand::rand_bytes(&mut client_scid[..]);
             let client_scid = ConnectionId::from_ref(&client_scid);
-            let client_addr = "127.0.0.1:1234".parse().unwrap();
+            let client_addr = Pipe::client_addr();
 
             let mut server_scid = [0; 16];
             rand::rand_bytes(&mut server_scid[..]);
             let server_scid = ConnectionId::from_ref(&server_scid);
-            let server_addr = "127.0.0.1:4321".parse().unwrap();
+            let server_addr = Pipe::server_addr();
 
             let mut config = Config::new(crate::PROTOCOL_VERSION)?;
-            config.set_application_protos(b"\x06proto1\x06proto2")?;
+            config.set_application_protos(&[b"proto1", b"proto2"])?;
             config.set_initial_max_data(30);
             config.set_initial_max_stream_data_bidi_local(15);
             config.set_initial_max_stream_data_bidi_remote(15);
             config.set_initial_max_streams_bidi(3);
             config.set_initial_max_streams_uni(3);
+            config.set_ack_delay_exponent(8);
 
             Ok(Pipe {
                 client: connect(
                     Some("quic.tech"),
                     &client_scid,
                     client_addr,
+                    server_addr,
                     &mut config,
                 )?,
-                server: accept(&server_scid, None, server_addr, server_config)?,
+                server: accept(
+                    &server_scid,
+                    None,
+                    server_addr,
+                    client_addr,
+                    server_config,
+                )?,
             })
         }
 
@@ -5548,16 +8164,20 @@
         }
 
         pub fn client_recv(&mut self, buf: &mut [u8]) -> Result<usize> {
+            let server_path = &self.server.paths.get_active().unwrap();
             let info = RecvInfo {
-                from: self.client.peer_addr,
+                to: server_path.peer_addr(),
+                from: server_path.local_addr(),
             };
 
             self.client.recv(buf, info)
         }
 
         pub fn server_recv(&mut self, buf: &mut [u8]) -> Result<usize> {
+            let client_path = &self.client.paths.get_active().unwrap();
             let info = RecvInfo {
-                from: self.server.peer_addr,
+                to: client_path.peer_addr(),
+                from: client_path.local_addr(),
             };
 
             self.server.recv(buf, info)
@@ -5570,13 +8190,47 @@
             let written = encode_pkt(&mut self.client, pkt_type, frames, buf)?;
             recv_send(&mut self.server, buf, written)
         }
+
+        pub fn client_update_key(&mut self) -> Result<()> {
+            let space =
+                &mut self.client.pkt_num_spaces[packet::Epoch::Application];
+
+            let open_next = space
+                .crypto_open
+                .as_ref()
+                .unwrap()
+                .derive_next_packet_key()
+                .unwrap();
+
+            let seal_next = space
+                .crypto_seal
+                .as_ref()
+                .unwrap()
+                .derive_next_packet_key()?;
+
+            let open_prev = space.crypto_open.replace(open_next);
+            space.crypto_seal.replace(seal_next);
+
+            space.key_update = Some(packet::KeyUpdate {
+                crypto_open: open_prev.unwrap(),
+                pn_on_update: space.next_pkt_num,
+                update_acked: true,
+                timer: time::Instant::now(),
+            });
+
+            self.client.key_phase = !self.client.key_phase;
+
+            Ok(())
+        }
     }
 
     pub fn recv_send(
         conn: &mut Connection, buf: &mut [u8], len: usize,
     ) -> Result<usize> {
+        let active_path = conn.paths.get_active()?;
         let info = RecvInfo {
-            from: conn.peer_addr,
+            to: active_path.local_addr(),
+            from: active_path.peer_addr(),
         };
 
         conn.recv(&mut buf[..len], info)?;
@@ -5595,11 +8249,12 @@
     }
 
     pub fn process_flight(
-        conn: &mut Connection, flight: Vec<Vec<u8>>,
+        conn: &mut Connection, flight: Vec<(Vec<u8>, SendInfo)>,
     ) -> Result<()> {
-        for mut pkt in flight {
+        for (mut pkt, si) in flight {
             let info = RecvInfo {
-                from: conn.peer_addr,
+                to: si.to,
+                from: si.from,
             };
 
             conn.recv(&mut pkt, info)?;
@@ -5608,21 +8263,26 @@
         Ok(())
     }
 
-    pub fn emit_flight(conn: &mut Connection) -> Result<Vec<Vec<u8>>> {
+    pub fn emit_flight_with_max_buffer(
+        conn: &mut Connection, out_size: usize,
+    ) -> Result<Vec<(Vec<u8>, SendInfo)>> {
         let mut flight = Vec::new();
 
         loop {
-            let mut out = vec![0u8; 65535];
+            let mut out = vec![0u8; out_size];
 
-            match conn.send(&mut out) {
-                Ok((written, _)) => out.truncate(written),
+            let info = match conn.send(&mut out) {
+                Ok((written, info)) => {
+                    out.truncate(written);
+                    info
+                },
 
                 Err(Error::Done) => break,
 
                 Err(e) => return Err(e),
             };
 
-            flight.push(out);
+            flight.push((out, info));
         }
 
         if flight.is_empty() {
@@ -5632,6 +8292,12 @@
         Ok(flight)
     }
 
+    pub fn emit_flight(
+        conn: &mut Connection,
+    ) -> Result<Vec<(Vec<u8>, SendInfo)>> {
+        emit_flight_with_max_buffer(conn, 65535)
+    }
+
     pub fn encode_pkt(
         conn: &mut Connection, pkt_type: packet::Type, frames: &[frame::Frame],
         buf: &mut [u8],
@@ -5645,25 +8311,38 @@
         let pn = space.next_pkt_num;
         let pn_len = 4;
 
+        let send_path = conn.paths.get_active()?;
+        let active_dcid_seq = send_path
+            .active_dcid_seq
+            .as_ref()
+            .ok_or(Error::InvalidState)?;
+        let active_scid_seq = send_path
+            .active_scid_seq
+            .as_ref()
+            .ok_or(Error::InvalidState)?;
+
         let hdr = Header {
             ty: pkt_type,
             version: conn.version,
-            dcid: ConnectionId::from_ref(&conn.dcid),
-            scid: ConnectionId::from_ref(&conn.scid),
+            dcid: ConnectionId::from_ref(
+                conn.ids.get_dcid(*active_dcid_seq)?.cid.as_ref(),
+            ),
+            scid: ConnectionId::from_ref(
+                conn.ids.get_scid(*active_scid_seq)?.cid.as_ref(),
+            ),
             pkt_num: 0,
             pkt_num_len: pn_len,
             token: conn.token.clone(),
             versions: None,
-            key_phase: false,
+            key_phase: conn.key_phase,
         };
 
         hdr.to_bytes(&mut b)?;
 
-        let payload_len = frames.iter().fold(0, |acc, x| acc + x.wire_len()) +
-            space.crypto_overhead().unwrap();
+        let payload_len = frames.iter().fold(0, |acc, x| acc + x.wire_len());
 
         if pkt_type != packet::Type::Short {
-            let len = pn_len + payload_len;
+            let len = pn_len + payload_len + space.crypto_overhead().unwrap();
             b.put_varint(len as u64)?;
         }
 
@@ -5688,6 +8367,7 @@
             pn_len,
             payload_len,
             payload_offset,
+            None,
             aead,
         )?;
 
@@ -5701,7 +8381,7 @@
     ) -> Result<Vec<frame::Frame>> {
         let mut b = octets::OctetsMut::with_slice(&mut buf[..len]);
 
-        let mut hdr = Header::from_bytes(&mut b, conn.scid.len()).unwrap();
+        let mut hdr = Header::from_bytes(&mut b, conn.source_id().len()).unwrap();
 
         let epoch = hdr.ty.to_epoch()?;
 
@@ -5709,7 +8389,7 @@
 
         let payload_len = b.cap();
 
-        packet::decrypt_hdr(&mut b, &mut hdr, &aead).unwrap();
+        packet::decrypt_hdr(&mut b, &mut hdr, aead).unwrap();
 
         let pn = packet::decode_pkt_num(
             conn.pkt_num_spaces[epoch].largest_rx_pkt_num,
@@ -5730,6 +8410,20 @@
 
         Ok(frames)
     }
+
+    pub fn create_cid_and_reset_token(
+        cid_len: usize,
+    ) -> (ConnectionId<'static>, u128) {
+        let mut cid = vec![0; cid_len];
+        rand::rand_bytes(&mut cid[..]);
+        let cid = ConnectionId::from_ref(&cid).into_owned();
+
+        let mut reset_token = [0; 16];
+        rand::rand_bytes(&mut reset_token);
+        let reset_token = u128::from_be_bytes(reset_token);
+
+        (cid, reset_token)
+    }
 }
 
 #[cfg(test)]
@@ -5742,7 +8436,7 @@
         let tp = TransportParams {
             original_destination_connection_id: None,
             max_idle_timeout: 30,
-            stateless_reset_token: Some(vec![0xba; 16]),
+            stateless_reset_token: Some(u128::from_be_bytes([0xba; 16])),
             max_udp_payload_size: 23_421,
             initial_max_data: 424_645_563,
             initial_max_stream_data_bidi_local: 154_323_123,
@@ -5764,7 +8458,7 @@
             TransportParams::encode(&tp, true, &mut raw_params).unwrap();
         assert_eq!(raw_params.len(), 94);
 
-        let new_tp = TransportParams::decode(&raw_params, false).unwrap();
+        let new_tp = TransportParams::decode(raw_params, false).unwrap();
 
         assert_eq!(new_tp, tp);
 
@@ -5794,16 +8488,51 @@
             TransportParams::encode(&tp, false, &mut raw_params).unwrap();
         assert_eq!(raw_params.len(), 69);
 
-        let new_tp = TransportParams::decode(&raw_params, true).unwrap();
+        let new_tp = TransportParams::decode(raw_params, true).unwrap();
 
         assert_eq!(new_tp, tp);
     }
 
     #[test]
+    fn transport_params_forbid_duplicates() {
+        // Given an encoded param.
+        let initial_source_connection_id = b"id";
+        let initial_source_connection_id_raw = [
+            15,
+            initial_source_connection_id.len() as u8,
+            initial_source_connection_id[0],
+            initial_source_connection_id[1],
+        ];
+
+        // No error when decoding the param.
+        let tp = TransportParams::decode(
+            initial_source_connection_id_raw.as_slice(),
+            true,
+        )
+        .unwrap();
+
+        assert_eq!(
+            tp.initial_source_connection_id,
+            Some(initial_source_connection_id.to_vec().into())
+        );
+
+        // Duplicate the param.
+        let mut raw_params = Vec::new();
+        raw_params.append(&mut initial_source_connection_id_raw.to_vec());
+        raw_params.append(&mut initial_source_connection_id_raw.to_vec());
+
+        // Decoding fails.
+        assert_eq!(
+            TransportParams::decode(raw_params.as_slice(), true),
+            Err(Error::InvalidTransportParam)
+        );
+    }
+
+    #[test]
     fn unknown_version() {
         let mut config = Config::new(0xbabababa).unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.verify_peer(false);
 
@@ -5831,7 +8560,7 @@
 
         let mut config = Config::new(0xbabababa).unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.verify_peer(false);
 
@@ -5858,7 +8587,7 @@
             .load_verify_locations_from_file("examples/rootca.crt")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
 
         let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
@@ -5869,7 +8598,7 @@
     fn missing_initial_source_connection_id() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Reset initial_source_connection_id.
         pipe.client
@@ -5891,7 +8620,7 @@
     fn invalid_initial_source_connection_id() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Scramble initial_source_connection_id.
         pipe.client
@@ -5911,26 +8640,24 @@
 
     #[test]
     fn handshake() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(
             pipe.client.application_proto(),
             pipe.server.application_proto()
         );
+
+        assert_eq!(pipe.server.server_name(), Some("quic.tech"));
     }
 
     #[test]
     fn handshake_done() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Disable session tickets on the server (SSL_OP_NO_TICKET) to avoid
         // triggering 1-RTT packet send with a CRYPTO frame.
-        pipe.server
-            .handshake
-            .lock()
-            .unwrap()
-            .set_options(0x0000_4000);
+        pipe.server.handshake.set_options(0x0000_4000);
 
         assert_eq!(pipe.handshake(), Ok(()));
 
@@ -5939,7 +8666,7 @@
 
     #[test]
     fn handshake_confirmation() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Client sends initial flight.
         let flight = testing::emit_flight(&mut pipe.client).unwrap();
@@ -5967,14 +8694,14 @@
 
         testing::process_flight(&mut pipe.server, flight).unwrap();
 
-        // Server completes handshake and sends HANDSHAKE_DONE.
+        // Server completes and confirms handshake, and sends HANDSHAKE_DONE.
         let flight = testing::emit_flight(&mut pipe.server).unwrap();
 
         assert!(pipe.client.is_established());
         assert!(!pipe.client.handshake_confirmed);
 
         assert!(pipe.server.is_established());
-        assert!(!pipe.server.handshake_confirmed);
+        assert!(pipe.server.handshake_confirmed);
 
         testing::process_flight(&mut pipe.client, flight).unwrap();
 
@@ -5985,11 +8712,10 @@
         assert!(pipe.client.handshake_confirmed);
 
         assert!(pipe.server.is_established());
-        assert!(!pipe.server.handshake_confirmed);
+        assert!(pipe.server.handshake_confirmed);
 
         testing::process_flight(&mut pipe.server, flight).unwrap();
 
-        // Server handshake is confirmed.
         assert!(pipe.client.is_established());
         assert!(pipe.client.handshake_confirmed);
 
@@ -6009,7 +8735,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -6039,7 +8765,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -6049,7 +8775,7 @@
 
         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
 
-        assert_eq!(pipe.client.set_session(&session), Ok(()));
+        assert_eq!(pipe.client.set_session(session), Ok(()));
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.client.is_established(), true);
@@ -6065,7 +8791,7 @@
 
         let mut config = Config::new(PROTOCOL_VERSION).unwrap();
         config
-            .set_application_protos(b"\x06proto3\x06proto4")
+            .set_application_protos(&[b"proto3\x06proto4"])
             .unwrap();
         config.verify_peer(false);
 
@@ -6095,7 +8821,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -6113,7 +8839,7 @@
 
         // Configure session on new connection.
         let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
-        assert_eq!(pipe.client.set_session(&session), Ok(()));
+        assert_eq!(pipe.client.set_session(session), Ok(()));
 
         // Client sends initial flight.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
@@ -6156,7 +8882,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -6174,11 +8900,11 @@
 
         // Configure session on new connection.
         let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
-        assert_eq!(pipe.client.set_session(&session), Ok(()));
+        assert_eq!(pipe.client.set_session(session), Ok(()));
 
         // Client sends initial flight.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
-        let mut initial = (&buf[..len]).to_vec();
+        let mut initial = buf[..len].to_vec();
 
         // Client sends 0-RTT packet.
         let pkt_type = packet::Type::ZeroRTT;
@@ -6191,7 +8917,7 @@
         let len =
             testing::encode_pkt(&mut pipe.client, pkt_type, &frames, &mut buf)
                 .unwrap();
-        let mut zrtt = (&buf[..len]).to_vec();
+        let mut zrtt = buf[..len].to_vec();
 
         // 0-RTT packet is received before the Initial one.
         assert_eq!(pipe.server_recv(&mut zrtt), Ok(zrtt.len()));
@@ -6227,7 +8953,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -6245,7 +8971,7 @@
 
         // Configure session on new connection.
         let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
-        assert_eq!(pipe.client.set_session(&session), Ok(()));
+        assert_eq!(pipe.client.set_session(session), Ok(()));
 
         // Client sends initial flight.
         pipe.client.send(&mut buf).unwrap();
@@ -6263,7 +8989,7 @@
                 .unwrap();
 
         // Simulate a truncated packet by sending one byte less.
-        let mut zrtt = (&buf[..len - 1]).to_vec();
+        let mut zrtt = buf[..len - 1].to_vec();
 
         // 0-RTT packet is received before the Initial one.
         assert_eq!(pipe.server_recv(&mut zrtt), Err(Error::InvalidPacket));
@@ -6279,7 +9005,7 @@
     fn handshake_downgrade_v1() {
         let mut config = Config::new(PROTOCOL_VERSION_DRAFT29).unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.verify_peer(false);
 
@@ -6300,24 +9026,24 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
 
         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
 
         let flight = testing::emit_flight(&mut pipe.client).unwrap();
-        let client_sent = flight.iter().fold(0, |out, p| out + p.len());
+        let client_sent = flight.iter().fold(0, |out, p| out + p.0.len());
         testing::process_flight(&mut pipe.server, flight).unwrap();
 
         let flight = testing::emit_flight(&mut pipe.server).unwrap();
-        let server_sent = flight.iter().fold(0, |out, p| out + p.len());
+        let server_sent = flight.iter().fold(0, |out, p| out + p.0.len());
 
         assert_eq!(server_sent, client_sent * MAX_AMPLIFICATION_FACTOR);
     }
 
     #[test]
     fn stream() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.client.stream_send(4, b"hello, world", true), Ok(12));
@@ -6348,7 +9074,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -6366,11 +9092,11 @@
 
         // Configure session on new connection.
         let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
-        assert_eq!(pipe.client.set_session(&session), Ok(()));
+        assert_eq!(pipe.client.set_session(session), Ok(()));
 
         // Client sends initial flight.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
-        let mut initial = (&buf[..len]).to_vec();
+        let mut initial = buf[..len].to_vec();
 
         assert_eq!(pipe.client.is_in_early_data(), true);
 
@@ -6378,7 +9104,7 @@
         assert_eq!(pipe.client.stream_send(4, b"hello, world", true), Ok(12));
 
         let (len, _) = pipe.client.send(&mut buf).unwrap();
-        let mut zrtt = (&buf[..len]).to_vec();
+        let mut zrtt = buf[..len].to_vec();
 
         // Server receives packets.
         assert_eq!(pipe.server_recv(&mut initial), Ok(initial.len()));
@@ -6406,7 +9132,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(2_u64.pow(32) + 5);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -6432,7 +9158,7 @@
     fn empty_stream_frame() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [frame::Frame::Stream {
@@ -6474,12 +9200,95 @@
     }
 
     #[test]
+    fn update_key_request() {
+        let mut b = [0; 15];
+
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Client sends message with key update request.
+        assert_eq!(pipe.client_update_key(), Ok(()));
+        assert_eq!(pipe.client.stream_send(4, b"hello", false), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Ensure server updates key and it correctly decrypts the message.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+        assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((5, false)));
+        assert_eq!(&b[..5], b"hello");
+
+        // Ensure ACK for key update.
+        assert!(
+            pipe.server.pkt_num_spaces[packet::Epoch::Application]
+                .key_update
+                .as_ref()
+                .unwrap()
+                .update_acked
+        );
+
+        // Server sends message with the new key.
+        assert_eq!(pipe.server.stream_send(4, b"world", true), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Ensure update key is completed and client can decrypt packet.
+        let mut r = pipe.client.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+        assert_eq!(pipe.client.stream_recv(4, &mut b), Ok((5, true)));
+        assert_eq!(&b[..5], b"world");
+    }
+
+    #[test]
+    fn update_key_request_twice_error() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        let frames = [frame::Frame::Stream {
+            stream_id: 4,
+            data: stream::RangeBuf::from(b"hello", 0, false),
+        }];
+
+        // Client sends stream frame with key update request.
+        assert_eq!(pipe.client_update_key(), Ok(()));
+        let written = testing::encode_pkt(
+            &mut pipe.client,
+            packet::Type::Short,
+            &frames,
+            &mut buf,
+        )
+        .unwrap();
+
+        // Server correctly decode with new key.
+        assert_eq!(pipe.server_recv(&mut buf[..written]), Ok(written));
+
+        // Client sends stream frame with another key update request before server
+        // ACK.
+        assert_eq!(pipe.client_update_key(), Ok(()));
+        let written = testing::encode_pkt(
+            &mut pipe.client,
+            packet::Type::Short,
+            &frames,
+            &mut buf,
+        )
+        .unwrap();
+
+        // Check server correctly closes the connection with a key update error
+        // for the peer.
+        assert_eq!(pipe.server_recv(&mut buf[..written]), Err(Error::KeyUpdate));
+    }
+
+    #[test]
     /// Tests that receiving a MAX_STREAM_DATA frame for a receive-only
     /// unidirectional stream is forbidden.
     fn max_stream_data_receive_uni() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // Client opens unidirectional stream.
@@ -6503,7 +9312,7 @@
     fn empty_payload() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // Send a packet with no frames.
@@ -6518,7 +9327,7 @@
     fn min_payload() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Send a non-ack-eliciting packet.
         let frames = [frame::Frame::Padding { len: 4 }];
@@ -6529,13 +9338,30 @@
                 .unwrap();
         assert_eq!(pipe.server_recv(&mut buf[..written]), Ok(written));
 
-        assert_eq!(pipe.server.max_send_bytes, 195);
+        let initial_path = pipe
+            .server
+            .paths
+            .get_active()
+            .expect("initial path not found");
+
+        assert_eq!(initial_path.max_send_bytes, 195);
 
         // Force server to send a single PING frame.
-        pipe.server.recovery.loss_probes[packet::EPOCH_INITIAL] = 1;
+        pipe.server
+            .paths
+            .get_active_mut()
+            .expect("no active path")
+            .recovery
+            .loss_probes[packet::Epoch::Initial] = 1;
 
-        // Artifically limit the amount of bytes the server can send.
-        pipe.server.max_send_bytes = 60;
+        let initial_path = pipe
+            .server
+            .paths
+            .get_active_mut()
+            .expect("initial path not found");
+
+        // Artificially limit the amount of bytes the server can send.
+        initial_path.max_send_bytes = 60;
 
         assert_eq!(pipe.server.send(&mut buf), Err(Error::Done));
     }
@@ -6544,20 +9370,20 @@
     fn flow_control_limit() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [
             frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
+            },
+            frame::Frame::Stream {
                 stream_id: 4,
                 data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
             },
             frame::Frame::Stream {
                 stream_id: 8,
-                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
-            },
-            frame::Frame::Stream {
-                stream_id: 12,
                 data: stream::RangeBuf::from(b"a", 0, false),
             },
         ];
@@ -6573,22 +9399,22 @@
     fn flow_control_limit_dup() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [
             // One byte less than stream limit.
             frame::Frame::Stream {
-                stream_id: 4,
+                stream_id: 0,
                 data: stream::RangeBuf::from(b"aaaaaaaaaaaaaa", 0, false),
             },
             // Same stream, but one byte more.
             frame::Frame::Stream {
-                stream_id: 4,
+                stream_id: 0,
                 data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
             },
             frame::Frame::Stream {
-                stream_id: 12,
+                stream_id: 8,
                 data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
             },
         ];
@@ -6601,16 +9427,16 @@
     fn flow_control_update() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [
             frame::Frame::Stream {
-                stream_id: 4,
+                stream_id: 0,
                 data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
             },
             frame::Frame::Stream {
-                stream_id: 8,
+                stream_id: 4,
                 data: stream::RangeBuf::from(b"a", 0, false),
             },
         ];
@@ -6619,11 +9445,11 @@
 
         assert!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf).is_ok());
 
+        pipe.server.stream_recv(0, &mut buf).unwrap();
         pipe.server.stream_recv(4, &mut buf).unwrap();
-        pipe.server.stream_recv(8, &mut buf).unwrap();
 
         let frames = [frame::Frame::Stream {
-            stream_id: 8,
+            stream_id: 4,
             data: stream::RangeBuf::from(b"a", 1, false),
         }];
 
@@ -6643,18 +9469,18 @@
         assert_eq!(
             iter.next(),
             Some(&frame::Frame::MaxStreamData {
-                stream_id: 4,
+                stream_id: 0,
                 max: 30
             })
         );
-        assert_eq!(iter.next(), Some(&frame::Frame::MaxData { max: 46 }));
+        assert_eq!(iter.next(), Some(&frame::Frame::MaxData { max: 61 }));
     }
 
     #[test]
     /// Tests that flow control is properly updated even when a stream is shut
     /// down.
     fn flow_control_drain() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // Client opens a stream and sends some data.
@@ -6688,7 +9514,7 @@
     fn stream_flow_control_limit_bidi() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [frame::Frame::Stream {
@@ -6707,7 +9533,7 @@
     fn stream_flow_control_limit_uni() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [frame::Frame::Stream {
@@ -6726,12 +9552,12 @@
     fn stream_flow_control_update() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [frame::Frame::Stream {
             stream_id: 4,
-            data: stream::RangeBuf::from(b"aaaaaaa", 0, false),
+            data: stream::RangeBuf::from(b"aaaaaaaaa", 0, false),
         }];
 
         let pkt_type = packet::Type::Short;
@@ -6742,7 +9568,7 @@
 
         let frames = [frame::Frame::Stream {
             stream_id: 4,
-            data: stream::RangeBuf::from(b"a", 7, false),
+            data: stream::RangeBuf::from(b"a", 9, false),
         }];
 
         let len = pipe
@@ -6762,7 +9588,7 @@
             iter.next(),
             Some(&frame::Frame::MaxStreamData {
                 stream_id: 4,
-                max: 22,
+                max: 24,
             })
         );
     }
@@ -6771,7 +9597,7 @@
     fn stream_left_bidi() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(3, pipe.client.peer_streams_left_bidi());
@@ -6797,7 +9623,7 @@
     fn stream_left_uni() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(3, pipe.client.peer_streams_left_uni());
@@ -6823,7 +9649,7 @@
     fn stream_limit_bidi() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [
@@ -6868,7 +9694,7 @@
     fn stream_limit_max_bidi() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [frame::Frame::MaxStreamsBidi { max: MAX_STREAM_ID }];
@@ -6891,7 +9717,7 @@
     fn stream_limit_uni() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [
@@ -6936,7 +9762,7 @@
     fn stream_limit_max_uni() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [frame::Frame::MaxStreamsUni { max: MAX_STREAM_ID }];
@@ -6959,7 +9785,7 @@
     fn streams_blocked_max_bidi() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [frame::Frame::StreamsBlockedBidi {
@@ -6984,7 +9810,7 @@
     fn streams_blocked_max_uni() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [frame::Frame::StreamsBlockedUni {
@@ -7009,7 +9835,7 @@
     fn stream_data_overlap() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [
@@ -7039,7 +9865,7 @@
     fn stream_data_overlap_with_reordering() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [
@@ -7066,29 +9892,188 @@
     }
 
     #[test]
+    /// Tests that receiving a valid RESET_STREAM frame when all data has
+    /// already been read, notifies the application.
+    fn reset_stream_data_recvd() {
+        let mut b = [0; 15];
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Client sends some data.
+        assert_eq!(pipe.client.stream_send(0, b"hello", false), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Server gets data and sends data back, closing stream.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(0));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.server.stream_recv(0, &mut b), Ok((5, false)));
+        assert!(!pipe.server.stream_finished(0));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.server.stream_send(0, b"", true), Ok(0));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        let mut r = pipe.client.readable();
+        assert_eq!(r.next(), Some(0));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.client.stream_recv(0, &mut b), Ok((0, true)));
+        assert!(pipe.client.stream_finished(0));
+
+        // Client sends RESET_STREAM, closing stream.
+        let frames = [frame::Frame::ResetStream {
+            stream_id: 0,
+            error_code: 42,
+            final_size: 5,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(39));
+
+        // Server is notified of stream readability, due to reset.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(0));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(
+            pipe.server.stream_recv(0, &mut b),
+            Err(Error::StreamReset(42))
+        );
+
+        assert!(pipe.server.stream_finished(0));
+
+        // Sending RESET_STREAM again shouldn't make stream readable again.
+        pipe.send_pkt_to_server(pkt_type, &frames, &mut buf)
+            .unwrap();
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+    }
+
+    #[test]
+    /// Tests that receiving a valid RESET_STREAM frame when all data has _not_
+    /// been read, discards all buffered data and notifies the application.
+    fn reset_stream_data_not_recvd() {
+        let mut b = [0; 15];
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Client sends some data.
+        assert_eq!(pipe.client.stream_send(0, b"h", false), Ok(1));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Server gets data and sends data back, closing stream.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(0));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.server.stream_recv(0, &mut b), Ok((1, false)));
+        assert!(!pipe.server.stream_finished(0));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.server.stream_send(0, b"", true), Ok(0));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        let mut r = pipe.client.readable();
+        assert_eq!(r.next(), Some(0));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(pipe.client.stream_recv(0, &mut b), Ok((0, true)));
+        assert!(pipe.client.stream_finished(0));
+
+        // Client sends RESET_STREAM, closing stream.
+        let frames = [frame::Frame::ResetStream {
+            stream_id: 0,
+            error_code: 42,
+            final_size: 5,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(39));
+
+        // Server is notified of stream readability, due to reset.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(0));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(
+            pipe.server.stream_recv(0, &mut b),
+            Err(Error::StreamReset(42))
+        );
+
+        assert!(pipe.server.stream_finished(0));
+
+        // Sending RESET_STREAM again shouldn't make stream readable again.
+        assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(39));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), None);
+    }
+
+    #[test]
+    /// Tests that RESET_STREAM frames exceeding the connection-level flow
+    /// control limit cause an error.
     fn reset_stream_flow_control() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        let frames = [
+            frame::Frame::Stream {
+                stream_id: 0,
+                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
+            },
+            frame::Frame::Stream {
+                stream_id: 4,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+            frame::Frame::ResetStream {
+                stream_id: 4,
+                error_code: 0,
+                final_size: 15,
+            },
+            frame::Frame::Stream {
+                stream_id: 8,
+                data: stream::RangeBuf::from(b"a", 0, false),
+            },
+        ];
+
+        let pkt_type = packet::Type::Short;
+        assert_eq!(
+            pipe.send_pkt_to_server(pkt_type, &frames, &mut buf),
+            Err(Error::FlowControl),
+        );
+    }
+
+    #[test]
+    /// Tests that RESET_STREAM frames exceeding the stream-level flow control
+    /// limit cause an error.
+    fn reset_stream_flow_control_stream() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [
             frame::Frame::Stream {
                 stream_id: 4,
-                data: stream::RangeBuf::from(b"aaaaaaaaaaaaaaa", 0, false),
-            },
-            frame::Frame::Stream {
-                stream_id: 8,
                 data: stream::RangeBuf::from(b"a", 0, false),
             },
             frame::Frame::ResetStream {
-                stream_id: 8,
+                stream_id: 4,
                 error_code: 0,
-                final_size: 15,
-            },
-            frame::Frame::Stream {
-                stream_id: 12,
-                data: stream::RangeBuf::from(b"a", 0, false),
+                final_size: 16, // Past stream's flow control limit.
             },
         ];
 
@@ -7103,12 +10088,10 @@
     fn path_challenge() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
-        let frames = [frame::Frame::PathChallenge {
-            data: vec![0xba; 8],
-        }];
+        let frames = [frame::Frame::PathChallenge { data: [0xba; 8] }];
 
         let pkt_type = packet::Type::Short;
 
@@ -7127,9 +10110,7 @@
 
         assert_eq!(
             iter.next(),
-            Some(&frame::Frame::PathResponse {
-                data: vec![0xba; 8],
-            })
+            Some(&frame::Frame::PathResponse { data: [0xba; 8] })
         );
     }
 
@@ -7139,7 +10120,7 @@
     fn early_1rtt_packet() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Client sends initial flight
         let flight = testing::emit_flight(&mut pipe.client).unwrap();
@@ -7154,7 +10135,7 @@
 
         // Emulate handshake packet delay by not making server process client
         // packet.
-        let delayed = flight.clone();
+        let delayed = flight;
 
         testing::emit_flight(&mut pipe.server).ok();
 
@@ -7192,7 +10173,7 @@
         // Note that `largest_rx_pkt_num` is initialized to 0, so we need to
         // send another 1-RTT packet to make this check meaningful.
         assert_eq!(
-            pipe.server.pkt_num_spaces[packet::EPOCH_APPLICATION]
+            pipe.server.pkt_num_spaces[packet::Epoch::Application]
                 .largest_rx_pkt_num,
             0
         );
@@ -7203,7 +10184,7 @@
         assert!(pipe.server.is_established());
 
         assert_eq!(
-            pipe.server.pkt_num_spaces[packet::EPOCH_APPLICATION]
+            pipe.server.pkt_num_spaces[packet::Epoch::Application]
                 .largest_rx_pkt_num,
             0
         );
@@ -7215,31 +10196,31 @@
 
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // Client sends some data, and closes stream.
-        assert_eq!(pipe.client.stream_send(4, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(0, b"hello", true), Ok(5));
         assert_eq!(pipe.advance(), Ok(()));
 
         // Server gets data.
         let mut r = pipe.server.readable();
-        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), Some(0));
         assert_eq!(r.next(), None);
 
-        assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((5, true)));
-        assert!(pipe.server.stream_finished(4));
+        assert_eq!(pipe.server.stream_recv(0, &mut b), Ok((5, true)));
+        assert!(pipe.server.stream_finished(0));
 
         let mut r = pipe.server.readable();
         assert_eq!(r.next(), None);
 
         // Server sends data, until blocked.
         let mut r = pipe.server.writable();
-        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), Some(0));
         assert_eq!(r.next(), None);
 
         loop {
-            if pipe.server.stream_send(4, b"world", false) == Ok(0) {
+            if pipe.server.stream_send(0, b"world", false) == Err(Error::Done) {
                 break;
             }
 
@@ -7251,7 +10232,7 @@
 
         // Client sends STOP_SENDING.
         let frames = [frame::Frame::StopSending {
-            stream_id: 4,
+            stream_id: 0,
             error_code: 42,
         }];
 
@@ -7272,7 +10253,7 @@
         assert_eq!(
             iter.next(),
             Some(&frame::Frame::ResetStream {
-                stream_id: 4,
+                stream_id: 0,
                 error_code: 42,
                 final_size: 15,
             })
@@ -7280,11 +10261,11 @@
 
         // Stream is writable, but writing returns an error.
         let mut r = pipe.server.writable();
-        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), Some(0));
         assert_eq!(r.next(), None);
 
         assert_eq!(
-            pipe.server.stream_send(4, b"world", true),
+            pipe.server.stream_send(0, b"world", true),
             Err(Error::StreamStopped(42)),
         );
 
@@ -7297,6 +10278,7 @@
         let frames = [frame::Frame::ACK {
             ack_delay: 15,
             ranges,
+            ecn_counts: None,
         }];
 
         assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(0));
@@ -7306,7 +10288,7 @@
 
         // Sending STOP_SENDING again shouldn't trigger RESET_STREAM again.
         let frames = [frame::Frame::StopSending {
-            stream_id: 4,
+            stream_id: 0,
             error_code: 42,
         }];
 
@@ -7319,7 +10301,7 @@
 
         assert_eq!(frames.len(), 1);
 
-        match frames.iter().next() {
+        match frames.first() {
             Some(frame::Frame::ACK { .. }) => (),
 
             f => panic!("expected ACK frame, got {:?}", f),
@@ -7335,7 +10317,7 @@
 
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // Client sends some data, and closes stream.
@@ -7353,11 +10335,15 @@
         let mut r = pipe.server.readable();
         assert_eq!(r.next(), None);
 
-        // Server sends data, and closes stream.
+        // Server sends data...
         let mut r = pipe.server.writable();
         assert_eq!(r.next(), Some(4));
         assert_eq!(r.next(), None);
 
+        assert_eq!(pipe.server.stream_send(4, b"world", false), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // ...and buffers more, and closes stream.
         assert_eq!(pipe.server.stream_send(4, b"world", true), Ok(5));
 
         // Client sends STOP_SENDING before server flushes stream.
@@ -7394,10 +10380,82 @@
     }
 
     #[test]
+    /// Tests that resetting a stream restores flow control for unsent data.
+    fn stop_sending_unsent_tx_cap() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.set_initial_max_data(15);
+        config.set_initial_max_stream_data_bidi_local(30);
+        config.set_initial_max_stream_data_bidi_remote(30);
+        config.set_initial_max_stream_data_uni(30);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(0);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Client sends some data.
+        assert_eq!(pipe.client.stream_send(4, b"hello", true), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+
+        let mut b = [0; 15];
+        assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((5, true)));
+
+        // Server sends some data.
+        assert_eq!(pipe.server.stream_send(4, b"hello", false), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Server buffers some data, until send capacity limit reached.
+        assert_eq!(pipe.server.stream_send(4, b"hello", false), Ok(5));
+        assert_eq!(pipe.server.stream_send(4, b"hello", false), Ok(5));
+        assert_eq!(
+            pipe.server.stream_send(4, b"hello", false),
+            Err(Error::Done)
+        );
+
+        // Client sends STOP_SENDING.
+        let frames = [frame::Frame::StopSending {
+            stream_id: 4,
+            error_code: 42,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        pipe.send_pkt_to_server(pkt_type, &frames, &mut buf)
+            .unwrap();
+
+        // Server can now send more data (on a different stream).
+        assert_eq!(pipe.client.stream_send(8, b"hello", true), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(pipe.server.stream_send(8, b"hello", false), Ok(5));
+        assert_eq!(pipe.server.stream_send(8, b"hello", false), Ok(5));
+        assert_eq!(
+            pipe.server.stream_send(8, b"hello", false),
+            Err(Error::Done)
+        );
+        assert_eq!(pipe.advance(), Ok(()));
+    }
+
+    #[test]
     fn stream_shutdown_read() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // Client sends some data.
@@ -7473,7 +10531,7 @@
     fn stream_shutdown_read_after_fin() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // Client sends some data.
@@ -7521,10 +10579,83 @@
     }
 
     #[test]
+    fn stream_shutdown_read_update_max_data() {
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(10000);
+        config.set_initial_max_stream_data_bidi_remote(10000);
+        config.set_initial_max_streams_bidi(10);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(0, b"a", false), Ok(1));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(pipe.server.stream_recv(0, &mut buf), Ok((1, false)));
+        assert_eq!(pipe.server.stream_shutdown(0, Shutdown::Read, 123), Ok(()));
+
+        assert_eq!(pipe.server.rx_data, 1);
+        assert_eq!(pipe.client.tx_data, 1);
+        assert_eq!(pipe.client.max_tx_data, 30);
+
+        assert_eq!(
+            pipe.client
+                .stream_send(0, &buf[..pipe.client.tx_cap], false),
+            Ok(29)
+        );
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(pipe.server.stream_readable(0), false); // nothing can be consumed
+
+        // The client has increased its tx_data, and server has received it, so
+        // it increases flow control accordingly.
+        assert_eq!(pipe.client.tx_data, 30);
+        assert_eq!(pipe.server.rx_data, 30);
+        assert_eq!(pipe.client.tx_cap, 45);
+    }
+
+    #[test]
+    fn stream_shutdown_uni() {
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Exchange some data on uni streams.
+        assert_eq!(pipe.client.stream_send(2, b"hello, world", false), Ok(10));
+        assert_eq!(pipe.server.stream_send(3, b"hello, world", false), Ok(10));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Test local and remote shutdown.
+        assert_eq!(pipe.client.stream_shutdown(2, Shutdown::Write, 42), Ok(()));
+        assert_eq!(
+            pipe.client.stream_shutdown(2, Shutdown::Read, 42),
+            Err(Error::InvalidStreamState(2))
+        );
+
+        assert_eq!(
+            pipe.client.stream_shutdown(3, Shutdown::Write, 42),
+            Err(Error::InvalidStreamState(3))
+        );
+        assert_eq!(pipe.client.stream_shutdown(3, Shutdown::Read, 42), Ok(()));
+    }
+
+    #[test]
     fn stream_shutdown_write() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // Client sends some data.
@@ -7544,6 +10675,7 @@
 
         // Server sends some data.
         assert_eq!(pipe.server.stream_send(4, b"goodbye, world", false), Ok(14));
+        assert_eq!(pipe.advance(), Ok(()));
 
         // Server shuts down stream.
         assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Write, 42), Ok(()));
@@ -7589,8 +10721,18 @@
 
         assert_eq!(pipe.server.stream_recv(4, &mut buf), Ok((15, true)));
 
+        // Client processes readable streams.
+        let mut r = pipe.client.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+
+        assert_eq!(
+            pipe.client.stream_recv(4, &mut buf),
+            Err(Error::StreamReset(42))
+        );
+
         // Stream is collected on both sides.
-        // TODO: assert_eq!(pipe.client.streams.len(), 0);
+        assert_eq!(pipe.client.streams.len(), 0);
         assert_eq!(pipe.server.streams.len(), 0);
 
         assert_eq!(
@@ -7600,12 +10742,79 @@
     }
 
     #[test]
+    /// Tests that shutting down a stream restores flow control for unsent data.
+    fn stream_shutdown_write_unsent_tx_cap() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.set_initial_max_data(15);
+        config.set_initial_max_stream_data_bidi_local(30);
+        config.set_initial_max_stream_data_bidi_remote(30);
+        config.set_initial_max_stream_data_uni(30);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(0);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Client sends some data.
+        assert_eq!(pipe.client.stream_send(4, b"hello", true), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), None);
+
+        let mut b = [0; 15];
+        assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((5, true)));
+
+        // Server sends some data.
+        assert_eq!(pipe.server.stream_send(4, b"hello", false), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Server buffers some data, until send capacity limit reached.
+        assert_eq!(pipe.server.stream_send(4, b"hello", false), Ok(5));
+        assert_eq!(pipe.server.stream_send(4, b"hello", false), Ok(5));
+        assert_eq!(
+            pipe.server.stream_send(4, b"hello", false),
+            Err(Error::Done)
+        );
+
+        // Client shouldn't update flow control.
+        assert_eq!(pipe.client.should_update_max_data(), false);
+
+        // Server shuts down stream.
+        assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Write, 42), Ok(()));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Server can now send more data (on a different stream).
+        assert_eq!(pipe.client.stream_send(8, b"hello", true), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(pipe.server.stream_send(8, b"hello", false), Ok(5));
+        assert_eq!(pipe.server.stream_send(8, b"hello", false), Ok(5));
+        assert_eq!(
+            pipe.server.stream_send(8, b"hello", false),
+            Err(Error::Done)
+        );
+        assert_eq!(pipe.advance(), Ok(()));
+    }
+
+    #[test]
     /// Tests that the order of flushable streams scheduled on the wire is the
     /// same as the order of `stream_send()` calls done by the application.
     fn stream_round_robin() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5));
@@ -7636,7 +10845,7 @@
             testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
 
         assert_eq!(
-            frames.iter().next(),
+            frames.first(),
             Some(&frame::Frame::Stream {
                 stream_id: 0,
                 data: stream::RangeBuf::from(b"aaaaa", 0, false),
@@ -7649,7 +10858,7 @@
             testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
 
         assert_eq!(
-            frames.iter().next(),
+            frames.first(),
             Some(&frame::Frame::Stream {
                 stream_id: 4,
                 data: stream::RangeBuf::from(b"aaaaa", 0, false),
@@ -7660,14 +10869,14 @@
     #[test]
     /// Tests the readable iterator.
     fn stream_readable() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // No readable streams.
         let mut r = pipe.client.readable();
         assert_eq!(r.next(), None);
 
-        assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.stream_send(0, b"aaaaa", false), Ok(5));
 
         let mut r = pipe.client.readable();
         assert_eq!(r.next(), None);
@@ -7679,22 +10888,22 @@
 
         // Server received stream.
         let mut r = pipe.server.readable();
-        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), Some(0));
         assert_eq!(r.next(), None);
 
         assert_eq!(
-            pipe.server.stream_send(4, b"aaaaaaaaaaaaaaa", false),
+            pipe.server.stream_send(0, b"aaaaaaaaaaaaaaa", false),
             Ok(15)
         );
         assert_eq!(pipe.advance(), Ok(()));
 
         let mut r = pipe.client.readable();
-        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), Some(0));
         assert_eq!(r.next(), None);
 
         // Client drains stream.
         let mut b = [0; 15];
-        pipe.client.stream_recv(4, &mut b).unwrap();
+        pipe.client.stream_recv(0, &mut b).unwrap();
         assert_eq!(pipe.advance(), Ok(()));
 
         let mut r = pipe.client.readable();
@@ -7702,19 +10911,19 @@
 
         // Server shuts down stream.
         let mut r = pipe.server.readable();
-        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), Some(0));
         assert_eq!(r.next(), None);
 
-        assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Read, 0), Ok(()));
+        assert_eq!(pipe.server.stream_shutdown(0, Shutdown::Read, 0), Ok(()));
 
         let mut r = pipe.server.readable();
         assert_eq!(r.next(), None);
 
         // Client creates multiple streams.
-        assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5));
         assert_eq!(pipe.advance(), Ok(()));
 
-        assert_eq!(pipe.client.stream_send(12, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5));
         assert_eq!(pipe.advance(), Ok(()));
 
         let mut r = pipe.server.readable();
@@ -7730,29 +10939,29 @@
     #[test]
     /// Tests the writable iterator.
     fn stream_writable() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // No writable streams.
         let mut w = pipe.client.writable();
         assert_eq!(w.next(), None);
 
-        assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.stream_send(0, b"aaaaa", false), Ok(5));
 
         // Client created stream.
         let mut w = pipe.client.writable();
-        assert_eq!(w.next(), Some(4));
+        assert_eq!(w.next(), Some(0));
         assert_eq!(w.next(), None);
 
         assert_eq!(pipe.advance(), Ok(()));
 
         // Server created stream.
         let mut w = pipe.server.writable();
-        assert_eq!(w.next(), Some(4));
+        assert_eq!(w.next(), Some(0));
         assert_eq!(w.next(), None);
 
         assert_eq!(
-            pipe.server.stream_send(4, b"aaaaaaaaaaaaaaa", false),
+            pipe.server.stream_send(0, b"aaaaaaaaaaaaaaa", false),
             Ok(15)
         );
 
@@ -7764,25 +10973,25 @@
 
         // Client drains stream.
         let mut b = [0; 15];
-        pipe.client.stream_recv(4, &mut b).unwrap();
+        pipe.client.stream_recv(0, &mut b).unwrap();
         assert_eq!(pipe.advance(), Ok(()));
 
         // Server stream is writable again.
         let mut w = pipe.server.writable();
-        assert_eq!(w.next(), Some(4));
+        assert_eq!(w.next(), Some(0));
         assert_eq!(w.next(), None);
 
-        // Server suts down stream.
-        assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Write, 0), Ok(()));
+        // Server shuts down stream.
+        assert_eq!(pipe.server.stream_shutdown(0, Shutdown::Write, 0), Ok(()));
 
         let mut w = pipe.server.writable();
         assert_eq!(w.next(), None);
 
         // Client creates multiple streams.
-        assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.stream_send(4, b"aaaaa", false), Ok(5));
         assert_eq!(pipe.advance(), Ok(()));
 
-        assert_eq!(pipe.client.stream_send(12, b"aaaaa", false), Ok(5));
+        assert_eq!(pipe.client.stream_send(8, b"aaaaa", false), Ok(5));
         assert_eq!(pipe.advance(), Ok(()));
 
         let mut w = pipe.server.writable();
@@ -7795,18 +11004,71 @@
         assert_eq!(w.len(), 0);
 
         // Server finishes stream.
-        assert_eq!(pipe.server.stream_send(12, b"aaaaa", true), Ok(5));
+        assert_eq!(pipe.server.stream_send(8, b"aaaaa", true), Ok(5));
 
         let mut w = pipe.server.writable();
-        assert_eq!(w.next(), Some(8));
+        assert_eq!(w.next(), Some(4));
         assert_eq!(w.next(), None);
     }
 
     #[test]
+    fn stream_writable_blocked() {
+        let mut config = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config.set_application_protos(&[b"h3"]).unwrap();
+        config.set_initial_max_data(70);
+        config.set_initial_max_stream_data_bidi_local(150000);
+        config.set_initial_max_stream_data_bidi_remote(150000);
+        config.set_initial_max_stream_data_uni(150000);
+        config.set_initial_max_streams_bidi(100);
+        config.set_initial_max_streams_uni(5);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Client creates stream and sends some data.
+        let send_buf = [0; 35];
+        assert_eq!(pipe.client.stream_send(0, &send_buf, false), Ok(35));
+
+        // Stream is still writable as it still has capacity.
+        assert_eq!(pipe.client.stream_writable_next(), Some(0));
+        assert_eq!(pipe.client.stream_writable_next(), None);
+
+        // Client fills stream, which becomes unwritable due to connection
+        // capacity.
+        let send_buf = [0; 36];
+        assert_eq!(pipe.client.stream_send(0, &send_buf, false), Ok(35));
+
+        assert_eq!(pipe.client.stream_writable_next(), None);
+
+        assert_eq!(pipe.client.tx_cap, 0);
+
+        assert_eq!(pipe.advance(), Ok(()));
+
+        let mut b = [0; 70];
+        pipe.server.stream_recv(0, &mut b).unwrap();
+
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // The connection capacity has increased and the stream is now writable
+        // again.
+        assert_ne!(pipe.client.tx_cap, 0);
+
+        assert_eq!(pipe.client.stream_writable_next(), Some(0));
+        assert_eq!(pipe.client.stream_writable_next(), None);
+    }
+
+    #[test]
     /// Tests that we don't exceed the per-connection flow control limit set by
     /// the peer.
     fn flow_control_limit_send() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(
@@ -7819,7 +11081,7 @@
             Ok(15)
         );
         assert_eq!(pipe.advance(), Ok(()));
-        assert_eq!(pipe.client.stream_send(8, b"a", false), Ok(0));
+        assert_eq!(pipe.client.stream_send(8, b"a", false), Err(Error::Done));
         assert_eq!(pipe.advance(), Ok(()));
 
         let mut r = pipe.server.readable();
@@ -7833,7 +11095,7 @@
     /// the server to close the connection immediately.
     fn invalid_initial_server() {
         let mut buf = [0; 65535];
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         let frames = [frame::Frame::Padding { len: 10 }];
 
@@ -7865,7 +11127,7 @@
     /// the client to close the connection immediately.
     fn invalid_initial_client() {
         let mut buf = [0; 65535];
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Client sends initial flight.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
@@ -7903,7 +11165,7 @@
     /// valid packet cause the server to close the connection immediately.
     fn invalid_initial_payload() {
         let mut buf = [0; 65535];
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         let mut b = octets::OctetsMut::with_slice(&mut buf);
 
@@ -7912,11 +11174,14 @@
         let pn = 0;
         let pn_len = packet::pkt_num_len(pn).unwrap();
 
+        let dcid = pipe.client.destination_id();
+        let scid = pipe.client.source_id();
+
         let hdr = Header {
             ty: packet::Type::Initial,
             version: pipe.client.version,
-            dcid: ConnectionId::from_ref(&pipe.client.dcid),
-            scid: ConnectionId::from_ref(&pipe.client.scid),
+            dcid: ConnectionId::from_ref(&dcid),
+            scid: ConnectionId::from_ref(&scid),
             pkt_num: 0,
             pkt_num_len: pn_len,
             token: pipe.client.token.clone(),
@@ -7945,8 +11210,7 @@
         let space = &mut pipe.client.pkt_num_spaces[epoch];
 
         // Use correct payload length when encrypting the packet.
-        let payload_len = frames.iter().fold(0, |acc, x| acc + x.wire_len()) +
-            space.crypto_overhead().unwrap();
+        let payload_len = frames.iter().fold(0, |acc, x| acc + x.wire_len());
 
         let aead = space.crypto_seal.as_ref().unwrap();
 
@@ -7956,6 +11220,7 @@
             pn_len,
             payload_len,
             payload_offset,
+            None,
             aead,
         )
         .unwrap();
@@ -7975,7 +11240,7 @@
     fn invalid_packet() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         let frames = [frame::Frame::Padding { len: 10 }];
@@ -8005,7 +11270,7 @@
     fn recv_empty_buffer() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.server_recv(&mut buf[..0]), Err(Error::BufferTooShort));
@@ -8022,7 +11287,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -8088,7 +11353,7 @@
     }
 
     #[test]
-    /// Tests that the MAX_STREAMS frame is sent for unirectional streams.
+    /// Tests that the MAX_STREAMS frame is sent for unidirectional streams.
     fn stream_limit_update_uni() {
         let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
         config
@@ -8098,7 +11363,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -8155,7 +11420,7 @@
     /// data in the buffer, and that the buffer becomes readable on the other
     /// side.
     fn stream_zero_length_fin() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(
@@ -8200,7 +11465,7 @@
     /// data in the buffer, that the buffer becomes readable on the other
     /// side and stays readable even if the stream is fin'd locally.
     fn stream_zero_length_fin_deferred_collection() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(
@@ -8256,11 +11521,30 @@
     }
 
     #[test]
+    /// Tests that the stream gets created with stream_send() even if there's
+    /// no data in the buffer and the fin flag is not set.
+    fn stream_zero_length_non_fin() {
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(0, b"", false), Ok(0));
+
+        // The stream now should have been created.
+        assert_eq!(pipe.client.streams.len(), 1);
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Sending an empty non-fin should not change any stream state on the
+        // other side.
+        let mut r = pipe.server.readable();
+        assert!(r.next().is_none());
+    }
+
+    #[test]
     /// Tests that completed streams are garbage collected.
     fn collect_streams() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.client.streams.len(), 0);
@@ -8324,7 +11608,7 @@
 
     #[test]
     fn peer_cert() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         match pipe.client.peer_cert() {
@@ -8335,6 +11619,29 @@
     }
 
     #[test]
+    fn peer_cert_chain() {
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert-big.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+
+        let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        match pipe.client.peer_cert_chain() {
+            Some(c) => assert_eq!(c.len(), 5),
+
+            None => panic!("missing server certificate chain"),
+        }
+    }
+
+    #[test]
     fn retry() {
         let mut buf = [0; 65535];
 
@@ -8346,7 +11653,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
 
         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
@@ -8385,7 +11692,14 @@
 
         // Server accepts connection.
         let from = "127.0.0.1:1234".parse().unwrap();
-        pipe.server = accept(&scid, Some(&odcid), from, &mut config).unwrap();
+        pipe.server = accept(
+            &scid,
+            Some(&odcid),
+            testing::Pipe::server_addr(),
+            from,
+            &mut config,
+        )
+        .unwrap();
         assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len));
 
         assert_eq!(pipe.advance(), Ok(()));
@@ -8406,7 +11720,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
 
         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
@@ -8441,7 +11755,9 @@
         // Server accepts connection and send first flight. But original
         // destination connection ID is ignored.
         let from = "127.0.0.1:1234".parse().unwrap();
-        pipe.server = accept(&scid, None, from, &mut config).unwrap();
+        pipe.server =
+            accept(&scid, None, testing::Pipe::server_addr(), from, &mut config)
+                .unwrap();
         assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len));
 
         let flight = testing::emit_flight(&mut pipe.server).unwrap();
@@ -8464,7 +11780,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
 
         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
@@ -8500,7 +11816,14 @@
         // destination connection ID is invalid.
         let from = "127.0.0.1:1234".parse().unwrap();
         let odcid = ConnectionId::from_ref(b"bogus value");
-        pipe.server = accept(&scid, Some(&odcid), from, &mut config).unwrap();
+        pipe.server = accept(
+            &scid,
+            Some(&odcid),
+            testing::Pipe::server_addr(),
+            from,
+            &mut config,
+        )
+        .unwrap();
         assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len));
 
         let flight = testing::emit_flight(&mut pipe.server).unwrap();
@@ -8521,7 +11844,7 @@
 
     #[test]
     fn connection_must_be_send() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         check_send(&mut pipe.client);
     }
 
@@ -8535,7 +11858,7 @@
 
     #[test]
     fn connection_must_be_sync() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         check_sync(&mut pipe.client);
     }
 
@@ -8543,7 +11866,7 @@
     fn data_blocked() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.client.stream_send(0, b"aaaaaaaaaa", false), Ok(10));
@@ -8582,7 +11905,7 @@
     fn stream_data_blocked() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.client.stream_send(0, b"aaaaa", false), Ok(5));
@@ -8645,9 +11968,57 @@
 
         assert_eq!(iter.next(), None);
 
-        // Send again from blocked stream and make sure it is marked as blocked
-        // again.
-        assert_eq!(pipe.client.stream_send(0, b"aaaaaa", false), Ok(0));
+        // Send again from blocked stream and make sure it is not marked as
+        // blocked again.
+        assert_eq!(
+            pipe.client.stream_send(0, b"aaaaaa", false),
+            Err(Error::Done)
+        );
+        assert_eq!(pipe.client.streams.blocked().len(), 0);
+        assert_eq!(pipe.client.send(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn stream_data_blocked_unblocked_flow_control() {
+        let mut buf = [0; 65535];
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        assert_eq!(
+            pipe.client.stream_send(0, b"aaaaaaaaaaaaaaah", false),
+            Ok(15)
+        );
+        assert_eq!(pipe.client.streams.blocked().len(), 1);
+        assert_eq!(pipe.advance(), Ok(()));
+        assert_eq!(pipe.client.streams.blocked().len(), 0);
+
+        // Send again on blocked stream. It's blocked at the same offset as
+        // previously, so it should not be marked as blocked again.
+        assert_eq!(pipe.client.stream_send(0, b"h", false), Err(Error::Done));
+        assert_eq!(pipe.client.streams.blocked().len(), 0);
+
+        // No matter how many times we try to write stream data tried, no
+        // packets containing STREAM_BLOCKED should be emitted.
+        assert_eq!(pipe.client.stream_send(0, b"h", false), Err(Error::Done));
+        assert_eq!(pipe.client.send(&mut buf), Err(Error::Done));
+
+        assert_eq!(pipe.client.stream_send(0, b"h", false), Err(Error::Done));
+        assert_eq!(pipe.client.send(&mut buf), Err(Error::Done));
+
+        assert_eq!(pipe.client.stream_send(0, b"h", false), Err(Error::Done));
+        assert_eq!(pipe.client.send(&mut buf), Err(Error::Done));
+
+        // Now read some data at the server to release flow control.
+        let mut r = pipe.server.readable();
+        assert_eq!(r.next(), Some(0));
+        assert_eq!(r.next(), None);
+
+        let mut b = [0; 10];
+        assert_eq!(pipe.server.stream_recv(0, &mut b), Ok((10, false)));
+        assert_eq!(&b[..10], b"aaaaaaaaaa");
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(pipe.client.stream_send(0, b"hhhhhhhhhh!", false), Ok(10));
         assert_eq!(pipe.client.streams.blocked().len(), 1);
 
         let (len, _) = pipe.client.send(&mut buf).unwrap();
@@ -8662,20 +12033,22 @@
             iter.next(),
             Some(&frame::Frame::StreamDataBlocked {
                 stream_id: 0,
-                limit: 15,
+                limit: 25,
             })
         );
 
-        assert_eq!(iter.next(), Some(&frame::Frame::Padding { len: 1 }));
+        // don't care about remaining received frames
 
-        assert_eq!(iter.next(), None);
+        assert_eq!(pipe.client.stream_send(0, b"!", false), Err(Error::Done));
+        assert_eq!(pipe.client.streams.blocked().len(), 0);
+        assert_eq!(pipe.client.send(&mut buf), Err(Error::Done));
     }
 
     #[test]
     fn app_limited_true() {
         let mut config = Config::new(PROTOCOL_VERSION).unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(50000);
         config.set_initial_max_stream_data_bidi_local(50000);
@@ -8701,14 +12074,22 @@
         assert_eq!(pipe.advance(), Ok(()));
 
         // app_limited should be true because we send less than cwnd.
-        assert_eq!(pipe.server.recovery.app_limited(), true);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .app_limited(),
+            true
+        );
     }
 
     #[test]
     fn app_limited_false() {
         let mut config = Config::new(PROTOCOL_VERSION).unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(50000);
         config.set_initial_max_stream_data_bidi_local(50000);
@@ -8736,14 +12117,164 @@
 
         // We can't create a new packet header because there is no room by cwnd.
         // app_limited should be false because we can't send more by cwnd.
-        assert_eq!(pipe.server.recovery.app_limited(), false);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .app_limited(),
+            false
+        );
+    }
+
+    #[test]
+    fn sends_ack_only_pkt_when_full_cwnd_and_ack_elicited() {
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.set_initial_max_data(50000);
+        config.set_initial_max_stream_data_bidi_local(50000);
+        config.set_initial_max_stream_data_bidi_remote(50000);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(3);
+        config.set_max_recv_udp_payload_size(1200);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Client sends stream data bigger than cwnd (it will never arrive to the
+        // server).
+        let send_buf1 = [0; 20000];
+        assert_eq!(pipe.client.stream_send(0, &send_buf1, false), Ok(12000));
+
+        testing::emit_flight(&mut pipe.client).ok();
+
+        // Server sends some stream data that will need ACKs.
+        assert_eq!(
+            pipe.server.stream_send(1, &send_buf1[..500], false),
+            Ok(500)
+        );
+
+        testing::process_flight(
+            &mut pipe.client,
+            testing::emit_flight(&mut pipe.server).unwrap(),
+        )
+        .unwrap();
+
+        let mut buf = [0; 2000];
+
+        let ret = pipe.client.send(&mut buf);
+
+        assert_eq!(pipe.client.tx_cap, 0);
+
+        assert!(matches!(ret, Ok((_, _))), "the client should at least send one packet to acknowledge the newly received data");
+
+        let (sent, _) = ret.unwrap();
+
+        assert_ne!(sent, 0, "the client should at least send a pure ACK packet");
+
+        let frames =
+            testing::decode_pkt(&mut pipe.server, &mut buf, sent).unwrap();
+        assert_eq!(1, frames.len());
+        assert!(
+            matches!(frames[0], frame::Frame::ACK { .. }),
+            "the packet sent by the client must be an ACK only packet"
+        );
+    }
+
+    /// Like sends_ack_only_pkt_when_full_cwnd_and_ack_elicited, but when
+    /// ack_eliciting is explicitly requested.
+    #[test]
+    fn sends_ack_only_pkt_when_full_cwnd_and_ack_elicited_despite_max_unacknowledging(
+    ) {
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.set_initial_max_data(50000);
+        config.set_initial_max_stream_data_bidi_local(50000);
+        config.set_initial_max_stream_data_bidi_remote(50000);
+        config.set_initial_max_streams_bidi(3);
+        config.set_initial_max_streams_uni(3);
+        config.set_max_recv_udp_payload_size(1200);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Client sends stream data bigger than cwnd (it will never arrive to the
+        // server). This exhausts the congestion window.
+        let send_buf1 = [0; 20000];
+        assert_eq!(pipe.client.stream_send(0, &send_buf1, false), Ok(12000));
+
+        testing::emit_flight(&mut pipe.client).ok();
+
+        // Client gets PING frames from server, which elicit ACK
+        let mut buf = [0; 2000];
+        for _ in 0..recovery::MAX_OUTSTANDING_NON_ACK_ELICITING {
+            let written = testing::encode_pkt(
+                &mut pipe.server,
+                packet::Type::Short,
+                &[frame::Frame::Ping],
+                &mut buf,
+            )
+            .unwrap();
+
+            pipe.client_recv(&mut buf[..written])
+                .expect("client recv ping");
+
+            // Client acknowledges despite a full congestion window
+            let ret = pipe.client.send(&mut buf);
+
+            assert!(matches!(ret, Ok((_, _))), "the client should at least send one packet to acknowledge the newly received data");
+
+            let (sent, _) = ret.unwrap();
+
+            assert_ne!(
+                sent, 0,
+                "the client should at least send a pure ACK packet"
+            );
+
+            let frames =
+                testing::decode_pkt(&mut pipe.server, &mut buf, sent).unwrap();
+
+            assert_eq!(1, frames.len());
+
+            assert!(
+                matches!(frames[0], frame::Frame::ACK { .. }),
+                "the packet sent by the client must be an ACK only packet"
+            );
+        }
+
+        // The client shouldn't need to send any more packets after the ACK only
+        // packet it just sent.
+        assert_eq!(
+            pipe.client.send(&mut buf),
+            Err(Error::Done),
+            "nothing for client to send after ACK-only packet"
+        );
     }
 
     #[test]
     fn app_limited_false_no_frame() {
         let mut config = Config::new(PROTOCOL_VERSION).unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(50000);
         config.set_initial_max_stream_data_bidi_local(50000);
@@ -8771,14 +12302,22 @@
 
         // We can't create a new packet header because there is no room by cwnd.
         // app_limited should be false because we can't send more by cwnd.
-        assert_eq!(pipe.server.recovery.app_limited(), false);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .app_limited(),
+            false
+        );
     }
 
     #[test]
     fn app_limited_false_no_header() {
         let mut config = Config::new(PROTOCOL_VERSION).unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(50000);
         config.set_initial_max_stream_data_bidi_local(50000);
@@ -8806,14 +12345,22 @@
 
         // We can't create a new frame because there is no room by cwnd.
         // app_limited should be false because we can't send more by cwnd.
-        assert_eq!(pipe.server.recovery.app_limited(), false);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .app_limited(),
+            false
+        );
     }
 
     #[test]
     fn app_limited_not_changed_on_no_new_frames() {
         let mut config = Config::new(PROTOCOL_VERSION).unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(50000);
         config.set_initial_max_stream_data_bidi_local(50000);
@@ -8835,23 +12382,39 @@
 
         // Client's app_limited is true because its bytes-in-flight
         // is much smaller than the current cwnd.
-        assert_eq!(pipe.client.recovery.app_limited(), true);
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .app_limited(),
+            true
+        );
 
         // Client has no new frames to send - returns Done.
         assert_eq!(testing::emit_flight(&mut pipe.client), Err(Error::Done));
 
         // Client's app_limited should remain the same.
-        assert_eq!(pipe.client.recovery.app_limited(), true);
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .app_limited(),
+            true
+        );
     }
 
     #[test]
     fn limit_ack_ranges() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
-        let epoch = packet::EPOCH_APPLICATION;
+        let epoch = packet::Epoch::Application;
 
         assert_eq!(pipe.server.pkt_num_spaces[epoch].recv_pkt_need_ack.len(), 0);
 
@@ -8907,7 +12470,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(1_000_000);
         config.set_initial_max_stream_data_bidi_local(1_000_000);
@@ -8993,7 +12556,7 @@
 
             let frames =
                 testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
-            let stream = frames.iter().next().unwrap();
+            let stream = frames.first().unwrap();
 
             assert_eq!(stream, &frame::Frame::Stream {
                 stream_id: 8,
@@ -9016,7 +12579,7 @@
 
             let frames =
                 testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
-            let stream = frames.iter().next().unwrap();
+            let stream = frames.first().unwrap();
 
             assert_eq!(stream, &frame::Frame::Stream {
                 stream_id: 16,
@@ -9039,7 +12602,7 @@
 
             let frames =
                 testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
-            let stream = frames.iter().next().unwrap();
+            let stream = frames.first().unwrap();
 
             assert_eq!(stream, &frame::Frame::Stream {
                 stream_id: 20,
@@ -9064,7 +12627,7 @@
                 testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
 
             assert_eq!(
-                frames.iter().next(),
+                frames.first(),
                 Some(&frame::Frame::Stream {
                     stream_id: 12,
                     data: stream::RangeBuf::from(&out, off, false),
@@ -9077,7 +12640,7 @@
             let frames =
                 testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
 
-            let stream = frames.iter().next().unwrap();
+            let stream = frames.first().unwrap();
 
             assert_eq!(stream, &frame::Frame::Stream {
                 stream_id: 4,
@@ -9100,7 +12663,7 @@
 
             let frames =
                 testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
-            let stream = frames.iter().next().unwrap();
+            let stream = frames.first().unwrap();
 
             assert_eq!(stream, &frame::Frame::Stream {
                 stream_id: 0,
@@ -9133,7 +12696,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -9186,7 +12749,7 @@
             testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
 
         assert_eq!(
-            frames.iter().next(),
+            frames.first(),
             Some(&frame::Frame::Stream {
                 stream_id: 8,
                 data: stream::RangeBuf::from(b"b", 0, false),
@@ -9200,7 +12763,7 @@
             testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
 
         assert_eq!(
-            frames.iter().next(),
+            frames.first(),
             Some(&frame::Frame::Stream {
                 stream_id: 0,
                 data: stream::RangeBuf::from(b"b", 0, false),
@@ -9214,7 +12777,7 @@
             testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
 
         assert_eq!(
-            frames.iter().next(),
+            frames.first(),
             Some(&frame::Frame::Stream {
                 stream_id: 12,
                 data: stream::RangeBuf::from(b"b", 0, false),
@@ -9227,7 +12790,7 @@
             testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
 
         assert_eq!(
-            frames.iter().next(),
+            frames.first(),
             Some(&frame::Frame::Stream {
                 stream_id: 4,
                 data: stream::RangeBuf::from(b"b", 0, false),
@@ -9253,7 +12816,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(1_000_000);
         config.set_initial_max_stream_data_bidi_local(1_000_000);
@@ -9377,7 +12940,7 @@
     fn early_retransmit() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         // Client sends stream data.
@@ -9394,12 +12957,28 @@
 
         pipe.client.on_timeout();
 
-        let epoch = packet::EPOCH_APPLICATION;
-        assert_eq!(pipe.client.recovery.loss_probes[epoch], 1);
+        let epoch = packet::Epoch::Application;
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .loss_probes[epoch],
+            1,
+        );
 
         // Client retransmits stream data in PTO probe.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
-        assert_eq!(pipe.client.recovery.loss_probes[epoch], 0);
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .loss_probes[epoch],
+            0,
+        );
 
         let frames =
             testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
@@ -9416,6 +12995,7 @@
                 data: stream::RangeBuf::from(b"b", 0, false),
             })
         );
+        assert_eq!(pipe.client.stats().retrans, 1);
     }
 
     #[test]
@@ -9423,7 +13003,7 @@
     fn dont_coalesce_probes() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Client sends Initial packet.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
@@ -9435,13 +13015,29 @@
 
         pipe.client.on_timeout();
 
-        let epoch = packet::EPOCH_INITIAL;
-        assert_eq!(pipe.client.recovery.loss_probes[epoch], 1);
+        let epoch = packet::Epoch::Initial;
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .loss_probes[epoch],
+            1,
+        );
 
         // Client sends PTO probe.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
         assert_eq!(len, 1200);
-        assert_eq!(pipe.client.recovery.loss_probes[epoch], 0);
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .loss_probes[epoch],
+            0,
+        );
 
         // Wait for PTO to expire.
         let timer = pipe.client.timeout().unwrap();
@@ -9449,24 +13045,48 @@
 
         pipe.client.on_timeout();
 
-        assert_eq!(pipe.client.recovery.loss_probes[epoch], 2);
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .loss_probes[epoch],
+            2,
+        );
 
         // Client sends first PTO probe.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
         assert_eq!(len, 1200);
-        assert_eq!(pipe.client.recovery.loss_probes[epoch], 1);
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .loss_probes[epoch],
+            1,
+        );
 
         // Client sends second PTO probe.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
         assert_eq!(len, 1200);
-        assert_eq!(pipe.client.recovery.loss_probes[epoch], 0);
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .loss_probes[epoch],
+            0,
+        );
     }
 
     #[test]
     fn coalesce_padding_short() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Client sends first flight.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
@@ -9508,7 +13128,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
 
         let mut pipe = testing::Pipe::with_server_config(&mut config).unwrap();
@@ -9552,7 +13172,7 @@
     fn handshake_packet_type_corruption() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
 
         // Client sends padded Initial.
         let (len, _) = pipe.client.send(&mut buf).unwrap();
@@ -9565,13 +13185,21 @@
         testing::process_flight(&mut pipe.client, flight).unwrap();
 
         // Client sends Initial packet with ACK.
-        let (ty, len) = pipe.client.send_single(&mut buf, false).unwrap();
+        let active_pid =
+            pipe.client.paths.get_active_path_id().expect("no active");
+        let (ty, len) = pipe
+            .client
+            .send_single(&mut buf, active_pid, false)
+            .unwrap();
         assert_eq!(ty, Type::Initial);
 
         assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len));
 
         // Client sends Handshake packet.
-        let (ty, len) = pipe.client.send_single(&mut buf, false).unwrap();
+        let (ty, len) = pipe
+            .client
+            .send_single(&mut buf, active_pid, false)
+            .unwrap();
         assert_eq!(ty, Type::Handshake);
 
         // Packet type is corrupted to Initial.
@@ -9586,7 +13214,7 @@
 
     #[test]
     fn dgram_send_fails_invalidstate() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(
@@ -9608,7 +13236,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -9627,14 +13255,26 @@
             assert_eq!(pipe.client.dgram_send(&send_buf), Ok(()));
         }
 
-        assert!(!pipe.client.recovery.app_limited());
+        assert!(!pipe
+            .client
+            .paths
+            .get_active()
+            .expect("no active")
+            .recovery
+            .app_limited());
         assert_eq!(pipe.client.dgram_send_queue.byte_size(), 1_000_000);
 
         let (len, _) = pipe.client.send(&mut buf).unwrap();
 
         assert_ne!(pipe.client.dgram_send_queue.byte_size(), 0);
         assert_ne!(pipe.client.dgram_send_queue.byte_size(), 1_000_000);
-        assert!(!pipe.client.recovery.app_limited());
+        assert!(!pipe
+            .client
+            .paths
+            .get_active()
+            .expect("no active")
+            .recovery
+            .app_limited());
 
         assert_eq!(pipe.server_recv(&mut buf[..len]), Ok(len));
 
@@ -9647,7 +13287,13 @@
         assert_ne!(pipe.client.dgram_send_queue.byte_size(), 0);
         assert_ne!(pipe.client.dgram_send_queue.byte_size(), 1_000_000);
 
-        assert!(!pipe.client.recovery.app_limited());
+        assert!(!pipe
+            .client
+            .paths
+            .get_active()
+            .expect("no active")
+            .recovery
+            .app_limited());
     }
 
     #[test]
@@ -9662,7 +13308,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -9699,7 +13345,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -9707,7 +13353,7 @@
         config.set_initial_max_stream_data_uni(10);
         config.set_initial_max_streams_bidi(3);
         config.set_initial_max_streams_uni(3);
-        config.enable_dgram(true, 10, 10);
+        config.enable_dgram(true, 2, 3);
         config.verify_peer(false);
 
         let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
@@ -9719,6 +13365,7 @@
         assert_eq!(pipe.client.dgram_send(b"hello, world"), Ok(()));
         assert_eq!(pipe.client.dgram_send(b"ciao, mondo"), Ok(()));
         assert_eq!(pipe.client.dgram_send(b"hola, mundo"), Ok(()));
+        assert!(pipe.client.is_dgram_send_queue_full());
 
         assert_eq!(pipe.client.dgram_send_queue_byte_size(), 34);
 
@@ -9727,6 +13374,7 @@
 
         assert_eq!(pipe.client.dgram_send_queue_len(), 2);
         assert_eq!(pipe.client.dgram_send_queue_byte_size(), 23);
+        assert!(!pipe.client.is_dgram_send_queue_full());
 
         // Before packets exchanged, no dgrams on server receive side.
         assert_eq!(pipe.server.dgram_recv_queue_len(), 0);
@@ -9739,11 +13387,13 @@
 
         assert_eq!(pipe.server.dgram_recv_queue_len(), 2);
         assert_eq!(pipe.server.dgram_recv_queue_byte_size(), 23);
+        assert!(pipe.server.is_dgram_recv_queue_full());
 
         let result1 = pipe.server.dgram_recv(&mut buf);
         assert_eq!(result1, Ok(12));
         assert_eq!(buf[0], b'h');
         assert_eq!(buf[1], b'e');
+        assert!(!pipe.server.is_dgram_recv_queue_full());
 
         let result2 = pipe.server.dgram_recv(&mut buf);
         assert_eq!(result2, Ok(11));
@@ -9769,7 +13419,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -9815,7 +13465,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -9862,7 +13512,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -9913,7 +13563,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(30);
         config.set_initial_max_stream_data_bidi_local(15);
@@ -9989,7 +13639,7 @@
     fn close() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.client.close(false, 0x1234, b"hello?"), Ok(()));
@@ -10005,7 +13655,7 @@
             testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
 
         assert_eq!(
-            frames.iter().next(),
+            frames.first(),
             Some(&frame::Frame::ConnectionClose {
                 error_code: 0x1234,
                 frame_type: 0,
@@ -10015,10 +13665,10 @@
     }
 
     #[test]
-    fn app_close() {
+    fn app_close_by_client() {
         let mut buf = [0; 65535];
 
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.client.close(true, 0x1234, b"hello!"), Ok(()));
@@ -10031,7 +13681,7 @@
             testing::decode_pkt(&mut pipe.server, &mut buf, len).unwrap();
 
         assert_eq!(
-            frames.iter().next(),
+            frames.first(),
             Some(&frame::Frame::ApplicationClose {
                 error_code: 0x1234,
                 reason: b"hello!".to_vec(),
@@ -10040,8 +13690,169 @@
     }
 
     #[test]
+    fn app_close_by_server_during_handshake_private_key_failure() {
+        let mut pipe = testing::Pipe::new().unwrap();
+        pipe.server.handshake.set_failing_private_key_method();
+
+        // Client sends initial flight.
+        let flight = testing::emit_flight(&mut pipe.client).unwrap();
+        assert_eq!(
+            testing::process_flight(&mut pipe.server, flight),
+            Err(Error::TlsFail)
+        );
+
+        let flight = testing::emit_flight(&mut pipe.server).unwrap();
+
+        // Both connections are not established.
+        assert!(!pipe.server.is_established());
+        assert!(!pipe.client.is_established());
+
+        // Connection should already be closed due the failure during key signing.
+        assert_eq!(
+            pipe.server.close(true, 123, b"fail whale"),
+            Err(Error::Done)
+        );
+
+        testing::process_flight(&mut pipe.client, flight).unwrap();
+
+        // Connection should already be closed due the failure during key signing.
+        assert_eq!(
+            pipe.client.close(true, 123, b"fail whale"),
+            Err(Error::Done)
+        );
+
+        // Connection is not established on the server / client (and never
+        // will be)
+        assert!(!pipe.server.is_established());
+        assert!(!pipe.client.is_established());
+
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(
+            pipe.server.local_error(),
+            Some(&ConnectionError {
+                is_app: false,
+                error_code: 0x01,
+                reason: vec![],
+            })
+        );
+        assert_eq!(
+            pipe.client.peer_error(),
+            Some(&ConnectionError {
+                is_app: false,
+                error_code: 0x01,
+                reason: vec![],
+            })
+        );
+    }
+
+    #[test]
+    fn app_close_by_server_during_handshake_not_established() {
+        let mut pipe = testing::Pipe::new().unwrap();
+
+        // Client sends initial flight.
+        let flight = testing::emit_flight(&mut pipe.client).unwrap();
+        testing::process_flight(&mut pipe.server, flight).unwrap();
+
+        let flight = testing::emit_flight(&mut pipe.server).unwrap();
+
+        // Both connections are not established.
+        assert!(!pipe.client.is_established() && !pipe.server.is_established());
+
+        // Server closes before connection is established.
+        pipe.server.close(true, 123, b"fail whale").unwrap();
+
+        testing::process_flight(&mut pipe.client, flight).unwrap();
+
+        // Connection is established on the client.
+        assert!(pipe.client.is_established());
+
+        // Client sends after connection is established.
+        pipe.client.stream_send(0, b"badauthtoken", true).unwrap();
+
+        let flight = testing::emit_flight(&mut pipe.client).unwrap();
+        testing::process_flight(&mut pipe.server, flight).unwrap();
+
+        // Connection is not established on the server (and never will be)
+        assert!(!pipe.server.is_established());
+
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(
+            pipe.server.local_error(),
+            Some(&ConnectionError {
+                is_app: false,
+                error_code: 0x0c,
+                reason: vec![],
+            })
+        );
+        assert_eq!(
+            pipe.client.peer_error(),
+            Some(&ConnectionError {
+                is_app: false,
+                error_code: 0x0c,
+                reason: vec![],
+            })
+        );
+    }
+
+    #[test]
+    fn app_close_by_server_during_handshake_established() {
+        let mut pipe = testing::Pipe::new().unwrap();
+
+        // Client sends initial flight.
+        let flight = testing::emit_flight(&mut pipe.client).unwrap();
+        testing::process_flight(&mut pipe.server, flight).unwrap();
+
+        let flight = testing::emit_flight(&mut pipe.server).unwrap();
+
+        // Both connections are not established.
+        assert!(!pipe.client.is_established() && !pipe.server.is_established());
+
+        testing::process_flight(&mut pipe.client, flight).unwrap();
+
+        // Connection is established on the client.
+        assert!(pipe.client.is_established());
+
+        // Client sends after connection is established.
+        pipe.client.stream_send(0, b"badauthtoken", true).unwrap();
+
+        let flight = testing::emit_flight(&mut pipe.client).unwrap();
+        testing::process_flight(&mut pipe.server, flight).unwrap();
+
+        // Connection is established on the server but the Handshake ACK has not
+        // been sent yet.
+        assert!(pipe.server.is_established());
+
+        // Server closes after connection is established.
+        pipe.server
+            .close(true, 123, b"Invalid authentication")
+            .unwrap();
+
+        // Server sends Handshake ACK and then 1RTT CONNECTION_CLOSE.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(
+            pipe.server.local_error(),
+            Some(&ConnectionError {
+                is_app: true,
+                error_code: 123,
+                reason: b"Invalid authentication".to_vec()
+            })
+        );
+        assert_eq!(
+            pipe.client.peer_error(),
+            Some(&ConnectionError {
+                is_app: true,
+                error_code: 123,
+                reason: b"Invalid authentication".to_vec()
+            })
+        );
+    }
+
+    #[test]
     fn peer_error() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.server.close(false, 0x1234, b"hello?"), Ok(()));
@@ -10059,7 +13870,7 @@
 
     #[test]
     fn app_peer_error() {
-        let mut pipe = testing::Pipe::default().unwrap();
+        let mut pipe = testing::Pipe::new().unwrap();
         assert_eq!(pipe.handshake(), Ok(()));
 
         assert_eq!(pipe.server.close(true, 0x1234, b"hello!"), Ok(()));
@@ -10076,6 +13887,25 @@
     }
 
     #[test]
+    fn local_error() {
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        assert_eq!(pipe.server.local_error(), None);
+
+        assert_eq!(pipe.server.close(true, 0x1234, b"hello!"), Ok(()));
+
+        assert_eq!(
+            pipe.server.local_error(),
+            Some(&ConnectionError {
+                is_app: true,
+                error_code: 0x1234u64,
+                reason: b"hello!".to_vec()
+            })
+        );
+    }
+
+    #[test]
     fn update_max_datagram_size() {
         let mut client_scid = [0; 16];
         rand::rand_bytes(&mut client_scid[..]);
@@ -10089,7 +13919,7 @@
 
         let mut client_config = Config::new(crate::PROTOCOL_VERSION).unwrap();
         client_config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         client_config.set_max_recv_udp_payload_size(1200);
 
@@ -10101,11 +13931,11 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         server_config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         server_config.verify_peer(false);
         server_config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         // Larger than the client
         server_config.set_max_send_udp_payload_size(1500);
@@ -10115,22 +13945,53 @@
                 Some("quic.tech"),
                 &client_scid,
                 client_addr,
+                server_addr,
                 &mut client_config,
             )
             .unwrap(),
-            server: accept(&server_scid, None, server_addr, &mut server_config)
-                .unwrap(),
+            server: accept(
+                &server_scid,
+                None,
+                server_addr,
+                client_addr,
+                &mut server_config,
+            )
+            .unwrap(),
         };
 
         // Before handshake
-        assert_eq!(pipe.server.recovery.max_datagram_size(), 1500);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .max_datagram_size(),
+            1500,
+        );
 
         assert_eq!(pipe.handshake(), Ok(()));
 
         // After handshake, max_datagram_size should match to client's
         // max_recv_udp_payload_size which is smaller
-        assert_eq!(pipe.server.recovery.max_datagram_size(), 1200);
-        assert_eq!(pipe.server.recovery.cwnd(), 12000);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .max_datagram_size(),
+            1200,
+        );
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .recovery
+                .cwnd(),
+            12000,
+        );
     }
 
     #[test]
@@ -10147,7 +14008,7 @@
             .load_priv_key_from_pem_file("examples/cert.key")
             .unwrap();
         config
-            .set_application_protos(b"\x06proto1\x06proto2")
+            .set_application_protos(&[b"proto1", b"proto2"])
             .unwrap();
         config.set_initial_max_data(100000);
         config.set_initial_max_stream_data_bidi_local(10000);
@@ -10189,30 +14050,1800 @@
         assert_eq!(pipe.server.stream_send(8, &buf[..5000], false), Ok(2000));
 
         // No more connection send capacity.
-        assert_eq!(pipe.server.stream_send(12, &buf[..5000], false), Ok(0));
+        assert_eq!(
+            pipe.server.stream_send(12, &buf[..5000], false),
+            Err(Error::Done)
+        );
         assert_eq!(pipe.server.tx_cap, 0);
 
         assert_eq!(pipe.advance(), Ok(()));
     }
+
+    #[cfg(feature = "boringssl-boring-crate")]
+    #[test]
+    fn user_provided_boring_ctx() -> Result<()> {
+        // Manually construct boring ssl ctx for server
+        let server_tls_ctx = {
+            let mut builder = boring::ssl::SslContextBuilder::new(
+                boring::ssl::SslMethod::tls(),
+            )
+            .unwrap();
+            builder
+                .set_certificate_chain_file("examples/cert.crt")
+                .unwrap();
+            builder
+                .set_private_key_file(
+                    "examples/cert.key",
+                    boring::ssl::SslFiletype::PEM,
+                )
+                .unwrap();
+            builder.build()
+        };
+
+        let mut server_config =
+            Config::with_boring_ssl_ctx(crate::PROTOCOL_VERSION, server_tls_ctx)?;
+        let mut client_config = Config::new(crate::PROTOCOL_VERSION)?;
+        client_config.load_cert_chain_from_pem_file("examples/cert.crt")?;
+        client_config.load_priv_key_from_pem_file("examples/cert.key")?;
+
+        for config in [&mut client_config, &mut server_config] {
+            config.set_application_protos(&[b"proto1", b"proto2"])?;
+            config.set_initial_max_data(30);
+            config.set_initial_max_stream_data_bidi_local(15);
+            config.set_initial_max_stream_data_bidi_remote(15);
+            config.set_initial_max_stream_data_uni(10);
+            config.set_initial_max_streams_bidi(3);
+            config.set_initial_max_streams_uni(3);
+            config.set_max_idle_timeout(180_000);
+            config.verify_peer(false);
+            config.set_ack_delay_exponent(8);
+        }
+
+        let mut client_scid = [0; 16];
+        rand::rand_bytes(&mut client_scid[..]);
+        let client_scid = ConnectionId::from_ref(&client_scid);
+        let client_addr = "127.0.0.1:1234".parse().unwrap();
+
+        let mut server_scid = [0; 16];
+        rand::rand_bytes(&mut server_scid[..]);
+        let server_scid = ConnectionId::from_ref(&server_scid);
+        let server_addr = "127.0.0.1:4321".parse().unwrap();
+
+        let mut pipe = testing::Pipe {
+            client: connect(
+                Some("quic.tech"),
+                &client_scid,
+                client_addr,
+                server_addr,
+                &mut client_config,
+            )?,
+            server: accept(
+                &server_scid,
+                None,
+                server_addr,
+                client_addr,
+                &mut server_config,
+            )?,
+        };
+
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        Ok(())
+    }
+
+    #[test]
+    /// Tests that resetting a stream restores flow control for unsent data.
+    fn last_tx_data_larger_than_tx_data() {
+        let mut config = Config::new(PROTOCOL_VERSION).unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.set_initial_max_data(12000);
+        config.set_initial_max_stream_data_bidi_local(20000);
+        config.set_initial_max_stream_data_bidi_remote(20000);
+        config.set_max_recv_udp_payload_size(1200);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_client_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Client opens stream 4 and 8.
+        assert_eq!(pipe.client.stream_send(4, b"a", true), Ok(1));
+        assert_eq!(pipe.client.stream_send(8, b"b", true), Ok(1));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Server reads stream data.
+        let mut b = [0; 15];
+        pipe.server.stream_recv(4, &mut b).unwrap();
+
+        // Server sends stream data close to cwnd (12000).
+        let buf = [0; 10000];
+        assert_eq!(pipe.server.stream_send(4, &buf, false), Ok(10000));
+
+        testing::emit_flight(&mut pipe.server).unwrap();
+
+        // Server buffers some data, until send capacity limit reached.
+        let mut buf = [0; 1200];
+        assert_eq!(pipe.server.stream_send(4, &buf, false), Ok(1200));
+        assert_eq!(pipe.server.stream_send(8, &buf, false), Ok(800));
+        assert_eq!(pipe.server.stream_send(4, &buf, false), Err(Error::Done));
+
+        // Wait for PTO to expire.
+        let timer = pipe.server.timeout().unwrap();
+        std::thread::sleep(timer + time::Duration::from_millis(1));
+
+        pipe.server.on_timeout();
+
+        // Server sends PTO probe (not limited to cwnd),
+        // to update last_tx_data.
+        let (len, _) = pipe.server.send(&mut buf).unwrap();
+        assert_eq!(len, 1200);
+
+        // Client sends STOP_SENDING to decrease tx_data
+        // by unsent data. It will make last_tx_data > tx_data
+        // and trigger #1232 bug.
+        let frames = [frame::Frame::StopSending {
+            stream_id: 4,
+            error_code: 42,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        pipe.send_pkt_to_server(pkt_type, &frames, &mut buf)
+            .unwrap();
+    }
+
+    /// Tests that when the client provides a new ConnectionId, it eventually
+    /// reaches the server and notifies the application.
+    #[test]
+    fn send_connection_ids() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(3);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // So far, there should not have any QUIC event.
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(pipe.client.source_cids_left(), 2);
+
+        let (scid, reset_token) = testing::create_cid_and_reset_token(16);
+        assert_eq!(pipe.client.new_source_cid(&scid, reset_token, false), Ok(1));
+
+        // Let exchange packets over the connection.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // At this point, the server should be notified that it has a new CID.
+        assert_eq!(pipe.server.available_dcids(), 1);
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(pipe.client.source_cids_left(), 1);
+
+        // Now, a second CID can be provided.
+        let (scid, reset_token) = testing::create_cid_and_reset_token(16);
+        assert_eq!(pipe.client.new_source_cid(&scid, reset_token, false), Ok(2));
+
+        // Let exchange packets over the connection.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // At this point, the server should be notified that it has a new CID.
+        assert_eq!(pipe.server.available_dcids(), 2);
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(pipe.client.source_cids_left(), 0);
+
+        // If now the client tries to send another CID, it reports an error
+        // since it exceeds the limit of active CIDs.
+        let (scid, reset_token) = testing::create_cid_and_reset_token(16);
+        assert_eq!(
+            pipe.client.new_source_cid(&scid, reset_token, false),
+            Err(Error::IdLimit),
+        );
+    }
+
+    #[test]
+    /// Exercices the handling of NEW_CONNECTION_ID and RETIRE_CONNECTION_ID
+    /// frames.
+    fn connection_id_handling() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // So far, there should not have any QUIC event.
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(pipe.client.source_cids_left(), 1);
+
+        let scid = pipe.client.source_id().into_owned();
+
+        let (scid_1, reset_token_1) = testing::create_cid_and_reset_token(16);
+        assert_eq!(
+            pipe.client.new_source_cid(&scid_1, reset_token_1, false),
+            Ok(1)
+        );
+
+        // Let exchange packets over the connection.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // At this point, the server should be notified that it has a new CID.
+        assert_eq!(pipe.server.available_dcids(), 1);
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(pipe.client.source_cids_left(), 0);
+
+        // Now we assume that the client wants to advertise more source
+        // Connection IDs than the advertised limit. This is valid if it
+        // requests its peer to retire enough Connection IDs to fit within the
+        // limits.
+
+        let (scid_2, reset_token_2) = testing::create_cid_and_reset_token(16);
+        assert_eq!(
+            pipe.client.new_source_cid(&scid_2, reset_token_2, true),
+            Ok(2)
+        );
+
+        // Let exchange packets over the connection.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // At this point, the server still have a spare DCID.
+        assert_eq!(pipe.server.available_dcids(), 1);
+        assert_eq!(pipe.server.path_event_next(), None);
+
+        // Client should have received a retired notification.
+        assert_eq!(pipe.client.retired_scid_next(), Some(scid));
+        assert_eq!(pipe.client.retired_scid_next(), None);
+
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(pipe.client.source_cids_left(), 0);
+
+        // The active Destination Connection ID of the server should now be the
+        // one with sequence number 1.
+        assert_eq!(pipe.server.destination_id(), scid_1);
+
+        // Now tries to experience CID retirement. If the server tries to remove
+        // non-existing DCIDs, it fails.
+        assert_eq!(
+            pipe.server.retire_destination_cid(0),
+            Err(Error::InvalidState)
+        );
+        assert_eq!(
+            pipe.server.retire_destination_cid(3),
+            Err(Error::InvalidState)
+        );
+
+        // Now it removes DCID with sequence 1.
+        assert_eq!(pipe.server.retire_destination_cid(1), Ok(()));
+
+        // Let exchange packets over the connection.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(pipe.client.retired_scid_next(), Some(scid_1));
+        assert_eq!(pipe.client.retired_scid_next(), None);
+
+        assert_eq!(pipe.server.destination_id(), scid_2);
+        assert_eq!(pipe.server.available_dcids(), 0);
+
+        // Trying to remove the last DCID triggers an error.
+        assert_eq!(
+            pipe.server.retire_destination_cid(2),
+            Err(Error::OutOfIdentifiers)
+        );
+    }
+
+    #[test]
+    fn lost_connection_id_frames() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        let scid = pipe.client.source_id().into_owned();
+
+        let (scid_1, reset_token_1) = testing::create_cid_and_reset_token(16);
+        assert_eq!(
+            pipe.client.new_source_cid(&scid_1, reset_token_1, false),
+            Ok(1)
+        );
+
+        // Packets are sent, but never received.
+        testing::emit_flight(&mut pipe.client).unwrap();
+
+        // Wait until timer expires. Since the RTT is very low, wait a bit more.
+        let timer = pipe.client.timeout().unwrap();
+        std::thread::sleep(timer + time::Duration::from_millis(1));
+
+        pipe.client.on_timeout();
+
+        // Let exchange packets over the connection.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // At this point, the server should be notified that it has a new CID.
+        assert_eq!(pipe.server.available_dcids(), 1);
+
+        // Now the server retires the first Destination CID.
+        assert_eq!(pipe.server.retire_destination_cid(0), Ok(()));
+
+        // But the packet never reaches the client.
+        testing::emit_flight(&mut pipe.server).unwrap();
+
+        // Wait until timer expires. Since the RTT is very low, wait a bit more.
+        let timer = pipe.server.timeout().unwrap();
+        std::thread::sleep(timer + time::Duration::from_millis(1));
+
+        pipe.server.on_timeout();
+
+        // Let exchange packets over the connection.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(pipe.client.retired_scid_next(), Some(scid));
+        assert_eq!(pipe.client.retired_scid_next(), None);
+    }
+
+    #[test]
+    fn sending_duplicate_scids() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(3);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        let (scid_1, reset_token_1) = testing::create_cid_and_reset_token(16);
+        assert_eq!(
+            pipe.client.new_source_cid(&scid_1, reset_token_1, false),
+            Ok(1)
+        );
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Trying to send the same CID with a different reset token raises an
+        // InvalidState error.
+        let reset_token_2 = reset_token_1.wrapping_add(1);
+        assert_eq!(
+            pipe.client.new_source_cid(&scid_1, reset_token_2, false),
+            Err(Error::InvalidState),
+        );
+
+        // Retrying to send the exact same CID with the same token returns the
+        // previously assigned CID seq, but without sending anything.
+        assert_eq!(
+            pipe.client.new_source_cid(&scid_1, reset_token_1, false),
+            Ok(1)
+        );
+        assert_eq!(pipe.client.ids.has_new_scids(), false);
+
+        // Now retire this new CID.
+        assert_eq!(pipe.server.retire_destination_cid(1), Ok(()));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // It is up to the application to ensure that a given SCID is not reused
+        // later.
+        assert_eq!(
+            pipe.client.new_source_cid(&scid_1, reset_token_1, false),
+            Ok(2),
+        );
+    }
+
+    // Utility function.
+    fn pipe_with_exchanged_cids(
+        config: &mut Config, client_scid_len: usize, server_scid_len: usize,
+        additional_cids: usize,
+    ) -> testing::Pipe {
+        let mut pipe = testing::Pipe::with_config_and_scid_lengths(
+            config,
+            client_scid_len,
+            server_scid_len,
+        )
+        .unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        let mut c_cids = Vec::new();
+        let mut c_reset_tokens = Vec::new();
+        let mut s_cids = Vec::new();
+        let mut s_reset_tokens = Vec::new();
+
+        for i in 0..additional_cids {
+            if client_scid_len > 0 {
+                let (c_cid, c_reset_token) =
+                    testing::create_cid_and_reset_token(client_scid_len);
+                c_cids.push(c_cid);
+                c_reset_tokens.push(c_reset_token);
+
+                assert_eq!(
+                    pipe.client.new_source_cid(
+                        &c_cids[i],
+                        c_reset_tokens[i],
+                        true
+                    ),
+                    Ok(i as u64 + 1)
+                );
+            }
+
+            if server_scid_len > 0 {
+                let (s_cid, s_reset_token) =
+                    testing::create_cid_and_reset_token(server_scid_len);
+                s_cids.push(s_cid);
+                s_reset_tokens.push(s_reset_token);
+                assert_eq!(
+                    pipe.server.new_source_cid(
+                        &s_cids[i],
+                        s_reset_tokens[i],
+                        true
+                    ),
+                    Ok(i as u64 + 1)
+                );
+            }
+        }
+
+        // Let exchange packets over the connection.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        if client_scid_len > 0 {
+            assert_eq!(pipe.server.available_dcids(), additional_cids);
+        }
+
+        if server_scid_len > 0 {
+            assert_eq!(pipe.client.available_dcids(), additional_cids);
+        }
+
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(pipe.client.path_event_next(), None);
+
+        pipe
+    }
+
+    #[test]
+    fn path_validation() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+
+        // We cannot probe a new path if there are not enough identifiers.
+        assert_eq!(
+            pipe.client.probe_path(client_addr_2, server_addr),
+            Err(Error::OutOfIdentifiers)
+        );
+
+        let (c_cid, c_reset_token) = testing::create_cid_and_reset_token(16);
+
+        assert_eq!(
+            pipe.client.new_source_cid(&c_cid, c_reset_token, true),
+            Ok(1)
+        );
+
+        let (s_cid, s_reset_token) = testing::create_cid_and_reset_token(16);
+        assert_eq!(
+            pipe.server.new_source_cid(&s_cid, s_reset_token, true),
+            Ok(1)
+        );
+
+        // We need to exchange the CIDs first.
+        assert_eq!(
+            pipe.client.probe_path(client_addr_2, server_addr),
+            Err(Error::OutOfIdentifiers)
+        );
+
+        // Let exchange packets over the connection.
+        assert_eq!(pipe.advance(), Ok(()));
+
+        assert_eq!(pipe.server.available_dcids(), 1);
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(pipe.client.available_dcids(), 1);
+        assert_eq!(pipe.client.path_event_next(), None);
+
+        // Now the path probing can work.
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+
+        // But the server cannot probe a yet-unseen path.
+        assert_eq!(
+            pipe.server.probe_path(server_addr, client_addr_2),
+            Err(Error::InvalidState),
+        );
+
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // The path should be validated at some point.
+        assert_eq!(
+            pipe.client.path_event_next(),
+            Some(PathEvent::Validated(client_addr_2, server_addr)),
+        );
+        assert_eq!(pipe.client.path_event_next(), None);
+
+        // The server should be notified of this new path.
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::New(server_addr, client_addr_2)),
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::Validated(server_addr, client_addr_2)),
+        );
+        assert_eq!(pipe.server.path_event_next(), None);
+
+        // The server can later probe the path again.
+        assert_eq!(pipe.server.probe_path(server_addr, client_addr_2), Ok(1));
+
+        // This should not trigger any event at client side.
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(pipe.server.path_event_next(), None);
+    }
+
+    #[test]
+    fn losing_probing_packets() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 16, 16, 1);
+
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+
+        // The client creates the PATH CHALLENGE, but it is lost.
+        testing::emit_flight(&mut pipe.client).unwrap();
+
+        // Wait until probing timer expires. Since the RTT is very low,
+        // wait a bit more.
+        let probed_pid = pipe
+            .client
+            .paths
+            .path_id_from_addrs(&(client_addr_2, server_addr))
+            .unwrap();
+        let probe_instant = pipe
+            .client
+            .paths
+            .get(probed_pid)
+            .unwrap()
+            .recovery
+            .loss_detection_timer()
+            .unwrap();
+        let timer = probe_instant.duration_since(time::Instant::now());
+        std::thread::sleep(timer + time::Duration::from_millis(1));
+
+        pipe.client.on_timeout();
+
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // The path should be validated at some point.
+        assert_eq!(
+            pipe.client.path_event_next(),
+            Some(PathEvent::Validated(client_addr_2, server_addr))
+        );
+        assert_eq!(pipe.client.path_event_next(), None);
+
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::New(server_addr, client_addr_2))
+        );
+        // The path should be validated at some point.
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::Validated(server_addr, client_addr_2))
+        );
+        assert_eq!(pipe.server.path_event_next(), None);
+    }
+
+    #[test]
+    fn failed_path_validation() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 16, 16, 1);
+
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+
+        for _ in 0..MAX_PROBING_TIMEOUTS {
+            // The client creates the PATH CHALLENGE, but it is always lost.
+            testing::emit_flight(&mut pipe.client).unwrap();
+
+            // Wait until probing timer expires. Since the RTT is very low,
+            // wait a bit more.
+            let probed_pid = pipe
+                .client
+                .paths
+                .path_id_from_addrs(&(client_addr_2, server_addr))
+                .unwrap();
+            let probe_instant = pipe
+                .client
+                .paths
+                .get(probed_pid)
+                .unwrap()
+                .recovery
+                .loss_detection_timer()
+                .unwrap();
+            let timer = probe_instant.duration_since(time::Instant::now());
+            std::thread::sleep(timer + time::Duration::from_millis(1));
+
+            pipe.client.on_timeout();
+        }
+
+        assert_eq!(
+            pipe.client.path_event_next(),
+            Some(PathEvent::FailedValidation(client_addr_2, server_addr)),
+        );
+    }
+
+    #[test]
+    fn client_discard_unknown_address() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_uni(3);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Server sends stream data.
+        assert_eq!(pipe.server.stream_send(3, b"a", true), Ok(1));
+
+        let mut flight =
+            testing::emit_flight(&mut pipe.server).expect("no packet");
+        // Let's change the address info.
+        flight
+            .iter_mut()
+            .for_each(|(_, si)| si.from = "127.0.0.1:9292".parse().unwrap());
+        assert_eq!(testing::process_flight(&mut pipe.client, flight), Ok(()));
+        assert_eq!(pipe.client.paths.len(), 1);
+    }
+
+    #[test]
+    fn path_validation_limited_mtu() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 16, 16, 1);
+
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+        // Limited MTU of 1199 bytes for some reason.
+        testing::process_flight(
+            &mut pipe.server,
+            testing::emit_flight_with_max_buffer(&mut pipe.client, 1199)
+                .expect("no packet"),
+        )
+        .expect("error when processing client packets");
+        testing::process_flight(
+            &mut pipe.client,
+            testing::emit_flight(&mut pipe.server).expect("no packet"),
+        )
+        .expect("error when processing client packets");
+        let probed_pid = pipe
+            .client
+            .paths
+            .path_id_from_addrs(&(client_addr_2, server_addr))
+            .unwrap();
+        assert_eq!(
+            pipe.client.paths.get(probed_pid).unwrap().validated(),
+            false,
+        );
+        assert_eq!(pipe.client.path_event_next(), None);
+        // Now let the client probe at its MTU.
+        assert_eq!(pipe.advance(), Ok(()));
+        assert_eq!(pipe.client.paths.get(probed_pid).unwrap().validated(), true);
+        assert_eq!(
+            pipe.client.path_event_next(),
+            Some(PathEvent::Validated(client_addr_2, server_addr))
+        );
+    }
+
+    #[test]
+    fn path_probing_dos() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 16, 16, 1);
+
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // The path should be validated at some point.
+        assert_eq!(
+            pipe.client.path_event_next(),
+            Some(PathEvent::Validated(client_addr_2, server_addr))
+        );
+        assert_eq!(pipe.client.path_event_next(), None);
+
+        // The server should be notified of this new path.
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::New(server_addr, client_addr_2))
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::Validated(server_addr, client_addr_2))
+        );
+        assert_eq!(pipe.server.path_event_next(), None);
+
+        assert_eq!(pipe.server.paths.len(), 2);
+
+        // Now forge a packet reusing the unverified path's CID over another
+        // 4-tuple.
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+        let client_addr_3 = "127.0.0.1:9012".parse().unwrap();
+        let mut flight =
+            testing::emit_flight(&mut pipe.client).expect("no generated packet");
+        flight
+            .iter_mut()
+            .for_each(|(_, si)| si.from = client_addr_3);
+        testing::process_flight(&mut pipe.server, flight)
+            .expect("failed to process");
+        assert_eq!(pipe.server.paths.len(), 2);
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::ReusedSourceConnectionId(
+                1,
+                (server_addr, client_addr_2),
+                (server_addr, client_addr_3)
+            ))
+        );
+        assert_eq!(pipe.server.path_event_next(), None);
+    }
+
+    #[test]
+    fn retiring_active_path_dcid() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 16, 16, 1);
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+
+        assert_eq!(
+            pipe.client.retire_destination_cid(0),
+            Err(Error::OutOfIdentifiers)
+        );
+    }
+
+    #[test]
+    fn send_on_path_test() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_initial_max_data(100000);
+        config.set_initial_max_stream_data_bidi_local(100000);
+        config.set_initial_max_stream_data_bidi_remote(100000);
+        config.set_initial_max_streams_bidi(2);
+        config.set_active_connection_id_limit(4);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 16, 16, 3);
+
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr = testing::Pipe::client_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+
+        let mut buf = [0; 65535];
+        // There is nothing to send on the initial path.
+        assert_eq!(
+            pipe.client.send_on_path(
+                &mut buf,
+                Some(client_addr),
+                Some(server_addr)
+            ),
+            Err(Error::Done)
+        );
+        // Client should send padded PATH_CHALLENGE.
+        let (sent, si) = pipe
+            .client
+            .send_on_path(&mut buf, Some(client_addr_2), Some(server_addr))
+            .expect("No error");
+        assert_eq!(sent, MIN_CLIENT_INITIAL_LEN);
+        assert_eq!(si.from, client_addr_2);
+        assert_eq!(si.to, server_addr);
+        // A non-existing 4-tuple raises an InvalidState.
+        let client_addr_3 = "127.0.0.1:9012".parse().unwrap();
+        let server_addr_2 = "127.0.0.1:9876".parse().unwrap();
+        assert_eq!(
+            pipe.client.send_on_path(
+                &mut buf,
+                Some(client_addr_3),
+                Some(server_addr)
+            ),
+            Err(Error::InvalidState)
+        );
+        assert_eq!(
+            pipe.client.send_on_path(
+                &mut buf,
+                Some(client_addr),
+                Some(server_addr_2)
+            ),
+            Err(Error::InvalidState)
+        );
+
+        // Let's introduce some additional path challenges and data exchange.
+        assert_eq!(pipe.client.probe_path(client_addr, server_addr_2), Ok(2));
+        assert_eq!(pipe.client.probe_path(client_addr_3, server_addr), Ok(3));
+        // Just to fit in two packets.
+        assert_eq!(pipe.client.stream_send(0, &buf[..1201], true), Ok(1201));
+
+        // PATH_CHALLENGE
+        let (sent, si) = pipe
+            .client
+            .send_on_path(&mut buf, Some(client_addr), None)
+            .expect("No error");
+        assert_eq!(sent, MIN_CLIENT_INITIAL_LEN);
+        assert_eq!(si.from, client_addr);
+        assert_eq!(si.to, server_addr_2);
+        // STREAM frame on active path.
+        let (_, si) = pipe
+            .client
+            .send_on_path(&mut buf, Some(client_addr), None)
+            .expect("No error");
+        assert_eq!(si.from, client_addr);
+        assert_eq!(si.to, server_addr);
+        // PATH_CHALLENGE
+        let (sent, si) = pipe
+            .client
+            .send_on_path(&mut buf, None, Some(server_addr))
+            .expect("No error");
+        assert_eq!(sent, MIN_CLIENT_INITIAL_LEN);
+        assert_eq!(si.from, client_addr_3);
+        assert_eq!(si.to, server_addr);
+        // STREAM frame on active path.
+        let (_, si) = pipe
+            .client
+            .send_on_path(&mut buf, None, Some(server_addr))
+            .expect("No error");
+        assert_eq!(si.from, client_addr);
+        assert_eq!(si.to, server_addr);
+
+        // No more data to exchange leads to Error::Done.
+        assert_eq!(
+            pipe.client.send_on_path(&mut buf, Some(client_addr), None),
+            Err(Error::Done)
+        );
+        assert_eq!(
+            pipe.client.send_on_path(&mut buf, None, Some(server_addr)),
+            Err(Error::Done)
+        );
+
+        assert_eq!(
+            pipe.client
+                .paths_iter(client_addr)
+                .collect::<Vec<_>>()
+                .sort(),
+            vec![server_addr, server_addr_2].sort(),
+        );
+        assert_eq!(
+            pipe.client
+                .paths_iter(client_addr_2)
+                .collect::<Vec<_>>()
+                .sort(),
+            vec![server_addr].sort(),
+        );
+        assert_eq!(
+            pipe.client
+                .paths_iter(client_addr_3)
+                .collect::<Vec<_>>()
+                .sort(),
+            vec![server_addr].sort(),
+        );
+    }
+
+    #[test]
+    fn connection_migration() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(3);
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 16, 16, 2);
+
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+        let client_addr_3 = "127.0.0.1:9012".parse().unwrap();
+        let client_addr_4 = "127.0.0.1:8908".parse().unwrap();
+
+        // Case 1: the client first probes the new address, the server too, and
+        // then migrates.
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+        assert_eq!(pipe.advance(), Ok(()));
+        assert_eq!(
+            pipe.client.path_event_next(),
+            Some(PathEvent::Validated(client_addr_2, server_addr))
+        );
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::New(server_addr, client_addr_2))
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::Validated(server_addr, client_addr_2))
+        );
+        assert_eq!(
+            pipe.client.is_path_validated(client_addr_2, server_addr),
+            Ok(true)
+        );
+        assert_eq!(
+            pipe.server.is_path_validated(server_addr, client_addr_2),
+            Ok(true)
+        );
+        // The server can never initiates the connection migration.
+        assert_eq!(
+            pipe.server.migrate(server_addr, client_addr_2),
+            Err(Error::InvalidState)
+        );
+        assert_eq!(pipe.client.migrate(client_addr_2, server_addr), Ok(1));
+        assert_eq!(pipe.client.stream_send(0, b"data", true), Ok(4));
+        assert_eq!(pipe.advance(), Ok(()));
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .local_addr(),
+            client_addr_2
+        );
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            server_addr
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::PeerMigrated(server_addr, client_addr_2))
+        );
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .local_addr(),
+            server_addr
+        );
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            client_addr_2
+        );
+
+        // Case 2: the client migrates on a path that was not previously
+        // validated, and has spare SCIDs/DCIDs to do so.
+        assert_eq!(pipe.client.migrate(client_addr_3, server_addr), Ok(2));
+        assert_eq!(pipe.client.stream_send(4, b"data", true), Ok(4));
+        assert_eq!(pipe.advance(), Ok(()));
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .local_addr(),
+            client_addr_3
+        );
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            server_addr
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::New(server_addr, client_addr_3))
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::Validated(server_addr, client_addr_3))
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::PeerMigrated(server_addr, client_addr_3))
+        );
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .local_addr(),
+            server_addr
+        );
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            client_addr_3
+        );
+
+        // Case 3: the client tries to migrate on the current active path.
+        // This is not an error, but it triggers nothing.
+        assert_eq!(pipe.client.migrate(client_addr_3, server_addr), Ok(2));
+        assert_eq!(pipe.client.stream_send(8, b"data", true), Ok(4));
+        assert_eq!(pipe.advance(), Ok(()));
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .local_addr(),
+            client_addr_3
+        );
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            server_addr
+        );
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .local_addr(),
+            server_addr
+        );
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            client_addr_3
+        );
+
+        // Case 4: the client tries to migrate on a path that was not previously
+        // validated, and has no spare SCIDs/DCIDs. Prevent active migration.
+        assert_eq!(
+            pipe.client.migrate(client_addr_4, server_addr),
+            Err(Error::OutOfIdentifiers)
+        );
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .local_addr(),
+            client_addr_3
+        );
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            server_addr
+        );
+    }
+
+    #[test]
+    fn connection_migration_zero_length_cid() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 0, 16, 1);
+
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+
+        // The client migrates on a path that was not previously
+        // validated, and has spare SCIDs/DCIDs to do so.
+        assert_eq!(pipe.client.migrate(client_addr_2, server_addr), Ok(1));
+        assert_eq!(pipe.client.stream_send(4, b"data", true), Ok(4));
+        assert_eq!(pipe.advance(), Ok(()));
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .local_addr(),
+            client_addr_2
+        );
+        assert_eq!(
+            pipe.client
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            server_addr
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::New(server_addr, client_addr_2))
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::Validated(server_addr, client_addr_2))
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::PeerMigrated(server_addr, client_addr_2))
+        );
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .local_addr(),
+            server_addr
+        );
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            client_addr_2
+        );
+    }
+
+    #[test]
+    fn connection_migration_reordered_non_probing() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(2);
+        config.set_initial_max_data(30);
+        config.set_initial_max_stream_data_bidi_local(15);
+        config.set_initial_max_stream_data_bidi_remote(15);
+        config.set_initial_max_stream_data_uni(10);
+        config.set_initial_max_streams_bidi(3);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 16, 16, 1);
+
+        let client_addr = testing::Pipe::client_addr();
+        let server_addr = testing::Pipe::server_addr();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+
+        assert_eq!(pipe.client.probe_path(client_addr_2, server_addr), Ok(1));
+        assert_eq!(pipe.advance(), Ok(()));
+        assert_eq!(
+            pipe.client.path_event_next(),
+            Some(PathEvent::Validated(client_addr_2, server_addr))
+        );
+        assert_eq!(pipe.client.path_event_next(), None);
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::New(server_addr, client_addr_2))
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::Validated(server_addr, client_addr_2))
+        );
+        assert_eq!(pipe.server.path_event_next(), None);
+
+        // A first flight sent from secondary address.
+        assert_eq!(pipe.client.stream_send(0, b"data", true), Ok(4));
+        let mut first = testing::emit_flight(&mut pipe.client).unwrap();
+        first.iter_mut().for_each(|(_, si)| si.from = client_addr_2);
+        // A second one, but sent from the original one.
+        assert_eq!(pipe.client.stream_send(4, b"data", true), Ok(4));
+        let second = testing::emit_flight(&mut pipe.client).unwrap();
+        // Second flight is received before first one.
+        assert_eq!(testing::process_flight(&mut pipe.server, second), Ok(()));
+        assert_eq!(testing::process_flight(&mut pipe.server, first), Ok(()));
+
+        // Server does not perform connection migration because of packet
+        // reordering.
+        assert_eq!(pipe.server.path_event_next(), None);
+        assert_eq!(
+            pipe.server
+                .paths
+                .get_active()
+                .expect("no active")
+                .peer_addr(),
+            client_addr
+        );
+    }
+
+    #[test]
+    fn resilience_against_migration_attack() {
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.verify_peer(false);
+        config.set_active_connection_id_limit(3);
+        config.set_initial_max_data(100000);
+        config.set_initial_max_stream_data_bidi_local(100000);
+        config.set_initial_max_stream_data_bidi_remote(100000);
+        config.set_initial_max_streams_bidi(2);
+
+        let mut pipe = pipe_with_exchanged_cids(&mut config, 16, 16, 1);
+
+        let client_addr = testing::Pipe::client_addr();
+        let server_addr = testing::Pipe::server_addr();
+        let spoofed_client_addr = "127.0.0.1:6666".parse().unwrap();
+
+        const DATA_BYTES: usize = 24000;
+        let buf = [42; DATA_BYTES];
+        let mut recv_buf = [0; DATA_BYTES];
+        assert_eq!(pipe.server.stream_send(1, &buf, true), Ok(12000));
+        assert_eq!(
+            testing::process_flight(
+                &mut pipe.client,
+                testing::emit_flight(&mut pipe.server).unwrap()
+            ),
+            Ok(())
+        );
+        let (rcv_data_1, _) = pipe.client.stream_recv(1, &mut recv_buf).unwrap();
+
+        // Fake the source address of client.
+        let mut faked_addr_flight =
+            testing::emit_flight(&mut pipe.client).unwrap();
+        faked_addr_flight
+            .iter_mut()
+            .for_each(|(_, si)| si.from = spoofed_client_addr);
+        assert_eq!(
+            testing::process_flight(&mut pipe.server, faked_addr_flight),
+            Ok(())
+        );
+        assert_eq!(pipe.server.stream_send(1, &buf[12000..], true), Ok(12000));
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::ReusedSourceConnectionId(
+                0,
+                (server_addr, client_addr),
+                (server_addr, spoofed_client_addr)
+            ))
+        );
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::New(server_addr, spoofed_client_addr))
+        );
+
+        assert_eq!(
+            pipe.server.is_path_validated(server_addr, client_addr),
+            Ok(true)
+        );
+        assert_eq!(
+            pipe.server
+                .is_path_validated(server_addr, spoofed_client_addr),
+            Ok(false)
+        );
+
+        // The client creates the PATH CHALLENGE, but it is always lost.
+        testing::emit_flight(&mut pipe.server).unwrap();
+
+        // Wait until probing timer expires. Since the RTT is very low,
+        // wait a bit more.
+        let probed_pid = pipe
+            .server
+            .paths
+            .path_id_from_addrs(&(server_addr, spoofed_client_addr))
+            .unwrap();
+        let probe_instant = pipe
+            .server
+            .paths
+            .get(probed_pid)
+            .unwrap()
+            .recovery
+            .loss_detection_timer()
+            .unwrap();
+        let timer = probe_instant.duration_since(time::Instant::now());
+        std::thread::sleep(timer + time::Duration::from_millis(1));
+
+        pipe.server.on_timeout();
+
+        // Because of the small ACK size, the server cannot send more to the
+        // client. Fallback on the previous active path.
+        assert_eq!(
+            pipe.server.path_event_next(),
+            Some(PathEvent::FailedValidation(
+                server_addr,
+                spoofed_client_addr
+            ))
+        );
+
+        assert_eq!(
+            pipe.server.is_path_validated(server_addr, client_addr),
+            Ok(true)
+        );
+        assert_eq!(
+            pipe.server
+                .is_path_validated(server_addr, spoofed_client_addr),
+            Ok(false)
+        );
+
+        let server_active_path = pipe.server.paths.get_active().unwrap();
+        assert_eq!(server_active_path.local_addr(), server_addr);
+        assert_eq!(server_active_path.peer_addr(), client_addr);
+        assert_eq!(pipe.advance(), Ok(()));
+        let (rcv_data_2, fin) =
+            pipe.client.stream_recv(1, &mut recv_buf).unwrap();
+        assert_eq!(fin, true);
+        assert_eq!(rcv_data_1 + rcv_data_2, DATA_BYTES);
+    }
+
+    #[test]
+    fn consecutive_non_ack_eliciting() {
+        let mut buf = [0; 65535];
+
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Client sends a bunch of PING frames, causing server to ACK (ACKs aren't
+        // ack-eliciting)
+        let frames = [frame::Frame::Ping];
+        let pkt_type = packet::Type::Short;
+        for _ in 0..24 {
+            let len = pipe
+                .send_pkt_to_server(pkt_type, &frames, &mut buf)
+                .unwrap();
+            assert!(len > 0);
+
+            let frames =
+                testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+            assert!(
+                frames
+                    .iter()
+                    .all(|frame| matches!(frame, frame::Frame::ACK { .. })),
+                "ACK only"
+            );
+        }
+
+        // After 24 non-ack-eliciting, an ACK is explicitly elicited with a PING
+        let len = pipe
+            .send_pkt_to_server(pkt_type, &frames, &mut buf)
+            .unwrap();
+        assert!(len > 0);
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+        assert!(
+            frames
+                .iter()
+                .any(|frame| matches!(frame, frame::Frame::Ping)),
+            "found a PING"
+        );
+    }
+
+    #[test]
+    fn send_ack_eliciting_causes_ping() {
+        // First establish a connection
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Queue a PING frame
+        pipe.server.send_ack_eliciting().unwrap();
+
+        // Make sure ping is sent
+        let mut buf = [0; 1500];
+        let (len, _) = pipe.server.send(&mut buf).unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+        let mut iter = frames.iter();
+
+        assert_eq!(iter.next(), Some(&frame::Frame::Ping));
+    }
+
+    #[test]
+    fn send_ack_eliciting_no_ping() {
+        // First establish a connection
+        let mut pipe = testing::Pipe::new().unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        // Queue a PING frame
+        pipe.server.send_ack_eliciting().unwrap();
+
+        // Send a stream frame, which is ACK-eliciting to make sure the ping is
+        // not sent
+        assert_eq!(pipe.server.stream_send(1, b"a", false), Ok(1));
+
+        // Make sure ping is not sent
+        let mut buf = [0; 1500];
+        let (len, _) = pipe.server.send(&mut buf).unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+        let mut iter = frames.iter();
+
+        assert!(matches!(
+            iter.next(),
+            Some(&frame::Frame::Stream {
+                stream_id: 1,
+                data: _
+            })
+        ));
+        assert!(iter.next().is_none());
+    }
+
+    /// Tests that streams do not keep being "writable" after being collected
+    /// on reset.
+    #[test]
+    fn stop_sending_stream_send_after_reset_stream_ack() {
+        let mut b = [0; 15];
+
+        let mut buf = [0; 65535];
+
+        let mut config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        config
+            .load_cert_chain_from_pem_file("examples/cert.crt")
+            .unwrap();
+        config
+            .load_priv_key_from_pem_file("examples/cert.key")
+            .unwrap();
+        config
+            .set_application_protos(&[b"proto1", b"proto2"])
+            .unwrap();
+        config.set_initial_max_data(999999999);
+        config.set_initial_max_stream_data_bidi_local(30);
+        config.set_initial_max_stream_data_bidi_remote(30);
+        config.set_initial_max_stream_data_uni(30);
+        config.set_initial_max_streams_bidi(1000);
+        config.set_initial_max_streams_uni(0);
+        config.verify_peer(false);
+
+        let mut pipe = testing::Pipe::with_config(&mut config).unwrap();
+        assert_eq!(pipe.handshake(), Ok(()));
+
+        assert_eq!(pipe.server.streams.len(), 0);
+        assert_eq!(pipe.server.readable().len(), 0);
+        assert_eq!(pipe.server.writable().len(), 0);
+
+        // Client opens a load of streams
+        assert_eq!(pipe.client.stream_send(0, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(4, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(8, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(12, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(16, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(20, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(24, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(28, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(32, b"hello", true), Ok(5));
+        assert_eq!(pipe.client.stream_send(36, b"hello", true), Ok(5));
+        assert_eq!(pipe.advance(), Ok(()));
+
+        // Server iterators are populated
+        let mut r = pipe.server.readable();
+        assert_eq!(r.len(), 10);
+        assert_eq!(r.next(), Some(28));
+        assert_eq!(r.next(), Some(12));
+        assert_eq!(r.next(), Some(24));
+        assert_eq!(r.next(), Some(8));
+        assert_eq!(r.next(), Some(36));
+        assert_eq!(r.next(), Some(20));
+        assert_eq!(r.next(), Some(4));
+        assert_eq!(r.next(), Some(32));
+        assert_eq!(r.next(), Some(16));
+        assert_eq!(r.next(), Some(0));
+        assert_eq!(r.next(), None);
+
+        let mut w = pipe.server.writable();
+        assert_eq!(w.len(), 10);
+        assert_eq!(w.next(), Some(28));
+        assert_eq!(w.next(), Some(12));
+        assert_eq!(w.next(), Some(24));
+        assert_eq!(w.next(), Some(8));
+        assert_eq!(w.next(), Some(36));
+        assert_eq!(w.next(), Some(20));
+        assert_eq!(w.next(), Some(4));
+        assert_eq!(w.next(), Some(32));
+        assert_eq!(w.next(), Some(16));
+        assert_eq!(w.next(), Some(0));
+        assert_eq!(w.next(), None);
+
+        // Read one stream
+        assert_eq!(pipe.server.stream_recv(0, &mut b), Ok((5, true)));
+        assert!(pipe.server.stream_finished(0));
+
+        assert_eq!(pipe.server.readable().len(), 9);
+        assert_eq!(pipe.server.writable().len(), 10);
+
+        assert_eq!(pipe.server.stream_writable(0, 0), Ok(true));
+
+        // Server sends data on stream 0, until blocked.
+        loop {
+            if pipe.server.stream_send(0, b"world", false) == Err(Error::Done) {
+                break;
+            }
+
+            assert_eq!(pipe.advance(), Ok(()));
+        }
+
+        assert_eq!(pipe.server.writable().len(), 9);
+        assert_eq!(pipe.server.stream_writable(0, 0), Ok(true));
+
+        // Client sends STOP_SENDING.
+        let frames = [frame::Frame::StopSending {
+            stream_id: 0,
+            error_code: 42,
+        }];
+
+        let pkt_type = packet::Type::Short;
+        let len = pipe
+            .send_pkt_to_server(pkt_type, &frames, &mut buf)
+            .unwrap();
+
+        // Server sent a RESET_STREAM frame in response.
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+
+        let mut iter = frames.iter();
+
+        // Skip ACK frame.
+        iter.next();
+
+        assert_eq!(
+            iter.next(),
+            Some(&frame::Frame::ResetStream {
+                stream_id: 0,
+                error_code: 42,
+                final_size: 30,
+            })
+        );
+
+        // Stream 0 is now writable in order to make apps aware of STOP_SENDING
+        // via returning an error.
+        let mut w = pipe.server.writable();
+        assert_eq!(w.len(), 10);
+
+        assert!(w.find(|&s| s == 0).is_some());
+        assert_eq!(
+            pipe.server.stream_writable(0, 1),
+            Err(Error::StreamStopped(42))
+        );
+
+        assert_eq!(pipe.server.writable().len(), 10);
+        assert_eq!(pipe.server.streams.len(), 10);
+
+        // Client acks RESET_STREAM frame.
+        let mut ranges = ranges::RangeSet::default();
+        ranges.insert(0..12);
+
+        let frames = [frame::Frame::ACK {
+            ack_delay: 15,
+            ranges,
+            ecn_counts: None,
+        }];
+
+        assert_eq!(pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), Ok(0));
+
+        // Stream is collected on the server after RESET_STREAM is acked.
+        assert_eq!(pipe.server.streams.len(), 9);
+
+        // Sending STOP_SENDING again shouldn't trigger RESET_STREAM again.
+        let frames = [frame::Frame::StopSending {
+            stream_id: 0,
+            error_code: 42,
+        }];
+
+        let len = pipe
+            .send_pkt_to_server(pkt_type, &frames, &mut buf)
+            .unwrap();
+
+        let frames =
+            testing::decode_pkt(&mut pipe.client, &mut buf, len).unwrap();
+
+        assert_eq!(frames.len(), 1);
+
+        match frames.first() {
+            Some(frame::Frame::ACK { .. }) => (),
+
+            f => panic!("expected ACK frame, got {:?}", f),
+        };
+
+        assert_eq!(pipe.server.streams.len(), 9);
+
+        // Stream 0 has been collected and must not be writable anymore.
+        let mut w = pipe.server.writable();
+        assert_eq!(w.len(), 9);
+        assert!(w.find(|&s| s == 0).is_none());
+
+        // If we called send before the client ACK of reset stream, it would
+        // have failed with StreamStopped.
+        assert_eq!(pipe.server.stream_send(0, b"world", true), Err(Error::Done),);
+
+        // Stream 0 is still not writable.
+        let mut w = pipe.server.writable();
+        assert_eq!(w.len(), 9);
+        assert!(w.find(|&s| s == 0).is_none());
+    }
 }
 
 pub use crate::packet::ConnectionId;
 pub use crate::packet::Header;
 pub use crate::packet::Type;
 
+pub use crate::path::PathEvent;
+pub use crate::path::PathStats;
+pub use crate::path::SocketAddrIter;
+
 pub use crate::recovery::CongestionControlAlgorithm;
 
 pub use crate::stream::StreamIter;
 
+mod cid;
 mod crypto;
 mod dgram;
 #[cfg(feature = "ffi")]
 mod ffi;
+mod flowcontrol;
 mod frame;
 pub mod h3;
 mod minmax;
-mod octets;
 mod packet;
+mod path;
 mod rand;
 mod ranges;
 mod recovery;
diff --git a/src/minmax.rs b/src/minmax.rs
index a8a23fd..274d138 100644
--- a/src/minmax.rs
+++ b/src/minmax.rs
@@ -108,7 +108,7 @@
     }
 
     /// Updates the max estimate based on the given measurement, and returns it.
-    pub fn _running_max(&mut self, win: Duration, time: Instant, meas: T) -> T {
+    pub fn running_max(&mut self, win: Duration, time: Instant, meas: T) -> T {
         let val = MinmaxSample { time, value: meas };
 
         let delta_time = time.duration_since(self.estimate[2].time);
@@ -173,7 +173,7 @@
 
     #[test]
     fn reset_filter_rtt() {
-        let mut f = Minmax::new(Duration::new(0, 0));
+        let mut f = Minmax::new(Duration::ZERO);
         let now = Instant::now();
         let rtt = Duration::from_millis(50);
 
@@ -211,7 +211,7 @@
 
     #[test]
     fn get_windowed_min_rtt() {
-        let mut f = Minmax::new(Duration::new(0, 0));
+        let mut f = Minmax::new(Duration::ZERO);
         let rtt_25 = Duration::from_millis(25);
         let rtt_24 = Duration::from_millis(24);
         let win = Duration::from_millis(500);
@@ -259,7 +259,7 @@
 
     #[test]
     fn get_windowed_max_rtt() {
-        let mut f = Minmax::new(Duration::new(0, 0));
+        let mut f = Minmax::new(Duration::ZERO);
         let rtt_25 = Duration::from_millis(25);
         let rtt_24 = Duration::from_millis(24);
         let win = Duration::from_millis(500);
@@ -269,13 +269,13 @@
         assert_eq!(rtt_max, rtt_24);
 
         time += Duration::from_millis(250);
-        rtt_max = f._running_max(win, time, rtt_25);
+        rtt_max = f.running_max(win, time, rtt_25);
         assert_eq!(rtt_max, rtt_25);
         assert_eq!(f.estimate[1].value, rtt_25);
         assert_eq!(f.estimate[2].value, rtt_25);
 
         time += Duration::from_millis(600);
-        rtt_max = f._running_max(win, time, rtt_24);
+        rtt_max = f.running_max(win, time, rtt_24);
         assert_eq!(rtt_max, rtt_24);
         assert_eq!(f.estimate[1].value, rtt_24);
         assert_eq!(f.estimate[2].value, rtt_24);
@@ -293,13 +293,13 @@
         assert_eq!(bw_max, bw_200);
 
         time += Duration::from_millis(5000);
-        bw_max = f._running_max(win, time, bw_500);
+        bw_max = f.running_max(win, time, bw_500);
         assert_eq!(bw_max, bw_500);
         assert_eq!(f.estimate[1].value, bw_500);
         assert_eq!(f.estimate[2].value, bw_500);
 
         time += Duration::from_millis(600);
-        bw_max = f._running_max(win, time, bw_200);
+        bw_max = f.running_max(win, time, bw_200);
         assert_eq!(bw_max, bw_200);
         assert_eq!(f.estimate[1].value, bw_200);
         assert_eq!(f.estimate[2].value, bw_200);
@@ -307,7 +307,7 @@
 
     #[test]
     fn get_windowed_min_estimates_rtt() {
-        let mut f = Minmax::new(Duration::new(0, 0));
+        let mut f = Minmax::new(Duration::ZERO);
         let rtt_25 = Duration::from_millis(25);
         let rtt_24 = Duration::from_millis(24);
         let rtt_23 = Duration::from_millis(23);
@@ -371,7 +371,7 @@
 
     #[test]
     fn get_windowed_max_estimates_rtt() {
-        let mut f = Minmax::new(Duration::new(0, 0));
+        let mut f = Minmax::new(Duration::ZERO);
         let rtt_25 = Duration::from_millis(25);
         let rtt_24 = Duration::from_millis(24);
         let rtt_23 = Duration::from_millis(23);
@@ -383,19 +383,19 @@
         assert_eq!(rtt_max, rtt_25);
 
         time += Duration::from_millis(300);
-        rtt_max = f._running_max(win, time, rtt_24);
+        rtt_max = f.running_max(win, time, rtt_24);
         assert_eq!(rtt_max, rtt_25);
         assert_eq!(f.estimate[1].value, rtt_24);
         assert_eq!(f.estimate[2].value, rtt_24);
 
         time += Duration::from_millis(300);
-        rtt_max = f._running_max(win, time, rtt_23);
+        rtt_max = f.running_max(win, time, rtt_23);
         assert_eq!(rtt_max, rtt_25);
         assert_eq!(f.estimate[1].value, rtt_24);
         assert_eq!(f.estimate[2].value, rtt_23);
 
         time += Duration::from_millis(300);
-        rtt_max = f._running_max(win, time, rtt_26);
+        rtt_max = f.running_max(win, time, rtt_26);
         assert_eq!(rtt_max, rtt_26);
         assert_eq!(f.estimate[1].value, rtt_26);
         assert_eq!(f.estimate[2].value, rtt_26);
@@ -415,19 +415,19 @@
         assert_eq!(bw_max, bw_500);
 
         time += Duration::from_millis(300);
-        bw_max = f._running_max(win, time, bw_400);
+        bw_max = f.running_max(win, time, bw_400);
         assert_eq!(bw_max, bw_500);
         assert_eq!(f.estimate[1].value, bw_400);
         assert_eq!(f.estimate[2].value, bw_400);
 
         time += Duration::from_millis(300);
-        bw_max = f._running_max(win, time, bw_300);
+        bw_max = f.running_max(win, time, bw_300);
         assert_eq!(bw_max, bw_500);
         assert_eq!(f.estimate[1].value, bw_400);
         assert_eq!(f.estimate[2].value, bw_300);
 
         time += Duration::from_millis(300);
-        bw_max = f._running_max(win, time, bw_600);
+        bw_max = f.running_max(win, time, bw_600);
         assert_eq!(bw_max, bw_600);
         assert_eq!(f.estimate[1].value, bw_600);
         assert_eq!(f.estimate[2].value, bw_600);
diff --git a/src/octets.rs b/src/octets.rs
deleted file mode 100644
index 3983667..0000000
--- a/src/octets.rs
+++ /dev/null
@@ -1,1277 +0,0 @@
-// Copyright (C) 2018-2019, Cloudflare, Inc.
-// All rights reserved.
-//
-// Redistribution and use in source and binary forms, with or without
-// modification, are permitted provided that the following conditions are
-// met:
-//
-//     * Redistributions of source code must retain the above copyright notice,
-//       this list of conditions and the following disclaimer.
-//
-//     * Redistributions in binary form must reproduce the above copyright
-//       notice, this list of conditions and the following disclaimer in the
-//       documentation and/or other materials provided with the distribution.
-//
-// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
-// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-#![allow(dead_code)]
-
-/// Zero-copy abstraction for parsing and constructing network packets.
-use std::mem;
-use std::ptr;
-
-/// A specialized [`Result`] type for [`OctetsMut`] operations.
-///
-/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
-/// [`OctetsMut`]: struct.OctetsMut.html
-pub type Result<T> = std::result::Result<T, BufferTooShortError>;
-
-/// An error indicating that the provided [`OctetsMut`] is not big enough.
-///
-/// [`OctetsMut`]: struct.OctetsMut.html
-#[derive(Clone, Copy, Debug, PartialEq)]
-pub struct BufferTooShortError;
-
-impl std::fmt::Display for BufferTooShortError {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        write!(f, "BufferTooShortError")
-    }
-}
-
-impl std::error::Error for BufferTooShortError {
-    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
-        None
-    }
-}
-
-macro_rules! peek_u {
-    ($b:expr, $ty:ty, $len:expr) => {{
-        let len = $len;
-        let src = &$b.buf[$b.off..];
-
-        if src.len() < len {
-            return Err(BufferTooShortError);
-        }
-
-        let mut out: $ty = 0;
-        unsafe {
-            let dst = &mut out as *mut $ty as *mut u8;
-            let off = (mem::size_of::<$ty>() - len) as isize;
-
-            ptr::copy_nonoverlapping(src.as_ptr(), dst.offset(off), len);
-        };
-
-        Ok(<$ty>::from_be(out))
-    }};
-}
-
-macro_rules! get_u {
-    ($b:expr, $ty:ty, $len:expr) => {{
-        let out = peek_u!($b, $ty, $len);
-
-        $b.off += $len;
-
-        out
-    }};
-}
-
-macro_rules! put_u {
-    ($b:expr, $ty:ty, $v:expr, $len:expr) => {{
-        let len = $len;
-
-        if $b.buf.len() < $b.off + len {
-            return Err(BufferTooShortError);
-        }
-
-        let v = $v;
-
-        #[allow(clippy::range_plus_one)]
-        let dst = &mut $b.buf[$b.off..($b.off + len)];
-
-        unsafe {
-            let src = &<$ty>::to_be(v) as *const $ty as *const u8;
-            let off = (mem::size_of::<$ty>() - len) as isize;
-
-            ptr::copy_nonoverlapping(src.offset(off), dst.as_mut_ptr(), len);
-        }
-
-        $b.off += $len;
-
-        Ok(dst)
-    }};
-}
-
-/// A zero-copy immutable byte buffer.
-///
-/// `Octets` wraps an in-memory buffer of bytes and provides utility functions
-/// for manipulating it. The underlying buffer is provided by the user and is
-/// not copied when creating an `Octets`. Operations are panic-free and will
-/// avoid indexing the buffer past its end.
-///
-/// Additionally, an offset (initially set to the start of the buffer) is
-/// incremented as bytes are read from / written to the buffer, to allow for
-/// sequential operations.
-#[derive(Debug, PartialEq)]
-pub struct Octets<'a> {
-    buf: &'a [u8],
-    off: usize,
-}
-
-impl<'a> Octets<'a> {
-    /// Creates an `Octets` from the given slice, without copying.
-    ///
-    /// Since there's no copy, the input slice needs to be mutable to allow
-    /// modifications.
-    pub fn with_slice(buf: &'a [u8]) -> Self {
-        Octets { buf, off: 0 }
-    }
-
-    /// Reads an unsigned 8-bit integer from the current offset and advances
-    /// the buffer.
-    pub fn get_u8(&mut self) -> Result<u8> {
-        get_u!(self, u8, 1)
-    }
-
-    /// Reads an unsigned 8-bit integer from the current offset without
-    /// advancing the buffer.
-    pub fn peek_u8(&mut self) -> Result<u8> {
-        peek_u!(self, u8, 1)
-    }
-
-    /// Reads an unsigned 16-bit integer in network byte-order from the current
-    /// offset and advances the buffer.
-    pub fn get_u16(&mut self) -> Result<u16> {
-        get_u!(self, u16, 2)
-    }
-
-    /// Reads an unsigned 24-bit integer in network byte-order from the current
-    /// offset and advances the buffer.
-    pub fn get_u24(&mut self) -> Result<u32> {
-        get_u!(self, u32, 3)
-    }
-
-    /// Reads an unsigned 32-bit integer in network byte-order from the current
-    /// offset and advances the buffer.
-    pub fn get_u32(&mut self) -> Result<u32> {
-        get_u!(self, u32, 4)
-    }
-
-    /// Reads an unsigned 64-bit integer in network byte-order from the current
-    /// offset and advances the buffer.
-    pub fn get_u64(&mut self) -> Result<u64> {
-        get_u!(self, u64, 8)
-    }
-
-    /// Reads an unsigned variable-length integer in network byte-order from
-    /// the current offset and advances the buffer.
-    pub fn get_varint(&mut self) -> Result<u64> {
-        let first = self.peek_u8()?;
-
-        let len = varint_parse_len(first);
-
-        if len > self.cap() {
-            return Err(BufferTooShortError);
-        }
-
-        let out = match len {
-            1 => u64::from(self.get_u8()?),
-
-            2 => u64::from(self.get_u16()? & 0x3fff),
-
-            4 => u64::from(self.get_u32()? & 0x3fffffff),
-
-            8 => self.get_u64()? & 0x3fffffffffffffff,
-
-            _ => unreachable!(),
-        };
-
-        Ok(out)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and advances
-    /// the buffer.
-    pub fn get_bytes(&mut self, len: usize) -> Result<Octets> {
-        if self.cap() < len {
-            return Err(BufferTooShortError);
-        }
-
-        let out = Octets {
-            buf: &self.buf[self.off..self.off + len],
-            off: 0,
-        };
-
-        self.off += len;
-
-        Ok(out)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and advances
-    /// the buffer, where `len` is an unsigned 8-bit integer prefix.
-    pub fn get_bytes_with_u8_length(&mut self) -> Result<Octets> {
-        let len = self.get_u8()?;
-        self.get_bytes(len as usize)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and advances
-    /// the buffer, where `len` is an unsigned 16-bit integer prefix in network
-    /// byte-order.
-    pub fn get_bytes_with_u16_length(&mut self) -> Result<Octets> {
-        let len = self.get_u16()?;
-        self.get_bytes(len as usize)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and advances
-    /// the buffer, where `len` is an unsigned variable-length integer prefix
-    /// in network byte-order.
-    pub fn get_bytes_with_varint_length(&mut self) -> Result<Octets> {
-        let len = self.get_varint()?;
-        self.get_bytes(len as usize)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and without
-    /// advancing the buffer.
-    pub fn peek_bytes(&self, len: usize) -> Result<Octets> {
-        if self.cap() < len {
-            return Err(BufferTooShortError);
-        }
-
-        let out = Octets {
-            buf: &self.buf[self.off..self.off + len],
-            off: 0,
-        };
-
-        Ok(out)
-    }
-
-    /// Returns a slice of `len` elements from the current offset.
-    pub fn slice(&'a self, len: usize) -> Result<&'a [u8]> {
-        if len > self.cap() {
-            return Err(BufferTooShortError);
-        }
-
-        Ok(&self.buf[self.off..self.off + len])
-    }
-
-    /// Returns a slice of `len` elements from the end of the buffer.
-    pub fn slice_last(&'a self, len: usize) -> Result<&'a [u8]> {
-        if len > self.cap() {
-            return Err(BufferTooShortError);
-        }
-
-        let cap = self.cap();
-        Ok(&self.buf[cap - len..])
-    }
-
-    /// Advances the buffer's offset.
-    pub fn skip(&mut self, skip: usize) -> Result<()> {
-        if skip > self.cap() {
-            return Err(BufferTooShortError);
-        }
-
-        self.off += skip;
-
-        Ok(())
-    }
-
-    /// Returns the remaining capacity in the buffer.
-    pub fn cap(&self) -> usize {
-        self.buf.len() - self.off
-    }
-
-    /// Returns the total length of the buffer.
-    pub fn len(&self) -> usize {
-        self.buf.len()
-    }
-
-    /// Returns the current offset of the buffer.
-    pub fn off(&self) -> usize {
-        self.off
-    }
-
-    /// Returns a reference to the internal buffer.
-    pub fn buf(&self) -> &[u8] {
-        self.buf
-    }
-
-    /// Copies the buffer from the current offset into a new `Vec<u8>`.
-    pub fn to_vec(&self) -> Vec<u8> {
-        self.as_ref().to_vec()
-    }
-}
-
-impl<'a> AsRef<[u8]> for Octets<'a> {
-    fn as_ref(&self) -> &[u8] {
-        &self.buf[self.off..]
-    }
-}
-
-/// A zero-copy mutable byte buffer.
-///
-/// Like `Octets` but mutable.
-#[derive(Debug, PartialEq)]
-pub struct OctetsMut<'a> {
-    buf: &'a mut [u8],
-    off: usize,
-}
-
-impl<'a> OctetsMut<'a> {
-    /// Creates an `OctetsMut` from the given slice, without copying.
-    ///
-    /// Since there's no copy, the input slice needs to be mutable to allow
-    /// modifications.
-    pub fn with_slice(buf: &'a mut [u8]) -> Self {
-        OctetsMut { buf, off: 0 }
-    }
-
-    /// Reads an unsigned 8-bit integer from the current offset and advances
-    /// the buffer.
-    pub fn get_u8(&mut self) -> Result<u8> {
-        get_u!(self, u8, 1)
-    }
-
-    /// Reads an unsigned 8-bit integer from the current offset without
-    /// advancing the buffer.
-    pub fn peek_u8(&mut self) -> Result<u8> {
-        peek_u!(self, u8, 1)
-    }
-
-    /// Writes an unsigned 8-bit integer at the current offset and advances
-    /// the buffer.
-    pub fn put_u8(&mut self, v: u8) -> Result<&mut [u8]> {
-        put_u!(self, u8, v, 1)
-    }
-
-    /// Reads an unsigned 16-bit integer in network byte-order from the current
-    /// offset and advances the buffer.
-    pub fn get_u16(&mut self) -> Result<u16> {
-        get_u!(self, u16, 2)
-    }
-
-    /// Writes an unsigned 16-bit integer in network byte-order at the current
-    /// offset and advances the buffer.
-    pub fn put_u16(&mut self, v: u16) -> Result<&mut [u8]> {
-        put_u!(self, u16, v, 2)
-    }
-
-    /// Reads an unsigned 24-bit integer in network byte-order from the current
-    /// offset and advances the buffer.
-    pub fn get_u24(&mut self) -> Result<u32> {
-        get_u!(self, u32, 3)
-    }
-
-    /// Writes an unsigned 24-bit integer in network byte-order at the current
-    /// offset and advances the buffer.
-    pub fn put_u24(&mut self, v: u32) -> Result<&mut [u8]> {
-        put_u!(self, u32, v, 3)
-    }
-
-    /// Reads an unsigned 32-bit integer in network byte-order from the current
-    /// offset and advances the buffer.
-    pub fn get_u32(&mut self) -> Result<u32> {
-        get_u!(self, u32, 4)
-    }
-
-    /// Writes an unsigned 32-bit integer in network byte-order at the current
-    /// offset and advances the buffer.
-    pub fn put_u32(&mut self, v: u32) -> Result<&mut [u8]> {
-        put_u!(self, u32, v, 4)
-    }
-
-    /// Reads an unsigned 64-bit integer in network byte-order from the current
-    /// offset and advances the buffer.
-    pub fn get_u64(&mut self) -> Result<u64> {
-        get_u!(self, u64, 8)
-    }
-
-    /// Writes an unsigned 64-bit integer in network byte-order at the current
-    /// offset and advances the buffer.
-    pub fn put_u64(&mut self, v: u64) -> Result<&mut [u8]> {
-        put_u!(self, u64, v, 8)
-    }
-
-    /// Reads an unsigned variable-length integer in network byte-order from
-    /// the current offset and advances the buffer.
-    pub fn get_varint(&mut self) -> Result<u64> {
-        let first = self.peek_u8()?;
-
-        let len = varint_parse_len(first);
-
-        if len > self.cap() {
-            return Err(BufferTooShortError);
-        }
-
-        let out = match len {
-            1 => u64::from(self.get_u8()?),
-
-            2 => u64::from(self.get_u16()? & 0x3fff),
-
-            4 => u64::from(self.get_u32()? & 0x3fffffff),
-
-            8 => self.get_u64()? & 0x3fffffffffffffff,
-
-            _ => unreachable!(),
-        };
-
-        Ok(out)
-    }
-
-    /// Writes an unsigned variable-length integer in network byte-order at the
-    /// current offset and advances the buffer.
-    pub fn put_varint(&mut self, v: u64) -> Result<&mut [u8]> {
-        self.put_varint_with_len(v, varint_len(v))
-    }
-
-    /// Writes an unsigned variable-length integer of the specified length, in
-    /// network byte-order at the current offset and advances the buffer.
-    pub fn put_varint_with_len(
-        &mut self, v: u64, len: usize,
-    ) -> Result<&mut [u8]> {
-        if self.cap() < len {
-            return Err(BufferTooShortError);
-        }
-
-        let buf = match len {
-            1 => self.put_u8(v as u8)?,
-
-            2 => {
-                let buf = self.put_u16(v as u16)?;
-                buf[0] |= 0x40;
-                buf
-            },
-
-            4 => {
-                let buf = self.put_u32(v as u32)?;
-                buf[0] |= 0x80;
-                buf
-            },
-
-            8 => {
-                let buf = self.put_u64(v)?;
-                buf[0] |= 0xc0;
-                buf
-            },
-
-            _ => panic!("value is too large for varint"),
-        };
-
-        Ok(buf)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and advances
-    /// the buffer.
-    pub fn get_bytes(&mut self, len: usize) -> Result<Octets> {
-        if self.cap() < len {
-            return Err(BufferTooShortError);
-        }
-
-        let out = Octets {
-            buf: &self.buf[self.off..self.off + len],
-            off: 0,
-        };
-
-        self.off += len;
-
-        Ok(out)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and advances
-    /// the buffer.
-    pub fn get_bytes_mut(&mut self, len: usize) -> Result<OctetsMut> {
-        if self.cap() < len {
-            return Err(BufferTooShortError);
-        }
-
-        let out = OctetsMut {
-            buf: &mut self.buf[self.off..self.off + len],
-            off: 0,
-        };
-
-        self.off += len;
-
-        Ok(out)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and advances
-    /// the buffer, where `len` is an unsigned 8-bit integer prefix.
-    pub fn get_bytes_with_u8_length(&mut self) -> Result<Octets> {
-        let len = self.get_u8()?;
-        self.get_bytes(len as usize)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and advances
-    /// the buffer, where `len` is an unsigned 16-bit integer prefix in network
-    /// byte-order.
-    pub fn get_bytes_with_u16_length(&mut self) -> Result<Octets> {
-        let len = self.get_u16()?;
-        self.get_bytes(len as usize)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and advances
-    /// the buffer, where `len` is an unsigned variable-length integer prefix
-    /// in network byte-order.
-    pub fn get_bytes_with_varint_length(&mut self) -> Result<Octets> {
-        let len = self.get_varint()?;
-        self.get_bytes(len as usize)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and without
-    /// advancing the buffer.
-    pub fn peek_bytes(&mut self, len: usize) -> Result<Octets> {
-        if self.cap() < len {
-            return Err(BufferTooShortError);
-        }
-
-        let out = Octets {
-            buf: &self.buf[self.off..self.off + len],
-            off: 0,
-        };
-
-        Ok(out)
-    }
-
-    /// Reads `len` bytes from the current offset without copying and without
-    /// advancing the buffer.
-    pub fn peek_bytes_mut(&mut self, len: usize) -> Result<OctetsMut> {
-        if self.cap() < len {
-            return Err(BufferTooShortError);
-        }
-
-        let out = OctetsMut {
-            buf: &mut self.buf[self.off..self.off + len],
-            off: 0,
-        };
-
-        Ok(out)
-    }
-
-    /// Writes `len` bytes from the current offset without copying and advances
-    /// the buffer.
-    pub fn put_bytes(&mut self, v: &[u8]) -> Result<()> {
-        let len = v.len();
-
-        if self.cap() < len {
-            return Err(BufferTooShortError);
-        }
-
-        if len == 0 {
-            return Ok(());
-        }
-
-        self.as_mut()[..len].copy_from_slice(v);
-
-        self.off += len;
-
-        Ok(())
-    }
-
-    /// Splits the buffer in two at the given absolute offset.
-    pub fn split_at(&mut self, off: usize) -> Result<(OctetsMut, OctetsMut)> {
-        if self.len() < off {
-            return Err(BufferTooShortError);
-        }
-
-        let (left, right) = self.buf.split_at_mut(off);
-
-        let first = OctetsMut { buf: left, off: 0 };
-
-        let last = OctetsMut { buf: right, off: 0 };
-
-        Ok((first, last))
-    }
-
-    /// Returns a slice of `len` elements from the current offset.
-    pub fn slice(&'a mut self, len: usize) -> Result<&'a mut [u8]> {
-        if len > self.cap() {
-            return Err(BufferTooShortError);
-        }
-
-        Ok(&mut self.buf[self.off..self.off + len])
-    }
-
-    /// Returns a slice of `len` elements from the end of the buffer.
-    pub fn slice_last(&'a mut self, len: usize) -> Result<&'a mut [u8]> {
-        if len > self.cap() {
-            return Err(BufferTooShortError);
-        }
-
-        let cap = self.cap();
-        Ok(&mut self.buf[cap - len..])
-    }
-
-    /// Advances the buffer's offset.
-    pub fn skip(&mut self, skip: usize) -> Result<()> {
-        if skip > self.cap() {
-            return Err(BufferTooShortError);
-        }
-
-        self.off += skip;
-
-        Ok(())
-    }
-
-    /// Returns the remaining capacity in the buffer.
-    pub fn cap(&self) -> usize {
-        self.buf.len() - self.off
-    }
-
-    /// Returns the total length of the buffer.
-    pub fn len(&self) -> usize {
-        self.buf.len()
-    }
-
-    /// Returns the current offset of the buffer.
-    pub fn off(&self) -> usize {
-        self.off
-    }
-
-    /// Returns a reference to the internal buffer.
-    pub fn buf(&self) -> &[u8] {
-        self.buf
-    }
-
-    /// Copies the buffer from the current offset into a new `Vec<u8>`.
-    pub fn to_vec(&self) -> Vec<u8> {
-        self.as_ref().to_vec()
-    }
-}
-
-impl<'a> AsRef<[u8]> for OctetsMut<'a> {
-    fn as_ref(&self) -> &[u8] {
-        &self.buf[self.off..]
-    }
-}
-
-impl<'a> AsMut<[u8]> for OctetsMut<'a> {
-    fn as_mut(&mut self) -> &mut [u8] {
-        &mut self.buf[self.off..]
-    }
-}
-
-/// Returns how many bytes it would take to encode `v` as a variable-length
-/// integer.
-pub fn varint_len(v: u64) -> usize {
-    if v <= 63 {
-        1
-    } else if v <= 16383 {
-        2
-    } else if v <= 1_073_741_823 {
-        4
-    } else if v <= 4_611_686_018_427_387_903 {
-        8
-    } else {
-        unreachable!()
-    }
-}
-
-/// Returns how long the variable-length integer is, given its first byte.
-pub fn varint_parse_len(first: u8) -> usize {
-    match first >> 6 {
-        0 => 1,
-        1 => 2,
-        2 => 4,
-        3 => 8,
-        _ => unreachable!(),
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn get_u() {
-        let d = [
-            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
-        ];
-
-        let mut b = Octets::with_slice(&d);
-        assert_eq!(b.cap(), 18);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.get_u8().unwrap(), 1);
-        assert_eq!(b.cap(), 17);
-        assert_eq!(b.off(), 1);
-
-        assert_eq!(b.get_u16().unwrap(), 0x203);
-        assert_eq!(b.cap(), 15);
-        assert_eq!(b.off(), 3);
-
-        assert_eq!(b.get_u24().unwrap(), 0x40506);
-        assert_eq!(b.cap(), 12);
-        assert_eq!(b.off(), 6);
-
-        assert_eq!(b.get_u32().unwrap(), 0x0708090a);
-        assert_eq!(b.cap(), 8);
-        assert_eq!(b.off(), 10);
-
-        assert_eq!(b.get_u64().unwrap(), 0x0b0c0d0e0f101112);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 18);
-
-        assert!(b.get_u8().is_err());
-        assert!(b.get_u16().is_err());
-        assert!(b.get_u24().is_err());
-        assert!(b.get_u32().is_err());
-        assert!(b.get_u64().is_err());
-    }
-
-    #[test]
-    fn get_u_mut() {
-        let mut d = [
-            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
-        ];
-
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.cap(), 18);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.get_u8().unwrap(), 1);
-        assert_eq!(b.cap(), 17);
-        assert_eq!(b.off(), 1);
-
-        assert_eq!(b.get_u16().unwrap(), 0x203);
-        assert_eq!(b.cap(), 15);
-        assert_eq!(b.off(), 3);
-
-        assert_eq!(b.get_u24().unwrap(), 0x40506);
-        assert_eq!(b.cap(), 12);
-        assert_eq!(b.off(), 6);
-
-        assert_eq!(b.get_u32().unwrap(), 0x0708090a);
-        assert_eq!(b.cap(), 8);
-        assert_eq!(b.off(), 10);
-
-        assert_eq!(b.get_u64().unwrap(), 0x0b0c0d0e0f101112);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 18);
-
-        assert!(b.get_u8().is_err());
-        assert!(b.get_u16().is_err());
-        assert!(b.get_u24().is_err());
-        assert!(b.get_u32().is_err());
-        assert!(b.get_u64().is_err());
-    }
-
-    #[test]
-    fn peek_u() {
-        let d = [1, 2];
-
-        let mut b = Octets::with_slice(&d);
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.peek_u8().unwrap(), 1);
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.peek_u8().unwrap(), 1);
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 0);
-
-        b.get_u16().unwrap();
-
-        assert!(b.peek_u8().is_err());
-    }
-
-    #[test]
-    fn peek_u_mut() {
-        let mut d = [1, 2];
-
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.peek_u8().unwrap(), 1);
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.peek_u8().unwrap(), 1);
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 0);
-
-        b.get_u16().unwrap();
-
-        assert!(b.peek_u8().is_err());
-    }
-
-    #[test]
-    fn get_bytes() {
-        let d = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
-        let mut b = Octets::with_slice(&d);
-        assert_eq!(b.cap(), 10);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.get_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
-        assert_eq!(b.cap(), 5);
-        assert_eq!(b.off(), 5);
-
-        assert_eq!(b.get_bytes(3).unwrap().as_ref(), [6, 7, 8]);
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 8);
-
-        assert!(b.get_bytes(3).is_err());
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 8);
-
-        assert_eq!(b.get_bytes(2).unwrap().as_ref(), [9, 10]);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 10);
-
-        assert!(b.get_bytes(2).is_err());
-    }
-
-    #[test]
-    fn get_bytes_mut() {
-        let mut d = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.cap(), 10);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.get_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
-        assert_eq!(b.cap(), 5);
-        assert_eq!(b.off(), 5);
-
-        assert_eq!(b.get_bytes(3).unwrap().as_ref(), [6, 7, 8]);
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 8);
-
-        assert!(b.get_bytes(3).is_err());
-        assert_eq!(b.cap(), 2);
-        assert_eq!(b.off(), 8);
-
-        assert_eq!(b.get_bytes(2).unwrap().as_ref(), [9, 10]);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 10);
-
-        assert!(b.get_bytes(2).is_err());
-    }
-
-    #[test]
-    fn peek_bytes() {
-        let d = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
-        let mut b = Octets::with_slice(&d);
-        assert_eq!(b.cap(), 10);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.peek_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
-        assert_eq!(b.cap(), 10);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.peek_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
-        assert_eq!(b.cap(), 10);
-        assert_eq!(b.off(), 0);
-
-        b.get_bytes(5).unwrap();
-    }
-
-    #[test]
-    fn peek_bytes_mut() {
-        let mut d = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.cap(), 10);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.peek_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
-        assert_eq!(b.cap(), 10);
-        assert_eq!(b.off(), 0);
-
-        assert_eq!(b.peek_bytes(5).unwrap().as_ref(), [1, 2, 3, 4, 5]);
-        assert_eq!(b.cap(), 10);
-        assert_eq!(b.off(), 0);
-
-        b.get_bytes(5).unwrap();
-    }
-
-    #[test]
-    fn get_varint() {
-        let d = [0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c];
-        let mut b = Octets::with_slice(&d);
-        assert_eq!(b.get_varint().unwrap(), 151288809941952652);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 8);
-
-        let d = [0x9d, 0x7f, 0x3e, 0x7d];
-        let mut b = Octets::with_slice(&d);
-        assert_eq!(b.get_varint().unwrap(), 494878333);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 4);
-
-        let d = [0x7b, 0xbd];
-        let mut b = Octets::with_slice(&d);
-        assert_eq!(b.get_varint().unwrap(), 15293);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 2);
-
-        let d = [0x40, 0x25];
-        let mut b = Octets::with_slice(&d);
-        assert_eq!(b.get_varint().unwrap(), 37);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 2);
-
-        let d = [0x25];
-        let mut b = Octets::with_slice(&d);
-        assert_eq!(b.get_varint().unwrap(), 37);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 1);
-    }
-
-    #[test]
-    fn get_varint_mut() {
-        let mut d = [0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c];
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.get_varint().unwrap(), 151288809941952652);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 8);
-
-        let mut d = [0x9d, 0x7f, 0x3e, 0x7d];
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.get_varint().unwrap(), 494878333);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 4);
-
-        let mut d = [0x7b, 0xbd];
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.get_varint().unwrap(), 15293);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 2);
-
-        let mut d = [0x40, 0x25];
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.get_varint().unwrap(), 37);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 2);
-
-        let mut d = [0x25];
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.get_varint().unwrap(), 37);
-        assert_eq!(b.cap(), 0);
-        assert_eq!(b.off(), 1);
-    }
-
-    #[test]
-    fn put_varint() {
-        let mut d = [0; 8];
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert!(b.put_varint(151288809941952652).is_ok());
-            assert_eq!(b.cap(), 0);
-            assert_eq!(b.off(), 8);
-        }
-        let exp = [0xc2, 0x19, 0x7c, 0x5e, 0xff, 0x14, 0xe8, 0x8c];
-        assert_eq!(&d, &exp);
-
-        let mut d = [0; 4];
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert!(b.put_varint(494878333).is_ok());
-            assert_eq!(b.cap(), 0);
-            assert_eq!(b.off(), 4);
-        }
-        let exp = [0x9d, 0x7f, 0x3e, 0x7d];
-        assert_eq!(&d, &exp);
-
-        let mut d = [0; 2];
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert!(b.put_varint(15293).is_ok());
-            assert_eq!(b.cap(), 0);
-            assert_eq!(b.off(), 2);
-        }
-        let exp = [0x7b, 0xbd];
-        assert_eq!(&d, &exp);
-
-        let mut d = [0; 1];
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert!(b.put_varint(37).is_ok());
-            assert_eq!(b.cap(), 0);
-            assert_eq!(b.off(), 1);
-        }
-        let exp = [0x25];
-        assert_eq!(&d, &exp);
-
-        let mut d = [0; 3];
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert!(b.put_varint(151288809941952652).is_err());
-            assert_eq!(b.cap(), 3);
-            assert_eq!(b.off(), 0);
-        }
-        let exp = [0; 3];
-        assert_eq!(&d, &exp);
-    }
-
-    #[test]
-    #[should_panic]
-    fn varint_too_large() {
-        let mut d = [0; 3];
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert!(b.put_varint(std::u64::MAX).is_err());
-    }
-
-    #[test]
-    fn put_u() {
-        let mut d = [0; 18];
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert_eq!(b.cap(), 18);
-            assert_eq!(b.off(), 0);
-
-            assert!(b.put_u8(1).is_ok());
-            assert_eq!(b.cap(), 17);
-            assert_eq!(b.off(), 1);
-
-            assert!(b.put_u16(0x203).is_ok());
-            assert_eq!(b.cap(), 15);
-            assert_eq!(b.off(), 3);
-
-            assert!(b.put_u24(0x40506).is_ok());
-            assert_eq!(b.cap(), 12);
-            assert_eq!(b.off(), 6);
-
-            assert!(b.put_u32(0x0708090a).is_ok());
-            assert_eq!(b.cap(), 8);
-            assert_eq!(b.off(), 10);
-
-            assert!(b.put_u64(0x0b0c0d0e0f101112).is_ok());
-            assert_eq!(b.cap(), 0);
-            assert_eq!(b.off(), 18);
-
-            assert!(b.put_u8(1).is_err());
-        }
-
-        let exp = [
-            1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
-        ];
-        assert_eq!(&d, &exp);
-    }
-
-    #[test]
-    fn put_bytes() {
-        let mut d = [0; 5];
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert_eq!(b.cap(), 5);
-            assert_eq!(b.off(), 0);
-
-            let p = [0x0a, 0x0b, 0x0c, 0x0d, 0x0e];
-            assert!(b.put_bytes(&p).is_ok());
-            assert_eq!(b.cap(), 0);
-            assert_eq!(b.off(), 5);
-
-            assert!(b.put_u8(1).is_err());
-        }
-
-        let exp = [0xa, 0xb, 0xc, 0xd, 0xe];
-        assert_eq!(&d, &exp);
-    }
-
-    #[test]
-    fn split() {
-        let mut d = b"helloworld".to_vec();
-
-        let mut b = OctetsMut::with_slice(&mut d);
-        assert_eq!(b.cap(), 10);
-        assert_eq!(b.off(), 0);
-        assert_eq!(b.as_ref(), b"helloworld");
-
-        assert!(b.get_bytes(5).is_ok());
-        assert_eq!(b.cap(), 5);
-        assert_eq!(b.off(), 5);
-        assert_eq!(b.as_ref(), b"world");
-
-        let off = b.off();
-
-        let (first, last) = b.split_at(off).unwrap();
-        assert_eq!(first.cap(), 5);
-        assert_eq!(first.off(), 0);
-        assert_eq!(first.as_ref(), b"hello");
-
-        assert_eq!(last.cap(), 5);
-        assert_eq!(last.off(), 0);
-        assert_eq!(last.as_ref(), b"world");
-    }
-
-    #[test]
-    fn split_at() {
-        let mut d = b"helloworld".to_vec();
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            let (first, second) = b.split_at(5).unwrap();
-
-            let mut exp1 = b"hello".to_vec();
-            assert_eq!(first.as_ref(), &mut exp1[..]);
-
-            let mut exp2 = b"world".to_vec();
-            assert_eq!(second.as_ref(), &mut exp2[..]);
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            let (first, second) = b.split_at(10).unwrap();
-
-            let mut exp1 = b"helloworld".to_vec();
-            assert_eq!(first.as_ref(), &mut exp1[..]);
-
-            let mut exp2 = b"".to_vec();
-            assert_eq!(second.as_ref(), &mut exp2[..]);
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            let (first, second) = b.split_at(9).unwrap();
-
-            let mut exp1 = b"helloworl".to_vec();
-            assert_eq!(first.as_ref(), &mut exp1[..]);
-
-            let mut exp2 = b"d".to_vec();
-            assert_eq!(second.as_ref(), &mut exp2[..]);
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert!(b.split_at(11).is_err());
-        }
-    }
-
-    #[test]
-    fn slice() {
-        let d = b"helloworld".to_vec();
-
-        {
-            let b = Octets::with_slice(&d);
-            let exp = b"hello".to_vec();
-            assert_eq!(b.slice(5), Ok(&exp[..]));
-        }
-
-        {
-            let b = Octets::with_slice(&d);
-            let exp = b"".to_vec();
-            assert_eq!(b.slice(0), Ok(&exp[..]));
-        }
-
-        {
-            let mut b = Octets::with_slice(&d);
-            b.get_bytes(5).unwrap();
-
-            let exp = b"world".to_vec();
-            assert_eq!(b.slice(5), Ok(&exp[..]));
-        }
-
-        {
-            let b = Octets::with_slice(&d);
-            assert!(b.slice(11).is_err());
-        }
-    }
-
-    #[test]
-    fn slice_mut() {
-        let mut d = b"helloworld".to_vec();
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            let mut exp = b"hello".to_vec();
-            assert_eq!(b.slice(5), Ok(&mut exp[..]));
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            let mut exp = b"".to_vec();
-            assert_eq!(b.slice(0), Ok(&mut exp[..]));
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            b.get_bytes(5).unwrap();
-
-            let mut exp = b"world".to_vec();
-            assert_eq!(b.slice(5), Ok(&mut exp[..]));
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert!(b.slice(11).is_err());
-        }
-    }
-
-    #[test]
-    fn slice_last() {
-        let d = b"helloworld".to_vec();
-
-        {
-            let b = Octets::with_slice(&d);
-            let exp = b"orld".to_vec();
-            assert_eq!(b.slice_last(4), Ok(&exp[..]));
-        }
-
-        {
-            let b = Octets::with_slice(&d);
-            let exp = b"d".to_vec();
-            assert_eq!(b.slice_last(1), Ok(&exp[..]));
-        }
-
-        {
-            let b = Octets::with_slice(&d);
-            let exp = b"".to_vec();
-            assert_eq!(b.slice_last(0), Ok(&exp[..]));
-        }
-
-        {
-            let b = Octets::with_slice(&d);
-            let exp = b"helloworld".to_vec();
-            assert_eq!(b.slice_last(10), Ok(&exp[..]));
-        }
-
-        {
-            let b = Octets::with_slice(&d);
-            assert!(b.slice_last(11).is_err());
-        }
-    }
-
-    #[test]
-    fn slice_last_mut() {
-        let mut d = b"helloworld".to_vec();
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            let mut exp = b"orld".to_vec();
-            assert_eq!(b.slice_last(4), Ok(&mut exp[..]));
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            let mut exp = b"d".to_vec();
-            assert_eq!(b.slice_last(1), Ok(&mut exp[..]));
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            let mut exp = b"".to_vec();
-            assert_eq!(b.slice_last(0), Ok(&mut exp[..]));
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            let mut exp = b"helloworld".to_vec();
-            assert_eq!(b.slice_last(10), Ok(&mut exp[..]));
-        }
-
-        {
-            let mut b = OctetsMut::with_slice(&mut d);
-            assert!(b.slice_last(11).is_err());
-        }
-    }
-}
diff --git a/src/packet.rs b/src/packet.rs
index e6180f2..39194f0 100644
--- a/src/packet.rs
+++ b/src/packet.rs
@@ -24,6 +24,10 @@
 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+use std::fmt::Display;
+use std::ops::Index;
+use std::ops::IndexMut;
+use std::ops::RangeInclusive;
 use std::time;
 
 use ring::aead;
@@ -32,7 +36,6 @@
 use crate::Result;
 
 use crate::crypto;
-use crate::octets;
 use crate::rand;
 use crate::ranges;
 use crate::stream;
@@ -50,20 +53,62 @@
 
 const SAMPLE_LEN: usize = 16;
 
-pub const EPOCH_INITIAL: usize = 0;
-pub const EPOCH_HANDSHAKE: usize = 1;
-pub const EPOCH_APPLICATION: usize = 2;
-pub const EPOCH_COUNT: usize = 3;
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+pub enum Epoch {
+    Initial     = 0,
+    Handshake   = 1,
+    Application = 2,
+}
 
-/// Packet number space epoch.
-///
-/// This should only ever be one of `EPOCH_INITIAL`, `EPOCH_HANDSHAKE` or
-/// `EPOCH_APPLICATION`, and can be used to index state specific to a packet
-/// number space in `Connection` and `Recovery`.
-pub type Epoch = usize;
+static EPOCHS: [Epoch; 3] =
+    [Epoch::Initial, Epoch::Handshake, Epoch::Application];
+
+impl Epoch {
+    /// Returns an ordered slice containing the `Epoch`s that fit in the
+    /// provided `range`.
+    pub fn epochs(range: RangeInclusive<Epoch>) -> &'static [Epoch] {
+        &EPOCHS[*range.start() as usize..=*range.end() as usize]
+    }
+
+    pub const fn count() -> usize {
+        3
+    }
+}
+
+impl Display for Epoch {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", usize::from(*self))
+    }
+}
+
+impl From<Epoch> for usize {
+    fn from(e: Epoch) -> Self {
+        e as usize
+    }
+}
+
+impl<T> Index<Epoch> for [T]
+where
+    T: Sized,
+{
+    type Output = T;
+
+    fn index(&self, index: Epoch) -> &Self::Output {
+        self.index(usize::from(index))
+    }
+}
+
+impl<T> IndexMut<Epoch> for [T]
+where
+    T: Sized,
+{
+    fn index_mut(&mut self, index: Epoch) -> &mut Self::Output {
+        self.index_mut(usize::from(index))
+    }
+}
 
 /// QUIC packet type.
-#[derive(Clone, Copy, Debug, PartialEq)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
 pub enum Type {
     /// Initial packet.
     Initial,
@@ -87,44 +132,43 @@
 impl Type {
     pub(crate) fn from_epoch(e: Epoch) -> Type {
         match e {
-            EPOCH_INITIAL => Type::Initial,
+            Epoch::Initial => Type::Initial,
 
-            EPOCH_HANDSHAKE => Type::Handshake,
+            Epoch::Handshake => Type::Handshake,
 
-            EPOCH_APPLICATION => Type::Short,
-
-            _ => unreachable!(),
+            Epoch::Application => Type::Short,
         }
     }
 
     pub(crate) fn to_epoch(self) -> Result<Epoch> {
         match self {
-            Type::Initial => Ok(EPOCH_INITIAL),
+            Type::Initial => Ok(Epoch::Initial),
 
-            Type::ZeroRTT => Ok(EPOCH_APPLICATION),
+            Type::ZeroRTT => Ok(Epoch::Application),
 
-            Type::Handshake => Ok(EPOCH_HANDSHAKE),
+            Type::Handshake => Ok(Epoch::Handshake),
 
-            Type::Short => Ok(EPOCH_APPLICATION),
+            Type::Short => Ok(Epoch::Application),
 
             _ => Err(Error::InvalidPacket),
         }
     }
 
     #[cfg(feature = "qlog")]
-    pub(crate) fn to_qlog(self) -> qlog::PacketType {
+    pub(crate) fn to_qlog(self) -> qlog::events::quic::PacketType {
         match self {
-            Type::Initial => qlog::PacketType::Initial,
+            Type::Initial => qlog::events::quic::PacketType::Initial,
 
-            Type::Retry => qlog::PacketType::Retry,
+            Type::Retry => qlog::events::quic::PacketType::Retry,
 
-            Type::Handshake => qlog::PacketType::Handshake,
+            Type::Handshake => qlog::events::quic::PacketType::Handshake,
 
-            Type::ZeroRTT => qlog::PacketType::ZeroRtt,
+            Type::ZeroRTT => qlog::events::quic::PacketType::ZeroRtt,
 
-            Type::VersionNegotiation => qlog::PacketType::VersionNegotiation,
+            Type::VersionNegotiation =>
+                qlog::events::quic::PacketType::VersionNegotiation,
 
-            Type::Short => qlog::PacketType::OneRtt,
+            Type::Short => qlog::events::quic::PacketType::OneRtt,
         }
     }
 }
@@ -230,7 +274,7 @@
     #[inline]
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         for c in self.as_ref() {
-            write!(f, "{:02x}", c)?;
+            write!(f, "{c:02x}")?;
         }
 
         Ok(())
@@ -238,7 +282,7 @@
 }
 
 /// A QUIC packet's header.
-#[derive(Clone, PartialEq)]
+#[derive(Clone, PartialEq, Eq)]
 pub struct Header<'a> {
     /// The type of the packet.
     pub ty: Type,
@@ -495,12 +539,12 @@
         if let Some(ref token) = self.token {
             write!(f, " token=")?;
             for b in token {
-                write!(f, "{:02x}", b)?;
+                write!(f, "{b:02x}")?;
             }
         }
 
         if let Some(ref versions) = self.versions {
-            write!(f, " versions={:x?}", versions)?;
+            write!(f, " versions={versions:x?}")?;
         }
 
         if self.ty == Type::Short {
@@ -512,11 +556,13 @@
 }
 
 pub fn pkt_num_len(pn: u64) -> Result<usize> {
-    let len = if pn < u64::from(std::u8::MAX) {
+    let len = if pn < u64::from(u8::MAX) {
         1
-    } else if pn < u64::from(std::u16::MAX) {
+    } else if pn < u64::from(u16::MAX) {
         2
-    } else if pn < u64::from(std::u32::MAX) {
+    } else if pn < 16_777_215u64 {
+        3
+    } else if pn < u64::from(u32::MAX) {
         4
     } else {
         return Err(Error::InvalidPacket);
@@ -625,7 +671,8 @@
 pub fn encrypt_hdr(
     b: &mut octets::OctetsMut, pn_len: usize, payload: &[u8], aead: &crypto::Seal,
 ) -> Result<()> {
-    let sample = &payload[4 - pn_len..16 + (4 - pn_len)];
+    let sample = &payload
+        [MAX_PKT_NUM_LEN - pn_len..SAMPLE_LEN + (MAX_PKT_NUM_LEN - pn_len)];
 
     let mask = aead.new_mask(sample)?;
 
@@ -649,17 +696,21 @@
 
 pub fn encrypt_pkt(
     b: &mut octets::OctetsMut, pn: u64, pn_len: usize, payload_len: usize,
-    payload_offset: usize, aead: &crypto::Seal,
+    payload_offset: usize, extra_in: Option<&[u8]>, aead: &crypto::Seal,
 ) -> Result<usize> {
     let (mut header, mut payload) = b.split_at(payload_offset)?;
 
-    // Encrypt + authenticate payload.
-    let ciphertext = payload.slice(payload_len)?;
-    aead.seal_with_u64_counter(pn, header.as_ref(), ciphertext)?;
+    let ciphertext_len = aead.seal_with_u64_counter(
+        pn,
+        header.as_ref(),
+        payload.as_mut(),
+        payload_len,
+        extra_in,
+    )?;
 
-    encrypt_hdr(&mut header, pn_len, ciphertext, aead)?;
+    encrypt_hdr(&mut header, pn_len, payload.as_ref(), aead)?;
 
-    Ok(payload_offset + payload_len)
+    Ok(payload_offset + ciphertext_len)
 }
 
 pub fn encode_pkt_num(pn: u64, b: &mut octets::OctetsMut) -> Result<()> {
@@ -691,9 +742,9 @@
     b.put_u32(0)?;
 
     b.put_u8(scid.len() as u8)?;
-    b.put_bytes(&scid)?;
+    b.put_bytes(scid)?;
     b.put_u8(dcid.len() as u8)?;
-    b.put_bytes(&dcid)?;
+    b.put_bytes(dcid)?;
     b.put_u32(crate::PROTOCOL_VERSION_V1)?;
     b.put_u32(crate::PROTOCOL_VERSION_DRAFT29)?;
     b.put_u32(crate::PROTOCOL_VERSION_DRAFT28)?;
@@ -810,11 +861,29 @@
         .map_err(|_| Error::CryptoFail)
 }
 
+pub struct KeyUpdate {
+    /// 1-RTT key used prior to a key update.
+    pub crypto_open: crypto::Open,
+
+    /// The packet number triggered the latest key-update.
+    ///
+    /// Incoming packets with lower pn should use this (prev) crypto key.
+    pub pn_on_update: u64,
+
+    /// Whether ACK frame for key-update has been sent.
+    pub update_acked: bool,
+
+    /// When the old key should be discarded.
+    pub timer: time::Instant,
+}
+
 pub struct PktNumSpace {
     pub largest_rx_pkt_num: u64,
 
     pub largest_rx_pkt_time: time::Instant,
 
+    pub largest_rx_non_probing_pkt_num: u64,
+
     pub next_pkt_num: u64,
 
     pub recv_pkt_need_ack: ranges::RangeSet,
@@ -823,6 +892,8 @@
 
     pub ack_elicited: bool,
 
+    pub key_update: Option<KeyUpdate>,
+
     pub crypto_open: Option<crypto::Open>,
     pub crypto_seal: Option<crypto::Seal>,
 
@@ -839,6 +910,8 @@
 
             largest_rx_pkt_time: time::Instant::now(),
 
+            largest_rx_non_probing_pkt_num: 0,
+
             next_pkt_num: 0,
 
             recv_pkt_need_ack: ranges::RangeSet::new(crate::MAX_ACK_RANGES),
@@ -847,6 +920,8 @@
 
             ack_elicited: false,
 
+            key_update: None,
+
             crypto_open: None,
             crypto_seal: None,
 
@@ -854,17 +929,23 @@
             crypto_0rtt_seal: None,
 
             crypto_stream: stream::Stream::new(
-                std::u64::MAX,
-                std::u64::MAX,
+                u64::MAX,
+                u64::MAX,
                 true,
                 true,
+                stream::MAX_STREAM_WINDOW,
             ),
         }
     }
 
     pub fn clear(&mut self) {
-        self.crypto_stream =
-            stream::Stream::new(std::u64::MAX, std::u64::MAX, true, true);
+        self.crypto_stream = stream::Stream::new(
+            u64::MAX,
+            u64::MAX,
+            true,
+            true,
+            stream::MAX_STREAM_WINDOW,
+        );
 
         self.ack_elicited = false;
     }
@@ -934,7 +1015,6 @@
     use super::*;
 
     use crate::crypto;
-    use crate::octets;
 
     #[test]
     fn retry() {
@@ -957,7 +1037,7 @@
         assert!(hdr.to_bytes(&mut b).is_ok());
 
         // Add fake retry integrity token.
-        b.put_bytes(&vec![0xba; 16]).unwrap();
+        b.put_bytes(&[0xba; 16]).unwrap();
 
         let mut b = octets::OctetsMut::with_slice(&mut d);
         assert_eq!(Header::from_bytes(&mut b, 9).unwrap(), hdr);
@@ -1204,7 +1284,7 @@
         assert!(!win.contains(1025));
         assert!(!win.contains(1026));
 
-        win.insert(std::u64::MAX - 1);
+        win.insert(u64::MAX - 1);
         assert!(win.contains(0));
         assert!(win.contains(1));
         assert!(win.contains(2));
@@ -1226,8 +1306,8 @@
         assert!(win.contains(1024));
         assert!(win.contains(1025));
         assert!(win.contains(1026));
-        assert!(!win.contains(std::u64::MAX - 2));
-        assert!(win.contains(std::u64::MAX - 1));
+        assert!(!win.contains(u64::MAX - 2));
+        assert!(win.contains(u64::MAX - 1));
     }
 
     fn assert_decrypt_initial_pkt(
@@ -1848,20 +1928,25 @@
             crypto::derive_initial_key_material(dcid, hdr.version, is_server)
                 .unwrap();
 
-        let overhead = aead.alg().tag_len();
-
-        let payload_len = frames.len() + overhead;
+        let payload_len = frames.len();
 
         let payload_offset = b.off();
 
         b.put_bytes(frames).unwrap();
 
-        let written =
-            encrypt_pkt(&mut b, pn, pn_len, payload_len, payload_offset, &aead)
-                .unwrap();
+        let written = encrypt_pkt(
+            &mut b,
+            pn,
+            pn_len,
+            payload_len,
+            payload_offset,
+            None,
+            &aead,
+        )
+        .unwrap();
 
         assert_eq!(written, expected_pkt.len());
-        assert_eq!(&out[..written], &expected_pkt[..]);
+        assert_eq!(&out[..written], expected_pkt);
     }
 
     #[test]
@@ -2744,17 +2829,22 @@
 
         let frames = [01];
 
-        let overhead = aead.alg().tag_len();
-
-        let payload_len = frames.len() + overhead;
+        let payload_len = frames.len();
 
         let payload_offset = b.off();
 
         b.put_bytes(&frames).unwrap();
 
-        let written =
-            encrypt_pkt(&mut b, pn, pn_len, payload_len, payload_offset, &aead)
-                .unwrap();
+        let written = encrypt_pkt(
+            &mut b,
+            pn,
+            pn_len,
+            payload_len,
+            payload_offset,
+            None,
+            &aead,
+        )
+        .unwrap();
 
         assert_eq!(written, expected_pkt.len());
         assert_eq!(&out[..written], &expected_pkt[..]);
@@ -2791,4 +2881,37 @@
             Err(Error::InvalidPacket)
         );
     }
+
+    #[test]
+    fn decrypt_pkt_too_small() {
+        let mut buf = [0; 65535];
+        let mut b = octets::OctetsMut::with_slice(&mut buf);
+
+        let hdr = Header {
+            ty: Type::Initial,
+            version: crate::PROTOCOL_VERSION,
+            dcid: ConnectionId::default(),
+            scid: ConnectionId::default(),
+            pkt_num: 0,
+            pkt_num_len: 0,
+            token: None,
+            versions: None,
+            key_phase: false,
+        };
+
+        hdr.to_bytes(&mut b).unwrap();
+
+        b.put_bytes(&[0; 1]).unwrap();
+
+        // No space for decryption.
+        let payload_len = 1;
+
+        let (aead, _) =
+            crypto::derive_initial_key_material(b"", hdr.version, true).unwrap();
+
+        assert_eq!(
+            decrypt_pkt(&mut b, 0, 1, payload_len, &aead),
+            Err(Error::CryptoFail)
+        );
+    }
 }
diff --git a/src/path.rs b/src/path.rs
new file mode 100644
index 0000000..ea59659
--- /dev/null
+++ b/src/path.rs
@@ -0,0 +1,1069 @@
+// Copyright (C) 2022, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use std::time;
+
+use std::collections::BTreeMap;
+use std::collections::VecDeque;
+use std::net::SocketAddr;
+
+use slab::Slab;
+
+use crate::Error;
+use crate::Result;
+
+use crate::recovery;
+use crate::recovery::HandshakeStatus;
+
+/// The different states of the path validation.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum PathState {
+    /// The path failed its validation.
+    Failed,
+
+    /// The path exists, but no path validation has been performed.
+    Unknown,
+
+    /// The path is under validation.
+    Validating,
+
+    /// The remote address has been validated, but not the path MTU.
+    ValidatingMTU,
+
+    /// The path has been validated.
+    Validated,
+}
+
+impl PathState {
+    #[cfg(feature = "ffi")]
+    pub fn to_c(self) -> libc::ssize_t {
+        match self {
+            PathState::Failed => -1,
+            PathState::Unknown => 0,
+            PathState::Validating => 1,
+            PathState::ValidatingMTU => 2,
+            PathState::Validated => 3,
+        }
+    }
+}
+
+/// A path-specific event.
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub enum PathEvent {
+    /// A new network path (local address, peer address) has been seen on a
+    /// received packet. Note that this event is only triggered for servers, as
+    /// the client is responsible from initiating new paths. The application may
+    /// then probe this new path, if desired.
+    New(SocketAddr, SocketAddr),
+
+    /// The related network path between local `SocketAddr` and peer
+    /// `SocketAddr` has been validated.
+    Validated(SocketAddr, SocketAddr),
+
+    /// The related network path between local `SocketAddr` and peer
+    /// `SocketAddr` failed to be validated. This network path will not be used
+    /// anymore, unless the application requests probing this path again.
+    FailedValidation(SocketAddr, SocketAddr),
+
+    /// The related network path between local `SocketAddr` and peer
+    /// `SocketAddr` has been closed and is now unusable on this connection.
+    Closed(SocketAddr, SocketAddr),
+
+    /// The stack observes that the Source Connection ID with the given sequence
+    /// number, initially used by the peer over the first pair of `SocketAddr`s,
+    /// is now reused over the second pair of `SocketAddr`s.
+    ReusedSourceConnectionId(
+        u64,
+        (SocketAddr, SocketAddr),
+        (SocketAddr, SocketAddr),
+    ),
+
+    /// The connection observed that the peer migrated over the network path
+    /// denoted by the pair of `SocketAddr`, i.e., non-probing packets have been
+    /// received on this network path. This is a server side only event.
+    ///
+    /// Note that this event is only raised if the path has been validated.
+    PeerMigrated(SocketAddr, SocketAddr),
+}
+
+/// A network path on which QUIC packets can be sent.
+#[derive(Debug)]
+pub struct Path {
+    /// The local address.
+    local_addr: SocketAddr,
+
+    /// The remote address.
+    peer_addr: SocketAddr,
+
+    /// Source CID sequence number used over that path.
+    pub active_scid_seq: Option<u64>,
+
+    /// Destination CID sequence number used over that path.
+    pub active_dcid_seq: Option<u64>,
+
+    /// The current validation state of the path.
+    state: PathState,
+
+    /// Is this path used to send non-probing packets.
+    active: bool,
+
+    /// Loss recovery and congestion control state.
+    pub recovery: recovery::Recovery,
+
+    /// Pending challenge data with the size of the packet containing them and
+    /// when they were sent.
+    in_flight_challenges: VecDeque<([u8; 8], usize, time::Instant)>,
+
+    /// The maximum challenge size that got acknowledged.
+    max_challenge_size: usize,
+
+    /// Number of consecutive (spaced by at least 1 RTT) probing packets lost.
+    probing_lost: usize,
+
+    /// Last instant when a probing packet got lost.
+    last_probe_lost_time: Option<time::Instant>,
+
+    /// Received challenge data.
+    received_challenges: VecDeque<[u8; 8]>,
+
+    /// Number of packets sent on this path.
+    pub sent_count: usize,
+
+    /// Number of packets received on this path.
+    pub recv_count: usize,
+
+    /// Total number of packets sent with data retransmitted from this path.
+    pub retrans_count: usize,
+
+    /// Total number of sent bytes over this path.
+    pub sent_bytes: u64,
+
+    /// Total number of bytes received over this path.
+    pub recv_bytes: u64,
+
+    /// Total number of bytes retransmitted from this path.
+    /// This counts only STREAM and CRYPTO data.
+    pub stream_retrans_bytes: u64,
+
+    /// Total number of bytes the server can send before the peer's address
+    /// is verified.
+    pub max_send_bytes: usize,
+
+    /// Whether the peer's address has been verified.
+    pub verified_peer_address: bool,
+
+    /// Whether the peer has verified our address.
+    pub peer_verified_local_address: bool,
+
+    /// Does it requires sending PATH_CHALLENGE?
+    challenge_requested: bool,
+
+    /// Whether the failure of this path was notified.
+    failure_notified: bool,
+
+    /// Whether the connection tries to migrate to this path, but it still needs
+    /// to be validated.
+    migrating: bool,
+
+    /// Whether or not we should force eliciting of an ACK (e.g. via PING frame)
+    pub needs_ack_eliciting: bool,
+}
+
+impl Path {
+    /// Create a new Path instance with the provided addresses, the remaining of
+    /// the fields being set to their default value.
+    pub fn new(
+        local_addr: SocketAddr, peer_addr: SocketAddr,
+        recovery_config: &recovery::RecoveryConfig, is_initial: bool,
+    ) -> Self {
+        let (state, active_scid_seq, active_dcid_seq) = if is_initial {
+            (PathState::Validated, Some(0), Some(0))
+        } else {
+            (PathState::Unknown, None, None)
+        };
+
+        Self {
+            local_addr,
+            peer_addr,
+            active_scid_seq,
+            active_dcid_seq,
+            state,
+            active: false,
+            recovery: recovery::Recovery::new_with_config(recovery_config),
+            in_flight_challenges: VecDeque::new(),
+            max_challenge_size: 0,
+            probing_lost: 0,
+            last_probe_lost_time: None,
+            received_challenges: VecDeque::new(),
+            sent_count: 0,
+            recv_count: 0,
+            retrans_count: 0,
+            sent_bytes: 0,
+            recv_bytes: 0,
+            stream_retrans_bytes: 0,
+            max_send_bytes: 0,
+            verified_peer_address: false,
+            peer_verified_local_address: false,
+            challenge_requested: false,
+            failure_notified: false,
+            migrating: false,
+            needs_ack_eliciting: false,
+        }
+    }
+
+    /// Returns the local address on which this path operates.
+    #[inline]
+    pub fn local_addr(&self) -> SocketAddr {
+        self.local_addr
+    }
+
+    /// Returns the peer address on which this path operates.
+    #[inline]
+    pub fn peer_addr(&self) -> SocketAddr {
+        self.peer_addr
+    }
+
+    /// Returns whether the path is working (i.e., not failed).
+    #[inline]
+    fn working(&self) -> bool {
+        self.state > PathState::Failed
+    }
+
+    /// Returns whether the path is active.
+    #[inline]
+    pub fn active(&self) -> bool {
+        self.active && self.working() && self.active_dcid_seq.is_some()
+    }
+
+    /// Returns whether the path can be used to send non-probing packets.
+    #[inline]
+    pub fn usable(&self) -> bool {
+        self.active() ||
+            (self.state == PathState::Validated &&
+                self.active_dcid_seq.is_some())
+    }
+
+    /// Returns whether the path is unused.
+    #[inline]
+    fn unused(&self) -> bool {
+        // FIXME: we should check that there is nothing in the sent queue.
+        !self.active() && self.active_dcid_seq.is_none()
+    }
+
+    /// Returns whether the path requires sending a probing packet.
+    #[inline]
+    pub fn probing_required(&self) -> bool {
+        !self.received_challenges.is_empty() || self.validation_requested()
+    }
+
+    /// Promotes the path to the provided state only if the new state is greater
+    /// than the current one.
+    fn promote_to(&mut self, state: PathState) {
+        if self.state < state {
+            self.state = state;
+        }
+    }
+
+    /// Returns whether the path is validated.
+    #[inline]
+    pub fn validated(&self) -> bool {
+        self.state == PathState::Validated
+    }
+
+    /// Returns whether this path failed its validation.
+    #[inline]
+    fn validation_failed(&self) -> bool {
+        self.state == PathState::Failed
+    }
+
+    // Returns whether this path is under path validation process.
+    #[inline]
+    pub fn under_validation(&self) -> bool {
+        matches!(self.state, PathState::Validating | PathState::ValidatingMTU)
+    }
+
+    /// Requests path validation.
+    #[inline]
+    pub fn request_validation(&mut self) {
+        self.challenge_requested = true;
+    }
+
+    /// Returns whether a validation is requested.
+    #[inline]
+    pub fn validation_requested(&self) -> bool {
+        self.challenge_requested
+    }
+
+    pub fn on_challenge_sent(&mut self) {
+        self.promote_to(PathState::Validating);
+        self.challenge_requested = false;
+    }
+
+    /// Handles the sending of PATH_CHALLENGE.
+    pub fn add_challenge_sent(
+        &mut self, data: [u8; 8], pkt_size: usize, sent_time: time::Instant,
+    ) {
+        self.on_challenge_sent();
+        self.in_flight_challenges
+            .push_back((data, pkt_size, sent_time));
+    }
+
+    pub fn on_challenge_received(&mut self, data: [u8; 8]) {
+        self.received_challenges.push_back(data);
+        self.peer_verified_local_address = true;
+    }
+
+    pub fn has_pending_challenge(&self, data: [u8; 8]) -> bool {
+        self.in_flight_challenges.iter().any(|(d, ..)| *d == data)
+    }
+
+    /// Returns whether the path is now validated.
+    pub fn on_response_received(&mut self, data: [u8; 8]) -> bool {
+        self.verified_peer_address = true;
+        self.probing_lost = 0;
+
+        let mut challenge_size = 0;
+        self.in_flight_challenges.retain(|(d, s, _)| {
+            if *d == data {
+                challenge_size = *s;
+                false
+            } else {
+                true
+            }
+        });
+
+        // The 4-tuple is reachable, but we didn't check Path MTU yet.
+        self.promote_to(PathState::ValidatingMTU);
+
+        self.max_challenge_size =
+            std::cmp::max(self.max_challenge_size, challenge_size);
+
+        if self.state == PathState::ValidatingMTU {
+            if self.max_challenge_size >= crate::MIN_CLIENT_INITIAL_LEN {
+                // Path MTU is sufficient for QUIC traffic.
+                self.promote_to(PathState::Validated);
+                return true;
+            }
+
+            // If the MTU was not validated, probe again.
+            self.request_validation();
+        }
+
+        false
+    }
+
+    fn on_failed_validation(&mut self) {
+        self.state = PathState::Failed;
+        self.active = false;
+    }
+
+    #[inline]
+    pub fn pop_received_challenge(&mut self) -> Option<[u8; 8]> {
+        self.received_challenges.pop_front()
+    }
+
+    pub fn on_loss_detection_timeout(
+        &mut self, handshake_status: HandshakeStatus, now: time::Instant,
+        is_server: bool, trace_id: &str,
+    ) -> (usize, usize) {
+        let (lost_packets, lost_bytes) = self.recovery.on_loss_detection_timeout(
+            handshake_status,
+            now,
+            trace_id,
+        );
+
+        let mut lost_probe_time = None;
+        self.in_flight_challenges.retain(|(_, _, sent_time)| {
+            if *sent_time <= now {
+                if lost_probe_time.is_none() {
+                    lost_probe_time = Some(*sent_time);
+                }
+                false
+            } else {
+                true
+            }
+        });
+
+        // If we lost probing packets, check if the path failed
+        // validation.
+        if let Some(lost_probe_time) = lost_probe_time {
+            self.last_probe_lost_time = match self.last_probe_lost_time {
+                Some(last) => {
+                    // Count a loss if at least 1-RTT happened.
+                    if lost_probe_time - last >= self.recovery.rtt() {
+                        self.probing_lost += 1;
+                        Some(lost_probe_time)
+                    } else {
+                        Some(last)
+                    }
+                },
+                None => {
+                    self.probing_lost += 1;
+                    Some(lost_probe_time)
+                },
+            };
+            // As a server, if requesting a challenge is not
+            // possible due to the amplification attack, declare the
+            // validation as failed.
+            if self.probing_lost >= crate::MAX_PROBING_TIMEOUTS ||
+                (is_server && self.max_send_bytes < crate::MIN_PROBING_SIZE)
+            {
+                self.on_failed_validation();
+            } else {
+                self.request_validation();
+            }
+        }
+
+        (lost_packets, lost_bytes)
+    }
+
+    pub fn stats(&self) -> PathStats {
+        PathStats {
+            local_addr: self.local_addr,
+            peer_addr: self.peer_addr,
+            validation_state: self.state,
+            active: self.active,
+            recv: self.recv_count,
+            sent: self.sent_count,
+            lost: self.recovery.lost_count,
+            retrans: self.retrans_count,
+            rtt: self.recovery.rtt(),
+            min_rtt: self.recovery.min_rtt(),
+            rttvar: self.recovery.rttvar(),
+            cwnd: self.recovery.cwnd(),
+            sent_bytes: self.sent_bytes,
+            recv_bytes: self.recv_bytes,
+            lost_bytes: self.recovery.bytes_lost,
+            stream_retrans_bytes: self.stream_retrans_bytes,
+            pmtu: self.recovery.max_datagram_size(),
+            delivery_rate: self.recovery.delivery_rate(),
+        }
+    }
+}
+
+/// An iterator over SocketAddr.
+#[derive(Default)]
+pub struct SocketAddrIter {
+    pub(crate) sockaddrs: Vec<SocketAddr>,
+}
+
+impl Iterator for SocketAddrIter {
+    type Item = SocketAddr;
+
+    #[inline]
+    fn next(&mut self) -> Option<Self::Item> {
+        self.sockaddrs.pop()
+    }
+}
+
+impl ExactSizeIterator for SocketAddrIter {
+    #[inline]
+    fn len(&self) -> usize {
+        self.sockaddrs.len()
+    }
+}
+
+/// All path-related information.
+pub struct PathMap {
+    /// The paths of the connection. Each of them has an internal identifier
+    /// that is used by `addrs_to_paths` and `ConnectionEntry`.
+    paths: Slab<Path>,
+
+    /// The maximum number of concurrent paths allowed.
+    max_concurrent_paths: usize,
+
+    /// The mapping from the (local `SocketAddr`, peer `SocketAddr`) to the
+    /// `Path` structure identifier.
+    addrs_to_paths: BTreeMap<(SocketAddr, SocketAddr), usize>,
+
+    /// Path-specific events to be notified to the application.
+    events: VecDeque<PathEvent>,
+
+    /// Whether this manager serves a connection as a server.
+    is_server: bool,
+}
+
+impl PathMap {
+    /// Creates a new `PathMap` with the initial provided `path` and a
+    /// capacity limit.
+    pub fn new(
+        mut initial_path: Path, max_concurrent_paths: usize, is_server: bool,
+    ) -> Self {
+        let mut paths = Slab::with_capacity(1); // most connections only have one path
+        let mut addrs_to_paths = BTreeMap::new();
+
+        let local_addr = initial_path.local_addr;
+        let peer_addr = initial_path.peer_addr;
+
+        // As it is the first path, it is active by default.
+        initial_path.active = true;
+
+        let active_path_id = paths.insert(initial_path);
+        addrs_to_paths.insert((local_addr, peer_addr), active_path_id);
+
+        Self {
+            paths,
+            max_concurrent_paths,
+            addrs_to_paths,
+            events: VecDeque::new(),
+            is_server,
+        }
+    }
+
+    /// Gets an immutable reference to the path identified by `path_id`. If the
+    /// provided `path_id` does not identify any current `Path`, returns an
+    /// [`InvalidState`].
+    ///
+    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
+    #[inline]
+    pub fn get(&self, path_id: usize) -> Result<&Path> {
+        self.paths.get(path_id).ok_or(Error::InvalidState)
+    }
+
+    /// Gets a mutable reference to the path identified by `path_id`. If the
+    /// provided `path_id` does not identify any current `Path`, returns an
+    /// [`InvalidState`].
+    ///
+    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
+    #[inline]
+    pub fn get_mut(&mut self, path_id: usize) -> Result<&mut Path> {
+        self.paths.get_mut(path_id).ok_or(Error::InvalidState)
+    }
+
+    #[inline]
+    /// Gets an immutable reference to the active path with the value of the
+    /// lowest identifier. If there is no active path, returns `None`.
+    pub fn get_active_with_pid(&self) -> Option<(usize, &Path)> {
+        self.paths.iter().find(|(_, p)| p.active())
+    }
+
+    /// Gets an immutable reference to the active path with the lowest
+    /// identifier. If there is no active path, returns an [`InvalidState`].
+    ///
+    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
+    #[inline]
+    pub fn get_active(&self) -> Result<&Path> {
+        self.get_active_with_pid()
+            .map(|(_, p)| p)
+            .ok_or(Error::InvalidState)
+    }
+
+    /// Gets the lowest active path identifier. If there is no active path,
+    /// returns an [`InvalidState`].
+    ///
+    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
+    #[inline]
+    pub fn get_active_path_id(&self) -> Result<usize> {
+        self.get_active_with_pid()
+            .map(|(pid, _)| pid)
+            .ok_or(Error::InvalidState)
+    }
+
+    /// Gets an mutable reference to the active path with the lowest identifier.
+    /// If there is no active path, returns an [`InvalidState`].
+    ///
+    /// [`InvalidState`]: enum.Error.html#variant.InvalidState
+    #[inline]
+    pub fn get_active_mut(&mut self) -> Result<&mut Path> {
+        self.paths
+            .iter_mut()
+            .map(|(_, p)| p)
+            .find(|p| p.active())
+            .ok_or(Error::InvalidState)
+    }
+
+    /// Returns an iterator over all existing paths.
+    #[inline]
+    pub fn iter(&self) -> slab::Iter<Path> {
+        self.paths.iter()
+    }
+
+    /// Returns a mutable iterator over all existing paths.
+    #[inline]
+    pub fn iter_mut(&mut self) -> slab::IterMut<Path> {
+        self.paths.iter_mut()
+    }
+
+    /// Returns the number of existing paths.
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.paths.len()
+    }
+
+    /// Returns the `Path` identifier related to the provided `addrs`.
+    #[inline]
+    pub fn path_id_from_addrs(
+        &self, addrs: &(SocketAddr, SocketAddr),
+    ) -> Option<usize> {
+        self.addrs_to_paths.get(addrs).copied()
+    }
+
+    /// Checks if creating a new path will not exceed the current `self.paths`
+    /// capacity. If yes, this method tries to remove one unused path. If it
+    /// fails to do so, returns [`Done`].
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    fn make_room_for_new_path(&mut self) -> Result<()> {
+        if self.paths.len() < self.max_concurrent_paths {
+            return Ok(());
+        }
+
+        let (pid_to_remove, _) = self
+            .paths
+            .iter()
+            .find(|(_, p)| p.unused())
+            .ok_or(Error::Done)?;
+
+        let path = self.paths.remove(pid_to_remove);
+        self.addrs_to_paths
+            .remove(&(path.local_addr, path.peer_addr));
+
+        self.notify_event(PathEvent::Closed(path.local_addr, path.peer_addr));
+
+        Ok(())
+    }
+
+    /// Records the provided `Path` and returns its assigned identifier.
+    ///
+    /// On success, this method takes care of creating a notification to the
+    /// serving application, if it serves a server-side connection.
+    ///
+    /// If there are already `max_concurrent_paths` currently recorded, this
+    /// method tries to remove an unused `Path` first. If it fails to do so,
+    /// it returns [`Done`].
+    ///
+    /// [`Done`]: enum.Error.html#variant.Done
+    pub fn insert_path(&mut self, path: Path, is_server: bool) -> Result<usize> {
+        self.make_room_for_new_path()?;
+
+        let local_addr = path.local_addr;
+        let peer_addr = path.peer_addr;
+
+        let pid = self.paths.insert(path);
+        self.addrs_to_paths.insert((local_addr, peer_addr), pid);
+
+        // Notifies the application if we are in server mode.
+        if is_server {
+            self.notify_event(PathEvent::New(local_addr, peer_addr));
+        }
+
+        Ok(pid)
+    }
+
+    /// Notifies a path event to the application served by the connection.
+    pub fn notify_event(&mut self, ev: PathEvent) {
+        self.events.push_back(ev);
+    }
+
+    /// Gets the first path event to be notified to the application.
+    pub fn pop_event(&mut self) -> Option<PathEvent> {
+        self.events.pop_front()
+    }
+
+    /// Notifies all failed validations to the application.
+    pub fn notify_failed_validations(&mut self) {
+        let validation_failed = self
+            .paths
+            .iter_mut()
+            .filter(|(_, p)| p.validation_failed() && !p.failure_notified);
+
+        for (_, p) in validation_failed {
+            self.events.push_back(PathEvent::FailedValidation(
+                p.local_addr,
+                p.peer_addr,
+            ));
+
+            p.failure_notified = true;
+        }
+    }
+
+    /// Finds a path candidate to be active and returns its identifier.
+    pub fn find_candidate_path(&self) -> Option<usize> {
+        // TODO: also consider unvalidated paths if there are no more validated.
+        self.paths
+            .iter()
+            .find(|(_, p)| p.usable())
+            .map(|(pid, _)| pid)
+    }
+
+    /// Handles incoming PATH_RESPONSE data.
+    pub fn on_response_received(&mut self, data: [u8; 8]) -> Result<()> {
+        let active_pid = self.get_active_path_id()?;
+
+        let challenge_pending =
+            self.iter_mut().find(|(_, p)| p.has_pending_challenge(data));
+
+        if let Some((pid, p)) = challenge_pending {
+            if p.on_response_received(data) {
+                let local_addr = p.local_addr;
+                let peer_addr = p.peer_addr;
+                let was_migrating = p.migrating;
+
+                p.migrating = false;
+
+                // Notifies the application.
+                self.notify_event(PathEvent::Validated(local_addr, peer_addr));
+
+                // If this path was the candidate for migration, notifies the
+                // application.
+                if pid == active_pid && was_migrating {
+                    self.notify_event(PathEvent::PeerMigrated(
+                        local_addr, peer_addr,
+                    ));
+                }
+            }
+        }
+        Ok(())
+    }
+
+    /// Sets the path with identifier 'path_id' to be active.
+    ///
+    /// There can be exactly one active path on which non-probing packets can be
+    /// sent. If another path is marked as active, it will be superseded by the
+    /// one having `path_id` as identifier.
+    ///
+    /// A server should always ensure that the active path is validated. If it
+    /// is already the case, it notifies the application that the connection
+    /// migrated. Otherwise, it triggers a path validation and defers the
+    /// notification once it is actually validated.
+    pub fn set_active_path(&mut self, path_id: usize) -> Result<()> {
+        let is_server = self.is_server;
+
+        if let Ok(old_active_path) = self.get_active_mut() {
+            old_active_path.active = false;
+        }
+
+        let new_active_path = self.get_mut(path_id)?;
+        new_active_path.active = true;
+
+        if is_server {
+            if new_active_path.validated() {
+                let local_addr = new_active_path.local_addr();
+                let peer_addr = new_active_path.peer_addr();
+
+                self.notify_event(PathEvent::PeerMigrated(local_addr, peer_addr));
+            } else {
+                new_active_path.migrating = true;
+
+                // Requests path validation if needed.
+                if !new_active_path.under_validation() {
+                    new_active_path.request_validation();
+                }
+            }
+        }
+
+        Ok(())
+    }
+
+    /// Handles potential connection migration.
+    pub fn on_peer_migrated(
+        &mut self, new_pid: usize, disable_dcid_reuse: bool,
+    ) -> Result<()> {
+        let active_path_id = self.get_active_path_id()?;
+
+        if active_path_id == new_pid {
+            return Ok(());
+        }
+
+        self.set_active_path(new_pid)?;
+
+        let no_spare_dcid = self.get_mut(new_pid)?.active_dcid_seq.is_none();
+
+        if no_spare_dcid && !disable_dcid_reuse {
+            self.get_mut(new_pid)?.active_dcid_seq =
+                self.get_mut(active_path_id)?.active_dcid_seq;
+        }
+
+        Ok(())
+    }
+}
+
+/// Statistics about the path of a connection.
+///
+/// It is part of the `Stats` structure returned by the [`stats()`] method.
+///
+/// [`stats()`]: struct.Connection.html#method.stats
+#[derive(Clone)]
+pub struct PathStats {
+    /// The local address of the path.
+    pub local_addr: SocketAddr,
+
+    /// The peer address of the path.
+    pub peer_addr: SocketAddr,
+
+    /// The path validation state.
+    pub validation_state: PathState,
+
+    /// Whether the path is marked as active.
+    pub active: bool,
+
+    /// The number of QUIC packets received.
+    pub recv: usize,
+
+    /// The number of QUIC packets sent.
+    pub sent: usize,
+
+    /// The number of QUIC packets that were lost.
+    pub lost: usize,
+
+    /// The number of sent QUIC packets with retransmitted data.
+    pub retrans: usize,
+
+    /// The estimated round-trip time of the connection.
+    pub rtt: time::Duration,
+
+    /// The minimum round-trip time observed.
+    pub min_rtt: Option<time::Duration>,
+
+    /// The estimated round-trip time variation in samples using a mean
+    /// variation.
+    pub rttvar: time::Duration,
+
+    /// The size of the connection's congestion window in bytes.
+    pub cwnd: usize,
+
+    /// The number of sent bytes.
+    pub sent_bytes: u64,
+
+    /// The number of received bytes.
+    pub recv_bytes: u64,
+
+    /// The number of bytes lost.
+    pub lost_bytes: u64,
+
+    /// The number of stream bytes retransmitted.
+    pub stream_retrans_bytes: u64,
+
+    /// The current PMTU for the connection.
+    pub pmtu: usize,
+
+    /// The most recent data delivery rate estimate in bytes/s.
+    ///
+    /// Note that this value could be inaccurate if the application does not
+    /// respect pacing hints (see [`SendInfo.at`] and [Pacing] for more
+    /// details).
+    ///
+    /// [`SendInfo.at`]: struct.SendInfo.html#structfield.at
+    /// [Pacing]: index.html#pacing
+    pub delivery_rate: u64,
+}
+
+impl std::fmt::Debug for PathStats {
+    #[inline]
+    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+        write!(
+            f,
+            "local_addr={:?} peer_addr={:?} ",
+            self.local_addr, self.peer_addr,
+        )?;
+        write!(
+            f,
+            "validation_state={:?} active={} ",
+            self.validation_state, self.active,
+        )?;
+        write!(
+            f,
+            "recv={} sent={} lost={} retrans={} rtt={:?} min_rtt={:?} rttvar={:?} cwnd={}",
+            self.recv, self.sent, self.lost, self.retrans, self.rtt, self.min_rtt, self.rttvar, self.cwnd,
+        )?;
+
+        write!(
+            f,
+            " sent_bytes={} recv_bytes={} lost_bytes={}",
+            self.sent_bytes, self.recv_bytes, self.lost_bytes,
+        )?;
+
+        write!(
+            f,
+            " stream_retrans_bytes={} pmtu={} delivery_rate={}",
+            self.stream_retrans_bytes, self.pmtu, self.delivery_rate,
+        )
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::rand;
+    use crate::MIN_CLIENT_INITIAL_LEN;
+
+    use crate::recovery::RecoveryConfig;
+    use crate::Config;
+
+    use super::*;
+
+    #[test]
+    fn path_validation_limited_mtu() {
+        let client_addr = "127.0.0.1:1234".parse().unwrap();
+        let client_addr_2 = "127.0.0.1:5678".parse().unwrap();
+        let server_addr = "127.0.0.1:4321".parse().unwrap();
+
+        let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        let recovery_config = RecoveryConfig::from_config(&config);
+
+        let path = Path::new(client_addr, server_addr, &recovery_config, true);
+        let mut path_mgr = PathMap::new(path, 2, false);
+
+        let probed_path =
+            Path::new(client_addr_2, server_addr, &recovery_config, false);
+        path_mgr.insert_path(probed_path, false).unwrap();
+
+        let pid = path_mgr
+            .path_id_from_addrs(&(client_addr_2, server_addr))
+            .unwrap();
+        path_mgr.get_mut(pid).unwrap().request_validation();
+        assert_eq!(path_mgr.get_mut(pid).unwrap().validation_requested(), true);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().probing_required(), true);
+
+        // Fake sending of PathChallenge in a packet of MIN_CLIENT_INITIAL_LEN - 1
+        // bytes.
+        let data = rand::rand_u64().to_be_bytes();
+        path_mgr.get_mut(pid).unwrap().add_challenge_sent(
+            data,
+            MIN_CLIENT_INITIAL_LEN - 1,
+            time::Instant::now(),
+        );
+
+        assert_eq!(path_mgr.get_mut(pid).unwrap().validation_requested(), false);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().probing_required(), false);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().under_validation(), true);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().validated(), false);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validating);
+        assert_eq!(path_mgr.pop_event(), None);
+
+        // Receives the response. The path is reachable, but the MTU is not
+        // validated yet.
+        path_mgr.on_response_received(data).unwrap();
+
+        assert_eq!(path_mgr.get_mut(pid).unwrap().validation_requested(), true);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().probing_required(), true);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().under_validation(), true);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().validated(), false);
+        assert_eq!(
+            path_mgr.get_mut(pid).unwrap().state,
+            PathState::ValidatingMTU
+        );
+        assert_eq!(path_mgr.pop_event(), None);
+
+        // Fake sending of PathChallenge in a packet of MIN_CLIENT_INITIAL_LEN
+        // bytes.
+        let data = rand::rand_u64().to_be_bytes();
+        path_mgr.get_mut(pid).unwrap().add_challenge_sent(
+            data,
+            MIN_CLIENT_INITIAL_LEN,
+            time::Instant::now(),
+        );
+
+        path_mgr.on_response_received(data).unwrap();
+
+        assert_eq!(path_mgr.get_mut(pid).unwrap().validation_requested(), false);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().probing_required(), false);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().under_validation(), false);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().validated(), true);
+        assert_eq!(path_mgr.get_mut(pid).unwrap().state, PathState::Validated);
+        assert_eq!(
+            path_mgr.pop_event(),
+            Some(PathEvent::Validated(client_addr_2, server_addr))
+        );
+    }
+
+    #[test]
+    fn multiple_probes() {
+        let client_addr = "127.0.0.1:1234".parse().unwrap();
+        let server_addr = "127.0.0.1:4321".parse().unwrap();
+
+        let config = Config::new(crate::PROTOCOL_VERSION).unwrap();
+        let recovery_config = RecoveryConfig::from_config(&config);
+
+        let path = Path::new(client_addr, server_addr, &recovery_config, true);
+        let mut client_path_mgr = PathMap::new(path, 2, false);
+        let mut server_path =
+            Path::new(server_addr, client_addr, &recovery_config, false);
+
+        let client_pid = client_path_mgr
+            .path_id_from_addrs(&(client_addr, server_addr))
+            .unwrap();
+
+        // First probe.
+        let data = rand::rand_u64().to_be_bytes();
+
+        client_path_mgr
+            .get_mut(client_pid)
+            .unwrap()
+            .add_challenge_sent(
+                data,
+                MIN_CLIENT_INITIAL_LEN,
+                time::Instant::now(),
+            );
+
+        // Second probe.
+        let data_2 = rand::rand_u64().to_be_bytes();
+
+        client_path_mgr
+            .get_mut(client_pid)
+            .unwrap()
+            .add_challenge_sent(
+                data_2,
+                MIN_CLIENT_INITIAL_LEN,
+                time::Instant::now(),
+            );
+        assert_eq!(
+            client_path_mgr
+                .get(client_pid)
+                .unwrap()
+                .in_flight_challenges
+                .len(),
+            2
+        );
+
+        // If we receive multiple challenges, we can store them.
+        server_path.on_challenge_received(data);
+        assert_eq!(server_path.received_challenges.len(), 1);
+        server_path.on_challenge_received(data_2);
+        assert_eq!(server_path.received_challenges.len(), 2);
+
+        // Response for first probe.
+        client_path_mgr.on_response_received(data).unwrap();
+        assert_eq!(
+            client_path_mgr
+                .get(client_pid)
+                .unwrap()
+                .in_flight_challenges
+                .len(),
+            1
+        );
+
+        // Response for second probe.
+        client_path_mgr.on_response_received(data_2).unwrap();
+        assert_eq!(
+            client_path_mgr
+                .get(client_pid)
+                .unwrap()
+                .in_flight_challenges
+                .len(),
+            0
+        );
+    }
+}
diff --git a/src/ranges.rs b/src/ranges.rs
index c390873..91ddb90 100644
--- a/src/ranges.rs
+++ b/src/ranges.rs
@@ -30,7 +30,7 @@
 use std::collections::BTreeMap;
 use std::collections::Bound;
 
-#[derive(Clone, PartialEq, PartialOrd)]
+#[derive(Clone, PartialEq, Eq, PartialOrd)]
 pub struct RangeSet {
     inner: BTreeMap<u64, u64>,
 
@@ -82,9 +82,7 @@
         }
 
         if self.inner.len() >= self.capacity {
-            if let Some(first) = self.inner.keys().next().copied() {
-                self.inner.remove(&first);
-            }
+            self.inner.pop_first();
         }
 
         self.inner.insert(start, end);
@@ -108,7 +106,6 @@
     }
 
     pub fn push_item(&mut self, item: u64) {
-        #[allow(clippy::range_plus_one)]
         self.insert(item..item + 1);
     }
 
@@ -155,7 +152,7 @@
 
 impl Default for RangeSet {
     fn default() -> Self {
-        Self::new(std::usize::MAX)
+        Self::new(usize::MAX)
     }
 }
 
@@ -191,7 +188,7 @@
             })
             .collect();
 
-        write!(f, "{:?}", ranges)
+        write!(f, "{ranges:?}")
     }
 }
 
diff --git a/src/recovery/bbr/init.rs b/src/recovery/bbr/init.rs
new file mode 100644
index 0000000..6a3f7ba
--- /dev/null
+++ b/src/recovery/bbr/init.rs
@@ -0,0 +1,93 @@
+// Copyright (C) 2022, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use super::*;
+use crate::recovery::Recovery;
+
+use std::time::Duration;
+use std::time::Instant;
+
+// BBR Functions at Initialization.
+//
+
+// 4.3.1.  Initialization Steps
+pub fn bbr_init(r: &mut Recovery) {
+    let rtt = r.rtt();
+    let bbr = &mut r.bbr_state;
+
+    bbr.rtprop = rtt;
+    bbr.rtprop_stamp = Instant::now();
+    bbr.next_round_delivered = r.delivery_rate.delivered();
+
+    r.send_quantum = r.max_datagram_size;
+
+    bbr_init_round_counting(r);
+    bbr_init_full_pipe(r);
+    bbr_init_pacing_rate(r);
+    bbr_enter_startup(r);
+}
+
+// 4.1.1.3.  Tracking Time for the BBR.BtlBw Max Filter
+fn bbr_init_round_counting(r: &mut Recovery) {
+    let bbr = &mut r.bbr_state;
+
+    bbr.next_round_delivered = 0;
+    bbr.round_start = false;
+    bbr.round_count = 0;
+}
+
+// 4.2.1.  Pacing Rate
+fn bbr_init_pacing_rate(r: &mut Recovery) {
+    let bbr = &mut r.bbr_state;
+
+    let srtt = r
+        .smoothed_rtt
+        .unwrap_or_else(|| Duration::from_millis(1))
+        .as_secs_f64();
+
+    // At init, cwnd is initcwnd.
+    let nominal_bandwidth = r.congestion_window as f64 / srtt;
+
+    bbr.pacing_rate = (bbr.pacing_gain * nominal_bandwidth) as u64;
+}
+
+// 4.3.2.1.  Startup Dynamics
+pub fn bbr_enter_startup(r: &mut Recovery) {
+    let bbr = &mut r.bbr_state;
+
+    bbr.state = BBRStateMachine::Startup;
+    bbr.pacing_gain = BBR_HIGH_GAIN;
+    bbr.cwnd_gain = BBR_HIGH_GAIN;
+}
+
+// 4.3.2.2.  Estimating When Startup has Filled the Pipe
+fn bbr_init_full_pipe(r: &mut Recovery) {
+    let bbr = &mut r.bbr_state;
+
+    bbr.filled_pipe = false;
+    bbr.full_bw = 0;
+    bbr.full_bw_count = 0;
+}
diff --git a/src/recovery/bbr/mod.rs b/src/recovery/bbr/mod.rs
new file mode 100644
index 0000000..742cfc7
--- /dev/null
+++ b/src/recovery/bbr/mod.rs
@@ -0,0 +1,840 @@
+// Copyright (C) 2022, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! BBR Congestion Control
+//!
+//! This implementation is based on the following draft:
+//! <https://tools.ietf.org/html/draft-cardwell-iccrg-bbr-congestion-control-00>
+
+use crate::minmax::Minmax;
+use crate::packet;
+use crate::recovery::*;
+
+use std::time::Duration;
+use std::time::Instant;
+
+pub static BBR: CongestionControlOps = CongestionControlOps {
+    on_init,
+    reset,
+    on_packet_sent,
+    on_packets_acked,
+    congestion_event,
+    collapse_cwnd,
+    checkpoint,
+    rollback,
+    has_custom_pacing,
+    debug_fmt,
+};
+
+/// A constant specifying the length of the BBR.BtlBw max filter window for
+/// BBR.BtlBwFilter, BtlBwFilterLen is 10 packet-timed round trips.
+const BTLBW_FILTER_LEN: Duration = Duration::from_secs(10);
+
+/// A constant specifying the minimum time interval between ProbeRTT states: 10
+/// secs.
+const PROBE_RTT_INTERVAL: Duration = Duration::from_secs(10);
+
+/// A constant specifying the length of the RTProp min filter window.
+const RTPROP_FILTER_LEN: Duration = PROBE_RTT_INTERVAL;
+
+/// A constant specifying the minimum gain value that will allow the sending
+/// rate to double each round (2/ln(2) ~= 2.89), used in Startup mode for both
+/// BBR.pacing_gain and BBR.cwnd_gain.
+const BBR_HIGH_GAIN: f64 = 2.89;
+
+/// The minimal cwnd value BBR tries to target using: 4 packets, or 4 * SMSS
+const BBR_MIN_PIPE_CWND_PKTS: usize = 4;
+
+/// The number of phases in the BBR ProbeBW gain cycle: 8.
+const BBR_GAIN_CYCLE_LEN: usize = 8;
+
+/// A constant specifying the minimum duration for which ProbeRTT state holds
+/// inflight to BBRMinPipeCwnd or fewer packets: 200 ms.
+const PROBE_RTT_DURATION: Duration = Duration::from_millis(200);
+
+/// Pacing Gain Cycle.
+const PACING_GAIN_CYCLE: [f64; BBR_GAIN_CYCLE_LEN] =
+    [5.0 / 4.0, 3.0 / 4.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
+
+/// A constant to check BBR.BtlBW is still growing.
+const BTLBW_GROWTH_TARGET: f64 = 1.25;
+
+/// BBR Internal State Machine.
+#[derive(Debug, PartialEq, Eq)]
+enum BBRStateMachine {
+    Startup,
+    Drain,
+    ProbeBW,
+    ProbeRTT,
+}
+
+/// BBR Specific State Variables.
+pub struct State {
+    // The current state of a BBR flow in the BBR state machine.
+    state: BBRStateMachine,
+
+    // The current pacing rate for a BBR flow, which controls inter-packet
+    // spacing.
+    pacing_rate: u64,
+
+    // BBR's estimated bottleneck bandwidth available to the transport flow,
+    // estimated from the maximum delivery rate sample in a sliding window.
+    btlbw: u64,
+
+    // The max filter used to estimate BBR.BtlBw.
+    btlbwfilter: Minmax<u64>,
+
+    // BBR's estimated two-way round-trip propagation delay of the path,
+    // estimated from the windowed minimum recent round-trip delay sample.
+    rtprop: Duration,
+
+    // The wall clock time at which the current BBR.RTProp sample was obtained.
+    rtprop_stamp: Instant,
+
+    // A boolean recording whether the BBR.RTprop has expired and is due for a
+    // refresh with an application idle period or a transition into ProbeRTT
+    // state.
+    rtprop_expired: bool,
+
+    // The dynamic gain factor used to scale BBR.BtlBw to produce
+    // BBR.pacing_rate.
+    pacing_gain: f64,
+
+    // The dynamic gain factor used to scale the estimated BDP to produce a
+    // congestion window (cwnd).
+    cwnd_gain: f64,
+
+    // A boolean that records whether BBR estimates that it has ever fully
+    // utilized its available bandwidth ("filled the pipe").
+    filled_pipe: bool,
+
+    // Count of packet-timed round trips elapsed so far.
+    round_count: u64,
+
+    // A boolean that BBR sets to true once per packet-timed round trip,
+    // on ACKs that advance BBR.round_count.
+    round_start: bool,
+
+    // packet.delivered value denoting the end of a packet-timed round trip.
+    next_round_delivered: usize,
+
+    // Timestamp when ProbeRTT state ends.
+    probe_rtt_done_stamp: Option<Instant>,
+
+    // Checking if a roundtrip in ProbeRTT state ends.
+    probe_rtt_round_done: bool,
+
+    // Checking if in the packet conservation mode during recovery.
+    packet_conservation: bool,
+
+    // Saved cwnd before loss recovery.
+    prior_cwnd: usize,
+
+    // Checking if restarting from idle.
+    idle_restart: bool,
+
+    // Baseline level delivery rate for full pipe estimator.
+    full_bw: u64,
+
+    // The number of round for full pipe estimator without much growth.
+    full_bw_count: usize,
+
+    // Last time cycle_index is updated.
+    cycle_stamp: Instant,
+
+    // Current index of pacing_gain_cycle[].
+    cycle_index: usize,
+
+    // The upper bound on the volume of data BBR allows in flight.
+    target_cwnd: usize,
+
+    // Whether in the recovery episode.
+    in_recovery: bool,
+
+    // Start time of the connection.
+    start_time: Instant,
+
+    // Newly marked lost data size in bytes.
+    newly_lost_bytes: usize,
+
+    // Newly acked data size in bytes.
+    newly_acked_bytes: usize,
+
+    // bytes_in_flight before processing this ACK.
+    prior_bytes_in_flight: usize,
+}
+
+impl State {
+    pub fn new() -> Self {
+        let now = Instant::now();
+
+        State {
+            state: BBRStateMachine::Startup,
+
+            pacing_rate: 0,
+
+            btlbw: 0,
+
+            btlbwfilter: Minmax::new(0),
+
+            rtprop: Duration::ZERO,
+
+            rtprop_stamp: now,
+
+            rtprop_expired: false,
+
+            pacing_gain: 0.0,
+
+            cwnd_gain: 0.0,
+
+            filled_pipe: false,
+
+            round_count: 0,
+
+            round_start: false,
+
+            next_round_delivered: 0,
+
+            probe_rtt_done_stamp: None,
+
+            probe_rtt_round_done: false,
+
+            packet_conservation: false,
+
+            prior_cwnd: 0,
+
+            idle_restart: false,
+
+            full_bw: 0,
+
+            full_bw_count: 0,
+
+            cycle_stamp: now,
+
+            cycle_index: 0,
+
+            target_cwnd: 0,
+
+            in_recovery: false,
+
+            start_time: now,
+
+            newly_lost_bytes: 0,
+
+            newly_acked_bytes: 0,
+
+            prior_bytes_in_flight: 0,
+        }
+    }
+}
+
+// When entering the recovery episode.
+fn bbr_enter_recovery(r: &mut Recovery, now: Instant) {
+    r.bbr_state.prior_cwnd = per_ack::bbr_save_cwnd(r);
+
+    r.congestion_window = r.bytes_in_flight +
+        r.bbr_state.newly_acked_bytes.max(r.max_datagram_size);
+    r.congestion_recovery_start_time = Some(now);
+
+    r.bbr_state.packet_conservation = true;
+    r.bbr_state.in_recovery = true;
+
+    // Start round now.
+    r.bbr_state.next_round_delivered = r.delivery_rate.delivered();
+}
+
+// When exiting the recovery episode.
+fn bbr_exit_recovery(r: &mut Recovery) {
+    r.congestion_recovery_start_time = None;
+
+    r.bbr_state.packet_conservation = false;
+    r.bbr_state.in_recovery = false;
+
+    per_ack::bbr_restore_cwnd(r);
+}
+
+// Congestion Control Hooks.
+//
+fn on_init(r: &mut Recovery) {
+    init::bbr_init(r);
+}
+
+fn reset(r: &mut Recovery) {
+    r.bbr_state = State::new();
+
+    init::bbr_init(r);
+}
+
+fn on_packet_sent(r: &mut Recovery, sent_bytes: usize, _now: Instant) {
+    r.bytes_in_flight += sent_bytes;
+
+    per_transmit::bbr_on_transmit(r);
+}
+
+fn on_packets_acked(
+    r: &mut Recovery, packets: &[Acked], _epoch: packet::Epoch, now: Instant,
+) {
+    r.bbr_state.newly_acked_bytes = packets.iter().fold(0, |acked_bytes, p| {
+        r.bbr_state.prior_bytes_in_flight = r.bytes_in_flight;
+
+        per_ack::bbr_update_model_and_state(r, p, now);
+
+        r.bytes_in_flight = r.bytes_in_flight.saturating_sub(p.size);
+
+        acked_bytes + p.size
+    });
+
+    if let Some(pkt) = packets.last() {
+        if !r.in_congestion_recovery(pkt.time_sent) {
+            // Upon exiting loss recovery.
+            bbr_exit_recovery(r);
+        }
+    }
+
+    per_ack::bbr_update_control_parameters(r, now);
+
+    r.bbr_state.newly_lost_bytes = 0;
+}
+
+fn congestion_event(
+    r: &mut Recovery, lost_bytes: usize, time_sent: Instant,
+    _epoch: packet::Epoch, now: Instant,
+) {
+    r.bbr_state.newly_lost_bytes = lost_bytes;
+
+    // Upon entering Fast Recovery.
+    if !r.in_congestion_recovery(time_sent) {
+        // Upon entering Fast Recovery.
+        bbr_enter_recovery(r, now);
+    }
+}
+
+fn collapse_cwnd(r: &mut Recovery) {
+    r.bbr_state.prior_cwnd = per_ack::bbr_save_cwnd(r);
+
+    reno::collapse_cwnd(r);
+}
+
+fn checkpoint(_r: &mut Recovery) {}
+
+fn rollback(_r: &mut Recovery) -> bool {
+    false
+}
+
+fn has_custom_pacing() -> bool {
+    true
+}
+
+fn debug_fmt(r: &Recovery, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+    let bbr = &r.bbr_state;
+
+    write!(
+         f,
+         "bbr={{ state={:?} btlbw={} rtprop={:?} pacing_rate={} pacing_gain={} cwnd_gain={} target_cwnd={} send_quantum={} filled_pipe={} round_count={} }}",
+         bbr.state, bbr.btlbw, bbr.rtprop, bbr.pacing_rate, bbr.pacing_gain, bbr.cwnd_gain, bbr.target_cwnd, r.send_quantum(), bbr.filled_pipe, bbr.round_count
+    )
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use crate::recovery;
+
+    use smallvec::smallvec;
+
+    #[test]
+    fn bbr_init() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
+
+        let mut r = Recovery::new(&cfg);
+
+        // on_init() is called in Connection::new(), so it need to be
+        // called manually here.
+        r.on_init();
+
+        assert_eq!(r.cwnd(), r.max_datagram_size * INITIAL_WINDOW_PACKETS);
+        assert_eq!(r.bytes_in_flight, 0);
+
+        assert_eq!(r.bbr_state.state, BBRStateMachine::Startup);
+    }
+
+    #[test]
+    fn bbr_send() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+
+        r.on_init();
+        r.on_packet_sent_cc(1000, now);
+
+        assert_eq!(r.bytes_in_flight, 1000);
+    }
+
+    #[test]
+    fn bbr_startup() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let mss = r.max_datagram_size;
+
+        r.on_init();
+
+        // Send 5 packets.
+        for pn in 0..5 {
+            let pkt = Sent {
+                pkt_num: pn,
+                frames: smallvec![],
+                time_sent: now,
+                time_acked: None,
+                time_lost: None,
+                size: mss,
+                ack_eliciting: true,
+                in_flight: true,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                has_data: false,
+            };
+
+            r.on_packet_sent(
+                pkt,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            );
+        }
+
+        let rtt = Duration::from_millis(50);
+        let now = now + rtt;
+        let cwnd_prev = r.cwnd();
+
+        let mut acked = ranges::RangeSet::default();
+        acked.insert(0..5);
+
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            ),
+            Ok((0, 0)),
+        );
+
+        assert_eq!(r.bbr_state.state, BBRStateMachine::Startup);
+        assert_eq!(r.cwnd(), cwnd_prev + mss * 5);
+        assert_eq!(r.bytes_in_flight, 0);
+        assert_eq!(
+            r.delivery_rate(),
+            ((mss * 5) as f64 / rtt.as_secs_f64()) as u64
+        );
+        assert_eq!(r.bbr_state.btlbw, r.delivery_rate());
+    }
+
+    #[test]
+    fn bbr_congestion_event() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let mss = r.max_datagram_size;
+
+        r.on_init();
+
+        // Send 5 packets.
+        for pn in 0..5 {
+            let pkt = Sent {
+                pkt_num: pn,
+                frames: smallvec![],
+                time_sent: now,
+                time_acked: None,
+                time_lost: None,
+                size: mss,
+                ack_eliciting: true,
+                in_flight: true,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                has_data: false,
+            };
+
+            r.on_packet_sent(
+                pkt,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            );
+        }
+
+        let rtt = Duration::from_millis(50);
+        let now = now + rtt;
+
+        // Make a packet loss to trigger a congestion event.
+        let mut acked = ranges::RangeSet::default();
+        acked.insert(4..5);
+
+        // 2 acked, 2 x MSS lost.
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            ),
+            Ok((2, 2400)),
+        );
+
+        // Sent: 0, 1, 2, 3, 4, Acked 4.
+        assert_eq!(r.cwnd(), mss * 4);
+        // Stil in flight: 2, 3.
+        assert_eq!(r.bytes_in_flight, mss * 2);
+    }
+
+    #[test]
+    fn bbr_drain() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let mss = r.max_datagram_size;
+
+        r.on_init();
+
+        let mut pn = 0;
+
+        // Stop right before filled_pipe=true.
+        for _ in 0..3 {
+            let pkt = Sent {
+                pkt_num: pn,
+                frames: smallvec![],
+                time_sent: now,
+                time_acked: None,
+                time_lost: None,
+                size: mss,
+                ack_eliciting: true,
+                in_flight: true,
+                delivered: r.delivery_rate.delivered(),
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                has_data: false,
+            };
+
+            r.on_packet_sent(
+                pkt,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            );
+
+            pn += 1;
+
+            let rtt = Duration::from_millis(50);
+
+            let now = now + rtt;
+
+            let mut acked = ranges::RangeSet::default();
+            acked.insert(0..pn);
+
+            assert_eq!(
+                r.on_ack_received(
+                    &acked,
+                    25,
+                    packet::Epoch::Application,
+                    HandshakeStatus::default(),
+                    now,
+                    "",
+                ),
+                Ok((0, 0)),
+            );
+        }
+
+        // Stop at right before filled_pipe=true.
+        for _ in 0..5 {
+            let pkt = Sent {
+                pkt_num: pn,
+                frames: smallvec![],
+                time_sent: now,
+                time_acked: None,
+                time_lost: None,
+                size: mss,
+                ack_eliciting: true,
+                in_flight: true,
+                delivered: r.delivery_rate.delivered(),
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                has_data: false,
+            };
+
+            r.on_packet_sent(
+                pkt,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            );
+
+            pn += 1;
+        }
+
+        let rtt = Duration::from_millis(50);
+        let now = now + rtt;
+
+        let mut acked = ranges::RangeSet::default();
+
+        // We sent 5 packets, but ack only one, to stay
+        // in Drain state.
+        acked.insert(0..pn - 4);
+
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            ),
+            Ok((0, 0)),
+        );
+
+        // Now we are in Drain state.
+        assert_eq!(r.bbr_state.filled_pipe, true);
+        assert_eq!(r.bbr_state.state, BBRStateMachine::Drain);
+        assert!(r.bbr_state.pacing_gain < 1.0);
+    }
+
+    #[test]
+    fn bbr_probe_bw() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let mss = r.max_datagram_size;
+
+        r.on_init();
+
+        let mut pn = 0;
+
+        // At 4th roundtrip, filled_pipe=true and switch to Drain,
+        // but move to ProbeBW immediately because bytes_in_flight is
+        // smaller than BBRInFlight(1).
+        for _ in 0..4 {
+            let pkt = Sent {
+                pkt_num: pn,
+                frames: smallvec![],
+                time_sent: now,
+                time_acked: None,
+                time_lost: None,
+                size: mss,
+                ack_eliciting: true,
+                in_flight: true,
+                delivered: r.delivery_rate.delivered(),
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                has_data: false,
+            };
+
+            r.on_packet_sent(
+                pkt,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            );
+
+            pn += 1;
+
+            let rtt = Duration::from_millis(50);
+            let now = now + rtt;
+
+            let mut acked = ranges::RangeSet::default();
+            acked.insert(0..pn);
+
+            assert_eq!(
+                r.on_ack_received(
+                    &acked,
+                    25,
+                    packet::Epoch::Application,
+                    HandshakeStatus::default(),
+                    now,
+                    "",
+                ),
+                Ok((0, 0)),
+            );
+        }
+
+        // Now we are in ProbeBW state.
+        assert_eq!(r.bbr_state.filled_pipe, true);
+        assert_eq!(r.bbr_state.state, BBRStateMachine::ProbeBW);
+
+        // In the first ProbeBW cycle, pacing_gain should be >= 1.0.
+        assert!(r.bbr_state.pacing_gain >= 1.0);
+    }
+
+    #[test]
+    fn bbr_probe_rtt() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::BBR);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let mss = r.max_datagram_size;
+
+        r.on_init();
+
+        let mut pn = 0;
+
+        // At 4th roundtrip, filled_pipe=true and switch to Drain,
+        // but move to ProbeBW immediately because bytes_in_flight is
+        // smaller than BBRInFlight(1).
+        for _ in 0..4 {
+            let pkt = Sent {
+                pkt_num: pn,
+                frames: smallvec![],
+                time_sent: now,
+                time_acked: None,
+                time_lost: None,
+                size: mss,
+                ack_eliciting: true,
+                in_flight: true,
+                delivered: r.delivery_rate.delivered(),
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                has_data: false,
+            };
+
+            r.on_packet_sent(
+                pkt,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            );
+
+            pn += 1;
+
+            let rtt = Duration::from_millis(50);
+            let now = now + rtt;
+
+            let mut acked = ranges::RangeSet::default();
+            acked.insert(0..pn);
+
+            assert_eq!(
+                r.on_ack_received(
+                    &acked,
+                    25,
+                    packet::Epoch::Application,
+                    HandshakeStatus::default(),
+                    now,
+                    "",
+                ),
+                Ok((0, 0)),
+            );
+        }
+
+        // Now we are in ProbeBW state.
+        assert_eq!(r.bbr_state.state, BBRStateMachine::ProbeBW);
+
+        // After RTPROP_FILTER_LEN (10s), switch to ProbeRTT.
+        let now = now + RTPROP_FILTER_LEN;
+
+        let pkt = Sent {
+            pkt_num: pn,
+            frames: smallvec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: mss,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: r.delivery_rate.delivered(),
+            delivered_time: now,
+            first_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            pkt,
+            packet::Epoch::Application,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+
+        pn += 1;
+
+        // Don't update rtprop by giving larger rtt than before.
+        // If rtprop is updated, rtprop expiry check is reset.
+        let rtt = Duration::from_millis(100);
+        let now = now + rtt;
+
+        let mut acked = ranges::RangeSet::default();
+        acked.insert(0..pn);
+
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            ),
+            Ok((0, 0)),
+        );
+
+        assert_eq!(r.bbr_state.state, BBRStateMachine::ProbeRTT);
+        assert_eq!(r.bbr_state.pacing_gain, 1.0);
+    }
+}
+
+mod init;
+mod pacing;
+mod per_ack;
+mod per_transmit;
diff --git a/src/recovery/bbr/pacing.rs b/src/recovery/bbr/pacing.rs
new file mode 100644
index 0000000..e5e21dd
--- /dev/null
+++ b/src/recovery/bbr/pacing.rs
@@ -0,0 +1,43 @@
+// Copyright (C) 2022, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use crate::recovery::Recovery;
+
+// BBR Transmit Packet Pacing Functions
+//
+
+// 4.2.1. Pacing Rate
+pub fn bbr_set_pacing_rate_with_gain(r: &mut Recovery, pacing_gain: f64) {
+    let rate = (pacing_gain * r.bbr_state.btlbw as f64) as u64;
+
+    if r.bbr_state.filled_pipe || rate > r.bbr_state.pacing_rate {
+        r.bbr_state.pacing_rate = rate;
+    }
+}
+
+pub fn bbr_set_pacing_rate(r: &mut Recovery) {
+    bbr_set_pacing_rate_with_gain(r, r.bbr_state.pacing_gain);
+}
diff --git a/src/recovery/bbr/per_ack.rs b/src/recovery/bbr/per_ack.rs
new file mode 100644
index 0000000..6fe5651
--- /dev/null
+++ b/src/recovery/bbr/per_ack.rs
@@ -0,0 +1,380 @@
+// Copyright (C) 2022, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use super::*;
+use crate::rand;
+use crate::recovery;
+
+use std::cmp;
+use std::time::Instant;
+
+/// 1.2Mbps in bytes/sec
+const PACING_RATE_1_2MBPS: u64 = 1200 * 1000 / 8;
+
+/// 24Mbps in bytes/sec
+const PACING_RATE_24MBPS: u64 = 24 * 1000 * 1000 / 8;
+
+/// The minimal cwnd value BBR tries to target, in bytes
+#[inline]
+fn bbr_min_pipe_cwnd(r: &mut Recovery) -> usize {
+    BBR_MIN_PIPE_CWND_PKTS * r.max_datagram_size
+}
+
+// BBR Functions when ACK is received.
+//
+pub fn bbr_update_model_and_state(
+    r: &mut Recovery, packet: &Acked, now: Instant,
+) {
+    bbr_update_btlbw(r, packet);
+    bbr_check_cycle_phase(r, now);
+    bbr_check_full_pipe(r);
+    bbr_check_drain(r, now);
+    bbr_update_rtprop(r, now);
+    bbr_check_probe_rtt(r, now);
+}
+
+pub fn bbr_update_control_parameters(r: &mut Recovery, now: Instant) {
+    pacing::bbr_set_pacing_rate(r);
+    bbr_set_send_quantum(r);
+
+    // Set outgoing packet pacing rate
+    // It is called here because send_quantum may be updated too.
+    r.set_pacing_rate(r.bbr_state.pacing_rate, now);
+
+    bbr_set_cwnd(r);
+}
+
+// BBR Functions while processing ACKs.
+//
+
+// 4.1.1.5.  Updating the BBR.BtlBw Max Filter
+fn bbr_update_btlbw(r: &mut Recovery, packet: &Acked) {
+    bbr_update_round(r, packet);
+
+    if r.delivery_rate() >= r.bbr_state.btlbw ||
+        !r.delivery_rate.sample_is_app_limited()
+    {
+        // Since minmax filter is based on time,
+        // start_time + (round_count as seconds) is used instead.
+        r.bbr_state.btlbw = r.bbr_state.btlbwfilter.running_max(
+            BTLBW_FILTER_LEN,
+            r.bbr_state.start_time + Duration::from_secs(r.bbr_state.round_count),
+            r.delivery_rate(),
+        );
+    }
+}
+
+// 4.1.1.3 Tracking Time for the BBR.BtlBw Max Filter
+fn bbr_update_round(r: &mut Recovery, packet: &Acked) {
+    let bbr = &mut r.bbr_state;
+
+    if packet.delivered >= bbr.next_round_delivered {
+        bbr.next_round_delivered = r.delivery_rate.delivered();
+        bbr.round_count += 1;
+        bbr.round_start = true;
+        bbr.packet_conservation = false;
+    } else {
+        bbr.round_start = false;
+    }
+}
+
+// 4.1.2.3. Updating the BBR.RTprop Min Filter
+fn bbr_update_rtprop(r: &mut Recovery, now: Instant) {
+    let bbr = &mut r.bbr_state;
+    let rs_rtt = r.delivery_rate.sample_rtt();
+
+    bbr.rtprop_expired = now > bbr.rtprop_stamp + RTPROP_FILTER_LEN;
+
+    if !rs_rtt.is_zero() && (rs_rtt <= bbr.rtprop || bbr.rtprop_expired) {
+        bbr.rtprop = rs_rtt;
+        bbr.rtprop_stamp = now;
+    }
+}
+
+// 4.2.2 Send Quantum
+fn bbr_set_send_quantum(r: &mut Recovery) {
+    let rate = r.bbr_state.pacing_rate;
+
+    r.send_quantum = match rate {
+        rate if rate < PACING_RATE_1_2MBPS => r.max_datagram_size,
+
+        rate if rate < PACING_RATE_24MBPS => 2 * r.max_datagram_size,
+
+        _ => cmp::min((rate / 1000_u64) as usize, 64 * 1024),
+    }
+}
+
+// 4.2.3.2 Target cwnd
+fn bbr_inflight(r: &mut Recovery, gain: f64) -> usize {
+    let bbr = &mut r.bbr_state;
+
+    if bbr.rtprop == Duration::MAX {
+        return r.max_datagram_size * INITIAL_WINDOW_PACKETS;
+    }
+
+    let quanta = 3 * r.send_quantum;
+    let estimated_bdp = bbr.btlbw as f64 * bbr.rtprop.as_secs_f64();
+
+    (gain * estimated_bdp) as usize + quanta
+}
+
+fn bbr_update_target_cwnd(r: &mut Recovery) {
+    r.bbr_state.target_cwnd = bbr_inflight(r, r.bbr_state.cwnd_gain);
+}
+
+// 4.2.3.4 Modulating cwnd in Loss Recovery
+pub fn bbr_save_cwnd(r: &mut Recovery) -> usize {
+    if !r.bbr_state.in_recovery && r.bbr_state.state != BBRStateMachine::ProbeRTT
+    {
+        r.congestion_window
+    } else {
+        r.congestion_window.max(r.bbr_state.prior_cwnd)
+    }
+}
+
+pub fn bbr_restore_cwnd(r: &mut Recovery) {
+    r.congestion_window = r.congestion_window.max(r.bbr_state.prior_cwnd);
+}
+
+fn bbr_modulate_cwnd_for_recovery(r: &mut Recovery) {
+    let acked_bytes = r.bbr_state.newly_acked_bytes;
+    let lost_bytes = r.bbr_state.newly_lost_bytes;
+
+    if lost_bytes > 0 {
+        // QUIC mininum cwnd is 2 x MSS.
+        r.congestion_window = r
+            .congestion_window
+            .saturating_sub(lost_bytes)
+            .max(r.max_datagram_size * recovery::MINIMUM_WINDOW_PACKETS);
+    }
+
+    if r.bbr_state.packet_conservation {
+        r.congestion_window =
+            r.congestion_window.max(r.bytes_in_flight + acked_bytes);
+    }
+}
+
+// 4.2.3.5 Modulating cwnd in ProbeRTT
+fn bbr_modulate_cwnd_for_probe_rtt(r: &mut Recovery) {
+    if r.bbr_state.state == BBRStateMachine::ProbeRTT {
+        r.congestion_window = r.congestion_window.min(bbr_min_pipe_cwnd(r))
+    }
+}
+
+// 4.2.3.6 Core cwnd Adjustment Mechanism
+fn bbr_set_cwnd(r: &mut Recovery) {
+    let acked_bytes = r.bbr_state.newly_acked_bytes;
+
+    bbr_update_target_cwnd(r);
+    bbr_modulate_cwnd_for_recovery(r);
+
+    if !r.bbr_state.packet_conservation {
+        if r.bbr_state.filled_pipe {
+            r.congestion_window = cmp::min(
+                r.congestion_window + acked_bytes,
+                r.bbr_state.target_cwnd,
+            )
+        } else if r.congestion_window < r.bbr_state.target_cwnd ||
+            r.delivery_rate.delivered() <
+                r.max_datagram_size * INITIAL_WINDOW_PACKETS
+        {
+            r.congestion_window += acked_bytes;
+        }
+
+        r.congestion_window = r.congestion_window.max(bbr_min_pipe_cwnd(r))
+    }
+
+    bbr_modulate_cwnd_for_probe_rtt(r);
+}
+
+// 4.3.2.2.  Estimating When Startup has Filled the Pipe
+fn bbr_check_full_pipe(r: &mut Recovery) {
+    // No need to check for a full pipe now.
+    if r.bbr_state.filled_pipe ||
+        !r.bbr_state.round_start ||
+        r.delivery_rate.sample_is_app_limited()
+    {
+        return;
+    }
+
+    // BBR.BtlBw still growing?
+    if r.bbr_state.btlbw >=
+        (r.bbr_state.full_bw as f64 * BTLBW_GROWTH_TARGET) as u64
+    {
+        // record new baseline level
+        r.bbr_state.full_bw = r.bbr_state.btlbw;
+        r.bbr_state.full_bw_count = 0;
+        return;
+    }
+
+    // another round w/o much growth
+    r.bbr_state.full_bw_count += 1;
+
+    if r.bbr_state.full_bw_count >= 3 {
+        r.bbr_state.filled_pipe = true;
+    }
+}
+
+// 4.3.3.  Drain
+fn bbr_enter_drain(r: &mut Recovery) {
+    let bbr = &mut r.bbr_state;
+
+    bbr.state = BBRStateMachine::Drain;
+
+    // pace slowly
+    bbr.pacing_gain = 1.0 / BBR_HIGH_GAIN;
+
+    // maintain cwnd
+    bbr.cwnd_gain = BBR_HIGH_GAIN;
+}
+
+fn bbr_check_drain(r: &mut Recovery, now: Instant) {
+    if r.bbr_state.state == BBRStateMachine::Startup && r.bbr_state.filled_pipe {
+        bbr_enter_drain(r);
+    }
+
+    if r.bbr_state.state == BBRStateMachine::Drain &&
+        r.bytes_in_flight <= bbr_inflight(r, 1.0)
+    {
+        // we estimate queue is drained
+        bbr_enter_probe_bw(r, now);
+    }
+}
+
+// 4.3.4.3.  Gain Cycling Algorithm
+fn bbr_enter_probe_bw(r: &mut Recovery, now: Instant) {
+    let bbr = &mut r.bbr_state;
+
+    bbr.state = BBRStateMachine::ProbeBW;
+    bbr.pacing_gain = 1.0;
+    bbr.cwnd_gain = 2.0;
+
+    // cycle_index will be one of (1, 2, 3, 4, 5, 6, 7). Since
+    // bbr_advance_cycle_phase() is called right next and it will
+    // increase cycle_index by 1, the actual cycle_index in the
+    // beginning of ProbeBW will be one of (2, 3, 4, 5, 6, 7, 0)
+    // to avoid index 1 (pacing_gain=3/4). See 4.3.4.2 for details.
+    bbr.cycle_index = BBR_GAIN_CYCLE_LEN -
+        1 -
+        (rand::rand_u64_uniform(BBR_GAIN_CYCLE_LEN as u64 - 1) as usize);
+
+    bbr_advance_cycle_phase(r, now);
+}
+
+fn bbr_check_cycle_phase(r: &mut Recovery, now: Instant) {
+    let bbr = &mut r.bbr_state;
+
+    if bbr.state == BBRStateMachine::ProbeBW && bbr_is_next_cycle_phase(r, now) {
+        bbr_advance_cycle_phase(r, now);
+    }
+}
+
+fn bbr_advance_cycle_phase(r: &mut Recovery, now: Instant) {
+    let bbr = &mut r.bbr_state;
+
+    bbr.cycle_stamp = now;
+    bbr.cycle_index = (bbr.cycle_index + 1) % BBR_GAIN_CYCLE_LEN;
+    bbr.pacing_gain = PACING_GAIN_CYCLE[bbr.cycle_index];
+}
+
+fn bbr_is_next_cycle_phase(r: &mut Recovery, now: Instant) -> bool {
+    let bbr = &mut r.bbr_state;
+    let lost_bytes = bbr.newly_lost_bytes;
+    let pacing_gain = bbr.pacing_gain;
+    let prior_in_flight = bbr.prior_bytes_in_flight;
+
+    let is_full_length = (now - bbr.cycle_stamp) > bbr.rtprop;
+
+    // pacing_gain == 1.0
+    if (pacing_gain - 1.0).abs() < f64::EPSILON {
+        return is_full_length;
+    }
+
+    if pacing_gain > 1.0 {
+        return is_full_length &&
+            (lost_bytes > 0 ||
+                prior_in_flight >= bbr_inflight(r, pacing_gain));
+    }
+
+    is_full_length || prior_in_flight <= bbr_inflight(r, 1.0)
+}
+
+// 4.3.5.  ProbeRTT
+fn bbr_check_probe_rtt(r: &mut Recovery, now: Instant) {
+    if r.bbr_state.state != BBRStateMachine::ProbeRTT &&
+        r.bbr_state.rtprop_expired &&
+        !r.bbr_state.idle_restart
+    {
+        bbr_enter_probe_rtt(r);
+
+        r.bbr_state.prior_cwnd = bbr_save_cwnd(r);
+        r.bbr_state.probe_rtt_done_stamp = None;
+    }
+
+    if r.bbr_state.state == BBRStateMachine::ProbeRTT {
+        bbr_handle_probe_rtt(r, now);
+    }
+
+    r.bbr_state.idle_restart = false;
+}
+
+fn bbr_enter_probe_rtt(r: &mut Recovery) {
+    let bbr = &mut r.bbr_state;
+
+    bbr.state = BBRStateMachine::ProbeRTT;
+    bbr.pacing_gain = 1.0;
+    bbr.cwnd_gain = 1.0;
+}
+
+fn bbr_handle_probe_rtt(r: &mut Recovery, now: Instant) {
+    // Ignore low rate samples during ProbeRTT.
+    r.delivery_rate.update_app_limited(true);
+
+    if let Some(probe_rtt_done_stamp) = r.bbr_state.probe_rtt_done_stamp {
+        if r.bbr_state.round_start {
+            r.bbr_state.probe_rtt_round_done = true;
+        }
+
+        if r.bbr_state.probe_rtt_round_done && now > probe_rtt_done_stamp {
+            r.bbr_state.rtprop_stamp = now;
+
+            bbr_restore_cwnd(r);
+            bbr_exit_probe_rtt(r, now);
+        }
+    } else if r.bytes_in_flight <= bbr_min_pipe_cwnd(r) {
+        r.bbr_state.probe_rtt_done_stamp = Some(now + PROBE_RTT_DURATION);
+        r.bbr_state.probe_rtt_round_done = false;
+        r.bbr_state.next_round_delivered = r.delivery_rate.delivered();
+    }
+}
+
+fn bbr_exit_probe_rtt(r: &mut Recovery, now: Instant) {
+    if r.bbr_state.filled_pipe {
+        bbr_enter_probe_bw(r, now);
+    } else {
+        init::bbr_enter_startup(r);
+    }
+}
diff --git a/src/recovery/bbr/per_transmit.rs b/src/recovery/bbr/per_transmit.rs
new file mode 100644
index 0000000..f454387
--- /dev/null
+++ b/src/recovery/bbr/per_transmit.rs
@@ -0,0 +1,46 @@
+// Copyright (C) 2022, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+use super::*;
+
+use crate::recovery::Recovery;
+
+// BBR Functions when trasmitting packets.
+//
+pub fn bbr_on_transmit(r: &mut Recovery) {
+    bbr_handle_restart_from_idle(r);
+}
+
+// 4.3.4.4.  Restarting From Idle
+fn bbr_handle_restart_from_idle(r: &mut Recovery) {
+    if r.bytes_in_flight == 0 && r.delivery_rate.app_limited() {
+        r.bbr_state.idle_restart = true;
+
+        if r.bbr_state.state == BBRStateMachine::ProbeBW {
+            pacing::bbr_set_pacing_rate_with_gain(r, 1.0);
+        }
+    }
+}
diff --git a/src/recovery/cubic.rs b/src/recovery/cubic.rs
index 8b091c4..62f7a4e 100644
--- a/src/recovery/cubic.rs
+++ b/src/recovery/cubic.rs
@@ -45,13 +45,16 @@
 use crate::recovery::Recovery;
 
 pub static CUBIC: CongestionControlOps = CongestionControlOps {
+    on_init,
+    reset,
     on_packet_sent,
-    on_packet_acked,
+    on_packets_acked,
     congestion_event,
     collapse_cwnd,
     checkpoint,
     rollback,
     has_custom_pacing,
+    debug_fmt,
 };
 
 /// CUBIC Constants.
@@ -61,9 +64,12 @@
 
 const C: f64 = 0.4;
 
-/// The packet count threshold to restore to the prior state if the
-/// lost packet count since the last checkpoint is less than the threshold.
-const RESTORE_COUNT_THRESHOLD: usize = 10;
+/// Threshold for rolling back state, as percentage of lost packets relative to
+/// cwnd.
+const ROLLBACK_THRESHOLD_PERCENT: usize = 20;
+
+/// Minimum threshold for rolling back state, as number of packets.
+const MIN_ROLLBACK_THRESHOLD: usize = 2;
 
 /// Default value of alpha_aimd in the beginning of congestion avoidance.
 const ALPHA_AIMD: f64 = 3.0 * (1.0 - BETA_CUBIC) / (1.0 + BETA_CUBIC);
@@ -103,8 +109,6 @@
 
     w_max: f64,
 
-    w_last_max: f64,
-
     k: f64,
 
     epoch_start: Option<Instant>,
@@ -142,6 +146,12 @@
     }
 }
 
+fn on_init(_r: &mut Recovery) {}
+
+fn reset(r: &mut Recovery) {
+    r.cubic_state = State::default();
+}
+
 fn collapse_cwnd(r: &mut Recovery) {
     let cubic = &mut r.cubic_state;
 
@@ -186,6 +196,14 @@
     reno::on_packet_sent(r, sent_bytes, now);
 }
 
+fn on_packets_acked(
+    r: &mut Recovery, packets: &[Acked], epoch: packet::Epoch, now: Instant,
+) {
+    for pkt in packets {
+        on_packet_acked(r, pkt, epoch, now);
+    }
+}
+
 fn on_packet_acked(
     r: &mut Recovery, packet: &Acked, epoch: packet::Epoch, now: Instant,
 ) {
@@ -194,6 +212,13 @@
     r.bytes_in_flight = r.bytes_in_flight.saturating_sub(packet.size);
 
     if in_congestion_recovery {
+        r.prr.on_packet_acked(
+            packet.size,
+            r.bytes_in_flight,
+            r.ssthresh,
+            r.max_datagram_size,
+        );
+
         return;
     }
 
@@ -205,16 +230,21 @@
     // <https://tools.ietf.org/id/draft-ietf-tcpm-rfc8312bis-00.html#section-4.9>
     //
     // When the recovery episode ends with recovering
-    // a few packets (less than RESTORE_COUNT_THRESHOLD), it's considered
-    // as spurious and restore to the previous state.
+    // a few packets (less than cwnd / mss * ROLLBACK_THRESHOLD_PERCENT(%)), it's
+    // considered as spurious and restore to the previous state.
     if r.congestion_recovery_start_time.is_some() {
         let new_lost = r.lost_count - r.cubic_state.prior.lost_count;
+        let rollback_threshold = (r.congestion_window / r.max_datagram_size) *
+            ROLLBACK_THRESHOLD_PERCENT /
+            100;
+        let rollback_threshold = rollback_threshold.max(MIN_ROLLBACK_THRESHOLD);
 
-        if r.congestion_window < r.cubic_state.prior.congestion_window &&
-            new_lost < RESTORE_COUNT_THRESHOLD
-        {
-            rollback(r);
-            return;
+        if new_lost < rollback_threshold {
+            let did_rollback = rollback(r);
+
+            if did_rollback {
+                return;
+            }
         }
     }
 
@@ -224,31 +254,29 @@
         r.bytes_acked_sl += packet.size;
 
         if r.bytes_acked_sl >= r.max_datagram_size {
-            r.congestion_window += r.max_datagram_size;
+            if r.hystart.in_css(epoch) {
+                r.congestion_window +=
+                    r.hystart.css_cwnd_inc(r.max_datagram_size);
+            } else {
+                r.congestion_window += r.max_datagram_size;
+            }
+
             r.bytes_acked_sl -= r.max_datagram_size;
         }
 
-        if r.hystart.enabled() &&
-            epoch == packet::EPOCH_APPLICATION &&
-            r.hystart.try_enter_lss(
-                packet,
-                r.latest_rtt,
-                r.congestion_window,
-                now,
-                r.max_datagram_size,
-            )
-        {
+        if r.hystart.on_packet_acked(epoch, packet, r.latest_rtt, now) {
+            // Exit to congestion avoidance if CSS ends.
             r.ssthresh = r.congestion_window;
         }
     } else {
         // Congestion avoidance.
         let ca_start_time;
 
-        // In LSS, use lss_start_time instead of congestion_recovery_start_time.
-        if r.hystart.in_lss(epoch) {
-            ca_start_time = r.hystart.lss_start_time().unwrap();
+        // In CSS, use css_start_time instead of congestion_recovery_start_time.
+        if r.hystart.in_css(epoch) {
+            ca_start_time = r.hystart.css_start_time().unwrap();
 
-            // Reset w_max and k when LSS started.
+            // Reset w_max and k when CSS started.
             if r.cubic_state.w_max == 0.0 {
                 r.cubic_state.w_max = r.congestion_window as f64;
                 r.cubic_state.k = 0.0;
@@ -274,7 +302,7 @@
             }
         }
 
-        let t = now - ca_start_time;
+        let t = now.saturating_duration_since(ca_start_time);
 
         // target = w_cubic(t + rtt)
         let target = r.cubic_state.w_cubic(t + r.min_rtt, r.max_datagram_size);
@@ -308,18 +336,6 @@
             cubic_cwnd += cubic_inc;
         }
 
-        // When in Limited Slow Start, take the max of CA cwnd and
-        // LSS cwnd.
-        if r.hystart.in_lss(epoch) {
-            let lss_cwnd_inc = r.hystart.lss_cwnd_inc(
-                packet.size,
-                r.congestion_window,
-                r.ssthresh,
-            );
-
-            cubic_cwnd = cmp::max(cubic_cwnd, r.congestion_window + lss_cwnd_inc);
-        }
-
         // Update the increment and increase cwnd by MSS.
         r.cubic_state.cwnd_inc += cubic_cwnd - r.congestion_window;
 
@@ -331,7 +347,8 @@
 }
 
 fn congestion_event(
-    r: &mut Recovery, time_sent: Instant, epoch: packet::Epoch, now: Instant,
+    r: &mut Recovery, _lost_bytes: usize, time_sent: Instant,
+    epoch: packet::Epoch, now: Instant,
 ) {
     let in_congestion_recovery = r.in_congestion_recovery(time_sent);
 
@@ -368,9 +385,11 @@
         r.cubic_state.w_est = r.congestion_window as f64;
         r.cubic_state.alpha_aimd = ALPHA_AIMD;
 
-        if r.hystart.in_lss(epoch) {
+        if r.hystart.in_css(epoch) {
             r.hystart.congestion_event();
         }
+
+        r.prr.congestion_event(r.bytes_in_flight);
     }
 }
 
@@ -383,23 +402,44 @@
     r.cubic_state.prior.lost_count = r.lost_count;
 }
 
-fn rollback(r: &mut Recovery) {
+fn rollback(r: &mut Recovery) -> bool {
+    // Don't go back to slow start.
+    if r.cubic_state.prior.congestion_window < r.cubic_state.prior.ssthresh {
+        return false;
+    }
+
+    if r.congestion_window >= r.cubic_state.prior.congestion_window {
+        return false;
+    }
+
     r.congestion_window = r.cubic_state.prior.congestion_window;
     r.ssthresh = r.cubic_state.prior.ssthresh;
     r.cubic_state.w_max = r.cubic_state.prior.w_max;
     r.cubic_state.k = r.cubic_state.prior.k;
     r.congestion_recovery_start_time = r.cubic_state.prior.epoch_start;
+
+    true
 }
 
 fn has_custom_pacing() -> bool {
     false
 }
 
+fn debug_fmt(r: &Recovery, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+    write!(
+        f,
+        "cubic={{ k={} w_max={} }} ",
+        r.cubic_state.k, r.cubic_state.w_max
+    )
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
     use crate::recovery::hystart;
 
+    use smallvec::smallvec;
+
     #[test]
     fn cubic_init() {
         let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
@@ -433,7 +473,7 @@
 
         let p = recovery::Sent {
             pkt_num: 0,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -442,7 +482,7 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
@@ -458,9 +498,14 @@
             pkt_num: p.pkt_num,
             time_sent: p.time_sent,
             size: p.size,
+            delivered: 0,
+            delivered_time: now,
+            first_sent_time: now,
+            is_app_limited: false,
+            rtt: Duration::ZERO,
         }];
 
-        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+        r.on_packets_acked(acked, packet::Epoch::Application, now);
 
         // Check if cwnd increased by packet size (slow start)
         assert_eq!(r.cwnd(), cwnd_prev + p.size);
@@ -476,7 +521,7 @@
 
         let p = recovery::Sent {
             pkt_num: 0,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -485,7 +530,7 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
@@ -502,20 +547,35 @@
                 pkt_num: p.pkt_num,
                 time_sent: p.time_sent,
                 size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             },
             Acked {
                 pkt_num: p.pkt_num,
                 time_sent: p.time_sent,
                 size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             },
             Acked {
                 pkt_num: p.pkt_num,
                 time_sent: p.time_sent,
                 size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             },
         ];
 
-        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+        r.on_packets_acked(acked, packet::Epoch::Application, now);
 
         // Acked 3 packets.
         assert_eq!(r.cwnd(), cwnd_prev + p.size * 3);
@@ -530,7 +590,12 @@
         let now = Instant::now();
         let prev_cwnd = r.cwnd();
 
-        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+        r.congestion_event(
+            r.max_datagram_size,
+            now,
+            packet::Epoch::Application,
+            now,
+        );
 
         // In CUBIC, after congestion event, cwnd will be reduced by (1 -
         // CUBIC_BETA)
@@ -552,7 +617,12 @@
         }
 
         // Trigger congestion event to update ssthresh
-        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+        r.congestion_event(
+            r.max_datagram_size,
+            now,
+            packet::Epoch::Application,
+            now,
+        );
 
         // After congestion event, cwnd will be reduced.
         let cur_cwnd = (prev_cwnd as f64 * BETA_CUBIC) as usize;
@@ -567,7 +637,7 @@
         now += rtt;
 
         // To avoid rollback
-        r.lost_count += RESTORE_COUNT_THRESHOLD;
+        r.lost_count += MIN_ROLLBACK_THRESHOLD;
 
         // During Congestion Avoidance, it will take
         // 5 ACKs to increase cwnd by 1 MSS.
@@ -576,9 +646,14 @@
                 pkt_num: 0,
                 time_sent: now,
                 size: r.max_datagram_size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             }];
 
-            r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+            r.on_packets_acked(acked, packet::Epoch::Application, now);
             now += rtt;
         }
 
@@ -597,7 +672,12 @@
         r.on_packet_sent_cc(30000, now);
 
         // Trigger congestion event to update ssthresh
-        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+        r.congestion_event(
+            r.max_datagram_size,
+            now,
+            packet::Epoch::Application,
+            now,
+        );
 
         // After persistent congestion, cwnd should be the minimum window
         r.collapse_cwnd();
@@ -611,9 +691,14 @@
             // To exit from recovery
             time_sent: now + Duration::from_millis(1),
             size: r.max_datagram_size,
+            delivered: 0,
+            delivered_time: now,
+            first_sent_time: now,
+            is_app_limited: false,
+            rtt: Duration::ZERO,
         }];
 
-        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+        r.on_packets_acked(acked, packet::Epoch::Application, now);
 
         // Slow start again - cwnd will be increased by 1 MSS
         assert_eq!(
@@ -623,19 +708,18 @@
     }
 
     #[test]
-    fn cubic_hystart_limited_slow_start() {
+    fn cubic_hystart_css_to_ss() {
         let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
         cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC);
         cfg.enable_hystart(true);
 
         let mut r = Recovery::new(&cfg);
         let now = Instant::now();
-        let pkt_num = 0;
-        let epoch = packet::EPOCH_APPLICATION;
+        let epoch = packet::Epoch::Application;
 
         let p = recovery::Sent {
             pkt_num: 0,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -644,99 +728,272 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         // 1st round.
         let n_rtt_sample = hystart::N_RTT_SAMPLE;
-        let pkts_1st_round = n_rtt_sample as u64;
-        r.hystart.start_round(pkt_num);
+        let mut send_pn = 0;
+        let mut ack_pn = 0;
 
-        let rtt_1st = 50;
+        let rtt_1st = Duration::from_millis(50);
 
         // Send 1st round packets.
         for _ in 0..n_rtt_sample {
             r.on_packet_sent_cc(p.size, now);
+            send_pn += 1;
         }
 
-        // Receving Acks.
-        let now = now + Duration::from_millis(rtt_1st);
+        r.hystart.start_round(send_pn - 1);
+
+        // Receiving Acks.
+        let now = now + rtt_1st;
         for _ in 0..n_rtt_sample {
-            r.update_rtt(
-                Duration::from_millis(rtt_1st),
-                Duration::from_millis(0),
-                now,
-            );
+            r.update_rtt(rtt_1st, Duration::from_millis(0), now);
 
             let acked = vec![Acked {
-                pkt_num: p.pkt_num,
+                pkt_num: ack_pn,
                 time_sent: p.time_sent,
                 size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             }];
 
             r.on_packets_acked(acked, epoch, now);
+            ack_pn += 1;
         }
 
-        // Not in LSS yet.
-        assert_eq!(r.hystart.lss_start_time().is_some(), false);
+        // Not in CSS yet.
+        assert_eq!(r.hystart.css_start_time().is_some(), false);
 
         // 2nd round.
-        r.hystart.start_round(pkts_1st_round * 2);
-
-        let mut rtt_2nd = 100;
-        let now = now + Duration::from_millis(rtt_2nd);
+        let mut rtt_2nd = Duration::from_millis(100);
+        let now = now + rtt_2nd;
 
         // Send 2nd round packets.
         for _ in 0..n_rtt_sample {
             r.on_packet_sent_cc(p.size, now);
+            send_pn += 1;
         }
+        r.hystart.start_round(send_pn - 1);
 
-        // Receving Acks.
-        // Last ack will cause to exit to LSS.
+        // Receiving Acks.
+        // Last ack will cause to exit to CSS.
         let mut cwnd_prev = r.cwnd();
 
         for _ in 0..n_rtt_sample {
             cwnd_prev = r.cwnd();
-            r.update_rtt(
-                Duration::from_millis(rtt_2nd),
-                Duration::from_millis(0),
-                now,
-            );
+            r.update_rtt(rtt_2nd, Duration::from_millis(0), now);
 
             let acked = vec![Acked {
-                pkt_num: p.pkt_num,
+                pkt_num: ack_pn,
                 time_sent: p.time_sent,
                 size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             }];
 
             r.on_packets_acked(acked, epoch, now);
+            ack_pn += 1;
 
-            // Keep increasing RTT so that hystart exits to LSS.
-            rtt_2nd += 4;
+            // Keep increasing RTT so that hystart exits to CSS.
+            rtt_2nd += rtt_2nd.saturating_add(Duration::from_millis(4));
         }
 
-        // Now we are in LSS.
-        assert_eq!(r.hystart.lss_start_time().is_some(), true);
+        // Now we are in CSS.
+        assert_eq!(r.hystart.css_start_time().is_some(), true);
         assert_eq!(r.cwnd(), cwnd_prev + r.max_datagram_size);
 
-        // Send a full cwnd.
-        r.on_packet_sent_cc(r.cwnd(), now);
-
-        // Ack'ing 4 packets to increase cwnd by 1 MSS during LSS
+        // 3rd round, which RTT is less than previous round to
+        // trigger back to Slow Start.
+        let rtt_3rd = Duration::from_millis(80);
+        let now = now + rtt_3rd;
         cwnd_prev = r.cwnd();
-        for _ in 0..4 {
+
+        // Send 3nd round packets.
+        for _ in 0..n_rtt_sample {
+            r.on_packet_sent_cc(p.size, now);
+            send_pn += 1;
+        }
+        r.hystart.start_round(send_pn - 1);
+
+        // Receiving Acks.
+        // Last ack will cause to exit to SS.
+        for _ in 0..n_rtt_sample {
+            r.update_rtt(rtt_3rd, Duration::from_millis(0), now);
+
             let acked = vec![Acked {
-                pkt_num: p.pkt_num,
+                pkt_num: ack_pn,
                 time_sent: p.time_sent,
                 size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             }];
+
             r.on_packets_acked(acked, epoch, now);
+            ack_pn += 1;
         }
 
-        // During LSS cwnd will be increased less than usual slow start.
+        // Now we are back in Slow Start.
+        assert_eq!(r.hystart.css_start_time().is_some(), false);
+        assert_eq!(
+            r.cwnd(),
+            cwnd_prev +
+                r.max_datagram_size / hystart::CSS_GROWTH_DIVISOR *
+                    hystart::N_RTT_SAMPLE
+        );
+    }
+
+    #[test]
+    fn cubic_hystart_css_to_ca() {
+        let mut cfg = crate::Config::new(crate::PROTOCOL_VERSION).unwrap();
+        cfg.set_cc_algorithm(recovery::CongestionControlAlgorithm::CUBIC);
+        cfg.enable_hystart(true);
+
+        let mut r = Recovery::new(&cfg);
+        let now = Instant::now();
+        let epoch = packet::Epoch::Application;
+
+        let p = recovery::Sent {
+            pkt_num: 0,
+            frames: smallvec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: r.max_datagram_size,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            first_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        // 1st round.
+        let n_rtt_sample = hystart::N_RTT_SAMPLE;
+        let mut send_pn = 0;
+        let mut ack_pn = 0;
+
+        let rtt_1st = Duration::from_millis(50);
+
+        // Send 1st round packets.
+        for _ in 0..n_rtt_sample {
+            r.on_packet_sent_cc(p.size, now);
+            send_pn += 1;
+        }
+
+        r.hystart.start_round(send_pn - 1);
+
+        // Receiving Acks.
+        let now = now + rtt_1st;
+        for _ in 0..n_rtt_sample {
+            r.update_rtt(rtt_1st, Duration::from_millis(0), now);
+
+            let acked = vec![Acked {
+                pkt_num: ack_pn,
+                time_sent: p.time_sent,
+                size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
+            }];
+
+            r.on_packets_acked(acked, epoch, now);
+            ack_pn += 1;
+        }
+
+        // Not in CSS yet.
+        assert_eq!(r.hystart.css_start_time().is_some(), false);
+
+        // 2nd round.
+        let mut rtt_2nd = Duration::from_millis(100);
+        let now = now + rtt_2nd;
+
+        // Send 2nd round packets.
+        for _ in 0..n_rtt_sample {
+            r.on_packet_sent_cc(p.size, now);
+            send_pn += 1;
+        }
+        r.hystart.start_round(send_pn - 1);
+
+        // Receiving Acks.
+        // Last ack will cause to exit to CSS.
+        let mut cwnd_prev = r.cwnd();
+
+        for _ in 0..n_rtt_sample {
+            cwnd_prev = r.cwnd();
+            r.update_rtt(rtt_2nd, Duration::from_millis(0), now);
+
+            let acked = vec![Acked {
+                pkt_num: ack_pn,
+                time_sent: p.time_sent,
+                size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
+            }];
+
+            r.on_packets_acked(acked, epoch, now);
+            ack_pn += 1;
+
+            // Keep increasing RTT so that hystart exits to CSS.
+            rtt_2nd += rtt_2nd.saturating_add(Duration::from_millis(4));
+        }
+
+        // Now we are in CSS.
+        assert_eq!(r.hystart.css_start_time().is_some(), true);
         assert_eq!(r.cwnd(), cwnd_prev + r.max_datagram_size);
+
+        // Run 5 (CSS_ROUNDS) in CSS, to exit to congestion avoidance.
+        let rtt_css = Duration::from_millis(100);
+        let now = now + rtt_css;
+
+        for _ in 0..hystart::CSS_ROUNDS {
+            // Send a round of packets.
+            for _ in 0..n_rtt_sample {
+                r.on_packet_sent_cc(p.size, now);
+                send_pn += 1;
+            }
+            r.hystart.start_round(send_pn - 1);
+
+            // Receiving Acks.
+            for _ in 0..n_rtt_sample {
+                r.update_rtt(rtt_css, Duration::from_millis(0), now);
+
+                let acked = vec![Acked {
+                    pkt_num: ack_pn,
+                    time_sent: p.time_sent,
+                    size: p.size,
+                    delivered: 0,
+                    delivered_time: now,
+                    first_sent_time: now,
+                    is_app_limited: false,
+                    rtt: Duration::ZERO,
+                }];
+
+                r.on_packets_acked(acked, epoch, now);
+                ack_pn += 1;
+            }
+        }
+
+        // Now we are in congestion avoidance.
+        assert_eq!(r.cwnd(), r.ssthresh);
     }
 
     #[test]
@@ -754,7 +1011,12 @@
         }
 
         // Trigger congestion event to update ssthresh
-        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+        r.congestion_event(
+            r.max_datagram_size,
+            now,
+            packet::Epoch::Application,
+            now,
+        );
 
         // After congestion event, cwnd will be reduced.
         let cur_cwnd = (prev_cwnd as f64 * BETA_CUBIC) as usize;
@@ -767,19 +1029,66 @@
             // To exit from recovery
             time_sent: now + rtt,
             size: r.max_datagram_size,
+            delivered: 0,
+            delivered_time: now,
+            first_sent_time: now,
+            is_app_limited: false,
+            rtt: Duration::ZERO,
         }];
 
         // Ack more than cwnd bytes with rtt=100ms
         r.update_rtt(rtt, Duration::from_millis(0), now);
 
-        // Trigger detecting sprurious congestion event
+        // Trigger detecting spurious congestion event
         r.on_packets_acked(
             acked,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             now + rtt + Duration::from_millis(5),
         );
 
-        // cwnd is restored to the previous one.
+        // This is from slow start, no rollback.
+        assert_eq!(r.cwnd(), cur_cwnd);
+
+        let now = now + rtt;
+
+        // Trigger another congestion event.
+        let prev_cwnd = r.cwnd();
+        r.congestion_event(
+            r.max_datagram_size,
+            now,
+            packet::Epoch::Application,
+            now,
+        );
+
+        // After congestion event, cwnd will be reduced.
+        let cur_cwnd = (cur_cwnd as f64 * BETA_CUBIC) as usize;
+        assert_eq!(r.cwnd(), cur_cwnd);
+
+        let rtt = Duration::from_millis(100);
+
+        let acked = vec![Acked {
+            pkt_num: 0,
+            // To exit from recovery
+            time_sent: now + rtt,
+            size: r.max_datagram_size,
+            delivered: 0,
+            delivered_time: now,
+            first_sent_time: now,
+            is_app_limited: false,
+            rtt: Duration::ZERO,
+        }];
+
+        // Ack more than cwnd bytes with rtt=100ms.
+        r.update_rtt(rtt, Duration::from_millis(0), now);
+
+        // Trigger detecting spurious congestion event.
+        r.on_packets_acked(
+            acked,
+            packet::Epoch::Application,
+            now + rtt + Duration::from_millis(5),
+        );
+
+        // cwnd is rolled back to the previous one.
         assert_eq!(r.cwnd(), prev_cwnd);
     }
 
@@ -798,7 +1107,12 @@
         }
 
         // Trigger congestion event to update ssthresh
-        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+        r.congestion_event(
+            r.max_datagram_size,
+            now,
+            packet::Epoch::Application,
+            now,
+        );
 
         // After 1st congestion event, cwnd will be reduced.
         let cur_cwnd = (prev_cwnd as f64 * BETA_CUBIC) as usize;
@@ -812,7 +1126,7 @@
         now += rtt;
 
         // To avoid rollback
-        r.lost_count += RESTORE_COUNT_THRESHOLD;
+        r.lost_count += MIN_ROLLBACK_THRESHOLD;
 
         // During Congestion Avoidance, it will take
         // 5 ACKs to increase cwnd by 1 MSS.
@@ -821,9 +1135,14 @@
                 pkt_num: 0,
                 time_sent: now,
                 size: r.max_datagram_size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             }];
 
-            r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+            r.on_packets_acked(acked, packet::Epoch::Application, now);
             now += rtt;
         }
 
@@ -834,7 +1153,12 @@
         // Fast convergence: now there is 2nd congestion event and
         // cwnd is not fully recovered to w_max, w_max will be
         // further reduced.
-        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+        r.congestion_event(
+            r.max_datagram_size,
+            now,
+            packet::Epoch::Application,
+            now,
+        );
 
         // After 2nd congestion event, cwnd will be reduced.
         let cur_cwnd = (prev_cwnd as f64 * BETA_CUBIC) as usize;
diff --git a/src/recovery/delivery_rate.rs b/src/recovery/delivery_rate.rs
index 97edeba..23c2def 100644
--- a/src/recovery/delivery_rate.rs
+++ b/src/recovery/delivery_rate.rs
@@ -1,4 +1,4 @@
-// Copyright (C) 2020, Cloudflare, Inc.
+// Copyright (C) 2020-2022, Cloudflare, Inc.
 // All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
@@ -27,127 +27,162 @@
 //! Delivery rate estimation.
 //!
 //! This implements the algorithm for estimating delivery rate as described in
-//! <https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-00>
-
-use std::cmp;
+//! <https://tools.ietf.org/html/draft-cheng-iccrg-delivery-rate-estimation-01>
 
 use std::time::Duration;
 use std::time::Instant;
 
+use crate::recovery::Acked;
 use crate::recovery::Sent;
 
-#[derive(Default)]
+#[derive(Debug)]
 pub struct Rate {
     delivered: usize,
 
-    delivered_time: Option<Instant>,
+    delivered_time: Instant,
 
-    recent_delivered_packet_sent_time: Option<Instant>,
+    first_sent_time: Instant,
 
-    app_limited_at_pkt: usize,
+    // Packet number of the last sent packet with app limited.
+    end_of_app_limited: u64,
 
+    // Packet number of the last sent packet.
+    last_sent_packet: u64,
+
+    // Packet number of the largest acked packet.
+    largest_acked: u64,
+
+    // Sample of rate estimation.
     rate_sample: RateSample,
 }
 
+impl Default for Rate {
+    fn default() -> Self {
+        let now = Instant::now();
+
+        Rate {
+            delivered: 0,
+
+            delivered_time: now,
+
+            first_sent_time: now,
+
+            end_of_app_limited: 0,
+
+            last_sent_packet: 0,
+
+            largest_acked: 0,
+
+            rate_sample: RateSample::default(),
+        }
+    }
+}
+
 impl Rate {
-    pub fn on_packet_sent(&mut self, pkt: &mut Sent, now: Instant) {
-        if self.delivered_time.is_none() {
-            self.delivered_time = Some(now);
+    pub fn on_packet_sent(&mut self, pkt: &mut Sent, bytes_in_flight: usize) {
+        // No packets in flight.
+        if bytes_in_flight == 0 {
+            self.first_sent_time = pkt.time_sent;
+            self.delivered_time = pkt.time_sent;
         }
 
-        if self.recent_delivered_packet_sent_time.is_none() {
-            self.recent_delivered_packet_sent_time = Some(now);
-        }
-
+        pkt.first_sent_time = self.first_sent_time;
+        pkt.delivered_time = self.delivered_time;
         pkt.delivered = self.delivered;
-        pkt.delivered_time = self.delivered_time.unwrap();
+        pkt.is_app_limited = self.app_limited();
 
-        pkt.recent_delivered_packet_sent_time =
-            self.recent_delivered_packet_sent_time.unwrap();
-
-        pkt.is_app_limited = self.app_limited_at_pkt > 0;
+        self.last_sent_packet = pkt.pkt_num;
     }
 
-    pub fn on_packet_acked(&mut self, pkt: &Sent, now: Instant) {
-        self.rate_sample.prior_time = Some(pkt.delivered_time);
-
+    // Update the delivery rate sample when a packet is acked.
+    pub fn update_rate_sample(&mut self, pkt: &Acked, now: Instant) {
         self.delivered += pkt.size;
-        self.delivered_time = Some(now);
+        self.delivered_time = now;
 
-        if pkt.delivered > self.rate_sample.prior_delivered {
+        // Update info using the newest packet. If rate_sample is not yet
+        // initialized, initialize with the first packet.
+        if self.rate_sample.prior_time.is_none() ||
+            pkt.delivered > self.rate_sample.prior_delivered
+        {
             self.rate_sample.prior_delivered = pkt.delivered;
-
+            self.rate_sample.prior_time = Some(pkt.delivered_time);
+            self.rate_sample.is_app_limited = pkt.is_app_limited;
             self.rate_sample.send_elapsed =
-                pkt.time_sent - pkt.recent_delivered_packet_sent_time;
-
+                pkt.time_sent.saturating_duration_since(pkt.first_sent_time);
+            self.rate_sample.rtt = pkt.rtt;
             self.rate_sample.ack_elapsed = self
                 .delivered_time
-                .unwrap()
-                .duration_since(pkt.delivered_time);
+                .saturating_duration_since(pkt.delivered_time);
 
-            self.recent_delivered_packet_sent_time = Some(pkt.time_sent);
+            self.first_sent_time = pkt.time_sent;
+        }
+
+        self.largest_acked = self.largest_acked.max(pkt.pkt_num);
+    }
+
+    pub fn generate_rate_sample(&mut self, min_rtt: Duration) {
+        // End app-limited phase if bubble is ACKed and gone.
+        if self.app_limited() && self.largest_acked > self.end_of_app_limited {
+            self.update_app_limited(false);
+        }
+
+        if self.rate_sample.prior_time.is_some() {
+            let interval = self
+                .rate_sample
+                .send_elapsed
+                .max(self.rate_sample.ack_elapsed);
+
+            self.rate_sample.delivered =
+                self.delivered - self.rate_sample.prior_delivered;
+            self.rate_sample.interval = interval;
+
+            if interval < min_rtt {
+                self.rate_sample.interval = Duration::ZERO;
+
+                // No reliable sample.
+                return;
+            }
+
+            if !interval.is_zero() {
+                // Fill in rate_sample with a rate sample.
+                self.rate_sample.delivery_rate =
+                    (self.rate_sample.delivered as f64 / interval.as_secs_f64())
+                        as u64;
+            }
         }
     }
 
-    pub fn estimate(&mut self) {
-        if (self.app_limited_at_pkt > 0) &&
-            (self.delivered > self.app_limited_at_pkt)
-        {
-            self.app_limited_at_pkt = 0;
-        }
-
-        match self.rate_sample.prior_time {
-            Some(_) => {
-                self.rate_sample.delivered =
-                    self.delivered - self.rate_sample.prior_delivered;
-
-                self.rate_sample.interval = cmp::max(
-                    self.rate_sample.send_elapsed,
-                    self.rate_sample.ack_elapsed,
-                );
-            },
-            None => return,
-        }
-
-        if self.rate_sample.interval.as_secs_f64() > 0.0 {
-            self.rate_sample.delivery_rate = (self.rate_sample.delivered as f64 /
-                self.rate_sample.interval.as_secs_f64())
-                as u64;
-        }
+    pub fn update_app_limited(&mut self, v: bool) {
+        self.end_of_app_limited = if v { self.last_sent_packet.max(1) } else { 0 }
     }
 
-    pub fn check_app_limited(&mut self, bytes_in_flight: usize) {
-        let limited = self.delivered + bytes_in_flight;
-        self.app_limited_at_pkt = if limited > 0 { limited } else { 1 };
+    pub fn app_limited(&mut self) -> bool {
+        self.end_of_app_limited != 0
     }
 
-    pub fn delivery_rate(&self) -> u64 {
+    pub fn delivered(&self) -> usize {
+        self.delivered
+    }
+
+    pub fn sample_delivery_rate(&self) -> u64 {
         self.rate_sample.delivery_rate
     }
-}
 
-impl std::fmt::Debug for Rate {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        write!(f, "delivered={:?} ", self.delivered)?;
+    pub fn sample_rtt(&self) -> Duration {
+        self.rate_sample.rtt
+    }
 
-        if let Some(t) = self.delivered_time {
-            write!(f, "delivered_time={:?} ", t.elapsed())?;
-        }
-
-        if let Some(t) = self.recent_delivered_packet_sent_time {
-            write!(f, "recent_delivered_packet_sent_time={:?} ", t.elapsed())?;
-        }
-
-        write!(f, "app_limited_at_pkt={:?} ", self.app_limited_at_pkt)?;
-
-        Ok(())
+    pub fn sample_is_app_limited(&self) -> bool {
+        self.rate_sample.is_app_limited
     }
 }
 
-#[derive(Default)]
+#[derive(Default, Debug)]
 struct RateSample {
     delivery_rate: u64,
 
+    is_app_limited: bool,
+
     interval: Duration,
 
     delivered: usize,
@@ -159,22 +194,8 @@
     send_elapsed: Duration,
 
     ack_elapsed: Duration,
-}
 
-impl std::fmt::Debug for RateSample {
-    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
-        write!(f, "delivery_rate={:?} ", self.delivery_rate)?;
-        write!(f, "interval={:?} ", self.interval)?;
-        write!(f, "delivered={:?} ", self.delivered)?;
-        write!(f, "prior_delivered={:?} ", self.prior_delivered)?;
-        write!(f, "send_elapsed={:?} ", self.send_elapsed)?;
-        if let Some(t) = self.prior_time {
-            write!(f, "prior_time={:?} ", t.elapsed())?;
-        }
-        write!(f, "ack_elapsed={:?}", self.ack_elapsed)?;
-
-        Ok(())
-    }
+    rtt: Duration,
 }
 
 #[cfg(test)]
@@ -183,117 +204,167 @@
 
     use crate::recovery::*;
 
+    use smallvec::smallvec;
+
     #[test]
     fn rate_check() {
         let config = Config::new(0xbabababa).unwrap();
-        let mut recovery = Recovery::new(&config);
+        let mut r = Recovery::new(&config);
 
-        let mut pkt_1 = Sent {
-            pkt_num: 0,
-            frames: vec![],
-            time_sent: Instant::now(),
-            time_acked: None,
-            time_lost: None,
-            size: 1200,
-            ack_eliciting: true,
-            in_flight: true,
-            delivered: 0,
-            delivered_time: Instant::now(),
-            recent_delivered_packet_sent_time: Instant::now(),
-            is_app_limited: false,
-            has_data: false,
-        };
+        let now = Instant::now();
+        let mss = r.max_datagram_size();
 
-        recovery
-            .delivery_rate
-            .on_packet_sent(&mut pkt_1, Instant::now());
-        std::thread::sleep(Duration::from_millis(50));
-        recovery
-            .delivery_rate
-            .on_packet_acked(&pkt_1, Instant::now());
+        // Send 2 packets.
+        for pn in 0..2 {
+            let pkt = Sent {
+                pkt_num: pn,
+                frames: smallvec![],
+                time_sent: now,
+                time_acked: None,
+                time_lost: None,
+                size: mss,
+                ack_eliciting: true,
+                in_flight: true,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                has_data: false,
+            };
 
-        let mut pkt_2 = Sent {
-            pkt_num: 1,
-            frames: vec![],
-            time_sent: Instant::now(),
-            time_acked: None,
-            time_lost: None,
-            size: 1200,
-            ack_eliciting: true,
-            in_flight: true,
-            delivered: 0,
-            delivered_time: Instant::now(),
-            recent_delivered_packet_sent_time: Instant::now(),
-            is_app_limited: false,
-            has_data: false,
-        };
+            r.on_packet_sent(
+                pkt,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            );
+        }
 
-        recovery
-            .delivery_rate
-            .on_packet_sent(&mut pkt_2, Instant::now());
-        std::thread::sleep(Duration::from_millis(50));
-        recovery
-            .delivery_rate
-            .on_packet_acked(&pkt_2, Instant::now());
-        recovery.delivery_rate.estimate();
+        let rtt = Duration::from_millis(50);
+        let now = now + rtt;
 
-        assert!(recovery.delivery_rate() > 0);
+        // Ack 2 packets.
+        for pn in 0..2 {
+            let acked = Acked {
+                pkt_num: pn,
+                time_sent: now,
+                size: mss,
+                rtt,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now.checked_sub(rtt).unwrap(),
+                is_app_limited: false,
+            };
+
+            r.delivery_rate.update_rate_sample(&acked, now);
+        }
+
+        // Update rate sample after 1 rtt.
+        r.delivery_rate.generate_rate_sample(rtt);
+
+        // Bytes acked so far.
+        assert_eq!(r.delivery_rate.delivered(), 2400);
+
+        // Estimated delivery rate = (1200 x 2) / 0.05s = 48000.
+        assert_eq!(r.delivery_rate(), 48000);
+    }
+
+    #[test]
+    fn app_limited_cwnd_full() {
+        let config = Config::new(0xbabababa).unwrap();
+        let mut r = Recovery::new(&config);
+
+        let now = Instant::now();
+        let mss = r.max_datagram_size();
+
+        // Send 10 packets to fill cwnd.
+        for pn in 0..10 {
+            let pkt = Sent {
+                pkt_num: pn,
+                frames: smallvec![],
+                time_sent: now,
+                time_acked: None,
+                time_lost: None,
+                size: mss,
+                ack_eliciting: true,
+                in_flight: true,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                has_data: false,
+            };
+
+            r.on_packet_sent(
+                pkt,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            );
+        }
+
+        assert_eq!(r.app_limited(), false);
+        assert_eq!(r.delivery_rate.sample_is_app_limited(), false);
     }
 
     #[test]
     fn app_limited_check() {
         let config = Config::new(0xbabababa).unwrap();
-        let mut recvry = Recovery::new(&config);
+        let mut r = Recovery::new(&config);
 
-        let mut pkt_1 = Sent {
-            pkt_num: 0,
-            frames: vec![],
-            time_sent: Instant::now(),
-            time_acked: None,
-            time_lost: None,
-            size: 1200,
-            ack_eliciting: true,
-            in_flight: true,
-            delivered: 0,
-            delivered_time: Instant::now(),
-            recent_delivered_packet_sent_time: Instant::now(),
-            is_app_limited: false,
-            has_data: false,
-        };
+        let now = Instant::now();
+        let mss = r.max_datagram_size();
 
-        recvry
-            .delivery_rate
-            .on_packet_sent(&mut pkt_1, Instant::now());
-        std::thread::sleep(Duration::from_millis(50));
-        recvry.delivery_rate.on_packet_acked(&pkt_1, Instant::now());
+        // Send 5 packets.
+        for pn in 0..5 {
+            let pkt = Sent {
+                pkt_num: pn,
+                frames: smallvec![],
+                time_sent: now,
+                time_acked: None,
+                time_lost: None,
+                size: mss,
+                ack_eliciting: true,
+                in_flight: true,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                has_data: false,
+            };
 
-        let mut pkt_2 = Sent {
-            pkt_num: 1,
-            frames: vec![],
-            time_sent: Instant::now(),
-            time_acked: None,
-            time_lost: None,
-            size: 1200,
-            ack_eliciting: true,
-            in_flight: true,
-            delivered: 0,
-            delivered_time: Instant::now(),
-            recent_delivered_packet_sent_time: Instant::now(),
-            is_app_limited: false,
-            has_data: false,
-        };
+            r.on_packet_sent(
+                pkt,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            );
+        }
 
-        recvry.app_limited = true;
-        recvry
-            .delivery_rate
-            .check_app_limited(recvry.bytes_in_flight);
-        recvry
-            .delivery_rate
-            .on_packet_sent(&mut pkt_2, Instant::now());
-        std::thread::sleep(Duration::from_millis(50));
-        recvry.delivery_rate.on_packet_acked(&pkt_2, Instant::now());
-        recvry.delivery_rate.estimate();
+        let rtt = Duration::from_millis(50);
+        let now = now + rtt;
 
-        assert_eq!(recvry.delivery_rate.app_limited_at_pkt, 0);
+        let mut acked = ranges::RangeSet::default();
+        acked.insert(0..5);
+
+        assert_eq!(
+            r.on_ack_received(
+                &acked,
+                25,
+                packet::Epoch::Application,
+                HandshakeStatus::default(),
+                now,
+                "",
+            ),
+            Ok((0, 0)),
+        );
+
+        assert_eq!(r.app_limited(), true);
+        // Rate sample is not app limited (all acked).
+        assert_eq!(r.delivery_rate.sample_is_app_limited(), false);
+        assert_eq!(r.delivery_rate.sample_rtt(), rtt);
     }
 }
diff --git a/src/recovery/hystart.rs b/src/recovery/hystart.rs
index 6a275bc..4d2ead5 100644
--- a/src/recovery/hystart.rs
+++ b/src/recovery/hystart.rs
@@ -28,7 +28,7 @@
 //!
 //! This implementation is based on the following I-D:
 //!
-//! <https://tools.ietf.org/html/draft-balasubramanian-tcpm-hystartplusplus-03>
+//! <https://datatracker.ietf.org/doc/html/draft-ietf-tcpm-hystartplusplus-04>
 
 use std::cmp;
 use std::time::Duration;
@@ -38,29 +38,33 @@
 use crate::recovery;
 
 /// Constants from I-D.
-const LOW_CWND: usize = 16;
-
 const MIN_RTT_THRESH: Duration = Duration::from_millis(4);
 
 const MAX_RTT_THRESH: Duration = Duration::from_millis(16);
 
-pub const LSS_DIVISOR: f64 = 0.25;
-
 pub const N_RTT_SAMPLE: usize = 8;
 
+pub const CSS_GROWTH_DIVISOR: usize = 4;
+
+pub const CSS_ROUNDS: usize = 5;
+
 #[derive(Default)]
 pub struct Hystart {
     enabled: bool,
 
     window_end: Option<u64>,
 
-    last_round_min_rtt: Option<Duration>,
+    last_round_min_rtt: Duration,
 
-    current_round_min_rtt: Option<Duration>,
+    current_round_min_rtt: Duration,
+
+    css_baseline_min_rtt: Duration,
 
     rtt_sample_count: usize,
 
-    lss_start_time: Option<Instant>,
+    css_start_time: Option<Instant>,
+
+    css_round_count: usize,
 }
 
 impl std::fmt::Debug for Hystart {
@@ -68,8 +72,10 @@
         write!(f, "window_end={:?} ", self.window_end)?;
         write!(f, "last_round_min_rtt={:?} ", self.last_round_min_rtt)?;
         write!(f, "current_round_min_rtt={:?} ", self.current_round_min_rtt)?;
+        write!(f, "css_baseline_min_rtt={:?} ", self.css_baseline_min_rtt)?;
         write!(f, "rtt_sample_count={:?} ", self.rtt_sample_count)?;
-        write!(f, "lss_start_time={:?} ", self.lss_start_time)?;
+        write!(f, "css_start_time={:?} ", self.css_start_time)?;
+        write!(f, "css_round_count={:?}", self.css_round_count)?;
 
         Ok(())
     }
@@ -80,101 +86,124 @@
         Self {
             enabled,
 
+            last_round_min_rtt: Duration::MAX,
+
+            current_round_min_rtt: Duration::MAX,
+
+            css_baseline_min_rtt: Duration::MAX,
+
             ..Default::default()
         }
     }
 
+    pub fn reset(&mut self) {
+        *self = Self::new(self.enabled);
+    }
+
     pub fn enabled(&self) -> bool {
         self.enabled
     }
 
-    pub fn lss_start_time(&self) -> Option<Instant> {
-        self.lss_start_time
+    pub fn css_start_time(&self) -> Option<Instant> {
+        self.css_start_time
     }
 
-    pub fn in_lss(&self, epoch: packet::Epoch) -> bool {
+    pub fn in_css(&self, epoch: packet::Epoch) -> bool {
         self.enabled &&
-            epoch == packet::EPOCH_APPLICATION &&
-            self.lss_start_time().is_some()
+            epoch == packet::Epoch::Application &&
+            self.css_start_time().is_some()
     }
 
     pub fn start_round(&mut self, pkt_num: u64) {
         if self.window_end.is_none() {
-            *self = Hystart {
-                enabled: self.enabled,
+            self.window_end = Some(pkt_num);
 
-                window_end: Some(pkt_num),
+            self.last_round_min_rtt = self.current_round_min_rtt;
 
-                last_round_min_rtt: self.current_round_min_rtt,
+            self.current_round_min_rtt = Duration::MAX;
 
-                current_round_min_rtt: None,
-
-                rtt_sample_count: 0,
-
-                lss_start_time: None,
-            };
+            self.rtt_sample_count = 0;
         }
     }
 
-    // Returns true if LSS started.
-    pub fn try_enter_lss(
-        &mut self, packet: &recovery::Acked, rtt: Duration, cwnd: usize,
-        now: Instant, max_datagram_size: usize,
+    // On receiving ACK. Returns true if need to enter Congestion Avoidance.
+    pub fn on_packet_acked(
+        &mut self, epoch: packet::Epoch, packet: &recovery::Acked, rtt: Duration,
+        now: Instant,
     ) -> bool {
-        if self.lss_start_time().is_none() {
-            if let Some(current_round_min_rtt) = self.current_round_min_rtt {
-                self.current_round_min_rtt =
-                    Some(cmp::min(current_round_min_rtt, rtt));
-            } else {
-                self.current_round_min_rtt = Some(rtt);
-            }
+        if !(self.enabled && epoch == packet::Epoch::Application) {
+            return false;
+        }
 
-            self.rtt_sample_count += 1;
+        self.current_round_min_rtt = cmp::min(self.current_round_min_rtt, rtt);
 
-            if cwnd >= (LOW_CWND * max_datagram_size) &&
-                self.rtt_sample_count >= N_RTT_SAMPLE &&
-                self.current_round_min_rtt.is_some() &&
-                self.last_round_min_rtt.is_some()
+        self.rtt_sample_count += 1;
+
+        // Slow Start.
+        if self.css_start_time().is_none() {
+            if self.rtt_sample_count >= N_RTT_SAMPLE &&
+                self.current_round_min_rtt != Duration::MAX &&
+                self.last_round_min_rtt != Duration::MAX
             {
                 // clamp(min_rtt_thresh, last_round_min_rtt/8,
                 // max_rtt_thresh)
-                let rtt_thresh = cmp::max(
-                    self.last_round_min_rtt.unwrap() / 8,
-                    MIN_RTT_THRESH,
-                );
+                let rtt_thresh =
+                    cmp::max(self.last_round_min_rtt / 8, MIN_RTT_THRESH);
                 let rtt_thresh = cmp::min(rtt_thresh, MAX_RTT_THRESH);
 
-                // Check if we can exit to LSS.
-                if self.current_round_min_rtt.unwrap() >=
-                    (self.last_round_min_rtt.unwrap() + rtt_thresh)
+                // Check if we can exit to CSS.
+                if self.current_round_min_rtt >=
+                    self.last_round_min_rtt.saturating_add(rtt_thresh)
                 {
-                    self.lss_start_time = Some(now);
+                    self.css_baseline_min_rtt = self.current_round_min_rtt;
+                    self.css_start_time = Some(now);
                 }
             }
+        } else {
+            // Conservative Slow Start.
+            if self.rtt_sample_count >= N_RTT_SAMPLE {
+                self.rtt_sample_count = 0;
 
-            // Check if we reached the end of the round.
-            if let Some(end_pkt_num) = self.window_end {
-                if packet.pkt_num >= end_pkt_num {
-                    // Start of a new round.
-                    self.window_end = None;
+                if self.current_round_min_rtt < self.css_baseline_min_rtt {
+                    self.css_baseline_min_rtt = Duration::MAX;
+
+                    // Back to Slow Start.
+                    self.css_start_time = None;
+                    self.css_round_count = 0;
                 }
             }
         }
 
-        self.lss_start_time.is_some()
+        // Check if we reached the end of the round.
+        if let Some(end_pkt_num) = self.window_end {
+            if packet.pkt_num >= end_pkt_num {
+                // Start of a new round.
+                self.window_end = None;
+
+                if self.css_start_time().is_some() {
+                    self.css_round_count += 1;
+
+                    // End of CSS - exit to congestion avoidance.
+                    if self.css_round_count >= CSS_ROUNDS {
+                        self.css_round_count = 0;
+                        return true;
+                    }
+                }
+            }
+        }
+
+        false
     }
 
-    // Return a cwnd increment during LSS (Limited Slow Start).
-    pub fn lss_cwnd_inc(
-        &self, pkt_size: usize, cwnd: usize, ssthresh: usize,
-    ) -> usize {
-        pkt_size / (cwnd as f64 / (LSS_DIVISOR * ssthresh as f64)) as usize
+    // Return a cwnd increment during CSS (Conservative Slow Start).
+    pub fn css_cwnd_inc(&self, pkt_size: usize) -> usize {
+        pkt_size / CSS_GROWTH_DIVISOR
     }
 
     // Exit HyStart++ when entering congestion avoidance.
     pub fn congestion_event(&mut self) {
         self.window_end = None;
-        self.lss_start_time = None;
+        self.css_start_time = None;
     }
 }
 
@@ -190,20 +219,17 @@
         hspp.start_round(pkt_num);
 
         assert_eq!(hspp.window_end, Some(pkt_num));
-        assert_eq!(hspp.current_round_min_rtt, None);
+        assert_eq!(hspp.current_round_min_rtt, Duration::MAX);
     }
 
     #[test]
-    fn lss_cwnd_inc() {
+    fn css_cwnd_inc() {
         let hspp = Hystart::default();
-
         let datagram_size = 1200;
-        let cwnd = 24000;
-        let ssthresh = 24000;
 
-        let lss_cwnd_inc = hspp.lss_cwnd_inc(datagram_size, cwnd, ssthresh);
+        let css_cwnd_inc = hspp.css_cwnd_inc(datagram_size);
 
-        assert_eq!((datagram_size as f64 * LSS_DIVISOR) as usize, lss_cwnd_inc);
+        assert_eq!(datagram_size / CSS_GROWTH_DIVISOR, css_cwnd_inc);
     }
 
     #[test]
diff --git a/src/recovery/mod.rs b/src/recovery/mod.rs
index 2bffa79..a053a1f 100644
--- a/src/recovery/mod.rs
+++ b/src/recovery/mod.rs
@@ -34,7 +34,6 @@
 use std::collections::VecDeque;
 
 use crate::Config;
-use crate::Error;
 use crate::Result;
 
 use crate::frame;
@@ -42,10 +41,17 @@
 use crate::packet;
 use crate::ranges;
 
-// Loss Recovery
-const PACKET_THRESHOLD: u64 = 3;
+#[cfg(feature = "qlog")]
+use qlog::events::EventData;
 
-const TIME_THRESHOLD: f64 = 9.0 / 8.0;
+use smallvec::SmallVec;
+
+// Loss Recovery
+const INITIAL_PACKET_THRESHOLD: u64 = 3;
+
+const MAX_PACKET_THRESHOLD: u64 = 20;
+
+const INITIAL_TIME_THRESHOLD: f64 = 9.0 / 8.0;
 
 const GRANULARITY: Duration = Duration::from_millis(1);
 
@@ -64,16 +70,23 @@
 
 const LOSS_REDUCTION_FACTOR: f64 = 0.5;
 
+const PACING_MULTIPLIER: f64 = 1.25;
+
+// How many non ACK eliciting packets we send before including a PING to solicit
+// an ACK.
+pub(super) const MAX_OUTSTANDING_NON_ACK_ELICITING: usize = 24;
+
 pub struct Recovery {
     loss_detection_timer: Option<Instant>,
 
     pto_count: u32,
 
-    time_of_last_sent_ack_eliciting_pkt: [Option<Instant>; packet::EPOCH_COUNT],
+    time_of_last_sent_ack_eliciting_pkt:
+        [Option<Instant>; packet::Epoch::count()],
 
-    largest_acked_pkt: [u64; packet::EPOCH_COUNT],
+    largest_acked_pkt: [u64; packet::Epoch::count()],
 
-    largest_sent_pkt: [u64; packet::EPOCH_COUNT],
+    largest_sent_pkt: [u64; packet::Epoch::count()],
 
     latest_rtt: Duration,
 
@@ -87,24 +100,30 @@
 
     pub max_ack_delay: Duration,
 
-    loss_time: [Option<Instant>; packet::EPOCH_COUNT],
+    loss_time: [Option<Instant>; packet::Epoch::count()],
 
-    sent: [VecDeque<Sent>; packet::EPOCH_COUNT],
+    sent: [VecDeque<Sent>; packet::Epoch::count()],
 
-    pub lost: [Vec<frame::Frame>; packet::EPOCH_COUNT],
+    pub lost: [Vec<frame::Frame>; packet::Epoch::count()],
 
-    pub acked: [Vec<frame::Frame>; packet::EPOCH_COUNT],
+    pub acked: [Vec<frame::Frame>; packet::Epoch::count()],
 
     pub lost_count: usize,
 
-    pub loss_probes: [usize; packet::EPOCH_COUNT],
+    pub lost_spurious_count: usize,
 
-    in_flight_count: [usize; packet::EPOCH_COUNT],
+    pub loss_probes: [usize; packet::Epoch::count()],
+
+    in_flight_count: [usize; packet::Epoch::count()],
 
     app_limited: bool,
 
     delivery_rate: delivery_rate::Rate,
 
+    pkt_thresh: u64,
+
+    time_thresh: f64,
+
     // Congestion control.
     cc_ops: &'static CongestionControlOps,
 
@@ -120,6 +139,8 @@
 
     bytes_sent: usize,
 
+    pub bytes_lost: u64,
+
     congestion_recovery_start_time: Option<Instant>,
 
     max_datagram_size: usize,
@@ -130,25 +151,62 @@
     hystart: hystart::Hystart,
 
     // Pacing.
-    pacing_rate: u64,
+    pub pacer: pacer::Pacer,
 
-    last_packet_scheduled_time: Option<Instant>,
+    // RFC6937 PRR.
+    prr: prr::PRR,
+
+    #[cfg(feature = "qlog")]
+    qlog_metrics: QlogMetrics,
+
+    // The maximum size of a data aggregate scheduled and
+    // transmitted together.
+    send_quantum: usize,
+
+    // BBR state.
+    bbr_state: bbr::State,
+
+    /// How many non-ack-eliciting packets have been sent.
+    outstanding_non_ack_eliciting: usize,
+}
+
+pub struct RecoveryConfig {
+    max_send_udp_payload_size: usize,
+    pub max_ack_delay: Duration,
+    cc_ops: &'static CongestionControlOps,
+    hystart: bool,
+    pacing: bool,
+}
+
+impl RecoveryConfig {
+    pub fn from_config(config: &Config) -> Self {
+        Self {
+            max_send_udp_payload_size: config.max_send_udp_payload_size,
+            max_ack_delay: Duration::ZERO,
+            cc_ops: config.cc_algorithm.into(),
+            hystart: config.hystart,
+            pacing: config.pacing,
+        }
+    }
 }
 
 impl Recovery {
-    pub fn new(config: &Config) -> Self {
+    pub fn new_with_config(recovery_config: &RecoveryConfig) -> Self {
+        let initial_congestion_window =
+            recovery_config.max_send_udp_payload_size * INITIAL_WINDOW_PACKETS;
+
         Recovery {
             loss_detection_timer: None,
 
             pto_count: 0,
 
-            time_of_last_sent_ack_eliciting_pkt: [None; packet::EPOCH_COUNT],
+            time_of_last_sent_ack_eliciting_pkt: [None; packet::Epoch::count()],
 
-            largest_acked_pkt: [std::u64::MAX; packet::EPOCH_COUNT],
+            largest_acked_pkt: [u64::MAX; packet::Epoch::count()],
 
-            largest_sent_pkt: [0; packet::EPOCH_COUNT],
+            largest_sent_pkt: [0; packet::Epoch::count()],
 
-            latest_rtt: Duration::new(0, 0),
+            latest_rtt: Duration::ZERO,
 
             // This field should be initialized to `INITIAL_RTT` for the initial
             // PTO calculation, but it also needs to be an `Option` to track
@@ -156,15 +214,15 @@
             // handled by the `rtt()` method instead.
             smoothed_rtt: None,
 
-            minmax_filter: minmax::Minmax::new(Duration::new(0, 0)),
+            minmax_filter: minmax::Minmax::new(Duration::ZERO),
 
-            min_rtt: Duration::new(0, 0),
+            min_rtt: Duration::ZERO,
 
             rttvar: INITIAL_RTT / 2,
 
-            max_ack_delay: Duration::new(0, 0),
+            max_ack_delay: recovery_config.max_ack_delay,
 
-            loss_time: [None; packet::EPOCH_COUNT],
+            loss_time: [None; packet::Epoch::count()],
 
             sent: [VecDeque::new(), VecDeque::new(), VecDeque::new()],
 
@@ -173,17 +231,21 @@
             acked: [Vec::new(), Vec::new(), Vec::new()],
 
             lost_count: 0,
+            lost_spurious_count: 0,
 
-            loss_probes: [0; packet::EPOCH_COUNT],
+            loss_probes: [0; packet::Epoch::count()],
 
-            in_flight_count: [0; packet::EPOCH_COUNT],
+            in_flight_count: [0; packet::Epoch::count()],
 
-            congestion_window: config.max_send_udp_payload_size *
-                INITIAL_WINDOW_PACKETS,
+            congestion_window: initial_congestion_window,
+
+            pkt_thresh: INITIAL_PACKET_THRESHOLD,
+
+            time_thresh: INITIAL_TIME_THRESHOLD,
 
             bytes_in_flight: 0,
 
-            ssthresh: std::usize::MAX,
+            ssthresh: usize::MAX,
 
             bytes_acked_sl: 0,
 
@@ -191,11 +253,13 @@
 
             bytes_sent: 0,
 
+            bytes_lost: 0,
+
             congestion_recovery_start_time: None,
 
-            max_datagram_size: config.max_send_udp_payload_size,
+            max_datagram_size: recovery_config.max_send_udp_payload_size,
 
-            cc_ops: config.cc_algorithm.into(),
+            cc_ops: recovery_config.cc_ops,
 
             delivery_rate: delivery_rate::Rate::default(),
 
@@ -203,14 +267,54 @@
 
             app_limited: false,
 
-            hystart: hystart::Hystart::new(config.hystart),
+            hystart: hystart::Hystart::new(recovery_config.hystart),
 
-            pacing_rate: 0,
+            pacer: pacer::Pacer::new(
+                recovery_config.pacing,
+                initial_congestion_window,
+                0,
+                recovery_config.max_send_udp_payload_size,
+            ),
 
-            last_packet_scheduled_time: None,
+            prr: prr::PRR::default(),
+
+            send_quantum: initial_congestion_window,
+
+            #[cfg(feature = "qlog")]
+            qlog_metrics: QlogMetrics::default(),
+
+            bbr_state: bbr::State::new(),
+
+            outstanding_non_ack_eliciting: 0,
         }
     }
 
+    pub fn new(config: &Config) -> Self {
+        Self::new_with_config(&RecoveryConfig::from_config(config))
+    }
+
+    pub fn on_init(&mut self) {
+        (self.cc_ops.on_init)(self);
+    }
+
+    pub fn reset(&mut self) {
+        self.congestion_window = self.max_datagram_size * INITIAL_WINDOW_PACKETS;
+        self.in_flight_count = [0; packet::Epoch::count()];
+        self.congestion_recovery_start_time = None;
+        self.ssthresh = usize::MAX;
+        (self.cc_ops.reset)(self);
+        self.hystart.reset();
+        self.prr = prr::PRR::default();
+    }
+
+    /// Returns whether or not we should elicit an ACK even if we wouldn't
+    /// otherwise have constructed an ACK eliciting packet.
+    pub fn should_elicit_ack(&self, epoch: packet::Epoch) -> bool {
+        self.loss_probes[epoch] > 0 ||
+            self.outstanding_non_ack_eliciting >=
+                MAX_OUTSTANDING_NON_ACK_ELICITING
+    }
+
     pub fn on_packet_sent(
         &mut self, mut pkt: Sent, epoch: packet::Epoch,
         handshake_status: HandshakeStatus, now: Instant, trace_id: &str,
@@ -220,13 +324,15 @@
         let sent_bytes = pkt.size;
         let pkt_num = pkt.pkt_num;
 
-        self.delivery_rate.on_packet_sent(&mut pkt, now);
+        if ack_eliciting {
+            self.outstanding_non_ack_eliciting = 0;
+        } else {
+            self.outstanding_non_ack_eliciting += 1;
+        }
 
         self.largest_sent_pkt[epoch] =
             cmp::max(self.largest_sent_pkt[epoch], pkt_num);
 
-        self.sent[epoch].push_back(pkt);
-
         if in_flight {
             if ack_eliciting {
                 self.time_of_last_sent_ack_eliciting_pkt[epoch] = Some(now);
@@ -234,17 +340,20 @@
 
             self.in_flight_count[epoch] += 1;
 
-            self.app_limited =
-                (self.bytes_in_flight + sent_bytes) < self.congestion_window;
+            self.update_app_limited(
+                (self.bytes_in_flight + sent_bytes) < self.congestion_window,
+            );
 
             self.on_packet_sent_cc(sent_bytes, now);
 
+            self.prr.on_packet_sent(sent_bytes);
+
             self.set_loss_detection_timer(handshake_status, now);
         }
 
         // HyStart++: Start of the round in a slow start.
         if self.hystart.enabled() &&
-            epoch == packet::EPOCH_APPLICATION &&
+            epoch == packet::Epoch::Application &&
             self.congestion_window < self.ssthresh
         {
             self.hystart.start_round(pkt_num);
@@ -253,14 +362,22 @@
         // Pacing: Set the pacing rate if CC doesn't do its own.
         if !(self.cc_ops.has_custom_pacing)() {
             if let Some(srtt) = self.smoothed_rtt {
-                let rate = (self.congestion_window as u64 * 1000000) /
-                    srtt.as_micros() as u64;
-                self.set_pacing_rate(rate);
+                let rate = PACING_MULTIPLIER * self.congestion_window as f64 /
+                    srtt.as_secs_f64();
+                self.set_pacing_rate(rate as u64, now);
             }
         }
 
         self.schedule_next_packet(epoch, now, sent_bytes);
 
+        pkt.time_sent = self.get_packet_send_time();
+
+        // bytes_in_flight is already updated. Use previous value.
+        self.delivery_rate
+            .on_packet_sent(&mut pkt, self.bytes_in_flight - sent_bytes);
+
+        self.sent[epoch].push_back(pkt);
+
         self.bytes_sent += sent_bytes;
         trace!("{} {:?}", trace_id, self);
     }
@@ -269,64 +386,51 @@
         (self.cc_ops.on_packet_sent)(self, sent_bytes, now);
     }
 
-    pub fn set_pacing_rate(&mut self, rate: u64) {
-        if rate != 0 {
-            self.pacing_rate = rate;
-        }
+    pub fn set_pacing_rate(&mut self, rate: u64, now: Instant) {
+        self.pacer.update(self.send_quantum, rate, now);
     }
 
-    pub fn get_packet_send_time(&self) -> Option<Instant> {
-        self.last_packet_scheduled_time
+    pub fn get_packet_send_time(&self) -> Instant {
+        self.pacer.next_time()
     }
 
     fn schedule_next_packet(
         &mut self, epoch: packet::Epoch, now: Instant, packet_size: usize,
     ) {
         // Don't pace in any of these cases:
-        //   * Packet epoch is not EPOCH_APPLICATION.
-        //   * Packet contains only ACK frames.
-        //   * The start of the connection.
-        if epoch != packet::EPOCH_APPLICATION ||
-            packet_size == 0 ||
-            self.bytes_sent <= self.congestion_window ||
-            self.pacing_rate == 0
-        {
-            self.last_packet_scheduled_time = Some(now);
-            return;
-        }
+        //   * Packet contains no data.
+        //   * Packet epoch is not Epoch::Application.
+        //   * The congestion window is within initcwnd.
 
-        self.last_packet_scheduled_time = match self.last_packet_scheduled_time {
-            Some(last_scheduled_time) => {
-                let interval: u64 =
-                    (packet_size as u64 * 1000000) / self.pacing_rate;
-                let interval = Duration::from_micros(interval);
-                let next_schedule_time = last_scheduled_time + interval;
-                Some(cmp::max(now, next_schedule_time))
-            },
+        let is_app = epoch == packet::Epoch::Application;
 
-            None => Some(now),
+        let in_initcwnd =
+            self.bytes_sent < self.max_datagram_size * INITIAL_WINDOW_PACKETS;
+
+        let sent_bytes = if !self.pacer.enabled() || !is_app || in_initcwnd {
+            0
+        } else {
+            packet_size
         };
+
+        self.pacer.send(sent_bytes, now);
     }
 
     pub fn on_ack_received(
         &mut self, ranges: &ranges::RangeSet, ack_delay: u64,
         epoch: packet::Epoch, handshake_status: HandshakeStatus, now: Instant,
         trace_id: &str,
-    ) -> Result<()> {
+    ) -> Result<(usize, usize)> {
         let largest_acked = ranges.last().unwrap();
 
-        // If the largest packet number acked exceeds any packet number we have
-        // sent, then the ACK is obviously invalid, so there's no need to
-        // continue further.
-        if largest_acked > self.largest_sent_pkt[epoch] {
-            if cfg!(feature = "fuzzing") {
-                return Ok(());
-            }
+        // While quiche used to consider ACK frames acknowledging packet numbers
+        // larger than the largest sent one as invalid, this is not true anymore
+        // if we consider a single packet number space and multiple paths. The
+        // simplest example is the case where the host sends a probing packet on
+        // a validating path, then receives an acknowledgment for that packet on
+        // the active one.
 
-            return Err(Error::InvalidPacket);
-        }
-
-        if self.largest_acked_pkt[epoch] == std::u64::MAX {
+        if self.largest_acked_pkt[epoch] == u64::MAX {
             self.largest_acked_pkt[epoch] = largest_acked;
         } else {
             self.largest_acked_pkt[epoch] =
@@ -340,24 +444,57 @@
 
         let mut newly_acked = Vec::new();
 
+        let mut undo_cwnd = false;
+
+        let max_rtt = cmp::max(self.latest_rtt, self.rtt());
+
         // Detect and mark acked packets, without removing them from the sent
         // packets list.
         for r in ranges.iter() {
-            let lowest_acked = r.start;
-            let largest_acked = r.end - 1;
+            let lowest_acked_in_block = r.start;
+            let largest_acked_in_block = r.end - 1;
 
             let unacked_iter = self.sent[epoch]
                 .iter_mut()
                 // Skip packets that precede the lowest acked packet in the block.
-                .skip_while(|p| p.pkt_num < lowest_acked)
+                .skip_while(|p| p.pkt_num < lowest_acked_in_block)
                 // Skip packets that follow the largest acked packet in the block.
-                .take_while(|p| p.pkt_num <= largest_acked)
+                .take_while(|p| p.pkt_num <= largest_acked_in_block)
                 // Skip packets that have already been acked or lost.
-                .filter(|p| p.time_acked.is_none() && p.time_lost.is_none());
+                .filter(|p| p.time_acked.is_none());
 
             for unacked in unacked_iter {
                 unacked.time_acked = Some(now);
 
+                // Check if acked packet was already declared lost.
+                if unacked.time_lost.is_some() {
+                    // Calculate new packet reordering threshold.
+                    let pkt_thresh =
+                        self.largest_acked_pkt[epoch] - unacked.pkt_num + 1;
+                    let pkt_thresh = cmp::min(MAX_PACKET_THRESHOLD, pkt_thresh);
+
+                    self.pkt_thresh = cmp::max(self.pkt_thresh, pkt_thresh);
+
+                    // Calculate new time reordering threshold.
+                    let loss_delay = max_rtt.mul_f64(self.time_thresh);
+
+                    // unacked.time_sent can be in the future due to
+                    // pacing.
+                    if now.saturating_duration_since(unacked.time_sent) >
+                        loss_delay
+                    {
+                        // TODO: do time threshold update
+                        self.time_thresh = 5_f64 / 4_f64;
+                    }
+
+                    if unacked.in_flight {
+                        undo_cwnd = true;
+                    }
+
+                    self.lost_spurious_count += 1;
+                    continue;
+                }
+
                 if unacked.ack_eliciting {
                     has_ack_eliciting = true;
                 }
@@ -365,13 +502,11 @@
                 largest_newly_acked_pkt_num = unacked.pkt_num;
                 largest_newly_acked_sent_time = unacked.time_sent;
 
-                self.acked[epoch].append(&mut unacked.frames);
+                self.acked[epoch].extend(unacked.frames.drain(..));
 
                 if unacked.in_flight {
                     self.in_flight_count[epoch] =
                         self.in_flight_count[epoch].saturating_sub(1);
-
-                    self.delivery_rate.on_packet_acked(&unacked, now);
                 }
 
                 newly_acked.push(Acked {
@@ -380,33 +515,53 @@
                     time_sent: unacked.time_sent,
 
                     size: unacked.size,
+
+                    rtt: now.saturating_duration_since(unacked.time_sent),
+
+                    delivered: unacked.delivered,
+
+                    delivered_time: unacked.delivered_time,
+
+                    first_sent_time: unacked.first_sent_time,
+
+                    is_app_limited: unacked.is_app_limited,
                 });
 
                 trace!("{} packet newly acked {}", trace_id, unacked.pkt_num);
             }
         }
 
-        self.delivery_rate.estimate();
+        // Undo congestion window update.
+        if undo_cwnd {
+            (self.cc_ops.rollback)(self);
+        }
 
         if newly_acked.is_empty() {
-            return Ok(());
+            return Ok((0, 0));
         }
 
         if largest_newly_acked_pkt_num == largest_acked && has_ack_eliciting {
-            let latest_rtt = now - largest_newly_acked_sent_time;
+            // The packet's sent time could be in the future if pacing is used
+            // and the network has a very short RTT.
+            let latest_rtt =
+                now.saturating_duration_since(largest_newly_acked_sent_time);
 
-            let ack_delay = if epoch == packet::EPOCH_APPLICATION {
+            let ack_delay = if epoch == packet::Epoch::Application {
                 Duration::from_micros(ack_delay)
             } else {
                 Duration::from_micros(0)
             };
 
-            self.update_rtt(latest_rtt, ack_delay, now);
+            // Don't update srtt if rtt is zero.
+            if !latest_rtt.is_zero() {
+                self.update_rtt(latest_rtt, ack_delay, now);
+            }
         }
 
         // Detect and mark lost packets without removing them from the sent
         // packets list.
-        self.detect_lost_packets(epoch, now, trace_id);
+        let (lost_packets, lost_bytes) =
+            self.detect_lost_packets(epoch, now, trace_id);
 
         self.on_packets_acked(newly_acked, epoch, now);
 
@@ -414,25 +569,26 @@
 
         self.set_loss_detection_timer(handshake_status, now);
 
-        self.drain_packets(epoch);
+        self.drain_packets(epoch, now);
 
-        Ok(())
+        Ok((lost_packets, lost_bytes))
     }
 
     pub fn on_loss_detection_timeout(
         &mut self, handshake_status: HandshakeStatus, now: Instant,
         trace_id: &str,
-    ) {
+    ) -> (usize, usize) {
         let (earliest_loss_time, epoch) = self.loss_time_and_space();
 
         if earliest_loss_time.is_some() {
             // Time threshold loss detection.
-            self.detect_lost_packets(epoch, now, trace_id);
+            let (lost_packets, lost_bytes) =
+                self.detect_lost_packets(epoch, now, trace_id);
 
             self.set_loss_detection_timer(handshake_status, now);
 
             trace!("{} {:?}", trace_id, self);
-            return;
+            return (lost_packets, lost_bytes);
         }
 
         let epoch = if self.bytes_in_flight > 0 {
@@ -446,9 +602,9 @@
             // more anti-amplification credit, a Handshake packet proves address
             // ownership.
             if handshake_status.has_handshake_keys {
-                packet::EPOCH_HANDSHAKE
+                packet::Epoch::Handshake
             } else {
-                packet::EPOCH_INITIAL
+                packet::Epoch::Initial
             }
         };
 
@@ -480,6 +636,8 @@
         self.set_loss_detection_timer(handshake_status, now);
 
         trace!("{} {:?}", trace_id, self);
+
+        (0, 0)
     }
 
     pub fn on_pkt_num_space_discarded(
@@ -518,22 +676,36 @@
     pub fn cwnd_available(&self) -> usize {
         // Ignore cwnd when sending probe packets.
         if self.loss_probes.iter().any(|&x| x > 0) {
-            return std::usize::MAX;
+            return usize::MAX;
         }
 
-        self.congestion_window.saturating_sub(self.bytes_in_flight)
+        // Open more space (snd_cnt) for PRR when allowed.
+        self.congestion_window.saturating_sub(self.bytes_in_flight) +
+            self.prr.snd_cnt
     }
 
     pub fn rtt(&self) -> Duration {
         self.smoothed_rtt.unwrap_or(INITIAL_RTT)
     }
 
+    pub fn min_rtt(&self) -> Option<Duration> {
+        if self.min_rtt == Duration::ZERO {
+            return None;
+        }
+
+        Some(self.min_rtt)
+    }
+
+    pub fn rttvar(&self) -> Duration {
+        self.rttvar
+    }
+
     pub fn pto(&self) -> Duration {
         self.rtt() + cmp::max(self.rttvar * 4, GRANULARITY)
     }
 
     pub fn delivery_rate(&self) -> u64 {
-        self.delivery_rate.delivery_rate()
+        self.delivery_rate.sample_delivery_rate()
     }
 
     pub fn max_datagram_size(&self) -> usize {
@@ -544,13 +716,20 @@
         let max_datagram_size =
             cmp::min(self.max_datagram_size, new_max_datagram_size);
 
-        // Congestion Window is updated only when it's not updated already.
+        // Update cwnd if it hasn't been updated yet.
         if self.congestion_window ==
             self.max_datagram_size * INITIAL_WINDOW_PACKETS
         {
             self.congestion_window = max_datagram_size * INITIAL_WINDOW_PACKETS;
         }
 
+        self.pacer = pacer::Pacer::new(
+            self.pacer.enabled(),
+            self.congestion_window,
+            0,
+            max_datagram_size,
+        );
+
         self.max_datagram_size = max_datagram_size;
     }
 
@@ -593,12 +772,13 @@
     }
 
     fn loss_time_and_space(&self) -> (Option<Instant>, packet::Epoch) {
-        let mut epoch = packet::EPOCH_INITIAL;
+        let mut epoch = packet::Epoch::Initial;
         let mut time = self.loss_time[epoch];
 
         // Iterate over all packet number spaces starting from Handshake.
-        #[allow(clippy::needless_range_loop)]
-        for e in packet::EPOCH_HANDSHAKE..packet::EPOCH_COUNT {
+        for &e in packet::Epoch::epochs(
+            packet::Epoch::Handshake..=packet::Epoch::Application,
+        ) {
             let new_time = self.loss_time[e];
 
             if time.is_none() || new_time < time {
@@ -618,22 +798,24 @@
         // Arm PTO from now when there are no inflight packets.
         if self.bytes_in_flight == 0 {
             if handshake_status.has_handshake_keys {
-                return (Some(now + duration), packet::EPOCH_HANDSHAKE);
+                return (Some(now + duration), packet::Epoch::Handshake);
             } else {
-                return (Some(now + duration), packet::EPOCH_INITIAL);
+                return (Some(now + duration), packet::Epoch::Initial);
             }
         }
 
         let mut pto_timeout = None;
-        let mut pto_space = packet::EPOCH_INITIAL;
+        let mut pto_space = packet::Epoch::Initial;
 
         // Iterate over all packet number spaces.
-        for e in packet::EPOCH_INITIAL..packet::EPOCH_COUNT {
+        for &e in packet::Epoch::epochs(
+            packet::Epoch::Initial..=packet::Epoch::Application,
+        ) {
             if self.in_flight_count[e] == 0 {
                 continue;
             }
 
-            if e == packet::EPOCH_APPLICATION {
+            if e == packet::Epoch::Application {
                 // Skip Application Data until handshake completes.
                 if !handshake_status.completed {
                     return (pto_timeout, pto_space);
@@ -678,20 +860,21 @@
 
     fn detect_lost_packets(
         &mut self, epoch: packet::Epoch, now: Instant, trace_id: &str,
-    ) {
+    ) -> (usize, usize) {
         let largest_acked = self.largest_acked_pkt[epoch];
 
         self.loss_time[epoch] = None;
 
         let loss_delay =
-            cmp::max(self.latest_rtt, self.rtt()).mul_f64(TIME_THRESHOLD);
+            cmp::max(self.latest_rtt, self.rtt()).mul_f64(self.time_thresh);
 
         // Minimum time of kGranularity before packets are deemed lost.
         let loss_delay = cmp::max(loss_delay, GRANULARITY);
 
         // Packets sent before this time are deemed lost.
-        let lost_send_time = now - loss_delay;
+        let lost_send_time = now.checked_sub(loss_delay).unwrap();
 
+        let mut lost_packets = 0;
         let mut lost_bytes = 0;
 
         let mut largest_lost_pkt = None;
@@ -706,9 +889,9 @@
         for unacked in unacked_iter {
             // Mark packet as lost, or set time when it should be marked.
             if unacked.time_sent <= lost_send_time ||
-                largest_acked >= unacked.pkt_num + PACKET_THRESHOLD
+                largest_acked >= unacked.pkt_num + self.pkt_thresh
             {
-                self.lost[epoch].append(&mut unacked.frames);
+                self.lost[epoch].extend(unacked.frames.drain(..));
 
                 unacked.time_lost = Some(now);
 
@@ -730,6 +913,7 @@
                     );
                 }
 
+                lost_packets += 1;
                 self.lost_count += 1;
             } else {
                 let loss_time = match self.loss_time[epoch] {
@@ -743,14 +927,18 @@
             }
         }
 
+        self.bytes_lost += lost_bytes as u64;
+
         if let Some(pkt) = largest_lost_pkt {
             self.on_packets_lost(lost_bytes, &pkt, epoch, now);
         }
 
-        self.drain_packets(epoch);
+        self.drain_packets(epoch, now);
+
+        (lost_packets, lost_bytes)
     }
 
-    fn drain_packets(&mut self, epoch: packet::Epoch) {
+    fn drain_packets(&mut self, epoch: packet::Epoch, now: Instant) {
         let mut lowest_non_expired_pkt_index = self.sent[epoch].len();
 
         // In order to avoid removing elements from the middle of the list
@@ -764,6 +952,13 @@
 
         // First, find the first element that is neither acked nor lost.
         for (i, pkt) in self.sent[epoch].iter().enumerate() {
+            if let Some(time_lost) = pkt.time_lost {
+                if time_lost + self.rtt() > now {
+                    lowest_non_expired_pkt_index = i;
+                    break;
+                }
+            }
+
             if pkt.time_acked.is_none() && pkt.time_lost.is_none() {
                 lowest_non_expired_pkt_index = i;
                 break;
@@ -777,9 +972,16 @@
     fn on_packets_acked(
         &mut self, acked: Vec<Acked>, epoch: packet::Epoch, now: Instant,
     ) {
-        for pkt in acked {
-            (self.cc_ops.on_packet_acked)(self, &pkt, epoch, now);
+        // Update delivery rate sample per acked packet.
+        for pkt in &acked {
+            self.delivery_rate.update_rate_sample(pkt, now);
         }
+
+        // Fill in a rate sample.
+        self.delivery_rate.generate_rate_sample(self.min_rtt);
+
+        // Call congestion control hooks.
+        (self.cc_ops.on_packets_acked)(self, &acked, epoch, now);
     }
 
     fn in_congestion_recovery(&self, sent_time: Instant) -> bool {
@@ -804,7 +1006,7 @@
     ) {
         self.bytes_in_flight = self.bytes_in_flight.saturating_sub(lost_bytes);
 
-        self.congestion_event(largest_lost_pkt.time_sent, epoch, now);
+        self.congestion_event(lost_bytes, largest_lost_pkt.time_sent, epoch, now);
 
         if self.in_persistent_congestion(largest_lost_pkt.pkt_num) {
             self.collapse_cwnd();
@@ -812,50 +1014,50 @@
     }
 
     fn congestion_event(
-        &mut self, time_sent: Instant, epoch: packet::Epoch, now: Instant,
+        &mut self, lost_bytes: usize, time_sent: Instant, epoch: packet::Epoch,
+        now: Instant,
     ) {
         if !self.in_congestion_recovery(time_sent) {
             (self.cc_ops.checkpoint)(self);
         }
 
-        (self.cc_ops.congestion_event)(self, time_sent, epoch, now);
+        (self.cc_ops.congestion_event)(self, lost_bytes, time_sent, epoch, now);
     }
 
     fn collapse_cwnd(&mut self) {
         (self.cc_ops.collapse_cwnd)(self);
     }
 
-    pub fn rate_check_app_limited(&mut self) {
-        if self.app_limited {
-            self.delivery_rate.check_app_limited(self.bytes_in_flight)
-        }
-    }
-
     pub fn update_app_limited(&mut self, v: bool) {
         self.app_limited = v;
     }
 
-    pub fn app_limited(&mut self) -> bool {
+    pub fn app_limited(&self) -> bool {
         self.app_limited
     }
 
+    pub fn delivery_rate_update_app_limited(&mut self, v: bool) {
+        self.delivery_rate.update_app_limited(v);
+    }
+
     #[cfg(feature = "qlog")]
-    pub fn to_qlog(&self) -> qlog::event::Event {
-        // QVis can't use all these fields and they can be large.
-        qlog::event::Event::metrics_updated(
-            Some(self.min_rtt.as_millis() as u64),
-            Some(self.rtt().as_millis() as u64),
-            Some(self.latest_rtt.as_millis() as u64),
-            Some(self.rttvar.as_millis() as u64),
-            None, // delay
-            None, // probe_count
-            Some(self.cwnd() as u64),
-            Some(self.bytes_in_flight as u64),
-            None, // ssthresh
-            None, // packets_in_flight
-            None, // in_recovery
-            None, // pacing_rate
-        )
+    pub fn maybe_qlog(&mut self) -> Option<EventData> {
+        let qlog_metrics = QlogMetrics {
+            min_rtt: self.min_rtt,
+            smoothed_rtt: self.rtt(),
+            latest_rtt: self.latest_rtt,
+            rttvar: self.rttvar,
+            cwnd: self.cwnd() as u64,
+            bytes_in_flight: self.bytes_in_flight as u64,
+            ssthresh: self.ssthresh as u64,
+            pacing_rate: self.pacer.rate(),
+        };
+
+        self.qlog_metrics.maybe_update(qlog_metrics)
+    }
+
+    pub fn send_quantum(&self) -> usize {
+        self.send_quantum
     }
 }
 
@@ -863,13 +1065,15 @@
 ///
 /// This enum provides currently available list of congestion control
 /// algorithms.
-#[derive(Debug, Copy, Clone, PartialEq)]
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
 #[repr(C)]
 pub enum CongestionControlAlgorithm {
     /// Reno congestion control algorithm. `reno` in a string form.
     Reno  = 0,
     /// CUBIC congestion control algorithm (default). `cubic` in a string form.
     CUBIC = 1,
+    /// BBR congestion control algorithm. `bbr` in a string form.
+    BBR   = 2,
 }
 
 impl FromStr for CongestionControlAlgorithm {
@@ -882,6 +1086,7 @@
         match name {
             "reno" => Ok(CongestionControlAlgorithm::Reno),
             "cubic" => Ok(CongestionControlAlgorithm::CUBIC),
+            "bbr" => Ok(CongestionControlAlgorithm::BBR),
 
             _ => Err(crate::Error::CongestionControl),
         }
@@ -889,13 +1094,22 @@
 }
 
 pub struct CongestionControlOps {
+    pub on_init: fn(r: &mut Recovery),
+
+    pub reset: fn(r: &mut Recovery),
+
     pub on_packet_sent: fn(r: &mut Recovery, sent_bytes: usize, now: Instant),
 
-    pub on_packet_acked:
-        fn(r: &mut Recovery, packet: &Acked, epoch: packet::Epoch, now: Instant),
+    pub on_packets_acked: fn(
+        r: &mut Recovery,
+        packets: &[Acked],
+        epoch: packet::Epoch,
+        now: Instant,
+    ),
 
     pub congestion_event: fn(
         r: &mut Recovery,
+        lost_bytes: usize,
         time_sent: Instant,
         epoch: packet::Epoch,
         now: Instant,
@@ -905,9 +1119,12 @@
 
     pub checkpoint: fn(r: &mut Recovery),
 
-    pub rollback: fn(r: &mut Recovery),
+    pub rollback: fn(r: &mut Recovery) -> bool,
 
     pub has_custom_pacing: fn() -> bool,
+
+    pub debug_fmt:
+        fn(r: &Recovery, formatter: &mut std::fmt::Formatter) -> std::fmt::Result,
 }
 
 impl From<CongestionControlAlgorithm> for &'static CongestionControlOps {
@@ -915,6 +1132,7 @@
         match algo {
             CongestionControlAlgorithm::Reno => &reno::RENO,
             CongestionControlAlgorithm::CUBIC => &cubic::CUBIC,
+            CongestionControlAlgorithm::BBR => &bbr::BBR,
         }
     }
 }
@@ -927,7 +1145,7 @@
 
                 if v > now {
                     let d = v.duration_since(now);
-                    write!(f, "timer={:?} ", d)?;
+                    write!(f, "timer={d:?} ")?;
                 } else {
                     write!(f, "timer=exp ")?;
                 }
@@ -954,17 +1172,15 @@
             self.congestion_recovery_start_time
         )?;
         write!(f, "{:?} ", self.delivery_rate)?;
-        write!(f, "pacing_rate={:?} ", self.pacing_rate)?;
-        write!(
-            f,
-            "last_packet_scheduled_time={:?} ",
-            self.last_packet_scheduled_time
-        )?;
+        write!(f, "pacer={:?} ", self.pacer)?;
 
         if self.hystart.enabled() {
             write!(f, "hystart={:?} ", self.hystart)?;
         }
 
+        // CC-specific debug info
+        (self.cc_ops.debug_fmt)(self, f)?;
+
         Ok(())
     }
 }
@@ -973,7 +1189,7 @@
 pub struct Sent {
     pub pkt_num: u64,
 
-    pub frames: Vec<frame::Frame>,
+    pub frames: SmallVec<[frame::Frame; 1]>,
 
     pub time_sent: Instant,
 
@@ -991,7 +1207,7 @@
 
     pub delivered_time: Instant,
 
-    pub recent_delivered_packet_sent_time: Instant,
+    pub first_sent_time: Instant,
 
     pub is_app_limited: bool,
 
@@ -1001,15 +1217,11 @@
 impl std::fmt::Debug for Sent {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
         write!(f, "pkt_num={:?} ", self.pkt_num)?;
-        write!(f, "pkt_sent_time={:?} ", self.time_sent.elapsed())?;
+        write!(f, "pkt_sent_time={:?} ", self.time_sent)?;
         write!(f, "pkt_size={:?} ", self.size)?;
         write!(f, "delivered={:?} ", self.delivered)?;
-        write!(f, "delivered_time={:?} ", self.delivered_time.elapsed())?;
-        write!(
-            f,
-            "recent_delivered_packet_sent_time={:?} ",
-            self.recent_delivered_packet_sent_time.elapsed()
-        )?;
+        write!(f, "delivered_time={:?} ", self.delivered_time)?;
+        write!(f, "first_sent_time={:?} ", self.first_sent_time)?;
         write!(f, "is_app_limited={} ", self.is_app_limited)?;
         write!(f, "has_data={} ", self.has_data)?;
 
@@ -1024,6 +1236,16 @@
     pub time_sent: Instant,
 
     pub size: usize,
+
+    pub rtt: Duration,
+
+    pub delivered: usize,
+
+    pub delivered_time: Instant,
+
+    pub first_sent_time: Instant,
+
+    pub is_app_limited: bool,
 }
 
 #[derive(Clone, Copy, Debug)]
@@ -1056,9 +1278,124 @@
     }
 }
 
+// We don't need to log all qlog metrics every time there is a recovery event.
+// Instead, we can log only the MetricsUpdated event data fields that we care
+// about, only when they change. To support this, the QLogMetrics structure
+// keeps a running picture of the fields.
+#[derive(Default)]
+#[cfg(feature = "qlog")]
+struct QlogMetrics {
+    min_rtt: Duration,
+    smoothed_rtt: Duration,
+    latest_rtt: Duration,
+    rttvar: Duration,
+    cwnd: u64,
+    bytes_in_flight: u64,
+    ssthresh: u64,
+    pacing_rate: u64,
+}
+
+#[cfg(feature = "qlog")]
+impl QlogMetrics {
+    // Make a qlog event if the latest instance of QlogMetrics is different.
+    //
+    // This function diffs each of the fields. A qlog MetricsUpdated event is
+    // only generated if at least one field is different. Where fields are
+    // different, the qlog event contains the latest value.
+    fn maybe_update(&mut self, latest: Self) -> Option<EventData> {
+        let mut emit_event = false;
+
+        let new_min_rtt = if self.min_rtt != latest.min_rtt {
+            self.min_rtt = latest.min_rtt;
+            emit_event = true;
+            Some(latest.min_rtt.as_secs_f32() * 1000.0)
+        } else {
+            None
+        };
+
+        let new_smoothed_rtt = if self.smoothed_rtt != latest.smoothed_rtt {
+            self.smoothed_rtt = latest.smoothed_rtt;
+            emit_event = true;
+            Some(latest.smoothed_rtt.as_secs_f32() * 1000.0)
+        } else {
+            None
+        };
+
+        let new_latest_rtt = if self.latest_rtt != latest.latest_rtt {
+            self.latest_rtt = latest.latest_rtt;
+            emit_event = true;
+            Some(latest.latest_rtt.as_secs_f32() * 1000.0)
+        } else {
+            None
+        };
+
+        let new_rttvar = if self.rttvar != latest.rttvar {
+            self.rttvar = latest.rttvar;
+            emit_event = true;
+            Some(latest.rttvar.as_secs_f32() * 1000.0)
+        } else {
+            None
+        };
+
+        let new_cwnd = if self.cwnd != latest.cwnd {
+            self.cwnd = latest.cwnd;
+            emit_event = true;
+            Some(latest.cwnd)
+        } else {
+            None
+        };
+
+        let new_bytes_in_flight =
+            if self.bytes_in_flight != latest.bytes_in_flight {
+                self.bytes_in_flight = latest.bytes_in_flight;
+                emit_event = true;
+                Some(latest.bytes_in_flight)
+            } else {
+                None
+            };
+
+        let new_ssthresh = if self.ssthresh != latest.ssthresh {
+            self.ssthresh = latest.ssthresh;
+            emit_event = true;
+            Some(latest.ssthresh)
+        } else {
+            None
+        };
+
+        let new_pacing_rate = if self.pacing_rate != latest.pacing_rate {
+            self.pacing_rate = latest.pacing_rate;
+            emit_event = true;
+            Some(latest.pacing_rate)
+        } else {
+            None
+        };
+
+        if emit_event {
+            // QVis can't use all these fields and they can be large.
+            return Some(EventData::MetricsUpdated(
+                qlog::events::quic::MetricsUpdated {
+                    min_rtt: new_min_rtt,
+                    smoothed_rtt: new_smoothed_rtt,
+                    latest_rtt: new_latest_rtt,
+                    rtt_variance: new_rttvar,
+                    pto_count: None,
+                    congestion_window: new_cwnd,
+                    bytes_in_flight: new_bytes_in_flight,
+                    ssthresh: new_ssthresh,
+                    packets_in_flight: None,
+                    pacing_rate: new_pacing_rate,
+                },
+            ));
+        }
+
+        None
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
+    use smallvec::smallvec;
 
     #[test]
     fn lookup_cc_algo_ok() {
@@ -1070,7 +1407,7 @@
     fn lookup_cc_algo_bad() {
         assert_eq!(
             CongestionControlAlgorithm::from_str("???"),
-            Err(Error::CongestionControl)
+            Err(crate::Error::CongestionControl)
         );
     }
 
@@ -1095,12 +1432,12 @@
 
         let mut now = Instant::now();
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 0);
 
         // Start by sending a few packets.
         let p = Sent {
             pkt_num: 0,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1109,24 +1446,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 1);
         assert_eq!(r.bytes_in_flight, 1000);
 
         let p = Sent {
             pkt_num: 1,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1135,24 +1472,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 2);
         assert_eq!(r.bytes_in_flight, 2000);
 
         let p = Sent {
             pkt_num: 2,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1161,24 +1498,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 3);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 3);
         assert_eq!(r.bytes_in_flight, 3000);
 
         let p = Sent {
             pkt_num: 3,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1187,19 +1524,19 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 4);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 4);
         assert_eq!(r.bytes_in_flight, 4000);
 
         // Wait for 10ms.
@@ -1213,15 +1550,15 @@
             r.on_ack_received(
                 &acked,
                 25,
-                packet::EPOCH_APPLICATION,
+                packet::Epoch::Application,
                 HandshakeStatus::default(),
                 now,
                 ""
             ),
-            Ok(())
+            Ok((0, 0))
         );
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 2);
         assert_eq!(r.bytes_in_flight, 2000);
         assert_eq!(r.lost_count, 0);
 
@@ -1230,13 +1567,13 @@
 
         // PTO.
         r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
-        assert_eq!(r.loss_probes[packet::EPOCH_APPLICATION], 1);
+        assert_eq!(r.loss_probes[packet::Epoch::Application], 1);
         assert_eq!(r.lost_count, 0);
         assert_eq!(r.pto_count, 1);
 
         let p = Sent {
             pkt_num: 4,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1245,24 +1582,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 3);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 3);
         assert_eq!(r.bytes_in_flight, 3000);
 
         let p = Sent {
             pkt_num: 5,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1271,19 +1608,19 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 4);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 4);
         assert_eq!(r.bytes_in_flight, 4000);
         assert_eq!(r.lost_count, 0);
 
@@ -1298,18 +1635,25 @@
             r.on_ack_received(
                 &acked,
                 25,
-                packet::EPOCH_APPLICATION,
+                packet::Epoch::Application,
                 HandshakeStatus::default(),
                 now,
                 ""
             ),
-            Ok(())
+            Ok((2, 2000))
         );
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 4);
         assert_eq!(r.bytes_in_flight, 0);
 
         assert_eq!(r.lost_count, 2);
+
+        // Wait 1 RTT.
+        now += r.rtt();
+
+        r.detect_lost_packets(packet::Epoch::Application, now, "");
+
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 0);
     }
 
     #[test]
@@ -1321,12 +1665,12 @@
 
         let mut now = Instant::now();
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 0);
 
         // Start by sending a few packets.
         let p = Sent {
             pkt_num: 0,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1335,24 +1679,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 1);
         assert_eq!(r.bytes_in_flight, 1000);
 
         let p = Sent {
             pkt_num: 1,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1361,24 +1705,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 2);
         assert_eq!(r.bytes_in_flight, 2000);
 
         let p = Sent {
             pkt_num: 2,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1387,24 +1731,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 3);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 3);
         assert_eq!(r.bytes_in_flight, 3000);
 
         let p = Sent {
             pkt_num: 3,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1413,19 +1757,19 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 4);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 4);
         assert_eq!(r.bytes_in_flight, 4000);
 
         // Wait for 10ms.
@@ -1440,15 +1784,15 @@
             r.on_ack_received(
                 &acked,
                 25,
-                packet::EPOCH_APPLICATION,
+                packet::Epoch::Application,
                 HandshakeStatus::default(),
                 now,
                 ""
             ),
-            Ok(())
+            Ok((0, 0))
         );
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 2);
         assert_eq!(r.bytes_in_flight, 1000);
         assert_eq!(r.lost_count, 0);
 
@@ -1457,12 +1801,19 @@
 
         // Packet is declared lost.
         r.on_loss_detection_timeout(HandshakeStatus::default(), now, "");
-        assert_eq!(r.loss_probes[packet::EPOCH_APPLICATION], 0);
+        assert_eq!(r.loss_probes[packet::Epoch::Application], 0);
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 2);
         assert_eq!(r.bytes_in_flight, 0);
 
         assert_eq!(r.lost_count, 1);
+
+        // Wait 1 RTT.
+        now += r.rtt();
+
+        r.detect_lost_packets(packet::Epoch::Application, now, "");
+
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 0);
     }
 
     #[test]
@@ -1474,12 +1825,12 @@
 
         let mut now = Instant::now();
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 0);
 
         // Start by sending a few packets.
         let p = Sent {
             pkt_num: 0,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1488,24 +1839,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 1);
         assert_eq!(r.bytes_in_flight, 1000);
 
         let p = Sent {
             pkt_num: 1,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1514,24 +1865,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 2);
         assert_eq!(r.bytes_in_flight, 2000);
 
         let p = Sent {
             pkt_num: 2,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1540,24 +1891,24 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 3);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 3);
         assert_eq!(r.bytes_in_flight, 3000);
 
         let p = Sent {
             pkt_num: 3,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -1566,19 +1917,19 @@
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 4);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 4);
         assert_eq!(r.bytes_in_flight, 4000);
 
         // Wait for 10ms.
@@ -1592,12 +1943,12 @@
             r.on_ack_received(
                 &acked,
                 25,
-                packet::EPOCH_APPLICATION,
+                packet::Epoch::Application,
                 HandshakeStatus::default(),
                 now,
                 ""
             ),
-            Ok(())
+            Ok((1, 1000))
         );
 
         now += Duration::from_millis(10);
@@ -1605,23 +1956,36 @@
         let mut acked = ranges::RangeSet::default();
         acked.insert(0..2);
 
+        assert_eq!(r.pkt_thresh, INITIAL_PACKET_THRESHOLD);
+
         assert_eq!(
             r.on_ack_received(
                 &acked,
                 25,
-                packet::EPOCH_APPLICATION,
+                packet::Epoch::Application,
                 HandshakeStatus::default(),
                 now,
                 ""
             ),
-            Ok(())
+            Ok((0, 0))
         );
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 4);
         assert_eq!(r.bytes_in_flight, 0);
 
         // Spurious loss.
         assert_eq!(r.lost_count, 1);
+        assert_eq!(r.lost_spurious_count, 1);
+
+        // Packet threshold was increased.
+        assert_eq!(r.pkt_thresh, 4);
+
+        // Wait 1 RTT.
+        now += r.rtt();
+
+        r.detect_lost_packets(packet::Epoch::Application, now, "");
+
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 0);
     }
 
     #[test]
@@ -1633,39 +1997,39 @@
 
         let mut now = Instant::now();
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 0);
 
-        // send out first packet.
+        // send out first packet (a full initcwnd).
         let p = Sent {
             pkt_num: 0,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
-            size: 6500,
+            size: 12000,
             ack_eliciting: true,
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1);
-        assert_eq!(r.bytes_in_flight, 6500);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 1);
+        assert_eq!(r.bytes_in_flight, 12000);
 
-        // First packet will be sent out immidiately.
-        assert_eq!(r.pacing_rate, 0);
-        assert_eq!(r.get_packet_send_time().unwrap(), now);
+        // First packet will be sent out immediately.
+        assert_eq!(r.pacer.rate(), 0);
+        assert_eq!(r.get_packet_send_time(), now);
 
         // Wait 50ms for ACK.
         now += Duration::from_millis(50);
@@ -1677,91 +2041,125 @@
             r.on_ack_received(
                 &acked,
                 10,
-                packet::EPOCH_APPLICATION,
+                packet::Epoch::Application,
                 HandshakeStatus::default(),
                 now,
                 ""
             ),
-            Ok(())
+            Ok((0, 0))
         );
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 0);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 0);
         assert_eq!(r.bytes_in_flight, 0);
         assert_eq!(r.smoothed_rtt.unwrap(), Duration::from_millis(50));
 
+        // 1 MSS increased.
+        assert_eq!(r.congestion_window, 12000 + 1200);
+
         // Send out second packet.
         let p = Sent {
             pkt_num: 1,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
-            size: 6500,
+            size: 6000,
             ack_eliciting: true,
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 1);
-        assert_eq!(r.bytes_in_flight, 6500);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 1);
+        assert_eq!(r.bytes_in_flight, 6000);
 
-        // Pacing is not done during intial phase of connection.
-        assert_eq!(r.get_packet_send_time().unwrap(), now);
+        // Pacing is not done during initial phase of connection.
+        assert_eq!(r.get_packet_send_time(), now);
 
         // Send the third packet out.
         let p = Sent {
             pkt_num: 2,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
-            size: 6500,
+            size: 6000,
             ack_eliciting: true,
             in_flight: true,
             delivered: 0,
             delivered_time: now,
-            recent_delivered_packet_sent_time: now,
+            first_sent_time: now,
             is_app_limited: false,
             has_data: false,
         };
 
         r.on_packet_sent(
             p,
-            packet::EPOCH_APPLICATION,
+            packet::Epoch::Application,
             HandshakeStatus::default(),
             now,
             "",
         );
 
-        assert_eq!(r.sent[packet::EPOCH_APPLICATION].len(), 2);
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 2);
+        assert_eq!(r.bytes_in_flight, 12000);
+
+        // Send the third packet out.
+        let p = Sent {
+            pkt_num: 3,
+            frames: smallvec![],
+            time_sent: now,
+            time_acked: None,
+            time_lost: None,
+            size: 1000,
+            ack_eliciting: true,
+            in_flight: true,
+            delivered: 0,
+            delivered_time: now,
+            first_sent_time: now,
+            is_app_limited: false,
+            has_data: false,
+        };
+
+        r.on_packet_sent(
+            p,
+            packet::Epoch::Application,
+            HandshakeStatus::default(),
+            now,
+            "",
+        );
+
+        assert_eq!(r.sent[packet::Epoch::Application].len(), 3);
         assert_eq!(r.bytes_in_flight, 13000);
-        assert_eq!(r.smoothed_rtt.unwrap(), Duration::from_millis(50));
 
         // We pace this outgoing packet. as all conditions for pacing
         // are passed.
-        assert_eq!(r.pacing_rate, (12000.0 / 0.05) as u64);
+        let pacing_rate =
+            (r.congestion_window as f64 * PACING_MULTIPLIER / 0.05) as u64;
+        assert_eq!(r.pacer.rate(), pacing_rate);
+
         assert_eq!(
-            r.get_packet_send_time().unwrap(),
-            now + Duration::from_micros(
-                (6500 * 1000000) / (12000.0 / 0.05) as u64
-            )
+            r.get_packet_send_time(),
+            now + Duration::from_secs_f64(12000.0 / pacing_rate as f64)
         );
     }
 }
 
+mod bbr;
 mod cubic;
 mod delivery_rate;
 mod hystart;
+mod pacer;
+mod prr;
 mod reno;
diff --git a/src/recovery/pacer.rs b/src/recovery/pacer.rs
new file mode 100644
index 0000000..ab54364
--- /dev/null
+++ b/src/recovery/pacer.rs
@@ -0,0 +1,250 @@
+// Copyright (C) 2022, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! Pacer provides the timestamp for the next packet to be sent based on the
+//! current send_quantum, pacing rate and last updated time.
+//!
+//! It's a kind of leaky bucket algorithm (RFC9002, 7.7 Pacing) but it considers
+//! max burst (send_quantum, in bytes) and provide the same timestamp for the
+//! same sized packets (except last one) to be GSO friendly, assuming we send
+//! packets using multiple sendmsg(), a sendmmsg(), or sendmsg() with GSO
+//! without waiting for new I/O events.
+//!
+//! After sending a burst of packets, the next timestamp will be updated based
+//! on the current pacing rate. It will make actual timestamp sent and recorded
+//! timestamp (Sent.time_sent) as close as possible. If GSO is not used, it will
+//! still try to provide close timestamp if the send burst is implemented.
+
+use std::time::Duration;
+use std::time::Instant;
+
+#[derive(Debug)]
+pub struct Pacer {
+    /// Whether pacing is enabled.
+    enabled: bool,
+
+    /// Bucket capacity (bytes).
+    capacity: usize,
+
+    /// Bucket used (bytes).
+    used: usize,
+
+    /// Sending pacing rate (bytes/sec).
+    rate: u64,
+
+    /// Timestamp of the last packet sent time update.
+    last_update: Instant,
+
+    /// Timestamp of the next packet to be sent.
+    next_time: Instant,
+
+    /// Current MSS.
+    max_datagram_size: usize,
+
+    /// Last packet size.
+    last_packet_size: Option<usize>,
+
+    /// Interval to be added in next burst.
+    iv: Duration,
+}
+
+impl Pacer {
+    pub fn new(
+        enabled: bool, capacity: usize, rate: u64, max_datagram_size: usize,
+    ) -> Self {
+        // Round capacity to MSS.
+        let capacity = capacity / max_datagram_size * max_datagram_size;
+
+        Pacer {
+            enabled,
+
+            capacity,
+
+            used: 0,
+
+            rate,
+
+            last_update: Instant::now(),
+
+            next_time: Instant::now(),
+
+            max_datagram_size,
+
+            last_packet_size: None,
+
+            iv: Duration::ZERO,
+        }
+    }
+
+    /// Returns whether pacing is enabled.
+    pub fn enabled(&self) -> bool {
+        self.enabled
+    }
+
+    /// Returns the current pacing rate.
+    pub fn rate(&self) -> u64 {
+        self.rate
+    }
+
+    /// Updates the bucket capacity or pacing_rate.
+    pub fn update(&mut self, capacity: usize, rate: u64, now: Instant) {
+        let capacity = capacity / self.max_datagram_size * self.max_datagram_size;
+
+        if self.capacity != capacity {
+            self.reset(now);
+        }
+
+        self.capacity = capacity;
+
+        self.rate = rate;
+    }
+
+    /// Resets the pacer for the next burst.
+    pub fn reset(&mut self, now: Instant) {
+        self.used = 0;
+
+        self.last_update = now;
+
+        self.next_time = self.next_time.max(now);
+
+        self.last_packet_size = None;
+
+        self.iv = Duration::ZERO;
+    }
+
+    /// Updates the timestamp for the packet to send.
+    pub fn send(&mut self, packet_size: usize, now: Instant) {
+        if self.rate == 0 {
+            self.reset(now);
+
+            return;
+        }
+
+        if !self.iv.is_zero() {
+            self.next_time = self.next_time.max(now) + self.iv;
+
+            self.iv = Duration::ZERO;
+        }
+
+        let interval =
+            Duration::from_secs_f64(self.capacity as f64 / self.rate as f64);
+
+        let elapsed = now.saturating_duration_since(self.last_update);
+
+        // If too old, reset it.
+        if elapsed > interval {
+            self.reset(now);
+        }
+
+        self.used += packet_size;
+
+        let same_size = if let Some(last_packet_size) = self.last_packet_size {
+            last_packet_size == packet_size
+        } else {
+            true
+        };
+
+        self.last_packet_size = Some(packet_size);
+
+        if self.used >= self.capacity || !same_size {
+            self.iv =
+                Duration::from_secs_f64(self.used as f64 / self.rate as f64);
+
+            self.used = 0;
+
+            self.last_update = now;
+
+            self.last_packet_size = None;
+        };
+    }
+
+    /// Returns the timestamp for the next packet.
+    pub fn next_time(&self) -> Instant {
+        self.next_time
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn pacer_update() {
+        let datagram_size = 1200;
+        let max_burst = datagram_size * 10;
+        let pacing_rate = 100_000;
+
+        let mut p = Pacer::new(true, max_burst, pacing_rate, datagram_size);
+
+        let now = Instant::now();
+
+        // Send 6000 (half of max_burst) -> no timestamp change yet.
+        p.send(6000, now);
+
+        assert!(now.duration_since(p.next_time()) < Duration::from_millis(1));
+
+        // Send 6000 bytes -> max_burst filled.
+        p.send(6000, now);
+
+        assert!(now.duration_since(p.next_time()) < Duration::from_millis(1));
+
+        // Start of a new burst.
+        let now = now + Duration::from_millis(5);
+
+        // Send 1000 bytes and next_time is updated.
+        p.send(1000, now);
+
+        let interval = max_burst as f64 / pacing_rate as f64;
+
+        assert_eq!(p.next_time() - now, Duration::from_secs_f64(interval));
+    }
+
+    #[test]
+    /// Same as pacer_update() but adds some idle time between transfers to
+    /// trigger a reset.
+    fn pacer_idle() {
+        let datagram_size = 1200;
+        let max_burst = datagram_size * 10;
+        let pacing_rate = 100_000;
+
+        let mut p = Pacer::new(true, max_burst, pacing_rate, datagram_size);
+
+        let now = Instant::now();
+
+        // Send 6000 (half of max_burst) -> no timestamp change yet.
+        p.send(6000, now);
+
+        assert!(now.duration_since(p.next_time()) < Duration::from_millis(1));
+
+        // Sleep 200ms to reset the idle pacer (at least 120ms).
+        let now = now + Duration::from_millis(200);
+
+        // Send 6000 bytes -> idle reset and a new burst  isstarted.
+        p.send(6000, now);
+
+        assert_eq!(p.next_time(), now);
+    }
+}
diff --git a/src/recovery/prr.rs b/src/recovery/prr.rs
new file mode 100644
index 0000000..312b260
--- /dev/null
+++ b/src/recovery/prr.rs
@@ -0,0 +1,238 @@
+// Copyright (C) 2021, Cloudflare, Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright notice,
+//       this list of conditions and the following disclaimer.
+//
+//     * Redistributions in binary form must reproduce the above copyright
+//       notice, this list of conditions and the following disclaimer in the
+//       documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+//! Proportional Rate Reduction
+//!
+//! This implementation is based on the following RFC:
+//!
+//! <https://datatracker.ietf.org/doc/html/rfc6937>
+
+use std::cmp;
+
+#[derive(Default, Debug)]
+pub struct PRR {
+    // Total bytes delivered during recovery.
+    prr_delivered: usize,
+
+    // FlightSize at the start of recovery.
+    recoverfs: usize,
+
+    // Total bytes sent during recovery.
+    prr_out: usize,
+
+    // Total additional bytes can be sent for retransmit during recovery.
+    pub snd_cnt: usize,
+}
+
+impl PRR {
+    pub fn on_packet_sent(&mut self, sent_bytes: usize) {
+        self.prr_out += sent_bytes;
+
+        self.snd_cnt = self.snd_cnt.saturating_sub(sent_bytes);
+    }
+
+    pub fn congestion_event(&mut self, bytes_in_flight: usize) {
+        self.prr_delivered = 0;
+
+        self.recoverfs = bytes_in_flight;
+
+        self.prr_out = 0;
+
+        self.snd_cnt = 0;
+    }
+
+    pub fn on_packet_acked(
+        &mut self, delivered_data: usize, pipe: usize, ssthresh: usize,
+        max_datagram_size: usize,
+    ) {
+        self.prr_delivered += delivered_data;
+
+        self.snd_cnt = if pipe > ssthresh {
+            // Proportional Rate Reduction.
+            if self.recoverfs > 0 {
+                ((self.prr_delivered * ssthresh + self.recoverfs - 1) /
+                    self.recoverfs)
+                    .saturating_sub(self.prr_out)
+            } else {
+                0
+            }
+        } else {
+            // PRR-SSRB.
+            let limit = cmp::max(
+                self.prr_delivered.saturating_sub(self.prr_out),
+                delivered_data,
+            ) + max_datagram_size;
+
+            // Attempt to catch up, as permitted by limit
+            cmp::min(ssthresh - pipe, limit)
+        };
+
+        // snd_cnt should be a positive number.
+        self.snd_cnt = cmp::max(self.snd_cnt, 0);
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn congestion_event() {
+        let mut prr = PRR::default();
+        let bytes_in_flight = 1000;
+
+        prr.congestion_event(bytes_in_flight);
+
+        assert_eq!(prr.recoverfs, bytes_in_flight);
+        assert_eq!(prr.snd_cnt, 0);
+    }
+
+    #[test]
+    fn on_packet_sent() {
+        let mut prr = PRR::default();
+        let bytes_in_flight = 1000;
+        let bytes_sent = 500;
+
+        prr.congestion_event(bytes_in_flight);
+
+        prr.on_packet_sent(bytes_sent);
+
+        assert_eq!(prr.prr_out, bytes_sent);
+        assert_eq!(prr.snd_cnt, 0);
+    }
+
+    #[test]
+    fn on_packet_acked_prr() {
+        let mut prr = PRR::default();
+        let max_datagram_size = 1000;
+        let bytes_in_flight = max_datagram_size * 10;
+        let ssthresh = bytes_in_flight / 2;
+        let acked = 1000;
+
+        prr.congestion_event(bytes_in_flight);
+
+        // pipe > ssthresh uses PRR algorithm.
+        let pipe = bytes_in_flight;
+
+        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);
+
+        assert_eq!(prr.snd_cnt, 500);
+
+        let snd_cnt = prr.snd_cnt;
+
+        // send one more allowed by snd_cnt
+        prr.on_packet_sent(snd_cnt);
+
+        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);
+
+        assert_eq!(prr.snd_cnt, 500);
+    }
+
+    #[test]
+    fn on_packet_acked_prr_overflow() {
+        let mut prr = PRR::default();
+        let max_datagram_size = 1000;
+        let bytes_in_flight = max_datagram_size * 10;
+        let ssthresh = bytes_in_flight / 2;
+        let acked = 1000;
+
+        prr.congestion_event(bytes_in_flight);
+
+        prr.on_packet_sent(max_datagram_size);
+
+        // pipe > ssthresh uses PRR algorithm.
+        let pipe = bytes_in_flight + max_datagram_size;
+
+        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);
+
+        assert_eq!(prr.snd_cnt, 0);
+    }
+
+    #[test]
+    fn on_packet_acked_prr_zero_in_flight() {
+        let mut prr = PRR::default();
+        let max_datagram_size = 1000;
+        let bytes_in_flight = 0;
+        let ssthresh = 3000;
+        let acked = 1000;
+
+        prr.congestion_event(bytes_in_flight);
+
+        // pipe > ssthresh uses PRR algorithm.
+        let pipe = ssthresh + 1000;
+
+        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);
+
+        assert_eq!(prr.snd_cnt, 0);
+    }
+
+    #[test]
+    fn on_packet_acked_prr_ssrb() {
+        let mut prr = PRR::default();
+        let max_datagram_size = 1000;
+        let bytes_in_flight = max_datagram_size * 10;
+        let ssthresh = bytes_in_flight / 2;
+        let acked = 1000;
+
+        prr.congestion_event(bytes_in_flight);
+
+        // pipe <= ssthresh uses PRR-SSRB algorithm.
+        let pipe = max_datagram_size;
+
+        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);
+
+        assert_eq!(prr.snd_cnt, 2000);
+
+        let snd_cnt = prr.snd_cnt;
+
+        // send one more allowed by snd_cnt
+        prr.on_packet_sent(snd_cnt);
+
+        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);
+
+        assert_eq!(prr.snd_cnt, 2000);
+    }
+
+    #[test]
+    fn on_packet_acked_prr_ssrb_overflow() {
+        let mut prr = PRR::default();
+        let max_datagram_size = 1000;
+        let bytes_in_flight = max_datagram_size * 10;
+        let ssthresh = bytes_in_flight / 2;
+        let acked = 500;
+
+        prr.congestion_event(bytes_in_flight);
+
+        // pipe <= ssthresh uses PRR-SSRB algorithm.
+        let pipe = max_datagram_size;
+
+        prr.on_packet_sent(max_datagram_size);
+
+        prr.on_packet_acked(acked, pipe, ssthresh, max_datagram_size);
+
+        assert_eq!(prr.snd_cnt, 1500);
+    }
+}
diff --git a/src/recovery/reno.rs b/src/recovery/reno.rs
index 404b63f..0b4a6c3 100644
--- a/src/recovery/reno.rs
+++ b/src/recovery/reno.rs
@@ -39,19 +39,34 @@
 use crate::recovery::Recovery;
 
 pub static RENO: CongestionControlOps = CongestionControlOps {
+    on_init,
+    reset,
     on_packet_sent,
-    on_packet_acked,
+    on_packets_acked,
     congestion_event,
     collapse_cwnd,
     checkpoint,
     rollback,
     has_custom_pacing,
+    debug_fmt,
 };
 
+pub fn on_init(_r: &mut Recovery) {}
+
+pub fn reset(_r: &mut Recovery) {}
+
 pub fn on_packet_sent(r: &mut Recovery, sent_bytes: usize, _now: Instant) {
     r.bytes_in_flight += sent_bytes;
 }
 
+fn on_packets_acked(
+    r: &mut Recovery, packets: &[Acked], epoch: packet::Epoch, now: Instant,
+) {
+    for pkt in packets {
+        on_packet_acked(r, pkt, epoch, now);
+    }
+}
+
 fn on_packet_acked(
     r: &mut Recovery, packet: &Acked, epoch: packet::Epoch, now: Instant,
 ) {
@@ -70,53 +85,30 @@
         // acknowledged bytes.
         r.bytes_acked_sl += packet.size;
 
-        if r.bytes_acked_sl >= r.max_datagram_size {
+        if r.hystart.in_css(epoch) {
+            r.congestion_window += r.hystart.css_cwnd_inc(r.max_datagram_size);
+        } else {
             r.congestion_window += r.max_datagram_size;
-            r.bytes_acked_sl -= r.max_datagram_size;
         }
 
-        if r.hystart.enabled() &&
-            epoch == packet::EPOCH_APPLICATION &&
-            r.hystart.try_enter_lss(
-                packet,
-                r.latest_rtt,
-                r.congestion_window,
-                now,
-                r.max_datagram_size,
-            )
-        {
+        if r.hystart.on_packet_acked(epoch, packet, r.latest_rtt, now) {
+            // Exit to congestion avoidance if CSS ends.
             r.ssthresh = r.congestion_window;
         }
     } else {
         // Congestion avoidance.
-        let mut reno_cwnd = r.congestion_window;
-
         r.bytes_acked_ca += packet.size;
 
         if r.bytes_acked_ca >= r.congestion_window {
             r.bytes_acked_ca -= r.congestion_window;
-            reno_cwnd += r.max_datagram_size;
-        }
-
-        // When in Limited Slow Start, take the max of CA cwnd and
-        // LSS cwnd.
-        if r.hystart.in_lss(epoch) {
-            let lss_cwnd_inc = r.hystart.lss_cwnd_inc(
-                packet.size,
-                r.congestion_window,
-                r.ssthresh,
-            );
-
-            r.congestion_window =
-                cmp::max(reno_cwnd, r.congestion_window + lss_cwnd_inc);
-        } else {
-            r.congestion_window = reno_cwnd;
+            r.congestion_window += r.max_datagram_size;
         }
     }
 }
 
 fn congestion_event(
-    r: &mut Recovery, time_sent: Instant, epoch: packet::Epoch, now: Instant,
+    r: &mut Recovery, _lost_bytes: usize, time_sent: Instant,
+    epoch: packet::Epoch, now: Instant,
 ) {
     // Start a new congestion event if packet was sent after the
     // start of the previous congestion recovery period.
@@ -137,7 +129,7 @@
 
         r.ssthresh = r.congestion_window;
 
-        if r.hystart.in_lss(epoch) {
+        if r.hystart.in_css(epoch) {
             r.hystart.congestion_event();
         }
     }
@@ -147,20 +139,31 @@
     r.congestion_window = r.max_datagram_size * recovery::MINIMUM_WINDOW_PACKETS;
     r.bytes_acked_sl = 0;
     r.bytes_acked_ca = 0;
+
+    if r.hystart.enabled() {
+        r.hystart.reset();
+    }
 }
 
 fn checkpoint(_r: &mut Recovery) {}
 
-fn rollback(_r: &mut Recovery) {}
+fn rollback(_r: &mut Recovery) -> bool {
+    true
+}
 
 fn has_custom_pacing() -> bool {
     false
 }
 
+fn debug_fmt(_r: &Recovery, _f: &mut std::fmt::Formatter) -> std::fmt::Result {
+    Ok(())
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
 
+    use smallvec::smallvec;
     use std::time::Duration;
 
     #[test]
@@ -199,7 +202,7 @@
 
         let p = recovery::Sent {
             pkt_num: 0,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -208,7 +211,7 @@
             in_flight: true,
             delivered: 0,
             delivered_time: std::time::Instant::now(),
-            recent_delivered_packet_sent_time: std::time::Instant::now(),
+            first_sent_time: std::time::Instant::now(),
             is_app_limited: false,
             has_data: false,
         };
@@ -224,9 +227,14 @@
             pkt_num: p.pkt_num,
             time_sent: p.time_sent,
             size: p.size,
+            delivered: 0,
+            delivered_time: now,
+            first_sent_time: now,
+            is_app_limited: false,
+            rtt: Duration::ZERO,
         }];
 
-        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+        r.on_packets_acked(acked, packet::Epoch::Application, now);
 
         // Check if cwnd increased by packet size (slow start).
         assert_eq!(r.cwnd(), cwnd_prev + p.size);
@@ -243,7 +251,7 @@
 
         let p = recovery::Sent {
             pkt_num: 0,
-            frames: vec![],
+            frames: smallvec![],
             time_sent: now,
             time_acked: None,
             time_lost: None,
@@ -252,7 +260,7 @@
             in_flight: true,
             delivered: 0,
             delivered_time: std::time::Instant::now(),
-            recent_delivered_packet_sent_time: std::time::Instant::now(),
+            first_sent_time: std::time::Instant::now(),
             is_app_limited: false,
             has_data: false,
         };
@@ -269,20 +277,35 @@
                 pkt_num: p.pkt_num,
                 time_sent: p.time_sent,
                 size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             },
             Acked {
                 pkt_num: p.pkt_num,
                 time_sent: p.time_sent,
                 size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             },
             Acked {
                 pkt_num: p.pkt_num,
                 time_sent: p.time_sent,
                 size: p.size,
+                delivered: 0,
+                delivered_time: now,
+                first_sent_time: now,
+                is_app_limited: false,
+                rtt: Duration::ZERO,
             },
         ];
 
-        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now);
+        r.on_packets_acked(acked, packet::Epoch::Application, now);
 
         // Acked 3 packets.
         assert_eq!(r.cwnd(), cwnd_prev + p.size * 3);
@@ -299,7 +322,12 @@
 
         let now = Instant::now();
 
-        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+        r.congestion_event(
+            r.max_datagram_size,
+            now,
+            packet::Epoch::Application,
+            now,
+        );
 
         // In Reno, after congestion event, cwnd will be cut in half.
         assert_eq!(prev_cwnd / 2, r.cwnd());
@@ -318,7 +346,12 @@
         r.on_packet_sent_cc(20000, now);
 
         // Trigger congestion event to update ssthresh
-        r.congestion_event(now, packet::EPOCH_APPLICATION, now);
+        r.congestion_event(
+            r.max_datagram_size,
+            now,
+            packet::Epoch::Application,
+            now,
+        );
 
         // After congestion event, cwnd will be reduced.
         let cur_cwnd =
@@ -333,11 +366,16 @@
             time_sent: now + rtt,
             // More than cur_cwnd to increase cwnd
             size: 8000,
+            delivered: 0,
+            delivered_time: now,
+            first_sent_time: now,
+            is_app_limited: false,
+            rtt: Duration::ZERO,
         }];
 
         // Ack more than cwnd bytes with rtt=100ms
         r.update_rtt(rtt, Duration::from_millis(0), now);
-        r.on_packets_acked(acked, packet::EPOCH_APPLICATION, now + rtt * 2);
+        r.on_packets_acked(acked, packet::Epoch::Application, now + rtt * 2);
 
         // After acking more than cwnd, expect cwnd increased by MSS
         assert_eq!(r.cwnd(), cur_cwnd + r.max_datagram_size);
diff --git a/src/stream.rs b/src/stream.rs
index dfb9b0d..6e978bb 100644
--- a/src/stream.rs
+++ b/src/stream.rs
@@ -29,16 +29,20 @@
 use std::sync::Arc;
 
 use std::collections::hash_map;
-
 use std::collections::BTreeMap;
 use std::collections::BinaryHeap;
 use std::collections::HashMap;
 use std::collections::HashSet;
 use std::collections::VecDeque;
 
+use std::time;
+
+use smallvec::SmallVec;
+
 use crate::Error;
 use crate::Result;
 
+use crate::flowcontrol;
 use crate::ranges;
 
 const DEFAULT_URGENCY: u8 = 127;
@@ -49,18 +53,57 @@
 #[cfg(not(test))]
 const SEND_BUFFER_SIZE: usize = 4096;
 
+// The default size of the receiver stream flow control window.
+const DEFAULT_STREAM_WINDOW: u64 = 32 * 1024;
+
+/// The maximum size of the receiver stream flow control window.
+pub const MAX_STREAM_WINDOW: u64 = 16 * 1024 * 1024;
+
+/// A simple no-op hasher for Stream IDs.
+///
+/// The QUIC protocol and quiche library guarantees stream ID uniqueness, so
+/// we can save effort by avoiding using a more complicated algorithm.
+#[derive(Default)]
+pub struct StreamIdHasher {
+    id: u64,
+}
+
+impl std::hash::Hasher for StreamIdHasher {
+    #[inline]
+    fn finish(&self) -> u64 {
+        self.id
+    }
+
+    #[inline]
+    fn write_u64(&mut self, id: u64) {
+        self.id = id;
+    }
+
+    #[inline]
+    fn write(&mut self, _: &[u8]) {
+        // We need a default write() for the trait but stream IDs will always
+        // be a u64 so we just delegate to write_u64.
+        unimplemented!()
+    }
+}
+
+type BuildStreamIdHasher = std::hash::BuildHasherDefault<StreamIdHasher>;
+
+pub type StreamIdHashMap<V> = HashMap<u64, V, BuildStreamIdHasher>;
+pub type StreamIdHashSet = HashSet<u64, BuildStreamIdHasher>;
+
 /// Keeps track of QUIC streams and enforces stream limits.
 #[derive(Default)]
 pub struct StreamMap {
     /// Map of streams indexed by stream ID.
-    streams: HashMap<u64, Stream>,
+    streams: StreamIdHashMap<Stream>,
 
     /// Set of streams that were completed and garbage collected.
     ///
     /// Instead of keeping the full stream state forever, we collect completed
     /// streams to save memory, but we still need to keep track of previously
     /// created streams, to prevent peers from re-creating them.
-    collected: HashSet<u64>,
+    collected: StreamIdHashSet,
 
     /// Peer's maximum bidirectional stream count limit.
     peer_max_streams_bidi: u64,
@@ -104,38 +147,43 @@
     /// Set of stream IDs corresponding to streams that have outstanding data
     /// to read. This is used to generate a `StreamIter` of streams without
     /// having to iterate over the full list of streams.
-    readable: HashSet<u64>,
+    pub readable: StreamIdHashSet,
 
     /// Set of stream IDs corresponding to streams that have enough flow control
     /// capacity to be written to, and is not finished. This is used to generate
     /// a `StreamIter` of streams without having to iterate over the full list
     /// of streams.
-    writable: HashSet<u64>,
+    pub writable: StreamIdHashSet,
 
     /// Set of stream IDs corresponding to streams that are almost out of flow
     /// control credit and need to send MAX_STREAM_DATA. This is used to
     /// generate a `StreamIter` of streams without having to iterate over the
     /// full list of streams.
-    almost_full: HashSet<u64>,
+    almost_full: StreamIdHashSet,
 
     /// Set of stream IDs corresponding to streams that are blocked. The value
     /// of the map elements represents the offset of the stream at which the
     /// blocking occurred.
-    blocked: HashMap<u64, u64>,
+    blocked: StreamIdHashMap<u64>,
 
     /// Set of stream IDs corresponding to streams that are reset. The value
     /// of the map elements is a tuple of the error code and final size values
     /// to include in the RESET_STREAM frame.
-    reset: HashMap<u64, (u64, u64)>,
+    reset: StreamIdHashMap<(u64, u64)>,
 
     /// Set of stream IDs corresponding to streams that are shutdown on the
     /// receive side, and need to send a STOP_SENDING frame. The value of the
     /// map elements is the error code to include in the STOP_SENDING frame.
-    stopped: HashMap<u64, u64>,
+    stopped: StreamIdHashMap<u64>,
+
+    /// The maximum size of a stream window.
+    max_stream_window: u64,
 }
 
 impl StreamMap {
-    pub fn new(max_streams_bidi: u64, max_streams_uni: u64) -> StreamMap {
+    pub fn new(
+        max_streams_bidi: u64, max_streams_uni: u64, max_stream_window: u64,
+    ) -> StreamMap {
         StreamMap {
             local_max_streams_bidi: max_streams_bidi,
             local_max_streams_bidi_next: max_streams_bidi,
@@ -143,6 +191,8 @@
             local_max_streams_uni: max_streams_uni,
             local_max_streams_uni_next: max_streams_uni,
 
+            max_stream_window,
+
             ..StreamMap::default()
         }
     }
@@ -173,7 +223,7 @@
         &mut self, id: u64, local_params: &crate::TransportParams,
         peer_params: &crate::TransportParams, local: bool, is_server: bool,
     ) -> Result<&mut Stream> {
-        let stream = match self.streams.entry(id) {
+        let (stream, is_new_and_writable) = match self.streams.entry(id) {
             hash_map::Entry::Vacant(v) => {
                 // Stream has already been closed and garbage collected.
                 if self.collected.contains(&id) {
@@ -205,58 +255,85 @@
                         (local_params.initial_max_stream_data_uni, 0),
                 };
 
+                // The two least significant bits from a stream id identify the
+                // type of stream. Truncate those bits to get the sequence for
+                // that stream type.
+                let stream_sequence = id >> 2;
+
                 // Enforce stream count limits.
                 match (is_local(id, is_server), is_bidi(id)) {
                     (true, true) => {
-                        if self.local_opened_streams_bidi >=
-                            self.peer_max_streams_bidi
-                        {
+                        let n = std::cmp::max(
+                            self.local_opened_streams_bidi,
+                            stream_sequence + 1,
+                        );
+
+                        if n > self.peer_max_streams_bidi {
                             return Err(Error::StreamLimit);
                         }
 
-                        self.local_opened_streams_bidi += 1;
+                        self.local_opened_streams_bidi = n;
                     },
 
                     (true, false) => {
-                        if self.local_opened_streams_uni >=
-                            self.peer_max_streams_uni
-                        {
+                        let n = std::cmp::max(
+                            self.local_opened_streams_uni,
+                            stream_sequence + 1,
+                        );
+
+                        if n > self.peer_max_streams_uni {
                             return Err(Error::StreamLimit);
                         }
 
-                        self.local_opened_streams_uni += 1;
+                        self.local_opened_streams_uni = n;
                     },
 
                     (false, true) => {
-                        if self.peer_opened_streams_bidi >=
-                            self.local_max_streams_bidi
-                        {
+                        let n = std::cmp::max(
+                            self.peer_opened_streams_bidi,
+                            stream_sequence + 1,
+                        );
+
+                        if n > self.local_max_streams_bidi {
                             return Err(Error::StreamLimit);
                         }
 
-                        self.peer_opened_streams_bidi += 1;
+                        self.peer_opened_streams_bidi = n;
                     },
 
                     (false, false) => {
-                        if self.peer_opened_streams_uni >=
-                            self.local_max_streams_uni
-                        {
+                        let n = std::cmp::max(
+                            self.peer_opened_streams_uni,
+                            stream_sequence + 1,
+                        );
+
+                        if n > self.local_max_streams_uni {
                             return Err(Error::StreamLimit);
                         }
 
-                        self.peer_opened_streams_uni += 1;
+                        self.peer_opened_streams_uni = n;
                     },
                 };
 
-                let s = Stream::new(max_rx_data, max_tx_data, is_bidi(id), local);
-                v.insert(s)
+                let s = Stream::new(
+                    max_rx_data,
+                    max_tx_data,
+                    is_bidi(id),
+                    local,
+                    self.max_stream_window,
+                );
+
+                let is_writable = s.is_writable();
+
+                (v.insert(s), is_writable)
             },
 
-            hash_map::Entry::Occupied(v) => v.into_mut(),
+            hash_map::Entry::Occupied(v) => (v.into_mut(), false),
         };
 
-        // Stream might already be writable due to initial flow control limits.
-        if stream.is_writable() {
+        // Newly created stream might already be writable due to initial flow
+        // control limits.
+        if is_new_and_writable {
             self.writable.insert(id);
         }
 
@@ -289,41 +366,42 @@
         };
     }
 
-    /// Removes and returns the first stream ID from the flushable streams
-    /// queue with the specified urgency.
+    /// Returns the first stream ID from the flushable streams
+    /// queue with the highest urgency.
     ///
-    /// Note that if the stream is still flushable after sending some of its
-    /// outstanding data, it needs to be added back to the queue.
-    pub fn pop_flushable(&mut self) -> Option<u64> {
-        // Remove the first element from the queue corresponding to the lowest
-        // urgency that has elements.
-        let (node, clear) =
-            if let Some((urgency, queues)) = self.flushable.iter_mut().next() {
-                let node = if !queues.0.is_empty() {
-                    queues.0.pop().map(|x| x.0)
-                } else {
-                    queues.1.pop_front()
-                };
-
-                let clear = if queues.0.is_empty() && queues.1.is_empty() {
-                    Some(*urgency)
+    /// Note that if the stream is no longer flushable after sending some of its
+    /// outstanding data, it needs to be removed from the queue.
+    pub fn peek_flushable(&mut self) -> Option<u64> {
+        self.flushable.iter_mut().next().and_then(|(_, queues)| {
+            queues.0.peek().map(|x| x.0).or_else(|| {
+                // When peeking incremental streams, make sure to move the current
+                // stream to the end of the queue so they are pocesses in a round
+                // robin fashion
+                if let Some(current_incremental) = queues.1.pop_front() {
+                    queues.1.push_back(current_incremental);
+                    Some(current_incremental)
                 } else {
                     None
-                };
+                }
+            })
+        })
+    }
 
-                (node, clear)
-            } else {
-                (None, None)
-            };
+    /// Remove the last peeked stream
+    pub fn remove_flushable(&mut self) {
+        let mut top_urgency = self
+            .flushable
+            .first_entry()
+            .expect("Remove previously peeked stream");
 
+        let queues = top_urgency.get_mut();
+        queues.0.pop().map(|x| x.0).or_else(|| queues.1.pop_back());
         // Remove the queue from the list of queues if it is now empty, so that
         // the next time `pop_flushable()` is called the next queue with elements
         // is used.
-        if let Some(urgency) = &clear {
-            self.flushable.remove(urgency);
+        if queues.0.is_empty() && queues.1.is_empty() {
+            top_urgency.remove();
         }
-
-        node
     }
 
     /// Adds or removes the stream ID to/from the readable streams set.
@@ -417,6 +495,11 @@
         self.local_max_streams_bidi = self.local_max_streams_bidi_next;
     }
 
+    /// Returns the current max_streams_bidi limit.
+    pub fn max_streams_bidi(&self) -> u64 {
+        self.local_max_streams_bidi
+    }
+
     /// Returns the new max_streams_bidi limit.
     pub fn max_streams_bidi_next(&mut self) -> u64 {
         self.local_max_streams_bidi_next
@@ -461,6 +544,9 @@
             }
         }
 
+        self.mark_readable(stream_id, false);
+        self.mark_writable(stream_id, false);
+
         self.streams.remove(&stream_id);
         self.collected.insert(stream_id);
     }
@@ -495,6 +581,11 @@
         self.stopped.iter()
     }
 
+    /// Returns true if the stream has been collected.
+    pub fn is_collected(&self, stream_id: u64) -> bool {
+        self.collected.contains(&stream_id)
+    }
+
     /// Returns true if there are any streams that have data to write.
     pub fn has_flushable(&self) -> bool {
         !self.flushable.is_empty()
@@ -558,6 +649,8 @@
     /// Send-side stream buffer.
     pub send: SendBuf,
 
+    pub send_lowat: usize,
+
     /// Whether the stream is bidirectional.
     pub bidi: bool,
 
@@ -578,10 +671,12 @@
     /// Creates a new stream with the given flow control limits.
     pub fn new(
         max_rx_data: u64, max_tx_data: u64, bidi: bool, local: bool,
+        max_window: u64,
     ) -> Stream {
         Stream {
-            recv: RecvBuf::new(max_rx_data),
+            recv: RecvBuf::new(max_rx_data, max_window),
             send: SendBuf::new(max_tx_data),
+            send_lowat: 1,
             bidi,
             local,
             data: None,
@@ -600,7 +695,7 @@
     pub fn is_writable(&self) -> bool {
         !self.send.shutdown &&
             !self.send.is_fin() &&
-            self.send.off < self.send.max_data
+            (self.send.off + self.send_lowat as u64) < self.send.max_data
     }
 
     /// Returns true if the stream has data to send and is allowed to send at
@@ -633,6 +728,11 @@
             (false, false) => self.recv.is_fin(),
         }
     }
+
+    /// Returns true if the stream is not storing incoming data.
+    pub fn is_draining(&self) -> bool {
+        self.recv.drain
+    }
 }
 
 /// Returns true if the stream was created locally.
@@ -648,12 +748,12 @@
 /// An iterator over QUIC streams.
 #[derive(Default)]
 pub struct StreamIter {
-    streams: Vec<u64>,
+    streams: SmallVec<[u64; 8]>,
 }
 
 impl StreamIter {
     #[inline]
-    fn from(streams: &HashSet<u64>) -> Self {
+    fn from(streams: &StreamIdHashSet) -> Self {
         StreamIter {
             streams: streams.iter().copied().collect(),
         }
@@ -685,7 +785,7 @@
 pub struct RecvBuf {
     /// Chunks of data received from the peer that have not yet been read by
     /// the application, ordered by offset.
-    data: BinaryHeap<RangeBuf>,
+    data: BTreeMap<u64, RangeBuf>,
 
     /// The lowest data offset that has yet to be read by the application.
     off: u64,
@@ -693,25 +793,28 @@
     /// The total length of data received on this stream.
     len: u64,
 
-    /// The maximum offset the peer is allowed to send us.
-    max_data: u64,
-
-    /// The updated maximum offset the peer is allowed to send us.
-    max_data_next: u64,
+    /// Receiver flow controller.
+    flow_control: flowcontrol::FlowControl,
 
     /// The final stream offset received from the peer, if any.
     fin_off: Option<u64>,
 
+    /// The error code received via RESET_STREAM.
+    error: Option<u64>,
+
     /// Whether incoming data is validated but not buffered.
     drain: bool,
 }
 
 impl RecvBuf {
     /// Creates a new receive buffer.
-    fn new(max_data: u64) -> RecvBuf {
+    fn new(max_data: u64, max_window: u64) -> RecvBuf {
         RecvBuf {
-            max_data,
-            max_data_next: max_data,
+            flow_control: flowcontrol::FlowControl::new(
+                max_data,
+                cmp::min(max_data, DEFAULT_STREAM_WINDOW),
+                max_window,
+            ),
             ..RecvBuf::default()
         }
     }
@@ -722,7 +825,7 @@
     /// as handling incoming data that overlaps data that is already in the
     /// buffer.
     pub fn write(&mut self, buf: RangeBuf) -> Result<()> {
-        if buf.max_off() > self.max_data {
+        if buf.max_off() > self.max_data() {
             return Err(Error::FlowControl);
         }
 
@@ -749,12 +852,6 @@
             return Ok(());
         }
 
-        // No need to process an empty buffer with the fin flag, if we already
-        // know the final size.
-        if buf.fin() && buf.is_empty() && self.fin_off.is_some() {
-            return Ok(());
-        }
-
         if buf.fin() {
             self.fin_off = Some(buf.max_off());
         }
@@ -777,41 +874,56 @@
             }
         }
 
-        let mut tmp_buf = Some(buf);
+        let mut tmp_bufs = VecDeque::with_capacity(2);
+        tmp_bufs.push_back(buf);
 
-        while let Some(mut buf) = tmp_buf {
-            tmp_buf = None;
-
+        'tmp: while let Some(mut buf) = tmp_bufs.pop_front() {
             // Discard incoming data below current stream offset. Bytes up to
             // `self.off` have already been received so we should not buffer
             // them again. This is also important to make sure `ready()` doesn't
             // get stuck when a buffer with lower offset than the stream's is
             // buffered.
-            if self.off > buf.off() {
-                buf = buf.split_off((self.off - buf.off()) as usize);
+            if self.off_front() > buf.off() {
+                buf = buf.split_off((self.off_front() - buf.off()) as usize);
             }
 
-            for b in &self.data {
-                // New buffer is fully contained in existing buffer.
-                if buf.off() >= b.off() && buf.max_off() <= b.max_off() {
-                    return Ok(());
-                }
+            // Handle overlapping data. If the incoming data's starting offset
+            // is above the previous maximum received offset, there is clearly
+            // no overlap so this logic can be skipped. However do still try to
+            // merge an empty final buffer (i.e. an empty buffer with the fin
+            // flag set, which is the only kind of empty buffer that should
+            // reach this point).
+            if buf.off() < self.max_off() || buf.is_empty() {
+                for (_, b) in self.data.range(buf.off()..) {
+                    let off = buf.off();
 
-                // New buffer's start overlaps existing buffer.
-                if buf.off() >= b.off() && buf.off() < b.max_off() {
-                    buf = buf.split_off((b.max_off() - buf.off()) as usize);
-                }
+                    // We are past the current buffer.
+                    if b.off() > buf.max_off() {
+                        break;
+                    }
 
-                // New buffer's end overlaps existing buffer.
-                if buf.off() < b.off() && buf.max_off() > b.off() {
-                    tmp_buf = Some(buf.split_off((b.off() - buf.off()) as usize));
+                    // New buffer is fully contained in existing buffer.
+                    if off >= b.off() && buf.max_off() <= b.max_off() {
+                        continue 'tmp;
+                    }
+
+                    // New buffer's start overlaps existing buffer.
+                    if off >= b.off() && off < b.max_off() {
+                        buf = buf.split_off((b.max_off() - off) as usize);
+                    }
+
+                    // New buffer's end overlaps existing buffer.
+                    if off < b.off() && buf.max_off() > b.off() {
+                        tmp_bufs
+                            .push_back(buf.split_off((b.off() - off) as usize));
+                    }
                 }
             }
 
             self.len = cmp::max(self.len, buf.max_off());
 
             if !self.drain {
-                self.data.push(buf);
+                self.data.insert(buf.max_off(), buf);
             }
         }
 
@@ -835,13 +947,19 @@
             return Err(Error::Done);
         }
 
-        while cap > 0 && self.ready() {
-            let mut buf = match self.data.peek_mut() {
-                Some(v) => v,
+        // The stream was reset, so return the error code instead.
+        if let Some(e) = self.error {
+            return Err(Error::StreamReset(e));
+        }
 
+        while cap > 0 && self.ready() {
+            let mut entry = match self.data.first_entry() {
+                Some(entry) => entry,
                 None => break,
             };
 
+            let buf = entry.get_mut();
+
             let buf_len = cmp::min(buf.len(), cap);
 
             out[len..len + buf_len].copy_from_slice(&buf[..buf_len]);
@@ -858,16 +976,17 @@
                 break;
             }
 
-            std::collections::binary_heap::PeekMut::pop(buf);
+            entry.remove();
         }
 
-        self.max_data_next = self.max_data_next.saturating_add(len as u64);
+        // Update consumed bytes for flow control.
+        self.flow_control.add_consumed(len as u64);
 
         Ok((len, self.is_fin()))
     }
 
     /// Resets the stream at the given offset.
-    pub fn reset(&mut self, final_size: u64) -> Result<usize> {
+    pub fn reset(&mut self, error_code: u64, final_size: u64) -> Result<usize> {
         // Stream's size is already known, forbid changing it.
         if let Some(fin_off) = self.fin_off {
             if fin_off != final_size {
@@ -880,21 +999,52 @@
             return Err(Error::FinalSize);
         }
 
-        self.fin_off = Some(final_size);
-
-        // Return how many bytes need to be removed from the connection flow
+        // Calculate how many bytes need to be removed from the connection flow
         // control.
-        Ok((final_size - self.len) as usize)
+        let max_data_delta = final_size - self.len;
+
+        if self.error.is_some() {
+            return Ok(max_data_delta as usize);
+        }
+
+        self.error = Some(error_code);
+
+        // Clear all data already buffered.
+        self.off = final_size;
+
+        self.data.clear();
+
+        // In order to ensure the application is notified when the stream is
+        // reset, enqueue a zero-length buffer at the final size offset.
+        let buf = RangeBuf::from(b"", final_size, true);
+        self.write(buf)?;
+
+        Ok(max_data_delta as usize)
     }
 
     /// Commits the new max_data limit.
-    pub fn update_max_data(&mut self) {
-        self.max_data = self.max_data_next;
+    pub fn update_max_data(&mut self, now: time::Instant) {
+        self.flow_control.update_max_data(now);
     }
 
     /// Return the new max_data limit.
     pub fn max_data_next(&mut self) -> u64 {
-        self.max_data_next
+        self.flow_control.max_data_next()
+    }
+
+    /// Return the current flow control limit.
+    fn max_data(&self) -> u64 {
+        self.flow_control.max_data()
+    }
+
+    /// Return the current window.
+    pub fn window(&self) -> u64 {
+        self.flow_control.window()
+    }
+
+    /// Autotune the window size.
+    pub fn autotune_window(&mut self, now: time::Instant, rtt: time::Duration) {
+        self.flow_control.autotune_window(now, rtt);
     }
 
     /// Shuts down receiving data.
@@ -913,18 +1063,13 @@
     }
 
     /// Returns the lowest offset of data buffered.
-    #[allow(dead_code)]
     pub fn off_front(&self) -> u64 {
         self.off
     }
 
     /// Returns true if we need to update the local flow control limit.
     pub fn almost_full(&self) -> bool {
-        // Send MAX_STREAM_DATA when the new limit is at least double the
-        // amount of data that can be received before blocking.
-        self.fin_off.is_none() &&
-            self.max_data_next != self.max_data &&
-            self.max_data_next / 2 > self.max_data - self.len
+        self.fin_off.is_none() && self.flow_control.should_update_max_data()
     }
 
     /// Returns the largest offset ever received.
@@ -946,9 +1091,8 @@
 
     /// Returns true if the stream has data to be read.
     fn ready(&self) -> bool {
-        let buf = match self.data.peek() {
+        let (_, buf) = match self.data.first_key_value() {
             Some(v) => v,
-
             None => return false,
         };
 
@@ -976,12 +1120,19 @@
     /// The maximum offset of data buffered in the stream.
     off: u64,
 
+    /// The maximum offset of data sent to the peer, regardless of
+    /// retransmissions.
+    emit_off: u64,
+
     /// The amount of data currently buffered.
     len: u64,
 
     /// The maximum offset we are allowed to send to the peer.
     max_data: u64,
 
+    /// The last offset the stream was blocked at, if any.
+    blocked_at: Option<u64>,
+
     /// The final stream offset written to the stream, if any.
     fin_off: Option<u64>,
 
@@ -1097,11 +1248,11 @@
             }
 
             let buf_len = cmp::min(buf.len(), out_len);
+            let partial = buf_len < buf.len();
 
             // Copy data to the output buffer.
             let out_pos = (next_off - out_off) as usize;
-            (&mut out[out_pos..out_pos + buf_len])
-                .copy_from_slice(&buf[..buf_len]);
+            out[out_pos..out_pos + buf_len].copy_from_slice(&buf[..buf_len]);
 
             self.len -= buf_len as u64;
 
@@ -1109,15 +1260,13 @@
 
             next_off = buf.off() + buf_len as u64;
 
-            if !buf.is_empty() && buf_len < buf.len() {
-                buf.consume(buf_len);
+            buf.consume(buf_len);
 
+            if partial {
                 // We reached the maximum capacity, so end here.
                 break;
             }
 
-            buf.consume(buf_len);
-
             self.pos += 1;
         }
 
@@ -1129,6 +1278,10 @@
         // propagate the final size.
         let fin = self.fin_off == Some(next_off);
 
+        // Record the largest offset that has been sent so we can accurately
+        // report final_size
+        self.emit_off = cmp::max(self.emit_off, next_off);
+
         Ok((out.len() - out_len, fin))
     }
 
@@ -1137,6 +1290,16 @@
         self.max_data = cmp::max(self.max_data, max_data);
     }
 
+    /// Updates the last offset the stream was blocked at, if any.
+    pub fn update_blocked_at(&mut self, blocked_at: Option<u64>) {
+        self.blocked_at = blocked_at;
+    }
+
+    /// The last offset the stream was blocked at, if any.
+    pub fn blocked_at(&self) -> Option<u64> {
+        self.blocked_at
+    }
+
     /// Increments the acked data offset.
     pub fn ack(&mut self, off: u64, len: usize) {
         self.acked.insert(off..off + len as u64);
@@ -1211,13 +1374,15 @@
             // Split the buffer into 2 if the retransmit range ends before the
             // buffer's final offset.
             let new_buf = if buf.off < max_off && max_off < buf.max_off() {
-                Some(buf.split_off((max_off - buf.off as u64) as usize))
+                Some(buf.split_off((max_off - buf.off) as usize))
             } else {
                 None
             };
 
-            // Advance the buffer's position if the retransmit range is past
-            // the buffer's starting offset.
+            let prev_pos = buf.pos;
+
+            // Reduce the buffer's position (expand the buffer) if the retransmit
+            // range is past the buffer's starting offset.
             buf.pos = if off > buf.off && off <= buf.max_off() {
                 cmp::min(buf.pos, buf.start + (off - buf.off) as usize)
             } else {
@@ -1226,7 +1391,7 @@
 
             self.pos = cmp::min(self.pos, i);
 
-            self.len += buf.len() as u64;
+            self.len += (prev_pos - buf.pos) as u64;
 
             if let Some(b) = new_buf {
                 self.data.insert(i + 1, b);
@@ -1235,8 +1400,11 @@
     }
 
     /// Resets the stream at the current offset and clears all buffered data.
-    pub fn reset(&mut self) -> Result<u64> {
-        self.write(b"", true)?;
+    pub fn reset(&mut self) -> (u64, u64) {
+        let unsent_off = cmp::max(self.off_front(), self.emit_off);
+        let unsent_len = self.off_back().saturating_sub(unsent_off);
+
+        self.fin_off = Some(unsent_off);
 
         // Drop all buffered data.
         self.data.clear();
@@ -1246,38 +1414,38 @@
 
         self.pos = 0;
         self.len = 0;
+        self.off = unsent_off;
 
-        Ok(self.fin_off.unwrap())
+        (self.emit_off, unsent_len)
     }
 
     /// Resets the streams and records the received error code.
     ///
     /// Calling this again after the first time has no effect.
-    pub fn stop(&mut self, error_code: u64) -> Result<u64> {
+    pub fn stop(&mut self, error_code: u64) -> Result<(u64, u64)> {
         if self.error.is_some() {
             return Err(Error::Done);
         }
 
-        let fin_off = self.reset()?;
+        let (max_off, unsent) = self.reset();
 
         self.error = Some(error_code);
 
-        Ok(fin_off)
+        Ok((max_off, unsent))
     }
 
     /// Shuts down sending data.
-    pub fn shutdown(&mut self) -> Result<u64> {
+    pub fn shutdown(&mut self) -> Result<(u64, u64)> {
         if self.shutdown {
             return Err(Error::Done);
         }
 
         self.shutdown = true;
 
-        self.reset()
+        Ok(self.reset())
     }
 
     /// Returns the largest offset of data buffered.
-    #[allow(dead_code)]
     pub fn off_back(&self) -> u64 {
         self.off
     }
@@ -1305,7 +1473,7 @@
 
     /// Returns true if all data in the stream has been sent.
     ///
-    /// This happens when the stream's send final size is knwon, and the
+    /// This happens when the stream's send final size is known, and the
     /// application has already written data up to that point.
     pub fn is_fin(&self) -> bool {
         if self.fin_off == Some(self.off) {
@@ -1379,7 +1547,7 @@
 pub struct RangeBuf {
     /// The internal buffer holding the data.
     ///
-    /// To avoid neeless allocations when a RangeBuf is split, this field is
+    /// To avoid needless allocations when a RangeBuf is split, this field is
     /// reference-counted and can be shared between multiple RangeBuf objects,
     /// and sliced using the `start` and `len` values.
     data: Arc<Vec<u8>>,
@@ -1445,12 +1613,12 @@
 
     /// Splits the buffer into two at the given index.
     pub fn split_off(&mut self, at: usize) -> RangeBuf {
-        if at > self.len {
-            panic!(
-                "`at` split index (is {}) should be <= len (is {})",
-                at, self.len
-            );
-        }
+        assert!(
+            at <= self.len,
+            "`at` split index (is {}) should be <= len (is {})",
+            at,
+            self.len
+        );
 
         let buf = RangeBuf {
             data: self.data.clone(),
@@ -1502,7 +1670,7 @@
 
     #[test]
     fn empty_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1512,7 +1680,7 @@
 
     #[test]
     fn empty_stream_frame() {
-        let mut recv = RecvBuf::new(15);
+        let mut recv = RecvBuf::new(15, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let buf = RangeBuf::from(b"hello", 0, false);
@@ -1568,7 +1736,7 @@
 
     #[test]
     fn ordered_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1605,7 +1773,7 @@
 
     #[test]
     fn split_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1645,7 +1813,7 @@
 
     #[test]
     fn incomplete_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1673,7 +1841,7 @@
 
     #[test]
     fn zero_len_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1701,7 +1869,7 @@
 
     #[test]
     fn past_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1740,7 +1908,7 @@
 
     #[test]
     fn fully_overlapping_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1771,7 +1939,7 @@
 
     #[test]
     fn fully_overlapping_read2() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1802,7 +1970,7 @@
 
     #[test]
     fn fully_overlapping_read3() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1833,7 +2001,7 @@
 
     #[test]
     fn fully_overlapping_read_multi() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1870,7 +2038,7 @@
 
     #[test]
     fn overlapping_start_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1900,7 +2068,7 @@
 
     #[test]
     fn overlapping_end_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1929,8 +2097,92 @@
     }
 
     #[test]
+    fn overlapping_end_twice_read() {
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"he", 0, false);
+        let second = RangeBuf::from(b"ow", 4, false);
+        let third = RangeBuf::from(b"rl", 7, false);
+        let fourth = RangeBuf::from(b"helloworld", 0, true);
+
+        assert!(recv.write(third).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.write(second).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 2);
+
+        assert!(recv.write(first).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 3);
+
+        assert!(recv.write(fourth).is_ok());
+        assert_eq!(recv.len, 10);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 6);
+
+        let (len, fin) = recv.emit(&mut buf).unwrap();
+        assert_eq!(len, 10);
+        assert_eq!(fin, true);
+        assert_eq!(&buf[..len], b"helloworld");
+        assert_eq!(recv.len, 10);
+        assert_eq!(recv.off, 10);
+
+        assert_eq!(recv.emit(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
+    fn overlapping_end_twice_and_contained_read() {
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
+        assert_eq!(recv.len, 0);
+
+        let mut buf = [0; 32];
+
+        let first = RangeBuf::from(b"hellow", 0, false);
+        let second = RangeBuf::from(b"barfoo", 10, true);
+        let third = RangeBuf::from(b"rl", 7, false);
+        let fourth = RangeBuf::from(b"elloworldbarfoo", 1, true);
+
+        assert!(recv.write(third).is_ok());
+        assert_eq!(recv.len, 9);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 1);
+
+        assert!(recv.write(second).is_ok());
+        assert_eq!(recv.len, 16);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 2);
+
+        assert!(recv.write(first).is_ok());
+        assert_eq!(recv.len, 16);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 3);
+
+        assert!(recv.write(fourth).is_ok());
+        assert_eq!(recv.len, 16);
+        assert_eq!(recv.off, 0);
+        assert_eq!(recv.data.len(), 5);
+
+        let (len, fin) = recv.emit(&mut buf).unwrap();
+        assert_eq!(len, 16);
+        assert_eq!(fin, true);
+        assert_eq!(&buf[..len], b"helloworldbarfoo");
+        assert_eq!(recv.len, 16);
+        assert_eq!(recv.off, 16);
+
+        assert_eq!(recv.emit(&mut buf), Err(Error::Done));
+    }
+
+    #[test]
     fn partially_multi_overlapping_reordered_read() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -1967,7 +2219,7 @@
 
     #[test]
     fn partially_multi_overlapping_reordered_read2() {
-        let mut recv = RecvBuf::new(std::u64::MAX);
+        let mut recv = RecvBuf::new(u64::MAX, DEFAULT_STREAM_WINDOW);
         assert_eq!(recv.len, 0);
 
         let mut buf = [0; 32];
@@ -2024,7 +2276,7 @@
     fn empty_write() {
         let mut buf = [0; 5];
 
-        let mut send = SendBuf::new(std::u64::MAX);
+        let mut send = SendBuf::new(u64::MAX);
         assert_eq!(send.len, 0);
 
         let (written, fin) = send.emit(&mut buf).unwrap();
@@ -2036,7 +2288,7 @@
     fn multi_write() {
         let mut buf = [0; 128];
 
-        let mut send = SendBuf::new(std::u64::MAX);
+        let mut send = SendBuf::new(u64::MAX);
         assert_eq!(send.len, 0);
 
         let first = b"something";
@@ -2059,7 +2311,7 @@
     fn split_write() {
         let mut buf = [0; 10];
 
-        let mut send = SendBuf::new(std::u64::MAX);
+        let mut send = SendBuf::new(u64::MAX);
         assert_eq!(send.len, 0);
 
         let first = b"something";
@@ -2102,7 +2354,7 @@
     fn resend() {
         let mut buf = [0; 15];
 
-        let mut send = SendBuf::new(std::u64::MAX);
+        let mut send = SendBuf::new(u64::MAX);
         assert_eq!(send.len, 0);
         assert_eq!(send.off_front(), 0);
 
@@ -2235,7 +2487,7 @@
     fn zero_len_write() {
         let mut buf = [0; 10];
 
-        let mut send = SendBuf::new(std::u64::MAX);
+        let mut send = SendBuf::new(u64::MAX);
         assert_eq!(send.len, 0);
 
         let first = b"something";
@@ -2257,7 +2509,7 @@
 
     #[test]
     fn recv_flow_control() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let mut buf = [0; 32];
@@ -2278,7 +2530,7 @@
 
         assert!(stream.recv.almost_full());
 
-        stream.recv.update_max_data();
+        stream.recv.update_max_data(time::Instant::now());
         assert_eq!(stream.recv.max_data_next(), 25);
         assert!(!stream.recv.almost_full());
 
@@ -2288,7 +2540,7 @@
 
     #[test]
     fn recv_past_fin() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let first = RangeBuf::from(b"hello", 0, true);
@@ -2300,7 +2552,7 @@
 
     #[test]
     fn recv_fin_dup() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let first = RangeBuf::from(b"hello", 0, true);
@@ -2318,7 +2570,7 @@
 
     #[test]
     fn recv_fin_change() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let first = RangeBuf::from(b"hello", 0, true);
@@ -2330,7 +2582,7 @@
 
     #[test]
     fn recv_fin_lower_than_received() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let first = RangeBuf::from(b"hello", 0, true);
@@ -2342,7 +2594,7 @@
 
     #[test]
     fn recv_fin_flow_control() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let mut buf = [0; 32];
@@ -2362,55 +2614,55 @@
 
     #[test]
     fn recv_fin_reset_mismatch() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let first = RangeBuf::from(b"hello", 0, true);
 
         assert_eq!(stream.recv.write(first), Ok(()));
-        assert_eq!(stream.recv.reset(10), Err(Error::FinalSize));
+        assert_eq!(stream.recv.reset(0, 10), Err(Error::FinalSize));
     }
 
     #[test]
     fn recv_reset_dup() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let first = RangeBuf::from(b"hello", 0, false);
 
         assert_eq!(stream.recv.write(first), Ok(()));
-        assert_eq!(stream.recv.reset(5), Ok(0));
-        assert_eq!(stream.recv.reset(5), Ok(0));
+        assert_eq!(stream.recv.reset(0, 5), Ok(0));
+        assert_eq!(stream.recv.reset(0, 5), Ok(0));
     }
 
     #[test]
     fn recv_reset_change() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let first = RangeBuf::from(b"hello", 0, false);
 
         assert_eq!(stream.recv.write(first), Ok(()));
-        assert_eq!(stream.recv.reset(5), Ok(0));
-        assert_eq!(stream.recv.reset(10), Err(Error::FinalSize));
+        assert_eq!(stream.recv.reset(0, 5), Ok(0));
+        assert_eq!(stream.recv.reset(0, 10), Err(Error::FinalSize));
     }
 
     #[test]
     fn recv_reset_lower_than_received() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
         assert!(!stream.recv.almost_full());
 
         let first = RangeBuf::from(b"hello", 0, false);
 
         assert_eq!(stream.recv.write(first), Ok(()));
-        assert_eq!(stream.recv.reset(4), Err(Error::FinalSize));
+        assert_eq!(stream.recv.reset(0, 4), Err(Error::FinalSize));
     }
 
     #[test]
     fn send_flow_control() {
         let mut buf = [0; 25];
 
-        let mut stream = Stream::new(0, 15, true, true);
+        let mut stream = Stream::new(0, 15, true, true, DEFAULT_STREAM_WINDOW);
 
         let first = b"hello";
         let second = b"world";
@@ -2453,7 +2705,7 @@
 
     #[test]
     fn send_past_fin() {
-        let mut stream = Stream::new(0, 15, true, true);
+        let mut stream = Stream::new(0, 15, true, true, DEFAULT_STREAM_WINDOW);
 
         let first = b"hello";
         let second = b"world";
@@ -2469,7 +2721,7 @@
 
     #[test]
     fn send_fin_dup() {
-        let mut stream = Stream::new(0, 15, true, true);
+        let mut stream = Stream::new(0, 15, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", true), Ok(5));
         assert!(stream.send.is_fin());
@@ -2480,7 +2732,7 @@
 
     #[test]
     fn send_undo_fin() {
-        let mut stream = Stream::new(0, 15, true, true);
+        let mut stream = Stream::new(0, 15, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", true), Ok(5));
         assert!(stream.send.is_fin());
@@ -2495,7 +2747,7 @@
     fn send_fin_max_data_match() {
         let mut buf = [0; 15];
 
-        let mut stream = Stream::new(0, 15, true, true);
+        let mut stream = Stream::new(0, 15, true, true, DEFAULT_STREAM_WINDOW);
 
         let slice = b"hellohellohello";
 
@@ -2511,7 +2763,7 @@
     fn send_fin_zero_length() {
         let mut buf = [0; 5];
 
-        let mut stream = Stream::new(0, 15, true, true);
+        let mut stream = Stream::new(0, 15, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", false), Ok(5));
         assert_eq!(stream.send.write(b"", true), Ok(0));
@@ -2527,7 +2779,7 @@
     fn send_ack() {
         let mut buf = [0; 5];
 
-        let mut stream = Stream::new(0, 15, true, true);
+        let mut stream = Stream::new(0, 15, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", false), Ok(5));
         assert_eq!(stream.send.write(b"world", false), Ok(5));
@@ -2557,7 +2809,7 @@
     fn send_ack_reordering() {
         let mut buf = [0; 5];
 
-        let mut stream = Stream::new(0, 15, true, true);
+        let mut stream = Stream::new(0, 15, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", false), Ok(5));
         assert_eq!(stream.send.write(b"world", false), Ok(5));
@@ -2594,7 +2846,7 @@
 
     #[test]
     fn recv_data_below_off() {
-        let mut stream = Stream::new(15, 0, true, true);
+        let mut stream = Stream::new(15, 0, true, true, DEFAULT_STREAM_WINDOW);
 
         let first = RangeBuf::from(b"hello", 0, false);
 
@@ -2616,7 +2868,7 @@
 
     #[test]
     fn stream_complete() {
-        let mut stream = Stream::new(30, 30, true, true);
+        let mut stream = Stream::new(30, 30, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", false), Ok(5));
         assert_eq!(stream.send.write(b"world", false), Ok(5));
@@ -2659,7 +2911,7 @@
     fn send_fin_zero_length_output() {
         let mut buf = [0; 5];
 
-        let mut stream = Stream::new(0, 15, true, true);
+        let mut stream = Stream::new(0, 15, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", false), Ok(5));
         assert_eq!(stream.send.off_front(), 0);
@@ -2684,7 +2936,7 @@
     fn send_emit() {
         let mut buf = [0; 5];
 
-        let mut stream = Stream::new(0, 20, true, true);
+        let mut stream = Stream::new(0, 20, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", false), Ok(5));
         assert_eq!(stream.send.write(b"world", false), Ok(5));
@@ -2736,7 +2988,7 @@
     fn send_emit_ack() {
         let mut buf = [0; 5];
 
-        let mut stream = Stream::new(0, 20, true, true);
+        let mut stream = Stream::new(0, 20, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", false), Ok(5));
         assert_eq!(stream.send.write(b"world", false), Ok(5));
@@ -2803,7 +3055,7 @@
     fn send_emit_retransmit() {
         let mut buf = [0; 5];
 
-        let mut stream = Stream::new(0, 20, true, true);
+        let mut stream = Stream::new(0, 20, true, true, DEFAULT_STREAM_WINDOW);
 
         assert_eq!(stream.send.write(b"hello", false), Ok(5));
         assert_eq!(stream.send.write(b"world", false), Ok(5));
@@ -3025,4 +3277,120 @@
 
         assert_eq!(&new_new_buf[..], b"");
     }
+
+    /// RFC9000 2.1: A stream ID that is used out of order results in all
+    /// streams of that type with lower-numbered stream IDs also being opened.
+    #[test]
+    fn stream_limit_auto_open() {
+        let local_tp = crate::TransportParams::default();
+        let peer_tp = crate::TransportParams::default();
+
+        let mut streams = StreamMap::new(5, 5, 5);
+
+        let stream_id = 500;
+        assert!(!is_local(stream_id, true), "stream id is peer initiated");
+        assert!(is_bidi(stream_id), "stream id is bidirectional");
+        assert_eq!(
+            streams
+                .get_or_create(stream_id, &local_tp, &peer_tp, false, true)
+                .err(),
+            Some(Error::StreamLimit),
+            "stream limit should be exceeded"
+        );
+    }
+
+    /// Stream limit should be satisfied regardless of what order we open
+    /// streams
+    #[test]
+    fn stream_create_out_of_order() {
+        let local_tp = crate::TransportParams::default();
+        let peer_tp = crate::TransportParams::default();
+
+        let mut streams = StreamMap::new(5, 5, 5);
+
+        for stream_id in [8, 12, 4] {
+            assert!(is_local(stream_id, false), "stream id is client initiated");
+            assert!(is_bidi(stream_id), "stream id is bidirectional");
+            assert!(streams
+                .get_or_create(stream_id, &local_tp, &peer_tp, false, true)
+                .is_ok());
+        }
+    }
+
+    /// Check stream limit boundary cases
+    #[test]
+    fn stream_limit_edge() {
+        let local_tp = crate::TransportParams::default();
+        let peer_tp = crate::TransportParams::default();
+
+        let mut streams = StreamMap::new(3, 3, 3);
+
+        // Highest permitted
+        let stream_id = 8;
+        assert!(streams
+            .get_or_create(stream_id, &local_tp, &peer_tp, false, true)
+            .is_ok());
+
+        // One more than highest permitted
+        let stream_id = 12;
+        assert_eq!(
+            streams
+                .get_or_create(stream_id, &local_tp, &peer_tp, false, true)
+                .err(),
+            Some(Error::StreamLimit)
+        );
+    }
+
+    /// Check SendBuf::len calculation on a retransmit case
+    #[test]
+    fn send_buf_len_on_retransmit() {
+        let mut buf = [0; 15];
+
+        let mut send = SendBuf::new(u64::MAX);
+        assert_eq!(send.len, 0);
+        assert_eq!(send.off_front(), 0);
+
+        let first = b"something";
+
+        assert!(send.write(first, false).is_ok());
+        assert_eq!(send.off_front(), 0);
+
+        assert_eq!(send.len, 9);
+
+        let (written, fin) = send.emit(&mut buf[..4]).unwrap();
+        assert_eq!(written, 4);
+        assert_eq!(fin, false);
+        assert_eq!(&buf[..written], b"some");
+        assert_eq!(send.len, 5);
+        assert_eq!(send.off_front(), 4);
+
+        send.retransmit(3, 5);
+        assert_eq!(send.len, 6);
+        assert_eq!(send.off_front(), 3);
+    }
+
+    #[test]
+    fn send_buf_final_size_retransmit() {
+        let mut buf = [0; 50];
+        let mut send = SendBuf::new(u64::MAX);
+
+        send.write(&buf, false).unwrap();
+        assert_eq!(send.off_front(), 0);
+
+        // Emit the whole buffer
+        let (written, _fin) = send.emit(&mut buf).unwrap();
+        assert_eq!(written, buf.len());
+        assert_eq!(send.off_front(), buf.len() as u64);
+
+        // Server decides to retransmit the last 10 bytes. It's possible
+        // it's not actually lost and that the client did receive it.
+        send.retransmit(40, 10);
+
+        // Server receives STOP_SENDING from client. The final_size we
+        // send in the RESET_STREAM should be 50. If we send anything less,
+        // it's a FINAL_SIZE_ERROR.
+        let (fin_off, unsent) = send.stop(0).unwrap();
+        assert_eq!(fin_off, 50);
+        assert_eq!(unsent, 0);
+    }
 }
diff --git a/src/tls.rs b/src/tls.rs
index ed7a5bf..e6e7ddb 100644
--- a/src/tls.rs
+++ b/src/tls.rs
@@ -43,11 +43,11 @@
 use crate::ConnectionError;
 
 use crate::crypto;
-use crate::octets;
 use crate::packet;
 
 const TLS1_3_VERSION: u16 = 0x0304;
 const TLS_ALERT_ERROR: u64 = 0x100;
+const INTERNAL_ERROR: u64 = 0x01;
 
 #[allow(non_camel_case_types)]
 #[repr(transparent)]
@@ -123,8 +123,50 @@
         extern fn(ssl: *mut SSL, level: crypto::Level, alert: u8) -> c_int,
 }
 
+#[cfg(test)]
+#[repr(C)]
+#[allow(non_camel_case_types)]
+#[allow(dead_code)]
+enum ssl_private_key_result_t {
+    ssl_private_key_success,
+    ssl_private_key_retry,
+    ssl_private_key_failure,
+}
+
+#[cfg(test)]
+#[repr(C)]
+#[allow(non_camel_case_types)]
+struct SSL_PRIVATE_KEY_METHOD {
+    sign: extern fn(
+        ssl: *mut SSL,
+        out: *mut u8,
+        out_len: *mut usize,
+        max_out: usize,
+        signature_algorithm: u16,
+        r#in: *const u8,
+        in_len: usize,
+    ) -> ssl_private_key_result_t,
+
+    decrypt: extern fn(
+        ssl: *mut SSL,
+        out: *mut u8,
+        out_len: *mut usize,
+        max_out: usize,
+        r#in: *const u8,
+        in_len: usize,
+    ) -> ssl_private_key_result_t,
+
+    complete: extern fn(
+        ssl: *mut SSL,
+        out: *mut u8,
+        out_len: *mut usize,
+        max_out: usize,
+    ) -> ssl_private_key_result_t,
+}
+
 lazy_static::lazy_static! {
-    static ref QUICHE_EX_DATA_INDEX: c_int = unsafe {
+    /// BoringSSL Extra Data Index for Quiche Connections
+    pub static ref QUICHE_EX_DATA_INDEX: c_int = unsafe {
         SSL_get_ex_new_index(0, ptr::null(), ptr::null(), ptr::null(), ptr::null())
     };
 }
@@ -154,10 +196,20 @@
         }
     }
 
+    #[cfg(feature = "boringssl-boring-crate")]
+    pub fn from_boring(ssl_ctx: boring::ssl::SslContext) -> Context {
+        use foreign_types_shared::ForeignType;
+
+        let mut ctx = Context(ssl_ctx.into_ptr() as _);
+        ctx.set_session_callback();
+
+        ctx
+    }
+
     pub fn new_handshake(&mut self) -> Result<Handshake> {
         unsafe {
-            let ssl = SSL_new(self.as_ptr());
-            Ok(Handshake(ssl))
+            let ssl = SSL_new(self.as_mut_ptr());
+            Ok(Handshake::new(ssl))
         }
     }
 
@@ -165,7 +217,7 @@
         let file = ffi::CString::new(file).map_err(|_| Error::TlsFail)?;
         map_result(unsafe {
             SSL_CTX_load_verify_locations(
-                self.as_ptr(),
+                self.as_mut_ptr(),
                 file.as_ptr(),
                 std::ptr::null(),
             )
@@ -178,7 +230,7 @@
         let path = ffi::CString::new(path).map_err(|_| Error::TlsFail)?;
         map_result(unsafe {
             SSL_CTX_load_verify_locations(
-                self.as_ptr(),
+                self.as_mut_ptr(),
                 std::ptr::null(),
                 path.as_ptr(),
             )
@@ -188,20 +240,20 @@
     pub fn use_certificate_chain_file(&mut self, file: &str) -> Result<()> {
         let cstr = ffi::CString::new(file).map_err(|_| Error::TlsFail)?;
         map_result(unsafe {
-            SSL_CTX_use_certificate_chain_file(self.as_ptr(), cstr.as_ptr())
+            SSL_CTX_use_certificate_chain_file(self.as_mut_ptr(), cstr.as_ptr())
         })
     }
 
     pub fn use_privkey_file(&mut self, file: &str) -> Result<()> {
         let cstr = ffi::CString::new(file).map_err(|_| Error::TlsFail)?;
         map_result(unsafe {
-            SSL_CTX_use_PrivateKey_file(self.as_ptr(), cstr.as_ptr(), 1)
+            SSL_CTX_use_PrivateKey_file(self.as_mut_ptr(), cstr.as_ptr(), 1)
         })
     }
 
     #[cfg(not(windows))]
     fn load_ca_certs(&mut self) -> Result<()> {
-        unsafe { map_result(SSL_CTX_set_default_verify_paths(self.as_ptr())) }
+        unsafe { map_result(SSL_CTX_set_default_verify_paths(self.as_mut_ptr())) }
     }
 
     #[cfg(windows)]
@@ -216,7 +268,7 @@
                 return Err(Error::TlsFail);
             }
 
-            let ctx_store = SSL_CTX_get_cert_store(self.as_ptr());
+            let ctx_store = SSL_CTX_get_cert_store(self.as_mut_ptr());
             if ctx_store.is_null() {
                 return Err(Error::TlsFail);
             }
@@ -258,44 +310,42 @@
             // This is needed to enable the session callback on the client. On
             // the server it doesn't do anything.
             SSL_CTX_set_session_cache_mode(
-                self.as_ptr(),
+                self.as_mut_ptr(),
                 0x0001, // SSL_SESS_CACHE_CLIENT
             );
 
-            SSL_CTX_sess_set_new_cb(self.as_ptr(), new_session);
+            SSL_CTX_sess_set_new_cb(self.as_mut_ptr(), new_session);
         };
     }
 
     pub fn set_verify(&mut self, verify: bool) {
-        let mode = if verify {
-            0x01 // SSL_VERIFY_PEER
-        } else {
-            0x00 // SSL_VERIFY_NONE
-        };
+        // true  -> 0x01 SSL_VERIFY_PEER
+        // false -> 0x00 SSL_VERIFY_NONE
+        let mode = i32::from(verify);
 
         unsafe {
-            SSL_CTX_set_verify(self.as_ptr(), mode, ptr::null());
+            SSL_CTX_set_verify(self.as_mut_ptr(), mode, ptr::null());
         }
     }
 
     pub fn enable_keylog(&mut self) {
         unsafe {
-            SSL_CTX_set_keylog_callback(self.as_ptr(), keylog);
+            SSL_CTX_set_keylog_callback(self.as_mut_ptr(), keylog);
         }
     }
 
-    pub fn set_alpn(&mut self, v: &[Vec<u8>]) -> Result<()> {
+    pub fn set_alpn(&mut self, v: &[&[u8]]) -> Result<()> {
         let mut protos: Vec<u8> = Vec::new();
 
         for proto in v {
             protos.push(proto.len() as u8);
-            protos.append(&mut proto.clone());
+            protos.extend_from_slice(proto);
         }
 
         // Configure ALPN for servers.
         unsafe {
             SSL_CTX_set_alpn_select_cb(
-                self.as_ptr(),
+                self.as_mut_ptr(),
                 select_alpn,
                 ptr::null_mut(),
             );
@@ -303,54 +353,78 @@
 
         // Configure ALPN for clients.
         map_result_zero_is_success(unsafe {
-            SSL_CTX_set_alpn_protos(self.as_ptr(), protos.as_ptr(), protos.len())
+            SSL_CTX_set_alpn_protos(
+                self.as_mut_ptr(),
+                protos.as_ptr(),
+                protos.len(),
+            )
         })
     }
 
     pub fn set_ticket_key(&mut self, key: &[u8]) -> Result<()> {
         map_result(unsafe {
-            SSL_CTX_set_tlsext_ticket_keys(self.as_ptr(), key.as_ptr(), key.len())
+            SSL_CTX_set_tlsext_ticket_keys(
+                self.as_mut_ptr(),
+                key.as_ptr(),
+                key.len(),
+            )
         })
     }
 
     pub fn set_early_data_enabled(&mut self, enabled: bool) {
-        let enabled = if enabled { 1 } else { 0 };
+        let enabled = i32::from(enabled);
 
         unsafe {
-            SSL_CTX_set_early_data_enabled(self.as_ptr(), enabled);
+            SSL_CTX_set_early_data_enabled(self.as_mut_ptr(), enabled);
         }
     }
 
-    fn as_ptr(&self) -> *mut SSL_CTX {
+    fn as_mut_ptr(&mut self) -> *mut SSL_CTX {
         self.0
     }
 }
 
+// NOTE: These traits are not automatically implemented for Context due to the
+// raw pointer it wraps. However, the underlying data is not aliased (as Context
+// should be its only owner), and there is no interior mutability, as the
+// pointer is not accessed directly outside of this module, and the Context
+// object API should preserve Rust's borrowing guarantees.
 unsafe impl std::marker::Send for Context {}
+unsafe impl std::marker::Sync for Context {}
 
 impl Drop for Context {
     fn drop(&mut self) {
-        unsafe { SSL_CTX_free(self.as_ptr()) }
+        unsafe { SSL_CTX_free(self.as_mut_ptr()) }
     }
 }
 
-pub struct Handshake(*mut SSL);
+pub struct Handshake {
+    /// Raw pointer
+    ptr: *mut SSL,
+    /// SSL_process_quic_post_handshake should be called when whenever
+    /// SSL_provide_quic_data is called to process the provided data.
+    provided_data_outstanding: bool,
+}
 
 impl Handshake {
     #[cfg(feature = "ffi")]
     pub unsafe fn from_ptr(ssl: *mut c_void) -> Handshake {
-        let ssl = ssl as *mut SSL;
-        Handshake(ssl)
+        Handshake::new(ssl as *mut SSL)
+    }
+
+    fn new(ptr: *mut SSL) -> Handshake {
+        Handshake {
+            ptr,
+            provided_data_outstanding: false,
+        }
     }
 
     pub fn get_error(&self, ret_code: c_int) -> c_int {
         unsafe { SSL_get_error(self.as_ptr(), ret_code) }
     }
 
-    pub fn init(&self, conn: &Connection) -> Result<()> {
-        self.set_state(conn.is_server);
-
-        self.set_ex_data(*QUICHE_EX_DATA_INDEX, conn)?;
+    pub fn init(&mut self, is_server: bool) -> Result<()> {
+        self.set_state(is_server);
 
         self.set_min_proto_version(TLS1_3_VERSION);
         self.set_max_proto_version(TLS1_3_VERSION);
@@ -366,81 +440,82 @@
         Ok(())
     }
 
-    pub fn use_legacy_codepoint(&self, use_legacy: bool) {
+    pub fn use_legacy_codepoint(&mut self, use_legacy: bool) {
         unsafe {
-            SSL_set_quic_use_legacy_codepoint(self.as_ptr(), use_legacy as c_int);
+            SSL_set_quic_use_legacy_codepoint(
+                self.as_mut_ptr(),
+                use_legacy as c_int,
+            );
         }
     }
 
-    pub fn set_state(&self, is_server: bool) {
+    pub fn set_state(&mut self, is_server: bool) {
         unsafe {
             if is_server {
-                SSL_set_accept_state(self.as_ptr());
+                SSL_set_accept_state(self.as_mut_ptr());
             } else {
-                SSL_set_connect_state(self.as_ptr());
+                SSL_set_connect_state(self.as_mut_ptr());
             }
         }
     }
 
-    pub fn set_ex_data<T>(&self, idx: c_int, data: &T) -> Result<()> {
+    pub fn set_ex_data<T>(&mut self, idx: c_int, data: *const T) -> Result<()> {
         map_result(unsafe {
-            let ptr = data as *const T as *const c_void;
-            SSL_set_ex_data(self.as_ptr(), idx, ptr)
+            let ptr = data as *const c_void;
+            SSL_set_ex_data(self.as_mut_ptr(), idx, ptr)
         })
     }
 
-    pub fn set_quic_method(&self) -> Result<()> {
+    pub fn set_quic_method(&mut self) -> Result<()> {
         map_result(unsafe {
-            SSL_set_quic_method(self.as_ptr(), &QUICHE_STREAM_METHOD)
+            SSL_set_quic_method(self.as_mut_ptr(), &QUICHE_STREAM_METHOD)
         })
     }
 
-    pub fn set_quic_early_data_context(&self, context: &[u8]) -> Result<()> {
+    pub fn set_quic_early_data_context(&mut self, context: &[u8]) -> Result<()> {
         map_result(unsafe {
             SSL_set_quic_early_data_context(
-                self.as_ptr(),
+                self.as_mut_ptr(),
                 context.as_ptr(),
                 context.len(),
             )
         })
     }
 
-    pub fn set_min_proto_version(&self, version: u16) {
-        unsafe { SSL_set_min_proto_version(self.as_ptr(), version) }
+    pub fn set_min_proto_version(&mut self, version: u16) {
+        unsafe { SSL_set_min_proto_version(self.as_mut_ptr(), version) }
     }
 
-    pub fn set_max_proto_version(&self, version: u16) {
-        unsafe { SSL_set_max_proto_version(self.as_ptr(), version) }
+    pub fn set_max_proto_version(&mut self, version: u16) {
+        unsafe { SSL_set_max_proto_version(self.as_mut_ptr(), version) }
     }
 
-    pub fn set_quiet_shutdown(&self, mode: bool) {
-        unsafe { SSL_set_quiet_shutdown(self.as_ptr(), if mode { 1 } else { 0 }) }
+    pub fn set_quiet_shutdown(&mut self, mode: bool) {
+        unsafe { SSL_set_quiet_shutdown(self.as_mut_ptr(), i32::from(mode)) }
     }
 
-    pub fn set_host_name(&self, name: &str) -> Result<()> {
+    pub fn set_host_name(&mut self, name: &str) -> Result<()> {
         let cstr = ffi::CString::new(name).map_err(|_| Error::TlsFail)?;
-        map_result_ssl(self, unsafe {
-            SSL_set_tlsext_host_name(self.as_ptr(), cstr.as_ptr())
-        })?;
+        let rc =
+            unsafe { SSL_set_tlsext_host_name(self.as_mut_ptr(), cstr.as_ptr()) };
+        self.map_result_ssl(rc)?;
 
-        let param = unsafe { SSL_get0_param(self.as_ptr()) };
+        let param = unsafe { SSL_get0_param(self.as_mut_ptr()) };
 
         map_result(unsafe {
             X509_VERIFY_PARAM_set1_host(param, cstr.as_ptr(), name.len())
         })
     }
 
-    pub fn set_quic_transport_params(&self, buf: &[u8]) -> Result<()> {
-        map_result_ssl(self, unsafe {
-            SSL_set_quic_transport_params(self.as_ptr(), buf.as_ptr(), buf.len())
-        })
-    }
-
-    #[cfg(test)]
-    pub fn set_options(&mut self, opts: u32) {
-        unsafe {
-            SSL_set_options(self.as_ptr(), opts);
-        }
+    pub fn set_quic_transport_params(&mut self, buf: &[u8]) -> Result<()> {
+        let rc = unsafe {
+            SSL_set_quic_transport_params(
+                self.as_mut_ptr(),
+                buf.as_ptr(),
+                buf.len(),
+            )
+        };
+        self.map_result_ssl(rc)
     }
 
     pub fn quic_transport_params(&self) -> &[u8] {
@@ -473,7 +548,24 @@
         unsafe { slice::from_raw_parts(ptr, len as usize) }
     }
 
-    pub fn set_session(&self, session: &[u8]) -> Result<()> {
+    pub fn server_name(&self) -> Option<&str> {
+        let s = unsafe {
+            let ptr = SSL_get_servername(
+                self.as_ptr(),
+                0, // TLSEXT_NAMETYPE_host_name
+            );
+
+            if ptr.is_null() {
+                return None;
+            }
+
+            ffi::CStr::from_ptr(ptr)
+        };
+
+        s.to_str().ok()
+    }
+
+    pub fn set_session(&mut self, session: &[u8]) -> Result<()> {
         unsafe {
             let ctx = SSL_get_SSL_CTX(self.as_ptr());
 
@@ -488,31 +580,55 @@
                 return Err(Error::TlsFail);
             }
 
-            let rc = SSL_set_session(self.as_ptr(), session);
+            let rc = SSL_set_session(self.as_mut_ptr(), session);
             SSL_SESSION_free(session);
 
             map_result(rc)
         }
     }
 
-    pub fn provide_data(&self, level: crypto::Level, buf: &[u8]) -> Result<()> {
-        map_result_ssl(self, unsafe {
-            SSL_provide_quic_data(self.as_ptr(), level, buf.as_ptr(), buf.len())
-        })
+    pub fn provide_data(
+        &mut self, level: crypto::Level, buf: &[u8],
+    ) -> Result<()> {
+        self.provided_data_outstanding = true;
+        let rc = unsafe {
+            SSL_provide_quic_data(
+                self.as_mut_ptr(),
+                level,
+                buf.as_ptr(),
+                buf.len(),
+            )
+        };
+        self.map_result_ssl(rc)
     }
 
-    pub fn do_handshake(&self) -> Result<()> {
-        map_result_ssl(self, unsafe { SSL_do_handshake(self.as_ptr()) })
+    pub fn do_handshake(&mut self, ex_data: &mut ExData) -> Result<()> {
+        self.set_ex_data(*QUICHE_EX_DATA_INDEX, ex_data)?;
+        let rc = unsafe { SSL_do_handshake(self.as_mut_ptr()) };
+        self.set_ex_data::<Connection>(*QUICHE_EX_DATA_INDEX, std::ptr::null())?;
+
+        self.set_transport_error(ex_data, rc);
+        self.map_result_ssl(rc)
     }
 
-    pub fn process_post_handshake(&self) -> Result<()> {
-        map_result_ssl(self, unsafe {
-            SSL_process_quic_post_handshake(self.as_ptr())
-        })
+    pub fn process_post_handshake(&mut self, ex_data: &mut ExData) -> Result<()> {
+        // If SSL_provide_quic_data hasn't been called since we last called
+        // SSL_process_quic_post_handshake, then there's nothing to do.
+        if !self.provided_data_outstanding {
+            return Ok(());
+        }
+        self.provided_data_outstanding = false;
+
+        self.set_ex_data(*QUICHE_EX_DATA_INDEX, ex_data)?;
+        let rc = unsafe { SSL_process_quic_post_handshake(self.as_mut_ptr()) };
+        self.set_ex_data::<Connection>(*QUICHE_EX_DATA_INDEX, std::ptr::null())?;
+
+        self.set_transport_error(ex_data, rc);
+        self.map_result_ssl(rc)
     }
 
-    pub fn reset_early_data_reject(&self) {
-        unsafe { SSL_reset_early_data_reject(self.as_ptr()) };
+    pub fn reset_early_data_reject(&mut self) {
+        unsafe { SSL_reset_early_data_reject(self.as_mut_ptr()) };
     }
 
     pub fn write_level(&self) -> crypto::Level {
@@ -534,7 +650,7 @@
             }
 
             let curve_name = SSL_get_curve_name(curve_id);
-            match std::ffi::CStr::from_ptr(curve_name).to_str() {
+            match ffi::CStr::from_ptr(curve_name).to_str() {
                 Ok(v) => v,
 
                 Err(_) => return None,
@@ -552,7 +668,7 @@
             }
 
             let sigalg_name = SSL_get_signature_algorithm_name(sigalg_id, 1);
-            match std::ffi::CStr::from_ptr(sigalg_name).to_str() {
+            match ffi::CStr::from_ptr(sigalg_name).to_str() {
                 Ok(v) => v,
 
                 Err(_) => return None,
@@ -562,7 +678,40 @@
         Some(sigalg.to_string())
     }
 
-    pub fn peer_cert(&self) -> Option<Vec<u8>> {
+    pub fn peer_cert_chain(&self) -> Option<Vec<&[u8]>> {
+        let cert_chain = unsafe {
+            let chain =
+                map_result_ptr(SSL_get0_peer_certificates(self.as_ptr())).ok()?;
+
+            let num = sk_num(chain);
+            if num <= 0 {
+                return None;
+            }
+
+            let mut cert_chain = vec![];
+            for i in 0..num {
+                let buffer =
+                    map_result_ptr(sk_value(chain, i) as *const CRYPTO_BUFFER)
+                        .ok()?;
+
+                let out_len = CRYPTO_BUFFER_len(buffer);
+                if out_len == 0 {
+                    return None;
+                }
+
+                let out = CRYPTO_BUFFER_data(buffer);
+                let slice = slice::from_raw_parts(out, out_len);
+
+                cert_chain.push(slice);
+            }
+
+            cert_chain
+        };
+
+        Some(cert_chain)
+    }
+
+    pub fn peer_cert(&self) -> Option<&[u8]> {
         let peer_cert = unsafe {
             let chain =
                 map_result_ptr(SSL_get0_peer_certificates(self.as_ptr())).ok()?;
@@ -573,19 +722,64 @@
             let buffer =
                 map_result_ptr(sk_value(chain, 0) as *const CRYPTO_BUFFER)
                     .ok()?;
+
             let out_len = CRYPTO_BUFFER_len(buffer);
             if out_len == 0 {
                 return None;
             }
 
             let out = CRYPTO_BUFFER_data(buffer);
-            let der = slice::from_raw_parts(out, out_len as usize);
-            der.to_vec()
+            slice::from_raw_parts(out, out_len)
         };
 
         Some(peer_cert)
     }
 
+    #[cfg(test)]
+    pub fn set_options(&mut self, opts: u32) {
+        unsafe {
+            SSL_set_options(self.as_mut_ptr(), opts);
+        }
+    }
+
+    // Only used for testing handling of failure during key signing.
+    #[cfg(test)]
+    pub fn set_failing_private_key_method(&mut self) {
+        extern fn failing_sign(
+            _ssl: *mut SSL, _out: *mut u8, _out_len: *mut usize, _max_out: usize,
+            _signature_algorithm: u16, _in: *const u8, _in_len: usize,
+        ) -> ssl_private_key_result_t {
+            ssl_private_key_result_t::ssl_private_key_failure
+        }
+
+        extern fn failing_decrypt(
+            _ssl: *mut SSL, _out: *mut u8, _out_len: *mut usize, _max_out: usize,
+            _in: *const u8, _in_len: usize,
+        ) -> ssl_private_key_result_t {
+            ssl_private_key_result_t::ssl_private_key_failure
+        }
+
+        extern fn failing_complete(
+            _ssl: *mut SSL, _out: *mut u8, _out_len: *mut usize, _max_out: usize,
+        ) -> ssl_private_key_result_t {
+            ssl_private_key_result_t::ssl_private_key_failure
+        }
+
+        static QUICHE_PRIVATE_KEY_METHOD: SSL_PRIVATE_KEY_METHOD =
+            SSL_PRIVATE_KEY_METHOD {
+                decrypt: failing_decrypt,
+                sign: failing_sign,
+                complete: failing_complete,
+            };
+
+        unsafe {
+            SSL_set_private_key_method(
+                self.as_mut_ptr(),
+                &QUICHE_PRIVATE_KEY_METHOD,
+            );
+        }
+    }
+
     pub fn is_completed(&self) -> bool {
         unsafe { SSL_in_init(self.as_ptr()) == 0 }
     }
@@ -599,22 +793,118 @@
     }
 
     pub fn clear(&mut self) -> Result<()> {
-        map_result_ssl(self, unsafe { SSL_clear(self.as_ptr()) })
+        let rc = unsafe { SSL_clear(self.as_mut_ptr()) };
+        self.map_result_ssl(rc)
     }
 
-    fn as_ptr(&self) -> *mut SSL {
-        self.0
+    fn as_ptr(&self) -> *const SSL {
+        self.ptr
+    }
+
+    fn as_mut_ptr(&mut self) -> *mut SSL {
+        self.ptr
+    }
+
+    fn map_result_ssl(&mut self, bssl_result: c_int) -> Result<()> {
+        match bssl_result {
+            1 => Ok(()),
+
+            _ => {
+                let ssl_err = self.get_error(bssl_result);
+                match ssl_err {
+                    // SSL_ERROR_SSL
+                    1 => {
+                        log_ssl_error();
+
+                        Err(Error::TlsFail)
+                    },
+
+                    // SSL_ERROR_WANT_READ
+                    2 => Err(Error::Done),
+
+                    // SSL_ERROR_WANT_WRITE
+                    3 => Err(Error::Done),
+
+                    // SSL_ERROR_WANT_X509_LOOKUP
+                    4 => Err(Error::Done),
+
+                    // SSL_ERROR_SYSCALL
+                    5 => Err(Error::TlsFail),
+
+                    // SSL_ERROR_PENDING_SESSION
+                    11 => Err(Error::Done),
+
+                    // SSL_ERROR_PENDING_CERTIFICATE
+                    12 => Err(Error::Done),
+
+                    // SSL_ERROR_WANT_PRIVATE_KEY_OPERATION
+                    13 => Err(Error::Done),
+
+                    // SSL_ERROR_PENDING_TICKET
+                    14 => Err(Error::Done),
+
+                    // SSL_ERROR_EARLY_DATA_REJECTED
+                    15 => {
+                        self.reset_early_data_reject();
+                        Err(Error::Done)
+                    },
+
+                    // SSL_ERROR_WANT_CERTIFICATE_VERIFY
+                    16 => Err(Error::Done),
+
+                    _ => Err(Error::TlsFail),
+                }
+            },
+        }
+    }
+
+    fn set_transport_error(&mut self, ex_data: &mut ExData, bssl_result: c_int) {
+        // SSL_ERROR_SSL
+        if self.get_error(bssl_result) == 1 {
+            // SSL_ERROR_SSL can't be recovered so ensure we set a
+            // local_error so the connection is closed.
+            // See https://www.openssl.org/docs/man1.1.1/man3/SSL_get_error.html
+            if ex_data.local_error.is_none() {
+                *ex_data.local_error = Some(ConnectionError {
+                    is_app: false,
+                    error_code: INTERNAL_ERROR,
+                    reason: Vec::new(),
+                })
+            }
+        }
     }
 }
 
+// NOTE: These traits are not automatically implemented for Handshake due to the
+// raw pointer it wraps. However, the underlying data is not aliased (as
+// Handshake should be its only owner), and there is no interior mutability, as
+// the pointer is not accessed directly outside of this module, and the
+// Handshake object API should preserve Rust's borrowing guarantees.
 unsafe impl std::marker::Send for Handshake {}
+unsafe impl std::marker::Sync for Handshake {}
 
 impl Drop for Handshake {
     fn drop(&mut self) {
-        unsafe { SSL_free(self.as_ptr()) }
+        unsafe { SSL_free(self.as_mut_ptr()) }
     }
 }
 
+pub struct ExData<'a> {
+    pub application_protos: &'a Vec<Vec<u8>>,
+
+    pub pkt_num_spaces: &'a mut [packet::PktNumSpace; packet::Epoch::count()],
+
+    pub session: &'a mut Option<Vec<u8>>,
+
+    pub local_error: &'a mut Option<super::ConnectionError>,
+
+    pub keylog: Option<&'a mut Box<dyn std::io::Write + Send + Sync>>,
+
+    pub trace_id: &'a str,
+
+    pub is_server: bool,
+}
+
 fn get_ex_data_from_ptr<'a, T>(ptr: *mut SSL, idx: c_int) -> Option<&'a mut T> {
     unsafe {
         let data = SSL_get_ex_data(ptr, idx) as *mut T;
@@ -639,23 +929,24 @@
     ssl: *mut SSL, level: crypto::Level, cipher: *const SSL_CIPHER,
     secret: *const u8, secret_len: usize,
 ) -> c_int {
-    let conn =
-        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
-            Some(v) => v,
+    let ex_data = match get_ex_data_from_ptr::<ExData>(ssl, *QUICHE_EX_DATA_INDEX)
+    {
+        Some(v) => v,
 
-            None => return 0,
-        };
+        None => return 0,
+    };
 
-    trace!("{} set read secret lvl={:?}", conn.trace_id, level);
+    trace!("{} set read secret lvl={:?}", ex_data.trace_id, level);
 
     let space = match level {
-        crypto::Level::Initial => &mut conn.pkt_num_spaces[packet::EPOCH_INITIAL],
+        crypto::Level::Initial =>
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Initial],
         crypto::Level::ZeroRTT =>
-            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
         crypto::Level::Handshake =>
-            &mut conn.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Handshake],
         crypto::Level::OneRTT =>
-            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
     };
 
     let aead = match get_cipher_from_ptr(cipher) {
@@ -665,10 +956,10 @@
     };
 
     // 0-RTT read secrets are present only on the server.
-    if level != crypto::Level::ZeroRTT || conn.is_server {
+    if level != crypto::Level::ZeroRTT || ex_data.is_server {
         let secret = unsafe { slice::from_raw_parts(secret, secret_len) };
 
-        let open = match crypto::Open::from_secret(aead, &secret) {
+        let open = match crypto::Open::from_secret(aead, secret) {
             Ok(v) => v,
 
             Err(_) => return 0,
@@ -689,23 +980,24 @@
     ssl: *mut SSL, level: crypto::Level, cipher: *const SSL_CIPHER,
     secret: *const u8, secret_len: usize,
 ) -> c_int {
-    let conn =
-        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
-            Some(v) => v,
+    let ex_data = match get_ex_data_from_ptr::<ExData>(ssl, *QUICHE_EX_DATA_INDEX)
+    {
+        Some(v) => v,
 
-            None => return 0,
-        };
+        None => return 0,
+    };
 
-    trace!("{} set write secret lvl={:?}", conn.trace_id, level);
+    trace!("{} set write secret lvl={:?}", ex_data.trace_id, level);
 
     let space = match level {
-        crypto::Level::Initial => &mut conn.pkt_num_spaces[packet::EPOCH_INITIAL],
+        crypto::Level::Initial =>
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Initial],
         crypto::Level::ZeroRTT =>
-            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
         crypto::Level::Handshake =>
-            &mut conn.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Handshake],
         crypto::Level::OneRTT =>
-            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
     };
 
     let aead = match get_cipher_from_ptr(cipher) {
@@ -715,10 +1007,10 @@
     };
 
     // 0-RTT write secrets are present only on the client.
-    if level != crypto::Level::ZeroRTT || !conn.is_server {
+    if level != crypto::Level::ZeroRTT || !ex_data.is_server {
         let secret = unsafe { slice::from_raw_parts(secret, secret_len) };
 
-        let seal = match crypto::Seal::from_secret(aead, &secret) {
+        let seal = match crypto::Seal::from_secret(aead, secret) {
             Ok(v) => v,
 
             Err(_) => return 0,
@@ -733,16 +1025,16 @@
 extern fn add_handshake_data(
     ssl: *mut SSL, level: crypto::Level, data: *const u8, len: usize,
 ) -> c_int {
-    let conn =
-        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
-            Some(v) => v,
+    let ex_data = match get_ex_data_from_ptr::<ExData>(ssl, *QUICHE_EX_DATA_INDEX)
+    {
+        Some(v) => v,
 
-            None => return 0,
-        };
+        None => return 0,
+    };
 
     trace!(
         "{} write message lvl={:?} len={}",
-        conn.trace_id,
+        ex_data.trace_id,
         level,
         len
     );
@@ -750,12 +1042,13 @@
     let buf = unsafe { slice::from_raw_parts(data, len) };
 
     let space = match level {
-        crypto::Level::Initial => &mut conn.pkt_num_spaces[packet::EPOCH_INITIAL],
+        crypto::Level::Initial =>
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Initial],
         crypto::Level::ZeroRTT => unreachable!(),
         crypto::Level::Handshake =>
-            &mut conn.pkt_num_spaces[packet::EPOCH_HANDSHAKE],
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Handshake],
         crypto::Level::OneRTT =>
-            &mut conn.pkt_num_spaces[packet::EPOCH_APPLICATION],
+            &mut ex_data.pkt_num_spaces[packet::Epoch::Application],
     };
 
     if space.crypto_stream.send.write(buf, false).is_err() {
@@ -773,22 +1066,22 @@
 }
 
 extern fn send_alert(ssl: *mut SSL, level: crypto::Level, alert: u8) -> c_int {
-    let conn =
-        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
-            Some(v) => v,
+    let ex_data = match get_ex_data_from_ptr::<ExData>(ssl, *QUICHE_EX_DATA_INDEX)
+    {
+        Some(v) => v,
 
-            None => return 0,
-        };
+        None => return 0,
+    };
 
     trace!(
         "{} send alert lvl={:?} alert={:x}",
-        conn.trace_id,
+        ex_data.trace_id,
         level,
         alert
     );
 
     let error: u64 = TLS_ALERT_ERROR + u64::from(alert);
-    conn.local_error = Some(ConnectionError {
+    *ex_data.local_error = Some(ConnectionError {
         is_app: false,
         error_code: error,
         reason: Vec::new(),
@@ -798,14 +1091,14 @@
 }
 
 extern fn keylog(ssl: *mut SSL, line: *const c_char) {
-    let conn =
-        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
-            Some(v) => v,
+    let ex_data = match get_ex_data_from_ptr::<ExData>(ssl, *QUICHE_EX_DATA_INDEX)
+    {
+        Some(v) => v,
 
-            None => return,
-        };
+        None => return,
+    };
 
-    if let Some(keylog) = &mut conn.keylog {
+    if let Some(keylog) = &mut ex_data.keylog {
         let data = unsafe { ffi::CStr::from_ptr(line).to_bytes() };
 
         let mut full_line = Vec::with_capacity(data.len() + 1);
@@ -820,14 +1113,14 @@
     ssl: *mut SSL, out: *mut *const u8, out_len: *mut u8, inp: *mut u8,
     in_len: c_uint, _arg: *mut c_void,
 ) -> c_int {
-    let conn =
-        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
-            Some(v) => v,
+    let ex_data = match get_ex_data_from_ptr::<ExData>(ssl, *QUICHE_EX_DATA_INDEX)
+    {
+        Some(v) => v,
 
-            None => return 3, // SSL_TLSEXT_ERR_NOACK
-        };
+        None => return 3, // SSL_TLSEXT_ERR_NOACK
+    };
 
-    if conn.application_protos.is_empty() {
+    if ex_data.application_protos.is_empty() {
         return 3; // SSL_TLSEXT_ERR_NOACK
     }
 
@@ -836,7 +1129,7 @@
     });
 
     while let Ok(proto) = protos.get_bytes_with_u8_length() {
-        let found = conn.application_protos.iter().any(|expected| {
+        let found = ex_data.application_protos.iter().any(|expected| {
             trace!(
                 "checking peer ALPN {:?} against {:?}",
                 std::str::from_utf8(proto.as_ref()),
@@ -865,16 +1158,15 @@
     3 // SSL_TLSEXT_ERR_NOACK
 }
 
-#[no_mangle]
 extern fn new_session(ssl: *mut SSL, session: *mut SSL_SESSION) -> c_int {
-    let conn =
-        match get_ex_data_from_ptr::<Connection>(ssl, *QUICHE_EX_DATA_INDEX) {
-            Some(v) => v,
+    let ex_data = match get_ex_data_from_ptr::<ExData>(ssl, *QUICHE_EX_DATA_INDEX)
+    {
+        Some(v) => v,
 
-            None => return 0,
-        };
+        None => return 0,
+    };
 
-    let handshake = Handshake(ssl);
+    let handshake = Handshake::new(ssl);
     let peer_params = handshake.quic_transport_params();
 
     // Serialize session object into buffer.
@@ -914,12 +1206,12 @@
         return 0;
     }
 
-    if buffer.write(&peer_params).is_err() {
+    if buffer.write(peer_params).is_err() {
         std::mem::forget(handshake);
         return 0;
     }
 
-    conn.session = Some(buffer);
+    *ex_data.session = Some(buffer);
 
     // Prevent handshake from being freed, as we still need it.
     std::mem::forget(handshake);
@@ -948,59 +1240,6 @@
     }
 }
 
-fn map_result_ssl(ssl: &Handshake, bssl_result: c_int) -> Result<()> {
-    match bssl_result {
-        1 => Ok(()),
-
-        _ => {
-            let ssl_err = ssl.get_error(bssl_result);
-            match ssl_err {
-                // SSL_ERROR_SSL
-                1 => {
-                    log_ssl_error();
-
-                    Err(Error::TlsFail)
-                },
-
-                // SSL_ERROR_WANT_READ
-                2 => Err(Error::Done),
-
-                // SSL_ERROR_WANT_WRITE
-                3 => Err(Error::Done),
-
-                // SSL_ERROR_WANT_X509_LOOKUP
-                4 => Err(Error::Done),
-
-                // SSL_ERROR_SYSCALL
-                5 => Err(Error::TlsFail),
-
-                // SSL_ERROR_PENDING_SESSION
-                11 => Err(Error::Done),
-
-                // SSL_ERROR_PENDING_CERTIFICATE
-                12 => Err(Error::Done),
-
-                // SSL_ERROR_WANT_PRIVATE_KEY_OPERATION
-                13 => Err(Error::Done),
-
-                // SSL_ERROR_PENDING_TICKET
-                14 => Err(Error::Done),
-
-                // SSL_ERROR_EARLY_DATA_REJECTED
-                15 => {
-                    ssl.reset_early_data_reject();
-                    Err(Error::Done)
-                },
-
-                // SSL_ERROR_WANT_CERTIFICATE_VERIFY
-                16 => Err(Error::Done),
-
-                _ => Err(Error::TlsFail),
-            }
-        },
-    }
-}
-
 fn log_ssl_error() {
     let err = [0; 1024];
 
@@ -1082,7 +1321,7 @@
 
     fn SSL_new(ctx: *mut SSL_CTX) -> *mut SSL;
 
-    fn SSL_get_error(ssl: *mut SSL, ret_code: c_int) -> c_int;
+    fn SSL_get_error(ssl: *const SSL, ret_code: c_int) -> c_int;
 
     fn SSL_set_accept_state(ssl: *mut SSL);
     fn SSL_set_connect_state(ssl: *mut SSL);
@@ -1092,21 +1331,21 @@
     fn SSL_set_ex_data(ssl: *mut SSL, idx: c_int, ptr: *const c_void) -> c_int;
     fn SSL_get_ex_data(ssl: *mut SSL, idx: c_int) -> *mut c_void;
 
-    fn SSL_get_current_cipher(ssl: *mut SSL) -> *const SSL_CIPHER;
+    fn SSL_get_current_cipher(ssl: *const SSL) -> *const SSL_CIPHER;
 
-    fn SSL_get_curve_id(ssl: *mut SSL) -> u16;
+    fn SSL_get_curve_id(ssl: *const SSL) -> u16;
     fn SSL_get_curve_name(curve: u16) -> *const c_char;
 
-    fn SSL_get_peer_signature_algorithm(ssl: *mut SSL) -> u16;
+    fn SSL_get_peer_signature_algorithm(ssl: *const SSL) -> u16;
     fn SSL_get_signature_algorithm_name(
         sigalg: u16, include_curve: i32,
     ) -> *const c_char;
 
     fn SSL_set_session(ssl: *mut SSL, session: *mut SSL_SESSION) -> c_int;
 
-    fn SSL_get_SSL_CTX(ssl: *mut SSL) -> *mut SSL_CTX;
+    fn SSL_get_SSL_CTX(ssl: *const SSL) -> *mut SSL_CTX;
 
-    fn SSL_get0_peer_certificates(ssl: *mut SSL) -> *const STACK_OF;
+    fn SSL_get0_peer_certificates(ssl: *const SSL) -> *const STACK_OF;
 
     fn SSL_set_min_proto_version(ssl: *mut SSL, version: u16);
     fn SSL_set_max_proto_version(ssl: *mut SSL, version: u16);
@@ -1119,9 +1358,6 @@
         ssl: *mut SSL, params: *const u8, params_len: usize,
     ) -> c_int;
 
-    #[cfg(test)]
-    fn SSL_set_options(ssl: *mut SSL, opts: u32) -> u32;
-
     fn SSL_set_quic_method(
         ssl: *mut SSL, quic_method: *const SSL_QUIC_METHOD,
     ) -> c_int;
@@ -1132,14 +1368,24 @@
         ssl: *mut SSL, context: *const u8, context_len: usize,
     ) -> c_int;
 
+    #[cfg(test)]
+    fn SSL_set_options(ssl: *mut SSL, opts: u32) -> u32;
+
+    #[cfg(test)]
+    fn SSL_set_private_key_method(
+        ssl: *mut SSL, key_method: *const SSL_PRIVATE_KEY_METHOD,
+    );
+
     fn SSL_get_peer_quic_transport_params(
-        ssl: *mut SSL, out_params: *mut *const u8, out_params_len: *mut usize,
+        ssl: *const SSL, out_params: *mut *const u8, out_params_len: *mut usize,
     );
 
     fn SSL_get0_alpn_selected(
-        ssl: *mut SSL, out: *mut *const u8, out_len: *mut u32,
+        ssl: *const SSL, out: *mut *const u8, out_len: *mut u32,
     );
 
+    fn SSL_get_servername(ssl: *const SSL, ty: c_int) -> *const c_char;
+
     fn SSL_provide_quic_data(
         ssl: *mut SSL, level: crypto::Level, data: *const u8, len: usize,
     ) -> c_int;
@@ -1150,13 +1396,13 @@
 
     fn SSL_do_handshake(ssl: *mut SSL) -> c_int;
 
-    fn SSL_quic_write_level(ssl: *mut SSL) -> crypto::Level;
+    fn SSL_quic_write_level(ssl: *const SSL) -> crypto::Level;
 
-    fn SSL_session_reused(ssl: *mut SSL) -> c_int;
+    fn SSL_session_reused(ssl: *const SSL) -> c_int;
 
-    fn SSL_in_init(ssl: *mut SSL) -> c_int;
+    fn SSL_in_init(ssl: *const SSL) -> c_int;
 
-    fn SSL_in_early_data(ssl: *mut SSL) -> c_int;
+    fn SSL_in_early_data(ssl: *const SSL) -> c_int;
 
     fn SSL_clear(ssl: *mut SSL) -> c_int;