diff options
author | Mike Yu <yumike@google.com> | 2022-12-08 18:43:58 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-12-08 18:43:58 +0000 |
commit | 747fd9a206e17c313cc1b117943305eced93f22b (patch) | |
tree | a680c8c7b840bfec949292f9785d1d085efeaa8b | |
parent | 4ee1fc18fea8cc01a8c13ae165d830ac1659db55 (diff) | |
parent | 21aebcb8b95c5c6d5d8f6b210af7aa420c28b59a (diff) | |
download | quiche-747fd9a206e17c313cc1b117943305eced93f22b.tar.gz |
Upgrade quiche to 0.14.0 am: 21aebcb8b9main-16k-with-phones
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/quiche/+/2299706
Change-Id: Ifb0da6667b8e3b0d944cae96d86cb64d542f9ccf
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
337 files changed, 27375 insertions, 16045 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 3186f7e..615faf7 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "ad9d9330cf77b212915c2b175e90063c2cc3ac78" - } -} + "sha1": "4d411c22413835f2d57f993d1c90c07813f803cd" + }, + "path_in_vcs": "quiche" +}
\ No newline at end of file @@ -61,6 +61,7 @@ rust_defaults { "liblibc", "liblibm", "liblog_rust", + "liboctets", "libring", ], prefer_rlib: true, @@ -129,6 +130,7 @@ rust_defaults { "liblibm", "liblog_rust", "libmio", + "liboctets", "libring", "liburl", ], @@ -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.59.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd4865004a46a0aafb2a0a5eb19d3c9fc46ee5f063a6cfc605c69ac9ecf5263d" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" 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.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6953f3fea6f9f20c15e81b3f4ef2217ea06cc05652a0db49c0abb35626b0fad1" +dependencies = [ + "bitflags", + "boring-sys", + "foreign-types", + "lazy_static", + "libc", +] [[package]] name = "boring-sys" -version = "1.1.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2416bce1bcabf0d7995ce0338ec2425b8766a4d5a39d758a3638008911642fc" +checksum = "9e43b1d5c3524929bc64d56e604f7362e357d509ff8b29903605fd72f4f41eae" dependencies = [ "bindgen", "cmake", @@ -76,42 +64,36 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.7.0" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" +checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" [[package]] name = "cc" -version = "1.0.68" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" [[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.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c" +checksum = "bf6b561dcf059c85bbe388e0a7b0a1469acb3934cc0cfa148613a830629e3049" dependencies = [ "glob", "libc", @@ -119,34 +101,19 @@ dependencies = [ ] [[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.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" +checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" 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,23 +121,23 @@ dependencies = [ [[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", + "strsim", "syn", ] [[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", @@ -178,17 +145,10 @@ dependencies = [ ] [[package]] -name = "env_logger" -version = "0.8.3" +name = "data-encoding" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17392a012ea30ef05a610aa97dfb49496e71c9f676b27879922ea5bdf60d9d3f" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" [[package]] name = "fnv" @@ -197,20 +157,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" 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 = "foreign-types-macros" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8469d0d40519bc608ec6863f1cc88f3f1deee15913f2f3b3e573d81ed38cccc" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" +name = "foreign-types-shared" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "glob" @@ -220,24 +191,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" [[package]] name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" - -[[package]] -name = "hermit-abi" -version = "0.1.18" +version = "0.11.2" 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 = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" [[package]] name = "ident_case" @@ -258,49 +214,30 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.6.2" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee" 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.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" [[package]] name = "js-sys" -version = "0.3.51" +version = "0.3.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83bdfbace3a0e81a4253f73b49e960b053e396a11012cbd49b9b74d6a2b67062" +checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397" 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 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.95" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "789da6d93f1b866ffe175afc5322a4d76c038605a1c3319bb57b06967ca98a36" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libloading" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a" +checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" dependencies = [ - "cfg-if 1.0.0", - "winapi 0.3.9", + "cfg-if", + "winapi", ] [[package]] name = "libm" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a" +checksum = "33a33a362ce288760ec6a508b94caaec573ae7d3bbbd91b87aa0bad4456839db" [[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.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" [[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.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799" dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", "libc", "log", - "miow", - "net2", - "slab", - "winapi 0.2.8", + "wasi", + "windows-sys", ] [[package]] -name = "miow" -version = "0.2.2" +name = "nom" +version = "7.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", + "memchr", + "minimal-lexical", ] [[package]] -name = "net2" -version = "0.2.37" +name = "num-traits" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", + "autocfg", ] [[package]] -name = "nom" -version = "5.1.2" +name = "octets" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "memchr", - "version_check", -] +checksum = "3fa906ec83d76442cc421a362fa8c131caa513ca19af87dc5736b6679b43240d" [[package]] name = "once_cell" -version = "1.7.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "7b10983b38c53aebdf33f542c6275b0f58a238129d00c4ae0e6fb59738d783ca" [[package]] name = "peeking_take_while" @@ -427,18 +355,18 @@ checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" [[package]] name = "proc-macro2" -version = "1.0.27" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038" +checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "qlog" -version = "0.4.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8777d5490145d6907198d48b3a907447689ce80e071b3d8a16a9d9fb3df02bc1" +checksum = "8d439010ca75633cd1a13f0ebc31e8fe982541d1db4ecd84a4d7280adc01d296" dependencies = [ "serde", "serde_derive", @@ -448,46 +376,47 @@ dependencies = [ [[package]] name = "quiche" -version = "0.9.0" +version = "0.14.0" dependencies = [ - "boring-sys", + "boring", "cmake", + "foreign-types-shared", "lazy_static", "libc", "libm", "log", "mio", + "octets", "qlog", "ring", + "sfv", "url", - "winapi 0.3.9", + "winapi", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1" dependencies = [ "proc-macro2", ] [[package]] name = "regex" -version = "1.5.4" +version = "1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" dependencies = [ - "aho-corasick", - "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.25" +version = "0.6.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" [[package]] name = "ring" @@ -501,7 +430,18 @@ dependencies = [ "spin", "untrusted", "web-sys", - "winapi 0.3.9", + "winapi", +] + +[[package]] +name = "rust_decimal" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22dc69eadbf0ee2110b8d20418c0c6edbaefec2811c4963dc17b6344e11fe0f8" +dependencies = [ + "arrayvec", + "num-traits", + "serde", ] [[package]] @@ -512,30 +452,30 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustversion" -version = "1.0.5" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" +checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" [[package]] name = "serde" -version = "1.0.126" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be" dependencies = [ "proc-macro2", "quote", @@ -544,9 +484,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.64" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79" +checksum = "9b7ce2b32a1aed03c558dc61a5cd328f15aff2dbc17daad8fb8af04d2100e15c" dependencies = [ "indexmap", "itoa", @@ -556,9 +496,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "1.9.2" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e3132bd01cfb74aac8b1b10083ad1f38dbf756df3176d5e63dd91e3f62a87f5" +checksum = "b827f2113224f3f19a665136f006709194bdfdcb1fdc1e4b2b5cbac8e0cced54" dependencies = [ "rustversion", "serde", @@ -567,9 +507,9 @@ dependencies = [ [[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", @@ -578,16 +518,21 @@ dependencies = [ ] [[package]] -name = "shlex" -version = "0.1.1" +name = "sfv" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +checksum = "5082e028484c60920c1b0dc8ab6bd4663f075e15095c5a9009fae779c01f6364" +dependencies = [ + "data-encoding", + "indexmap", + "rust_decimal", +] [[package]] -name = "slab" -version = "0.4.3" +name = "shlex" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" [[package]] name = "spin" @@ -597,50 +542,26 @@ checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[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.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82" +checksum = "fbaf6116ab8924f39d52792136fb74fd60a80194cf1b1c6ffa6453eef1c3f942" dependencies = [ "proc-macro2", "quote", - "unicode-xid", -] - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -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", + "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", ] @@ -653,12 +574,15 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "unicode-bidi" -version = "0.3.5" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeb8be209bb1c96b7c177c7420d26e04eccacb0eeae6b980e35fcb74678107e0" -dependencies = [ - "matches", -] +checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" + +[[package]] +name = "unicode-ident" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee" [[package]] name = "unicode-normalization" @@ -670,18 +594,6 @@ dependencies = [ ] [[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,32 +611,26 @@ dependencies = [ ] [[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.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54ee1d4ed486f78874278e63e4069fc1ab9f6a18ca492076ffb90c5eb2997fd" +checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.74" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b33f6a0694ccfea53d94db8b2ed1c3a8a4c86dd936b13b9f0a15ec4a451b900" +checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4" dependencies = [ "bumpalo", "lazy_static", @@ -737,9 +643,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.74" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "088169ca61430fe1e58b8096c24975251700e7b1f6fd91cc9d59b04fb9b18bd4" +checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -747,9 +653,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.74" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be2241542ff3d9f241f5e2cb6dd09b37efe786df8851c54957683a49f0987a97" +checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b" dependencies = [ "proc-macro2", "quote", @@ -760,36 +666,21 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.74" +version = "0.2.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7cff876b8f18eed75a66cf49b65e7f967cb354a7aa16003fb55dbfd25b44b4f" +checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" [[package]] name = "web-sys" -version = "0.3.51" +version = "0.3.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e828417b379f3df7111d3a2a9e5753706cae29c41f7c4029ee9fd77f3e09e582" +checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283" 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 +691,56 @@ dependencies = [ ] [[package]] -name = "winapi-build" -version = "0.1.1" +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] -name = "winapi-i686-pc-windows-gnu" +name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "winapi-util" -version = "0.1.5" +name = "windows-sys" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" dependencies = [ - "winapi 0.3.9", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows_aarch64_msvc" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] -name = "ws2_32-sys" -version = "0.2.1" +name = "windows_i686_gnu" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] +checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" + +[[package]] +name = "windows_i686_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" @@ -3,38 +3,67 @@ # 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" name = "quiche" -version = "0.9.0" +version = "0.14.0" 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 +79,48 @@ version = "0.2" version = "0.4" features = ["std"] +[dependencies.octets] +version = "0.1" + [dependencies.qlog] -version = "0.4" +version = "0.7" optional = true [dependencies.ring] version = "0.16" + +[dependencies.sfv] +version = "0.9" +optional = true + [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", +] diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 12bddf8..b217d2a 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "quiche" -version = "0.9.0" +version = "0.14.0" authors = ["Alessandro Ghedini <alessandro@ghedini.me>"] edition = "2018" build = "src/build.rs" @@ -13,9 +13,7 @@ license = "BSD-2-Clause" include = [ "/*.md", "/*.toml", - "/CODEOWNERS", "/COPYING", - "/benches", "/deps/boringssl/**/*.[chS]", "/deps/boringssl/**/*.asm", "/deps/boringssl/src/**/*.cc", @@ -31,23 +29,25 @@ include = [ [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" @@ -58,21 +58,18 @@ libc = "0.2" libm = "0.2" ring = "0.16" lazy_static = "1" -boring-sys = { version = "1.0.2", optional = true } -qlog = { version = "0.4", path = "tools/qlog", optional = true } +octets = { version = "0.1", path = "../octets" } +boring = { version = "2.0.0", optional = true } +foreign-types-shared = { version = "0.3.0", optional = true } +qlog = { version = "0.7", path = "../qlog", optional = true } +sfv = { version = "0.9", optional = true } [target."cfg(windows)".dependencies] -winapi = { version = "0.3", features = ["wincrypt"] } +winapi = { version = "0.3", features = ["wincrypt", "ws2def", "ws2ipdef"] } [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"] @@ -1,5 +1,5 @@ name: "quiche" -description: "\360\237\245\247 Savoury implementation of the QUIC transport protocol and HTTP/3" +description: "\ud83e\udd67 Savoury implementation of the QUIC transport protocol and HTTP/3" third_party { url { type: HOMEPAGE @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/quiche/quiche-0.9.0.crate" + value: "https://static.crates.io/crates/quiche/quiche-0.14.0.crate" } - version: "0.9.0" + version: "0.14.0" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 6 - day: 8 + year: 2022 + month: 9 + day: 20 } } @@ -1 +1 @@ -include platform/prebuilts/rust:/OWNERS +include platform/prebuilts/rust:master:/OWNERS @@ -32,12 +32,11 @@ 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/ [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 +44,18 @@ Getting Started ### 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 @@ -97,9 +94,11 @@ incoming packets that belong to that connection from the network: ```rust loop { - let read = socket.recv(&mut buf).unwrap(); + let (read, from) = socket.recv_from(&mut buf).unwrap(); - let read = match conn.recv(&mut buf[..read]) { + let recv_info = quiche::RecvInfo { from }; + + let read = match conn.recv(&mut buf[..read], recv_info) { Ok(v) => v, Err(e) => { @@ -117,7 +116,7 @@ instead: ```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 +130,7 @@ loop { }, }; - socket.send(&out[..write]).unwrap(); + socket.send_to(&out[..write], &send_info.to).unwrap(); } ``` @@ -154,7 +153,7 @@ conn.on_timeout(); // 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 +167,29 @@ loop { }, }; - 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 @@ -221,11 +239,11 @@ receiving HTTP requests and responses on top of the QUIC transport protocol. [`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 +260,12 @@ be linked directly into C/C++ applications. 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.54 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 +306,34 @@ the BoringSSL directory with the ``QUICHE_BSSL_PATH`` environment variable: ### 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 +341,10 @@ You can build the quiche library using the following procedure. Note that 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/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 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CLANG) 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 @@ add_definitions(-DBORINGSSL_IMPLEMENTATION) # 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 @@ endif() 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 @@ set( 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 @@ add_library( 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 @@ add_library( 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 @@ add_library( 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 @@ add_library( 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 @@ add_library( 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 @@ add_library( 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 @@ add_executable( 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 @@ const uint32_t kOpenSSLReasonValues[] = { 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 @@ const uint32_t kOpenSSLReasonValues[] = { 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 @@ const uint32_t kOpenSSLReasonValues[] = { 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 @@ const uint32_t kOpenSSLReasonValues[] = { 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 @@ const uint32_t kOpenSSLReasonValues[] = { 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 @@ const uint32_t kOpenSSLReasonValues[] = { 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 @@ const char kOpenSSLReasonStringData[] = "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 @@ const char kOpenSSLReasonStringData[] = "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 @@ const char kOpenSSLReasonStringData[] = "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 @@ const char kOpenSSLReasonStringData[] = "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 @@ const char kOpenSSLReasonStringData[] = "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 @@ const char kOpenSSLReasonStringData[] = "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 @@ const char kOpenSSLReasonStringData[] = "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 @@ const char kOpenSSLReasonStringData[] = "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 @@ const char kOpenSSLReasonStringData[] = "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 @@ const char kOpenSSLReasonStringData[] = "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 @@ _gcm_init_v8: 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 @@ _gcm_gmult_v8: .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 @@ Ldone_v8: 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 @@ _gcm_init_v8: 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 @@ Ldone_v8: .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 @@ gcm_init_v8: 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 @@ gcm_gmult_v8: .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 @@ gcm_ghash_v8: 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 @@ gcm_init_v8: 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 @@ gcm_ghash_v8: .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 @@ endif() 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(CMAKE_COMPILER_IS_GNUCXX OR CLANG) 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 @@ if(WIN32) 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 @@ endif() 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 @@ endfunction() # 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 @@ endif() 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 @@ include_directories(../include) 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 @@ if(NOT OPENSSL_NO_ASM) 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 @@ if(NOT OPENSSL_NO_ASM) 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 @@ endif() function(perlasm dest src) get_filename_component(dir ${dest} DIRECTORY) - if ("${dir}" STREQUAL "") + if(dir STREQUAL "") set(dir ".") endif() @@ -104,7 +104,7 @@ if(FIPS_DELOCATE OR FIPS_SHARED) ) endif() -if(${ARCH} STREQUAL "arm") +if(ARCH STREQUAL "arm") set( CRYPTO_ARCH_SOURCES @@ -115,7 +115,7 @@ if(${ARCH} STREQUAL "arm") ) endif() -if(${ARCH} STREQUAL "aarch64") +if(ARCH STREQUAL "aarch64") set( CRYPTO_ARCH_SOURCES @@ -124,7 +124,7 @@ if(${ARCH} STREQUAL "aarch64") ) endif() -if(${ARCH} STREQUAL "ppc64le") +if(ARCH STREQUAL "ppc64le") set( CRYPTO_ARCH_SOURCES @@ -132,7 +132,7 @@ if(${ARCH} STREQUAL "ppc64le") ) endif() -if(${ARCH} STREQUAL "x86") +if(ARCH STREQUAL "x86") set( CRYPTO_ARCH_SOURCES @@ -141,7 +141,7 @@ if(${ARCH} STREQUAL "x86") ) endif() -if(${ARCH} STREQUAL "x86_64") +if(ARCH STREQUAL "x86_64") set( CRYPTO_ARCH_SOURCES @@ -211,6 +211,7 @@ add_library( 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 @@ add_library( 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 @@ add_library( 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 @@ add_executable( 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); } -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); - - len = a->length; +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) { - if (a->flags & ASN1_STRING_FLAG_BITS_LEFT) { - bits = (int)a->flags & 0x07; - } else { - for (; len > 0; len--) { - if (a->data[len - 1]) - break; + uint8_t last = str->data[len - 1]; + assert(last != 0); + for (; padding_bits < 7; padding_bits++) { + if (last & (1 << padding_bits)) { + 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; + } + *out_padding_bits = padding_bits; + return len; +} - ret = 1 + len; - if (pp == NULL) - return (ret); +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; +} - p = *pp; +int i2c_ASN1_BIT_STRING(const ASN1_BIT_STRING *a, unsigned char **pp) +{ + 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; + } - *(p++) = (unsigned char)bits; - d = a->data; - OPENSSL_memcpy(p, d, len); + uint8_t *p = *pp; + *(p++) = bits; + OPENSSL_memcpy(p, a->data, len); + if (len > 0) { + p[len - 1] &= (0xff << bits); + } p += len; - if (len > 0) - p[-1] &= (0xff << bits); *pp = p; return (ret); } @@ -251,7 +256,7 @@ int ASN1_BIT_STRING_get_bit(const ASN1_BIT_STRING *a, int n) * '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 @@ int i2d_ASN1_BOOLEAN(int a, unsigned char **pp) } 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 @@ void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x) 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 @@ void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x) 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 @@ ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s, 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 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, } /* 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 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, 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 @@ int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, 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 @@ ASN1_OBJECT *c2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp, } } - /* - * 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 @@ ASN1_OBJECT *c2i_ASN1_OBJECT(ASN1_OBJECT **a, const unsigned char **pp, 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 @@ void ASN1_OBJECT_free(ASN1_OBJECT *a) 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); + if (len < 0) { + len = strlen((const char *)s); + } - 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; + 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; + } } - if (t61) - return (V_ASN1_T61STRING); - if (ia5) - return (V_ASN1_IA5STRING); - return (V_ASN1_PRINTABLESTRING); + + return printable ? V_ASN1_PRINTABLESTRING : V_ASN1_IA5STRING; } diff --git a/deps/boringssl/src/crypto/x509/a_strex.c b/deps/boringssl/src/crypto/asn1/a_strex.c index 2c4824e..7829d67 100644 --- a/deps/boringssl/src/crypto/x509/a_strex.c +++ b/deps/boringssl/src/crypto/asn1/a_strex.c @@ -54,23 +54,27 @@ * copied and put under another distribution licence * [including the GNU Public Licence.] */ -#include <openssl/x509.h> +#include <openssl/asn1.h> +#include <ctype.h> #include <inttypes.h> #include <string.h> -#include <openssl/asn1.h> +#include <openssl/bio.h> #include <openssl/mem.h> -#include <openssl/obj.h> #include "charmap.h" -#include "../asn1/asn1_locl.h" +#include "internal.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. - */ + +// 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) @@ -79,26 +83,12 @@ ASN1_STRFLGS_ESC_CTRL | \ ASN1_STRFLGS_ESC_MSB) -static int send_bio_chars(void *arg, const void *buf, int len) +static int maybe_write(BIO *out, const void *buf, int len) { - if (!arg) - return 1; - if (BIO_write(arg, buf, len) != len) - return 0; - return 1; + /* If |out| is NULL, ignore the output but report the length. */ + return out == NULL || BIO_write(out, buf, len) == len; } -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 @@ -108,20 +98,20 @@ typedef int char_io (void *arg, const void *buf, int len); #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) + 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 (!io_ch(arg, tmphex, 10)) + if (!maybe_write(out, tmphex, 10)) return -1; return 10; } if (c > 0xff) { BIO_snprintf(tmphex, sizeof tmphex, "\\U%04" PRIX32, c); - if (!io_ch(arg, tmphex, 6)) + if (!maybe_write(out, tmphex, 6)) return -1; return 6; } @@ -135,19 +125,19 @@ static int do_esc_char(uint32_t c, unsigned char flags, char *do_quotes, if (chflgs & ASN1_STRFLGS_ESC_QUOTE) { if (do_quotes) *do_quotes = 1; - if (!io_ch(arg, &chtmp, 1)) + if (!maybe_write(out, &chtmp, 1)) return -1; return 1; } - if (!io_ch(arg, "\\", 1)) + if (!maybe_write(out, "\\", 1)) return -1; - if (!io_ch(arg, &chtmp, 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 (!io_ch(arg, tmphex, 3)) + if (!maybe_write(out, tmphex, 3)) return -1; return 3; } @@ -156,11 +146,11 @@ static int do_esc_char(uint32_t c, unsigned char flags, char *do_quotes, * character itself: backslash. */ if (chtmp == '\\' && flags & ESC_FLAGS) { - if (!io_ch(arg, "\\\\", 2)) + if (!maybe_write(out, "\\\\", 2)) return -1; return 2; } - if (!io_ch(arg, &chtmp, 1)) + if (!maybe_write(out, &chtmp, 1)) return -1; return 1; } @@ -175,8 +165,7 @@ static int do_esc_char(uint32_t c, unsigned char flags, char *do_quotes, */ static int do_buf(unsigned char *buf, int buflen, - int type, unsigned char flags, char *quotes, char_io *io_ch, - void *arg) + int type, unsigned char flags, char *quotes, BIO *out) { int i, outlen, len, charwidth; unsigned char orflags, *p, *q; @@ -208,6 +197,8 @@ static int do_buf(unsigned char *buf, int buflen, 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; @@ -248,17 +239,14 @@ static int do_buf(unsigned char *buf, int buflen, * 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); + 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, - io_ch, arg); + len = do_esc_char(c, (unsigned char)(flags | orflags), quotes, out); if (len < 0) return -1; outlen += len; @@ -269,19 +257,18 @@ static int do_buf(unsigned char *buf, int buflen, /* 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 int do_hex_dump(BIO *out, unsigned char *buf, int buflen) { static const char hexdig[] = "0123456789ABCDEF"; unsigned char *p, *q; char hextmp[2]; - if (arg) { + if (out) { p = buf; q = buf + buflen; while (p != q) { hextmp[0] = hexdig[*p >> 4]; hextmp[1] = hexdig[*p & 0xf]; - if (!io_ch(arg, hextmp, 2)) + if (!maybe_write(out, hextmp, 2)) return -1; p++; } @@ -292,41 +279,55 @@ static int do_hex_dump(char_io *io_ch, void *arg, unsigned char *buf, /* * "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. + * encoding. This uses the RFC 2253 #01234 format. */ -static int do_dump(unsigned long lflags, char_io *io_ch, void *arg, - const ASN1_STRING *str) +static int do_dump(unsigned long lflags, BIO *out, 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)) + if (!maybe_write(out, "#", 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) + 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; - t.value.ptr = (char *)str; - der_len = i2d_ASN1_TYPE(&t, NULL); - der_buf = OPENSSL_malloc(der_len); - if (!der_buf) + /* 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; - p = der_buf; - i2d_ASN1_TYPE(&t, &p); - outlen = do_hex_dump(io_ch, arg, der_buf, der_len); + } + int outlen = do_hex_dump(out, der_buf, der_len); OPENSSL_free(der_buf); - if (outlen < 0) + if (outlen < 0) { return -1; + } return outlen + 1; } @@ -353,8 +354,7 @@ static const signed char tag2nbyte[] = { * an error occurred. */ -static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags, - const ASN1_STRING *str) +int ASN1_STRING_print_ex(BIO *out, const ASN1_STRING *str, unsigned long lflags) { int outlen, len; int type; @@ -372,7 +372,7 @@ static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags, const char *tagname; tagname = ASN1_tag2str(type); outlen += strlen(tagname); - if (!io_ch(arg, tagname, outlen) || !io_ch(arg, ":", 1)) + if (!maybe_write(out, tagname, outlen) || !maybe_write(out, ":", 1)) return -1; outlen++; } @@ -396,7 +396,7 @@ static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags, } if (type == -1) { - len = do_dump(lflags, io_ch, arg, str); + len = do_dump(lflags, out, str); if (len < 0) return -1; outlen += len; @@ -415,219 +415,41 @@ static int do_print_ex(char_io *io_ch, void *arg, unsigned long lflags, type |= BUF_TYPE_CONVUTF8; } - len = do_buf(str->data, str->length, type, flags, "es, io_ch, NULL); + len = do_buf(str->data, str->length, type, flags, "es, NULL); if (len < 0) return -1; outlen += len; if (quotes) outlen += 2; - if (!arg) + if (!out) return outlen; - if (quotes && !io_ch(arg, "\"", 1)) + if (quotes && !maybe_write(out, "\"", 1)) return -1; - if (do_buf(str->data, str->length, type, flags, NULL, io_ch, arg) < 0) + if (do_buf(str->data, str->length, type, flags, NULL, out) < 0) return -1; - if (quotes && !io_ch(arg, "\"", 1)) + if (quotes && !maybe_write(out, "\"", 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 ASN1_STRING_print_ex_fp(FILE *fp, const ASN1_STRING *str, + 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) + 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; - 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); + int ret = ASN1_STRING_print_ex(bio, str, flags); + BIO_free(bio); + return ret; } -#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) +int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in) { ASN1_STRING stmp, *str = &stmp; int mbflag, type, ret; @@ -643,11 +465,186 @@ int ASN1_STRING_to_UTF8(unsigned char **out, ASN1_STRING *in) stmp.data = NULL; stmp.length = 0; stmp.flags = 0; - ret = - ASN1_mbstring_copy(&str, in->data, in->length, mbflag, - B_ASN1_UTF8STRING); + 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 @@ DEFINE_STACK_OF(ASN1_STRING_TABLE) 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 @@ ASN1_STRING *ASN1_STRING_set_by_NID(ASN1_STRING **out, 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 @@ ASN1_STRING *ASN1_STRING_set_by_NID(ASN1_STRING **out, * 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 @@ static int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *t) 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 @@ int ASN1_TIME_diff(int *pday, int *psec, 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; } -void ASN1_TYPE_set(ASN1_TYPE *a, int type, void *value) +const void *asn1_type_value_as_pointer(const ASN1_TYPE *a) { - if (a->value.ptr != NULL) { - ASN1_TYPE **tmp_a = &a; - ASN1_primitive_free((ASN1_VALUE **)tmp_a, NULL); + 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) +{ + 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 @@ int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t) 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 @@ int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len) 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 @@ const char *ASN1_tag2str(int tag) }; 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 @@ TEST(ASN1Test, IntegerSetting) { 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); + + len = i2d_func(obj, nullptr); + ASSERT_GT(len, 0); + EXPECT_EQ(len, static_cast<int>(expected.size())); - std::vector<uint8_t> buf(expected.size()); - uint8_t *ptr = buf.data(); - ASSERT_EQ(i2d_func(obj, &ptr), len); + 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, SerializeObject) { 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 index 3305ad1..3305ad1 100644 --- a/deps/boringssl/src/crypto/x509/charmap.h +++ b/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 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 @@ int OPENSSL_gmtime_diff(int *out_days, int *out_secs, const struct tm *from, /* 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_restore(int *len, unsigned char **out, ASN1_VALUE **pval, 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 @@ static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out, 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 @@ static int asn1_template_ex_i2d(ASN1_VALUE **pval, unsigned char **out, 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 @@ static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out, 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 @@ static int asn1_set_seq_out(STACK_OF(ASN1_VALUE) *sk, unsigned char **out, 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 @@ static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *putype, /* 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 @@ static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *putype, 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 @@ void ASN1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it) 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 void asn1_template_clear(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt) 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 @@ static int ASN1_primitive_new(ASN1_VALUE **pval, const ASN1_ITEM *it) 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_new(BIO *bio) { } 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 @@ static int conn_new(BIO *bio) { 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 void conn_close_socket(BIO *bio) { } 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_new(BIO *bio) { } 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 @@ BIO *BIO_new_fp(FILE *stream, int close_flag) { 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 @@ static const BIO_METHOD methods_filep = { 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 void bio_destroy_pair(BIO *bio) { } 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 @@ static int closesocket(int sock) { } #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_free(BIO *bio) { } 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 @@ static const BIO_METHOD methods_sockp = { 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 @@ TEST(CBSTest, GetPrefixedBad) { 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, InitUninitialized) { } 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 @@ TEST(CBBTest, Basic) { 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 @@ TEST(CBBTest, Basic) { 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 @@ int CBB_add_bytes(CBB *cbb, const uint8_t *data, size_t len) { 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 @@ int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out) { 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 @@ static const struct KnownAEAD kAEADs[] = { "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 @@ static const struct KnownAEAD kAEADs[] = { "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 @@ TEST_P(PerAEADTest, ABI) { : 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_cipherbynid(int nid) { } 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, WycheproofAESCBC) { } }); } + +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 @@ static int aead_tls_open(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, 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 @@ static int aead_aes_128_cbc_sha1_tls_implicit_iv_init( 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 @@ static int aead_aes_256_cbc_sha1_tls_implicit_iv_init( 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 @@ static const EVP_AEAD aead_aes_128_cbc_sha1_tls_implicit_iv = { 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 @@ static const EVP_AEAD aead_aes_256_cbc_sha1_tls_implicit_iv = { 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 @@ const EVP_AEAD *EVP_aead_aes_128_cbc_sha1_tls_implicit_iv(void) { 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 @@ const EVP_AEAD *EVP_aead_aes_256_cbc_sha1_tls_implicit_iv(void) { 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 @@ void EVP_tls_cbc_copy_mac(uint8_t *out, size_t md_size, const uint8_t *in, // 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 @@ int EVP_tls_cbc_record_digest_supported(const EVP_MD *md); // 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 @@ int EVP_tls_cbc_record_digest_supported(const EVP_MD *md); // 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 @@ void EVP_tls_cbc_copy_mac(uint8_t *out, size_t md_size, const uint8_t *in, 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); -} +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_sha256_transform(HASH_CTX *ctx, const uint8_t *block) { - SHA256_Transform(&ctx->sha256, block); -} + // 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); + } -static void tls1_sha512_transform(HASH_CTX *ctx, const uint8_t *block) { - SHA512_Transform(&ctx->sha512, block); -} + // 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; + } -// 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); -} + input_idx += SHA_CBLOCK - block_start; -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); + // 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]; + } } -} -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); + // 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 @@ int EVP_tls_cbc_digest_record(const EVP_MD *md, uint8_t *md_out, 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); - - // 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); - } - } - - uint8_t mac_out[EVP_MAX_MD_SIZE]; - OPENSSL_memset(mac_out, 0, sizeof(mac_out)); - - // 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; - } + SHA_CTX ctx; + SHA1_Init(&ctx); + SHA1_Update(&ctx, hmac_pad, SHA_CBLOCK); + SHA1_Update(&ctx, header, 13); - 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; - } + // 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; } - 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 public minimum length directly. This reduces the number of blocks + // that must be computed in constant-time. + SHA1_Update(&ctx, data, min_data_size); + + // 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 @@ struct conf_st { 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 @@ static void value_free(CONF_VALUE *value) { 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 *NCONF_get_string(const CONF *conf, const char *section, 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 @@ static int def_load_bio(CONF *conf, BIO *in, long *out_error_line) { 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 @@ void OPENSSL_cpuid_setup(void) { 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 @@ void OPENSSL_cpuid_setup(void) { 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 @@ TEST(CryptoTest, Version) { 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 @@ static void table_select(ge_precomp *t, int pos, signed char b) { // // 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 @@ typedef struct { } 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 @@ static const struct nid_to_digest nid_to_digest_mapping[] = { {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 @@ static const EVP_MD *cbs_to_md(const CBS *cbs) { } 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 @@ static void TestDigest(const DigestTestVector *test) { 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 @@ static void TestDigest(const DigestTestVector *test) { 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 @@ static void TestDigest(const DigestTestVector *test) { 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 @@ static void TestDigest(const DigestTestVector *test) { 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 @@ static void TestDigest(const DigestTestVector *test) { 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 @@ void ERR_clear_system_error(void) { 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 @@ static const char *const kLibraryNames[ERR_NUM_LIBS] = { "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 @@ const char *ERR_lib_error_string(uint32_t packed_error) { 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 @@ const char *ERR_reason_error_string(uint32_t packed_error) { 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 @@ TEST(ErrTest, String) { // 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 @@ int EVP_PKEY_CTX_get_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD **out_md) { 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 @@ if(${ARCH} STREQUAL "x86_64") ) endif() -if(${ARCH} STREQUAL "x86") +if(ARCH STREQUAL "x86") set( BCM_ASM_SOURCES @@ -40,7 +40,7 @@ if(${ARCH} STREQUAL "x86") ) endif() -if(${ARCH} STREQUAL "arm") +if(ARCH STREQUAL "arm") set( BCM_ASM_SOURCES @@ -56,7 +56,7 @@ if(${ARCH} STREQUAL "arm") ) endif() -if(${ARCH} STREQUAL "aarch64") +if(ARCH STREQUAL "aarch64") set( BCM_ASM_SOURCES @@ -71,7 +71,7 @@ if(${ARCH} STREQUAL "aarch64") ) endif() -if(${ARCH} STREQUAL "ppc64le") +if(ARCH STREQUAL "ppc64le") set( BCM_ASM_SOURCES @@ -160,7 +160,7 @@ if(FIPS_DELOCATE) 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 @@ if(FIPS_DELOCATE) 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 @@ if(FIPS_DELOCATE) OBJECT fips_shared_support.c - is_fips.c ) add_dependencies(fipsmodule global_target) @@ -240,7 +242,6 @@ elseif(FIPS_SHARED) OBJECT fips_shared_support.c - is_fips.c ) add_dependencies(fipsmodule global_target) @@ -273,7 +274,6 @@ else() 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 @@ static void TestQuotient(BIGNUMFileTest *t, BN_CTX *ctx) { 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 @@ static void TestQuotient(BIGNUMFileTest *t, BN_CTX *ctx) { } } - 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 @@ int BN_div(BIGNUM *quotient, BIGNUM *rem, const BIGNUM *numerator, // 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 @@ int BN_div(BIGNUM *quotient, BIGNUM *rem, const BIGNUM *numerator, 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 @@ void bn_mod_add_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b, 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 @@ int bn_div_consttime(BIGNUM *quotient, BIGNUM *remainder, 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 @@ int bn_lcm_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) { 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_comba4(BN_ULONG r[8], const BN_ULONG a[4], const BN_ULONG b[4]); 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 @@ int bn_sqr_consttime(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx); // 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 @@ int EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, ctx->buf_len = 0; ctx->final_used = 0; - ctx->block_mask = ctx->cipher->block_size - 1; return 1; } @@ -238,16 +238,31 @@ int EVP_DecryptInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher, 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 @@ int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len, 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 @@ int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len, } } - 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 @@ int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len, *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 @@ int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len, *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_EncryptFinal_ex(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len) { 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 @@ int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len, 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 @@ typedef struct { 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; + + 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; + } + } - mode = ctx->cipher->flags & EVP_CIPH_MODE_MASK; 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 @@ static int aes_gcm_init_key(EVP_CIPHER_CTX *ctx, const uint8_t *key, 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 @@ int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) { 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 @@ static void sha512_256_init(EVP_MD_CTX *ctx) { 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 @@ extern "C" { // 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. -// -// 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. +// block function that performs the actual hash calculations. // -// |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 @@ extern "C" { // 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|. - -#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN) -#error "DATA_ORDER must be defined!" -#endif - -#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_; - +// |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. + +// 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); + +// 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_set_to_safe_point(const EC_GROUP *group, EC_RAW_POINT *out) { 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 @@ void EC_KEY_free(EC_KEY *r) { 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 @@ struct ec_key_st { 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,70 +199,74 @@ int ECDSA_do_verify(const uint8_t *digest, size_t digest_len, 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); - - // 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)); + if (ec_scalar_is_zero(group, &r)) { + *out_retry = 1; + return NULL; + } - ret = 1; + // 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); + + // s = m + priv_key * r. + EC_SCALAR tmp; + digest_to_scalar(group, &tmp, digest, digest_len); + ec_scalar_add(group, &s, &s, &tmp); + + // 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; + } -err: - OPENSSL_cleanse(&k, sizeof(k)); + 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_do_sign(const uint8_t *digest, size_t digest_len, - const EC_KEY *eckey) { +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; @@ -272,57 +277,63 @@ ECDSA_SIG *ECDSA_do_sign(const uint8_t *digest, size_t digest_len, OPENSSL_PUT_ERROR(ECDSA, ERR_R_PASSED_NULL_PARAMETER); return NULL; } - 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); + 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); +} - 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; - } - - // 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); +// 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); +} - // Compute tmp = m + priv_key * r. - ec_scalar_add(group, &tmp, &m, &s); +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) { + OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED); + return NULL; + } - // 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; - } + 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 BIGNUM *order = EC_GROUP_get0_order(group); + const EC_SCALAR *priv_key = &eckey->priv_key->scalar; - ok = 1; + // 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); -err: - if (!ok) { - ECDSA_SIG_free(ret); - ret = NULL; + for (;;) { + EC_SCALAR k; + if (!ec_random_nonzero_scalar(group, &k, additional_data)) { + return NULL; + } + + int retry; + ECDSA_SIG *sig = + ecdsa_sign_impl(group, &retry, priv_key, &k, digest, digest_len); + if (sig != NULL || !retry) { + return sig; + } } - 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 @@ TEST(ECDSATest, BuiltinCurves) { 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 @@ TEST(ECDSATest, SignTestVectors) { 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 @@ TEST(ECDSATest, SignTestVectors) { 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 @@ uint8_t *MD4(const uint8_t *data, size_t len, uint8_t out[MD4_DIGEST_LENGTH]) { 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 @@ int MD4_Init(MD4_CTX *md4) { 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 @@ void md4_block_data_order(uint32_t *state, const uint8_t *data, size_t num); } 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 @@ void md4_block_data_order(uint32_t *state, const uint8_t *data, size_t num) { 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 @@ void md4_block_data_order(uint32_t *state, const uint8_t *data, size_t num) { } } -#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 @@ void md4_block_data_order(uint32_t *state, const uint8_t *data, size_t num) { #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 @@ static void md5_block_data_order(uint32_t *state, const uint8_t *data, 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 @@ static void md5_block_data_order(uint32_t *state, const uint8_t *data, #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 @@ static void md5_block_data_order(uint32_t *state, const uint8_t *data, 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 @@ static void md5_block_data_order(uint32_t *state, const uint8_t *data, #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 @@ static void md5_block_data_order(uint32_t *state, const uint8_t *data, #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) { - size_t n; - const uint8_t *iv = ivec; - 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); + size_t n; + const uint8_t *iv = ivec; 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_encrypt(const uint8_t *in, uint8_t *out, size_t len, 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 @@ void CRYPTO_cbc128_decrypt(const uint8_t *in, uint8_t *out, size_t len, } 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 @@ void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len, } 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 @@ void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len, } 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 @@ static void ctr128_inc(uint8_t *counter) { } 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 @@ void CRYPTO_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len, 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 @@ void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len, 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 @@ void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len, } (*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 @@ void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len, 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 @@ static const size_t kSizeTWithoutLower4Bits = (size_t) -16; #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 @@ int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const AES_KEY *key, (*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 @@ int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const AES_KEY *key, (*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 @@ int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const AES_KEY *key, (*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 @@ int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const AES_KEY *key, (*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 @@ static void gcm_mul64_nohw(uint64_t *out_lo, uint64_t *out_hi, uint64_t a, #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 @@ TEST(GCMTest, ABI) { 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 @@ extern "C" { #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 @@ typedef struct { 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 @@ OPENSSL_STATIC_ASSERT(16 % sizeof(size_t) == 0, 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 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len, // 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 @@ void RAND_load_entropy(const uint8_t *entropy, size_t entropy_len, // 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 @@ int CRYPTO_sysrand_if_available(uint8_t *buf, size_t len); #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 @@ struct rand_thread_state { // 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 @@ void CRYPTO_get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len, #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 @@ static void get_seed_entropy(uint8_t *out_entropy, size_t out_entropy_len, 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 @@ static void rand_get_seed(struct rand_thread_state *state, 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 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len, // 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 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len, 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 @@ void RAND_bytes_with_additional_data(uint8_t *out, size_t out_len, } #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 @@ static void init_once(void) { } #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 @@ static void wait_for_entropy(void) { 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 @@ static void wait_for_entropy(void) { 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 @@ static int fill_with_entropy(uint8_t *out, size_t len, int block, int seed) { 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 @@ static int fill_with_entropy(uint8_t *out, size_t len, int block, int seed) { 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 @@ static int fill_with_entropy(uint8_t *out, size_t len, int block, int seed) { 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_sysrand(uint8_t *out, size_t requested) { } } -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 @@ static const struct pkcs1_sig_prefix kPKCS1SigPrefixes[] = { }; 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 @@ int RSA_add_pkcs1_prefix(uint8_t **out_msg, size_t *out_msg_len, 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 @@ int RSA_add_pkcs1_prefix(uint8_t **out_msg, size_t *out_msg_len, 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 @@ int RSA_add_pkcs1_prefix(uint8_t **out_msg, size_t *out_msg_len, } 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 @@ int RSA_add_pkcs1_prefix(uint8_t **out_msg, size_t *out_msg_len, 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 @@ int RSA_sign(int hash_nid, const uint8_t *in, unsigned in_len, uint8_t *out, 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 @@ err: } 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 @@ int RSA_sign_pss_mgf1(RSA *rsa, size_t *out_len, uint8_t *out, size_t max_out, 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 @@ int RSA_verify(int hash_nid, const uint8_t *msg, size_t msg_len, 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 @@ int RSA_verify(int hash_nid, const uint8_t *msg, size_t msg_len, } 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 @@ out: 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 @@ int RSA_verify_pss_mgf1(RSA *rsa, const uint8_t *msg, size_t msg_len, 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 @@ err: } 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 @@ static int check_mod_inverse(int *out_ok, const BIGNUM *a, const BIGNUM *ainv, 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 @@ int RSA_check_key(const RSA *key) { // 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 @@ int RSA_check_key(const RSA *key) { 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 @@ int rsa_check_public_key(const RSA *rsa) { 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 @@ int rsa_check_public_key(const RSA *rsa) { // [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 @@ static int rsa_generate_key_impl(RSA *rsa, int bits, const BIGNUM *e_value, // 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 @@ int boringssl_fips_self_test( // 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 @@ uint8_t *SHA1(const uint8_t *data, size_t len, uint8_t out[SHA_DIGEST_LENGTH]) { return out; } -#define DATA_ORDER_IS_BIG_ENDIAN - -#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) +#if !defined(SHA1_ASM) +static void sha1_block_data_order(uint32_t *state, const uint8_t *data, + size_t num); +#endif + +void SHA1_Transform(SHA_CTX *c, const uint8_t data[SHA_CBLOCK]) { + sha1_block_data_order(c->h, data, 1); +} + +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 HASH_UPDATE SHA1_Update -#define HASH_TRANSFORM SHA1_Transform -#define HASH_FINAL SHA1_Final -#define HASH_BLOCK_DATA_ORDER sha1_block_data_order #define ROTATE(a, n) (((a) << (n)) | ((a) >> (32 - (n)))) #define Xupdate(a, ix, ia, ib, ic, id) \ do { \ @@ -114,13 +118,6 @@ uint8_t *SHA1(const uint8_t *data, size_t len, uint8_t out[SHA_DIGEST_LENGTH]) { (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 @@ static void sha1_block_data_order(uint32_t *state, const uint8_t *data, #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 @@ static void sha1_block_data_order(uint32_t *state, const uint8_t *data, 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 @@ static void sha1_block_data_order(uint32_t *state, const uint8_t *data, } #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 @@ static void sha1_block_data_order(uint32_t *state, const uint8_t *data, #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 @@ uint8_t *SHA256(const uint8_t *data, size_t len, return out; } -int SHA224_Update(SHA256_CTX *ctx, const void *data, size_t len) { - return SHA256_Update(ctx, data, len); -} +#ifndef SHA256_ASM +static void sha256_block_data_order(uint32_t *state, const uint8_t *in, + size_t num); +#endif -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); +void SHA256_Transform(SHA256_CTX *c, const uint8_t data[SHA256_CBLOCK]) { + sha256_block_data_order(c->h, data, 1); } -#define DATA_ORDER_IS_BIG_ENDIAN +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; +} -#define HASH_CTX SHA256_CTX -#define HASH_CBLOCK 64 -#define HASH_DIGEST_LENGTH 32 +int SHA224_Update(SHA256_CTX *ctx, const void *data, size_t len) { + return SHA256_Update(ctx, data, len); +} -// 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) +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; + } -#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 + 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; +} -#include "../digest/md32_common.h" +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 @@ static void sha256_block_data_order(uint32_t *state, const uint8_t *data, 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 @@ void SHA256_TransformBlocks(uint32_t state[8], const uint8_t *data, 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 @@ void SHA256_TransformBlocks(uint32_t state[8], const uint8_t *data, #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 *SHA512_256(const uint8_t *data, size_t len, 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 @@ static void sha512_block_data_order(uint64_t *state, const uint8_t *in, 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 @@ int SHA512_256_Update(SHA512_CTX *sha, const void *data, size_t len) { 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_Update(SHA512_CTX *c, const void *in_data, size_t len) { } 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 @@ int SHA512_Final(uint8_t out[SHA512_DIGEST_LENGTH], SHA512_CTX *sha) { } 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 @@ int SHA512_Final(uint8_t out[SHA512_DIGEST_LENGTH], SHA512_CTX *sha) { 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 @@ static const uint64_t K512[80] = { #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 @@ static void sha512_block_data_order(uint64_t *state, const uint8_t *in, 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 @@ static void sha512_block_data_order(uint64_t *state, const uint8_t *in, 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,59 +12,66 @@ * 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"; -static int add_label_string(CBB *cbb, const char *label) { - return CBB_add_bytes(cbb, (const uint8_t *)label, strlen(label)); -} +// Low-level labeled KDF functions. -// 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}; +static const char kHpkeVersionId[] = "HPKE-v1"; -// 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 add_label_string(CBB *cbb, const char *label) { + return CBB_add_bytes(cbb, (const uint8_t *)label, strlen(label)); } static int hpke_labeled_extract(const EVP_MD *hkdf_md, uint8_t *out_key, @@ -72,10 +79,10 @@ static int hpke_labeled_extract(const EVP_MD *hkdf_md, uint8_t *out_key, 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 @@ static int hpke_labeled_expand(const EVP_MD *hkdf_md, uint8_t *out_key, 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 @@ static int hpke_labeled_expand(const EVP_MD *hkdf_md, uint8_t *out_key, 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; } -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(); +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; } - OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); - return NULL; + + 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)); } -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(); +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; } - OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR); - return NULL; + EVP_HPKE_KEY_zero(key); + return key; } -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) { +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; +} + +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 @@ static int hpke_key_schedule(EVP_HPKE_CTX *hpke, uint8_t mode, 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 @@ static int hpke_key_schedule(EVP_HPKE_CTX *hpke, uint8_t mode, } // 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 @@ void EVP_HPKE_CTX_cleanup(EVP_HPKE_CTX *ctx) { 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); -} - -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; - } - 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)) { - return 0; +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; } - return 1; + EVP_HPKE_CTX_zero(ctx); + return ctx; } -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; +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_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)) { - 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)) { +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_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)) { +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; } -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 @@ class HPKETestVector { 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; - - switch (mode_) { - case HPKEMode::kBase: - ASSERT_GT(secret_key_e_.size(), 0u); - ASSERT_EQ(psk_.size(), 0u); - ASSERT_EQ(psk_id_.size(), 0u); - - // 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())); - - // 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; - - case HPKEMode::kPSK: - ASSERT_GT(secret_key_e_.size(), 0u); - ASSERT_GT(psk_.size(), 0u); - ASSERT_GT(psk_id_.size(), 0u); - - // 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; - } + 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()); + + // 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(); + } - VerifyEncryptions(sender_ctx.get(), receiver_ctx.get()); - VerifyExports(sender_ctx.get()); - VerifyExports(receiver_ctx.get()); + 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_)); + + 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_)); + + // 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())); + + VerifyRecipient(recipient_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 @@ class HPKETestVector { 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 @@ class HPKETestVector { 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 FileTestReadInt(FileTest *file_test, T *out, const std::string &key) { 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 @@ bool HPKETestVector::ReadFromFileTest(FileTest *t) { 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 @@ TEST(HPKETest, VerifyTestVectors) { // 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 @@ TEST(HPKETest, RoundTrip) { 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); - - for (uint16_t kdf_id : kdf_ids) { - for (uint16_t aead_id : aead_ids) { + 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 (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())); - - // 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())); + 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 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 @@ TEST(HPKETest, RoundTrip) { 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 @@ TEST(HPKETest, X25519EncapSmallOrderPoint) { 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); - - 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}; + ScopedEVP_HPKE_KEY key; + ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256())); - 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)); - - // 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)); + 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 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 @@ TEST(HPKETest, SenderInvalidOpen) { 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 @@ TEST(HPKETest, SenderInvalidOpen) { // 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 @@ TEST(HPKETest, SenderInvalidOpen) { 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), - {}}; +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); + + 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(); +} - // Generate the receiver's keypair. +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); - // Vary the PSK and PSKID inputs for the sender and receiver, trying all four - // permutations of empty and nonempty inputs. + // 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); +} - for (const auto &psk : kPSKValues) { - for (const auto &psk_id : kPSKValues) { - const bool kExpectSuccess = psk.size() > 0 && psk_id.size() > 0; +TEST(HPKETest, SetupRecipientWrongLengthEnc) { + ScopedEVP_HPKE_KEY key; + ASSERT_TRUE(EVP_HPKE_KEY_generate(key.get(), EVP_hpke_x25519_hkdf_sha256())); - ASSERT_EQ(ERR_get_error(), 0u); + const uint8_t bogus_enc[X25519_PUBLIC_VALUE_LEN + 5] = {0xff}; - 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); - - 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(); - - 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); - - 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(); - } - } + 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(); +} + +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 @@ movq %rsp, %rbp .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 @@ vpaddw %ymm5, %ymm7, %ymm15 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 @@ vpaddw %ymm13, %ymm15, %ymm1 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 @@ vmovdqa %ymm1, 4128(%rax) 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 @@ vpaddw %ymm5, %ymm7, %ymm15 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 @@ vpaddw %ymm13, %ymm15, %ymm1 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 @@ vmovdqa %ymm1, 4160(%rax) 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 328(%rsi), %ymm12 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 @@ vpaddw %ymm5, %ymm7, %ymm15 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 @@ vpaddw %ymm13, %ymm15, %ymm1 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 @@ vmovdqa %ymm1, 4192(%rax) 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 @@ vpaddw %ymm5, %ymm7, %ymm15 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 @@ vpaddw %ymm13, %ymm15, %ymm1 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 @@ vmovdqa %ymm1, 4128(%r11) 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 @@ vpaddw %ymm5, %ymm7, %ymm15 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 @@ vpaddw %ymm13, %ymm15, %ymm1 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 @@ vmovdqa %ymm1, 4160(%r11) 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 328(%rdx), %ymm12 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 @@ vpaddw %ymm5, %ymm7, %ymm15 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 @@ vpaddw %ymm13, %ymm15, %ymm1 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 @@ vmovdqa %ymm1, 4192(%r11) 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 @@ vpaddw %ymm13, %ymm15, %ymm1 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 @@ vpunpcklwd 1248(%rax), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpunpcklwd 1280(%rax), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpunpcklwd 1312(%rax), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm6, %ymm11, %ymm15 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 @@ vpunpcklwd 1248(%r11), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpunpcklwd 1280(%r11), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpunpcklwd 1312(%r11), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm6, %ymm11, %ymm15 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 @@ vmovdqa 1568(%r9), %ymm11 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 @@ vpmullw %ymm2, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm7, %ymm15 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 @@ vpmullw %ymm4, %ymm7, %ymm15 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 @@ vpmullw %ymm4, %ymm8, %ymm15 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 @@ vpmullw %ymm4, %ymm9, %ymm15 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 @@ vmovdqa 1728(%r9), %ymm10 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 @@ vpmullw %ymm2, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm8, %ymm15 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 @@ vpmullw %ymm4, %ymm7, %ymm15 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 %ymm0, %ymm7, %ymm8 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 $704, %r9 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 @@ vpmullw %ymm2, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm7, %ymm15 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 @@ vpmullw %ymm4, %ymm7, %ymm15 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 @@ vpmullw %ymm4, %ymm8, %ymm15 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 @@ vpmullw %ymm4, %ymm9, %ymm15 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 @@ vpmullw %ymm2, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm8, %ymm15 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 @@ vpmullw %ymm4, %ymm7, %ymm15 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 %ymm0, %ymm7, %ymm8 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 @@ vpmullw %ymm2, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm7, %ymm15 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 @@ vpmullw %ymm4, %ymm7, %ymm15 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 @@ vpmullw %ymm4, %ymm8, %ymm15 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 @@ vpmullw %ymm4, %ymm9, %ymm15 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 @@ vpmullw %ymm2, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm8, %ymm15 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 @@ vpmullw %ymm4, %ymm7, %ymm15 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 %ymm0, %ymm7, %ymm8 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 @@ vpmullw %ymm2, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm7, %ymm15 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 @@ vpmullw %ymm4, %ymm7, %ymm15 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 @@ vpmullw %ymm4, %ymm8, %ymm15 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 @@ vpmullw %ymm4, %ymm9, %ymm15 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 @@ vpmullw %ymm2, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm7, %ymm15 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 @@ vpmullw %ymm3, %ymm8, %ymm15 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 @@ vpmullw %ymm4, %ymm7, %ymm15 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 %ymm0, %ymm7, %ymm8 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 4224(%r10), %ymm0, %ymm0 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 4256(%r10), %ymm0, %ymm0 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 4288(%r10), %ymm0, %ymm0 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 4320(%r10), %ymm0, %ymm0 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 4352(%r10), %ymm0, %ymm0 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 4384(%r10), %ymm0, %ymm0 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 4416(%r10), %ymm0, %ymm0 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 4448(%r10), %ymm0, %ymm0 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 4480(%r10), %ymm0, %ymm0 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 4512(%r10), %ymm0, %ymm0 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 @@ vpunpcklwd 3232(%r10), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpunpcklwd 3744(%r10), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpunpcklwd 4256(%r10), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpunpcklwd 4640(%r10), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpunpcklwd 5152(%r10), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpunpcklwd 5664(%r10), %ymm2, %ymm0 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 @@ vinserti128 $0, %xmm9, %ymm2, %ymm15 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm3, %ymm3 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 @@ vpsllw $7, %ymm5, %ymm7 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 @@ vpermq $206, %ymm9, %ymm9 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 @@ vpermq $206, %ymm9, %ymm9 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 @@ vpermq $206, %ymm9, %ymm9 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 @@ vpand mask_mod8192(%rip), %ymm3, %ymm3 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm10, %ymm10 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 @@ vpsllw $7, %ymm11, %ymm3 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 @@ vpermq $206, %ymm7, %ymm7 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 @@ vpermq $206, %ymm7, %ymm7 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 @@ vpermq $206, %ymm7, %ymm7 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 @@ vpand mask_mod8192(%rip), %ymm10, %ymm10 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm8, %ymm8 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 @@ vpsllw $7, %ymm5, %ymm10 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 @@ vpermq $206, %ymm3, %ymm3 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 @@ vpermq $206, %ymm3, %ymm3 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 @@ vpermq $206, %ymm3, %ymm3 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 @@ vpand mask_mod8192(%rip), %ymm8, %ymm8 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm4, %ymm4 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 @@ vpsllw $7, %ymm11, %ymm8 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 @@ vpermq $206, %ymm10, %ymm10 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 @@ vpermq $206, %ymm10, %ymm10 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 @@ vpermq $206, %ymm10, %ymm10 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 @@ vpand mask_mod8192(%rip), %ymm4, %ymm4 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm9, %ymm9 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 @@ vpsllw $7, %ymm5, %ymm4 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 @@ vmovdqu 0(%rdi), %ymm7 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 @@ vpermq $206, %ymm2, %ymm2 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 @@ vpermq $206, %ymm2, %ymm2 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 @@ vpermq $206, %ymm2, %ymm2 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm7, %ymm7 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 @@ vpsllw $7, %ymm11, %ymm9 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 @@ vmovdqu 88(%rdi), %ymm3 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 @@ vpermq $206, %ymm8, %ymm8 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 @@ vpermq $206, %ymm8, %ymm8 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 @@ vpermq $206, %ymm8, %ymm8 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm3, %ymm3 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 @@ vpsllw $7, %ymm5, %ymm7 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 @@ vmovdqu 176(%rdi), %ymm10 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 @@ vpermq $206, %ymm4, %ymm4 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 @@ vpermq $206, %ymm4, %ymm4 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 @@ vpermq $206, %ymm4, %ymm4 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm10, %ymm10 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 @@ vpsllw $7, %ymm11, %ymm3 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 @@ vmovdqu 264(%rdi), %ymm2 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 @@ vpermq $206, %ymm9, %ymm9 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 @@ vpermq $206, %ymm9, %ymm9 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 @@ vpermq $206, %ymm9, %ymm9 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm2, %ymm2 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 @@ vpsllw $7, %ymm5, %ymm10 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 @@ vpand mask5_3_5_3(%rip), %ymm10, %ymm10 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 @@ vpand mask_mod8192(%rip), %ymm2, %ymm2 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm8, %ymm8 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 @@ vpsllw $7, %ymm11, %ymm2 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 @@ vpand mask5_3_5_3(%rip), %ymm2, %ymm2 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 @@ vpand mask_mod8192(%rip), %ymm8, %ymm8 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm4, %ymm4 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 @@ vpsllw $7, %ymm5, %ymm8 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 @@ vpand mask5_3_5_3(%rip), %ymm8, %ymm8 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 @@ vpand mask_mod8192(%rip), %ymm4, %ymm4 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm9, %ymm9 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 @@ vpsllw $7, %ymm11, %ymm4 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 @@ vpand mask5_3_5_3(%rip), %ymm4, %ymm4 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 @@ vpand mask_mod8192(%rip), %ymm9, %ymm9 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm3, %ymm3 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 @@ vpsllw $7, %ymm5, %ymm9 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 @@ vpermq $206, %ymm7, %ymm7 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm10, %ymm10 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 @@ vpsllw $7, %ymm11, %ymm3 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 @@ vpermq $206, %ymm4, %ymm4 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm2, %ymm2 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 @@ vpsllw $7, %ymm5, %ymm10 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 @@ vpermq $206, %ymm9, %ymm9 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm8, %ymm8 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 @@ vpsllw $7, %ymm11, %ymm2 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 @@ vpermq $206, %ymm3, %ymm3 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsubw %ymm0, %ymm7, %ymm7 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm7, %ymm7 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 @@ vpsllw $7, %ymm5, %ymm8 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 @@ vpand mask5_3_5_3(%rip), %ymm8, %ymm8 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 @@ vpand mask_mod8192(%rip), %ymm3, %ymm3 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm4, %ymm4 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 @@ vpsllw $7, %ymm11, %ymm7 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 @@ vpand mask5_3_5_3(%rip), %ymm7, %ymm7 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 @@ vpand mask_mod8192(%rip), %ymm2, %ymm2 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm9, %ymm9 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 @@ vpsllw $7, %ymm5, %ymm4 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 @@ vpand mask5_3_5_3(%rip), %ymm4, %ymm4 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 @@ vpand mask_mod8192(%rip), %ymm8, %ymm8 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm3, %ymm3 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 @@ vpsllw $7, %ymm11, %ymm9 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 @@ vpand mask5_3_5_3(%rip), %ymm9, %ymm9 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm2, %ymm2 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 @@ vpsllw $7, %ymm5, %ymm3 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 @@ vpermq $139, %ymm10, %ymm10 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 @@ vpand mask_mod8192(%rip), %ymm2, %ymm2 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm8, %ymm8 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 @@ vpsllw $7, %ymm11, %ymm2 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 @@ vpermq $139, %ymm9, %ymm9 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 @@ vpand mask_mod8192(%rip), %ymm8, %ymm8 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm7, %ymm7 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 @@ vpsllw $7, %ymm5, %ymm8 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 @@ vpermq $139, %ymm3, %ymm3 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 @@ vpand mask_mod8192(%rip), %ymm7, %ymm7 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 @@ vpsrld $1, %ymm6, %ymm6 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 @@ vpsrld $1, %ymm4, %ymm4 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 @@ vpsllw $7, %ymm11, %ymm7 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 @@ vpmullw %ymm13, %ymm9, %ymm9 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 @@ vpermq $139, %ymm2, %ymm2 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 @@ vmovdqu %xmm4, 1384(%rdi) 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 @@ OPENSSL_UNUSED static void poly_print(const struct poly *p) { 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 @@ static void poly_mul_vec_aux(vec_t *restrict out, vec_t *restrict scratch, } // 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 @@ static void poly_mul_vec(struct poly *out, const struct poly *x, 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 @@ static void poly_mul_novec_aux(uint16_t *out, uint16_t *scratch, } // 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 @@ static void poly_mul_novec(struct poly *out, const struct poly *x, 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 @@ static void poly_invert_mod2(struct poly *out, const struct poly *in) { } // 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 @@ static void poly_invert(struct poly *out, const struct poly *in) { // 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 @@ static struct public_key *public_key_from_external( 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 @@ static struct private_key *private_key_from_external( 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); + 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_short_sample(&vars->m, in); + poly_short_sample(&vars->r, in + HRSS_SAMPLE_BYTES); + poly_lift(&vars->m_lifted, &vars->m); + + poly_mul(&vars->scratch, &vars->prh_plus_m, &vars->r, &pub->ph); for (unsigned i = 0; i < N; i++) { - prh_plus_m.v[i] += m_lifted.v[i]; + vars->prh_plus_m.v[i] += vars->m_lifted.v[i]; } - poly_marshal(out_ciphertext, &prh_plus_m); + poly_marshal(out_ciphertext, &vars->prh_plus_m); + + poly_marshal_mod3(vars->m_bytes, &vars->m); + poly_marshal_mod3(vars->r_bytes, &vars->r); - uint8_t m_bytes[HRSS_POLY3_BYTES], r_bytes[HRSS_POLY3_BYTES]; - poly_marshal_mod3(m_bytes, &m); - poly_marshal_mod3(r_bytes, &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); - 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); + 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 @@ void HRSS_decap(uint8_t out_shared_key[HRSS_KEY_BYTES], // 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); - - 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); - - ok &= constant_time_is_zero_w(CRYPTO_memcmp(ciphertext, expected_ciphertext, - sizeof(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); - - for (unsigned i = 0; i < sizeof(shared_key); i++) { + assert(ciphertext_len == sizeof(vars->expected_ciphertext)); + poly_marshal(vars->expected_ciphertext, &vars->c); + + 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, vars->expected_ciphertext, + sizeof(vars->expected_ciphertext))); + + 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(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 @@ TEST(HRSS, Basic) { 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 @@ TEST(HRSS, Basic) { 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 @@ TEST(HRSS, Random) { 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 @@ TEST(HRSS, Random) { 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 @@ TEST(HRSS, Random) { 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 @@ TEST(HRSS, Golden) { 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 @@ TEST(HRSS, Golden) { } 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 @@ TEST(HRSS, Golden) { }; 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 @@ TEST(HRSS, ABI) { 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 @@ OPENSSL_EXPORT void HRSS_poly3_invert(struct poly3 *out, #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 @@ typedef __uint128_t uint128_t; #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 @@ static inline int buffers_alias(const uint8_t *a, size_t a_len, 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 @@ OPENSSL_EXPORT void CRYPTO_once(CRYPTO_once_t *once, void (*init)(void)); // 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 @@ BSSL_NAMESPACE_END 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 @@ static inline void *OPENSSL_memset(void *dst, int c, size_t n) { 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 @@ void BORINGSSL_FIPS_abort(void) __attribute__((noreturn)); 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 kMinNumBuckets = 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 @@ struct lhash_st { 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 @@ _LHASH *lh_new(lhash_hash_func hash, lhash_cmp_func comp) { return ret; } -void lh_free(_LHASH *lh) { +void OPENSSL_lh_free(_LHASH *lh) { if (lh == NULL) { return; } @@ -129,7 +140,7 @@ void lh_free(_LHASH *lh) { 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 @@ static LHASH_ITEM **get_next_ptr_by_key(const _LHASH *lh, const void *key, 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 @@ static void lh_maybe_resize(_LHASH *lh) { } } -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 @@ int lh_insert(_LHASH *lh, void **old_data, void *data, 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 @@ void *lh_delete(_LHASH *lh, const void *data, 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 @@ void lh_doall_arg(_LHASH *lh, void (*func)(void *, void *), void *arg) { // 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 @@ static const char *Lookup( 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 @@ TEST(LHashTest, Basic) { &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 @@ TEST(LHashTest, Basic) { // 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 @@ static void __asan_unpoison_memory_region(const void *addr, size_t size) {} #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 @@ void OPENSSL_free(void *orig_ptr) { 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 @@ uint32_t OPENSSL_hash32(const void *ptr, size_t len) { 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 @@ int BIO_vsnprintf(char *buf, size_t n, const char *format, va_list args) { } 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 @@ OPENSSL_EXPORT int OBJ_nid2cbb(CBB *out, int nid) { 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 @@ ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) { } if (nid != NID_undef) { - return (ASN1_OBJECT*) OBJ_nid2obj(nid); + return OBJ_nid2obj(nid); } } @@ -484,7 +488,7 @@ static int cmp_data(const ASN1_OBJECT *a, const ASN1_OBJECT *b) { } 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 int cmp_short_name(const ASN1_OBJECT *a, const ASN1_OBJECT *b) { } 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 @@ TEST(ObjTest, TestSignatureAlgorithms) { 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 @@ static bool ExpectObj2Txt(const uint8_t *der, size_t der_len, 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 @@ static bool ExpectObj2Txt(const uint8_t *der, size_t der_len, } 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 @@ TEST(ObjTest, TestObj2Txt) { 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 @@ TEST(ObjTest, TestObj2Txt) { 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 @@ RSA *PEM_read_bio_RSAPrivateKey(BIO *bp, RSA **rsa, pem_password_cb *cb, 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 @@ RSA *PEM_read_RSAPrivateKey(FILE *fp, RSA **rsa, pem_password_cb *cb, void *u) return pkey_get_rsa(pktmp, rsa); } -#endif - IMPLEMENT_PEM_write_cb_const(RSAPrivateKey, RSA, PEM_STRING_RSA, RSAPrivateKey) @@ -205,7 +201,6 @@ IMPLEMENT_PEM_write_cb_const(DSAPrivateKey, DSA, PEM_STRING_DSA, 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 @@ DSA *PEM_read_DSAPrivateKey(FILE *fp, DSA **dsa, pem_password_cb *cb, void *u) 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 @@ IMPLEMENT_PEM_write_cb(ECPrivateKey, EC_KEY, PEM_STRING_ECPRIVATEKEY, 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 @@ EC_KEY *PEM_read_ECPrivateKey(FILE *fp, EC_KEY **eckey, pem_password_cb *cb, 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 @@ STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk, 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 @@ void PEM_dek_info(char *buf, const char *type, int len, char *str) 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 @@ void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x, BIO_free(b); return ret; } -#endif static int check_pem(const char *nm, const char *name) { @@ -252,7 +250,6 @@ int PEM_bytes_read_bio(unsigned char **pdata, long *plen, char **pnm, 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 @@ int PEM_ASN1_write(i2d_of_void *i2d, const char *name, FILE *fp, 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 @@ static int load_iv(char **fromp, unsigned char *to, int num) 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 @@ int PEM_write(FILE *fp, const char *name, const char *header, 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 @@ int PEM_write_bio(BIO *bp, const char *name, const char *header, 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 @@ int PEM_read(FILE *fp, char **name, char **header, unsigned char **data, 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 @@ EVP_PKEY *d2i_PKCS8PrivateKey_bio(BIO *bp, EVP_PKEY **x, pem_password_cb *cb, 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 @@ EVP_PKEY *d2i_PKCS8PrivateKey_fp(FILE *fp, EVP_PKEY **x, pem_password_cb *cb, 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 @@ int PEM_write_bio_PrivateKey(BIO *bp, EVP_PKEY *x, const EVP_CIPHER *enc, 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 @@ int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc, return ret; } -#endif /* Transparently read in PKCS#3 or X9.42 DH parameters */ @@ -203,7 +201,6 @@ DH *PEM_read_bio_DHparams(BIO *bp, DH **x, pem_password_cb *cb, void *u) 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 @@ DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u) 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 @@ static void TestCertReparse(const uint8_t *der_bytes, size_t der_len) { 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 @@ static void TestCRLReparse(const uint8_t *der_bytes, size_t der_len) { 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, PEMCerts) { 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 @@ static int pkcs7_bundle_certificates_cb(CBB *out, const void *arg) { } } - 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 @@ static int pkcs7_bundle_crls_cb(CBB *out, const void *arg) { } } - 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 @@ static PKCS7 *pkcs7_new(CBS *cbs) { 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, -}; - -// 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, -}; +std::string GetTestData(const char *path); +// 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 @@ static void TestCompat(bssl::Span<const uint8_t> der) { } 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 @@ TEST(PKCS12Test, RoundTrip) { {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 @@ int PKCS12_parse(const PKCS12 *p12, const char *password, EVP_PKEY **out_pkey, // 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 @@ static int add_cert_bag(CBB *cbb, X509 *cert, const char *name, 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 @@ PKCS12 *PKCS12_create(const char *password, const char *name, 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 @@ PKCS12 *PKCS12_create(const char *password, const char *name, 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 @@ PKCS12 *PKCS12_create(const char *password, const char *name, // 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 @@ PKCS12 *PKCS12_create(const char *password, const char *name, !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 @@ OPENSSL_STATIC_ASSERT( 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 @@ void CRYPTO_sysrand(uint8_t *out, size_t requested) { 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 @@ void CRYPTO_sysrand(uint8_t *out, size_t requested) { 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 @@ void RAND_need_entropy(size_t bytes_needed) { 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 @@ RAND_METHOD *RAND_OpenSSL(void) { 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 @@ void CRYPTO_sysrand(uint8_t *out, size_t requested) { 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 @@ RSA *RSA_parse_public_key(CBS *cbs) { 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_once_t g_thread_local_init_once = PTHREAD_ONCE_INIT; 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_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 @@ int ASN1_item_verify(const ASN1_ITEM *it, X509_ALGOR *a, 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 @@ int x509_digest_sign_algorithm(EVP_MD_CTX *ctx, X509_ALGOR *algor) { 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 @@ extern "C" { #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 @@ int x509_digest_sign_algorithm(EVP_MD_CTX *ctx, X509_ALGOR *algor); * 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 @@ static const EVP_MD *rsa_algor_to_md(X509_ALGOR *alg) { } /* 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 @@ err: 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 @@ int X509_CRL_print_fp(FILE *fp, X509_CRL *x) BIO_free(b); return ret; } -#endif int X509_CRL_print(BIO *out, X509_CRL *x) { @@ -86,7 +84,13 @@ int X509_CRL_print(BIO *out, X509_CRL *x) 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 @@ int X509_CRL_print(BIO *out, X509_CRL *x) 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 @@ int X509_CRL_print(BIO *out, X509_CRL *x) 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 @@ int X509_print_fp(FILE *fp, X509 *x) { 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 @@ int X509_signature_print(BIO *bp, const X509_ALGOR *sigalg, 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 @@ int X509_CERT_AUX_print(BIO *out, X509_CERT_AUX *aux, int indent) 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_count(const STACK_OF(X509_ATTRIBUTE) *x) 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 @@ STACK_OF(X509_ATTRIBUTE) *X509at_add1_attr_by_txt(STACK_OF(X509_ATTRIBUTE) 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 @@ X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_NID(X509_ATTRIBUTE **attr, int nid, 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 @@ X509_ATTRIBUTE *X509_ATTRIBUTE_create_by_OBJ(X509_ATTRIBUTE **attr, 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_OBJ(X509_ATTRIBUTE **attr, } 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 @@ int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, 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 @@ int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, 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 @@ int X509_ATTRIBUTE_set1_data(X509_ATTRIBUTE *attr, int attrtype, 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 @@ ASN1_OBJECT *X509_ATTRIBUTE_get0_object(X509_ATTRIBUTE *attr) } 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 @@ int X509_issuer_and_serial_cmp(const X509 *a, const X509 *b) 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 @@ int X509_chain_check_suiteb(int *perror_depth, X509 *x, STACK_OF(X509) *chain, } 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 @@ int X509_chain_check_suiteb(int *perror_depth, X509 *x, STACK_OF(X509) *chain, 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 @@ int X509_REVOKED_add1_ext_i2d(X509_REVOKED *x, int nid, void *value, int crit, { 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 @@ int X509_REQ_check_private_key(X509_REQ *x, EVP_PKEY *k) 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) { + return NULL; + } - 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; + 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; } - if (!ext || (ext->type != V_ASN1_SEQUENCE)) + + 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; - p = ext->value.sequence->data; + } + 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 @@ STACK_OF(X509_EXTENSION) *X509_REQ_get_extensions(X509_REQ *req) * 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 @@ int X509_REQ_get_attr_by_NID(const X509_REQ *req, int nid, int lastpos) 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(X509_REQ *req, X509_ATTRIBUTE *attr) } 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 @@ int X509_set_version(X509 *x, long version) 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 @@ const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x) 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 @@ o/1TpfOMSGhMyMoyPrk= -----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 @@ static bssl::UniquePtr<STACK_OF(X509_CRL)> CRLsToStack( 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 @@ static int Verify(X509 *leaf, const std::vector<X509 *> &roots, 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 @@ TEST(X509Test, TestCRL) { 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 @@ TEST(X509Test, TestCRL) { 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 @@ TEST(X509Test, ManyNamesAndConstraints) { {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 @@ TEST(X509Test, RSASignManual) { 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 @@ TEST(X509Test, TestFromBufferModified) { 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 @@ TEST(X509Test, X509NameSet) { 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(X509Test, GeneralName) { } } } + +// 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_count(const STACK_OF(X509_EXTENSION) *x) 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_OBJ(const STACK_OF(X509_EXTENSION) *sk, 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; + } - if (sk == NULL) - return (-1); lastpos++; - if (lastpos < 0) + if (lastpos < 0) { lastpos = 0; - n = sk_X509_EXTENSION_num(sk); + } + + crit = !!crit; + int 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); + const X509_EXTENSION *ex = sk_X509_EXTENSION_value(sk, lastpos); + if (X509_EXTENSION_get_critical(ex) == crit) { + return lastpos; + } } - return (-1); + return -1; } X509_EXTENSION *X509v3_get_ext(const STACK_OF(X509_EXTENSION) *x, int loc) @@ -172,11 +177,9 @@ STACK_OF(X509_EXTENSION) *X509v3_add_ext(STACK_OF(X509_EXTENSION) **x, 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 @@ ASN1_OCTET_STRING *X509_EXTENSION_get_data(X509_EXTENSION *ex) 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 @@ static int check_id_error(X509_STORE_CTX *ctx, int errcode) 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_hosts(X509 *x, X509_VERIFY_PARAM_ID *id) 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 @@ static int check_crl_path(X509_STORE_CTX *ctx, X509 *x) } /* - * 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 X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) 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 @@ int X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time) 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 @@ ASN1_TIME *X509_time_adj_ex(ASN1_TIME *s, { 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 @@ X509_CRL *X509_CRL_diff(X509_CRL *base, X509_CRL *newer, } /* 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 @@ void X509_STORE_CTX_set0_param(X509_STORE_CTX *ctx, X509_VERIFY_PARAM *param) 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 @@ static void str_free(char *s) #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 @@ static int int_x509_param_set_hosts(X509_VERIFY_PARAM_ID *id, int mode, 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 int int_x509_param_set_hosts(X509_VERIFY_PARAM_ID *id, int mode, 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 @@ static void x509_verify_param_zero(X509_VERIFY_PARAM *param) 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 @@ void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param) if (param == NULL) return; x509_verify_param_zero(param); - OPENSSL_free(param->id); OPENSSL_free(param); } @@ -233,11 +223,6 @@ void X509_VERIFY_PARAM_free(X509_VERIFY_PARAM *param) (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 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, { 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 @@ int X509_VERIFY_PARAM_inherit(X509_VERIFY_PARAM *dest, } /* 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_policies(X509_VERIFY_PARAM *param, 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_set1_host(X509_VERIFY_PARAM *param, 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 @@ int X509_VERIFY_PARAM_add1_host(X509_VERIFY_PARAM *param, 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 @@ int X509_VERIFY_PARAM_set1_ip(X509_VERIFY_PARAM *param, 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 @@ int X509_VERIFY_PARAM_set1_ip_asc(X509_VERIFY_PARAM *param, const char *ipasc) 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 @@ const char *X509_VERIFY_PARAM_get0_name(const X509_VERIFY_PARAM *param) 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 @@ int i2d_X509_CRL_tbs(X509_CRL *crl, unsigned char **outp) { 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 @@ int X509_NAME_ENTRY_set_data(X509_NAME_ENTRY *ne, int type, 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_FUNCTIONS(X509_ALGOR) 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 @@ int X509_ALGOR_set0(X509_ALGOR *alg, const ASN1_OBJECT *aobj, int ptype, 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 @@ int X509_ALGOR_set0(X509_ALGOR *alg, const ASN1_OBJECT *aobj, int ptype, 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 @@ int NETSCAPE_SPKI_verify(NETSCAPE_SPKI *spki, EVP_PKEY *pkey) 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 @@ int i2d_X509_fp(FILE *fp, X509 *x509) { return ASN1_item_i2d_fp(ASN1_ITEM_rptr(X509), fp, x509); } -#endif X509 *d2i_X509_bio(BIO *bp, X509 **x509) { @@ -159,7 +160,6 @@ int i2d_X509_bio(BIO *bp, X509 *x509) 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 @@ int i2d_X509_CRL_fp(FILE *fp, X509_CRL *crl) { 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 @@ int i2d_X509_CRL_bio(BIO *bp, X509_CRL *crl) 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 @@ int i2d_X509_REQ_fp(FILE *fp, X509_REQ *req) { 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 @@ int i2d_X509_REQ_bio(BIO *bp, X509_REQ *req) 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_I2D_FP(RSA, i2d_RSAPublicKey_fp, i2d_RSAPublicKey_bio) 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_D2I_BIO(RSA, d2i_RSA_PUBKEY_bio, d2i_RSA_PUBKEY) 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_D2I_BIO(DSA, d2i_DSA_PUBKEY_bio, d2i_DSA_PUBKEY) 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 @@ int X509_NAME_digest(const X509_NAME *data, const EVP_MD *type, (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 @@ int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, EVP_PKEY *key) 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)) + } + + ret->object = obj; + if (!sk_ASN1_TYPE_push(ret->set, val)) { goto err; + } + + ASN1_TYPE_set(val, attrtype, value); + return ret; - ASN1_TYPE_set(val, atrtype, 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 @@ static int crl_set_issuers(X509_CRL *crl) 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 @@ static int crl_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, 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 @@ int X509_CRL_get0_by_cert(X509_CRL *crl, X509_REVOKED **ret, X509 *x) 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 @@ void *X509_CRL_get_meth_data(X509_CRL *crl) { 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 @@ int X509_NAME_set(X509_NAME **xn, X509_NAME *name) 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 @@ EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) 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 **ppkalg, - const unsigned char **pk, int *ppklen, - X509_ALGOR **pa, X509_PUBKEY *pub) +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 (ppkalg) - *ppkalg = pub->algor->algorithm; - if (pk) { - *pk = pub->public_key->data; - *ppklen = pub->public_key->length; + 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; } - 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_SEQUENCE_enc(X509_REQ_INFO, enc, rinf_cb) = { 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 @@ static int x509_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, } } - /* 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 @@ OPENSSL_EXPORT int x509v3_looks_like_dns_name(const unsigned char *in, // 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 @@ void policy_data_free(X509_POLICY_DATA *data) /* * 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 @@ DEFINE_STACK_OF(X509_POLICY_DATA) /* * 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 @@ static int tree_link_matching_nodes(X509_POLICY_LEVEL *curr, } /* - * 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 @@ static int tree_link_nodes(X509_POLICY_LEVEL *curr, } /* - * 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 @@ static int tree_prune(X509_POLICY_TREE *tree, X509_POLICY_LEVEL *curr) 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 @@ static STACK_OF(CONF_VALUE) *i2v_AUTHORITY_KEYID(X509V3_EXT_METHOD *method, 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) { + STACK_OF(CONF_VALUE) *tmpextlist = + i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist); + if (tmpextlist == NULL) { + goto err; + } + extlist = tmpextlist; } - if (akeyid->issuer) - extlist = i2v_GENERAL_NAMES(NULL, akeyid->issuer, extlist); 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 @@ STACK_OF(CONF_VALUE) *i2v_GENERAL_NAMES(X509V3_EXT_METHOD *method, 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 @@ STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method, 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 @@ STACK_OF(CONF_VALUE) *i2v_GENERAL_NAME(X509V3_EXT_METHOD *method, 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 @@ static POLICYINFO *policy_section(X509V3_CTX *ctx, 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 @@ static POLICYQUALINFO *notice_section(X509V3_CTX *ctx, 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 @@ static void print_qualifiers(BIO *out, STACK_OF(POLICYQUALINFO) *quals, 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 @@ static void print_notice(BIO *out, USERNOTICE *notice, int indent) 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 @@ static void print_notice(BIO *out, USERNOTICE *notice, int indent) 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 @@ static void *v2i_crld(const X509V3_EXT_METHOD *method, 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 @@ static int nc_dn(X509_NAME *nm, X509_NAME *base) 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_dns(ASN1_IA5STRING *dns, ASN1_IA5STRING *base) static int nc_email(ASN1_IA5STRING *eml, ASN1_IA5STRING *base) { - const char *baseptr = (char *)base->data; - const char *emlptr = (char *)eml->data; - - const char *baseat = strchr(baseptr, '@'); - const char *emlat = strchr(emlptr, '@'); - if (!emlat) + CBS eml_cbs, base_cbs; + CBS_init(&eml_cbs, eml->data, eml->length); + CBS_init(&base_cbs, base->data, base->length); + + /* 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 */ - - p = strchr(hostptr, ':'); - /* Otherwise look for trailing slash */ - if (!p) - p = strchr(hostptr, '/'); + } - if (!p) - hostlen = strlen(hostptr); - else - hostlen = p - hostptr; + /* 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; + } - 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 @@ static int i2r_pci(X509V3_EXT_METHOD *method, PROXY_CERT_INFO_EXTENSION *pci, 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 @@ int X509V3_EXT_print(BIO *out, X509_EXTENSION *ext, unsigned long flag, { 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 @@ int X509V3_extensions_print(BIO *bp, const char *title, 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 @@ static int unknown_ext_print(BIO *out, X509_EXTENSION *ext, 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 @@ int X509V3_EXT_print_fp(FILE *fp, X509_EXTENSION *ext, int flag, int indent) 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 @@ int x509v3_cache_extensions(X509 *x) 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 STACK_OF(OPENSSL_STRING) *get_email(X509_NAME *name, 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 @@ ASN1_INTEGER *s2i_ASN1_INTEGER(X509V3_EXT_METHOD *method, const char *value) 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 void str_free(OPENSSL_STRING str) 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 @@ int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags) 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 @@ int X509_check_ip_asc(X509 *x, const char *ipasc, unsigned int flags) /* * 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 *a2i_IPADDRESS(const char *ipasc) 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 @@ ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc) 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 @@ ASN1_OCTET_STRING *a2i_IPADDRESS_NC(const char *ipasc) 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 @@ int a2i_ipadd(unsigned char *ipout, const char *ipasc) } } -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 @@ typedef struct { 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 @@ static X509 *make_cert(void) 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 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher, 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 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher, 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 @@ int RIPEMD160_Init(RIPEMD160_CTX *ctx) { } 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 @@ static void ripemd160_block_data_order(uint32_t h[5], const uint8_t *data, 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_server ssl) 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 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { 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 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len) { } 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_192_gcm(void); 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_CTX_aead(const EVP_AEAD_CTX *ctx); 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 @@ extern "C" { #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 @@ extern "C" { // 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 @@ extern "C" { #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 @@ extern "C" { // 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. -// -// 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. +// See bit string documentation below for how the data and flags are used. +// +// 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 @@ struct asn1_string_st { // 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 @@ OPENSSL_EXPORT int ASN1_STRING_length(const ASN1_STRING *str); // 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_EXPORT int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len); // |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. - - -// 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 +// 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); -// For use with ASN1_mbstring_copy() +// 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. It is used with TeletexString -// which, in turn, is treated as Latin-1 rather than T.61 by OpenSSL and most -// other software. +// |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) -#define DECLARE_ASN1_SET_OF(type) // filled in by mkstack.pl -#define IMPLEMENT_ASN1_SET_OF(type) // nothing, no longer needed +// 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) -// 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 +// 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. + DEFINE_STACK_OF(ASN1_OBJECT) // ASN1_ENCODING structure: this is used to save the received @@ -386,10 +865,6 @@ typedef struct ASN1_ENCODING_st { #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 @@ typedef struct asn1_string_table_st { 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 @@ typedef const ASN1_ITEM ASN1_ITEM_EXP; #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 @@ typedef struct BIT_STRING_BITNAME_st { 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 @@ OPENSSL_EXPORT ASN1_OBJECT *d2i_ASN1_OBJECT(ASN1_OBJECT **a, 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 @@ OPENSSL_EXPORT ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **a, 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_UTCTIME) 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 i2a_ASN1_STRING(BIO *bp, const ASN1_STRING *a, int type); 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 int ASN1_object_size(int constructed, int length, int tag); 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 void *ASN1_item_unpack(const ASN1_STRING *oct, 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 @@ struct ASN1_ADB_TABLE_st { /* 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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { // 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_NODE_st X509_POLICY_NODE; 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_aead_st EVP_AEAD; 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 spake2_ctx_st SPAKE2_CTX; 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 trust_token_method_st TRUST_TOKEN_METHOD; 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 @@ class StackAllocated { 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 int BIO_read_asn1(BIO *bio, uint8_t **out, size_t *out_len, 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 @@ struct cbs_st { // 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 @@ OPENSSL_EXPORT int CBS_get_u16_length_prefixed(CBS *cbs, CBS *out); // 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 @@ OPENSSL_EXPORT int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag); // 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 @@ extern "C" { // 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 @@ struct evp_cipher_ctx_st { // 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 @@ OPENSSL_EXPORT void NCONF_free(CONF *conf); // |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 @@ OPENSSL_INLINE const uint32_t *OPENSSL_ia32cap_get(void) { #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 @@ OPENSSL_EXPORT int CRYPTO_has_broken_NEON(void); // 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 @@ OPENSSL_EXPORT int CRYPTO_is_confidential_build(void); // 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 int BORINGSSL_self_test(void); 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 @@ OPENSSL_EXPORT void EVP_MD_CTX_free(EVP_MD_CTX *ctx); // 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 @@ OPENSSL_EXPORT void EVP_MD_CTX_set_flags(EVP_MD_CTX *ctx, int flags); // 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 @@ BSSL_NAMESPACE_BEGIN 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_set_generator(EC_GROUP *group, 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 @@ extern "C" { // 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 @@ OPENSSL_EXPORT int ECDSA_sign(int type, const uint8_t *digest, // 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 @@ OPENSSL_EXPORT int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s); // 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 int ECDSA_SIG_to_bytes(uint8_t **out_bytes, size_t *out_len, 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 @@ OPENSSL_EXPORT char *ERR_error_string_n(uint32_t packed_error, char *buf, 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 EVP_PKEY *EVP_PKEY_CTX_get0_pkey(EVP_PKEY_CTX *ctx); 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_sign(EVP_PKEY_CTX *ctx, uint8_t *sig, 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 @@ OPENSSL_EXPORT int EVP_PKEY_CTX_set_ec_paramgen_curve_nid(EVP_PKEY_CTX *ctx, // 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 @@ OPENSSL_EXPORT void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher, 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 @@ BSSL_NAMESPACE_END #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 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 @@ OPENSSL_EXPORT int HKDF(uint8_t *out_key, size_t out_len, const EVP_MD *digest, // 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 @@ struct HRSS_public_key { (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_EXPORT int CRYPTO_memcmp(const void *a, const void *b, size_t len); // 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 @@ extern "C" { // 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 @@ OPENSSL_EXPORT int OBJ_txt2nid(const char *s); // 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 @@ extern "C" { // 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 @@ extern "C" { 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 @@ extern "C" { // 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 @@ extern "C" { 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 @@ DECLARE_STACK_OF(X509_CRL) // 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_raw_certificates( 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 @@ OPENSSL_EXPORT int PKCS7_bundle_certificates( // |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 @@ OPENSSL_EXPORT int PKCS7_bundle_CRLs(CBB *out, const STACK_OF(X509_CRL) *crls); // 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 @@ OPENSSL_EXPORT int PKCS7_get_PEM_certificates(STACK_OF(X509) *out_certs, // 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 struct { 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 @@ OPENSSL_EXPORT int PKCS7_type_is_signedAndEnveloped(const PKCS7 *p7); #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 @@ OPENSSL_EXPORT int PKCS7_type_is_signedAndEnveloped(const PKCS7 *p7); // 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 @@ OPENSSL_EXPORT int i2d_PKCS12_fp(FILE *fp, const PKCS12 *p12); // // 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 @@ OPENSSL_EXPORT int PKCS12_verify_mac(const PKCS12 *p12, const char *password, // 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 @@ OPENSSL_EXPORT RAND_METHOD *RAND_OpenSSL(void); // 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 @@ OPENSSL_EXPORT int RSA_private_decrypt(size_t flen, const uint8_t *from, // 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); +// +// 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 |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. +// 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 |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 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. -// -// It returns 1 on success or zero 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 |RSA_sign_pss_mgf1| or the |EVP_PKEY| interface) is preferred for new -// protocols. +// 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. +// +// 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. +// +// 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 @@ OPENSSL_EXPORT int RSA_padding_add_PKCS1_OAEP_mgf1( 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 @@ class SpanBase { 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 @@ class Span : private internal::SpanBase<const T> { 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 @@ class Span : private internal::SpanBase<const T> { 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 @@ OPENSSL_EXPORT int SSL_get_error(const SSL *ssl, int ret_code); // 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 @@ OPENSSL_EXPORT int SSL_get_error(const SSL *ssl, int ret_code); // 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 @@ enum ssl_private_key_result_t BORINGSSL_ENUM_INT { // 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_set_private_key_method( 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 @@ OPENSSL_EXPORT int SSL_SESSION_set1_id_context(SSL_SESSION *session, // 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 @@ OPENSSL_EXPORT SSL_CTX *SSL_set_SSL_CTX(SSL *ssl, SSL_CTX *ctx); // 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 @@ OPENSSL_EXPORT int SSL_CTX_set_alpn_protos(SSL_CTX *ctx, const uint8_t *protos, // 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 @@ OPENSSL_EXPORT int SSL_set_alpn_protos(SSL *ssl, const uint8_t *protos, // 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 @@ OPENSSL_EXPORT int SSL_select_next_proto(uint8_t **out, uint8_t *out_len, // 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 @@ OPENSSL_EXPORT int SSL_CTX_set1_tls_channel_id(SSL_CTX *ctx, // 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_CTX_set_srtp_profiles(SSL_CTX *ctx, 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 @@ OPENSSL_EXPORT int SSL_delegated_credential_used(const SSL *ssl); // // 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 @@ OPENSSL_EXPORT int SSL_delegated_credential_used(const SSL *ssl); // 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 @@ OPENSSL_EXPORT int SSL_delegated_credential_used(const SSL *ssl); // |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 @@ struct ssl_quic_method_st { // 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 @@ OPENSSL_EXPORT void SSL_get_peer_quic_transport_params( // 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 @@ enum ssl_early_data_reason_t BORINGSSL_ENUM_INT { 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 @@ OPENSSL_EXPORT const char *SSL_early_data_reason_string( 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 @@ OPENSSL_EXPORT void SSL_set_enable_ech_grease(SSL *ssl, int enable); #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_read_sequence(const SSL *ssl); 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 @@ OPENSSL_EXPORT void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx, 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_set_tmp_ecdh(SSL *ssl, const EC_KEY *ec_key); 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 @@ OPENSSL_EXPORT int SSL_CTX_set_tlsext_status_arg(SSL_CTX *ctx, void *arg); // 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 @@ BSSL_NAMESPACE_BEGIN 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 @@ OPENSSL_EXPORT bool SSL_get_traffic_secrets( 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 @@ BSSL_NAMESPACE_END #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 @@ BSSL_NAMESPACE_END #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 @@ extern "C" { #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 @@ extern "C" { #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 RFC8879 + +// ExtensionType value from RFC 9000 +#define TLSEXT_TYPE_quic_transport_parameters 57 + +// 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 @@ extern "C" { #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 @@ extern "C" { // 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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #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 @@ extern "C" { #define X509v3_KU_DECIPHER_ONLY 0x8000 #define X509v3_KU_UNDEF 0xffff -DEFINE_STACK_OF(X509_ALGOR) -DECLARE_ASN1_SET_OF(X509_ALGOR) - -typedef STACK_OF(X509_ALGOR) X509_ALGORS; +struct X509_algor_st { + ASN1_OBJECT *algorithm; + ASN1_TYPE *parameter; +} /* X509_ALGOR */; -struct X509_val_st { - ASN1_TIME *notBefore; - ASN1_TIME *notAfter; -} /* X509_VAL */; +DECLARE_ASN1_FUNCTIONS(X509_ALGOR) -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 */; +DEFINE_STACK_OF(X509_ALGOR) -struct X509_name_entry_st { - ASN1_OBJECT *object; - ASN1_STRING *value; - int set; - int size; // temp variable -} /* X509_NAME_ENTRY */; +typedef STACK_OF(X509_ALGOR) X509_ALGORS; 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_STACK_OF(X509_TRUST) #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_STACK_OF(X509_TRUST) #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 @@ struct x509_revoked_st { }; 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 @@ extern "C" { // 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 @@ OPENSSL_EXPORT int X509_set_notBefore(X509 *x509, const ASN1_TIME *tm); // 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 @@ OPENSSL_EXPORT X509_CINF *X509_get_cert_info(const X509 *x509); // |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 @@ OPENSSL_EXPORT X509_NAME *X509_REQ_get_subject_name(const X509_REQ *req); // 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 @@ OPENSSL_EXPORT ASN1_TIME *X509_CRL_get_nextUpdate(X509_CRL *crl); // 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 @@ OPENSSL_EXPORT int X509_NAME_digest(const X509_NAME *name, const EVP_MD *md, // 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 int i2d_PrivateKey_fp(FILE *fp, EVP_PKEY *pkey); 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_CRL *X509_CRL_dup(X509_CRL *crl); 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_ENTRY_set(const X509_NAME_ENTRY *ne); 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 const char *X509_get_default_private_dir(void); 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_ENTRY) 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_algo(X509 *x509, const X509_ALGOR *algo); 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_trust_clear(X509 *x); 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 @@ OPENSSL_EXPORT int ASN1_item_digest(const ASN1_ITEM *it, const EVP_MD *type, 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 @@ OPENSSL_EXPORT int ASN1_item_sign_ctx(const ASN1_ITEM *it, X509_ALGOR *algor1, 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); +// 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); + +// 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 *x); -OPENSSL_EXPORT const X509_ALGOR *X509_get0_tbs_sigalg(const X509 *x); + 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); -OPENSSL_EXPORT int X509_REQ_set_version(X509_REQ *x, 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, + const ASN1_OBJECT *obj, + int attrtype, + const unsigned char *data, int len); -OPENSSL_EXPORT int X509_REQ_add1_attr_by_NID(X509_REQ *req, int nid, int type, - const unsigned char *bytes, + +// 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 type, - const unsigned char *bytes, + const char *attrname, 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_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 @@ OPENSSL_EXPORT int i2d_re_X509_CRL_tbs(X509_CRL *crl, unsigned char **outp); // 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 const ASN1_TIME *X509_REVOKED_get0_revocationDate( 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 @@ OPENSSL_EXPORT int X509_chain_check_suiteb(int *perror_depth, X509 *x, 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 unsigned long X509_NAME_hash_old(X509_NAME *x); 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_CRL_print_fp(FILE *bp, X509_CRL *x); 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 @@ OPENSSL_EXPORT ASN1_OBJECT *X509_NAME_ENTRY_get_object( 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 void *X509_get_ext_d2i(const X509 *x509, int nid, 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 void *X509_CRL_get_ext_d2i(const X509_CRL *crl, int nid, 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 @@ OPENSSL_EXPORT int X509_REVOKED_add1_ext_i2d(X509_REVOKED *x, int nid, 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); + +// 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); -OPENSSL_EXPORT int X509_EXTENSION_get_critical(X509_EXTENSION *ex); +// 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 @@ OPENSSL_EXPORT int PKCS8_pkey_get0(ASN1_OBJECT **ppkalg, 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(RSA_PSS_PARAMS, RSA_PSS_PARAMS_free) 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_REQ, X509_REQ_free) 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 @@ The X509_STORE then calls a function to actually verify the 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_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_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_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_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_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 - -/* 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 - -#define X509_V_ERR_NAME_CONSTRAINTS_WITHOUT_SANS 67 - -/* 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 - -/* 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 - -#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) - -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); +#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_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_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_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_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 + +// 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 + +// 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 + +// 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 + +// 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 + +#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) + +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 @@ X509_STORE_get_cleanup(X509_STORE *ctx); 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 X509_LOOKUP_METHOD *X509_LOOKUP_file(void); 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_STACK_OF(X509V3_EXT_METHOD) #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 @@ typedef struct GENERAL_NAME_st { } GENERAL_NAME; DEFINE_STACK_OF(GENERAL_NAME) -DECLARE_ASN1_SET_OF(GENERAL_NAME) typedef STACK_OF(GENERAL_NAME) GENERAL_NAMES; @@ -218,7 +215,6 @@ typedef struct ACCESS_DESCRIPTION_st { } 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 @@ struct DIST_POINT_st { 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 @@ typedef struct POLICYQUALINFO_st { } POLICYQUALINFO; DEFINE_STACK_OF(POLICYQUALINFO) -DECLARE_ASN1_SET_OF(POLICYQUALINFO) typedef struct POLICYINFO_st { ASN1_OBJECT *policyid; @@ -296,7 +290,6 @@ typedef struct POLICYINFO_st { 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 @@ OPENSSL_EXPORT STACK_OF(CONF_VALUE) *i2v_ASN1_BIT_STRING( 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_section_free(X509V3_CTX *ctx, 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 @@ OPENSSL_EXPORT void *X509V3_EXT_d2i(const X509_EXTENSION *ext); // 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_key_usage(X509 *x); 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 uint32_t X509_get_extended_key_usage(X509 *x); 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 @@ OPENSSL_EXPORT const ASN1_OCTET_STRING *X509_get0_authority_key_id(X509 *x509); // 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 @@ OPENSSL_EXPORT const GENERAL_NAMES *X509_get0_authority_issuer(X509 *x509); // 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 int X509_check_ip_asc(X509 *x, const char *ipasc, 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 @@ BSSL_NAMESPACE_BEGIN 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 @@ BSSL_NAMESPACE_END #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 @@ set( 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 @@ set( 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 @@ set( 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 @@ add_library( 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 @@ add_library( 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 @@ void dtls_clear_outgoing_messages(SSL *ssl) { 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 @@ bool dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { 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 @@ static int send_flight(SSL *ssl) { 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 @@ static int send_flight(SSL *ssl) { 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 @@ int SSL_set_srtp_profiles(SSL *ssl, const char *profiles) { 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 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 @@ static bool is_post_quantum_group(uint16_t id) { } 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 @@ bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out, // 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 @@ bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out, // 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; - } - - // 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; + } 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); } - 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 @@ bool tls1_check_group_id(const SSL_HANDSHAKE *hs, uint16_t group_id) { 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 @@ bool tls12_check_peer_sigalg(const SSL_HANDSHAKE *hs, uint8_t *out_alert, 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 @@ bool tls12_check_peer_sigalg(const SSL_HANDSHAKE *hs, uint8_t *out_alert, // 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 @@ static bool dont_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_sni_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { !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,179 +624,117 @@ static bool ext_sni_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { } -// Encrypted Client Hello (ECH) +// Encrypted ClientHello (ECH) // -// https://tools.ietf.org/html/draft-ietf-tls-esni-09 - -// 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; -} +// https://tools.ietf.org/html/draft-ietf-tls-esni-13 -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); + if (hs->ech_client_outer.empty()) { + return true; + } - // 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; + CBB ech_body; 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)))) { + !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; } - return CBB_flush(out); -} - -static bool ext_ech_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { - if (hs->max_version < TLS1_3_VERSION) { - return true; - } - if (hs->config->ech_grease_enabled) { - return ext_ech_add_clienthello_grease(hs, out); - } - // 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; + if (contents == nullptr) { return true; } - return true; -} -static bool ext_ech_is_inner_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { + uint8_t type; + if (!CBS_get_u8(contents, &type)) { + return false; + } + 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_is_inner_parse_clienthello(SSL_HANDSHAKE *hs, - uint8_t *out_alert, - CBS *contents) { - if (contents == nullptr) { +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; } - if (CBS_len(contents) > 0) { - *out_alert = SSL_AD_ILLEGAL_PARAMETER; + + // 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; } - hs->ech_is_inner_present = true; - return true; + 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); } @@ -771,10 +742,13 @@ static bool ext_ech_is_inner_parse_clienthello(SSL_HANDSHAKE *hs, // // 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 @@ static bool ext_ri_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_ems_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_ticket_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_sigalgs_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert, // // 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 @@ static bool ext_ocsp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_npn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_sct_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_alpn_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { } 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 @@ static bool ext_alpn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, 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 @@ static bool ext_alpn_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, 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 @@ bool ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert, 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 @@ bool ssl_negotiate_alpn(SSL_HANDSHAKE *hs, uint8_t *out_alert, 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 @@ static bool ext_alpn_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { 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 @@ static bool ext_channel_id_parse_clienthello(SSL_HANDSHAKE *hs, 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 @@ static bool ext_channel_id_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_srtp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { } if (!CBB_add_u8(&contents, 0 /* empty use_mki value */) || - !CBB_flush(out)) { + !CBB_flush(out_compressible)) { return false; } @@ -1738,6 +1751,7 @@ static bool ext_srtp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, // 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 @@ static bool ext_srtp_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, 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_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert, 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 @@ static bool ext_srtp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { 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 @@ static bool ext_srtp_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_ec_point_add_extension(SSL_HANDSHAKE *hs, CBB *out) { 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 @@ static bool ext_ec_point_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static size_t ext_pre_shared_key_clienthello_length(SSL_HANDSHAKE *hs) { 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 @@ static bool ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { // 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 @@ static bool ext_pre_shared_key_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { !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 @@ bool ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_psk_key_exchange_modes_parse_clienthello(SSL_HANDSHAKE *hs, // // 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 @@ static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { return true; } - if (!ssl->enable_early_data) { - ssl->s3->early_data_reason = ssl_early_data_disabled; - 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; + if (!hs->early_data_offered) { 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 @@ static bool ext_early_data_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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())) { - 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 */))) { + 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; } + } + 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 @@ static bool ext_key_share_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { 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_serverhello(SSL_HANDSHAKE *hs, } 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 @@ bool ssl_ext_key_share_parse_clienthello(SSL_HANDSHAKE *hs, bool *out_found, } } - 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 @@ bool ssl_ext_key_share_add_serverhello(SSL_HANDSHAKE *hs, CBB *out, // // 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 @@ static bool ext_supported_versions_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) 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 @@ static bool ext_supported_versions_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) // // 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 @@ static bool ext_cookie_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { // 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 @@ static bool ext_supported_groups_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { } } - return CBB_flush(out); + return CBB_flush(out_compressible); } static bool ext_supported_groups_parse_serverhello(SSL_HANDSHAKE *hs, @@ -2626,158 +2605,11 @@ static bool ext_supported_groups_parse_clienthello(SSL_HANDSHAKE *hs, 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 @@ static bool ext_quic_transport_params_add_clienthello_impl( 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 @@ static bool ext_quic_transport_params_add_clienthello_impl( 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 @@ static bool ext_quic_transport_params_add_serverhello_impl( 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 @@ static bool ext_quic_transport_params_add_serverhello_legacy(SSL_HANDSHAKE *hs, // // 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 @@ static bool ext_delegated_credential_parse_clienthello(SSL_HANDSHAKE *hs, // 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 @@ static bool cert_compression_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { 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 @@ static bool cert_compression_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { } } - return first || CBB_flush(out); + return first || CBB_flush(out_compressible); } static bool cert_compression_parse_serverhello(SSL_HANDSHAKE *hs, @@ -3099,8 +2937,22 @@ static bool cert_compression_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) { // // 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 @@ static bool ext_alps_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { } 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 @@ static bool ext_alps_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) { } } - 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 @@ bool ssl_negotiate_alps(SSL_HANDSHAKE *hs, uint8_t *out_alert, 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 @@ static const struct tls_extension kExtensions[] = { }, { 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 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_renegotiate, - NULL, ext_ri_add_clienthello, ext_ri_parse_serverhello, ext_ri_parse_clienthello, @@ -3278,7 +3118,6 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_supported_groups, - NULL, ext_supported_groups_add_clienthello, ext_supported_groups_parse_serverhello, ext_supported_groups_parse_clienthello, @@ -3286,7 +3125,6 @@ static const struct tls_extension kExtensions[] = { }, { 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 @@ static const struct tls_extension kExtensions[] = { }, { 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 @@ static const struct tls_extension kExtensions[] = { }, { 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 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_status_request, - NULL, ext_ocsp_add_clienthello, ext_ocsp_parse_serverhello, ext_ocsp_parse_clienthello, @@ -3320,7 +3155,6 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_signature_algorithms, - NULL, ext_sigalgs_add_clienthello, forbid_parse_serverhello, ext_sigalgs_parse_clienthello, @@ -3328,7 +3162,6 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_next_proto_neg, - NULL, ext_npn_add_clienthello, ext_npn_parse_serverhello, ext_npn_parse_clienthello, @@ -3336,7 +3169,6 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_certificate_timestamp, - NULL, ext_sct_add_clienthello, ext_sct_parse_serverhello, ext_sct_parse_clienthello, @@ -3344,7 +3176,6 @@ static const struct tls_extension kExtensions[] = { }, { 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 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_srtp, - ext_srtp_init, ext_srtp_add_clienthello, ext_srtp_parse_serverhello, ext_srtp_parse_clienthello, @@ -3360,7 +3190,6 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_key_share, - NULL, ext_key_share_add_clienthello, forbid_parse_serverhello, ignore_parse_clienthello, @@ -3368,7 +3197,6 @@ static const struct tls_extension kExtensions[] = { }, { 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 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_early_data, - NULL, ext_early_data_add_clienthello, ext_early_data_parse_serverhello, ext_early_data_parse_clienthello, @@ -3384,7 +3211,6 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_supported_versions, - NULL, ext_supported_versions_add_clienthello, forbid_parse_serverhello, ignore_parse_clienthello, @@ -3392,15 +3218,13 @@ static const struct tls_extension kExtensions[] = { }, { 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 @@ static const struct tls_extension kExtensions[] = { }, { 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 @@ static const struct tls_extension kExtensions[] = { }, { TLSEXT_TYPE_delegated_credential, - NULL, ext_delegated_credential_add_clienthello, forbid_parse_serverhello, ext_delegated_credential_parse_clienthello, @@ -3440,7 +3253,6 @@ static const struct tls_extension kExtensions[] = { }, { 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 @@ static_assert(kNumExtensions <= 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 @@ static const struct tls_extension *tls_extension_find(uint32_t *out_index, 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 @@ bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, // 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 @@ bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, } 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 @@ bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out, } } - 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 @@ err: 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 @@ bool ssl_parse_clienthello_tlsext(SSL_HANDSHAKE *hs, 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_scan_serverhello_tlsext(SSL_HANDSHAKE *hs, CBS *cbs, 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 @@ static bool ssl_check_serverhello_tlsext(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_ticket_aead_result_t decrypt_ticket_with_cipher_ctx( 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 @@ enum ssl_ticket_aead_result_t ssl_process_ticket( 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 @@ enum ssl_ticket_aead_result_t ssl_process_ticket( 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 @@ enum ssl_ticket_aead_result_t ssl_process_ticket( // 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 @@ enum ssl_ticket_aead_result_t ssl_process_ticket( } } + 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 @@ bool tls1_verify_channel_id(SSL_HANDSHAKE *hs, const SSLMessage &msg) { 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 @@ bool tls1_record_handshake_hashes_for_channel_id(SSL_HANDSHAKE *hs) { 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 @@ bool SSL_serialize_handoff(const SSL *ssl, CBB *out, !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 @@ static bool apply_remote_features(SSL *ssl, CBS *in) { // 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 @@ bool SSL_serialize_handback(const SSL *ssl, CBB *out) { } 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 @@ bool SSL_serialize_handback(const SSL *ssl, CBB *out) { !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 @@ bool SSL_serialize_handback(const SSL *ssl, CBB *out) { !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 @@ bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback) { } 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 @@ bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback) { !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 @@ bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback) { 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 @@ bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback) { 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 @@ bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback) { 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 @@ bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback) { } 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 @@ BSSL_NAMESPACE_BEGIN 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 @@ SSL_HANDSHAKE::SSL_HANDSHAKE(SSL *ssl_arg) 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 @@ void SSL_HANDSHAKE::ResizeSecrets(size_t hash_len) { 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_hash_message(SSL_HANDSHAKE *hs, const SSLMessage &msg) { } 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 @@ bool ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert, 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 @@ bool ssl_parse_extensions(const CBS *cbs, uint8_t *out_alert, } // 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 @@ enum ssl_verify_result_t ssl_reverify_peer_cert(SSL_HANDSHAKE *hs, 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 @@ uint16_t ssl_get_grease_value(SSL_HANDSHAKE *hs, 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 @@ const SSL_SESSION *ssl_handshake_session(const SSL_HANDSHAKE *hs) { 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 @@ int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) { 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 @@ int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) { 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 @@ int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) { 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 @@ int ssl_run_handshake(SSL_HANDSHAKE *hs, bool *out_early_return) { 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 @@ enum ssl_client_hs_state_t { // 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 void ssl_get_client_disabled(SSL_HANDSHAKE *hs, uint32_t *out_mask_a, } } -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 @@ static bool ssl_write_client_cipher_list(SSL_HANDSHAKE *hs, CBB *out) { 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 @@ static bool ssl_write_client_cipher_list(SSL_HANDSHAKE *hs, CBB *out) { } } - 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 @@ static bool ssl_write_client_cipher_list(SSL_HANDSHAKE *hs, CBB *out) { 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); + SSLExtension supported_versions(TLSEXT_TYPE_supported_versions); + CBS extensions = server_hello.extensions; + if (!ssl_parse_extensions(&extensions, out_alert, {&supported_versions}, + /*ignore_unknown=*/true)) { 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, - /*ignore_unknown=*/true)) { - ssl_send_alert(ssl, SSL3_AL_FATAL, alert); - return false; + if (!supported_versions.present) { + *out_version = server_hello.legacy_version; + return true; } - // 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 (!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 @@ static enum ssl_hs_wait_t do_start_connect(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_start_connect(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_start_connect(SSL_HANDSHAKE *hs) { } } - 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 @@ static enum ssl_hs_wait_t do_enter_early_data(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_hello_verify_request(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_hello_verify_request(SSL_HANDSHAKE *hs) { return ssl_hs_error; } - if (!ssl_write_client_hello(hs)) { + if (!ssl_add_client_hello(hs)) { return ssl_hs_error; } @@ -550,6 +645,38 @@ static enum ssl_hs_wait_t do_read_hello_verify_request(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { // 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { } } - 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { } // 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_send_client_certificate(SSL_HANDSHAKE *hs) { 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_certificate_verify(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_send_client_finished(SSL_HANDSHAKE *hs) { } } - 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 enum ssl_hs_wait_t do_send_client_finished(SSL_HANDSHAKE *hs) { } 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 @@ static bool can_false_start(const SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_session_ticket(SSL_HANDSHAKE *hs) { 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; - - // 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; + hs->new_session->ticket_lifetime_hint = ticket_lifetime_hint; - 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_read_server_finished(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_finish_client_handshake(SSL_HANDSHAKE *hs) { } 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 @@ static bool is_probably_jdk11_with_tls13(const SSL_CLIENT_HELLO *client_hello) { 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 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { } 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 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { } 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 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_client_hello(SSL_HANDSHAKE *hs) { 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_read_client_hello(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_select_certificate(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { } 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 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { 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 enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { } 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 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { // 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 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { // 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 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { } } - 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 @@ static enum ssl_hs_wait_t do_send_server_key_exchange(SSL_HANDSHAKE *hs) { } } + hs->can_release_private_key = true; if (!ssl_add_message_cbb(ssl, cbb.get())) { return ssl_hs_error; } @@ -1415,6 +1541,7 @@ static enum ssl_hs_wait_t do_read_client_key_exchange(SSL_HANDSHAKE *hs) { } 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_next_proto(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_finish_server_handshake(SSL_HANDSHAKE *hs) { 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 @@ enum ssl_hs_wait_t ssl_server_handshake(SSL_HANDSHAKE *hs) { 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 @@ const char *ssl_server_handshake_state(SSL_HANDSHAKE *hs) { 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 @@ class Array { 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 @@ class GrowableArray { 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 @@ class GrowableArray { 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 @@ bool ssl_get_version_range(const SSL_HANDSHAKE *hs, uint16_t *out_min_version, 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 @@ class SSLTranscript { 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 @@ class SSLTranscript { // 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 @@ class SSLTranscript { // 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 @@ class SSLTranscript { // 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 @@ class SSLKeyShare { // |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 @@ class SSLKeyShare { 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 ssl_on_certificate_selected(SSL_HANDSHAKE *hs); 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 @@ bool tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, // 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 ClientHello. +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; -// Encrypted Client Hello. + // 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); -// 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); + // 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 @@ enum ssl_hs_wait_t { 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 @@ enum ssl_hs_wait_t { 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 @@ enum ssl_grease_index_t { 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 @@ enum handback_t { 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 @@ struct SSL_HANDSHAKE { 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 @@ struct SSL_HANDSHAKE { 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 @@ struct SSL_HANDSHAKE { 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 @@ struct SSL_HANDSHAKE { // 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; + + // 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 value of the previously sent KeyShare extension by - // the client in TLS 1.3. + // 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 @@ struct SSL_HANDSHAKE { // 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 @@ struct SSL_HANDSHAKE { // 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 @@ struct SSL_HANDSHAKE { // 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 @@ struct SSL_HANDSHAKE { // 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 @@ struct SSL_HANDSHAKE { // 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 @@ bool tls13_process_new_session_ticket(SSL *ssl, const SSLMessage &msg); 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 @@ bool ssl_ext_pre_shared_key_add_serverhello(SSL_HANDSHAKE *hs, CBB *out); // 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 @@ bool tls13_get_cert_verify_signature_input( 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_is_alpn_protocol_allowed(const SSL_HANDSHAKE *hs, 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 @@ bool ssl_output_cert_chain(SSL_HANDSHAKE *hs); // 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 @@ bool ssl_log_secret(const SSL *ssl, const char *label, // 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 @@ bool ssl_client_cipher_list_contains_cipher( // 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 @@ bool tls12_check_peer_sigalg(const SSL_HANDSHAKE *hs, uint8_t *out_alert, #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 @@ struct SSL_PROTOCOL_METHOD { // 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 @@ enum ssl_shutdown_t { 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 @@ struct SSL3_STATE { // 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 @@ struct SSL3_STATE { 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 @@ struct SSL3_STATE { // 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 @@ struct SSL_CONFIG { 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 @@ struct SSL_CONFIG { // 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 @@ struct SSL_CONFIG { // 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 @@ struct SSL_CONFIG { // 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 @@ struct SSL_CONFIG { // 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_is_key_type_supported(int key_type); 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_rebase_time(SSL *ssl, SSL_SESSION *session); 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 @@ int tls_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf, 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 @@ bool tls1_set_curves(Array<uint16_t> *out_group_ids, Span<const int> curves); // 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 @@ bool tls1_channel_id_hash(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len); // 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 @@ struct ssl_ctx_st { 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 @@ struct ssl_ctx_st { // 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 @@ struct ssl_ctx_st { // 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 @@ struct ssl_session_st { 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 @@ static bool add_record_to_flight(SSL *ssl, uint8_t type, 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 @@ bool tls_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) { 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 @@ bool tls_flush_pending_hs_data(SSL *ssl) { 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 @@ int tls_flush_flight(SSL *ssl) { } } + 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 @@ SSL3_STATE::SSL3_STATE() 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 @@ int tls_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *in, 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 @@ int tls_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *in, 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 @@ bool ssl_cert_check_key_usage(const CBS *in, enum ssl_key_usage_t bit) { // 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 @@ static constexpr SSL_CIPHER kCiphers[] = { SSL_HANDSHAKE_MAC_DEFAULT, }, - // GCM ciphersuites from RFC5288 + // GCM ciphersuites from RFC 5288 // Cipher 9C { @@ -346,7 +346,7 @@ static constexpr SSL_CIPHER kCiphers[] = { 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 @@ class ECKeyShare : public SSLKeyShare { 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 @@ class X25519KeyShare : public SSLKeyShare { 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 @@ class CECPQ2KeyShare : public SSLKeyShare { 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 @@ class CECPQ2KeyShare : public SSLKeyShare { 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 @@ class CECPQ2KeyShare : public SSLKeyShare { 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(uint16_t group_id) { 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 @@ ssl_open_record_t ssl_open_app_data(SSL *ssl, Span<uint8_t> *out, 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 @@ static bool ssl_can_renegotiate(const SSL *ssl) { 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 @@ ssl_ctx_st::ssl_ctx_st(const SSL_METHOD *ssl_method) 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 *SSL_new(SSL_CTX *ctx) { 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 @@ SSL_CONFIG::SSL_CONFIG(SSL *ssl_arg) 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_read(SSL *ssl, void *buf, int num) { 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 @@ int SSL_write(SSL *ssl, const void *buf, int num) { 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 @@ int SSL_write(SSL *ssl, const void *buf, int num) { 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 @@ const char *SSL_early_data_reason_string(enum ssl_early_data_reason_t reason) { 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 @@ int SSL_get_error(const SSL *ssl, int ret_code) { 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 @@ const char *SSL_error_description(int err) { 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 @@ const char *SSL_error_description(int err) { 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 @@ int SSL_renegotiate(SSL *ssl) { 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 @@ void SSL_CTX_set_next_proto_select_cb( 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 @@ int SSL_CTX_set1_tls_channel_id(SSL_CTX *ctx, EVP_PKEY *private_key) { } ctx->channel_id_private = UpRef(private_key); - ctx->channel_id_enabled = true; - return 1; } @@ -2382,8 +2329,6 @@ int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) { } ssl->config->channel_id_private = UpRef(private_key); - ssl->config->channel_id_enabled = true; - return 1; } @@ -2395,25 +2340,6 @@ size_t SSL_get_tls_channel_id(SSL *ssl, uint8_t *out, size_t max_out) { 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 @@ void SSL_CTX_set_current_time_cb(SSL_CTX *ctx, 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 @@ void SSL_CTX_set_grease_enabled(SSL_CTX *ctx, int enabled) { 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 @@ void SSL_CTX_set_false_start_allowed_without_alpn(SSL_CTX *ctx, int allowed) { 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 @@ enum ssl_private_key_result_t ssl_private_key_sign( 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 @@ enum ssl_private_key_result_t ssl_private_key_decrypt(SSL_HANDSHAKE *hs, 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 CRYPTO_EX_DATA_CLASS g_ex_data_class = 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 @@ const EVP_MD *ssl_session_get_digest(const SSL_SESSION *session) { 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 @@ int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) { 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 @@ int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server) { 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 @@ enum ssl_hs_wait_t ssl_get_prev_session(SSL_HANDSHAKE *hs, return ssl_hs_ok; } -static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *session, int lock) { - int ret = 0; +static bool remove_session(SSL_CTX *ctx, SSL_SESSION *session, bool lock) { + if (session == nullptr || session->session_id_length == 0) { + return false; + } - 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_lock_write(&ctx->lock); + } - if (lock) { - CRYPTO_MUTEX_unlock_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 (ret) { - if (ctx->remove_session_cb != NULL) { - ctx->remove_session_cb(ctx, found_session); - } - SSL_SESSION_free(found_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 ret; + return found; } void ssl_set_session(SSL *ssl, SSL_SESSION *session) { @@ -854,6 +840,98 @@ static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *session) { } } +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_should_be_single_use(const SSL_SESSION *session) { } 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 @@ void *SSL_SESSION_get_ex_data(const SSL_SESSION *session, int idx) { } 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 @@ static void timeout_doall_arg(SSL_SESSION *session, void *void_param) { 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 @@ void (*SSL_CTX_get_info_callback(SSL_CTX *ctx))(const SSL *ssl, int type, 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 @@ const char *SSL_alert_desc_string_long(int value) { 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 @@ TEST(SSLTest, Padding) { } } +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 @@ static bssl::UniquePtr<X509> GetTestCertificate() { "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 @@ static bssl::UniquePtr<EVP_PKEY> GetTestKey() { "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 @@ static bssl::UniquePtr<X509> GetECDSATestCertificate() { "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 @@ static bssl::UniquePtr<EVP_PKEY> GetECDSATestKey() { "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 @@ static bssl::UniquePtr<EVP_PKEY> GetChainTestKey() { "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 @@ TEST(SSLTest, AddClientCA) { 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 @@ static const uint8_t kTestName[] = { 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 @@ class SSLVersionTest : public ::testing::TestWithParam<VersionParam> { 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 @@ TEST_P(SSLVersionTest, OneSidedShutdown) { } 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 @@ TEST_P(SSLVersionTest, OneSidedShutdown) { 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 @@ TEST(SSLTest, ClientHello) { } } -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_P(SSLVersionTest, SNICallback) { // 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 @@ class TicketAEADMethodTest : 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 @@ TEST_P(TicketAEADMethodTest, Resume) { 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, SelectNextProto) { 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, SealRecord) { 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, SealRecordInPlace) { 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, SealRecordTrailingData) { 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_P(SSLVersionTest, SSLPending) { // 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 @@ static int XORDecompressFunc(SSL *ssl, CRYPTO_BUFFER **out, 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 @@ void MoveBIOs(SSL *dest, SSL *src) { 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 @@ TEST(SSLTest, Handoff) { 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(); - } - if (is_resume && config.early_data) { - EXPECT_GT(g_last_session->ticket_max_early_data, 0u); + SSL_set_session(client.get(), g_last_session.get()); + if (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 @@ TEST(SSLTest, Handoff) { 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, Handoff) { 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, ApplyHandoffRemovesUnsupportedCurves) { 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 @@ TEST_P(SSLVersionTest, SessionCacheThreads) { } } - // 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 @@ TEST_P(SSLVersionTest, SessionCacheThreads) { } 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 @@ class QUICMethodTest : public testing::Test { 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 @@ TEST_P(SSLVersionTest, UnrelatedServerNoResume) { 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, WriteWhileExplicitRenegotiate) { 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 @@ TEST(SSLTest, CopyWithoutEarlyData) { 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 @@ TEST(SSLTest, CopyWithoutEarlyData) { 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, CopyWithoutEarlyData) { 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, ProcessTLS13NewSessionTicket) { 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, BIO) { } } +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 @@ bool SSLTranscript::Init() { 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 @@ bool SSLTranscript::UpdateForHelloRetryRequest() { 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 @@ bool SSLTranscript::Update(Span<const uint8_t> in) { 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::GetHash(uint8_t *out, size_t *out_len) { 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 @@ uint16_t ssl_protocol_version(const SSL *ssl) { 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 @@ bool ssl_supports_version(SSL_HANDSHAKE *hs, uint16_t version) { 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 @@ static bool ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session, 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 @@ static bool ssl_crypto_x509_session_verify_cert_chain(SSL_SESSION *session, 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 @@ static bool ssl_crypto_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) { 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 @@ static bool ssl_crypto_x509_ssl_auto_chain_if_needed(SSL_HANDSHAKE *hs) { 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 @@ int SSL_CTX_load_verify_locations(SSL_CTX *ctx, const char *ca_file, 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 @@ OPENSSL_MSVC_PRAGMA(comment(lib, "Ws2_32.lib")) #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 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume, } } - 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 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume, 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 @@ static bool DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session, 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 @@ static bool DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session, } 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 @@ static bool DoConnection(bssl::UniquePtr<SSL_SESSION> *out_session, } 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 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, 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 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, 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 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, 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 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, } // 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 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, // 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 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, !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 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, 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 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, } 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 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session, 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 @@ int main(int argc, char **argv) { 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 @@ const uint8_t kALPNProtocols[] = { 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 @@ class TLSFuzzer { 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 @@ class TLSFuzzer { } 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 @@ class TLSFuzzer { } 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 @@ class TLSFuzzer { 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 @@ class TLSFuzzer { 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 @@ class TLSFuzzer { 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 @@ static const uint16_t kHandoffTag = 3; // 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 @@ bool RetryAsync(SSL *ssl, int ret) { 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 @@ int CheckIdempotentError(const char *name, SSL *ssl, 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 @@ static bool Proxy(BIO *socket, bool async, int control, int rfd, int wfd) { return false; } switch (msg) { - case kControlMsgHandback: + case kControlMsgDone: return true; case kControlMsgError: return false; @@ -303,17 +298,90 @@ static bool Proxy(BIO *socket, bool async, int control, int rfd, int wfd) { 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 @@ static bool RunHandshaker(BIO *bio, const TestConfig *config, bool is_resume, 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 @@ static bool RunHandshaker(BIO *bio, const TestConfig *config, bool is_resume, // 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]); - fflush(stdout); - fflush(stderr); - - 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) { + if (pipe(wfd) != 0) { + perror("pipe"); return false; } - assert(kFdProxyToHandshaker != wfd[1]); - if (rfd[0] != kFdProxyToHandshaker && - posix_spawn_file_actions_adddup2(&actions, rfd[0], - kFdProxyToHandshaker) != 0) { + 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; } - 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; - } + rfd0_closer.Reset(); + wfd1_closer.Reset(); - 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 @@ static bool RunHandshaker(BIO *bio, const TestConfig *config, bool is_resume, } 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 @@ static bool RunHandshaker(BIO *bio, const TestConfig *config, bool is_resume, // 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 @@ static bool PrepareHandoff(SSL *ssl, SettingsWriter *writer, 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 @@ static bool PrepareHandoff(SSL *ssl, SettingsWriter *writer, 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 @@ bool DoSplitHandshake(UniquePtr<SSL> *ssl, SettingsWriter *writer, 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 @@ bool DoSplitHandshake(UniquePtr<SSL> *ssl, SettingsWriter *writer, 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 @@ bool RetryAsync(SSL *ssl, int ret); // 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 @@ int CheckIdempotentError(const char *name, SSL *ssl, std::function<int()> func); 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 @@ using namespace bssl; 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 @@ bool Handshaker(const TestConfig *config, int rfd, int wfd, 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 @@ bool Handshaker(const TestConfig *config, int rfd, int wfd, // 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 @@ bool Handshaker(const TestConfig *config, int rfd, int wfd, } } 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 SignalError() { 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 @@ int main(int argc, char **argv) { // 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 @@ bool MockQuicTransport::WriteApplicationData(const uint8_t *in, size_t len) { 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::Commit() { } 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 @@ struct SettingsWriter { 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 @@ const Flag<bool> kBoolFlags[] = { {"-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 @@ const Flag<bool> kBoolFlags[] = { {"-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 @@ const Flag<bool> kBoolFlags[] = { {"-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 @@ const Flag<bool> kBoolFlags[] = { {"-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 @@ const Flag<std::string> kStringFlags[] = { {"-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::unique_ptr<std::string>> kOptionalStringFlags[] = { }; 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<std::string> kBase64Flags[] = { 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 @@ const Flag<int> kIntFlags[] = { {"-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 @@ const Flag<std::vector<int>> kIntVectorFlags[] = { {"-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 @@ const Flag<std::vector<std::pair<std::string, std::string>>> {"-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 @@ bool ParseFlag(char *flag, int argc, char **argv, int *i, 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 @@ bool ParseFlag(char *flag, int argc, char **argv, int *i, 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 @@ bool ParseFlag(char *flag, int argc, char **argv, int *i, } 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 @@ bool ParseFlag(char *flag, int argc, char **argv, int *i, 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 @@ bool ParseConfig(int argc, char **argv, 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 @@ static void MessageCallback(int is_write, int version, int content_type, 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 @@ static void InfoCallback(const SSL *ssl, int type, int val) { 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 @@ static int AlpnSelectCallback(SSL *ssl, const uint8_t **out, uint8_t *outlen, 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 @@ static bool CheckVerifyCallback(SSL *ssl) { } } + 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 @@ static int CertVerifyCallback(X509_STORE_CTX *store_ctx, void *arg) { 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 @@ static int ClientCertCallback(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) { 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 @@ static ssl_private_key_result_t AsyncPrivateKeySign( } 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 @@ static ssl_private_key_result_t AsyncPrivateKeyDecrypt(SSL *ssl, uint8_t *out, 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 @@ static ssl_private_key_result_t AsyncPrivateKeyDecrypt(SSL *ssl, uint8_t *out, 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 @@ static ssl_private_key_result_t AsyncPrivateKeyComplete(SSL *ssl, uint8_t *out, 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 @@ static bool InstallCertificate(SSL *ssl) { 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 bool InstallCertificate(SSL *ssl) { 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 @@ static enum ssl_select_cert_result_t SelectCertificateCallback( 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 @@ static const SSL_QUIC_METHOD g_quic_method = { 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 @@ bssl::UniquePtr<SSL_CTX> TestConfig::SetupCtx(SSL_CTX *old_ctx) const { 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 @@ bssl::UniquePtr<SSL_CTX> TestConfig::SetupCtx(SSL_CTX *old_ctx) const { 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 @@ bssl::UniquePtr<SSL_CTX> TestConfig::SetupCtx(SSL_CTX *old_ctx) const { 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 @@ static int CertCallback(SSL *ssl, void *arg) { } 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 @@ bssl::UniquePtr<SSL> TestConfig::NewSSL( 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 @@ bssl::UniquePtr<SSL> TestConfig::NewSSL( 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 @@ bssl::UniquePtr<SSL> TestConfig::NewSSL( } } } - 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 @@ struct TestConfig { 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 @@ struct TestConfig { 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 @@ struct TestConfig { 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 @@ struct TestConfig { 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 @@ struct TestConfig { 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 @@ struct TestConfig { 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 @@ struct TestConfig { 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 @@ struct TestConfig { 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 @@ struct TestState { // 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 @@ struct TestState { 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 @@ struct TestState { 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 @@ struct TestState { // 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 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg, } // 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 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg, // 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 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg, } } - 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 @@ bool tls13_process_certificate(SSL_HANDSHAKE *hs, const SSLMessage &msg, 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 @@ bool tls13_add_certificate(SSL_HANDSHAKE *hs) { 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 @@ enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs) { 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 @@ static bool close_early_data(SSL_HANDSHAKE *hs, ssl_encryption_level_t level) { 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 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) { return ssl_hs_error; } - if (!ssl_check_message_type(ssl, msg, SSL3_MT_SERVER_HELLO)) { - 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); + 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; } - 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 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) { } } - 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 @@ static enum ssl_hs_wait_t do_read_hello_retry_request(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_send_second_client_hello(SSL_HANDSHAKE *hs) { // 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { 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), - 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)) { + // 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; } - // 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; + 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); } - // 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}, - }; + OPENSSL_memcpy(ssl->s3->server_random, CBS_data(&server_hello.random), + SSL3_RANDOM_SIZE); - 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { } 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { } 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { // 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 @@ static enum ssl_hs_wait_t do_read_server_hello(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_encrypted_extensions(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_encrypted_extensions(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_certificate_request(SSL_HANDSHAKE *hs) { } - 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 @@ static enum ssl_hs_wait_t do_read_certificate_request(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_send_end_of_early_data(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_send_client_certificate(SSL_HANDSHAKE *hs) { 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_send_client_certificate_verify(SSL_HANDSHAKE *hs) { 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 @@ UniquePtr<SSL_SESSION> tls13_create_session_with_ticket(SSL *ssl, CBS *body) { 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 @@ UniquePtr<SSL_SESSION> tls13_create_session_with_ticket(SSL *ssl, CBS *body) { } } - // 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 @@ static bool hkdf_extract_to_secret(SSL_HANDSHAKE *hs, Span<const uint8_t> in) { } 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 @@ bool tls13_init_key_schedule(SSL_HANDSHAKE *hs, Span<const uint8_t> psk) { 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 @@ bool tls13_advance_key_schedule(SSL_HANDSHAKE *hs, Span<const uint8_t> in) { 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 @@ static const char kTLS13LabelServerApplicationTraffic[] = "s ap traffic"; 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,98 +417,97 @@ bool tls13_export_keying_material(SSL *ssl, Span<uint8_t> out, 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)) { - 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, + 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)) || - !tls13_verify_data(out, out_len, digest, version, binder_key, context)) { + MakeConstSpan(binder_context, binder_context_len))) { return false; } - assert(*out_len == EVP_MD_size(digest)); - 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) { + // Hash the transcript and truncated ClientHello. + if (client_hello.size() < binders_len) { + OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } - client_hello = client_hello.subspan(0, client_hello.size() - binders_len - 2); - + 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; - 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)) { + if (!transcript.CopyToHashContext(ctx.get(), digest) || + !EVP_DigestUpdate(ctx.get(), truncated.data(), + truncated.size()) || + !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) { return false; } - *out_len = len; + if (!tls13_verify_data(out, out_len, digest, session->ssl_version, binder_key, + MakeConstSpan(context, context_len))) { + return false; + } + + assert(*out_len == EVP_MD_size(digest)); 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 @@ bool tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session, 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)) { +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; +} + +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}; + + // 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; } - // 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))) { + 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; } - if (out.size() > accept_confirmation.size()) { - OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); + 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; } - OPENSSL_memcpy(out.data(), accept_confirmation.data(), out.size()); - return true; + + 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 @@ static const uint8_t kZeroes[EVP_MAX_MD_SIZE] = {0}; // 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; + } + + 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, dhe_secret); + return tls13_advance_key_schedule(hs, secret); } static int ssl_ext_supported_versions_add_serverhello(SSL_HANDSHAKE *hs, @@ -132,7 +155,7 @@ static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) { (!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 @@ static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) { } } - // 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 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { // 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 @@ static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_ticket_aead_result_t select_session( 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 bool quic_ticket_compatible(const SSL_SESSION *session, 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 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { &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 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { } 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 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_select_session(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_send_hello_retry_request(SSL_HANDSHAKE *hs) { !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 @@ static enum ssl_hs_wait_t do_read_second_client_hello(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_second_client_hello(SSL_HANDSHAKE *hs) { } } - 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 @@ static enum ssl_hs_wait_t do_read_second_client_hello(SSL_HANDSHAKE *hs) { } 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 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) { 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_certificate_verify(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_second_client_flight(SSL_HANDSHAKE *hs) { 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 @@ static enum ssl_hs_wait_t do_read_client_encrypted_extensions( 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 @@ static enum ssl_hs_wait_t do_read_client_encrypted_extensions( // 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_client_certificate_verify(SSL_HANDSHAKE *hs) { 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 @@ static bool tls_set_read_state(SSL *ssl, ssl_encryption_level_t level, } 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 @@ static bool tls_set_write_state(SSL *ssl, ssl_encryption_level_t level, } 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 @@ add_executable( 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 @@ bool GetUnsigned(unsigned *out, const std::string &arg_name, 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 @@ static const struct argument kArguments[] = { "-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 @@ static const struct argument kArguments[] = { "-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 @@ static bool DoConnection(SSL_CTX *ctx, 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 @@ static bool DoConnection(SSL_CTX *ctx, } 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 @@ bool Client(const std::vector<std::string> &args) { 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 ReadAll(std::vector<uint8_t> *out, FILE *file) { } } } + +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 const struct argument kArguments[] = { }, }; -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 @@ bool GenerateEd25519Key(const std::vector<std::string> &args) { 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 @@ bool GetUnsigned(unsigned *out, const std::string &arg_name, 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 @@ static const struct argument kArguments[] = { "-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 @@ bool Server(const std::vector<std::string> &args) { } } + 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 @@ static bool SpeedRSA(const std::string &selected) { 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 @@ static bool SpeedRSAKeyGen(const std::string &selected) { 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 @@ static bool SpeedAEADChunk(const EVP_AEAD *aead, std::string name, 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 @@ static bool SpeedHRSS(const std::string &selected) { 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 @@ static bool SpeedHRSS(const std::string &selected) { 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 @@ static bool SpeedHRSS(const std::string &selected) { 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 @@ static const Tool kTools[] = { { "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 @@ void PrintConnectionInfo(BIO *bio, const SSL *ssl) { 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 @@ if(FIPS) 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 @@ static bool ReadAll(int fd, void *in_data, size_t data_len) { 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 @@ static bool WriteReply(int fd, Args... args) { 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 @@ static bool GetConfig(const Span<const uint8_t> args[]) { "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 @@ static bool GetConfig(const Span<const uint8_t> args[]) { "P-521" ], "hashAlg": [ + "SHA-1", "SHA2-224", "SHA2-256", "SHA2-384", @@ -676,9 +777,10 @@ static bool GetConfig(const Span<const uint8_t> args[]) { }, { "algorithm": "CMAC-AES", + "acvptoolTestOnly": true, "revision": "1.0", "capabilities": [{ - "direction": ["gen"], + "direction": ["gen", "ver"], "msgLen": [{ "min": 0, "max": 65536, @@ -715,6 +817,12 @@ static bool GetConfig(const Span<const uint8_t> args[]) { "initiator", "responder" ] + }, + "staticUnified": { + "kasRole": [ + "initiator", + "responder" + ] } }, "domainParameterGenerationMethods": [ @@ -740,35 +848,57 @@ static bool GetConfig(const Span<const uint8_t> args[]) { ] } ])"; - 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 @@ static uint32_t GetIterations(const Span<const uint8_t> iterations_bytes) { 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 @@ static bool AES(const Span<const uint8_t> args[]) { } } - 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 @@ static bool AES_CBC(const Span<const uint8_t> args[]) { } } - 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 @@ static bool AES_CTR(const Span<const uint8_t> args[]) { 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 @@ static bool AES_CTR(const Span<const uint8_t> args[]) { 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 @@ static bool AESGCMSetup(EVP_AEAD_CTX *ctx, Span<const uint8_t> tag_len_span, 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 @@ static bool AESCCMSetup(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)); 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 @@ static bool AESCCMSetup(EVP_AEAD_CTX *ctx, Span<const uint8_t> tag_len_span, 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 @@ static bool AEADSeal(const Span<const uint8_t> args[]) { } 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 @@ static bool AEADOpen(const Span<const uint8_t> args[]) { 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 @@ static bool AESKeyWrapSetup(AES_KEY *out, bool decrypt, Span<const uint8_t> key, } 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 @@ static bool AESKeyWrapSeal(const Span<const uint8_t> args[]) { 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 @@ static bool AESKeyWrapOpen(const Span<const uint8_t> args[]) { 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 @@ static bool AESPaddedKeyWrapSeal(const Span<const uint8_t> args[]) { 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 @@ static bool AESPaddedKeyWrapOpen(const Span<const uint8_t> args[]) { 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 @@ static bool TDES(const Span<const uint8_t> args[]) { } 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 @@ static bool TDES(const Span<const uint8_t> args[]) { } } - 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 @@ static bool TDES_CBC(const Span<const uint8_t> args[]) { } } - 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 @@ static bool HMAC(const Span<const uint8_t> args[]) { 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 @@ static bool DRBG(const Span<const uint8_t> args[]) { 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 @@ static std::pair<std::vector<uint8_t>, std::vector<uint8_t>> GetPublicKeyBytes( 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 @@ static bool ECDSAKeyGen(const Span<const uint8_t> args[]) { 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 @@ static bssl::UniquePtr<BIGNUM> BytesToBIGNUM(Span<const uint8_t> bytes) { 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 @@ static bool ECDSAKeyVer(const Span<const uint8_t> args[]) { 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 const EVP_MD *HashFromName(Span<const uint8_t> name) { } } -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 @@ static bool ECDSASigGen(const Span<const uint8_t> args[]) { 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 @@ static bool ECDSASigVer(const Span<const uint8_t> args[]) { 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 @@ static bool CMAC_AES(const Span<const uint8_t> args[]) { 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 @@ static bool CMAC_AESVerify(const Span<const uint8_t> args[]) { 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 @@ static RSA* GetRSAKey(unsigned bits) { 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 @@ static bool RSAKeyGen(const Span<const uint8_t> args[]) { 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 @@ static bool RSAKeyGen(const Span<const uint8_t> args[]) { 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 @@ static bool RSAKeyGen(const Span<const uint8_t> args[]) { 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 @@ static bool RSASigGen(const Span<const uint8_t> args[]) { 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 @@ static bool RSASigVer(const Span<const uint8_t> args[]) { } 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 @@ static bool TLSKDF(const Span<const uint8_t> args[]) { 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 @@ static bool ECDH(const Span<const uint8_t> args[]) { 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 @@ static bool ECDH(const Span<const uint8_t> args[]) { 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 @@ static bool ECDH(const Span<const uint8_t> args[]) { 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 @@ static bool ECDH(const Span<const uint8_t> args[]) { 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 @@ static bool FFDH(const Span<const uint8_t> args[]) { 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 @@ static bool FFDH(const Span<const uint8_t> args[]) { 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 @@ static bool FFDH(const Span<const uint8_t> args[]) { 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 @@ static constexpr struct { {"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 @@ static constexpr struct { {"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 @@ gcm_init_v8: 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 @@ gcm_gmult_v8: .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 @@ Ldone_v8: 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 @@ LIBSSL_DIR = $(dir $(shell find ${BUILD_DIR} -name libssl.a)) 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.rs b/examples/client.rs index 88490aa..24966e7 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -52,7 +52,7 @@ fn main() { 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 @@ fn main() { // 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(); @@ -120,7 +115,7 @@ fn main() { 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; @@ -216,7 +211,7 @@ fn main() { ); 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 +246,7 @@ fn main() { }, }; - 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; diff --git a/examples/http3-client.c b/examples/http3-client.c index 6b263ff..1dad8c8 100644 --- a/examples/http3-client.c +++ b/examples/http3-client.c @@ -98,6 +98,14 @@ static void flush_egress(struct ev_loop *loop, struct conn_io *conn_io) { 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 +117,7 @@ static int for_each_header(uint8_t *name, size_t name_len, 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; @@ -244,6 +253,16 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { 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 +297,17 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { } 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; diff --git a/examples/http3-client.rs b/examples/http3-client.rs index 2acb2ca..d4b8219 100644 --- a/examples/http3-client.rs +++ b/examples/http3-client.rs @@ -29,6 +29,8 @@ extern crate log; use std::net::ToSocketAddrs; +use quiche::h3::NameValue; + use ring::rand::*; const MAX_DATAGRAM_SIZE: usize = 1350; @@ -50,7 +52,7 @@ fn main() { 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 @@ fn main() { // 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(); @@ -119,7 +116,7 @@ fn main() { 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; @@ -238,7 +235,8 @@ fn main() { 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 +264,19 @@ fn main() { 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 +313,7 @@ fn main() { }, }; - 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; @@ -328,3 +337,14 @@ fn hex_dump(buf: &[u8]) -> String { vec.join("") } + +fn hdrs_to_strings(hdrs: &[quiche::h3::Header]) -> Vec<(String, String)> { + hdrs.iter() + .map(|h| { + ( + String::from_utf8(h.name().into()).unwrap(), + String::from_utf8(h.value().into()).unwrap(), + ) + }) + .collect() +} diff --git a/examples/http3-server.c b/examples/http3-server.c index 73c29ae..058ee6b 100644 --- a/examples/http3-server.c +++ b/examples/http3-server.c @@ -205,7 +205,7 @@ static struct conn_io *create_conn(uint8_t *scid, size_t scid_len, 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); @@ -442,6 +442,12 @@ static void recv_cb(EV_P_ ev_io *w, int revents) { 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; diff --git a/examples/http3-server.rs b/examples/http3-server.rs index e84d1ea..c2d9d84 100644 --- a/examples/http3-server.rs +++ b/examples/http3-server.rs @@ -46,7 +46,7 @@ struct PartialResponse { } struct Client { - conn: std::pin::Pin<Box<quiche::Connection>>, + conn: quiche::Connection, http3_conn: Option<quiche::h3::Connection>, @@ -70,20 +70,15 @@ fn main() { } // 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(); @@ -198,7 +193,7 @@ fn main() { 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 +230,7 @@ fn main() { 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; @@ -360,8 +355,15 @@ fn main() { 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 +405,7 @@ fn main() { }, }; - 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 +502,7 @@ fn handle_request( info!( "{} got request {:?} on stream id {}", conn.trace_id(), - headers, + hdrs_to_strings(headers), stream_id ); @@ -620,7 +622,7 @@ fn handle_writable(client: &mut Client, stream_id: u64) { 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 +659,14 @@ fn handle_writable(client: &mut Client, stream_id: u64) { client.partial_responses.remove(&stream_id); } } + +fn hdrs_to_strings(hdrs: &[quiche::h3::Header]) -> Vec<(String, String)> { + hdrs.iter() + .map(|h| { + ( + String::from_utf8(h.name().into()).unwrap(), + String::from_utf8(h.value().into()).unwrap(), + ) + }) + .collect() +} diff --git a/examples/server.c b/examples/server.c index a97250f..b6ac1f2 100644 --- a/examples/server.c +++ b/examples/server.c @@ -203,7 +203,7 @@ static struct conn_io *create_conn(uint8_t *scid, size_t scid_len, 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); diff --git a/examples/server.rs b/examples/server.rs index 90f0102..9a846e2 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -42,7 +42,7 @@ struct PartialResponse { } struct Client { - conn: std::pin::Pin<Box<quiche::Connection>>, + conn: quiche::Connection, partial_responses: HashMap<u64, PartialResponse>, } @@ -64,20 +64,15 @@ fn main() { } // 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(); @@ -192,7 +187,7 @@ fn main() { 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 +224,7 @@ fn main() { 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; @@ -348,7 +343,7 @@ fn main() { }, }; - 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 +494,7 @@ fn handle_writable(client: &mut Client, stream_id: u64) { 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..25318f9 100644 --- a/include/quiche.h +++ b/include/quiche.h @@ -34,6 +34,16 @@ extern "C" { #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,6 +107,9 @@ enum quiche_error { // 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, @@ -129,6 +142,10 @@ int quiche_config_load_priv_key_from_pem_file(quiche_config *config, 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); @@ -199,6 +216,12 @@ 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); + // Frees the config object. void quiche_config_free(quiche_config *config); @@ -285,6 +308,9 @@ typedef struct { 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(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); @@ -350,6 +376,9 @@ void quiche_conn_destination_id(quiche_conn *conn, const uint8_t **out, size_t * void quiche_conn_application_proto(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(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); @@ -377,6 +406,9 @@ uint64_t quiche_conn_peer_streams_left_uni(quiche_conn *conn); // Returns true if the connection is closed. bool quiche_conn_is_closed(quiche_conn *conn); +// Returns true if the connection was closed due to the idle timeout. +bool quiche_conn_is_timed_out(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, @@ -385,6 +417,14 @@ bool quiche_conn_peer_error(quiche_conn *conn, 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(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,14 +455,71 @@ typedef struct { // The number of QUIC packets that were lost. size_t lost; + // The number of sent QUIC packets with retranmitted data. + size_t retrans; + // The estimated round-trip time of the connection (in nanoseconds). uint64_t rtt; // The size of the connection's congestion window in bytes. size_t cwnd; - // The estimated data delivery rate in bytes/s. + // The number of sent bytes. + uint64_t sent_bytes; + + // The number of recevied 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 current PMTU for the connection. + size_t pmtu; + + // The most recent data delivery rate estimate in bytes/s. uint64_t delivery_rate; + + // 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. @@ -437,7 +534,7 @@ ssize_t quiche_conn_dgram_recv_front_len(quiche_conn *conn); // Returns the number of items in the DATAGRAM receive queue. ssize_t quiche_conn_dgram_recv_queue_len(quiche_conn *conn); -///Returns the total size of all items in the DATAGRAM receive queue. +// 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 number of items in the DATAGRAM send queue. @@ -469,73 +566,123 @@ void quiche_conn_free(quiche_conn *conn); #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, }; // Stores configuration shared between multiple connections. @@ -544,8 +691,8 @@ typedef struct Http3Config 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); @@ -573,6 +720,8 @@ enum quiche_h3_event_type { 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; @@ -596,6 +745,16 @@ int quiche_h3_event_for_each_header(quiche_h3_event *ev, 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); @@ -610,6 +769,12 @@ typedef struct { 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 +789,7 @@ int quiche_h3_send_response(quiche_h3_conn *conn, quiche_conn *quic_conn, 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 +800,23 @@ ssize_t quiche_h3_send_body(quiche_h3_conn *conn, quiche_conn *quic_conn, 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); + +// 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..f77d468 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 38dd21b..bc3ee19 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 @@ index aced8a6..578cc68 100644 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,25 @@ index aced8a6..578cc68 100644 "liblazy_static", "liblibc", "liblibm", - "liblog_rust", +@@ -63,10 +64,8 @@ rust_ffi_shared { + "liboctets", "libring", ], - 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", - ], -@@ -69,54 +82,20 @@ rust_ffi_shared { + apex_available: [ + "//apex_available:platform", + "com.android.resolv", +@@ -74,26 +73,10 @@ rust_ffi_shared { + min_sdk_version: "29", + } - rust_library { - name: "libquiche", +-rust_library { +- name: "libquiche", - host_supported: true, - crate_name: "quiche", - cargo_env_compat: true, @@ -65,26 +60,23 @@ index aced8a6..578cc68 100644 - "liblibc", - "liblibm", - "liblog_rust", +- "liboctets", - "libring", - ], - 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", +@@ -104,28 +87,22 @@ rust_library { + 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 +92,21 @@ index aced8a6..578cc68 100644 - "liblibc", - "liblibm", - "liblog_rust", +- "liboctets", - "libring", -- ], ++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 +114,7 @@ index aced8a6..578cc68 100644 "libssl", ], apex_available: [ -@@ -111,17 +90,13 @@ rust_library_rlib { +@@ -135,17 +112,13 @@ rust_ffi_static { min_sdk_version: "29", } @@ -128,8 +133,8 @@ index aced8a6..578cc68 100644 - }, edition: "2018", features: [ - "boringssl", -@@ -136,10 +132,6 @@ rust_test { + "boringssl-vendored", +@@ -161,10 +134,6 @@ rust_test { "libring", "liburl", ], @@ -140,7 +145,7 @@ index aced8a6..578cc68 100644 data: [ "examples/cert.crt", "examples/cert.key", -@@ -149,3 +118,26 @@ rust_test { +@@ -172,3 +141,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/src/build.rs b/src/build.rs index 875f556..e675bfe 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")]), @@ -97,17 +77,11 @@ fn get_boringssl_cmake_config() -> cmake::Config { // 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); @@ -199,11 +173,10 @@ fn get_boringssl_cmake_config() -> cmake::Config { 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 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); @@ -222,14 +195,31 @@ Version: {} Libs: -Wl,-rpath,${{libdir}} -L${{libdir}} -lquiche Cflags: -I${{includedir}} ", - include_dir, target_dir, version + include_dir, + target_dir.to_str().unwrap(), + version ); 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(); @@ -249,13 +239,14 @@ fn main() { println!("cargo:rustc-link-lib=static=ssl"); } - if cfg!(feature = "boring-sys") { + if cfg!(feature = "boringssl-boring-crate") { println!("cargo:rustc-link-lib=static=crypto"); println!("cargo:rustc-link-lib=static=ssl"); } // 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/crypto.rs b/src/crypto.rs index 6e33957..079961f 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -24,9 +24,14 @@ // 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; @@ -68,11 +73,13 @@ pub enum Algorithm { } 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 +100,11 @@ impl Algorithm { } 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,20 +112,28 @@ impl Algorithm { 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, + ctx: EVP_AEAD_CTX, - key: aead::LessSafeKey, + hp_key: aead::quic::HeaderProtectionKey, nonce: Vec<u8>, } @@ -124,20 +143,17 @@ impl Open { alg: Algorithm, key: &[u8], iv: &[u8], hp_key: &[u8], ) -> Result<Open> { Ok(Open { + alg, + + ctx: make_aead_ctx(alg, key)?, + 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, }) } @@ -149,9 +165,9 @@ impl Open { let mut iv = vec![0; nonce_len]; let mut pn_key = vec![0; key_len]; - derive_pkt_key(aead, &secret, &mut key)?; - derive_pkt_iv(aead, &secret, &mut iv)?; - derive_hdr_key(aead, &secret, &mut pn_key)?; + derive_pkt_key(aead, secret, &mut key)?; + derive_pkt_iv(aead, secret, &mut iv)?; + derive_hdr_key(aead, secret, &mut pn_key)?; Open::new(aead, &key, &iv, &pn_key) } @@ -163,16 +179,37 @@ impl Open { return Ok(buf.len()); } + let tag_len = self.alg().tag_len(); + + let mut out_len = match buf.len().checked_sub(tag_len) { + Some(n) => n, + None => return Err(Error::CryptoFail), + }; + + let max_out_len = out_len; + let nonce = make_nonce(&self.nonce, counter); - let ad = aead::Aad::from(ad); + let rc = unsafe { + EVP_AEAD_CTX_open( + &self.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 + ) + }; - let plain = self - .key - .open_in_place(nonce, ad, buf) - .map_err(|_| Error::CryptoFail)?; + if rc != 1 { + return Err(Error::CryptoFail); + } - Ok(plain.len()) + Ok(out_len) } pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> { @@ -196,9 +233,9 @@ impl Open { pub struct Seal { alg: Algorithm, - hp_key: aead::quic::HeaderProtectionKey, + ctx: EVP_AEAD_CTX, - key: aead::LessSafeKey, + hp_key: aead::quic::HeaderProtectionKey, nonce: Vec<u8>, } @@ -208,20 +245,17 @@ impl Seal { alg: Algorithm, key: &[u8], iv: &[u8], hp_key: &[u8], ) -> Result<Seal> { Ok(Seal { + alg, + + ctx: make_aead_ctx(alg, key)?, + 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, }) } @@ -233,40 +267,66 @@ impl Seal { let mut iv = vec![0; nonce_len]; let mut pn_key = vec![0; key_len]; - derive_pkt_key(aead, &secret, &mut key)?; - derive_pkt_iv(aead, &secret, &mut iv)?; - derive_hdr_key(aead, &secret, &mut pn_key)?; + derive_pkt_key(aead, secret, &mut key)?; + derive_pkt_iv(aead, secret, &mut iv)?; + derive_hdr_key(aead, secret, &mut pn_key)?; Seal::new(aead, &key, &iv, &pn_key) } 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 tag_len = self.alg().tag_len(); - let ad = aead::Aad::from(ad); + let mut out_tag_len = tag_len; - let tag_len = self.alg().tag_len(); + let (extra_in_ptr, extra_in_len) = match extra_in { + Some(v) => (v.as_ptr(), v.len()), - let in_out_len = - buf.len().checked_sub(tag_len).ok_or(Error::CryptoFail)?; + None => (std::ptr::null(), 0), + }; - let (in_out, tag_out) = buf.split_at_mut(in_out_len); + // Make sure all the outputs combined fit in the buffer. + if in_len + tag_len + extra_in_len > buf.len() { + return Err(Error::CryptoFail); + } - let tag = self - .key - .seal_in_place_separate_tag(nonce, ad, in_out) - .map_err(|_| Error::CryptoFail)?; + let nonce = make_nonce(&self.nonce, counter); - // Append the AEAD tag to the end of the sealed buffer. - tag_out.copy_from_slice(tag.as_ref()); + let rc = unsafe { + EVP_AEAD_CTX_seal_scatter( + &self.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(()) + Ok(in_len + out_tag_len) } pub fn new_mask(&self, sample: &[u8]) -> Result<[u8; 5]> { @@ -297,7 +357,7 @@ pub fn derive_initial_key_material( 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]; @@ -418,6 +478,31 @@ pub fn derive_pkt_iv( 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 +521,9 @@ fn hkdf_expand_label( 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 +531,7 @@ fn make_nonce(iv: &[u8], counter: u64) -> aead::Nonce { *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 +545,49 @@ impl hkdf::KeyType for ArbitraryOutputLen { } } +#[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..5da185e 100644 --- a/src/dgram.rs +++ b/src/dgram.rs @@ -46,13 +46,14 @@ impl DatagramQueue { } } - 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.push_back(data); + Ok(()) } @@ -76,7 +76,7 @@ use crate::*; #[no_mangle] pub extern fn quiche_version() -> *const u8 { - //static VERSION: &str = concat!("0.9.0", "\0"); + //static VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\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 +166,19 @@ pub extern fn quiche_config_load_verify_locations_from_file( } #[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); } @@ -307,6 +320,18 @@ pub extern fn quiche_config_set_max_send_udp_payload_size( } #[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_free(config: *mut Config) { unsafe { Box::from_raw(config) }; } @@ -395,7 +420,7 @@ pub extern fn quiche_accept( let from = std_addr_from_c(from, from_len); match accept(&scid, odcid.as_ref(), from, config) { - Ok(c) => Box::into_raw(Pin::into_inner(c)), + Ok(c) => Box::into_raw(Box::new(c)), Err(_) => ptr::null_mut(), } @@ -418,7 +443,7 @@ pub extern fn quiche_connect( let to = std_addr_from_c(to, to_len); match connect(server_name, &scid, to, config) { - Ok(c) => Box::into_raw(Pin::into_inner(c)), + Ok(c) => Box::into_raw(Box::new(c)), Err(_) => ptr::null_mut(), } @@ -503,7 +528,7 @@ pub extern fn quiche_conn_new_with_tls( tls, is_server, ) { - Ok(c) => Box::into_raw(Pin::into_inner(c)), + Ok(c) => Box::into_raw(Box::new(c)), Err(_) => ptr::null_mut(), } @@ -877,6 +902,20 @@ pub extern fn quiche_conn_application_proto( } #[no_mangle] +pub extern fn quiche_conn_peer_cert( + conn: &mut 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, ) { @@ -911,6 +950,11 @@ pub extern fn quiche_conn_is_closed(conn: &mut Connection) -> bool { } #[no_mangle] +pub extern fn quiche_conn_is_timed_out(conn: &mut 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, reason: &mut *const u8, reason_len: &mut size_t, @@ -930,6 +974,25 @@ pub extern fn quiche_conn_peer_error( } #[no_mangle] +pub extern fn quiche_conn_local_error( + conn: &mut 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 +1014,28 @@ pub struct Stats { recv: usize, sent: usize, lost: usize, + retrans: usize, rtt: u64, cwnd: usize, + sent_bytes: u64, + lost_bytes: u64, + recv_bytes: u64, + stream_retrans_bytes: u64, + pmtu: usize, delivery_rate: u64, + 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, } #[no_mangle] @@ -963,9 +1045,34 @@ pub extern fn quiche_conn_stats(conn: &Connection, out: &mut Stats) { 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.lost_bytes = stats.lost_bytes; + out.recv_bytes = stats.recv_bytes; + out.stream_retrans_bytes = stats.stream_retrans_bytes; + out.pmtu = stats.pmtu; out.delivery_rate = stats.delivery_rate; + 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, + } } #[no_mangle] @@ -1073,6 +1180,11 @@ pub extern fn quiche_conn_peer_streams_left_uni(conn: &mut Connection) -> u64 { conn.peer_streams_left_uni() } +#[no_mangle] +pub extern fn quiche_conn_send_quantum(conn: &mut 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 { 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..f568b29 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -24,19 +24,36 @@ // 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, Debug, PartialEq)] +pub struct EcnCounts { + ect0_count: u64, + ect1_count: u64, + ecn_ce_count: u64, +} + #[derive(Clone, PartialEq)] pub enum Frame { Padding { @@ -48,6 +65,7 @@ pub enum Frame { ACK { ack_delay: u64, ranges: ranges::RangeSet, + ecn_counts: Option<EcnCounts>, }, ResetStream { @@ -124,7 +142,7 @@ pub enum Frame { seq_num: u64, retire_prior_to: u64, conn_id: Vec<u8>, - reset_token: Vec<u8>, + reset_token: [u8; 16], }, RetireConnectionId { @@ -132,11 +150,11 @@ pub enum Frame { }, PathChallenge { - data: Vec<u8>, + data: [u8; 8], }, PathResponse { - data: Vec<u8>, + data: [u8; 8], }, ConnectionClose { @@ -155,6 +173,10 @@ pub enum Frame { Datagram { data: Vec<u8>, }, + + DatagramHeader { + length: usize, + }, } impl Frame { @@ -180,7 +202,7 @@ impl Frame { 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 +267,11 @@ impl Frame { 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 +279,19 @@ impl Frame { }, 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 +365,16 @@ impl Frame { 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 +397,12 @@ impl Frame { 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 { @@ -382,7 +430,7 @@ impl Frame { Frame::Crypto { data } => { encode_crypto_header(data.off() as u64, data.len() as u64, b)?; - b.put_bytes(&data)?; + b.put_bytes(data)?; }, Frame::CryptoHeader { .. } => (), @@ -391,7 +439,7 @@ impl Frame { b.put_varint(0x07)?; b.put_varint(token.len() as u64)?; - b.put_bytes(&token)?; + b.put_bytes(token)?; }, Frame::Stream { stream_id, data } => { @@ -403,7 +451,7 @@ impl Frame { b, )?; - b.put_bytes(&data)?; + b.put_bytes(data)?; }, Frame::StreamHeader { .. } => (), @@ -517,16 +565,12 @@ impl Frame { }, Frame::Datagram { data } => { - let mut ty: u8 = 0x30; - - // Always encode length - ty |= 0x01; + encode_dgram_header(data.len() as u64, b)?; - 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 +582,11 @@ impl Frame { 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 +610,12 @@ impl Frame { 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 }, @@ -723,9 +777,15 @@ impl Frame { 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 +800,194 @@ impl Frame { ) } - pub fn shrink_for_retransmission(&mut self) { - if let Frame::Datagram { data } = self { - *data = Vec::new(); - } - } - #[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), - - Frame::Crypto { data } => qlog::QuicFrame::crypto( - data.off().to_string(), - data.len().to_string(), - ), - - Frame::CryptoHeader { offset, length } => - qlog::QuicFrame::crypto(offset.to_string(), length.to_string()), - - Frame::NewToken { token } => qlog::QuicFrame::new_token( - token.len().to_string(), - "TODO: https://github.com/quiclog/internet-drafts/issues/36" - .to_string(), - ), - - Frame::Stream { stream_id, data } => qlog::QuicFrame::stream( - stream_id.to_string(), - data.off().to_string(), - data.len().to_string(), - data.fin(), - None, - ), + } => QuicFrame::StopSending { + stream_id: *stream_id, + error_code: *error_code, + }, + + Frame::Crypto { data } => QuicFrame::Crypto { + offset: data.off(), + length: data.len() as u64, + }, + + Frame::CryptoHeader { offset, length } => QuicFrame::Crypto { + offset: *offset, + length: *length as u64, + }, + + Frame::NewToken { token } => QuicFrame::NewToken { + token: qlog::Token { + // TODO: pick the token type some how + ty: Some(qlog::TokenType::StatelessReset), + length: None, + data: qlog::HexSlice::maybe_string(Some(token)), + details: None, + }, + }, + + Frame::Stream { stream_id, data } => QuicFrame::Stream { + stream_id: *stream_id, + offset: data.off() as u64, + length: data.len() as u64, + fin: data.fin().then(|| 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, - ), - - Frame::MaxData { max } => qlog::QuicFrame::max_data(max.to_string()), - - Frame::MaxStreamData { stream_id, max } => - qlog::QuicFrame::max_stream_data( - stream_id.to_string(), - max.to_string(), - ), - - Frame::MaxStreamsBidi { max } => qlog::QuicFrame::max_streams( - qlog::StreamType::Bidirectional, - max.to_string(), - ), - - Frame::MaxStreamsUni { max } => qlog::QuicFrame::max_streams( - qlog::StreamType::Unidirectional, - max.to_string(), - ), + } => QuicFrame::Stream { + stream_id: *stream_id, + offset: *offset, + length: *length as u64, + fin: fin.then(|| true), + raw: None, + }, + + Frame::MaxData { max } => QuicFrame::MaxData { maximum: *max }, + + Frame::MaxStreamData { stream_id, max } => QuicFrame::MaxStreamData { + stream_id: *stream_id, + maximum: *max, + }, + + Frame::MaxStreamsBidi { max } => QuicFrame::MaxStreams { + stream_type: StreamType::Bidirectional, + maximum: *max, + }, + + 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(), - ), - - Frame::StreamsBlockedBidi { limit } => - qlog::QuicFrame::streams_blocked( - qlog::StreamType::Bidirectional, - limit.to_string(), - ), - - Frame::StreamsBlockedUni { limit } => - qlog::QuicFrame::streams_blocked( - qlog::StreamType::Unidirectional, - limit.to_string(), - ), + QuicFrame::StreamDataBlocked { + stream_id: *stream_id, + limit: *limit, + }, + + Frame::StreamsBlockedBidi { limit } => QuicFrame::StreamsBlocked { + stream_type: StreamType::Bidirectional, + limit: *limit, + }, + + 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: Some(qlog::Token { + ty: Some(qlog::TokenType::StatelessReset), + length: None, + data: qlog::HexSlice::maybe_string(Some(reset_token)), + details: None, + }), + }, 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), + raw_error_code: None, // raw error is no different for us + reason: Some(String::from_utf8(reason.clone()).unwrap()), + 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 */ - ), - - Frame::HandshakeDone => qlog::QuicFrame::handshake_done(), - - Frame::Datagram { data } => - qlog::QuicFrame::datagram(data.len().to_string(), None), + QuicFrame::ConnectionClose { + error_space: Some(ErrorSpace::ApplicationError), + error_code: Some(*error_code), + raw_error_code: None, // raw error is no different for us + reason: Some(String::from_utf8(reason.clone()).unwrap()), + trigger_frame_type: None, // don't know trigger type + }, + + Frame::HandshakeDone => QuicFrame::HandshakeDone, + + Frame::Datagram { data } => QuicFrame::Datagram { + length: data.len() as u64, + raw: None, + }, + + Frame::DatagramHeader { length } => QuicFrame::Datagram { + length: *length as u64, + raw: None, + }, } } } @@ -925,8 +1003,16 @@ impl std::fmt::Debug for Frame { 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={} blocks={:?} ecn_counts={:?}", + ack_delay, ranges, ecn_counts + )?; }, Frame::ResetStream { @@ -1065,7 +1151,11 @@ impl std::fmt::Debug for Frame { }, 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 +1163,9 @@ impl std::fmt::Debug for Frame { } } -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 +1179,6 @@ fn parse_ack_frame(_ty: u64, b: &mut octets::Octets) -> Result<Frame> { 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 +1197,26 @@ fn parse_ack_frame(_ty: u64, b: &mut octets::Octets) -> Result<Frame> { 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 +1259,20 @@ pub fn encode_stream_header( 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; @@ -1268,6 +1388,7 @@ mod tests { let frame = Frame::ACK { ack_delay: 874_656_534, ranges, + ecn_counts: None, }; let wire_len = { @@ -1291,6 +1412,48 @@ mod tests { } #[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 +1848,7 @@ mod tests { 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 +1902,7 @@ mod tests { 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 +1930,7 @@ mod tests { 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 +2044,14 @@ mod tests { 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 +2075,5 @@ mod tests { }; 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..d32b1be 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 libc::ssize_t; 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 @@ pub extern fn quiche_h3_config_new() -> *mut h3::Config { } #[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] @@ -85,6 +87,29 @@ pub extern fn quiche_h3_conn_new_with_transport( } #[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 +139,10 @@ pub extern fn quiche_h3_event_type(ev: &h3::Event) -> u32 { h3::Event::Datagram { .. } => 3, h3::Event::GoAway { .. } => 4, + + h3::Event::Reset { .. } => 5, + + h3::Event::PriorityUpdate { .. } => 6, } } @@ -207,17 +236,15 @@ pub extern fn quiche_h3_send_response( #[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 +290,49 @@ pub extern fn quiche_h3_recv_body( } #[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_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..46b802d 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,11 +36,13 @@ pub const SETTINGS_FRAME_TYPE_ID: u64 = 0x4; 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_H3_DATAGRAM: u64 = 0x276; // Permit between 16 maximally-encoded and 128 minimally-encoded SETTINGS. const MAX_SETTINGS_PAYLOAD_SIZE: usize = 256; @@ -59,11 +62,12 @@ pub enum Frame { }, Settings { - max_header_list_size: Option<u64>, + max_field_section_size: Option<u64>, qpack_max_table_capacity: Option<u64>, qpack_blocked_streams: Option<u64>, h3_datagram: Option<u64>, grease: Option<(u64, u64)>, + raw: Option<Vec<(u64, u64)>>, }, PushPromise { @@ -79,7 +83,20 @@ pub enum Frame { 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 +133,14 @@ impl Frame { 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 +172,17 @@ impl Frame { }, Frame::Settings { - max_header_list_size, + max_field_section_size, qpack_max_table_capacity, qpack_blocked_streams, 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); } @@ -184,8 +209,8 @@ impl Frame { 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)?; + if let Some(val) = max_field_section_size { + b.put_varint(SETTINGS_MAX_FIELD_SECTION_SIZE)?; b.put_varint(*val as u64)?; } @@ -236,22 +261,161 @@ impl Frame { 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 as u64)?; + 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 as u64)?; + b.put_bytes(priority_field_value)?; + }, + + Frame::Unknown { .. } => unreachable!(), } Ok(before - b.cap()) } + + #[cfg(feature = "qlog")] + pub fn to_qlog(&self) -> Http3Frame { + 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, + 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) = 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 { + raw_frame_type: *raw_type, + raw_length: Some(*payload_length as u32), + raw: 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 } => { @@ -259,12 +423,13 @@ impl std::fmt::Debug for Frame { }, 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={:?}, qpack_max_table={:?}, qpack_blocked={:?} raw={:?}", max_field_section_size, qpack_max_table_capacity, qpack_blocked_streams, raw)?; }, Frame::PushPromise { @@ -287,8 +452,32 @@ impl std::fmt::Debug for Frame { 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 +488,11 @@ impl std::fmt::Debug for Frame { 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 h3_datagram = None; + let mut raw = Vec::new(); // Reject SETTINGS frames that are too long. if settings_length > MAX_SETTINGS_PAYLOAD_SIZE { @@ -310,28 +500,32 @@ fn parse_settings_frame( } 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()?; + + // MAX_SETTINGS_PAYLOAD_SIZE protects us from storing too many raw + // settings. + raw.push((identifier, value)); - match setting_ty { + 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 { + if value > 1 { return Err(super::Error::SettingsError); } - h3_datagram = Some(settings_val); + h3_datagram = Some(value); }, // Reserved values overlap with HTTP/2 and MUST be rejected @@ -344,11 +538,12 @@ fn parse_settings_frame( } Ok(Frame::Settings { - max_header_list_size, + max_field_section_size, qpack_max_table_capacity, qpack_blocked_streams, h3_datagram, grease: None, + raw: Some(raw), }) } @@ -365,6 +560,31 @@ fn parse_push_promise( }) } +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,12 +676,20 @@ mod tests { 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_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), h3_datagram: Some(0), grease: None, + raw: Some(raw_settings), }; let frame_payload_len = 9; @@ -490,20 +718,31 @@ mod tests { 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), 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_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), h3_datagram: Some(0), grease: None, + raw: Some(raw_settings), }; let frame_payload_len = 11; @@ -531,12 +770,15 @@ mod tests { 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, h3_datagram: None, grease: None, + raw: Some(raw_settings), }; let frame_payload_len = 3; @@ -564,12 +806,15 @@ mod tests { 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, h3_datagram: Some(1), grease: None, + raw: Some(raw_settings), }; let frame_payload_len = 3; @@ -598,11 +843,12 @@ mod tests { 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, h3_datagram: Some(5), grease: None, + raw: Default::default(), }; let frame_payload_len = 3; @@ -629,12 +875,18 @@ mod tests { 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), h3_datagram: None, grease: None, + raw: Some(raw_settings), }; let frame_payload_len = 4; @@ -837,9 +1089,79 @@ mod tests { } #[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..f3d9e06 100644 --- a/src/h3/mod.rs +++ b/src/h3/mod.rs @@ -164,8 +164,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. //! }, @@ -220,8 +226,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 +281,31 @@ //! [`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; + +#[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::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. /// @@ -286,6 +319,24 @@ pub const APPLICATION_PROTOCOL: &[u8] = b"\x02h3\x05h3-29\x05h3-28\x05h3-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://datatracker.ietf.org/doc/html/draft-ietf-httpbis-priority-12#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 @@ -406,7 +457,7 @@ impl Error { 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,6 +466,8 @@ impl Error { Error::MessageError => -18, Error::ConnectError => -19, Error::VersionFallback => -20, + + Error::TransportError(quic_error) => quic_error.to_c() - 1000, } } } @@ -449,22 +502,22 @@ impl std::convert::From<octets::BufferTooShortError> for Error { /// 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>, } 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, }) } - /// 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 +526,8 @@ impl Config { /// /// [`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. @@ -530,7 +583,7 @@ 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) } } @@ -574,6 +627,11 @@ pub enum Event { /// 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 +645,129 @@ pub enum Event { /// [`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)] +#[repr(C)] +pub struct Priority { + urgency: u8, + incremental: bool, +} + +impl Default for Priority { + fn default() -> Self { + Priority { + urgency: PRIORITY_URGENCY_DEFAULT as u8, + 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://datatracker.ietf.org/doc/html/draft-ietf-httpbis-priority-12#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 as u8, 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 h3_datagram: Option<u64>, + pub raw: Option<Vec<(u64, u64)>>, } struct QpackStreams { @@ -610,7 +782,7 @@ pub struct Connection { 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 +793,6 @@ pub struct Connection { qpack_encoder: qpack::Encoder, qpack_decoder: qpack::Decoder, - #[allow(dead_code)] local_qpack_streams: QpackStreams, peer_qpack_streams: QpackStreams, @@ -638,7 +809,6 @@ pub struct Connection { } impl Connection { - #[allow(clippy::unnecessary_wraps)] fn new( config: &Config, is_server: bool, enable_dgram: bool, ) -> Result<Connection> { @@ -652,20 +822,22 @@ impl Connection { 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, 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, + raw: Default::default(), }, control_stream_id: None, @@ -774,6 +946,10 @@ impl Connection { 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 +982,10 @@ impl Connection { &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 +994,32 @@ impl Connection { /// 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://datatracker.ietf.org/doc/html/draft-ietf-httpbis-priority-12#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 +1036,7 @@ impl Connection { 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 +1056,17 @@ impl Connection { 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 +1077,13 @@ impl Connection { }, }; - 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); + 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)?; - 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 +1093,27 @@ impl Connection { 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(); + + 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, + }); - // Sending header block separately avoids unnecessary copy. - conn.stream_send(stream_id, &header_block, fin)?; + q.add_event_data_now(ev_data).ok(); + }); if let Some(s) = self.streams.get_mut(&stream_id) { s.initialize_local(); @@ -1009,6 +1183,12 @@ impl Connection { }, }; + if stream_cap < overhead + body.len() { + // Ensure the peer is notified that the connection or stream is + // blocked when the stream's capacity is limited by flow control. + let _ = conn.stream_writable(stream_id, overhead + body.len()); + } + // Make sure there is enough capacity to send the DATA frame header. if stream_cap < overhead { return Err(Error::Done); @@ -1026,14 +1206,6 @@ impl Connection { 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 +1215,26 @@ impl Connection { // 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 fin && written == body.len() && conn.stream_finished(stream_id) { self.streams.remove(&stream_id); } @@ -1073,7 +1265,7 @@ impl Connection { b.put_varint(flow_id)?; b.put_bytes(buf)?; - conn.dgram_send(&d)?; + conn.dgram_send_vec(d)?; Ok(()) } @@ -1215,6 +1407,30 @@ impl Connection { Ok(total) } + /// 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 +1451,10 @@ impl Connection { /// 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 +1465,12 @@ impl Connection { /// [`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 +1534,11 @@ impl Connection { 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 +1610,17 @@ impl Connection { 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 +1630,13 @@ impl Connection { 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 +1677,22 @@ impl Connection { 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), + old: None, + new: H3StreamType::QpackEncode, + associated_push_id: None, + }); + + q.add_event_data_now(ev_data).ok(); + }); Ok(()) } @@ -1442,9 +1700,22 @@ impl Connection { 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), + old: None, + new: H3StreamType::QpackDecode, + associated_push_id: None, + }); + + q.add_event_data_now(ev_data).ok(); + }); Ok(()) } @@ -1483,8 +1754,6 @@ impl Connection { 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 +1761,24 @@ impl Connection { 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 +1788,27 @@ impl Connection { 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 +1817,21 @@ impl Connection { 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), + old: None, + new: H3StreamType::Unknown, + associated_push_id: None, + }); + + q.add_event_data_now(ev_data).ok(); + }); }, Err(Error::IdError) => { @@ -1528,9 +1848,22 @@ impl Connection { /// 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 = + self.open_uni_stream(conn, stream::HTTP3_CONTROL_STREAM_TYPE_ID)?; + + 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), + old: None, + new: 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 +1872,14 @@ impl Connection { }; 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, h3_datagram: self.local_settings.h3_datagram, grease, + raw: Default::default(), }; let mut d = [42; 128]; @@ -1557,6 +1891,25 @@ impl Connection { 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 +1978,19 @@ impl Connection { 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), + old: None, + new: 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. @@ -1757,13 +2123,38 @@ impl Connection { 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 +2168,7 @@ impl Connection { 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,7 +2184,8 @@ impl Connection { }, }; - 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) => (), @@ -1866,28 +2258,46 @@ impl Connection { 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, h3_datagram, + raw, .. } => { self.peer_settings = ConnectionSettings { - max_header_list_size, + max_field_section_size, qpack_max_table_capacity, qpack_blocked_streams, h3_datagram, + raw, }; if let Some(1) = h3_datagram { @@ -1915,11 +2325,11 @@ impl Connection { 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 + .max_field_section_size .unwrap_or(std::u64::MAX); let headers = match self @@ -1942,6 +2352,30 @@ impl Connection { }, }; + 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 +2508,116 @@ impl Connection { // 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) @@ -2127,6 +2670,7 @@ pub mod testing { 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 +2684,8 @@ pub mod testing { 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)?, }) } @@ -3110,6 +3654,484 @@ mod tests { } #[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::default().unwrap(); + s.handshake().unwrap(); + + s.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 0, + priority_field_value: b"u=3".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + 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::default().unwrap(); + s.handshake().unwrap(); + + s.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 0, + priority_field_value: b"u=3".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + assert_eq!(s.poll_server(), Ok((0, Event::PriorityUpdate))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + // Once the PriorityUpdate event was fired, subsequent frames will not + // rearm it. + s.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 0, + priority_field_value: b"u=5".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + 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.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 0, + priority_field_value: b"u=7".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + 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::default().unwrap(); + s.handshake().unwrap(); + + s.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 0, + priority_field_value: b"u=3".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + assert_eq!(s.poll_server(), Ok((0, Event::PriorityUpdate))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + s.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 4, + priority_field_value: b"u=1".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + assert_eq!(s.poll_server(), Ok((4, Event::PriorityUpdate))); + assert_eq!(s.poll_server(), Err(Error::Done)); + + s.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 8, + priority_field_value: b"u=2".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + 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::default().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::default().unwrap(); + s.handshake().unwrap(); + + s.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 0, + priority_field_value: b"u=3".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + 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.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 0, + priority_field_value: b"u=3".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + // 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::default().unwrap(); + s.handshake().unwrap(); + + s.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 0, + priority_field_value: b"u=3".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + 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.send_frame_client( + frame::Frame::PriorityUpdateRequest { + prioritized_element_id: 0, + priority_field_value: b"u=3".to_vec(), + }, + s.client.control_stream_id.unwrap(), + false, + ) + .unwrap(); + + // 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::default().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::default().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::default().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::default().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::default().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(); @@ -3311,7 +4333,7 @@ mod tests { 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] @@ -3392,7 +4414,7 @@ mod tests { 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(); @@ -3503,7 +4525,7 @@ mod tests { 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)); @@ -3556,6 +4578,61 @@ mod tests { } #[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"\x02h3").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) + ); + + // 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.client.send_request(&mut s.pipe.client, &req, true), Ok(0)); + } + + #[test] /// Test handling of 0-length DATA writes with and without fin. fn zero_length_data() { let mut s = Session::default().unwrap(); @@ -3737,11 +4814,12 @@ mod tests { ); let settings = frame::Frame::Settings { - max_header_list_size: None, + max_field_section_size: None, qpack_max_table_capacity: None, qpack_blocked_streams: None, h3_datagram: Some(1), grease: None, + raw: Default::default(), }; s.send_frame_client(settings, s.client.control_stream_id.unwrap(), false) @@ -4485,6 +5563,183 @@ mod tests { 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::default().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::default().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::default().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 @@ impl Representation { } /// 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 @@ fn decode_str(b: &mut octets::Octets) -> Result<Vec<u8>> { 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..ae75f27 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; 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 { @@ -163,8 +156,6 @@ fn encode_str(v: &[u8], prefix: usize, b: &mut octets::OctetsMut) -> Result<()> 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..04391ab 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; 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..8a14aa3 100644 --- a/src/h3/qpack/mod.rs +++ b/src/h3/qpack/mod.rs @@ -70,8 +70,8 @@ impl std::error::Error for Error { } } -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 } } diff --git a/src/h3/stream.rs b/src/h3/stream.rs index 58e2873..cd2c10e 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; @@ -48,6 +46,20 @@ pub enum Type { Unknown, } +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::Data, + 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)] pub enum State { /// Reading the stream's type. @@ -141,6 +153,9 @@ pub struct Stream { /// 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 @@ impl Stream { local_initialized: false, data_event_triggered: false, + + last_priority_update: None, } } @@ -327,6 +344,11 @@ impl Stream { 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); @@ -441,20 +463,24 @@ impl Stream { } /// 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 +561,21 @@ impl Stream { 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,29 +586,31 @@ impl Stream { 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::*; #[test] @@ -579,12 +622,19 @@ mod tests { 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), h3_datagram: None, grease: None, + raw: Some(raw_settings), }; b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap(); @@ -622,7 +672,7 @@ mod tests { // 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); } @@ -635,12 +685,19 @@ mod tests { 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), h3_datagram: None, grease: None, + raw: Some(raw_settings), }; b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap(); @@ -679,7 +736,7 @@ mod tests { // 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. @@ -700,12 +757,19 @@ mod tests { 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), h3_datagram: None, grease: None, + raw: Some(raw_settings), }; b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap(); @@ -743,12 +807,20 @@ mod tests { 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), h3_datagram: None, grease: None, + raw: Some(raw_settings), }; b.put_varint(HTTP3_CONTROL_STREAM_TYPE_ID).unwrap(); @@ -837,7 +909,7 @@ mod tests { // 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. @@ -930,7 +1002,7 @@ mod tests { // 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. @@ -196,6 +196,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 @@ -251,6 +270,7 @@ //! [`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,25 +306,61 @@ //! 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::time; use std::net::SocketAddr; -use std::pin::Pin; use std::str::FromStr; -use std::sync::Mutex; - use std::collections::VecDeque; /// The current QUIC wire version. @@ -361,6 +417,16 @@ const MAX_UNDECRYPTABLE_PACKETS: usize = 10; 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; + /// A specialized [`Result`] type for quiche operations. /// /// This type is used throughout quiche's public API for any operation that @@ -419,6 +485,12 @@ pub enum Error { /// 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, @@ -458,6 +530,7 @@ impl Error { Error::FinalSize => -13, Error::CongestionControl => -14, Error::StreamStopped { .. } => -15, + Error::StreamReset { .. } => -16, } } } @@ -494,6 +567,10 @@ pub struct SendInfo { pub to: SocketAddr, /// The time to send the packet out. + /// + /// See [Pacing] for more details. + /// + /// [Pacing]: index.html#pacing pub at: time::Instant, } @@ -524,17 +601,28 @@ pub enum Shutdown { 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>>, @@ -548,6 +636,9 @@ pub struct Config { dgram_send_max_queue_len: usize, max_send_udp_payload_size: usize, + + max_connection_window: u64, + max_stream_window: u64, } // See https://quicwg.org/base-drafts/rfc9000.html#section-15 @@ -565,12 +656,28 @@ impl Config { /// # 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, @@ -584,6 +691,9 @@ impl Config { 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, }) } @@ -600,10 +710,7 @@ impl Config { /// # 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 +725,7 @@ impl Config { /// # 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 +741,7 @@ impl Config { /// # 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 +759,7 @@ impl Config { 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 +767,7 @@ impl Config { /// 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 +786,7 @@ impl Config { /// [`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,12 +800,12 @@ impl Config { /// 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. @@ -728,7 +829,7 @@ impl Config { /// # Ok::<(), quiche::Error>(()) /// ``` pub fn set_application_protos(&mut self, protos: &[u8]) -> Result<()> { - let mut b = octets::Octets::with_slice(&protos); + let mut b = octets::Octets::with_slice(protos); let mut protos_list = Vec::new(); @@ -738,10 +839,7 @@ impl Config { self.application_protos = protos_list; - self.tls_ctx - .lock() - .unwrap() - .set_alpn(&self.application_protos) + self.tls_ctx.set_alpn(&self.application_protos) } /// Sets the `max_idle_timeout` transport parameter, in milliseconds. @@ -921,6 +1019,20 @@ impl Config { 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; + } } /// A QUIC connection. @@ -947,11 +1059,7 @@ pub struct Connection { 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. /// @@ -973,15 +1081,14 @@ pub struct Connection { /// Total number of sent packets. sent_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, @@ -995,10 +1102,23 @@ pub struct Connection { /// Peer's flow control limit for the connection. max_tx_data: u64, + /// Last tx_data before running a full send() loop. + last_tx_data: u64, + /// Total number of bytes the server can send before the peer's address /// is verified. max_send_bytes: usize, + /// 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, + /// Streams map, indexed by stream ID. streams: stream::StreamMap, @@ -1022,7 +1142,7 @@ pub struct Connection { peer_error: Option<ConnectionError>, /// Received path challenge. - challenge: Option<Vec<u8>>, + challenge: Option<[u8; 8]>, /// The connection-level limit at which send blocking occurred. blocked_limit: Option<u64>, @@ -1067,9 +1187,12 @@ pub struct Connection { /// 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, @@ -1080,19 +1203,17 @@ pub struct Connection { /// 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, @@ -1124,7 +1245,7 @@ pub struct Connection { pub fn accept( scid: &ConnectionId, odcid: Option<&ConnectionId>, from: SocketAddr, config: &mut Config, -) -> Result<Pin<Box<Connection>>> { +) -> Result<Connection> { let conn = Connection::new(scid, odcid, from, config, true)?; Ok(conn) @@ -1150,11 +1271,11 @@ pub fn accept( 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)?; +) -> Result<Connection> { + let mut conn = Connection::new(scid, None, to, 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) @@ -1289,40 +1410,94 @@ macro_rules! push_frame_to_pkt { }}; } -/// 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()?; + ) -> Result<Connection> { + let tls = config.tls_ctx.new_handshake()?; Connection::with_tls(scid, odcid, 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>>> { + ) -> 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(); - let mut conn = Box::pin(Connection { + let mut conn = Connection { version: config.version, dcid: ConnectionId::default(), @@ -1340,11 +1515,11 @@ impl Connection { local_transport_params: config.local_transport_params.clone(), - handshake: Mutex::new(tls), + handshake: tls, session: None, - recovery: recovery::Recovery::new(&config), + recovery: recovery::Recovery::new(config), peer_addr: peer, @@ -1352,22 +1527,32 @@ impl Connection { recv_count: 0, sent_count: 0, + retrans_count: 0, + sent_bytes: 0, + recv_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_data: 0, max_tx_data: 0, + last_tx_data: 0, + + stream_retrans_bytes: 0, max_send_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, @@ -1413,6 +1598,7 @@ impl Connection { handshake_completed: false, handshake_done_sent: false, + handshake_done_acked: false, handshake_confirmed: false, @@ -1420,15 +1606,14 @@ impl Connection { 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,7 +1624,7 @@ impl Connection { ), emit_dgram: true, - }); + }; if let Some(odcid) = odcid { conn.local_transport_params @@ -1454,11 +1639,9 @@ impl Connection { conn.local_transport_params.initial_source_connection_id = Some(scid.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()?; @@ -1485,6 +1668,8 @@ impl Connection { conn.derived_initial_secrets = true; } + conn.recovery.on_init(); + Ok(conn) } @@ -1501,22 +1686,54 @@ impl Connection { /// 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 +1742,33 @@ impl Connection { 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 = self.local_transport_params.to_qlog( - qlog::TransportOwner::Local, - self.version, - handshake.alpn_protocol(), - handshake.cipher(), - ); + let ev_data = self + .local_transport_params + .to_qlog(TransportOwner::Local, self.handshake.cipher()); - streamer.add_event(ev).ok(); + // 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(); - self.qlog_streamer = Some(streamer); + self.qlog.streamer = Some(streamer); } /// Configures the given session for resumption. @@ -1574,10 +1787,7 @@ impl Connection { 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)?; @@ -1803,7 +2013,7 @@ impl Connection { // Reset connection state to force sending another Initial packet. 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 = Some(aead_open); @@ -1811,8 +2021,6 @@ impl Connection { 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 @@ -1862,7 +2070,7 @@ impl Connection { // Reset connection state to force sending another Initial packet. 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 = Some(aead_open); @@ -1881,8 +2089,6 @@ impl Connection { 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 @@ -1984,7 +2190,7 @@ impl Connection { 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) })?; @@ -2004,36 +2210,15 @@ impl Connection { pn ); - qlog_with!(self.qlog_streamer, q, { - let packet_size = b.len(); - - 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), - ); - - q.add_event(qlog::event::Event::packet_received( - hdr.ty.to_qlog(), - qlog_pkt_hdr, - Some(Vec::new()), - None, - None, - None, - )) - .ok(); - }); + #[cfg(feature = "qlog")] + let mut qlog_frames = vec![]; 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) @@ -2083,12 +2268,15 @@ impl Connection { // ACK and PADDING. let mut ack_elicited = false; - // Process packet payload. + // Process packet payload. If a frame cannot be processed, store the + // error and stop further packet processing. + let mut frame_processing_err = None; + 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() { @@ -2096,43 +2284,68 @@ impl Connection { } 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(); - }); - - return Err(e); + 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, { + if let Some(ev_data) = self.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(); - - let ev = self.peer_transport_params.to_qlog( - qlog::TransportOwner::Remote, - self.version, - handshake.alpn_protocol(), - handshake.cipher(), - ); + 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()); - q.add_event(ev).ok(); + q.add_event_data_with_instant(ev_data, now).ok(); - self.qlogged_peer_params = true; + self.qlog.logged_peer_params = true; } }); } @@ -2181,6 +2394,14 @@ impl Connection { } }, + 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, @@ -2222,15 +2443,14 @@ impl Connection { } // 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; let read = b.off() + aead_tag_len; + self.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. @@ -2260,14 +2480,18 @@ impl Connection { /// * 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). + /// /// [`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 /// /// ## Examples: /// @@ -2386,6 +2610,8 @@ impl Connection { } if done == 0 { + self.last_tx_data = self.tx_data; + return Err(Error::Done); } @@ -2403,10 +2629,7 @@ impl Connection { let info = SendInfo { to: self.peer_addr, - at: self - .recovery - .get_packet_send_time() - .unwrap_or_else(time::Instant::now), + at: self.recovery.get_packet_send_time(), }; Ok((done, info)) @@ -2441,6 +2664,10 @@ impl Connection { .crypto_stream .send .retransmit(offset, length); + + self.stream_retrans_bytes += length as u64; + + self.retrans_count += 1; }, frame::Frame::StreamHeader { @@ -2475,6 +2702,10 @@ impl Connection { incremental, ); } + + self.stream_retrans_bytes += length as u64; + + self.retrans_count += 1; }, frame::Frame::ACK { .. } => { @@ -2491,7 +2722,9 @@ impl Connection { .mark_reset(stream_id, true, error_code, final_size); }, - frame::Frame::HandshakeDone => { + // 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; }, @@ -2616,6 +2849,7 @@ impl Connection { let frame = frame::Frame::ACK { ack_delay, ranges: self.pkt_num_spaces[epoch].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) { @@ -2625,10 +2859,7 @@ impl Connection { if pkt_type == packet::Type::Short && !is_closing { // Create HANDSHAKE_DONE frame. - if self.is_established() && - !self.handshake_done_sent && - self.is_server - { + if self.should_send_handshake_done() { let frame = frame::Frame::HandshakeDone; if push_frame_to_pkt!(b, frames, frame, left) { @@ -2692,19 +2923,30 @@ impl Connection { }, }; + // Autotune the stream window size. + stream.recv.autotune_window(now, self.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. + self.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 +2954,19 @@ impl Connection { } // Create MAX_DATA frame as needed. - if self.almost_full && self.max_rx_data < self.max_rx_data_next { + if self.almost_full && self.max_rx_data() < self.max_rx_data_next() { + // Autotune the connection window size. + self.flow_control.autotune_window(now, self.recovery.rtt()); + let frame = frame::Frame::MaxData { - max: self.max_rx_data_next, + max: self.max_rx_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; + self.flow_control.update_max_data(now); ack_eliciting = true; in_flight = true; @@ -2823,10 +3068,8 @@ impl Connection { } // Create PATH_RESPONSE frame. - if let Some(ref challenge) = self.challenge { - let frame = frame::Frame::PathResponse { - data: challenge.clone(), - }; + if let Some(challenge) = self.challenge { + let frame = frame::Frame::PathResponse { data: challenge }; if push_frame_to_pkt!(b, frames, frame, left) { self.challenge = None; @@ -2926,11 +3169,55 @@ impl Connection { { if let Some(max_dgram_payload) = self.dgram_max_writable_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; @@ -2980,9 +3267,8 @@ impl Connection { // 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. // @@ -3061,7 +3347,7 @@ impl Connection { // 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. + // Create PING for PTO probe if no other ack-eliciting frame is sent. if self.recovery.loss_probes[epoch] > 0 && !ack_eliciting && left >= 1 && @@ -3115,11 +3401,10 @@ impl Connection { } 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 @@ -3134,41 +3419,50 @@ impl Connection { pn ); - qlog_with!(self.qlog_streamer, q, { - let qlog_pkt_hdr = qlog::PacketHeader::with_type( + #[cfg(feature = "qlog")] + let mut qlog_frames = Vec::with_capacity(frames.len()); + + for frame in &mut frames { + trace!("{} tx frm {:?}", self.trace_id, frame); + + qlog_with_type!(QLOG_PACKET_TX, self.qlog, _q, { + qlog_frames.push(frame.to_qlog()); + }); + } + + qlog_with_type!(QLOG_PACKET_TX, self.qlog, q, { + let qlog_pkt_hdr = qlog::events::quic::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(); - }); - - for frame in &mut frames { - trace!("{} tx frm {:?}", self.trace_id, frame); + // 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, + }; - qlog_with!(self.qlog_streamer, q, { - q.add_frame(frame.to_qlog(), false).ok(); + let ev_data = EventData::PacketSent(qlog::events::quic::PacketSent { + 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, }); - // 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(); + q.add_event_data_with_instant(ev_data, now).ok(); }); let aead = match self.pkt_num_spaces[epoch].crypto_seal { @@ -3182,6 +3476,7 @@ impl Connection { pn_len, payload_len, payload_offset, + None, aead, )?; @@ -3196,11 +3491,15 @@ impl Connection { in_flight, delivered: 0, delivered_time: now, - recent_delivered_packet_sent_time: now, + first_sent_time: now, is_app_limited: false, has_data, }; + if in_flight && self.delivery_rate_check_if_app_limited() { + self.recovery.delivery_rate_update_app_limited(true); + } + self.recovery.on_packet_sent( sent_pkt, epoch, @@ -3209,14 +3508,16 @@ impl Connection { &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) = self.recovery.maybe_qlog() { + q.add_event_data_with_instant(ev_data, now).ok(); + } }); self.pkt_num_spaces[epoch].next_pkt_num += 1; self.sent_count += 1; + self.sent_bytes += written as u64; if self.dgram_send_queue.byte_size() > self.recovery.cwnd_available() { self.recovery.update_app_limited(false); @@ -3244,6 +3545,19 @@ impl Connection { 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 conjuction with segmentatation + /// offloading mechanisms as the maximum limit for outgoing aggregates of + /// multiple packets. + #[inline] + pub fn send_quantum(&mut self) -> usize { + self.recovery.send_quantum() + } + /// 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 +3566,11 @@ impl Connection { /// 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: /// @@ -3288,19 +3606,34 @@ impl Connection { 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, + + 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.max_rx_data_next = self.max_rx_data_next.saturating_add(read as u64); + 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 +3646,18 @@ impl Connection { 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), + data: None, + }); + + let now = time::Instant::now(); + q.add_event_data_with_instant(ev_data, now).ok(); }); if self.should_update_max_data() { @@ -3337,6 +3672,11 @@ impl Connection { /// 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. @@ -3394,7 +3734,13 @@ impl Connection { // 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. let cap = self.tx_cap; + if cap == 0 && !(fin && buf.is_empty()) { + return Err(Error::Done); + } let (buf, fin) = if cap < buf.len() { (&buf[..cap], false) @@ -3431,8 +3777,12 @@ impl Connection { 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); } @@ -3453,20 +3803,24 @@ impl Connection { self.tx_data += sent as u64; - self.recovery.rate_check_app_limited(); + 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), + data: None, + }); - 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(); + 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) } @@ -3540,7 +3894,14 @@ impl Connection { }, 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); + + // Update send capacity. + self.update_tx_cap(); self.streams.mark_reset(stream_id, true, err, final_size); @@ -3585,6 +3946,50 @@ impl Connection { stream.is_readable() } + /// 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. + /// + /// 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. + /// + /// [`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(stream_id) { + Some(v) => v, + + None => return Err(Error::InvalidStreamState(stream_id)), + }; + + 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(); + self.streams.mark_blocked(stream_id, true, max_off); + } + + 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 @@ -3810,6 +4215,21 @@ impl Connection { } } + /// 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 @@ -3891,10 +4311,35 @@ impl Connection { /// ``` 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())?; + + if self.dgram_send_queue.byte_size() > self.recovery.cwnd_available() { + self.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 { @@ -4013,7 +4458,7 @@ impl Connection { let now = time::Instant::now(); if timeout <= now { - return Some(time::Duration::new(0, 0)); + return Some(time::Duration::ZERO); } return Some(timeout.duration_since(now)); @@ -4032,7 +4477,7 @@ impl Connection { 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,11 +4494,12 @@ impl Connection { 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; } } @@ -4068,12 +4514,11 @@ impl Connection { &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) = self.recovery.maybe_qlog() { + q.add_event_data_with_instant(ev_data, now).ok(); + } }); - - return; } } } @@ -4136,10 +4581,16 @@ impl Connection { 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 serialized cryptographic session for the connection. @@ -4149,8 +4600,8 @@ impl Connection { /// /// [`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. @@ -4180,14 +4631,14 @@ impl Connection { /// 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. @@ -4222,21 +4673,38 @@ impl Connection { 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 { @@ -4244,9 +4712,48 @@ impl Connection { recv: self.recv_count, sent: self.sent_count, lost: self.recovery.lost_count, + retrans: self.retrans_count, cwnd: self.recovery.cwnd(), rtt: self.recovery.rtt(), + sent_bytes: self.sent_bytes, + lost_bytes: self.recovery.bytes_lost, + recv_bytes: self.recv_bytes, + stream_retrans_bytes: self.stream_retrans_bytes, + pmtu: self.recovery.max_datagram_size(), delivery_rate: self.recovery.delivery_rate(), + 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, } } @@ -4259,10 +4766,7 @@ impl Connection { &mut raw_params, )?; - self.handshake - .lock() - .unwrap() - .set_quic_transport_params(raw_params)?; + self.handshake.set_quic_transport_params(raw_params)?; Ok(()) } @@ -4336,10 +4840,7 @@ impl Connection { 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); @@ -4359,14 +4860,27 @@ impl Connection { /// /// If the connection is already established, it does nothing. fn do_handshake(&mut self) -> Result<()> { - let handshake = self.handshake.lock().unwrap(); + 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 +4890,11 @@ impl Connection { // 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,23 +4905,15 @@ impl Connection { Err(e) => return Err(e), }; - self.handshake_completed = handshake.is_completed(); - - self.alpn = handshake.alpn_protocol().to_vec(); + self.handshake_completed = self.handshake.is_completed(); - let cipher = handshake.cipher(); - let curve = handshake.curve(); - let sigalg = handshake.sigalg(); - let is_resumed = handshake.is_resumed(); + self.alpn = self.handshake.alpn_protocol().to_vec(); - 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)?; } @@ -4422,8 +4925,13 @@ impl Connection { } 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.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(()) } @@ -4437,7 +4945,7 @@ impl Connection { .as_ref() .map_or(false, |conn_err| !conn_err.is_app) { - let epoch = match self.handshake.lock().unwrap().write_level() { + let epoch = match self.handshake.write_level() { crypto::Level::Initial => packet::EPOCH_INITIAL, crypto::Level::ZeroRTT => unreachable!(), crypto::Level::Handshake => packet::EPOCH_HANDSHAKE, @@ -4478,7 +4986,7 @@ impl Connection { // If there are flushable, almost full or blocked streams, use the // Application epoch. 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() || @@ -4493,7 +5001,8 @@ impl Connection { self.streams.has_reset() || self.streams.has_stopped()) { - 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); } @@ -4528,7 +5037,9 @@ impl Connection { 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, @@ -4547,6 +5058,10 @@ impl Connection { self.handshake_confirmed = true; } + if self.delivery_rate_check_if_app_limited() { + self.recovery.delivery_rate_update_app_limited(true); + } + self.recovery.on_ack_received( &ranges, ack_delay, @@ -4564,8 +5079,8 @@ impl Connection { 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 +5089,8 @@ impl Connection { 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 +5109,20 @@ impl Connection { 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 +5157,15 @@ impl Connection { 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.streams .mark_reset(stream_id, true, error_code, final_size); @@ -4655,17 +5189,10 @@ impl Connection { 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()?; }, frame::Frame::CryptoHeader { .. } => unreachable!(), @@ -4681,7 +5208,7 @@ impl Connection { 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,9 +5236,11 @@ impl Connection { return Err(Error::FlowControl); } + let was_readable = stream.is_readable(); + stream.recv.write(data)?; - if stream.is_readable() { + if !was_readable && stream.is_readable() { self.streams.mark_readable(stream_id, true); } @@ -4858,8 +5387,10 @@ impl Connection { self.dgram_recv_queue.pop(); } - self.dgram_recv_queue.push(&data)?; + self.dgram_recv_queue.push(data)?; }, + + frame::Frame::DatagramHeader { .. } => unreachable!(), } Ok(()) @@ -4889,8 +5420,22 @@ impl Connection { /// 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 the updated connection level flow control limit. + fn max_rx_data_next(&self) -> u64 { + self.flow_control.max_data_next() + } + + /// 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. @@ -4936,6 +5481,36 @@ impl Connection { completed: self.is_established(), } } + + /// Updates send capacity. + fn update_tx_cap(&mut self) { + self.tx_cap = cmp::min( + self.recovery.cwnd_available() as u64, + 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. + self.tx_cap >= self.recovery.cwnd_available() && + (self.tx_data.saturating_sub(self.last_tx_data)) < + self.recovery.cwnd_available() as u64 && + self.recovery.cwnd_available() > 0 + } } /// Maps an `Error` to `Error::Done`, or itself. @@ -4959,7 +5534,7 @@ impl Connection { 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 { @@ -4989,14 +5564,78 @@ pub struct Stats { /// 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 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, + + /// 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 +5643,75 @@ impl std::fmt::Debug for Stats { 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={} rtt={:?} cwnd={}", + self.recv, self.sent, self.lost, self.retrans, self.rtt, self.cwnd, + )?; + + 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, " }}") } } @@ -5218,7 +5923,7 @@ impl TransportParams { 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)?; } }; @@ -5234,7 +5939,7 @@ impl TransportParams { if is_server { if let Some(ref token) = tp.stateless_reset_token { TransportParams::encode_param(&mut b, 0x0002, token.len())?; - b.put_bytes(&token)?; + b.put_bytes(token)?; } } @@ -5336,13 +6041,13 @@ impl TransportParams { 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 +6068,56 @@ impl TransportParams { /// 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 = Some(qlog::Token { + ty: Some(qlog::TokenType::StatelessReset), + length: None, + data: qlog::HexSlice::maybe_string( + self.stateless_reset_token.as_ref(), + ), + details: None, + }); + + 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,8 +6127,8 @@ pub mod testing { use super::*; pub struct Pipe { - pub client: Pin<Box<Connection>>, - pub server: Pin<Box<Connection>>, + pub client: Connection, + pub server: Connection, } impl Pipe { @@ -5421,7 +6145,7 @@ pub mod testing { 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) } @@ -5468,6 +6192,7 @@ pub mod testing { 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( @@ -5498,6 +6223,7 @@ pub mod testing { 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( @@ -5659,11 +6385,10 @@ pub mod testing { 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 +6413,7 @@ pub mod testing { pn_len, payload_len, payload_offset, + None, aead, )?; @@ -5709,7 +6435,7 @@ pub mod testing { 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, @@ -5918,6 +6644,8 @@ mod tests { pipe.client.application_proto(), pipe.server.application_proto() ); + + assert_eq!(pipe.server.server_name(), Some("quic.tech")); } #[test] @@ -5926,11 +6654,7 @@ mod tests { // 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(())); @@ -6534,7 +7258,7 @@ mod tests { // Force server to send a single PING frame. pipe.server.recovery.loss_probes[packet::EPOCH_INITIAL] = 1; - // Artifically limit the amount of bytes the server can send. + // Artificially limit the amount of bytes the server can send. pipe.server.max_send_bytes = 60; assert_eq!(pipe.server.send(&mut buf), Err(Error::Done)); @@ -6647,7 +7371,7 @@ mod tests { max: 30 }) ); - assert_eq!(iter.next(), Some(&frame::Frame::MaxData { max: 46 })); + assert_eq!(iter.next(), Some(&frame::Frame::MaxData { max: 61 })); } #[test] @@ -6731,7 +7455,7 @@ mod tests { 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 +7466,7 @@ mod tests { 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 +7486,7 @@ mod tests { iter.next(), Some(&frame::Frame::MaxStreamData { stream_id: 4, - max: 22, + max: 24, }) ); } @@ -7066,6 +7790,136 @@ mod tests { } #[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::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Client sends some data. + assert_eq!(pipe.client.stream_send(4, 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(4)); + assert_eq!(r.next(), None); + + assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((5, false))); + assert!(!pipe.server.stream_finished(4)); + + let mut r = pipe.server.readable(); + assert_eq!(r.next(), None); + + assert_eq!(pipe.server.stream_send(4, b"", true), Ok(0)); + assert_eq!(pipe.advance(), Ok(())); + + 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((0, true))); + assert!(pipe.client.stream_finished(4)); + + // Client sends RESET_STREAM, closing stream. + let frames = [frame::Frame::ResetStream { + stream_id: 4, + 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(4)); + assert_eq!(r.next(), None); + + assert_eq!( + pipe.server.stream_recv(4, &mut b), + Err(Error::StreamReset(42)) + ); + + assert!(pipe.server.stream_finished(4)); + + // 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 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::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + // Client sends some data. + assert_eq!(pipe.client.stream_send(4, 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(4)); + assert_eq!(r.next(), None); + + assert_eq!(pipe.server.stream_recv(4, &mut b), Ok((1, false))); + assert!(!pipe.server.stream_finished(4)); + + let mut r = pipe.server.readable(); + assert_eq!(r.next(), None); + + assert_eq!(pipe.server.stream_send(4, b"", true), Ok(0)); + assert_eq!(pipe.advance(), Ok(())); + + 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((0, true))); + assert!(pipe.client.stream_finished(4)); + + // Client sends RESET_STREAM, closing stream. + let frames = [frame::Frame::ResetStream { + stream_id: 4, + 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(4)); + assert_eq!(r.next(), None); + + assert_eq!( + pipe.server.stream_recv(4, &mut b), + Err(Error::StreamReset(42)) + ); + + assert!(pipe.server.stream_finished(4)); + + // 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]; @@ -7100,15 +7954,41 @@ mod tests { } #[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::default().unwrap(); + assert_eq!(pipe.handshake(), Ok(())); + + let frames = [ + 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: 16, // Past stream's flow control limit. + }, + ]; + + let pkt_type = packet::Type::Short; + assert_eq!( + pipe.send_pkt_to_server(pkt_type, &frames, &mut buf), + Err(Error::FlowControl), + ); + } + + #[test] fn path_challenge() { let mut buf = [0; 65535]; let mut pipe = testing::Pipe::default().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 +8007,7 @@ mod tests { assert_eq!( iter.next(), - Some(&frame::Frame::PathResponse { - data: vec![0xba; 8], - }) + Some(&frame::Frame::PathResponse { data: [0xba; 8] }) ); } @@ -7239,7 +8117,7 @@ mod tests { assert_eq!(r.next(), None); loop { - if pipe.server.stream_send(4, b"world", false) == Ok(0) { + if pipe.server.stream_send(4, b"world", false) == Err(Error::Done) { break; } @@ -7297,6 +8175,7 @@ mod tests { 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)); @@ -7353,11 +8232,15 @@ mod tests { 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,6 +8277,82 @@ mod tests { } #[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"\x06proto1\x06proto2") + .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) + ); + assert_eq!( + pipe.server.stream_send(8, 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]; @@ -7544,6 +8503,7 @@ mod tests { // 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 +8549,18 @@ mod tests { 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,6 +8570,77 @@ mod tests { } #[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"\x06proto1\x06proto2") + .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) + ); + assert_eq!( + pipe.server.stream_send(8, 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() { @@ -7772,7 +8813,7 @@ mod tests { assert_eq!(w.next(), Some(4)); assert_eq!(w.next(), None); - // Server suts down stream. + // Server shuts down stream. assert_eq!(pipe.server.stream_shutdown(4, Shutdown::Write, 0), Ok(())); let mut w = pipe.server.writable(); @@ -7819,7 +8860,7 @@ mod tests { 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(); @@ -7945,8 +8986,7 @@ mod tests { 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 +8996,7 @@ mod tests { pn_len, payload_len, payload_offset, + None, aead, ) .unwrap(); @@ -8088,7 +9129,7 @@ mod tests { } #[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 @@ -8256,6 +9297,25 @@ mod tests { } #[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::default().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]; @@ -8645,9 +9705,57 @@ mod tests { 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::default().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,13 +9770,15 @@ mod tests { 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] @@ -9416,6 +10526,7 @@ mod tests { data: stream::RangeBuf::from(b"b", 0, false), }) ); + assert_eq!(pipe.client.stats().retrans, 1); } #[test] @@ -10076,6 +11187,25 @@ mod tests { } #[test] + fn local_error() { + let mut pipe = testing::Pipe::default().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[..]); @@ -10189,11 +11319,140 @@ mod tests { 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"\x06proto1\x06proto2")?; + 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, + &mut client_config, + )?, + server: accept(&server_scid, None, server_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"\x06proto1\x06proto2") + .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(); + } } pub use crate::packet::ConnectionId; @@ -10208,10 +11467,10 @@ mod crypto; mod dgram; #[cfg(feature = "ffi")] mod ffi; +mod flowcontrol; mod frame; pub mod h3; mod minmax; -mod octets; mod packet; mod rand; mod ranges; diff --git a/src/minmax.rs b/src/minmax.rs index a8a23fd..8d81c28 100644 --- a/src/minmax.rs +++ b/src/minmax.rs @@ -173,7 +173,7 @@ mod tests { #[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 @@ mod tests { #[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 @@ mod tests { #[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); @@ -307,7 +307,7 @@ mod tests { #[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 @@ mod tests { #[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); 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..cc06031 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -32,7 +32,6 @@ use crate::Error; use crate::Result; use crate::crypto; -use crate::octets; use crate::rand; use crate::ranges; use crate::stream; @@ -112,19 +111,20 @@ impl Type { } #[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, } } } @@ -649,17 +649,21 @@ pub fn encrypt_hdr( 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 +695,9 @@ pub fn negotiate_version( 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)?; @@ -858,13 +862,19 @@ impl PktNumSpace { std::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( + std::u64::MAX, + std::u64::MAX, + true, + true, + stream::MAX_STREAM_WINDOW, + ); self.ack_elicited = false; } @@ -934,7 +944,6 @@ mod tests { use super::*; use crate::crypto; - use crate::octets; #[test] fn retry() { @@ -1848,17 +1857,22 @@ mod tests { 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[..]); @@ -2744,17 +2758,22 @@ mod tests { 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 +2810,37 @@ mod tests { 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/ranges.rs b/src/ranges.rs index c390873..b10eb35 100644 --- a/src/ranges.rs +++ b/src/ranges.rs @@ -108,7 +108,6 @@ impl RangeSet { } pub fn push_item(&mut self, item: u64) { - #[allow(clippy::range_plus_one)] self.insert(item..item + 1); } diff --git a/src/recovery/cubic.rs b/src/recovery/cubic.rs index 8b091c4..090a8f4 100644 --- a/src/recovery/cubic.rs +++ b/src/recovery/cubic.rs @@ -45,13 +45,15 @@ use crate::recovery::CongestionControlOps; use crate::recovery::Recovery; pub static CUBIC: CongestionControlOps = CongestionControlOps { + on_init, on_packet_sent, - on_packet_acked, + on_packets_acked, congestion_event, collapse_cwnd, checkpoint, rollback, has_custom_pacing, + debug_fmt, }; /// CUBIC Constants. @@ -61,9 +63,12 @@ const BETA_CUBIC: f64 = 0.7; 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 +108,6 @@ struct PriorState { w_max: f64, - w_last_max: f64, - k: f64, epoch_start: Option<Instant>, @@ -142,6 +145,8 @@ impl State { } } +fn on_init(_r: &mut Recovery) {} + fn collapse_cwnd(r: &mut Recovery) { let cubic = &mut r.cubic_state; @@ -186,6 +191,14 @@ fn on_packet_sent(r: &mut Recovery, sent_bytes: usize, now: Instant) { 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 +207,13 @@ fn on_packet_acked( 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 +225,21 @@ fn on_packet_acked( // <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 +249,29 @@ fn on_packet_acked( 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 +297,7 @@ fn on_packet_acked( } } - 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 +331,6 @@ fn on_packet_acked( 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 +342,8 @@ fn on_packet_acked( } 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 +380,11 @@ fn congestion_event( 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,18 +397,37 @@ fn checkpoint(r: &mut Recovery) { 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::*; @@ -442,7 +475,7 @@ mod tests { 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,6 +491,11 @@ mod tests { 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); @@ -485,7 +523,7 @@ mod tests { 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,16 +540,31 @@ mod tests { 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, }, ]; @@ -530,7 +583,12 @@ mod tests { 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 +610,12 @@ mod tests { } // 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 +630,7 @@ mod tests { 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,6 +639,11 @@ mod tests { 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); @@ -597,7 +665,12 @@ mod tests { 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,6 +684,11 @@ mod tests { // 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); @@ -623,14 +701,13 @@ mod tests { } #[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 p = recovery::Sent { @@ -644,99 +721,272 @@ mod tests { 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; } + r.hystart.start_round(send_pn - 1); + // Receving Acks. - let now = now + Duration::from_millis(rtt_1st); + 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. + // 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); + + // Receving 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; + } + + // 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: vec![], + 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); + + // Receving 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; } - // During LSS cwnd will be increased less than usual slow start. + // 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); + + // Receving 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); + + // Receving 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 +1004,12 @@ mod tests { } // 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,6 +1022,11 @@ mod tests { // 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 @@ -779,7 +1039,49 @@ mod tests { 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 sprurious 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 +1100,12 @@ mod tests { } // 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 +1119,7 @@ mod tests { 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,6 +1128,11 @@ mod tests { 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); @@ -834,7 +1146,12 @@ mod tests { // 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..dcc3e48 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,164 @@ //! 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, + + first_sent_time: Instant, - recent_delivered_packet_sent_time: Option<Instant>, + // Packet number of the last sent packet with app limited. + end_of_app_limited: u64, - app_limited_at_pkt: usize, + // 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 Rate { - pub fn on_packet_sent(&mut self, pkt: &mut Sent, now: Instant) { - if self.delivered_time.is_none() { - self.delivered_time = Some(now); +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(), } + } +} - if self.recent_delivered_packet_sent_time.is_none() { - self.recent_delivered_packet_sent_time = Some(now); +impl Rate { + pub fn on_packet_sent( + &mut self, pkt: &mut Sent, bytes_in_flight: usize, now: Instant, + ) { + // No packets in flight yet? + if bytes_in_flight == 0 { + self.first_sent_time = now; + self.delivered_time = 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.recent_delivered_packet_sent_time = - self.recent_delivered_packet_sent_time.unwrap(); + pkt.is_app_limited = self.app_limited(); - 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; } - } - 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; - } + self.largest_acked = self.largest_acked.max(pkt.pkt_num); + } - 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, + 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.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; + 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 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 update_app_limited(&mut self, v: bool) { + self.end_of_app_limited = if v { self.last_sent_packet.max(1) } else { 0 } } - pub fn delivery_rate(&self) -> u64 { - self.rate_sample.delivery_rate + pub fn app_limited(&mut self) -> bool { + self.end_of_app_limited != 0 } -} - -impl std::fmt::Debug for Rate { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "delivered={:?} ", self.delivered)?; - if let Some(t) = self.delivered_time { - write!(f, "delivered_time={:?} ", t.elapsed())?; - } + pub fn _delivered(&self) -> usize { + self.delivered + } - if let Some(t) = self.recent_delivered_packet_sent_time { - write!(f, "recent_delivered_packet_sent_time={:?} ", t.elapsed())?; - } + pub fn sample_delivery_rate(&self) -> u64 { + self.rate_sample.delivery_rate + } - write!(f, "app_limited_at_pkt={:?} ", self.app_limited_at_pkt)?; + pub fn _sample_rtt(&self) -> Duration { + self.rate_sample.rtt + } - 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 +196,8 @@ struct RateSample { 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)] @@ -186,114 +209,163 @@ mod tests { #[test] fn rate_check() { let config = Config::new(0xbabababa).unwrap(); - let mut recovery = 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, - }; - - 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()); - - 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, - }; - - 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(); - - assert!(recovery.delivery_rate() > 0); + let mut r = Recovery::new(&config); + + let now = Instant::now(); + let mss = r.max_datagram_size(); + + // Send 2 packets. + for pn in 0..2 { + let pkt = Sent { + pkt_num: pn, + frames: vec![], + 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; + + // 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 - rtt, + 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: vec![], + 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 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, - }; - - 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()); - - 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, - }; - - 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(); - - assert_eq!(recvry.delivery_rate.app_limited_at_pkt, 0); + let mut r = Recovery::new(&config); + + let now = Instant::now(); + let mss = r.max_datagram_size(); + + // Send 5 packets. + for pn in 0..5 { + let pkt = Sent { + pkt_num: pn, + frames: vec![], + 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 mut acked = ranges::RangeSet::default(); + acked.insert(0..5); + + assert_eq!( + r.on_ack_received( + &acked, + 25, + packet::EPOCH_APPLICATION, + HandshakeStatus::default(), + now, + "", + ), + Ok(()), + ); + + 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..2285951 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::packet; 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: Duration, - current_round_min_rtt: Option<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 @@ impl std::fmt::Debug for Hystart { 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 @@ impl Hystart { 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() + 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, - - window_end: Some(pkt_num), + self.window_end = Some(pkt_num); - last_round_min_rtt: self.current_round_min_rtt, + self.last_round_min_rtt = self.current_round_min_rtt; - current_round_min_rtt: None, + self.current_round_min_rtt = Duration::MAX; - 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; + + 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; } } + } - // 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; + // 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; + } } } } - self.lss_start_time.is_some() + 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 @@ mod tests { 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..7b9bce3 100644 --- a/src/recovery/mod.rs +++ b/src/recovery/mod.rs @@ -42,10 +42,15 @@ use crate::minmax; use crate::packet; use crate::ranges; +#[cfg(feature = "qlog")] +use qlog::events::EventData; + // Loss Recovery -const PACKET_THRESHOLD: u64 = 3; +const INITIAL_PACKET_THRESHOLD: u64 = 3; + +const MAX_PACKET_THRESHOLD: u64 = 20; -const TIME_THRESHOLD: f64 = 9.0 / 8.0; +const INITIAL_TIME_THRESHOLD: f64 = 9.0 / 8.0; const GRANULARITY: Duration = Duration::from_millis(1); @@ -64,6 +69,8 @@ const MINIMUM_WINDOW_PACKETS: usize = 2; const LOSS_REDUCTION_FACTOR: f64 = 0.5; +const PACING_MULTIPLIER: f64 = 1.25; + pub struct Recovery { loss_detection_timer: Option<Instant>, @@ -97,6 +104,8 @@ pub struct Recovery { pub lost_count: usize, + pub lost_spurious_count: usize, + pub loss_probes: [usize; packet::EPOCH_COUNT], in_flight_count: [usize; packet::EPOCH_COUNT], @@ -105,6 +114,10 @@ pub struct Recovery { delivery_rate: delivery_rate::Rate, + pkt_thresh: u64, + + time_thresh: f64, + // Congestion control. cc_ops: &'static CongestionControlOps, @@ -120,6 +133,8 @@ pub struct Recovery { bytes_sent: usize, + pub bytes_lost: u64, + congestion_recovery_start_time: Option<Instant>, max_datagram_size: usize, @@ -132,7 +147,17 @@ pub struct Recovery { // Pacing. pacing_rate: u64, - last_packet_scheduled_time: Option<Instant>, + last_packet_scheduled_time: 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, } impl Recovery { @@ -148,7 +173,7 @@ impl Recovery { 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,13 +181,13 @@ impl Recovery { // 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: Duration::ZERO, loss_time: [None; packet::EPOCH_COUNT], @@ -173,6 +198,7 @@ impl Recovery { acked: [Vec::new(), Vec::new(), Vec::new()], lost_count: 0, + lost_spurious_count: 0, loss_probes: [0; packet::EPOCH_COUNT], @@ -181,6 +207,10 @@ impl Recovery { congestion_window: config.max_send_udp_payload_size * INITIAL_WINDOW_PACKETS, + pkt_thresh: INITIAL_PACKET_THRESHOLD, + + time_thresh: INITIAL_TIME_THRESHOLD, + bytes_in_flight: 0, ssthresh: std::usize::MAX, @@ -191,6 +221,8 @@ impl Recovery { bytes_sent: 0, + bytes_lost: 0, + congestion_recovery_start_time: None, max_datagram_size: config.max_send_udp_payload_size, @@ -207,10 +239,22 @@ impl Recovery { pacing_rate: 0, - last_packet_scheduled_time: None, + last_packet_scheduled_time: Instant::now(), + + prr: prr::PRR::default(), + + send_quantum: config.max_send_udp_payload_size * + INITIAL_WINDOW_PACKETS, + + #[cfg(feature = "qlog")] + qlog_metrics: QlogMetrics::default(), } } + pub fn on_init(&mut self) { + (self.cc_ops.on_init)(self); + } + pub fn on_packet_sent( &mut self, mut pkt: Sent, epoch: packet::Epoch, handshake_status: HandshakeStatus, now: Instant, trace_id: &str, @@ -220,12 +264,11 @@ impl Recovery { let sent_bytes = pkt.size; let pkt_num = pkt.pkt_num; - self.delivery_rate.on_packet_sent(&mut pkt, now); - self.largest_sent_pkt[epoch] = cmp::max(self.largest_sent_pkt[epoch], pkt_num); - self.sent[epoch].push_back(pkt); + self.delivery_rate + .on_packet_sent(&mut pkt, self.bytes_in_flight, now); if in_flight { if ack_eliciting { @@ -234,11 +277,14 @@ impl Recovery { 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); } @@ -253,14 +299,18 @@ impl Recovery { // 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); } } self.schedule_next_packet(epoch, now, sent_bytes); + pkt.time_sent = self.get_packet_send_time(); + + self.sent[epoch].push_back(pkt); + self.bytes_sent += sent_bytes; trace!("{} {:?}", trace_id, self); } @@ -275,7 +325,7 @@ impl Recovery { } } - pub fn get_packet_send_time(&self) -> Option<Instant> { + pub fn get_packet_send_time(&self) -> Instant { self.last_packet_scheduled_time } @@ -291,21 +341,17 @@ impl Recovery { self.bytes_sent <= self.congestion_window || self.pacing_rate == 0 { - self.last_packet_scheduled_time = Some(now); + self.last_packet_scheduled_time = + cmp::max(self.last_packet_scheduled_time, now); + return; } - 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 interval = packet_size as f64 / self.pacing_rate as f64; + let interval = Duration::from_secs_f64(interval); + let next_schedule_time = self.last_packet_scheduled_time + interval; - None => Some(now), - }; + self.last_packet_scheduled_time = cmp::max(now, next_schedule_time); } pub fn on_ack_received( @@ -340,24 +386,57 @@ impl Recovery { 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; } @@ -370,8 +449,6 @@ impl Recovery { 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,20 +457,36 @@ impl Recovery { 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(()); } 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 { Duration::from_micros(ack_delay) @@ -414,7 +507,7 @@ impl Recovery { self.set_loss_detection_timer(handshake_status, now); - self.drain_packets(epoch); + self.drain_packets(epoch, now); Ok(()) } @@ -521,7 +614,9 @@ impl Recovery { return std::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 { @@ -533,7 +628,7 @@ impl Recovery { } pub fn delivery_rate(&self) -> u64 { - self.delivery_rate.delivery_rate() + self.delivery_rate.sample_delivery_rate() } pub fn max_datagram_size(&self) -> usize { @@ -597,7 +692,6 @@ impl Recovery { 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 { let new_time = self.loss_time[e]; @@ -684,7 +778,7 @@ impl Recovery { 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); @@ -706,7 +800,7 @@ impl Recovery { 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); @@ -743,14 +837,16 @@ impl Recovery { } } + 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); } - 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 +860,13 @@ impl Recovery { // 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 +880,16 @@ impl Recovery { 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 +914,7 @@ impl Recovery { ) { 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,25 +922,20 @@ impl Recovery { } 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; } @@ -839,23 +944,27 @@ impl Recovery { 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, + }; + + self.qlog_metrics.maybe_update(qlog_metrics) + } + + pub fn send_quantum(&self) -> usize { + self.send_quantum } } @@ -889,13 +998,20 @@ impl FromStr for CongestionControlAlgorithm { } pub struct CongestionControlOps { + pub on_init: 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 +1021,12 @@ pub struct CongestionControlOps { 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 { @@ -965,6 +1084,9 @@ impl std::fmt::Debug for Recovery { write!(f, "hystart={:?} ", self.hystart)?; } + // CC-specific debug info + (self.cc_ops.debug_fmt)(self, f)?; + Ok(()) } } @@ -991,7 +1113,7 @@ pub struct Sent { pub delivered_time: Instant, - pub recent_delivered_packet_sent_time: Instant, + pub first_sent_time: Instant, pub is_app_limited: bool, @@ -1005,11 +1127,7 @@ impl std::fmt::Debug for 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, "first_sent_time={:?} ", self.first_sent_time.elapsed())?; write!(f, "is_app_limited={} ", self.is_app_limited)?; write!(f, "has_data={} ", self.has_data)?; @@ -1024,6 +1142,16 @@ pub struct Acked { 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,6 +1184,111 @@ fn sub_abs(lhs: Duration, rhs: Duration) -> Duration { } } +// 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, +} + +#[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 + }; + + 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: None, + }, + )); + } + + None + } +} + #[cfg(test)] mod tests { use super::*; @@ -1109,7 +1342,7 @@ mod tests { 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, }; @@ -1135,7 +1368,7 @@ mod tests { 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, }; @@ -1161,7 +1394,7 @@ mod tests { 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, }; @@ -1187,7 +1420,7 @@ mod tests { 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, }; @@ -1245,7 +1478,7 @@ mod tests { 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, }; @@ -1271,7 +1504,7 @@ mod tests { 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, }; @@ -1306,10 +1539,17 @@ mod tests { Ok(()) ); - 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] @@ -1335,7 +1575,7 @@ mod tests { 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, }; @@ -1361,7 +1601,7 @@ mod tests { 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, }; @@ -1387,7 +1627,7 @@ mod tests { 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, }; @@ -1413,7 +1653,7 @@ mod tests { 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, }; @@ -1459,10 +1699,17 @@ mod tests { r.on_loss_detection_timeout(HandshakeStatus::default(), now, ""); 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] @@ -1488,7 +1735,7 @@ mod tests { 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, }; @@ -1514,7 +1761,7 @@ mod tests { 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, }; @@ -1540,7 +1787,7 @@ mod tests { 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, }; @@ -1566,7 +1813,7 @@ mod tests { 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, }; @@ -1605,6 +1852,8 @@ mod tests { 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, @@ -1617,11 +1866,22 @@ mod tests { Ok(()) ); - 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] @@ -1647,7 +1907,7 @@ mod tests { 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, }; @@ -1665,7 +1925,7 @@ mod tests { // First packet will be sent out immidiately. assert_eq!(r.pacing_rate, 0); - assert_eq!(r.get_packet_send_time().unwrap(), now); + assert_eq!(r.get_packet_send_time(), now); // Wait 50ms for ACK. now += Duration::from_millis(50); @@ -1701,7 +1961,7 @@ mod tests { 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, }; @@ -1718,7 +1978,7 @@ mod tests { assert_eq!(r.bytes_in_flight, 6500); // Pacing is not done during intial phase of connection. - assert_eq!(r.get_packet_send_time().unwrap(), now); + assert_eq!(r.get_packet_send_time(), now); // Send the third packet out. let p = Sent { @@ -1732,7 +1992,7 @@ mod tests { 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, }; @@ -1751,12 +2011,11 @@ mod tests { // 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 = (12000.0 * PACING_MULTIPLIER / 0.05) as u64; + assert_eq!(r.pacing_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(6500.0 / pacing_rate as f64) ); } } @@ -1764,4 +2023,5 @@ mod tests { mod cubic; mod delivery_rate; mod hystart; +mod prr; mod reno; 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..eb8942b 100644 --- a/src/recovery/reno.rs +++ b/src/recovery/reno.rs @@ -39,19 +39,31 @@ use crate::recovery::CongestionControlOps; use crate::recovery::Recovery; pub static RENO: CongestionControlOps = CongestionControlOps { + on_init, 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 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 +82,30 @@ fn on_packet_acked( // 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 +126,7 @@ fn congestion_event( r.ssthresh = r.congestion_window; - if r.hystart.in_lss(epoch) { + if r.hystart.in_css(epoch) { r.hystart.congestion_event(); } } @@ -147,16 +136,26 @@ pub fn collapse_cwnd(r: &mut Recovery) { 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::*; @@ -208,7 +207,7 @@ mod tests { 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,6 +223,11 @@ mod tests { 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); @@ -252,7 +256,7 @@ mod tests { 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,16 +273,31 @@ mod tests { 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, }, ]; @@ -299,7 +318,12 @@ mod tests { 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 +342,12 @@ mod tests { 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,6 +362,11 @@ mod tests { 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 diff --git a/src/stream.rs b/src/stream.rs index dfb9b0d..80e174f 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -36,9 +36,12 @@ use std::collections::HashMap; use std::collections::HashSet; use std::collections::VecDeque; +use std::time; + use crate::Error; use crate::Result; +use crate::flowcontrol; use crate::ranges; const DEFAULT_URGENCY: u8 = 127; @@ -49,18 +52,57 @@ const SEND_BUFFER_SIZE: usize = 5; #[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 +146,43 @@ pub struct StreamMap { /// 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>, + 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>, + 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 +190,8 @@ impl StreamMap { local_max_streams_uni: max_streams_uni, local_max_streams_uni_next: max_streams_uni, + max_stream_window, + ..StreamMap::default() } } @@ -248,7 +297,13 @@ impl StreamMap { }, }; - let s = Stream::new(max_rx_data, max_tx_data, is_bidi(id), local); + let s = Stream::new( + max_rx_data, + max_tx_data, + is_bidi(id), + local, + self.max_stream_window, + ); v.insert(s) }, @@ -417,6 +472,11 @@ impl StreamMap { 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 @@ -495,6 +555,11 @@ impl StreamMap { 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() @@ -578,9 +643,10 @@ impl Stream { /// 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), bidi, local, @@ -653,7 +719,7 @@ pub struct StreamIter { impl StreamIter { #[inline] - fn from(streams: &HashSet<u64>) -> Self { + fn from(streams: &StreamIdHashSet) -> Self { StreamIter { streams: streams.iter().copied().collect(), } @@ -693,25 +759,28 @@ pub struct RecvBuf { /// 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 +791,7 @@ impl RecvBuf { /// 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); } @@ -777,34 +846,43 @@ impl RecvBuf { } } - let mut tmp_buf = Some(buf); - - while let Some(mut buf) = tmp_buf { - tmp_buf = None; + let mut tmp_bufs = VecDeque::with_capacity(2); + tmp_bufs.push_back(buf); + '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(()); - } - - // 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); - } - - // 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)); + // 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 { + // New buffer is fully contained in existing buffer. + if buf.off() >= b.off() && buf.max_off() <= b.max_off() { + continue 'tmp; + } + + // 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); + } + + // New buffer's end overlaps existing buffer. + if buf.off() < b.off() && buf.max_off() > b.off() { + tmp_bufs.push_back( + buf.split_off((b.off() - buf.off()) as usize), + ); + } } } @@ -835,6 +913,11 @@ impl RecvBuf { return Err(Error::Done); } + // 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 buf = match self.data.peek_mut() { Some(v) => v, @@ -861,13 +944,14 @@ impl RecvBuf { std::collections::binary_heap::PeekMut::pop(buf); } - 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 +964,52 @@ impl RecvBuf { 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 +1028,13 @@ impl RecvBuf { } /// 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. @@ -982,6 +1092,9 @@ pub struct SendBuf { /// 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,6 +1210,7 @@ impl SendBuf { } 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; @@ -1109,15 +1223,13 @@ impl SendBuf { 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; } @@ -1137,6 +1249,16 @@ impl SendBuf { 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); @@ -1235,8 +1357,11 @@ impl SendBuf { } /// 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) -> Result<(u64, u64)> { + let unsent_off = self.off_front(); + let unsent_len = self.off_back() - unsent_off; + + self.fin_off = Some(unsent_off); // Drop all buffered data. self.data.clear(); @@ -1246,27 +1371,28 @@ impl SendBuf { self.pos = 0; self.len = 0; + self.off = unsent_off; - Ok(self.fin_off.unwrap()) + Ok((self.fin_off.unwrap(), 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 (fin_off, unsent) = self.reset()?; self.error = Some(error_code); - Ok(fin_off) + Ok((fin_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); } @@ -1277,7 +1403,6 @@ impl SendBuf { } /// Returns the largest offset of data buffered. - #[allow(dead_code)] pub fn off_back(&self) -> u64 { self.off } @@ -1305,7 +1430,7 @@ impl SendBuf { /// 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 +1504,7 @@ impl SendBuf { 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 +1570,12 @@ impl RangeBuf { /// 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 +1627,7 @@ mod tests { #[test] fn empty_read() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1512,7 +1637,7 @@ mod tests { #[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 +1693,7 @@ mod tests { #[test] fn ordered_read() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1605,7 +1730,7 @@ mod tests { #[test] fn split_read() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1645,7 +1770,7 @@ mod tests { #[test] fn incomplete_read() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1673,7 +1798,7 @@ mod tests { #[test] fn zero_len_read() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1701,7 +1826,7 @@ mod tests { #[test] fn past_read() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1740,7 +1865,7 @@ mod tests { #[test] fn fully_overlapping_read() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1771,7 +1896,7 @@ mod tests { #[test] fn fully_overlapping_read2() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1802,7 +1927,7 @@ mod tests { #[test] fn fully_overlapping_read3() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1833,7 +1958,7 @@ mod tests { #[test] fn fully_overlapping_read_multi() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1870,7 +1995,7 @@ mod tests { #[test] fn overlapping_start_read() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1900,7 +2025,7 @@ mod tests { #[test] fn overlapping_end_read() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1929,8 +2054,92 @@ mod tests { } #[test] + fn overlapping_end_twice_read() { + let mut recv = RecvBuf::new(std::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(std::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(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -1967,7 +2176,7 @@ mod tests { #[test] fn partially_multi_overlapping_reordered_read2() { - let mut recv = RecvBuf::new(std::u64::MAX); + let mut recv = RecvBuf::new(std::u64::MAX, DEFAULT_STREAM_WINDOW); assert_eq!(recv.len, 0); let mut buf = [0; 32]; @@ -2257,7 +2466,7 @@ mod tests { #[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 +2487,7 @@ mod tests { 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 +2497,7 @@ mod tests { #[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 +2509,7 @@ mod tests { #[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 +2527,7 @@ mod tests { #[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 +2539,7 @@ mod tests { #[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 +2551,7 @@ mod tests { #[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 +2571,55 @@ mod tests { #[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 +2662,7 @@ mod tests { #[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 +2678,7 @@ mod tests { #[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 +2689,7 @@ mod tests { #[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 +2704,7 @@ mod tests { 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 +2720,7 @@ mod tests { 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 +2736,7 @@ mod tests { 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 +2766,7 @@ mod tests { 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 +2803,7 @@ mod tests { #[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 +2825,7 @@ mod tests { #[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 +2868,7 @@ mod tests { 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 +2893,7 @@ mod tests { 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 +2945,7 @@ mod tests { 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 +3012,7 @@ mod tests { 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)); @@ -43,7 +43,6 @@ use crate::Connection; use crate::ConnectionError; use crate::crypto; -use crate::octets; use crate::packet; const TLS1_3_VERSION: u16 = 0x0304; @@ -124,7 +123,8 @@ struct SSL_QUIC_METHOD { } 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,9 +154,19 @@ impl Context { } } + #[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()); + let ssl = SSL_new(self.as_mut_ptr()); Ok(Handshake(ssl)) } } @@ -165,7 +175,7 @@ impl Context { 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 +188,7 @@ impl Context { 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 +198,20 @@ impl Context { 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 +226,7 @@ impl Context { 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,11 +268,11 @@ impl Context { // 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); }; } @@ -274,13 +284,13 @@ impl Context { }; 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); } } @@ -295,7 +305,7 @@ impl Context { // Configure ALPN for servers. unsafe { SSL_CTX_set_alpn_select_cb( - self.as_ptr(), + self.as_mut_ptr(), select_alpn, ptr::null_mut(), ); @@ -303,13 +313,21 @@ impl Context { // 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(), + ) }) } @@ -317,20 +335,26 @@ impl Context { let enabled = if enabled { 1 } else { 0 }; 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()) } } } @@ -347,10 +371,8 @@ impl Handshake { 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,80 +388,90 @@ impl Handshake { 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(), if mode { 1 } else { 0 }) + } } - 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()) }; + map_result_ssl(self, 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()) - }) + 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(), + ) + }; + map_result_ssl(self, rc) } #[cfg(test)] pub fn set_options(&mut self, opts: u32) { unsafe { - SSL_set_options(self.as_ptr(), opts); + SSL_set_options(self.as_mut_ptr(), opts); } } @@ -473,7 +505,24 @@ impl Handshake { 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 +537,45 @@ impl Handshake { 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<()> { + let rc = unsafe { + SSL_provide_quic_data( + self.as_mut_ptr(), + level, + buf.as_ptr(), + buf.len(), + ) + }; + map_result_ssl(self, 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())?; + + map_result_ssl(self, 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<()> { + 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())?; + + map_result_ssl(self, 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 +597,7 @@ impl Handshake { } 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 +615,7 @@ impl Handshake { } 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 +625,7 @@ impl Handshake { Some(sigalg.to_string()) } - pub fn peer_cert(&self) -> Option<Vec<u8>> { + 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,14 +636,14 @@ impl Handshake { 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 as usize) }; Some(peer_cert) @@ -599,22 +662,49 @@ impl Handshake { } 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()) }; + map_result_ssl(self, rc) + } + + fn as_ptr(&self) -> *const SSL { + self.0 } - fn as_ptr(&self) -> *mut SSL { + fn as_mut_ptr(&mut self) -> *mut SSL { self.0 } } +// 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 +729,24 @@ extern fn set_read_secret( 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 +756,10 @@ extern fn set_read_secret( }; // 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 +780,24 @@ extern fn set_write_secret( 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 +807,10 @@ extern fn set_write_secret( }; // 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 +825,16 @@ extern fn set_write_secret( 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 +842,13 @@ extern fn add_handshake_data( 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 +866,22 @@ extern fn flush_flight(_ssl: *mut SSL) -> c_int { } 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 +891,14 @@ extern fn send_alert(ssl: *mut SSL, level: crypto::Level, alert: u8) -> c_int { } 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 +913,14 @@ extern fn select_alpn( 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 +929,7 @@ extern fn select_alpn( }); 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,14 +958,13 @@ extern fn select_alpn( 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 peer_params = handshake.quic_transport_params(); @@ -914,12 +1006,12 @@ extern fn new_session(ssl: *mut SSL, session: *mut SSL_SESSION) -> c_int { 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,7 +1040,7 @@ fn map_result_ptr<'a, T>(bssl_result: *const T) -> Result<&'a T> { } } -fn map_result_ssl(ssl: &Handshake, bssl_result: c_int) -> Result<()> { +fn map_result_ssl(ssl: &mut Handshake, bssl_result: c_int) -> Result<()> { match bssl_result { 1 => Ok(()), @@ -1082,7 +1174,7 @@ extern { 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 +1184,21 @@ extern { 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); @@ -1133,13 +1225,15 @@ extern { ) -> c_int; 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 +1244,13 @@ extern { 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; |