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, "es, 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, "es, 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, ¶m_type, ¶m_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(), ¶m_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,
+ ¶m->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(¶m->id->email, ¶m->id->emaillen,
+ !int_x509_param_set1(¶m->email, ¶m->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 **)¶m->id->ip, ¶m->id->iplen,
+ !int_x509_param_set1((char **)¶m->ip, ¶m->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(©, CBS_len(a) - CBS_len(b));
+ return equal_case(©, 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(©);
+ out->client_hello_len = CBS_len(©) - 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, ¶ms) ||
- !CBB_add_bytes(¶ms, 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, ¶ms_list) ||
- !CBS_get_u8(¶ms_list, ¶m) ||
- CBS_len(¶ms_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, ¶ms) ||
- CBS_len(¶ms) == 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, ¶ms) ||
- !CBB_add_u8(¶ms, 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(©, &extensions) ||
- CBS_len(©) != 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 ¶ms) {
+ 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;