diff options
Diffstat (limited to 'vendor/rustls')
117 files changed, 49352 insertions, 0 deletions
diff --git a/vendor/rustls/.cargo-checksum.json b/vendor/rustls/.cargo-checksum.json new file mode 100644 index 00000000..441dc8f0 --- /dev/null +++ b/vendor/rustls/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"16463c44989ca7b3256ddb327730574dd03fa25ba4c117e92c26884a00f23ec9","Cargo.toml":"46f1cf9ca064bd16baf3416b8efff9a0d8f6e88935495dbd21cddf4c798f142a","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-ISC":"7cfafc877eccc46c0e346ccbaa5c51bb6b894d2b818e617d970211e232785ad4","LICENSE-MIT":"709e3175b4212f7b13aa93971c9f62ff8c69ec45ad8c6532a7e0c41d7a7d6f8c","README.md":"1f3cec2c07d17bf1c3860fa6a692953fd456315134a1abe79ae4cc0c8cc1ca35","benches/benchmarks.rs":"251ec5926d31af7087308224ac3bcf2a58b3fb1965083da1c75779957e30cedf","build.rs":"380b9a051325baa7d4957bd9a4f1a637c27a663610b1b502f9524530f6995f4d","examples/internal/test_ca.rs":"bcb952d082cc46e53e8ef0ac547cda0cd990e7d36f0e9512448aa5ef4dd5e056","src/bs_debug.rs":"4e4ff4ee2d1db612ba7367e88ee587d525a973167c87b28bb2b35555a9eda27b","src/builder.rs":"3cfebc1faeb5e2b7fbb8ede68aca1240c7ba69061f3381a1a4ded87f11bf5d6a","src/check.rs":"8e5b8a35aebe931c60afa009159a398cf81f300c21898d2002fec88b42d13ad7","src/client/builder.rs":"dedb310e92f9bf837f2500bb9bc8bb3f02975477682232bc91801fe891a2048f","src/client/client_conn.rs":"beb8e4d6720370ab6696f082aede044e62e47e69246b044f5d1101691149b4ef","src/client/common.rs":"c745f89b8fd2381118dda319ab69c71a410212d879d380fb8205aa74ece52b48","src/client/ech.rs":"622eeccb85ea350b01e597821bc92fee8939f87c30c028cf043133a663cb9d89","src/client/handy.rs":"7137eeea1cbd8df5a8620e5934a2c3a6889d992caf45c5b13f33072c46ddefb1","src/client/hs.rs":"0b48abf24f0fc6093fe17b628a3d3989f6f35018e322d63d0b90c2066319d2a9","src/client/test.rs":"ca72f595d0f2c9ee713bf68ad647bbfaf402bfa19d7a6e610e98f57464b34221","src/client/tls12.rs":"39b2fd1c88d5b764cfc7f183f6dda7dcf38cf7384a17adfa3e26a5a5ff2d325d","src/client/tls13.rs":"e5db6db56ca31dbe270a4bfe7cf7c99929d05babb3760484baef8c9e1acad35e","src/common_state.rs":"d60d2790e97aee52667e8b9f18d25f3b6066cbcc6bed2e6153d2932525b6d0d5","src/compress.rs":"b3d5d65cd16f8ae98d0398697a4d86fbfb9a5a0db8243b553ea1d763992b5544","src/conn.rs":"72fad622038e07436da895faf2eaadc5a9b5e4fd2a1ac4d68d73ef47959da37c","src/conn/kernel.rs":"ecdbb41cd6e4aa95c33a16584723befe6b310754452174cee9ab201917ffb87e","src/conn/unbuffered.rs":"843c2eaa532cdce077cb6f33972d9da5149de0558d4aab86dddb7f5c59aa9d92","src/crypto/aws_lc_rs/hpke.rs":"9786831e6b839d52b91b5ba1b1bfc77cb1c859489e19ccfa7f84940dfc5c706e","src/crypto/aws_lc_rs/mod.rs":"e566e21c3a8f7aac1eba68ca195f51dea16127f79eb37b9f883eec1d2df7d32f","src/crypto/aws_lc_rs/pq/hybrid.rs":"a1ac53e228fe7bb6851b3a73b1ba4aa64f09bdbc77a09053f59ae89c8ea152b8","src/crypto/aws_lc_rs/pq/mlkem.rs":"c6b18343b1fb337e95994fb5d3ea473785bd681d383a215e81a232d46f6d5ef5","src/crypto/aws_lc_rs/pq/mod.rs":"4848bbd5780a2061c0bd0837312061a8bef131a1db1af8c025eb0195dba8a866","src/crypto/aws_lc_rs/sign.rs":"972f4deb09c5b64091bc9e16bd8062e39aceffb94bad5cc77ab58af066e35543","src/crypto/aws_lc_rs/ticketer.rs":"a16431d3ac21b944e6b43a78f930e13bd82603483b85cfa823f43f2a86eb0e32","src/crypto/aws_lc_rs/tls12.rs":"9e3a9220b68ce2a340d2289d376107bf4b75bc73319d1562c2800c5c01476d28","src/crypto/aws_lc_rs/tls13.rs":"e9998aa39049aa886c34c7ffb0da3b8d74388ce823bd310d07e1cd83b3559857","src/crypto/cipher.rs":"e2551ee5086adecd749e1cedc794398299104323ec14027b66e6d17c127f5ece","src/crypto/hash.rs":"eaa21b2d8f11afb9f5e41794e3461bcc18c950c53153d1fc37dfd23e3bdc1094","src/crypto/hmac.rs":"82ac9b1da0295b0ab70adec4ce321fb87d7873f530652a8b86397655042d035b","src/crypto/hpke.rs":"b65ace9c09cc0f83888c000b369376d8b8ca8a49e016d1aa0a9691c7921fdfbc","src/crypto/mod.rs":"d375a0ddf9da06669436fde13d9593e1dd400043348d0709466220bde0b73b6b","src/crypto/ring/hash.rs":"d4b594369971c8e8dc275a85beea83b51f5134174a4b37f620e130a51266d6ea","src/crypto/ring/hmac.rs":"7df8a4c33176c4e53306c23285ed965c9e35e0aebcc6fd03f1cd68759122ae94","src/crypto/ring/kx.rs":"d385431f42e76b7aa8bd0749ea256da268239d762114b40a37bee8306b07229d","src/crypto/ring/mod.rs":"b43827457ae0248f1ec3888464d40875f80e60b2d785e38517c79dda819b7675","src/crypto/ring/quic.rs":"73ced30aa8e5cf01578ca71f5546075557123d1dd7a73deb9579e93153f35a9c","src/crypto/ring/sign.rs":"6bb88454d5751ce1262d68c670ba41a52f88cd152b75d0740743ef603a43f650","src/crypto/ring/ticketer.rs":"2ec1193097d3032fc31dda08277cfb0fcb8defc6667af09d4ff1bd16be95eba4","src/crypto/ring/tls12.rs":"a3a65ebd7cf6fe7157dd19500d35ac7a948970fc64f94909ec4cc22e889e3a44","src/crypto/ring/tls13.rs":"2d093ce74d9ad824ed15f0a18abf5d845fd701a5b8de2d7db088e706dd49ab62","src/crypto/signer.rs":"3d99f11b9472eb7b316312cf94157edeb5d21ac189ed114450219f5bd60123f9","src/crypto/tls12.rs":"b729e02f64415570ba68fdde0a17cfaa60062f4d694001658b0467672a4e626d","src/crypto/tls13.rs":"feec69f36dcc738ccf735bc69e12a767857d8b52ad9af139b2b8584990b651d3","src/enums.rs":"807d39e7720ab3be31b7064fa2a67593d7246598edf6fe012f80a363afca417c","src/error.rs":"d85c232735d313be3661d564c2858fe26ebe72b203a127c3a681ef44d3c95ffd","src/hash_hs.rs":"6a03da8c8aea2f44f162843bc803a0dfb683b4b99137e5e5f5a6dd7978b02849","src/key_log.rs":"6aeb8c45365e6d53c51246fe8773243096b8c6806274cdcc3ffd1bb82fe360e9","src/key_log_file.rs":"9e781ef5e267b56afa0e70f7aeced53e5060814c71d4175741f076d5aa55fa4d","src/lib.rs":"1e1e14d37121fc3b2f3ddce24ae51f7f911d67b93a0fc968fde0db025a941eb8","src/limited_cache.rs":"a53eb38036ab1db04fe7c561d804076690631dedb17040eaa006f1264cfed097","src/lock.rs":"a4d9befd9a53e7dfbd7fb5d30ca16bcdd54e61f3fc9b353992cd45928818d70e","src/manual/defaults.rs":"71e854184d355b71a96280d82f3e83224491300e861655916d76f44325bf626a","src/manual/features.rs":"a7fecd590287388215fa6bcc6403f425897f3d4ea4f44b72a5dee9bfa2ee8345","src/manual/fips.rs":"50881a17d61c65cd855c1295572cd9b6381cf60008555b841b5e3bfca93dc712","src/manual/howto.rs":"df03dcc9abd0e8c11c13573680d43c84fd92876e4b128fb8bd881a58e07a0ae2","src/manual/implvulns.rs":"b42abb13ce142e83da012f7fa47759091c43216628abd46563de9910748efe5b","src/manual/mod.rs":"45e5d510140d668553030695a45e6ad50da2274e097e3e8fa8900446944a7576","src/manual/tlsvulns.rs":"9d2dc1d4f8f5703b67eb159d698325788863795848a246e36bef779dbba252be","src/msgs/alert.rs":"cf85febb6f1bd2719dd4767f6dea61468e2cee211f507956e93cfe79a9139afc","src/msgs/base.rs":"b6bb842fcfdf2d800e0594cb63c574663e335d4725dc870ee5fb26256f9eba64","src/msgs/ccs.rs":"61117e8e8cc2fb7b030360f959b66d6392d34a601f4ae1dc7276bb444462a742","src/msgs/codec.rs":"fa53201ef4387a453c626c37138a6a2de2d2ff13d85c0ec34f8b13ee20daeeaf","src/msgs/deframer/buffers.rs":"48bb18eff4e5859ec56aba8910488a791457afabc79feba0b09375e11bcca459","src/msgs/deframer/handshake.rs":"e7309acb70aa406dbd6c2c35c17e9d8fe036e9d24e4d29b362b37aa59bae9944","src/msgs/deframer/mod.rs":"bab59c49aa59b4d44e49928ab3aa6b58a5d167412585017bdae88cd32ce23847","src/msgs/enums.rs":"2366725846e1daa22481cfe5351d68199f833e88eb8d12095c00bc78fc77eb18","src/msgs/ffdhe_groups.rs":"22a2abdede92e082300998e0b0d49cc915a995713db2cdd811b4ff3502c1c619","src/msgs/fragmenter.rs":"c324f9ac144999ec36673736d0ef026122be9d4f6eb1f2908c01461cebe1122d","src/msgs/handshake.rs":"08219f5e36f75163f2c64e3dbf1a4506ef0d1266a42791e33863609225f0c3c8","src/msgs/handshake_test.rs":"bb73da98b08bfeea59762441c56a5aa0d9a17f30468ef5f6f87d8feb6cc1ef28","src/msgs/macros.rs":"2d4efa38989d480f8d36015d4487beedf9ec5e16aeacd3f9980d9ec61720890c","src/msgs/message/inbound.rs":"85c75ca765f5f7ce70b2f1afbb4d7a1568c1098e7be5267a16a4cb20b50b7ed5","src/msgs/message/mod.rs":"f1bdfa1ee08e31e54c85dd1c47d090afee672bd50bb6074aaa9c51d08feae3bf","src/msgs/message/outbound.rs":"fb214090aff53ea7228519feeac4444fa4b0f389c9af6dc062bf6efbe6c8a8ff","src/msgs/message_test.rs":"b3889ee3db6b8a28241fc1795209e2202f942445d8ec8ed0b08424ad6ad56dd0","src/msgs/mod.rs":"39c54a233a8bd46497c27b3e2626d8aee173d4c41ec8670386bb8c1f19249d07","src/msgs/persist.rs":"33ae762fe42981379082622bd66b2133e224cbaef012535834a4f2eeeaef9a72","src/polyfill.rs":"a6c6dcb5c42aa481099e60b85fd16d84559daa68bbef215c5cb02f93d5c65bec","src/quic.rs":"7bc4e28e1173b223b3242e8915383be0ae5476d73ad623280cd96cd00181a258","src/rand.rs":"c0c55d130516c8415836187c9050ecb5269ed7b51f65d70b40305f05f9ba57fb","src/record_layer.rs":"299c725f5bb3e032ecb17e9d1b228a1fa6fc580714c1470d5ff93c54592dc97d","src/server/builder.rs":"53cc5765fd1646c675a239f442fc212e8081369e0dfb1795aaed972d3e02a4a7","src/server/common.rs":"aef13bfc295f17218aa9b157f69c6fdfb9802ee90263acd43e880465a707bb20","src/server/handy.rs":"85ce7ea135eb5582703cb41eb8a5ec7d1d6b9266a130935d291eb71c1327394d","src/server/hs.rs":"5cfe9298ddf83d22786870460dd22b803d450ae4f0cc13e1e83c67512a24838f","src/server/server_conn.rs":"6ae7036a0ffe9c9d4c579e4aea33fa2be7ebf8056b9ad0417f31a64996559be6","src/server/test.rs":"c51e092a1e0bb8721bc66dc2a6de33ba173603f1cb51dab6b422f24c6b0d8f1c","src/server/tls12.rs":"4db5408886f09341063bde2d143f3bc29c2e4456c942fd1d16309dd4bcd3371c","src/server/tls13.rs":"a399e36edbda2f10d2b1e000d30555a4d8e23a40969d3121895bcb2f2be9758c","src/stream.rs":"00e4d9671226311ee50840a00a9e70d0921838a8a780aca6b78e4e208822d5e4","src/suites.rs":"82f697a876e4dcd720d2317d2ffd807c2ff79c081a4de3f2acf3fdf850b18980","src/test_macros.rs":"e5f98aa388375cc195cef7bae2cdba8b33c795ce48e8ff228fbfef153a493c47","src/ticketer.rs":"b722e6be43f8920ca495e4a3cbd624a4e69d61e1ad7584cfae0d8bc06429290a","src/time_provider.rs":"cdc75369bb353ca1edfb1d700a6467e42779c1c0cba5d5bd7d9f67f392ea92b1","src/tls12/mod.rs":"159fb6a519534986e1d1fd2326d30ffa0422650d9fe636c3dac8a5bd113c7c8b","src/tls13/key_schedule.rs":"dfe3a46dc72f4b175c70f0e0504819697a4876eed1e68715348d3f30585185cf","src/tls13/mod.rs":"f22e73dd261d1cc94076a9d3e0147352d7aa4c86872566bb79c93a8ad9f1c1a7","src/vecbuf.rs":"e8941e43ff21527ca3e8f6c9a9dc8526fab2e9a2676acb29065f075f98b622d6","src/verify.rs":"ba9dbc12548b0a1d3813e5c51b54ce2073fe6a87aaca9f6368185fb452add659","src/verifybench.rs":"5e4bd4bc5e2b93e4ee2f7f15b552e10c1c2f4bde7e60d44f426a0fc97f7b5d71","src/versions.rs":"125d9981b8c02ed6dcbabfdd76721fef3537f41d424ac1fb79e088331061fb22","src/webpki/anchors.rs":"82011d21d85376f695044157dbb0659ac42bbf8dcbf671e6c19e6e6bc355c9b7","src/webpki/client_verifier.rs":"c6051e79858a70ce746fb55208751a429c691c1bb76bcaca238be310ae11d652","src/webpki/mod.rs":"96075da2044ee4d89ba5647d832bfad06a35a2aa628924e9ed3570d06bc56e2f","src/webpki/server_verifier.rs":"1678d02f7fa352b990915de691959e1d90309ba631b714992afd1e98600cb301","src/webpki/verify.rs":"fcbc90dc30793cbe7f15f363fda793d6e08a132a82d003c1f9e88019a6e1f2b4","src/x509.rs":"e036e80ceb688d1b7ff2ff82a63e99545678b262cf5af0c331fbebc714db4444"},"package":"2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1"}
\ No newline at end of file diff --git a/vendor/rustls/Cargo.lock b/vendor/rustls/Cargo.lock new file mode 100644 index 00000000..29fb7bfa --- /dev/null +++ b/vendor/rustls/Cargo.lock @@ -0,0 +1,1131 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "anstream" +version = "0.6.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] + +[[package]] +name = "asn1-rs" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "aws-lc-fips-sys" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e99d74bb793a19f542ae870a6edafbc5ecf0bc0ba01d4636b7f7e0aba9ee9bd3" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "regex", +] + +[[package]] +name = "aws-lc-rs" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" +dependencies = [ + "aws-lc-fips-sys", + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bencher" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdb4953a096c551ce9ace855a604d702e6e62d77fac690575ae347571717f5" + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + +[[package]] +name = "brotli" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "cc" +version = "1.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[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.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der-parser" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jiff" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.172" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "macro_rules_attribute" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520" +dependencies = [ + "macro_rules_attribute-proc_macro", + "paste", +] + +[[package]] +name = "macro_rules_attribute-proc_macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oid-registry" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pem" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +dependencies = [ + "base64", + "serde", +] + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dee91521343f4c5c6a63edd65e54f31f5c92fe8978c40a4282f8372194c6a7d" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + +[[package]] +name = "rcgen" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "218a7fbb357f6da42c9fd3610b1a5128d087d460e5386eaa5040705c464611dc" +dependencies = [ + "aws-lc-rs", + "pem", + "rustls-pki-types", + "time", + "yasna", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.29" +dependencies = [ + "aws-lc-rs", + "base64", + "bencher", + "brotli", + "brotli-decompressor", + "env_logger", + "hashbrown", + "hex", + "log", + "macro_rules_attribute", + "num-bigint", + "once_cell", + "rcgen", + "ring", + "rustls-pki-types", + "rustls-webpki", + "rustversion", + "serde", + "serde_json", + "subtle", + "time", + "webpki-roots", + "x509-parser", + "zeroize", + "zlib-rs", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "webpki-roots" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "x509-parser" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4569f339c0c402346d4a75a9e39cf8dad310e287eef1ff56d4c68e5067f53460" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "time", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[package]] +name = "zlib-rs" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a" diff --git a/vendor/rustls/Cargo.toml b/vendor/rustls/Cargo.toml new file mode 100644 index 00000000..a7504ff5 --- /dev/null +++ b/vendor/rustls/Cargo.toml @@ -0,0 +1,233 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# 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. +# +# 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 = "2021" +rust-version = "1.71" +name = "rustls" +version = "0.23.29" +build = "build.rs" +exclude = [ + "src/testdata", + "tests/**", +] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Rustls is a modern TLS library written in Rust." +homepage = "https://github.com/rustls/rustls" +readme = "README.md" +categories = [ + "network-programming", + "cryptography", +] +license = "Apache-2.0 OR ISC OR MIT" +repository = "https://github.com/rustls/rustls" + +[package.metadata.cargo-semver-checks.lints] +enum_no_repr_variant_discriminant_changed = "warn" + +[package.metadata.cargo_check_external_types] +allowed_external_types = [ + "rustls_pki_types", + "rustls_pki_types::*", +] + +[package.metadata.docs.rs] +features = [ + "read_buf", + "ring", +] +rustdoc-args = [ + "--cfg", + "docsrs", +] + +[features] +aws-lc-rs = ["aws_lc_rs"] +aws_lc_rs = [ + "dep:aws-lc-rs", + "webpki/aws-lc-rs", + "aws-lc-rs/aws-lc-sys", + "aws-lc-rs/prebuilt-nasm", +] +brotli = [ + "dep:brotli", + "dep:brotli-decompressor", + "std", +] +custom-provider = [] +default = [ + "aws_lc_rs", + "logging", + "prefer-post-quantum", + "std", + "tls12", +] +fips = [ + "aws_lc_rs", + "aws-lc-rs?/fips", + "webpki/aws-lc-rs-fips", +] +logging = ["log"] +prefer-post-quantum = ["aws_lc_rs"] +read_buf = [ + "rustversion", + "std", +] +ring = [ + "dep:ring", + "webpki/ring", +] +std = [ + "webpki/std", + "pki-types/std", + "once_cell/std", +] +tls12 = [] +zlib = ["dep:zlib-rs"] + +[lib] +name = "rustls" +path = "src/lib.rs" + +[[example]] +name = "test_ca" +path = "examples/internal/test_ca.rs" + +[[bench]] +name = "benchmarks" +path = "benches/benchmarks.rs" +harness = false +required-features = ["ring"] + +[dependencies.aws-lc-rs] +version = "1.12" +optional = true +default-features = false + +[dependencies.brotli] +version = "8" +features = ["std"] +optional = true +default-features = false + +[dependencies.brotli-decompressor] +version = "5.0.0" +optional = true + +[dependencies.hashbrown] +version = "0.15" +features = [ + "default-hasher", + "inline-more", +] +optional = true +default-features = false + +[dependencies.log] +version = "0.4.8" +optional = true + +[dependencies.once_cell] +version = "1.16" +features = [ + "alloc", + "race", +] +default-features = false + +[dependencies.pki-types] +version = "1.12" +features = ["alloc"] +package = "rustls-pki-types" + +[dependencies.ring] +version = "0.17" +optional = true + +[dependencies.subtle] +version = "2.5.0" +default-features = false + +[dependencies.webpki] +version = "0.103.4" +features = ["alloc"] +default-features = false +package = "rustls-webpki" + +[dependencies.zeroize] +version = "1.7" + +[dependencies.zlib-rs] +version = "0.5" +optional = true + +[dev-dependencies.base64] +version = "0.22" + +[dev-dependencies.bencher] +version = "0.1.5" + +[dev-dependencies.env_logger] +version = "0.11" + +[dev-dependencies.hex] +version = "0.4" + +[dev-dependencies.log] +version = "0.4.8" + +[dev-dependencies.macro_rules_attribute] +version = "0.2" + +[dev-dependencies.num-bigint] +version = "0.4.4" + +[dev-dependencies.rcgen] +version = "0.14" +features = [ + "pem", + "aws_lc_rs", +] +default-features = false + +[dev-dependencies.serde] +version = "1" +features = ["derive"] + +[dev-dependencies.serde_json] +version = "1" + +[dev-dependencies.time] +version = "0.3.6" +default-features = false + +[dev-dependencies.webpki-roots] +version = "1" + +[dev-dependencies.x509-parser] +version = "0.17" + +[build-dependencies.rustversion] +version = "1.0.6" +optional = true + +[lints.rust.unexpected_cfgs] +level = "warn" +priority = 0 +check-cfg = [ + "cfg(bench)", + "cfg(coverage_nightly)", + "cfg(read_buf)", +] diff --git a/vendor/rustls/LICENSE-APACHE b/vendor/rustls/LICENSE-APACHE new file mode 100644 index 00000000..16fe87b0 --- /dev/null +++ b/vendor/rustls/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/vendor/rustls/LICENSE-ISC b/vendor/rustls/LICENSE-ISC new file mode 100644 index 00000000..03acf1bd --- /dev/null +++ b/vendor/rustls/LICENSE-ISC @@ -0,0 +1,15 @@ +ISC License (ISC) +Copyright (c) 2016, Joseph Birr-Pixton <jpixton@gmail.com> + +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. diff --git a/vendor/rustls/LICENSE-MIT b/vendor/rustls/LICENSE-MIT new file mode 100644 index 00000000..ef480e6f --- /dev/null +++ b/vendor/rustls/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2016 Joseph Birr-Pixton <jpixton@gmail.com> + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/vendor/rustls/README.md b/vendor/rustls/README.md new file mode 100644 index 00000000..5b8f0124 --- /dev/null +++ b/vendor/rustls/README.md @@ -0,0 +1,222 @@ +<p align="center"> + <img width="460" height="300" src="https://raw.githubusercontent.com/rustls/rustls/main/admin/rustls-logo-web.png"> +</p> + +<p align="center"> +Rustls is a modern TLS library written in Rust. +</p> + +# Status + +Rustls is used in production at many organizations and projects. We aim to maintain +reasonable API surface stability but the API may evolve as we make changes to accommodate +new features or performance improvements. + +We have a [roadmap](ROADMAP.md) for our future plans. We also have [benchmarks](BENCHMARKING.md) to +prevent performance regressions and to let you evaluate rustls on your target hardware. + +If you'd like to help out, please see [CONTRIBUTING.md](CONTRIBUTING.md). + +[](https://github.com/rustls/rustls/actions/workflows/build.yml?query=branch%3Amain) +[](https://codecov.io/gh/rustls/rustls/) +[](https://docs.rs/rustls/) +[](https://discord.gg/MCSB76RU96) +[](https://www.bestpractices.dev/projects/9034) + +## Changelog + +The detailed list of changes in each release can be found at +https://github.com/rustls/rustls/releases. + +# Documentation + +https://docs.rs/rustls/ + +# Approach + +Rustls is a TLS library that aims to provide a good level of cryptographic security, +requires no configuration to achieve that security, and provides no unsafe features or +obsolete cryptography by default. + +Rustls implements TLS1.2 and TLS1.3 for both clients and servers. See [the full +list of protocol features](https://docs.rs/rustls/latest/rustls/manual/_04_features/index.html). + +### Platform support + +While Rustls itself is platform independent, by default it uses [`aws-lc-rs`] for implementing +the cryptography in TLS. See [the aws-lc-rs FAQ][aws-lc-rs-platforms-faq] for more details of the +platform/architecture support constraints in aws-lc-rs. + +[`ring`] is also available via the `ring` crate feature: see +[the supported `ring` target platforms][ring-target-platforms]. + +By providing a custom instance of the [`crypto::CryptoProvider`] struct, you +can replace all cryptography dependencies of rustls. This is a route to being portable +to a wider set of architectures and environments, or compliance requirements. See the +[`crypto::CryptoProvider`] documentation for more details. + +Specifying `default-features = false` when depending on rustls will remove the implicit +dependency on aws-lc-rs. + +Rustls requires Rust 1.71 or later. It has an optional dependency on zlib-rs which requires 1.75 or later. + +[ring-target-platforms]: https://github.com/briansmith/ring/blob/2e8363b433fa3b3962c877d9ed2e9145612f3160/include/ring-core/target.h#L18-L64 +[`crypto::CryptoProvider`]: https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html +[`ring`]: https://crates.io/crates/ring +[aws-lc-rs-platforms-faq]: https://aws.github.io/aws-lc-rs/faq.html#can-i-run-aws-lc-rs-on-x-platform-or-architecture +[`aws-lc-rs`]: https://crates.io/crates/aws-lc-rs + +### Cryptography providers + +Since Rustls 0.22 it has been possible to choose the provider of the cryptographic primitives +that Rustls uses. This may be appealing if you have specific platform, compliance or feature +requirements that aren't met by the default provider, [`aws-lc-rs`]. + +Users that wish to customize the provider in use can do so when constructing `ClientConfig` +and `ServerConfig` instances using the `with_crypto_provider` method on the respective config +builder types. See the [`crypto::CryptoProvider`] documentation for more details. + +#### Built-in providers + +Rustls ships with two built-in providers controlled by associated crate features: + +* [`aws-lc-rs`] - enabled by default, available with the `aws_lc_rs` crate feature enabled. +* [`ring`] - available with the `ring` crate feature enabled. + +See the documentation for [`crypto::CryptoProvider`] for details on how providers are +selected. + +#### Third-party providers + +The community has also started developing third-party providers for Rustls: + +* [`boring-rustls-provider`] - a work-in-progress provider that uses [`boringssl`] for +cryptography. +* [`rustls-graviola`] - a provider that uses [`graviola`] for cryptography. +* [`rustls-mbedtls-provider`] - a provider that uses [`mbedtls`] for cryptography. +* [`rustls-openssl`] - a provider that uses [OpenSSL] for cryptography. +* [`rustls-rustcrypto`] - an experimental provider that uses the crypto primitives +from [`RustCrypto`] for cryptography. +* [`rustls-symcrypt`] - a provider that uses Microsoft's [SymCrypt] library. +* [`rustls-wolfcrypt-provider`] - a work-in-progress provider that uses [`wolfCrypt`] for cryptography. + +[`rustls-graviola`]: https://crates.io/crates/rustls-graviola +[`graviola`]: https://github.com/ctz/graviola +[`rustls-mbedtls-provider`]: https://github.com/fortanix/rustls-mbedtls-provider +[`mbedtls`]: https://github.com/Mbed-TLS/mbedtls +[`rustls-openssl`]: https://github.com/tofay/rustls-openssl +[OpenSSL]: https://openssl-library.org/ +[`rustls-symcrypt`]: https://github.com/microsoft/rustls-symcrypt +[SymCrypt]: https://github.com/microsoft/SymCrypt +[`boring-rustls-provider`]: https://github.com/janrueth/boring-rustls-provider +[`boringssl`]: https://github.com/google/boringssl +[`rustls-rustcrypto`]: https://github.com/RustCrypto/rustls-rustcrypto +[`RustCrypto`]: https://github.com/RustCrypto +[`rustls-wolfcrypt-provider`]: https://github.com/wolfSSL/rustls-wolfcrypt-provider +[`wolfCrypt`]: https://www.wolfssl.com/products/wolfcrypt + +#### Custom provider + +We also provide a simple example of writing your own provider in the [custom provider example]. +This example implements a minimal provider using parts of the [`RustCrypto`] ecosystem. + +See the [Making a custom CryptoProvider] section of the documentation for more information +on this topic. + +[custom provider example]: https://github.com/rustls/rustls/tree/main/provider-example/ +[`RustCrypto`]: https://github.com/RustCrypto +[Making a custom CryptoProvider]: https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html#making-a-custom-cryptoprovider + +# Example code + +Our [examples] directory contains demos that show how to handle I/O using the +[`stream::Stream`] helper, as well as more complex asynchronous I/O using [`mio`]. +If you're already using Tokio for an async runtime you may prefer to use +[`tokio-rustls`] instead of interacting with rustls directly. + +The [`mio`] based examples are the most complete, and discussed below. Users +new to Rustls may prefer to look at the simple client/server examples before +diving in to the more complex MIO examples. + +[examples]: examples/ +[`stream::Stream`]: https://docs.rs/rustls/latest/rustls/struct.Stream.html +[`mio`]: https://docs.rs/mio/latest/mio/ +[`tokio-rustls`]: https://docs.rs/tokio-rustls/latest/tokio_rustls/ + +## Client example program + +The MIO client example program is named `tlsclient-mio`. + +Some sample runs: + +``` +$ cargo run --bin tlsclient-mio -- --http mozilla-modern.badssl.com +HTTP/1.1 200 OK +Server: nginx/1.6.2 (Ubuntu) +Date: Wed, 01 Jun 2016 18:44:00 GMT +Content-Type: text/html +Content-Length: 644 +(...) +``` + +or + +``` +$ cargo run --bin tlsclient-mio -- --http expired.badssl.com +TLS error: InvalidCertificate(Expired) +Connection closed +``` + +Run `cargo run --bin tlsclient-mio -- --help` for more options. + +## Server example program + +The MIO server example program is named `tlsserver-mio`. + +Here's a sample run; we start a TLS echo server, then connect to it with +`openssl` and `tlsclient-mio`: + +``` +$ cargo run --bin tlsserver-mio -- --certs test-ca/rsa-2048/end.fullchain --key test-ca/rsa-2048/end.key -p 8443 echo & +$ echo hello world | openssl s_client -ign_eof -quiet -connect localhost:8443 +depth=2 CN = ponytown RSA CA +verify error:num=19:self signed certificate in certificate chain +hello world +^C +$ echo hello world | cargo run --bin tlsclient-mio -- --cafile test-ca/rsa-2048/ca.cert --port 8443 localhost +hello world +^C +``` + +Run `cargo run --bin tlsserver-mio -- --help` for more options. + +# License + +Rustls is distributed under the following three licenses: + +- Apache License version 2.0. +- MIT license. +- ISC license. + +These are included as LICENSE-APACHE, LICENSE-MIT and LICENSE-ISC +respectively. You may use this software under the terms of any +of these licenses, at your option. + +# Project Membership + +- Joe Birr-Pixton ([@ctz], Project Founder - full-time funded by [Prossimo]) +- Dirkjan Ochtman ([@djc], Co-maintainer) +- Daniel McCarney ([@cpu], Co-maintainer) +- Josh Aas ([@bdaehlie], Project Management) + +[@ctz]: https://github.com/ctz +[@djc]: https://github.com/djc +[@cpu]: https://github.com/cpu +[@bdaehlie]: https://github.com/bdaehlie +[Prossimo]: https://www.memorysafety.org/initiative/rustls/ + +# Code of conduct + +This project adopts the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct). +Please email rustls-mod@googlegroups.com to report any instance of misconduct, or if you +have any comments or questions on the Code of Conduct. diff --git a/vendor/rustls/benches/benchmarks.rs b/vendor/rustls/benches/benchmarks.rs new file mode 100644 index 00000000..5aebc995 --- /dev/null +++ b/vendor/rustls/benches/benchmarks.rs @@ -0,0 +1,20 @@ +#![cfg(feature = "ring")] +#![allow(clippy::disallowed_types)] + +use std::io; +use std::sync::Arc; + +use bencher::{Bencher, benchmark_group, benchmark_main}; +use rustls::ServerConnection; +use rustls::crypto::ring as provider; +use rustls_test::{FailsReads, KeyType, make_server_config}; + +fn bench_ewouldblock(c: &mut Bencher) { + let server_config = make_server_config(KeyType::Rsa2048, &provider::default_provider()); + let mut server = ServerConnection::new(Arc::new(server_config)).unwrap(); + let mut read_ewouldblock = FailsReads::new(io::ErrorKind::WouldBlock); + c.iter(|| server.read_tls(&mut read_ewouldblock)); +} + +benchmark_group!(benches, bench_ewouldblock); +benchmark_main!(benches); diff --git a/vendor/rustls/build.rs b/vendor/rustls/build.rs new file mode 100644 index 00000000..8c0bd2aa --- /dev/null +++ b/vendor/rustls/build.rs @@ -0,0 +1,13 @@ +//! This build script allows us to enable the `read_buf` language feature only +//! for Rust Nightly. +//! +//! See the comment in lib.rs to understand why we need this. + +#[cfg_attr(feature = "read_buf", rustversion::not(nightly))] +fn main() {} + +#[cfg(feature = "read_buf")] +#[rustversion::nightly] +fn main() { + println!("cargo:rustc-cfg=read_buf"); +} diff --git a/vendor/rustls/examples/internal/test_ca.rs b/vendor/rustls/examples/internal/test_ca.rs new file mode 100644 index 00000000..1df38484 --- /dev/null +++ b/vendor/rustls/examples/internal/test_ca.rs @@ -0,0 +1,352 @@ +use std::collections::HashMap; +use std::env; +use std::fs::{self, File}; +use std::io::Write; +use std::net::IpAddr; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::Duration; + +use rcgen::string::Ia5String; +use rcgen::{ + BasicConstraints, Certificate, CertificateParams, CertificateRevocationListParams, + DistinguishedName, DnType, ExtendedKeyUsagePurpose, IsCa, Issuer, KeyIdMethod, KeyPair, + KeyUsagePurpose, PKCS_ECDSA_P256_SHA256, PKCS_ECDSA_P384_SHA384, PKCS_ECDSA_P521_SHA512, + PKCS_ED25519, PKCS_RSA_SHA256, PKCS_RSA_SHA384, PKCS_RSA_SHA512, RevocationReason, + RevokedCertParams, RsaKeySize, SanType, SerialNumber, SignatureAlgorithm, +}; +use time::OffsetDateTime; + +fn main() -> Result<(), Box<dyn std::error::Error>> { + let mut certified_keys = HashMap::< + (Role, &'static SignatureAlgorithm), + (Issuer<'static, KeyPair>, Certificate), + >::with_capacity(ROLES.len() * SIG_ALGS.len()); + + for role in ROLES { + for alg in SIG_ALGS { + // Generate a key pair and serialize it to a PEM encoded file. + let key_pair = alg.key_pair(); + let mut key_pair_file = File::create(role.key_file_path(alg))?; + key_pair_file.write_all(key_pair.serialize_pem().as_bytes())?; + + // Issue a certificate for the key pair. For trust anchors, this will be self-signed. + // Otherwise we dig out the issuer and issuer_key for the issuer, which should have + // been produced in earlier iterations based on the careful ordering of roles. + let (params, cert) = match role { + Role::TrustAnchor => { + let params = role.params(alg); + let cert = params.self_signed(&key_pair)?; + (params, cert) + } + Role::Intermediate => { + let issuer = certified_keys + .get(&(Role::TrustAnchor, alg.inner)) + .unwrap(); + let params = role.params(alg); + let cert = params.signed_by(&key_pair, &issuer.0)?; + (params, cert) + } + Role::EndEntity | Role::Client => { + let issuer = certified_keys + .get(&(Role::Intermediate, alg.inner)) + .unwrap(); + let params = role.params(alg); + let cert = params.signed_by(&key_pair, &issuer.0)?; + (params, cert) + } + }; + + // Serialize the issued certificate to a PEM encoded file. + let mut cert_file = File::create(role.cert_pem_file_path(alg))?; + cert_file.write_all(cert.pem().as_bytes())?; + // And to a DER encoded file. + let mut cert_file = File::create(role.cert_der_file_path(alg))?; + cert_file.write_all(cert.der())?; + + // If we're not a trust anchor, generate a CRL for the certificate we just issued. + if role != Role::TrustAnchor { + // The CRL will be signed by the issuer of the certificate being revoked. For + // intermediates this will be the trust anchor, and for client/EE certs this will + // be the intermediate. + let issuer = match role { + Role::Intermediate => certified_keys + .get(&(Role::TrustAnchor, alg.inner)) + .unwrap(), + Role::EndEntity | Role::Client => certified_keys + .get(&(Role::Intermediate, alg.inner)) + .unwrap(), + _ => panic!("unexpected role for CRL generation: {role:?}"), + }; + + let revoked_crl = + crl_for_serial(params.serial_number.clone().unwrap()).signed_by(&issuer.0)?; + let mut revoked_crl_file = File::create( + alg.output_directory() + .join(format!("{}.revoked.crl.pem", role.label())), + )?; + revoked_crl_file.write_all(revoked_crl.pem().unwrap().as_bytes())?; + + let expired_crl = expired_crl().signed_by(&issuer.0)?; + let mut expired_crl_file = File::create( + alg.output_directory() + .join(format!("{}.expired.crl.pem", role.label())), + )?; + expired_crl_file.write_all(expired_crl.pem().unwrap().as_bytes())?; + } + + // When we're issuing end entity or client certs we have a bit of extra work to do + // now that we have full chains in hand. + if matches!(role, Role::EndEntity | Role::Client) { + let root = &certified_keys + .get(&(Role::TrustAnchor, alg.inner)) + .unwrap() + .1; + let intermediate = &certified_keys + .get(&(Role::Intermediate, alg.inner)) + .unwrap() + .1; + + // Write the PEM chain and full chain files for the end entity and client certs. + // Chain files include the intermediate and root certs, while full chain files include + // the end entity or client cert as well. + for f in [ + ("chain", &[intermediate, root][..]), + ("fullchain", &[&cert, intermediate, root][..]), + ] { + let mut chain_file = File::create(alg.output_directory().join(format!( + "{}.{}", + role.label(), + f.0 + )))?; + for cert in f.1 { + chain_file.write_all(cert.pem().as_bytes())?; + } + } + + // Write the PEM public key for the end entity and client. + let mut raw_public_key_file = File::create( + alg.output_directory() + .join(format!("{}.spki.pem", role.label())), + )?; + raw_public_key_file.write_all(key_pair.public_key_pem().as_bytes())?; + } + + certified_keys.insert((role, alg.inner), (Issuer::new(params, key_pair), cert)); + } + } + + Ok(()) +} + +fn crl_for_serial(serial_number: SerialNumber) -> CertificateRevocationListParams { + let now = OffsetDateTime::now_utc(); + CertificateRevocationListParams { + this_update: now, + next_update: now + Duration::from_secs(60 * 60 * 24 * 365 * 100), // 100 years + crl_number: SerialNumber::from(1234), + issuing_distribution_point: None, + revoked_certs: vec![RevokedCertParams { + serial_number, + revocation_time: now, + reason_code: Some(RevocationReason::KeyCompromise), + invalidity_date: None, + }], + key_identifier_method: KeyIdMethod::Sha256, + } +} + +fn expired_crl() -> CertificateRevocationListParams { + let now = OffsetDateTime::now_utc(); + CertificateRevocationListParams { + this_update: now - Duration::from_secs(60), + next_update: now, + crl_number: SerialNumber::from(1234), + issuing_distribution_point: None, + revoked_certs: vec![], + key_identifier_method: KeyIdMethod::Sha256, + } +} + +// Note: these are ordered such that the data dependencies for issuance are satisfied. +const ROLES: [Role; 4] = [ + Role::TrustAnchor, + Role::Intermediate, + Role::EndEntity, + Role::Client, +]; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] +enum Role { + Client, + EndEntity, + Intermediate, + TrustAnchor, +} + +impl Role { + fn params(&self, alg: &'static SigAlgContext) -> CertificateParams { + let mut params = CertificateParams::default(); + params.distinguished_name = self.common_name(alg); + params.use_authority_key_identifier_extension = true; + let serial = SERIAL_NUMBER.fetch_add(1, Ordering::SeqCst); + params.serial_number = Some(SerialNumber::from_slice(&serial.to_be_bytes()[..])); + + match self { + Self::TrustAnchor | Self::Intermediate => { + params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); + params.key_usages = ISSUER_KEY_USAGES.to_vec(); + params.extended_key_usages = ISSUER_EXTENDED_KEY_USAGES.to_vec(); + } + Self::EndEntity | Self::Client => { + params.is_ca = IsCa::NoCa; + params.key_usages = EE_KEY_USAGES.to_vec(); + params.subject_alt_names = vec![ + SanType::DnsName(Ia5String::try_from("testserver.com".to_string()).unwrap()), + SanType::DnsName( + Ia5String::try_from("second.testserver.com".to_string()).unwrap(), + ), + SanType::DnsName(Ia5String::try_from("localhost".to_string()).unwrap()), + SanType::IpAddress(IpAddr::from_str("198.51.100.1").unwrap()), + SanType::IpAddress(IpAddr::from_str("2001:db8::1").unwrap()), + ]; + } + } + + // Client certificates additionally get the client auth EKU. + if *self == Self::Client { + params.extended_key_usages = vec![ExtendedKeyUsagePurpose::ClientAuth]; + } + + params + } + + fn common_name(&self, alg: &'static SigAlgContext) -> DistinguishedName { + let mut distinguished_name = DistinguishedName::new(); + distinguished_name.push( + DnType::CommonName, + match self { + Self::Client => "ponytown client".to_owned(), + Self::EndEntity => "testserver.com".to_owned(), + Self::Intermediate => { + format!("ponytown {} level 2 intermediate", alg.issuer_cn) + } + Self::TrustAnchor => format!("ponytown {} CA", alg.issuer_cn), + }, + ); + distinguished_name + } + + fn key_file_path(&self, alg: &'static SigAlgContext) -> PathBuf { + alg.output_directory() + .join(format!("{}.key", self.label())) + } + + fn cert_pem_file_path(&self, alg: &'static SigAlgContext) -> PathBuf { + alg.output_directory() + .join(format!("{}.cert", self.label())) + } + + fn cert_der_file_path(&self, alg: &'static SigAlgContext) -> PathBuf { + alg.output_directory() + .join(format!("{}.der", self.label())) + } + + fn label(&self) -> &'static str { + match self { + Self::Client => "client", + Self::EndEntity => "end", + Self::Intermediate => "inter", + Self::TrustAnchor => "ca", + } + } +} + +// Note: for convenience we use the RSA sigalg digest algorithm to inform the RSA modulus +// size, mapping SHA256 to RSA 2048, SHA384 to RSA 3072, and SHA512 to RSA 4096. +static SIG_ALGS: &[SigAlgContext] = &[ + SigAlgContext { + inner: &PKCS_RSA_SHA256, + issuer_cn: "RSA 2048", + }, + SigAlgContext { + inner: &PKCS_RSA_SHA384, + issuer_cn: "RSA 3072", + }, + SigAlgContext { + inner: &PKCS_RSA_SHA512, + issuer_cn: "RSA 4096", + }, + SigAlgContext { + inner: &PKCS_ECDSA_P256_SHA256, + issuer_cn: "ECDSA p256", + }, + SigAlgContext { + inner: &PKCS_ECDSA_P384_SHA384, + issuer_cn: "ECDSA p384", + }, + SigAlgContext { + inner: &PKCS_ECDSA_P521_SHA512, + issuer_cn: "ECDSA p521", + }, + SigAlgContext { + inner: &PKCS_ED25519, + issuer_cn: "EdDSA", + }, +]; + +struct SigAlgContext { + pub(crate) inner: &'static SignatureAlgorithm, + pub(crate) issuer_cn: &'static str, +} + +impl SigAlgContext { + fn output_directory(&self) -> PathBuf { + let output_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()) + .join("../") + .join("test-ca") + .join( + self.issuer_cn + .to_lowercase() + .replace(' ', "-"), + ); + fs::create_dir_all(&output_dir).unwrap(); + output_dir + } + + fn key_pair(&self) -> KeyPair { + if *self.inner == PKCS_RSA_SHA256 { + KeyPair::generate_rsa_for(&PKCS_RSA_SHA256, RsaKeySize::_2048) + } else if *self.inner == PKCS_RSA_SHA384 { + KeyPair::generate_rsa_for(&PKCS_RSA_SHA384, RsaKeySize::_3072) + } else if *self.inner == PKCS_RSA_SHA512 { + KeyPair::generate_rsa_for(&PKCS_RSA_SHA512, RsaKeySize::_4096) + } else { + KeyPair::generate_for(self.inner) + } + .unwrap() + } +} + +const ISSUER_KEY_USAGES: &[KeyUsagePurpose; 7] = &[ + KeyUsagePurpose::CrlSign, + KeyUsagePurpose::KeyCertSign, + KeyUsagePurpose::DigitalSignature, + KeyUsagePurpose::ContentCommitment, + KeyUsagePurpose::KeyEncipherment, + KeyUsagePurpose::DataEncipherment, + KeyUsagePurpose::KeyAgreement, +]; + +const ISSUER_EXTENDED_KEY_USAGES: &[ExtendedKeyUsagePurpose; 2] = &[ + ExtendedKeyUsagePurpose::ServerAuth, + ExtendedKeyUsagePurpose::ClientAuth, +]; + +const EE_KEY_USAGES: &[KeyUsagePurpose; 2] = &[ + KeyUsagePurpose::DigitalSignature, + KeyUsagePurpose::ContentCommitment, +]; + +static SERIAL_NUMBER: AtomicU64 = AtomicU64::new(1); diff --git a/vendor/rustls/src/bs_debug.rs b/vendor/rustls/src/bs_debug.rs new file mode 100644 index 00000000..eac4149f --- /dev/null +++ b/vendor/rustls/src/bs_debug.rs @@ -0,0 +1,80 @@ +use core::fmt; + +/// Alternative implementation of `fmt::Debug` for byte slice. +/// +/// Standard `Debug` implementation for `[u8]` is comma separated +/// list of numbers. Since large amount of byte strings are in fact +/// ASCII strings or contain a lot of ASCII strings (e. g. HTTP), +/// it is convenient to print strings as ASCII when possible. +/// +/// This struct wraps `&[u8]` just to override `fmt::Debug`. +/// +/// `BsDebug` is not a part of public API of bytes crate. +pub(crate) struct BsDebug<'a>(pub(crate) &'a [u8]); + +impl fmt::Debug for BsDebug<'_> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(fmt, "b\"")?; + for &c in self.0 { + // https://doc.rust-lang.org/reference.html#byte-escapes + if c == b'\n' { + write!(fmt, "\\n")?; + } else if c == b'\r' { + write!(fmt, "\\r")?; + } else if c == b'\t' { + write!(fmt, "\\t")?; + } else if c == b'\\' || c == b'"' { + write!(fmt, "\\{}", c as char)?; + } else if c == b'\0' { + write!(fmt, "\\0")?; + // ASCII printable + } else if (0x20..0x7f).contains(&c) { + write!(fmt, "{}", c as char)?; + } else { + write!(fmt, "\\x{c:02x}")?; + } + } + write!(fmt, "\"")?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use std::format; + use std::prelude::v1::*; + + use super::BsDebug; + + #[test] + fn debug() { + let vec: Vec<_> = (0..0x100).map(|b| b as u8).collect(); + + let expected = "b\"\ + \\0\\x01\\x02\\x03\\x04\\x05\\x06\\x07\ + \\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\ + \\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\ + \\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f\ + \x20!\\\"#$%&'()*+,-./0123456789:;<=>?\ + @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_\ + `abcdefghijklmnopqrstuvwxyz{|}~\\x7f\ + \\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\ + \\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\ + \\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\ + \\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\ + \\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\ + \\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\ + \\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\ + \\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\ + \\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\ + \\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\ + \\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\ + \\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\ + \\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\ + \\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\ + \\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\ + \\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff\""; + + assert_eq!(expected, format!("{:?}", BsDebug(&vec))); + } +} diff --git a/vendor/rustls/src/builder.rs b/vendor/rustls/src/builder.rs new file mode 100644 index 00000000..f8ff6140 --- /dev/null +++ b/vendor/rustls/src/builder.rs @@ -0,0 +1,290 @@ +use alloc::format; +use alloc::vec::Vec; +use core::fmt; +use core::marker::PhantomData; + +use crate::client::EchMode; +use crate::crypto::CryptoProvider; +use crate::error::Error; +use crate::msgs::handshake::ALL_KEY_EXCHANGE_ALGORITHMS; +use crate::sync::Arc; +use crate::time_provider::TimeProvider; +use crate::versions; +#[cfg(doc)] +use crate::{ClientConfig, ServerConfig}; + +/// A [builder] for [`ServerConfig`] or [`ClientConfig`] values. +/// +/// To get one of these, call [`ServerConfig::builder()`] or [`ClientConfig::builder()`]. +/// +/// To build a config, you must make at least two decisions (in order): +/// +/// - How should this client or server verify certificates provided by its peer? +/// - What certificates should this client or server present to its peer? +/// +/// For settings besides these, see the fields of [`ServerConfig`] and [`ClientConfig`]. +/// +/// The usual choice for protocol primitives is to call +/// [`ClientConfig::builder`]/[`ServerConfig::builder`] +/// which will use rustls' default cryptographic provider and safe defaults for ciphersuites and +/// supported protocol versions. +/// +/// ``` +/// # #[cfg(feature = "aws_lc_rs")] { +/// # rustls::crypto::aws_lc_rs::default_provider().install_default(); +/// use rustls::{ClientConfig, ServerConfig}; +/// ClientConfig::builder() +/// // ... +/// # ; +/// +/// ServerConfig::builder() +/// // ... +/// # ; +/// # } +/// ``` +/// +/// You may also override the choice of protocol versions: +/// +/// ```no_run +/// # #[cfg(feature = "aws_lc_rs")] { +/// # rustls::crypto::aws_lc_rs::default_provider().install_default(); +/// # use rustls::ServerConfig; +/// ServerConfig::builder_with_protocol_versions(&[&rustls::version::TLS13]) +/// // ... +/// # ; +/// # } +/// ``` +/// +/// Overriding the default cryptographic provider introduces a `Result` that must be unwrapped, +/// because the config builder checks for consistency of the choices made. For instance, it's an error to +/// configure only TLS 1.2 cipher suites while specifying that TLS 1.3 should be the only supported protocol +/// version. +/// +/// If you configure a smaller set of protocol primitives than the default, you may get a smaller binary, +/// since the code for the unused ones can be optimized away at link time. +/// +/// After choosing protocol primitives, you must choose (a) how to verify certificates and (b) what certificates +/// (if any) to send to the peer. The methods to do this are specific to whether you're building a ClientConfig +/// or a ServerConfig, as tracked by the [`ConfigSide`] type parameter on the various impls of ConfigBuilder. +/// +/// # ClientConfig certificate configuration +/// +/// For a client, _certificate verification_ must be configured either by calling one of: +/// - [`ConfigBuilder::with_root_certificates`] or +/// - [`ConfigBuilder::dangerous()`] and [`DangerousClientConfigBuilder::with_custom_certificate_verifier`] +/// +/// Next, _certificate sending_ (also known as "client authentication", "mutual TLS", or "mTLS") must be configured +/// or disabled using one of: +/// - [`ConfigBuilder::with_no_client_auth`] - to not send client authentication (most common) +/// - [`ConfigBuilder::with_client_auth_cert`] - to always send a specific certificate +/// - [`ConfigBuilder::with_client_cert_resolver`] - to send a certificate chosen dynamically +/// +/// For example: +/// +/// ``` +/// # #[cfg(feature = "aws_lc_rs")] { +/// # rustls::crypto::aws_lc_rs::default_provider().install_default(); +/// # use rustls::ClientConfig; +/// # let root_certs = rustls::RootCertStore::empty(); +/// ClientConfig::builder() +/// .with_root_certificates(root_certs) +/// .with_no_client_auth(); +/// # } +/// ``` +/// +/// # ServerConfig certificate configuration +/// +/// For a server, _certificate verification_ must be configured by calling one of: +/// - [`ConfigBuilder::with_no_client_auth`] - to not require client authentication (most common) +/// - [`ConfigBuilder::with_client_cert_verifier`] - to use a custom verifier +/// +/// Next, _certificate sending_ must be configured by calling one of: +/// - [`ConfigBuilder::with_single_cert`] - to send a specific certificate +/// - [`ConfigBuilder::with_single_cert_with_ocsp`] - to send a specific certificate, plus stapled OCSP +/// - [`ConfigBuilder::with_cert_resolver`] - to send a certificate chosen dynamically +/// +/// For example: +/// +/// ```no_run +/// # #[cfg(feature = "aws_lc_rs")] { +/// # rustls::crypto::aws_lc_rs::default_provider().install_default(); +/// # use rustls::ServerConfig; +/// # let certs = vec![]; +/// # let private_key = pki_types::PrivateKeyDer::from( +/// # pki_types::PrivatePkcs8KeyDer::from(vec![]) +/// # ); +/// ServerConfig::builder() +/// .with_no_client_auth() +/// .with_single_cert(certs, private_key) +/// .expect("bad certificate/key"); +/// # } +/// ``` +/// +/// # Types +/// +/// ConfigBuilder uses the [typestate] pattern to ensure at compile time that each required +/// configuration item is provided exactly once. This is tracked in the `State` type parameter, +/// which can have these values: +/// +/// - [`WantsVersions`] +/// - [`WantsVerifier`] +/// - [`WantsClientCert`] +/// - [`WantsServerCert`] +/// +/// The other type parameter is `Side`, which is either `ServerConfig` or `ClientConfig` +/// depending on whether the ConfigBuilder was built with [`ServerConfig::builder()`] or +/// [`ClientConfig::builder()`]. +/// +/// You won't need to write out either of these type parameters explicitly. If you write a +/// correct chain of configuration calls they will be used automatically. If you write an +/// incorrect chain of configuration calls you will get an error message from the compiler +/// mentioning some of these types. +/// +/// Additionally, ServerConfig and ClientConfig carry a private field containing a +/// [`CryptoProvider`], from [`ClientConfig::builder_with_provider()`] or +/// [`ServerConfig::builder_with_provider()`]. This determines which cryptographic backend +/// is used. The default is [the process-default provider](`CryptoProvider::get_default`). +/// +/// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html +/// [typestate]: http://cliffle.com/blog/rust-typestate/ +/// [`ServerConfig`]: crate::ServerConfig +/// [`ServerConfig::builder`]: crate::ServerConfig::builder +/// [`ClientConfig`]: crate::ClientConfig +/// [`ClientConfig::builder()`]: crate::ClientConfig::builder() +/// [`ServerConfig::builder()`]: crate::ServerConfig::builder() +/// [`ClientConfig::builder_with_provider()`]: crate::ClientConfig::builder_with_provider() +/// [`ServerConfig::builder_with_provider()`]: crate::ServerConfig::builder_with_provider() +/// [`ConfigBuilder<ClientConfig, WantsVerifier>`]: struct.ConfigBuilder.html#impl-3 +/// [`ConfigBuilder<ServerConfig, WantsVerifier>`]: struct.ConfigBuilder.html#impl-6 +/// [`WantsClientCert`]: crate::client::WantsClientCert +/// [`WantsServerCert`]: crate::server::WantsServerCert +/// [`CryptoProvider::get_default`]: crate::crypto::CryptoProvider::get_default +/// [`DangerousClientConfigBuilder::with_custom_certificate_verifier`]: crate::client::danger::DangerousClientConfigBuilder::with_custom_certificate_verifier +#[derive(Clone)] +pub struct ConfigBuilder<Side: ConfigSide, State> { + pub(crate) state: State, + pub(crate) provider: Arc<CryptoProvider>, + pub(crate) time_provider: Arc<dyn TimeProvider>, + pub(crate) side: PhantomData<Side>, +} + +impl<Side: ConfigSide, State> ConfigBuilder<Side, State> { + /// Return the crypto provider used to construct this builder. + pub fn crypto_provider(&self) -> &Arc<CryptoProvider> { + &self.provider + } +} + +impl<Side: ConfigSide, State: fmt::Debug> fmt::Debug for ConfigBuilder<Side, State> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let side_name = core::any::type_name::<Side>(); + let (ty, _) = side_name + .split_once('<') + .unwrap_or((side_name, "")); + let (_, name) = ty.rsplit_once("::").unwrap_or(("", ty)); + + f.debug_struct(&format!("ConfigBuilder<{name}, _>",)) + .field("state", &self.state) + .finish() + } +} + +/// Config builder state where the caller must supply TLS protocol versions. +/// +/// For more information, see the [`ConfigBuilder`] documentation. +#[derive(Clone, Debug)] +pub struct WantsVersions {} + +impl<S: ConfigSide> ConfigBuilder<S, WantsVersions> { + /// Accept the default protocol versions: both TLS1.2 and TLS1.3 are enabled. + pub fn with_safe_default_protocol_versions( + self, + ) -> Result<ConfigBuilder<S, WantsVerifier>, Error> { + self.with_protocol_versions(versions::DEFAULT_VERSIONS) + } + + /// Use a specific set of protocol versions. + pub fn with_protocol_versions( + self, + versions: &[&'static versions::SupportedProtocolVersion], + ) -> Result<ConfigBuilder<S, WantsVerifier>, Error> { + let mut any_usable_suite = false; + for suite in &self.provider.cipher_suites { + if versions.contains(&suite.version()) { + any_usable_suite = true; + break; + } + } + + if !any_usable_suite { + return Err(Error::General("no usable cipher suites configured".into())); + } + + if self.provider.kx_groups.is_empty() { + return Err(Error::General("no kx groups configured".into())); + } + + // verifying cipher suites have matching kx groups + let mut supported_kx_algos = Vec::with_capacity(ALL_KEY_EXCHANGE_ALGORITHMS.len()); + for group in self.provider.kx_groups.iter() { + let kx = group.name().key_exchange_algorithm(); + if !supported_kx_algos.contains(&kx) { + supported_kx_algos.push(kx); + } + // Small optimization. We don't need to go over other key exchange groups + // if we already cover all supported key exchange algorithms + if supported_kx_algos.len() == ALL_KEY_EXCHANGE_ALGORITHMS.len() { + break; + } + } + + for cs in self.provider.cipher_suites.iter() { + let cs_kx = cs.key_exchange_algorithms(); + if cs_kx + .iter() + .any(|kx| supported_kx_algos.contains(kx)) + { + continue; + } + let suite_name = cs.common().suite; + return Err(Error::General(alloc::format!( + "Ciphersuite {suite_name:?} requires {cs_kx:?} key exchange, but no {cs_kx:?}-compatible \ + key exchange groups were present in `CryptoProvider`'s `kx_groups` field", + ))); + } + + Ok(ConfigBuilder { + state: WantsVerifier { + versions: versions::EnabledVersions::new(versions), + client_ech_mode: None, + }, + provider: self.provider, + time_provider: self.time_provider, + side: self.side, + }) + } +} + +/// Config builder state where the caller must supply a verifier. +/// +/// For more information, see the [`ConfigBuilder`] documentation. +#[derive(Clone, Debug)] +pub struct WantsVerifier { + pub(crate) versions: versions::EnabledVersions, + pub(crate) client_ech_mode: Option<EchMode>, +} + +/// Helper trait to abstract [`ConfigBuilder`] over building a [`ClientConfig`] or [`ServerConfig`]. +/// +/// [`ClientConfig`]: crate::ClientConfig +/// [`ServerConfig`]: crate::ServerConfig +pub trait ConfigSide: sealed::Sealed {} + +impl ConfigSide for crate::ClientConfig {} +impl ConfigSide for crate::ServerConfig {} + +mod sealed { + pub trait Sealed {} + impl Sealed for crate::ClientConfig {} + impl Sealed for crate::ServerConfig {} +} diff --git a/vendor/rustls/src/check.rs b/vendor/rustls/src/check.rs new file mode 100644 index 00000000..9875bc8b --- /dev/null +++ b/vendor/rustls/src/check.rs @@ -0,0 +1,70 @@ +use crate::enums::{ContentType, HandshakeType}; +use crate::error::Error; +use crate::log::warn; +use crate::msgs::message::MessagePayload; + +/// For a Message $m, and a HandshakePayload enum member $payload_type, +/// return Ok(payload) if $m is both a handshake message and one that +/// has the given $payload_type. If not, return Err(rustls::Error) quoting +/// $handshake_type as the expected handshake type. +macro_rules! require_handshake_msg( + ( $m:expr, $handshake_type:path, $payload_type:path ) => ( + match &$m.payload { + MessagePayload::Handshake { parsed: $crate::msgs::handshake::HandshakeMessagePayload( + $payload_type(hm), + ), .. } => Ok(hm), + payload => Err($crate::check::inappropriate_handshake_message( + payload, + &[$crate::ContentType::Handshake], + &[$handshake_type])) + } + ) +); + +/// Like require_handshake_msg, but moves the payload out of $m. +macro_rules! require_handshake_msg_move( + ( $m:expr, $handshake_type:path, $payload_type:path ) => ( + match $m.payload { + MessagePayload::Handshake { parsed: $crate::msgs::handshake::HandshakeMessagePayload( + $payload_type(hm), + ), .. } => Ok(hm), + payload => + Err($crate::check::inappropriate_handshake_message( + &payload, + &[$crate::ContentType::Handshake], + &[$handshake_type])) + } + ) +); + +pub(crate) fn inappropriate_message( + payload: &MessagePayload<'_>, + content_types: &[ContentType], +) -> Error { + warn!( + "Received a {:?} message while expecting {content_types:?}", + payload.content_type(), + ); + Error::InappropriateMessage { + expect_types: content_types.to_vec(), + got_type: payload.content_type(), + } +} + +pub(crate) fn inappropriate_handshake_message( + payload: &MessagePayload<'_>, + content_types: &[ContentType], + handshake_types: &[HandshakeType], +) -> Error { + match payload { + MessagePayload::Handshake { parsed, .. } => { + let got_type = parsed.0.handshake_type(); + warn!("Received a {got_type:?} handshake message while expecting {handshake_types:?}",); + Error::InappropriateHandshakeMessage { + expect_types: handshake_types.to_vec(), + got_type, + } + } + payload => inappropriate_message(payload, content_types), + } +} diff --git a/vendor/rustls/src/client/builder.rs b/vendor/rustls/src/client/builder.rs new file mode 100644 index 00000000..b301e750 --- /dev/null +++ b/vendor/rustls/src/client/builder.rs @@ -0,0 +1,186 @@ +use alloc::vec::Vec; +use core::marker::PhantomData; + +use pki_types::{CertificateDer, PrivateKeyDer}; + +use super::client_conn::Resumption; +use crate::builder::{ConfigBuilder, WantsVerifier}; +use crate::client::{ClientConfig, EchMode, ResolvesClientCert, handy}; +use crate::error::Error; +use crate::key_log::NoKeyLog; +use crate::sign::{CertifiedKey, SingleCertAndKey}; +use crate::sync::Arc; +use crate::versions::TLS13; +use crate::webpki::{self, WebPkiServerVerifier}; +use crate::{WantsVersions, compress, verify, versions}; + +impl ConfigBuilder<ClientConfig, WantsVersions> { + /// Enable Encrypted Client Hello (ECH) in the given mode. + /// + /// This implicitly selects TLS 1.3 as the only supported protocol version to meet the + /// requirement to support ECH. + /// + /// The `ClientConfig` that will be produced by this builder will be specific to the provided + /// [`crate::client::EchConfig`] and may not be appropriate for all connections made by the program. + /// In this case the configuration should only be shared by connections intended for domains + /// that offer the provided [`crate::client::EchConfig`] in their DNS zone. + pub fn with_ech( + self, + mode: EchMode, + ) -> Result<ConfigBuilder<ClientConfig, WantsVerifier>, Error> { + let mut res = self.with_protocol_versions(&[&TLS13][..])?; + res.state.client_ech_mode = Some(mode); + Ok(res) + } +} + +impl ConfigBuilder<ClientConfig, WantsVerifier> { + /// Choose how to verify server certificates. + /// + /// Using this function does not configure revocation. If you wish to + /// configure revocation, instead use: + /// + /// ```diff + /// - .with_root_certificates(root_store) + /// + .with_webpki_verifier( + /// + WebPkiServerVerifier::builder_with_provider(root_store, crypto_provider) + /// + .with_crls(...) + /// + .build()? + /// + ) + /// ``` + pub fn with_root_certificates( + self, + root_store: impl Into<Arc<webpki::RootCertStore>>, + ) -> ConfigBuilder<ClientConfig, WantsClientCert> { + let algorithms = self + .provider + .signature_verification_algorithms; + self.with_webpki_verifier( + WebPkiServerVerifier::new_without_revocation(root_store, algorithms).into(), + ) + } + + /// Choose how to verify server certificates using a webpki verifier. + /// + /// See [`webpki::WebPkiServerVerifier::builder`] and + /// [`webpki::WebPkiServerVerifier::builder_with_provider`] for more information. + pub fn with_webpki_verifier( + self, + verifier: Arc<WebPkiServerVerifier>, + ) -> ConfigBuilder<ClientConfig, WantsClientCert> { + ConfigBuilder { + state: WantsClientCert { + versions: self.state.versions, + verifier, + client_ech_mode: self.state.client_ech_mode, + }, + provider: self.provider, + time_provider: self.time_provider, + side: PhantomData, + } + } + + /// Access configuration options whose use is dangerous and requires + /// extra care. + pub fn dangerous(self) -> danger::DangerousClientConfigBuilder { + danger::DangerousClientConfigBuilder { cfg: self } + } +} + +/// Container for unsafe APIs +pub(super) mod danger { + use core::marker::PhantomData; + + use crate::client::WantsClientCert; + use crate::sync::Arc; + use crate::{ClientConfig, ConfigBuilder, WantsVerifier, verify}; + + /// Accessor for dangerous configuration options. + #[derive(Debug)] + pub struct DangerousClientConfigBuilder { + /// The underlying ClientConfigBuilder + pub cfg: ConfigBuilder<ClientConfig, WantsVerifier>, + } + + impl DangerousClientConfigBuilder { + /// Set a custom certificate verifier. + pub fn with_custom_certificate_verifier( + self, + verifier: Arc<dyn verify::ServerCertVerifier>, + ) -> ConfigBuilder<ClientConfig, WantsClientCert> { + ConfigBuilder { + state: WantsClientCert { + versions: self.cfg.state.versions, + verifier, + client_ech_mode: self.cfg.state.client_ech_mode, + }, + provider: self.cfg.provider, + time_provider: self.cfg.time_provider, + side: PhantomData, + } + } + } +} + +/// A config builder state where the caller needs to supply whether and how to provide a client +/// certificate. +/// +/// For more information, see the [`ConfigBuilder`] documentation. +#[derive(Clone)] +pub struct WantsClientCert { + versions: versions::EnabledVersions, + verifier: Arc<dyn verify::ServerCertVerifier>, + client_ech_mode: Option<EchMode>, +} + +impl ConfigBuilder<ClientConfig, WantsClientCert> { + /// Sets a single certificate chain and matching private key for use + /// in client authentication. + /// + /// `cert_chain` is a vector of DER-encoded certificates. + /// `key_der` is a DER-encoded private key as PKCS#1, PKCS#8, or SEC1. The + /// `aws-lc-rs` and `ring` [`CryptoProvider`][crate::CryptoProvider]s support + /// all three encodings, but other `CryptoProviders` may not. + /// + /// This function fails if `key_der` is invalid. + pub fn with_client_auth_cert( + self, + cert_chain: Vec<CertificateDer<'static>>, + key_der: PrivateKeyDer<'static>, + ) -> Result<ClientConfig, Error> { + let certified_key = CertifiedKey::from_der(cert_chain, key_der, &self.provider)?; + Ok(self.with_client_cert_resolver(Arc::new(SingleCertAndKey::from(certified_key)))) + } + + /// Do not support client auth. + pub fn with_no_client_auth(self) -> ClientConfig { + self.with_client_cert_resolver(Arc::new(handy::FailResolveClientCert {})) + } + + /// Sets a custom [`ResolvesClientCert`]. + pub fn with_client_cert_resolver( + self, + client_auth_cert_resolver: Arc<dyn ResolvesClientCert>, + ) -> ClientConfig { + ClientConfig { + provider: self.provider, + alpn_protocols: Vec::new(), + resumption: Resumption::default(), + max_fragment_size: None, + client_auth_cert_resolver, + versions: self.state.versions, + enable_sni: true, + verifier: self.state.verifier, + key_log: Arc::new(NoKeyLog {}), + enable_secret_extraction: false, + enable_early_data: false, + #[cfg(feature = "tls12")] + require_ems: cfg!(feature = "fips"), + time_provider: self.time_provider, + cert_compressors: compress::default_cert_compressors().to_vec(), + cert_compression_cache: Arc::new(compress::CompressionCache::default()), + cert_decompressors: compress::default_cert_decompressors().to_vec(), + ech_mode: self.state.client_ech_mode, + } + } +} diff --git a/vendor/rustls/src/client/client_conn.rs b/vendor/rustls/src/client/client_conn.rs new file mode 100644 index 00000000..d214c2a1 --- /dev/null +++ b/vendor/rustls/src/client/client_conn.rs @@ -0,0 +1,1062 @@ +use alloc::vec::Vec; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::{fmt, mem}; + +use pki_types::{ServerName, UnixTime}; + +use super::handy::NoClientSessionStorage; +use super::hs::{self, ClientHelloInput}; +#[cfg(feature = "std")] +use crate::WantsVerifier; +use crate::builder::ConfigBuilder; +use crate::client::{EchMode, EchStatus}; +use crate::common_state::{CommonState, Protocol, Side}; +use crate::conn::{ConnectionCore, UnbufferedConnectionCommon}; +use crate::crypto::{CryptoProvider, SupportedKxGroup}; +use crate::enums::{CipherSuite, ProtocolVersion, SignatureScheme}; +use crate::error::Error; +use crate::kernel::KernelConnection; +use crate::log::trace; +use crate::msgs::enums::NamedGroup; +use crate::msgs::handshake::ClientExtensionsInput; +use crate::msgs::persist; +use crate::suites::{ExtractedSecrets, SupportedCipherSuite}; +use crate::sync::Arc; +#[cfg(feature = "std")] +use crate::time_provider::DefaultTimeProvider; +use crate::time_provider::TimeProvider; +use crate::unbuffered::{EncryptError, TransmitTlsData}; +#[cfg(doc)] +use crate::{DistinguishedName, crypto}; +use crate::{KeyLog, WantsVersions, compress, sign, verify, versions}; + +/// A trait for the ability to store client session data, so that sessions +/// can be resumed in future connections. +/// +/// Generally all data in this interface should be treated as +/// **highly sensitive**, containing enough key material to break all security +/// of the corresponding session. +/// +/// `set_`, `insert_`, `remove_` and `take_` operations are mutating; this isn't +/// expressed in the type system to allow implementations freedom in +/// how to achieve interior mutability. `Mutex` is a common choice. +pub trait ClientSessionStore: fmt::Debug + Send + Sync { + /// Remember what `NamedGroup` the given server chose. + fn set_kx_hint(&self, server_name: ServerName<'static>, group: NamedGroup); + + /// This should return the value most recently passed to `set_kx_hint` + /// for the given `server_name`. + /// + /// If `None` is returned, the caller chooses the first configured group, + /// and an extra round trip might happen if that choice is unsatisfactory + /// to the server. + fn kx_hint(&self, server_name: &ServerName<'_>) -> Option<NamedGroup>; + + /// Remember a TLS1.2 session. + /// + /// At most one of these can be remembered at a time, per `server_name`. + fn set_tls12_session( + &self, + server_name: ServerName<'static>, + value: persist::Tls12ClientSessionValue, + ); + + /// Get the most recently saved TLS1.2 session for `server_name` provided to `set_tls12_session`. + fn tls12_session( + &self, + server_name: &ServerName<'_>, + ) -> Option<persist::Tls12ClientSessionValue>; + + /// Remove and forget any saved TLS1.2 session for `server_name`. + fn remove_tls12_session(&self, server_name: &ServerName<'static>); + + /// Remember a TLS1.3 ticket that might be retrieved later from `take_tls13_ticket`, allowing + /// resumption of this session. + /// + /// This can be called multiple times for a given session, allowing multiple independent tickets + /// to be valid at once. The number of times this is called is controlled by the server, so + /// implementations of this trait should apply a reasonable bound of how many items are stored + /// simultaneously. + fn insert_tls13_ticket( + &self, + server_name: ServerName<'static>, + value: persist::Tls13ClientSessionValue, + ); + + /// Return a TLS1.3 ticket previously provided to `add_tls13_ticket`. + /// + /// Implementations of this trait must return each value provided to `add_tls13_ticket` _at most once_. + fn take_tls13_ticket( + &self, + server_name: &ServerName<'static>, + ) -> Option<persist::Tls13ClientSessionValue>; +} + +/// A trait for the ability to choose a certificate chain and +/// private key for the purposes of client authentication. +pub trait ResolvesClientCert: fmt::Debug + Send + Sync { + /// Resolve a client certificate chain/private key to use as the client's + /// identity. + /// + /// `root_hint_subjects` is an optional list of certificate authority + /// subject distinguished names that the client can use to help + /// decide on a client certificate the server is likely to accept. If + /// the list is empty, the client should send whatever certificate it + /// has. The hints are expected to be DER-encoded X.500 distinguished names, + /// per [RFC 5280 A.1]. See [`DistinguishedName`] for more information + /// on decoding with external crates like `x509-parser`. + /// + /// `sigschemes` is the list of the [`SignatureScheme`]s the server + /// supports. + /// + /// Return `None` to continue the handshake without any client + /// authentication. The server may reject the handshake later + /// if it requires authentication. + /// + /// [RFC 5280 A.1]: https://www.rfc-editor.org/rfc/rfc5280#appendix-A.1 + fn resolve( + &self, + root_hint_subjects: &[&[u8]], + sigschemes: &[SignatureScheme], + ) -> Option<Arc<sign::CertifiedKey>>; + + /// Return true if the client only supports raw public keys. + /// + /// See [RFC 7250](https://www.rfc-editor.org/rfc/rfc7250). + fn only_raw_public_keys(&self) -> bool { + false + } + + /// Return true if any certificates at all are available. + fn has_certs(&self) -> bool; +} + +/// Common configuration for (typically) all connections made by a program. +/// +/// Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots +/// from the operating system to add to the [`RootCertStore`] passed to `with_root_certificates()` +/// (the rustls-native-certs crate is often used for this) may take on the order of a few hundred +/// milliseconds. +/// +/// These must be created via the [`ClientConfig::builder()`] or [`ClientConfig::builder_with_provider()`] +/// function. +/// +/// Note that using [`ConfigBuilder<ClientConfig, WantsVersions>::with_ech()`] will produce a common +/// configuration specific to the provided [`crate::client::EchConfig`] that may not be appropriate +/// for all connections made by the program. In this case the configuration should only be shared +/// by connections intended for domains that offer the provided [`crate::client::EchConfig`] in +/// their DNS zone. +/// +/// # Defaults +/// +/// * [`ClientConfig::max_fragment_size`]: the default is `None` (meaning 16kB). +/// * [`ClientConfig::resumption`]: supports resumption with up to 256 server names, using session +/// ids or tickets, with a max of eight tickets per server. +/// * [`ClientConfig::alpn_protocols`]: the default is empty -- no ALPN protocol is negotiated. +/// * [`ClientConfig::key_log`]: key material is not logged. +/// * [`ClientConfig::cert_decompressors`]: depends on the crate features, see [`compress::default_cert_decompressors()`]. +/// * [`ClientConfig::cert_compressors`]: depends on the crate features, see [`compress::default_cert_compressors()`]. +/// * [`ClientConfig::cert_compression_cache`]: caches the most recently used 4 compressions +/// +/// [`RootCertStore`]: crate::RootCertStore +#[derive(Clone, Debug)] +pub struct ClientConfig { + /// Which ALPN protocols we include in our client hello. + /// If empty, no ALPN extension is sent. + pub alpn_protocols: Vec<Vec<u8>>, + + /// How and when the client can resume a previous session. + /// + /// # Sharing `resumption` between `ClientConfig`s + /// In a program using many `ClientConfig`s it may improve resumption rates + /// (which has a significant impact on connection performance) if those + /// configs share a single `Resumption`. + /// + /// However, resumption is only allowed between two `ClientConfig`s if their + /// `client_auth_cert_resolver` (ie, potential client authentication credentials) + /// and `verifier` (ie, server certificate verification settings) are + /// the same (according to `Arc::ptr_eq`). + /// + /// To illustrate, imagine two `ClientConfig`s `A` and `B`. `A` fully validates + /// the server certificate, `B` does not. If `A` and `B` shared a resumption store, + /// it would be possible for a session originated by `B` to be inserted into the + /// store, and then resumed by `A`. This would give a false impression to the user + /// of `A` that the server certificate is fully validated. + pub resumption: Resumption, + + /// The maximum size of plaintext input to be emitted in a single TLS record. + /// A value of None is equivalent to the [TLS maximum] of 16 kB. + /// + /// rustls enforces an arbitrary minimum of 32 bytes for this field. + /// Out of range values are reported as errors from [ClientConnection::new]. + /// + /// Setting this value to a little less than the TCP MSS may improve latency + /// for stream-y workloads. + /// + /// [TLS maximum]: https://datatracker.ietf.org/doc/html/rfc8446#section-5.1 + /// [ClientConnection::new]: crate::client::ClientConnection::new + pub max_fragment_size: Option<usize>, + + /// How to decide what client auth certificate/keys to use. + pub client_auth_cert_resolver: Arc<dyn ResolvesClientCert>, + + /// Whether to send the Server Name Indication (SNI) extension + /// during the client handshake. + /// + /// The default is true. + pub enable_sni: bool, + + /// How to output key material for debugging. The default + /// does nothing. + pub key_log: Arc<dyn KeyLog>, + + /// Allows traffic secrets to be extracted after the handshake, + /// e.g. for kTLS setup. + pub enable_secret_extraction: bool, + + /// Whether to send data on the first flight ("early data") in + /// TLS 1.3 handshakes. + /// + /// The default is false. + pub enable_early_data: bool, + + /// If set to `true`, requires the server to support the extended + /// master secret extraction method defined in [RFC 7627]. + /// + /// The default is `true` if the `fips` crate feature is enabled, + /// `false` otherwise. + /// + /// It must be set to `true` to meet FIPS requirement mentioned in section + /// **D.Q Transition of the TLS 1.2 KDF to Support the Extended Master + /// Secret** from [FIPS 140-3 IG.pdf]. + /// + /// [RFC 7627]: https://datatracker.ietf.org/doc/html/rfc7627 + /// [FIPS 140-3 IG.pdf]: https://csrc.nist.gov/csrc/media/Projects/cryptographic-module-validation-program/documents/fips%20140-3/FIPS%20140-3%20IG.pdf + #[cfg(feature = "tls12")] + pub require_ems: bool, + + /// Provides the current system time + pub time_provider: Arc<dyn TimeProvider>, + + /// Source of randomness and other crypto. + pub(super) provider: Arc<CryptoProvider>, + + /// Supported versions, in no particular order. The default + /// is all supported versions. + pub(super) versions: versions::EnabledVersions, + + /// How to verify the server certificate chain. + pub(super) verifier: Arc<dyn verify::ServerCertVerifier>, + + /// How to decompress the server's certificate chain. + /// + /// If this is non-empty, the [RFC8779] certificate compression + /// extension is offered, and any compressed certificates are + /// transparently decompressed during the handshake. + /// + /// This only applies to TLS1.3 connections. It is ignored for + /// TLS1.2 connections. + /// + /// [RFC8779]: https://datatracker.ietf.org/doc/rfc8879/ + pub cert_decompressors: Vec<&'static dyn compress::CertDecompressor>, + + /// How to compress the client's certificate chain. + /// + /// If a server supports this extension, and advertises support + /// for one of the compression algorithms included here, the + /// client certificate will be compressed according to [RFC8779]. + /// + /// This only applies to TLS1.3 connections. It is ignored for + /// TLS1.2 connections. + /// + /// [RFC8779]: https://datatracker.ietf.org/doc/rfc8879/ + pub cert_compressors: Vec<&'static dyn compress::CertCompressor>, + + /// Caching for compressed certificates. + /// + /// This is optional: [`compress::CompressionCache::Disabled`] gives + /// a cache that does no caching. + pub cert_compression_cache: Arc<compress::CompressionCache>, + + /// How to offer Encrypted Client Hello (ECH). The default is to not offer ECH. + pub(super) ech_mode: Option<EchMode>, +} + +impl ClientConfig { + /// Create a builder for a client configuration with + /// [the process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider] + /// and safe protocol version defaults. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + #[cfg(feature = "std")] + pub fn builder() -> ConfigBuilder<Self, WantsVerifier> { + Self::builder_with_protocol_versions(versions::DEFAULT_VERSIONS) + } + + /// Create a builder for a client configuration with + /// [the process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider] + /// and the provided protocol versions. + /// + /// Panics if + /// - the supported versions are not compatible with the provider (eg. + /// the combination of ciphersuites supported by the provider and supported + /// versions lead to zero cipher suites being usable), + /// - if a `CryptoProvider` cannot be resolved using a combination of + /// the crate features and process default. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + #[cfg(feature = "std")] + pub fn builder_with_protocol_versions( + versions: &[&'static versions::SupportedProtocolVersion], + ) -> ConfigBuilder<Self, WantsVerifier> { + // Safety assumptions: + // 1. that the provider has been installed (explicitly or implicitly) + // 2. that the process-level default provider is usable with the supplied protocol versions. + Self::builder_with_provider( + CryptoProvider::get_default_or_install_from_crate_features().clone(), + ) + .with_protocol_versions(versions) + .unwrap() + } + + /// Create a builder for a client configuration with a specific [`CryptoProvider`]. + /// + /// This will use the provider's configured ciphersuites. You must additionally choose + /// which protocol versions to enable, using `with_protocol_versions` or + /// `with_safe_default_protocol_versions` and handling the `Result` in case a protocol + /// version is not supported by the provider's ciphersuites. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + #[cfg(feature = "std")] + pub fn builder_with_provider( + provider: Arc<CryptoProvider>, + ) -> ConfigBuilder<Self, WantsVersions> { + ConfigBuilder { + state: WantsVersions {}, + provider, + time_provider: Arc::new(DefaultTimeProvider), + side: PhantomData, + } + } + /// Create a builder for a client configuration with no default implementation details. + /// + /// This API must be used by `no_std` users. + /// + /// You must provide a specific [`TimeProvider`]. + /// + /// You must provide a specific [`CryptoProvider`]. + /// + /// This will use the provider's configured ciphersuites. You must additionally choose + /// which protocol versions to enable, using `with_protocol_versions` or + /// `with_safe_default_protocol_versions` and handling the `Result` in case a protocol + /// version is not supported by the provider's ciphersuites. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + pub fn builder_with_details( + provider: Arc<CryptoProvider>, + time_provider: Arc<dyn TimeProvider>, + ) -> ConfigBuilder<Self, WantsVersions> { + ConfigBuilder { + state: WantsVersions {}, + provider, + time_provider, + side: PhantomData, + } + } + + /// Return true if connections made with this `ClientConfig` will + /// operate in FIPS mode. + /// + /// This is different from [`CryptoProvider::fips()`]: [`CryptoProvider::fips()`] + /// is concerned only with cryptography, whereas this _also_ covers TLS-level + /// configuration that NIST recommends, as well as ECH HPKE suites if applicable. + pub fn fips(&self) -> bool { + let mut is_fips = self.provider.fips(); + + #[cfg(feature = "tls12")] + { + is_fips = is_fips && self.require_ems + } + + if let Some(ech_mode) = &self.ech_mode { + is_fips = is_fips && ech_mode.fips(); + } + + is_fips + } + + /// Return the crypto provider used to construct this client configuration. + pub fn crypto_provider(&self) -> &Arc<CryptoProvider> { + &self.provider + } + + /// Access configuration options whose use is dangerous and requires + /// extra care. + pub fn dangerous(&mut self) -> danger::DangerousClientConfig<'_> { + danger::DangerousClientConfig { cfg: self } + } + + pub(super) fn needs_key_share(&self) -> bool { + self.supports_version(ProtocolVersion::TLSv1_3) + } + + /// We support a given TLS version if it's quoted in the configured + /// versions *and* at least one ciphersuite for this version is + /// also configured. + pub(crate) fn supports_version(&self, v: ProtocolVersion) -> bool { + self.versions.contains(v) + && self + .provider + .cipher_suites + .iter() + .any(|cs| cs.version().version == v) + } + + #[cfg(feature = "std")] + pub(crate) fn supports_protocol(&self, proto: Protocol) -> bool { + self.provider + .cipher_suites + .iter() + .any(|cs| cs.usable_for_protocol(proto)) + } + + pub(super) fn find_cipher_suite(&self, suite: CipherSuite) -> Option<SupportedCipherSuite> { + self.provider + .cipher_suites + .iter() + .copied() + .find(|&scs| scs.suite() == suite) + } + + pub(super) fn find_kx_group( + &self, + group: NamedGroup, + version: ProtocolVersion, + ) -> Option<&'static dyn SupportedKxGroup> { + self.provider + .kx_groups + .iter() + .copied() + .find(|skxg| skxg.usable_for_version(version) && skxg.name() == group) + } + + pub(super) fn current_time(&self) -> Result<UnixTime, Error> { + self.time_provider + .current_time() + .ok_or(Error::FailedToGetCurrentTime) + } +} + +/// Configuration for how/when a client is allowed to resume a previous session. +#[derive(Clone, Debug)] +pub struct Resumption { + /// How we store session data or tickets. The default is to use an in-memory + /// [super::handy::ClientSessionMemoryCache]. + pub(super) store: Arc<dyn ClientSessionStore>, + + /// What mechanism is used for resuming a TLS 1.2 session. + pub(super) tls12_resumption: Tls12Resumption, +} + +impl Resumption { + /// Create a new `Resumption` that stores data for the given number of sessions in memory. + /// + /// This is the default `Resumption` choice, and enables resuming a TLS 1.2 session with + /// a session id or RFC 5077 ticket. + #[cfg(feature = "std")] + pub fn in_memory_sessions(num: usize) -> Self { + Self { + store: Arc::new(super::handy::ClientSessionMemoryCache::new(num)), + tls12_resumption: Tls12Resumption::SessionIdOrTickets, + } + } + + /// Use a custom [`ClientSessionStore`] implementation to store sessions. + /// + /// By default, enables resuming a TLS 1.2 session with a session id or RFC 5077 ticket. + pub fn store(store: Arc<dyn ClientSessionStore>) -> Self { + Self { + store, + tls12_resumption: Tls12Resumption::SessionIdOrTickets, + } + } + + /// Disable all use of session resumption. + pub fn disabled() -> Self { + Self { + store: Arc::new(NoClientSessionStorage), + tls12_resumption: Tls12Resumption::Disabled, + } + } + + /// Configure whether TLS 1.2 sessions may be resumed, and by what mechanism. + /// + /// This is meaningless if you've disabled resumption entirely, which is the case in `no-std` + /// contexts. + pub fn tls12_resumption(mut self, tls12: Tls12Resumption) -> Self { + self.tls12_resumption = tls12; + self + } +} + +impl Default for Resumption { + /// Create an in-memory session store resumption with up to 256 server names, allowing + /// a TLS 1.2 session to resume with a session id or RFC 5077 ticket. + fn default() -> Self { + #[cfg(feature = "std")] + let ret = Self::in_memory_sessions(256); + + #[cfg(not(feature = "std"))] + let ret = Self::disabled(); + + ret + } +} + +/// What mechanisms to support for resuming a TLS 1.2 session. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Tls12Resumption { + /// Disable 1.2 resumption. + Disabled, + /// Support 1.2 resumption using session ids only. + SessionIdOnly, + /// Support 1.2 resumption using session ids or RFC 5077 tickets. + /// + /// See[^1] for why you might like to disable RFC 5077 by instead choosing the `SessionIdOnly` + /// option. Note that TLS 1.3 tickets do not have those issues. + /// + /// [^1]: <https://words.filippo.io/we-need-to-talk-about-session-tickets/> + SessionIdOrTickets, +} + +/// Container for unsafe APIs +pub(super) mod danger { + use super::ClientConfig; + use super::verify::ServerCertVerifier; + use crate::sync::Arc; + + /// Accessor for dangerous configuration options. + #[derive(Debug)] + pub struct DangerousClientConfig<'a> { + /// The underlying ClientConfig + pub cfg: &'a mut ClientConfig, + } + + impl DangerousClientConfig<'_> { + /// Overrides the default `ServerCertVerifier` with something else. + pub fn set_certificate_verifier(&mut self, verifier: Arc<dyn ServerCertVerifier>) { + self.cfg.verifier = verifier; + } + } +} + +#[derive(Debug, PartialEq)] +enum EarlyDataState { + Disabled, + Ready, + Accepted, + AcceptedFinished, + Rejected, +} + +#[derive(Debug)] +pub(super) struct EarlyData { + state: EarlyDataState, + left: usize, +} + +impl EarlyData { + fn new() -> Self { + Self { + left: 0, + state: EarlyDataState::Disabled, + } + } + + pub(super) fn is_enabled(&self) -> bool { + matches!(self.state, EarlyDataState::Ready | EarlyDataState::Accepted) + } + + #[cfg(feature = "std")] + fn is_accepted(&self) -> bool { + matches!( + self.state, + EarlyDataState::Accepted | EarlyDataState::AcceptedFinished + ) + } + + pub(super) fn enable(&mut self, max_data: usize) { + assert_eq!(self.state, EarlyDataState::Disabled); + self.state = EarlyDataState::Ready; + self.left = max_data; + } + + pub(super) fn rejected(&mut self) { + trace!("EarlyData rejected"); + self.state = EarlyDataState::Rejected; + } + + pub(super) fn accepted(&mut self) { + trace!("EarlyData accepted"); + assert_eq!(self.state, EarlyDataState::Ready); + self.state = EarlyDataState::Accepted; + } + + pub(super) fn finished(&mut self) { + trace!("EarlyData finished"); + self.state = match self.state { + EarlyDataState::Accepted => EarlyDataState::AcceptedFinished, + _ => panic!("bad EarlyData state"), + } + } + + fn check_write_opt(&mut self, sz: usize) -> Option<usize> { + match self.state { + EarlyDataState::Disabled => unreachable!(), + EarlyDataState::Ready | EarlyDataState::Accepted => { + let take = if self.left < sz { + mem::replace(&mut self.left, 0) + } else { + self.left -= sz; + sz + }; + + Some(take) + } + EarlyDataState::Rejected | EarlyDataState::AcceptedFinished => None, + } + } +} + +#[cfg(feature = "std")] +mod connection { + use alloc::vec::Vec; + use core::fmt; + use core::ops::{Deref, DerefMut}; + use std::io; + + use pki_types::ServerName; + + use super::{ClientConnectionData, ClientExtensionsInput}; + use crate::ClientConfig; + use crate::client::EchStatus; + use crate::common_state::Protocol; + use crate::conn::{ConnectionCommon, ConnectionCore}; + use crate::error::Error; + use crate::suites::ExtractedSecrets; + use crate::sync::Arc; + + /// Stub that implements io::Write and dispatches to `write_early_data`. + pub struct WriteEarlyData<'a> { + sess: &'a mut ClientConnection, + } + + impl<'a> WriteEarlyData<'a> { + fn new(sess: &'a mut ClientConnection) -> Self { + WriteEarlyData { sess } + } + + /// How many bytes you may send. Writes will become short + /// once this reaches zero. + pub fn bytes_left(&self) -> usize { + self.sess + .inner + .core + .data + .early_data + .bytes_left() + } + } + + impl io::Write for WriteEarlyData<'_> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.sess.write_early_data(buf) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } + + impl super::EarlyData { + fn check_write(&mut self, sz: usize) -> io::Result<usize> { + self.check_write_opt(sz) + .ok_or_else(|| io::Error::from(io::ErrorKind::InvalidInput)) + } + + fn bytes_left(&self) -> usize { + self.left + } + } + + /// This represents a single TLS client connection. + pub struct ClientConnection { + inner: ConnectionCommon<ClientConnectionData>, + } + + impl fmt::Debug for ClientConnection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ClientConnection") + .finish() + } + } + + impl ClientConnection { + /// Make a new ClientConnection. `config` controls how + /// we behave in the TLS protocol, `name` is the + /// name of the server we want to talk to. + pub fn new(config: Arc<ClientConfig>, name: ServerName<'static>) -> Result<Self, Error> { + Self::new_with_alpn(config.clone(), name, config.alpn_protocols.clone()) + } + + /// Make a new ClientConnection with custom ALPN protocols. + pub fn new_with_alpn( + config: Arc<ClientConfig>, + name: ServerName<'static>, + alpn_protocols: Vec<Vec<u8>>, + ) -> Result<Self, Error> { + Ok(Self { + inner: ConnectionCommon::from(ConnectionCore::for_client( + config, + name, + ClientExtensionsInput::from_alpn(alpn_protocols), + Protocol::Tcp, + )?), + }) + } + /// Returns an `io::Write` implementer you can write bytes to + /// to send TLS1.3 early data (a.k.a. "0-RTT data") to the server. + /// + /// This returns None in many circumstances when the capability to + /// send early data is not available, including but not limited to: + /// + /// - The server hasn't been talked to previously. + /// - The server does not support resumption. + /// - The server does not support early data. + /// - The resumption data for the server has expired. + /// + /// The server specifies a maximum amount of early data. You can + /// learn this limit through the returned object, and writes through + /// it will process only this many bytes. + /// + /// The server can choose not to accept any sent early data -- + /// in this case the data is lost but the connection continues. You + /// can tell this happened using `is_early_data_accepted`. + pub fn early_data(&mut self) -> Option<WriteEarlyData<'_>> { + if self + .inner + .core + .data + .early_data + .is_enabled() + { + Some(WriteEarlyData::new(self)) + } else { + None + } + } + + /// Returns True if the server signalled it will process early data. + /// + /// If you sent early data and this returns false at the end of the + /// handshake then the server will not process the data. This + /// is not an error, but you may wish to resend the data. + pub fn is_early_data_accepted(&self) -> bool { + self.inner.core.is_early_data_accepted() + } + + /// Extract secrets, so they can be used when configuring kTLS, for example. + /// Should be used with care as it exposes secret key material. + pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + self.inner.dangerous_extract_secrets() + } + + /// Return the connection's Encrypted Client Hello (ECH) status. + pub fn ech_status(&self) -> EchStatus { + self.inner.core.data.ech_status + } + + /// Returns the number of TLS1.3 tickets that have been received. + pub fn tls13_tickets_received(&self) -> u32 { + self.inner.tls13_tickets_received + } + + /// Return true if the connection was made with a `ClientConfig` that is FIPS compatible. + /// + /// This is different from [`crate::crypto::CryptoProvider::fips()`]: + /// it is concerned only with cryptography, whereas this _also_ covers TLS-level + /// configuration that NIST recommends, as well as ECH HPKE suites if applicable. + pub fn fips(&self) -> bool { + self.inner.core.common_state.fips + } + + fn write_early_data(&mut self, data: &[u8]) -> io::Result<usize> { + self.inner + .core + .data + .early_data + .check_write(data.len()) + .map(|sz| { + self.inner + .send_early_plaintext(&data[..sz]) + }) + } + } + + impl Deref for ClientConnection { + type Target = ConnectionCommon<ClientConnectionData>; + + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl DerefMut for ClientConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } + } + + #[doc(hidden)] + impl<'a> TryFrom<&'a mut crate::Connection> for &'a mut ClientConnection { + type Error = (); + + fn try_from(value: &'a mut crate::Connection) -> Result<Self, Self::Error> { + use crate::Connection::*; + match value { + Client(conn) => Ok(conn), + Server(_) => Err(()), + } + } + } + + impl From<ClientConnection> for crate::Connection { + fn from(conn: ClientConnection) -> Self { + Self::Client(conn) + } + } +} +#[cfg(feature = "std")] +pub use connection::{ClientConnection, WriteEarlyData}; + +impl ConnectionCore<ClientConnectionData> { + pub(crate) fn for_client( + config: Arc<ClientConfig>, + name: ServerName<'static>, + extra_exts: ClientExtensionsInput<'static>, + proto: Protocol, + ) -> Result<Self, Error> { + let mut common_state = CommonState::new(Side::Client); + common_state.set_max_fragment_size(config.max_fragment_size)?; + common_state.protocol = proto; + common_state.enable_secret_extraction = config.enable_secret_extraction; + common_state.fips = config.fips(); + let mut data = ClientConnectionData::new(); + + let mut cx = hs::ClientContext { + common: &mut common_state, + data: &mut data, + // `start_handshake` won't produce plaintext + sendable_plaintext: None, + }; + + let input = ClientHelloInput::new(name, &extra_exts, &mut cx, config)?; + let state = input.start_handshake(extra_exts, &mut cx)?; + Ok(Self::new(state, data, common_state)) + } + + #[cfg(feature = "std")] + pub(crate) fn is_early_data_accepted(&self) -> bool { + self.data.early_data.is_accepted() + } +} + +/// Unbuffered version of `ClientConnection` +/// +/// See the [`crate::unbuffered`] module docs for more details +pub struct UnbufferedClientConnection { + inner: UnbufferedConnectionCommon<ClientConnectionData>, +} + +impl UnbufferedClientConnection { + /// Make a new ClientConnection. `config` controls how we behave in the TLS protocol, `name` is + /// the name of the server we want to talk to. + pub fn new(config: Arc<ClientConfig>, name: ServerName<'static>) -> Result<Self, Error> { + Self::new_with_extensions( + config.clone(), + name, + ClientExtensionsInput::from_alpn(config.alpn_protocols.clone()), + ) + } + + /// Make a new UnbufferedClientConnection with custom ALPN protocols. + pub fn new_with_alpn( + config: Arc<ClientConfig>, + name: ServerName<'static>, + alpn_protocols: Vec<Vec<u8>>, + ) -> Result<Self, Error> { + Self::new_with_extensions( + config, + name, + ClientExtensionsInput::from_alpn(alpn_protocols), + ) + } + + fn new_with_extensions( + config: Arc<ClientConfig>, + name: ServerName<'static>, + extensions: ClientExtensionsInput<'static>, + ) -> Result<Self, Error> { + Ok(Self { + inner: UnbufferedConnectionCommon::from(ConnectionCore::for_client( + config, + name, + extensions, + Protocol::Tcp, + )?), + }) + } + + /// Extract secrets, so they can be used when configuring kTLS, for example. + /// Should be used with care as it exposes secret key material. + #[deprecated = "dangerous_extract_secrets() does not support session tickets or \ + key updates, use dangerous_into_kernel_connection() instead"] + pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + self.inner.dangerous_extract_secrets() + } + + /// Extract secrets and a [`KernelConnection`] object. + /// + /// This allows you use rustls to manage keys and then manage encryption and + /// decryption yourself (e.g. for kTLS). + /// + /// Should be used with care as it exposes secret key material. + /// + /// See the [`crate::kernel`] documentations for details on prerequisites + /// for calling this method. + pub fn dangerous_into_kernel_connection( + self, + ) -> Result<(ExtractedSecrets, KernelConnection<ClientConnectionData>), Error> { + self.inner + .core + .dangerous_into_kernel_connection() + } + + /// Returns the number of TLS1.3 tickets that have been received. + pub fn tls13_tickets_received(&self) -> u32 { + self.inner.tls13_tickets_received + } +} + +impl Deref for UnbufferedClientConnection { + type Target = UnbufferedConnectionCommon<ClientConnectionData>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for UnbufferedClientConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl TransmitTlsData<'_, ClientConnectionData> { + /// returns an adapter that allows encrypting early (RTT-0) data before transmitting the + /// already encoded TLS data + /// + /// IF allowed by the protocol + pub fn may_encrypt_early_data(&mut self) -> Option<MayEncryptEarlyData<'_>> { + if self + .conn + .core + .data + .early_data + .is_enabled() + { + Some(MayEncryptEarlyData { conn: self.conn }) + } else { + None + } + } +} + +/// Allows encrypting early (RTT-0) data +pub struct MayEncryptEarlyData<'c> { + conn: &'c mut UnbufferedConnectionCommon<ClientConnectionData>, +} + +impl MayEncryptEarlyData<'_> { + /// Encrypts `application_data` into the `outgoing_tls` buffer + /// + /// returns the number of bytes that were written into `outgoing_tls`, or an error if + /// the provided buffer was too small. In the error case, `outgoing_tls` is not modified + pub fn encrypt( + &mut self, + early_data: &[u8], + outgoing_tls: &mut [u8], + ) -> Result<usize, EarlyDataError> { + let Some(allowed) = self + .conn + .core + .data + .early_data + .check_write_opt(early_data.len()) + else { + return Err(EarlyDataError::ExceededAllowedEarlyData); + }; + + self.conn + .core + .common_state + .write_plaintext(early_data[..allowed].into(), outgoing_tls) + .map_err(|e| e.into()) + } +} + +/// Errors that may arise when encrypting early (RTT-0) data +#[derive(Debug)] +pub enum EarlyDataError { + /// Cannot encrypt more early data due to imposed limits + ExceededAllowedEarlyData, + /// Encryption error + Encrypt(EncryptError), +} + +impl From<EncryptError> for EarlyDataError { + fn from(v: EncryptError) -> Self { + Self::Encrypt(v) + } +} + +impl fmt::Display for EarlyDataError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ExceededAllowedEarlyData => f.write_str("cannot send any more early data"), + Self::Encrypt(e) => fmt::Display::fmt(e, f), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for EarlyDataError {} + +/// State associated with a client connection. +#[derive(Debug)] +pub struct ClientConnectionData { + pub(super) early_data: EarlyData, + pub(super) ech_status: EchStatus, +} + +impl ClientConnectionData { + fn new() -> Self { + Self { + early_data: EarlyData::new(), + ech_status: EchStatus::NotOffered, + } + } +} + +impl crate::conn::SideData for ClientConnectionData {} diff --git a/vendor/rustls/src/client/common.rs b/vendor/rustls/src/client/common.rs new file mode 100644 index 00000000..9afa0c34 --- /dev/null +++ b/vendor/rustls/src/client/common.rs @@ -0,0 +1,119 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; + +use super::ResolvesClientCert; +use crate::log::{debug, trace}; +use crate::msgs::enums::ExtensionType; +use crate::msgs::handshake::{CertificateChain, DistinguishedName, ProtocolName, ServerExtensions}; +use crate::sync::Arc; +use crate::{SignatureScheme, compress, sign}; + +#[derive(Debug)] +pub(super) struct ServerCertDetails<'a> { + pub(super) cert_chain: CertificateChain<'a>, + pub(super) ocsp_response: Vec<u8>, +} + +impl<'a> ServerCertDetails<'a> { + pub(super) fn new(cert_chain: CertificateChain<'a>, ocsp_response: Vec<u8>) -> Self { + Self { + cert_chain, + ocsp_response, + } + } + + pub(super) fn into_owned(self) -> ServerCertDetails<'static> { + let Self { + cert_chain, + ocsp_response, + } = self; + ServerCertDetails { + cert_chain: cert_chain.into_owned(), + ocsp_response, + } + } +} + +pub(super) struct ClientHelloDetails { + pub(super) alpn_protocols: Vec<ProtocolName>, + pub(super) sent_extensions: Vec<ExtensionType>, + pub(super) extension_order_seed: u16, + pub(super) offered_cert_compression: bool, +} + +impl ClientHelloDetails { + pub(super) fn new(alpn_protocols: Vec<ProtocolName>, extension_order_seed: u16) -> Self { + Self { + alpn_protocols, + sent_extensions: Vec::new(), + extension_order_seed, + offered_cert_compression: false, + } + } + + pub(super) fn server_sent_unsolicited_extensions( + &self, + received_exts: &ServerExtensions<'_>, + allowed_unsolicited: &[ExtensionType], + ) -> bool { + let mut extensions = received_exts.collect_used(); + extensions.extend( + received_exts + .unknown_extensions + .iter() + .map(|ext| ExtensionType::from(*ext)), + ); + for ext_type in extensions { + if !self.sent_extensions.contains(&ext_type) && !allowed_unsolicited.contains(&ext_type) + { + trace!("Unsolicited extension {ext_type:?}"); + return true; + } + } + + false + } +} + +pub(super) enum ClientAuthDetails { + /// Send an empty `Certificate` and no `CertificateVerify`. + Empty { auth_context_tls13: Option<Vec<u8>> }, + /// Send a non-empty `Certificate` and a `CertificateVerify`. + Verify { + certkey: Arc<sign::CertifiedKey>, + signer: Box<dyn sign::Signer>, + auth_context_tls13: Option<Vec<u8>>, + compressor: Option<&'static dyn compress::CertCompressor>, + }, +} + +impl ClientAuthDetails { + pub(super) fn resolve( + resolver: &dyn ResolvesClientCert, + canames: Option<&[DistinguishedName]>, + sigschemes: &[SignatureScheme], + auth_context_tls13: Option<Vec<u8>>, + compressor: Option<&'static dyn compress::CertCompressor>, + ) -> Self { + let acceptable_issuers = canames + .unwrap_or_default() + .iter() + .map(|p| p.as_ref()) + .collect::<Vec<&[u8]>>(); + + if let Some(certkey) = resolver.resolve(&acceptable_issuers, sigschemes) { + if let Some(signer) = certkey.key.choose_scheme(sigschemes) { + debug!("Attempting client auth"); + return Self::Verify { + certkey, + signer, + auth_context_tls13, + compressor, + }; + } + } + + debug!("Client auth requested but no cert/sigscheme available"); + Self::Empty { auth_context_tls13 } + } +} diff --git a/vendor/rustls/src/client/ech.rs b/vendor/rustls/src/client/ech.rs new file mode 100644 index 00000000..616ebbfc --- /dev/null +++ b/vendor/rustls/src/client/ech.rs @@ -0,0 +1,899 @@ +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; + +use pki_types::{DnsName, EchConfigListBytes, ServerName}; +use subtle::ConstantTimeEq; + +use crate::CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV; +use crate::client::tls13; +use crate::crypto::SecureRandom; +use crate::crypto::hash::Hash; +use crate::crypto::hpke::{EncapsulatedSecret, Hpke, HpkePublicKey, HpkeSealer, HpkeSuite}; +use crate::hash_hs::{HandshakeHash, HandshakeHashBuffer}; +use crate::log::{debug, trace, warn}; +use crate::msgs::base::{Payload, PayloadU16}; +use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::enums::{ExtensionType, HpkeKem}; +use crate::msgs::handshake::{ + ClientExtensions, ClientHelloPayload, EchConfigContents, EchConfigPayload, Encoding, + EncryptedClientHello, EncryptedClientHelloOuter, HandshakeMessagePayload, HandshakePayload, + HelloRetryRequest, HpkeKeyConfig, HpkeSymmetricCipherSuite, PresharedKeyBinder, + PresharedKeyOffer, Random, ServerHelloPayload, ServerNamePayload, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist; +use crate::msgs::persist::Retrieved; +use crate::tls13::key_schedule::{ + KeyScheduleEarly, KeyScheduleHandshakeStart, server_ech_hrr_confirmation_secret, +}; +use crate::{ + AlertDescription, ClientConfig, CommonState, EncryptedClientHelloError, Error, + PeerIncompatible, PeerMisbehaved, ProtocolVersion, Tls13CipherSuite, +}; + +/// Controls how Encrypted Client Hello (ECH) is used in a client handshake. +#[derive(Clone, Debug)] +pub enum EchMode { + /// ECH is enabled and the ClientHello will be encrypted based on the provided + /// configuration. + Enable(EchConfig), + + /// No ECH configuration is available but the client should act as though it were. + /// + /// This is an anti-ossification measure, sometimes referred to as "GREASE"[^0]. + /// [^0]: <https://www.rfc-editor.org/rfc/rfc8701> + Grease(EchGreaseConfig), +} + +impl EchMode { + /// Returns true if the ECH mode will use a FIPS approved HPKE suite. + pub fn fips(&self) -> bool { + match self { + Self::Enable(ech_config) => ech_config.suite.fips(), + Self::Grease(grease_config) => grease_config.suite.fips(), + } + } +} + +impl From<EchConfig> for EchMode { + fn from(config: EchConfig) -> Self { + Self::Enable(config) + } +} + +impl From<EchGreaseConfig> for EchMode { + fn from(config: EchGreaseConfig) -> Self { + Self::Grease(config) + } +} + +/// Configuration for performing encrypted client hello. +/// +/// Note: differs from the protocol-encoded EchConfig (`EchConfigMsg`). +#[derive(Clone, Debug)] +pub struct EchConfig { + /// The selected EchConfig. + pub(crate) config: EchConfigPayload, + + /// An HPKE instance corresponding to a suite from the `config` we have selected as + /// a compatible choice. + pub(crate) suite: &'static dyn Hpke, +} + +impl EchConfig { + /// Construct an EchConfig by selecting a ECH config from the provided bytes that is compatible + /// with one of the given HPKE suites. + /// + /// The config list bytes should be sourced from a DNS-over-HTTPS lookup resolving the `HTTPS` + /// resource record for the host name of the server you wish to connect via ECH, + /// and extracting the ECH configuration from the `ech` parameter. The extracted bytes should + /// be base64 decoded to yield the `EchConfigListBytes` you provide to rustls. + /// + /// One of the provided ECH configurations must be compatible with the HPKE provider's supported + /// suites or an error will be returned. + /// + /// See the [`ech-client.rs`] example for a complete example of fetching ECH configs from DNS. + /// + /// [`ech-client.rs`]: https://github.com/rustls/rustls/blob/main/examples/src/bin/ech-client.rs + pub fn new( + ech_config_list: EchConfigListBytes<'_>, + hpke_suites: &[&'static dyn Hpke], + ) -> Result<Self, Error> { + let ech_configs = Vec::<EchConfigPayload>::read(&mut Reader::init(&ech_config_list)) + .map_err(|_| { + Error::InvalidEncryptedClientHello(EncryptedClientHelloError::InvalidConfigList) + })?; + + // Note: we name the index var _i because if the log feature is disabled + // it is unused. + #[cfg_attr(not(feature = "logging"), allow(clippy::unused_enumerate_index))] + for (_i, config) in ech_configs.iter().enumerate() { + let contents = match config { + EchConfigPayload::V18(contents) => contents, + EchConfigPayload::Unknown { + version: _version, .. + } => { + warn!( + "ECH config {} has unsupported version {:?}", + _i + 1, + _version + ); + continue; // Unsupported version. + } + }; + + if contents.has_unknown_mandatory_extension() || contents.has_duplicate_extension() { + warn!("ECH config has duplicate, or unknown mandatory extensions: {contents:?}",); + continue; // Unsupported, or malformed extensions. + } + + let key_config = &contents.key_config; + for cipher_suite in &key_config.symmetric_cipher_suites { + if cipher_suite.aead_id.tag_len().is_none() { + continue; // Unsupported EXPORT_ONLY AEAD cipher suite. + } + + let suite = HpkeSuite { + kem: key_config.kem_id, + sym: *cipher_suite, + }; + if let Some(hpke) = hpke_suites + .iter() + .find(|hpke| hpke.suite() == suite) + { + debug!( + "selected ECH config ID {:?} suite {:?} public_name {:?}", + key_config.config_id, suite, contents.public_name + ); + return Ok(Self { + config: config.clone(), + suite: *hpke, + }); + } + } + } + + Err(EncryptedClientHelloError::NoCompatibleConfig.into()) + } + + pub(super) fn state( + &self, + server_name: ServerName<'static>, + config: &ClientConfig, + ) -> Result<EchState, Error> { + EchState::new( + self, + server_name.clone(), + config + .client_auth_cert_resolver + .has_certs(), + config.provider.secure_random, + config.enable_sni, + ) + } + + /// Compute the HPKE `SetupBaseS` `info` parameter for this ECH configuration. + /// + /// See <https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-17#section-6.1>. + pub(crate) fn hpke_info(&self) -> Vec<u8> { + let mut info = Vec::with_capacity(128); + // "tls ech" || 0x00 || ECHConfig + info.extend_from_slice(b"tls ech\0"); + self.config.encode(&mut info); + info + } +} + +/// Configuration for GREASE Encrypted Client Hello. +#[derive(Clone, Debug)] +pub struct EchGreaseConfig { + pub(crate) suite: &'static dyn Hpke, + pub(crate) placeholder_key: HpkePublicKey, +} + +impl EchGreaseConfig { + /// Construct a GREASE ECH configuration. + /// + /// This configuration is used when the client wishes to offer ECH to prevent ossification, + /// but doesn't have a real ECH configuration to use for the remote server. In this case + /// a placeholder or "GREASE"[^0] extension is used. + /// + /// Returns an error if the HPKE provider does not support the given suite. + /// + /// [^0]: <https://www.rfc-editor.org/rfc/rfc8701> + pub fn new(suite: &'static dyn Hpke, placeholder_key: HpkePublicKey) -> Self { + Self { + suite, + placeholder_key, + } + } + + /// Build a GREASE ECH extension based on the placeholder configuration. + /// + /// See <https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#name-grease-ech> for + /// more information. + pub(crate) fn grease_ext( + &self, + secure_random: &'static dyn SecureRandom, + inner_name: ServerName<'static>, + outer_hello: &ClientHelloPayload, + ) -> Result<EncryptedClientHello, Error> { + trace!("Preparing GREASE ECH extension"); + + // Pick a random config id. + let mut config_id: [u8; 1] = [0; 1]; + secure_random.fill(&mut config_id[..])?; + + let suite = self.suite.suite(); + + // Construct a dummy ECH state - we don't have a real ECH config from a server since + // this is for GREASE. + let mut grease_state = EchState::new( + &EchConfig { + config: EchConfigPayload::V18(EchConfigContents { + key_config: HpkeKeyConfig { + config_id: config_id[0], + kem_id: HpkeKem::DHKEM_P256_HKDF_SHA256, + public_key: PayloadU16::new(self.placeholder_key.0.clone()), + symmetric_cipher_suites: vec![suite.sym], + }, + maximum_name_length: 0, + public_name: DnsName::try_from("filler").unwrap(), + extensions: Vec::default(), + }), + suite: self.suite, + }, + inner_name, + false, + secure_random, + false, // Does not matter if we enable/disable SNI here. Inner hello is not used. + )?; + + // Construct an inner hello using the outer hello - this allows us to know the size of + // dummy payload we should use for the GREASE extension. + let encoded_inner_hello = grease_state.encode_inner_hello(outer_hello, None, &None); + + // Generate a payload of random data equivalent in length to a real inner hello. + let payload_len = encoded_inner_hello.len() + + suite + .sym + .aead_id + .tag_len() + // Safety: we have confirmed the AEAD is supported when building the config. All + // supported AEADs have a tag length. + .unwrap(); + let mut payload = vec![0; payload_len]; + secure_random.fill(&mut payload)?; + + // Return the GREASE extension. + Ok(EncryptedClientHello::Outer(EncryptedClientHelloOuter { + cipher_suite: suite.sym, + config_id: config_id[0], + enc: PayloadU16::new(grease_state.enc.0), + payload: PayloadU16::new(payload), + })) + } +} + +/// An enum representing ECH offer status. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum EchStatus { + /// ECH was not offered - it is a normal TLS handshake. + NotOffered, + /// GREASE ECH was sent. This is not considered offering ECH. + Grease, + /// ECH was offered but we do not yet know whether the offer was accepted or rejected. + Offered, + /// ECH was offered and the server accepted. + Accepted, + /// ECH was offered and the server rejected. + Rejected, +} + +/// Contextual data for a TLS client handshake that has offered encrypted client hello (ECH). +pub(crate) struct EchState { + // The public DNS name from the ECH configuration we've chosen - this is included as the SNI + // value for the "outer" client hello. It can only be a DnsName, not an IP address. + pub(crate) outer_name: DnsName<'static>, + // If we're resuming in the inner hello, this is the early key schedule to use for encrypting + // early data if the ECH offer is accepted. + pub(crate) early_data_key_schedule: Option<KeyScheduleEarly>, + // A random value we use for the inner hello. + pub(crate) inner_hello_random: Random, + // A transcript buffer maintained for the inner hello. Once ECH is confirmed we switch to + // using this transcript for the handshake. + pub(crate) inner_hello_transcript: HandshakeHashBuffer, + // A source of secure random data. + secure_random: &'static dyn SecureRandom, + // An HPKE sealer context that can be used for encrypting ECH data. + sender: Box<dyn HpkeSealer>, + // The ID of the ECH configuration we've chosen - this is included in the outer ECH extension. + config_id: u8, + // The private server name we'll use for the inner protected hello. + inner_name: ServerName<'static>, + // The advertised maximum name length from the ECH configuration we've chosen - this is used + // for padding calculations. + maximum_name_length: u8, + // A supported symmetric cipher suite from the ECH configuration we've chosen - this is + // included in the outer ECH extension. + cipher_suite: HpkeSymmetricCipherSuite, + // A secret encapsulated to the public key of the remote server. This is included in the + // outer ECH extension for non-retry outer hello messages. + enc: EncapsulatedSecret, + // Whether the inner client hello should contain a server name indication (SNI) extension. + enable_sni: bool, + // The extensions sent in the inner hello. + sent_extensions: Vec<ExtensionType>, +} + +impl EchState { + pub(crate) fn new( + config: &EchConfig, + inner_name: ServerName<'static>, + client_auth_enabled: bool, + secure_random: &'static dyn SecureRandom, + enable_sni: bool, + ) -> Result<Self, Error> { + let EchConfigPayload::V18(config_contents) = &config.config else { + // the public EchConfig::new() constructor ensures we only have supported + // configurations. + unreachable!("ECH config version mismatch"); + }; + let key_config = &config_contents.key_config; + + // Encapsulate a secret for the server's public key, and set up a sender context + // we can use to seal messages. + let (enc, sender) = config.suite.setup_sealer( + &config.hpke_info(), + &HpkePublicKey(key_config.public_key.0.clone()), + )?; + + // Start a new transcript buffer for the inner hello. + let mut inner_hello_transcript = HandshakeHashBuffer::new(); + if client_auth_enabled { + inner_hello_transcript.set_client_auth_enabled(); + } + + Ok(Self { + secure_random, + sender, + config_id: key_config.config_id, + inner_name, + outer_name: config_contents.public_name.clone(), + maximum_name_length: config_contents.maximum_name_length, + cipher_suite: config.suite.suite().sym, + enc, + inner_hello_random: Random::new(secure_random)?, + inner_hello_transcript, + early_data_key_schedule: None, + enable_sni, + sent_extensions: Vec::new(), + }) + } + + /// Construct a ClientHelloPayload offering ECH. + /// + /// An outer hello, with a protected inner hello for the `inner_name` will be returned, and the + /// ECH context will be updated to reflect the inner hello that was offered. + /// + /// If `retry_req` is `Some`, then the outer hello will be constructed for a hello retry request. + /// + /// If `resuming` is `Some`, then the inner hello will be constructed for a resumption handshake. + pub(crate) fn ech_hello( + &mut self, + mut outer_hello: ClientHelloPayload, + retry_req: Option<&HelloRetryRequest>, + resuming: &Option<Retrieved<&persist::Tls13ClientSessionValue>>, + ) -> Result<ClientHelloPayload, Error> { + trace!( + "Preparing ECH offer {}", + if retry_req.is_some() { "for retry" } else { "" } + ); + + // Construct the encoded inner hello and update the transcript. + let encoded_inner_hello = self.encode_inner_hello(&outer_hello, retry_req, resuming); + + // Complete the ClientHelloOuterAAD with an ech extension, the payload should be a placeholder + // of size L, all zeroes. L == length of encrypting encoded client hello inner w/ the selected + // HPKE AEAD. (sum of plaintext + tag length, typically). + let payload_len = encoded_inner_hello.len() + + self + .cipher_suite + .aead_id + .tag_len() + // Safety: we've already verified this AEAD is supported when loading the config + // that was used to create the ECH context. All supported AEADs have a tag length. + .unwrap(); + + // Outer hello's created in response to a hello retry request omit the enc value. + let enc = match retry_req.is_some() { + true => Vec::default(), + false => self.enc.0.clone(), + }; + + fn outer_hello_ext(ctx: &EchState, enc: Vec<u8>, payload: Vec<u8>) -> EncryptedClientHello { + EncryptedClientHello::Outer(EncryptedClientHelloOuter { + cipher_suite: ctx.cipher_suite, + config_id: ctx.config_id, + enc: PayloadU16::new(enc), + payload: PayloadU16::new(payload), + }) + } + + // The outer handshake is not permitted to resume a session. If we're resuming in the + // inner handshake we remove the PSK extension from the outer hello, replacing it + // with a GREASE PSK to implement the "ClientHello Malleability Mitigation" mentioned + // in 10.12.3. + if let Some(psk_offer) = outer_hello.preshared_key_offer.as_mut() { + self.grease_psk(psk_offer)?; + } + + // To compute the encoded AAD we add a placeholder extension with an empty payload. + outer_hello.encrypted_client_hello = + Some(outer_hello_ext(self, enc.clone(), vec![0; payload_len])); + + // Next we compute the proper extension payload. + let payload = self + .sender + .seal(&outer_hello.get_encoding(), &encoded_inner_hello)?; + + // And then we replace the placeholder extension with the real one. + outer_hello.encrypted_client_hello = Some(outer_hello_ext(self, enc, payload)); + + Ok(outer_hello) + } + + /// Confirm whether an ECH offer was accepted based on examining the server hello. + pub(crate) fn confirm_acceptance( + self, + ks: &mut KeyScheduleHandshakeStart, + server_hello: &ServerHelloPayload, + server_hello_encoded: &Payload<'_>, + hash: &'static dyn Hash, + ) -> Result<Option<EchAccepted>, Error> { + // Start the inner transcript hash now that we know the hash algorithm to use. + let inner_transcript = self + .inner_hello_transcript + .start_hash(hash); + + // Fork the transcript that we've started with the inner hello to use for a confirmation step. + // We need to preserve the original inner_transcript to use if this confirmation succeeds. + let mut confirmation_transcript = inner_transcript.clone(); + + // Add the server hello confirmation - this is computed by altering the received + // encoding rather than reencoding it. + confirmation_transcript + .add_message(&Self::server_hello_conf(server_hello, server_hello_encoded)); + + // Derive a confirmation secret from the inner hello random and the confirmation transcript. + let derived = ks.server_ech_confirmation_secret( + self.inner_hello_random.0.as_ref(), + confirmation_transcript.current_hash(), + ); + + // Check that first 8 digits of the derived secret match the last 8 digits of the original + // server random. This match signals that the server accepted the ECH offer. + // Indexing safety: Random is [0; 32] by construction. + + match ConstantTimeEq::ct_eq(derived.as_ref(), server_hello.random.0[24..].as_ref()).into() { + true => { + trace!("ECH accepted by server"); + Ok(Some(EchAccepted { + transcript: inner_transcript, + random: self.inner_hello_random, + sent_extensions: self.sent_extensions, + })) + } + false => { + trace!("ECH rejected by server"); + Ok(None) + } + } + } + + pub(crate) fn confirm_hrr_acceptance( + &self, + hrr: &HelloRetryRequest, + cs: &Tls13CipherSuite, + common: &mut CommonState, + ) -> Result<bool, Error> { + // The client checks for the "encrypted_client_hello" extension. + let ech_conf = match &hrr.encrypted_client_hello { + // If none is found, the server has implicitly rejected ECH. + None => return Ok(false), + // Otherwise, if it has a length other than 8, the client aborts the + // handshake with a "decode_error" alert. + Some(ech_conf) if ech_conf.bytes().len() != 8 => { + return Err({ + common.send_fatal_alert( + AlertDescription::DecodeError, + PeerMisbehaved::IllegalHelloRetryRequestWithInvalidEch, + ) + }); + } + Some(ech_conf) => ech_conf, + }; + + // Otherwise the client computes hrr_accept_confirmation as described in Section + // 7.2.1 + let confirmation_transcript = self.inner_hello_transcript.clone(); + let mut confirmation_transcript = + confirmation_transcript.start_hash(cs.common.hash_provider); + confirmation_transcript.rollup_for_hrr(); + confirmation_transcript.add_message(&Self::hello_retry_request_conf(hrr)); + + let derived = server_ech_hrr_confirmation_secret( + cs.hkdf_provider, + &self.inner_hello_random.0, + confirmation_transcript.current_hash(), + ); + + match ConstantTimeEq::ct_eq(derived.as_ref(), ech_conf.bytes()).into() { + true => { + trace!("ECH accepted by server in hello retry request"); + Ok(true) + } + false => { + trace!("ECH rejected by server in hello retry request"); + Ok(false) + } + } + } + + /// Update the ECH context inner hello transcript based on a received hello retry request message. + /// + /// This will start the in-progress transcript using the given `hash`, convert it into an HRR + /// buffer, and then add the hello retry message `m`. + pub(crate) fn transcript_hrr_update(&mut self, hash: &'static dyn Hash, m: &Message<'_>) { + trace!("Updating ECH inner transcript for HRR"); + + let inner_transcript = self + .inner_hello_transcript + .clone() + .start_hash(hash); + + let mut inner_transcript_buffer = inner_transcript.into_hrr_buffer(); + inner_transcript_buffer.add_message(m); + self.inner_hello_transcript = inner_transcript_buffer; + } + + // 5.1 "Encoding the ClientHelloInner" + fn encode_inner_hello( + &mut self, + outer_hello: &ClientHelloPayload, + retryreq: Option<&HelloRetryRequest>, + resuming: &Option<Retrieved<&persist::Tls13ClientSessionValue>>, + ) -> Vec<u8> { + // Start building an inner hello using the outer_hello as a template. + let mut inner_hello = ClientHelloPayload { + // Some information is copied over as-is. + client_version: outer_hello.client_version, + session_id: outer_hello.session_id, + compression_methods: outer_hello.compression_methods.clone(), + + // We will build up the included extensions ourselves. + extensions: Box::new(ClientExtensions::default()), + + // Set the inner hello random to the one we generated when creating the ECH state. + // We hold on to the inner_hello_random in the ECH state to use later for confirming + // whether ECH was accepted or not. + random: self.inner_hello_random, + + // We remove the empty renegotiation info SCSV from the outer hello's ciphersuite. + // Similar to the TLS 1.2 specific extensions we will filter out, this is seen as a + // TLS 1.2 only feature by bogo. + cipher_suites: outer_hello + .cipher_suites + .iter() + .filter(|cs| **cs != TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + .cloned() + .collect(), + }; + + inner_hello.order_seed = outer_hello.order_seed; + + // The inner hello will always have an inner variant of the ECH extension added. + // See Section 6.1 rule 4. + inner_hello.encrypted_client_hello = Some(EncryptedClientHello::Inner); + + let inner_sni = match &self.inner_name { + // The inner hello only gets a SNI value if enable_sni is true and the inner name + // is a domain name (not an IP address). + ServerName::DnsName(dns_name) if self.enable_sni => Some(dns_name), + _ => None, + }; + + // Now we consider each of the outer hello's extensions - we can either: + // 1. Omit the extension if it isn't appropriate (e.g. is a TLS 1.2 extension). + // 2. Add the extension to the inner hello as-is. + // 3. Compress the extension, by collecting it into a list of to-be-compressed + // extensions we'll handle separately. + let outer_extensions = outer_hello.used_extensions_in_encoding_order(); + let mut compressed_exts = Vec::with_capacity(outer_extensions.len()); + for ext in outer_extensions { + // Some outer hello extensions are only useful in the context where a TLS 1.3 + // connection allows TLS 1.2. This isn't the case for ECH so we skip adding them + // to the inner hello. + if matches!( + ext, + ExtensionType::ExtendedMasterSecret + | ExtensionType::SessionTicket + | ExtensionType::ECPointFormats + ) { + continue; + } + + if ext == ExtensionType::ServerName { + // We may want to replace the outer hello SNI with our own inner hello specific SNI. + if let Some(sni_value) = inner_sni { + inner_hello.server_name = Some(ServerNamePayload::from(sni_value)); + } + // We don't want to add, or compress, the SNI from the outer hello. + continue; + } + + // Compressed extensions need to be put aside to include in one contiguous block. + // Uncompressed extensions get added directly to the inner hello. + if ext.ech_compress() { + compressed_exts.push(ext); + } + + inner_hello.clone_one(outer_hello, ext); + } + + // We've added all the uncompressed extensions. Now we need to add the contiguous + // block of to-be-compressed extensions. + inner_hello.contiguous_extensions = compressed_exts.clone(); + + // Note which extensions we're sending in the inner hello. This may differ from + // the outer hello (e.g. the inner hello may omit SNI while the outer hello will + // always have the ECH cover name in SNI). + self.sent_extensions = inner_hello.collect_used(); + + // If we're resuming, we need to update the PSK binder in the inner hello. + if let Some(resuming) = resuming.as_ref() { + let mut chp = HandshakeMessagePayload(HandshakePayload::ClientHello(inner_hello)); + + // Retain the early key schedule we get from processing the binder. + self.early_data_key_schedule = Some(tls13::fill_in_psk_binder( + resuming, + &self.inner_hello_transcript, + &mut chp, + )); + + // fill_in_psk_binder works on an owned HandshakeMessagePayload, so we need to + // extract our inner hello back out of it to retain ownership. + inner_hello = match chp.0 { + HandshakePayload::ClientHello(chp) => chp, + // Safety: we construct the HMP above and know its type unconditionally. + _ => unreachable!(), + }; + } + + trace!("ECH Inner Hello: {inner_hello:#?}"); + + // Encode the inner hello according to the rules required for ECH. This differs + // from the standard encoding in several ways. Notably this is where we will + // replace the block of contiguous to-be-compressed extensions with a marker. + let mut encoded_hello = inner_hello.ech_inner_encoding(compressed_exts); + + // Calculate padding + // max_name_len = L + let max_name_len = self.maximum_name_length; + let max_name_len = if max_name_len > 0 { max_name_len } else { 255 }; + + let padding_len = match &self.inner_name { + ServerName::DnsName(name) => { + // name.len() = D + // max(0, L - D) + core::cmp::max( + 0, + max_name_len.saturating_sub(name.as_ref().len() as u8) as usize, + ) + } + _ => { + // L + 9 + // "This is the length of a "server_name" extension with an L-byte name." + // We widen to usize here to avoid overflowing u8 + u8. + max_name_len as usize + 9 + } + }; + + // Let L be the length of the EncodedClientHelloInner with all the padding computed so far + // Let N = 31 - ((L - 1) % 32) and add N bytes of padding. + let padding_len = 31 - ((encoded_hello.len() + padding_len - 1) % 32); + encoded_hello.extend(vec![0; padding_len]); + + // Construct the inner hello message that will be used for the transcript. + let inner_hello_msg = Message { + version: match retryreq { + // <https://datatracker.ietf.org/doc/html/rfc8446#section-5.1>: + // "This value MUST be set to 0x0303 for all records generated + // by a TLS 1.3 implementation ..." + Some(_) => ProtocolVersion::TLSv1_2, + // "... other than an initial ClientHello (i.e., one not + // generated after a HelloRetryRequest), where it MAY also be + // 0x0301 for compatibility purposes" + // + // (retryreq == None means we're in the "initial ClientHello" case) + None => ProtocolVersion::TLSv1_0, + }, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientHello(inner_hello), + )), + }; + + // Update the inner transcript buffer with the inner hello message. + self.inner_hello_transcript + .add_message(&inner_hello_msg); + + encoded_hello + } + + // See https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#name-grease-psk + fn grease_psk(&self, psk_offer: &mut PresharedKeyOffer) -> Result<(), Error> { + for ident in psk_offer.identities.iter_mut() { + // "For each PSK identity advertised in the ClientHelloInner, the + // client generates a random PSK identity with the same length." + self.secure_random + .fill(&mut ident.identity.0)?; + // "It also generates a random, 32-bit, unsigned integer to use as + // the obfuscated_ticket_age." + let mut ticket_age = [0_u8; 4]; + self.secure_random + .fill(&mut ticket_age)?; + ident.obfuscated_ticket_age = u32::from_be_bytes(ticket_age); + } + + // "Likewise, for each inner PSK binder, the client generates a random string + // of the same length." + psk_offer.binders = psk_offer + .binders + .iter() + .map(|old_binder| { + // We can't access the wrapped binder PresharedKeyBinder's PayloadU8 mutably, + // so we construct new PresharedKeyBinder's from scratch with the same length. + let mut new_binder = vec![0; old_binder.as_ref().len()]; + self.secure_random + .fill(&mut new_binder)?; + Ok::<PresharedKeyBinder, Error>(PresharedKeyBinder::from(new_binder)) + }) + .collect::<Result<_, _>>()?; + Ok(()) + } + + fn server_hello_conf( + server_hello: &ServerHelloPayload, + server_hello_encoded: &Payload<'_>, + ) -> Message<'static> { + // The confirmation is computed over the server hello, which has had + // its `random` field altered to zero the final 8 bytes. + // + // nb. we don't require that we can round-trip a `ServerHelloPayload`, to + // allow for efficiency in its in-memory representation. That means + // we operate here on the received encoding, as the confirmation needs + // to be computed on that. + let mut encoded = server_hello_encoded.clone().into_vec(); + encoded[SERVER_HELLO_ECH_CONFIRMATION_SPAN].fill(0x00); + + Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::Handshake { + encoded: Payload::Owned(encoded), + parsed: HandshakeMessagePayload(HandshakePayload::ServerHello( + server_hello.clone(), + )), + }, + } + } + + fn hello_retry_request_conf(retry_req: &HelloRetryRequest) -> Message<'_> { + Self::ech_conf_message(HandshakeMessagePayload( + HandshakePayload::HelloRetryRequest(retry_req.clone()), + )) + } + + fn ech_conf_message(hmp: HandshakeMessagePayload<'_>) -> Message<'_> { + let mut hmp_encoded = Vec::new(); + hmp.payload_encode(&mut hmp_encoded, Encoding::EchConfirmation); + Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::Handshake { + encoded: Payload::new(hmp_encoded), + parsed: hmp, + }, + } + } +} + +/// The last eight bytes of the ServerHello's random, taken from a Handshake message containing it. +/// +/// This has: +/// - a HandshakeType (1 byte), +/// - an exterior length (3 bytes), +/// - the legacy_version (2 bytes), and +/// - the balance of the random field (24 bytes). +const SERVER_HELLO_ECH_CONFIRMATION_SPAN: core::ops::Range<usize> = + (1 + 3 + 2 + 24)..(1 + 3 + 2 + 32); + +/// Returned from EchState::check_acceptance when the server has accepted the ECH offer. +/// +/// Holds the state required to continue the handshake with the inner hello from the ECH offer. +pub(crate) struct EchAccepted { + pub(crate) transcript: HandshakeHash, + pub(crate) random: Random, + pub(crate) sent_extensions: Vec<ExtensionType>, +} + +pub(crate) fn fatal_alert_required( + retry_configs: Option<Vec<EchConfigPayload>>, + common: &mut CommonState, +) -> Error { + common.send_fatal_alert( + AlertDescription::EncryptedClientHelloRequired, + PeerIncompatible::ServerRejectedEncryptedClientHello(retry_configs), + ) +} + +#[cfg(test)] +mod tests { + use crate::enums::CipherSuite; + use crate::msgs::handshake::{Random, ServerExtensions, SessionId}; + + use super::*; + + #[test] + fn server_hello_conf_alters_server_hello_random() { + let server_hello = ServerHelloPayload { + legacy_version: ProtocolVersion::TLSv1_2, + random: Random([0xffu8; 32]), + session_id: SessionId::empty(), + cipher_suite: CipherSuite::TLS13_AES_256_GCM_SHA384, + compression_method: crate::msgs::enums::Compression::Null, + extensions: Box::new(ServerExtensions::default()), + }; + let message = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ServerHello(server_hello.clone()), + )), + }; + let Message { + payload: + MessagePayload::Handshake { + encoded: server_hello_encoded_before, + .. + }, + .. + } = &message + else { + unreachable!("ServerHello is a handshake message"); + }; + + let message = EchState::server_hello_conf(&server_hello, server_hello_encoded_before); + + let Message { + payload: + MessagePayload::Handshake { + encoded: server_hello_encoded_after, + .. + }, + .. + } = &message + else { + unreachable!("ServerHello is a handshake message"); + }; + + assert_eq!( + std::format!("{server_hello_encoded_before:x?}"), + "020000280303ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001302000000", + "beforehand eight bytes at end of Random should be 0xff here ^^^^^^^^^^^^^^^^ " + ); + assert_eq!( + std::format!("{server_hello_encoded_after:x?}"), + "020000280303ffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000001302000000", + " afterwards those bytes are zeroed ^^^^^^^^^^^^^^^^ " + ); + } +} diff --git a/vendor/rustls/src/client/handy.rs b/vendor/rustls/src/client/handy.rs new file mode 100644 index 00000000..3ad3073b --- /dev/null +++ b/vendor/rustls/src/client/handy.rs @@ -0,0 +1,390 @@ +use pki_types::ServerName; + +use crate::enums::SignatureScheme; +use crate::msgs::persist; +use crate::sync::Arc; +use crate::{NamedGroup, client, sign}; + +/// An implementer of `ClientSessionStore` which does nothing. +#[derive(Debug)] +pub(super) struct NoClientSessionStorage; + +impl client::ClientSessionStore for NoClientSessionStorage { + fn set_kx_hint(&self, _: ServerName<'static>, _: NamedGroup) {} + + fn kx_hint(&self, _: &ServerName<'_>) -> Option<NamedGroup> { + None + } + + fn set_tls12_session(&self, _: ServerName<'static>, _: persist::Tls12ClientSessionValue) {} + + fn tls12_session(&self, _: &ServerName<'_>) -> Option<persist::Tls12ClientSessionValue> { + None + } + + fn remove_tls12_session(&self, _: &ServerName<'_>) {} + + fn insert_tls13_ticket(&self, _: ServerName<'static>, _: persist::Tls13ClientSessionValue) {} + + fn take_tls13_ticket(&self, _: &ServerName<'_>) -> Option<persist::Tls13ClientSessionValue> { + None + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +mod cache { + use alloc::collections::VecDeque; + use core::fmt; + + use pki_types::ServerName; + + use crate::lock::Mutex; + use crate::msgs::persist; + use crate::{NamedGroup, limited_cache}; + + const MAX_TLS13_TICKETS_PER_SERVER: usize = 8; + + struct ServerData { + kx_hint: Option<NamedGroup>, + + // Zero or one TLS1.2 sessions. + #[cfg(feature = "tls12")] + tls12: Option<persist::Tls12ClientSessionValue>, + + // Up to MAX_TLS13_TICKETS_PER_SERVER TLS1.3 tickets, oldest first. + tls13: VecDeque<persist::Tls13ClientSessionValue>, + } + + impl Default for ServerData { + fn default() -> Self { + Self { + kx_hint: None, + #[cfg(feature = "tls12")] + tls12: None, + tls13: VecDeque::with_capacity(MAX_TLS13_TICKETS_PER_SERVER), + } + } + } + + /// An implementer of `ClientSessionStore` that stores everything + /// in memory. + /// + /// It enforces a limit on the number of entries to bound memory usage. + pub struct ClientSessionMemoryCache { + servers: Mutex<limited_cache::LimitedCache<ServerName<'static>, ServerData>>, + } + + impl ClientSessionMemoryCache { + /// Make a new ClientSessionMemoryCache. `size` is the + /// maximum number of stored sessions. + #[cfg(feature = "std")] + pub fn new(size: usize) -> Self { + let max_servers = size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1) + / MAX_TLS13_TICKETS_PER_SERVER; + Self { + servers: Mutex::new(limited_cache::LimitedCache::new(max_servers)), + } + } + + /// Make a new ClientSessionMemoryCache. `size` is the + /// maximum number of stored sessions. + #[cfg(not(feature = "std"))] + pub fn new<M: crate::lock::MakeMutex>(size: usize) -> Self { + let max_servers = size.saturating_add(MAX_TLS13_TICKETS_PER_SERVER - 1) + / MAX_TLS13_TICKETS_PER_SERVER; + Self { + servers: Mutex::new::<M>(limited_cache::LimitedCache::new(max_servers)), + } + } + } + + impl super::client::ClientSessionStore for ClientSessionMemoryCache { + fn set_kx_hint(&self, server_name: ServerName<'static>, group: NamedGroup) { + self.servers + .lock() + .unwrap() + .get_or_insert_default_and_edit(server_name, |data| data.kx_hint = Some(group)); + } + + fn kx_hint(&self, server_name: &ServerName<'_>) -> Option<NamedGroup> { + self.servers + .lock() + .unwrap() + .get(server_name) + .and_then(|sd| sd.kx_hint) + } + + fn set_tls12_session( + &self, + _server_name: ServerName<'static>, + _value: persist::Tls12ClientSessionValue, + ) { + #[cfg(feature = "tls12")] + self.servers + .lock() + .unwrap() + .get_or_insert_default_and_edit(_server_name.clone(), |data| { + data.tls12 = Some(_value) + }); + } + + fn tls12_session( + &self, + _server_name: &ServerName<'_>, + ) -> Option<persist::Tls12ClientSessionValue> { + #[cfg(not(feature = "tls12"))] + return None; + + #[cfg(feature = "tls12")] + self.servers + .lock() + .unwrap() + .get(_server_name) + .and_then(|sd| sd.tls12.as_ref().cloned()) + } + + fn remove_tls12_session(&self, _server_name: &ServerName<'static>) { + #[cfg(feature = "tls12")] + self.servers + .lock() + .unwrap() + .get_mut(_server_name) + .and_then(|data| data.tls12.take()); + } + + fn insert_tls13_ticket( + &self, + server_name: ServerName<'static>, + value: persist::Tls13ClientSessionValue, + ) { + self.servers + .lock() + .unwrap() + .get_or_insert_default_and_edit(server_name.clone(), |data| { + if data.tls13.len() == data.tls13.capacity() { + data.tls13.pop_front(); + } + data.tls13.push_back(value); + }); + } + + fn take_tls13_ticket( + &self, + server_name: &ServerName<'static>, + ) -> Option<persist::Tls13ClientSessionValue> { + self.servers + .lock() + .unwrap() + .get_mut(server_name) + .and_then(|data| data.tls13.pop_back()) + } + } + + impl fmt::Debug for ClientSessionMemoryCache { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // Note: we omit self.servers as it may contain sensitive data. + f.debug_struct("ClientSessionMemoryCache") + .finish() + } + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +pub use cache::ClientSessionMemoryCache; + +#[derive(Debug)] +pub(super) struct FailResolveClientCert {} + +impl client::ResolvesClientCert for FailResolveClientCert { + fn resolve( + &self, + _root_hint_subjects: &[&[u8]], + _sigschemes: &[SignatureScheme], + ) -> Option<Arc<sign::CertifiedKey>> { + None + } + + fn has_certs(&self) -> bool { + false + } +} + +/// An exemplar `ResolvesClientCert` implementation that always resolves to a single +/// [RFC 7250] raw public key. +/// +/// [RFC 7250]: https://tools.ietf.org/html/rfc7250 +#[derive(Clone, Debug)] +pub struct AlwaysResolvesClientRawPublicKeys(Arc<sign::CertifiedKey>); +impl AlwaysResolvesClientRawPublicKeys { + /// Create a new `AlwaysResolvesClientRawPublicKeys` instance. + pub fn new(certified_key: Arc<sign::CertifiedKey>) -> Self { + Self(certified_key) + } +} + +impl client::ResolvesClientCert for AlwaysResolvesClientRawPublicKeys { + fn resolve( + &self, + _root_hint_subjects: &[&[u8]], + _sigschemes: &[SignatureScheme], + ) -> Option<Arc<sign::CertifiedKey>> { + Some(self.0.clone()) + } + + fn only_raw_public_keys(&self) -> bool { + true + } + + /// Returns true if the resolver is ready to present an identity. + /// + /// Even though the function is called `has_certs`, it returns true + /// although only an RPK (Raw Public Key) is available, not an actual certificate. + fn has_certs(&self) -> bool { + true + } +} + +#[cfg(test)] +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use std::prelude::v1::*; + + use pki_types::{ServerName, UnixTime}; + + use super::NoClientSessionStorage; + use super::provider::cipher_suite; + use crate::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; + use crate::client::{ClientSessionStore, ResolvesClientCert}; + use crate::msgs::base::PayloadU16; + use crate::msgs::enums::NamedGroup; + use crate::msgs::handshake::CertificateChain; + #[cfg(feature = "tls12")] + use crate::msgs::handshake::SessionId; + use crate::msgs::persist::Tls13ClientSessionValue; + use crate::pki_types::CertificateDer; + use crate::suites::SupportedCipherSuite; + use crate::sync::Arc; + use crate::{DigitallySignedStruct, Error, SignatureScheme, sign}; + + #[test] + fn test_noclientsessionstorage_does_nothing() { + let c = NoClientSessionStorage {}; + let name = ServerName::try_from("example.com").unwrap(); + let now = UnixTime::now(); + let server_cert_verifier: Arc<dyn ServerCertVerifier> = Arc::new(DummyServerCertVerifier); + let resolves_client_cert: Arc<dyn ResolvesClientCert> = Arc::new(DummyResolvesClientCert); + + c.set_kx_hint(name.clone(), NamedGroup::X25519); + assert_eq!(None, c.kx_hint(&name)); + + #[cfg(feature = "tls12")] + { + use crate::msgs::persist::Tls12ClientSessionValue; + let SupportedCipherSuite::Tls12(tls12_suite) = + cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + else { + unreachable!() + }; + + c.set_tls12_session( + name.clone(), + Tls12ClientSessionValue::new( + tls12_suite, + SessionId::empty(), + Arc::new(PayloadU16::empty()), + &[], + CertificateChain::default(), + &server_cert_verifier, + &resolves_client_cert, + now, + 0, + true, + ), + ); + assert!(c.tls12_session(&name).is_none()); + c.remove_tls12_session(&name); + } + + let SupportedCipherSuite::Tls13(tls13_suite) = cipher_suite::TLS13_AES_256_GCM_SHA384 + else { + unreachable!(); + }; + c.insert_tls13_ticket( + name.clone(), + Tls13ClientSessionValue::new( + tls13_suite, + Arc::new(PayloadU16::empty()), + &[], + CertificateChain::default(), + &server_cert_verifier, + &resolves_client_cert, + now, + 0, + 0, + 0, + ), + ); + assert!(c.take_tls13_ticket(&name).is_none()); + } + + #[derive(Debug)] + struct DummyServerCertVerifier; + + impl ServerCertVerifier for DummyServerCertVerifier { + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result<ServerCertVerified, Error> { + unreachable!() + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + unreachable!() + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + unreachable!() + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { + unreachable!() + } + } + + #[derive(Debug)] + struct DummyResolvesClientCert; + + impl ResolvesClientCert for DummyResolvesClientCert { + #[cfg_attr(coverage_nightly, coverage(off))] + fn resolve( + &self, + _root_hint_subjects: &[&[u8]], + _sigschemes: &[SignatureScheme], + ) -> Option<Arc<sign::CertifiedKey>> { + unreachable!() + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn has_certs(&self) -> bool { + unreachable!() + } + } +} diff --git a/vendor/rustls/src/client/hs.rs b/vendor/rustls/src/client/hs.rs new file mode 100644 index 00000000..4669f33d --- /dev/null +++ b/vendor/rustls/src/client/hs.rs @@ -0,0 +1,1178 @@ +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; +use core::ops::Deref; + +use pki_types::ServerName; + +#[cfg(feature = "tls12")] +use super::tls12; +use super::{ResolvesClientCert, Tls12Resumption}; +use crate::SupportedCipherSuite; +#[cfg(feature = "logging")] +use crate::bs_debug; +use crate::check::inappropriate_handshake_message; +use crate::client::client_conn::ClientConnectionData; +use crate::client::common::ClientHelloDetails; +use crate::client::ech::EchState; +use crate::client::{ClientConfig, EchMode, EchStatus, tls13}; +use crate::common_state::{CommonState, HandshakeKind, KxState, State}; +use crate::conn::ConnectionRandoms; +use crate::crypto::{ActiveKeyExchange, KeyExchangeAlgorithm}; +use crate::enums::{ + AlertDescription, CertificateType, CipherSuite, ContentType, HandshakeType, ProtocolVersion, +}; +use crate::error::{Error, PeerIncompatible, PeerMisbehaved}; +use crate::hash_hs::HandshakeHashBuffer; +use crate::log::{debug, trace}; +use crate::msgs::base::Payload; +use crate::msgs::enums::{Compression, ExtensionType}; +use crate::msgs::handshake::{ + CertificateStatusRequest, ClientExtensions, ClientExtensionsInput, ClientHelloPayload, + ClientSessionTicket, EncryptedClientHello, HandshakeMessagePayload, HandshakePayload, + HelloRetryRequest, KeyShareEntry, ProtocolName, PskKeyExchangeModes, Random, ServerNamePayload, + SessionId, SupportedEcPointFormats, SupportedProtocolVersions, TransportParameters, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist; +use crate::sync::Arc; +use crate::tls13::key_schedule::KeyScheduleEarly; +use crate::verify::ServerCertVerifier; + +pub(super) type NextState<'a> = Box<dyn State<ClientConnectionData> + 'a>; +pub(super) type NextStateOrError<'a> = Result<NextState<'a>, Error>; +pub(super) type ClientContext<'a> = crate::common_state::Context<'a, ClientConnectionData>; + +struct ExpectServerHello { + input: ClientHelloInput, + transcript_buffer: HandshakeHashBuffer, + // The key schedule for sending early data. + // + // If the server accepts the PSK used for early data then + // this is used to compute the rest of the key schedule. + // Otherwise, it is thrown away. + // + // If this is `None` then we do not support early data. + early_data_key_schedule: Option<KeyScheduleEarly>, + offered_key_share: Option<Box<dyn ActiveKeyExchange>>, + suite: Option<SupportedCipherSuite>, + ech_state: Option<EchState>, +} + +struct ExpectServerHelloOrHelloRetryRequest { + next: ExpectServerHello, + extra_exts: ClientExtensionsInput<'static>, +} + +pub(super) struct ClientHelloInput { + pub(super) config: Arc<ClientConfig>, + pub(super) resuming: Option<persist::Retrieved<ClientSessionValue>>, + pub(super) random: Random, + pub(super) sent_tls13_fake_ccs: bool, + pub(super) hello: ClientHelloDetails, + pub(super) session_id: SessionId, + pub(super) server_name: ServerName<'static>, + pub(super) prev_ech_ext: Option<EncryptedClientHello>, +} + +impl ClientHelloInput { + pub(super) fn new( + server_name: ServerName<'static>, + extra_exts: &ClientExtensionsInput<'_>, + cx: &mut ClientContext<'_>, + config: Arc<ClientConfig>, + ) -> Result<Self, Error> { + let mut resuming = ClientSessionValue::retrieve(&server_name, &config, cx); + let session_id = match &mut resuming { + Some(_resuming) => { + debug!("Resuming session"); + match &mut _resuming.value { + #[cfg(feature = "tls12")] + ClientSessionValue::Tls12(inner) => { + // If we have a ticket, we use the sessionid as a signal that + // we're doing an abbreviated handshake. See section 3.4 in + // RFC5077. + if !inner.ticket().0.is_empty() { + inner.session_id = SessionId::random(config.provider.secure_random)?; + } + Some(inner.session_id) + } + _ => None, + } + } + _ => { + debug!("Not resuming any session"); + None + } + }; + + // https://tools.ietf.org/html/rfc8446#appendix-D.4 + // https://tools.ietf.org/html/draft-ietf-quic-tls-34#section-8.4 + let session_id = match session_id { + Some(session_id) => session_id, + None if cx.common.is_quic() => SessionId::empty(), + None if !config.supports_version(ProtocolVersion::TLSv1_3) => SessionId::empty(), + None => SessionId::random(config.provider.secure_random)?, + }; + + let hello = ClientHelloDetails::new( + extra_exts + .protocols + .clone() + .unwrap_or_default(), + crate::rand::random_u16(config.provider.secure_random)?, + ); + + Ok(Self { + resuming, + random: Random::new(config.provider.secure_random)?, + sent_tls13_fake_ccs: false, + hello, + session_id, + server_name, + prev_ech_ext: None, + config, + }) + } + + pub(super) fn start_handshake( + self, + extra_exts: ClientExtensionsInput<'static>, + cx: &mut ClientContext<'_>, + ) -> NextStateOrError<'static> { + let mut transcript_buffer = HandshakeHashBuffer::new(); + if self + .config + .client_auth_cert_resolver + .has_certs() + { + transcript_buffer.set_client_auth_enabled(); + } + + let key_share = if self.config.needs_key_share() { + Some(tls13::initial_key_share( + &self.config, + &self.server_name, + &mut cx.common.kx_state, + )?) + } else { + None + }; + + let ech_state = match self.config.ech_mode.as_ref() { + Some(EchMode::Enable(ech_config)) => { + Some(ech_config.state(self.server_name.clone(), &self.config)?) + } + _ => None, + }; + + emit_client_hello_for_retry( + transcript_buffer, + None, + key_share, + extra_exts, + None, + self, + cx, + ech_state, + ) + } +} + +/// Emits the initial ClientHello or a ClientHello in response to +/// a HelloRetryRequest. +/// +/// `retryreq` and `suite` are `None` if this is the initial +/// ClientHello. +fn emit_client_hello_for_retry( + mut transcript_buffer: HandshakeHashBuffer, + retryreq: Option<&HelloRetryRequest>, + key_share: Option<Box<dyn ActiveKeyExchange>>, + extra_exts: ClientExtensionsInput<'static>, + suite: Option<SupportedCipherSuite>, + mut input: ClientHelloInput, + cx: &mut ClientContext<'_>, + mut ech_state: Option<EchState>, +) -> NextStateOrError<'static> { + let config = &input.config; + // Defense in depth: the ECH state should be None if ECH is disabled based on config + // builder semantics. + let forbids_tls12 = cx.common.is_quic() || ech_state.is_some(); + + let supported_versions = SupportedProtocolVersions { + tls12: config.supports_version(ProtocolVersion::TLSv1_2) && !forbids_tls12, + tls13: config.supports_version(ProtocolVersion::TLSv1_3), + }; + + // should be unreachable thanks to config builder + assert!(supported_versions.any(|_| true)); + + let mut exts = Box::new(ClientExtensions { + // offer groups which are usable for any offered version + named_groups: Some( + config + .provider + .kx_groups + .iter() + .filter(|skxg| supported_versions.any(|v| skxg.usable_for_version(v))) + .map(|skxg| skxg.name()) + .collect(), + ), + supported_versions: Some(supported_versions), + signature_schemes: Some( + config + .verifier + .supported_verify_schemes(), + ), + extended_master_secret_request: Some(()), + certificate_status_request: Some(CertificateStatusRequest::build_ocsp()), + protocols: extra_exts.protocols.clone(), + ..Default::default() + }); + + match extra_exts.transport_parameters.clone() { + Some(TransportParameters::Quic(v)) => exts.transport_parameters = Some(v), + Some(TransportParameters::QuicDraft(v)) => exts.transport_parameters_draft = Some(v), + None => {} + }; + + if supported_versions.tls13 { + if let Some(cas_extension) = config.verifier.root_hint_subjects() { + exts.certificate_authority_names = Some(cas_extension.to_owned()); + } + } + + // Send the ECPointFormat extension only if we are proposing ECDHE + if config + .provider + .kx_groups + .iter() + .any(|skxg| skxg.name().key_exchange_algorithm() == KeyExchangeAlgorithm::ECDHE) + { + exts.ec_point_formats = Some(SupportedEcPointFormats::default()); + } + + exts.server_name = match (ech_state.as_ref(), config.enable_sni) { + // If we have ECH state we have a "cover name" to send in the outer hello + // as the SNI domain name. This happens unconditionally so we ignore the + // `enable_sni` value. That will be used later to decide what to do for + // the protected inner hello's SNI. + (Some(ech_state), _) => Some(ServerNamePayload::from(&ech_state.outer_name)), + + // If we have no ECH state, and SNI is enabled, try to use the input server_name + // for the SNI domain name. + (None, true) => match &input.server_name { + ServerName::DnsName(dns_name) => Some(ServerNamePayload::from(dns_name)), + _ => None, + }, + + // If we have no ECH state, and SNI is not enabled, there's nothing to do. + (None, false) => None, + }; + + if let Some(key_share) = &key_share { + debug_assert!(supported_versions.tls13); + let mut shares = vec![KeyShareEntry::new(key_share.group(), key_share.pub_key())]; + + if !retryreq + .map(|rr| rr.key_share.is_some()) + .unwrap_or_default() + { + // Only for the initial client hello, or a HRR that does not specify a kx group, + // see if we can send a second KeyShare for "free". We only do this if the same + // algorithm is also supported separately by our provider for this version + // (`find_kx_group` looks that up). + if let Some((component_group, component_share)) = + key_share + .hybrid_component() + .filter(|(group, _)| { + config + .find_kx_group(*group, ProtocolVersion::TLSv1_3) + .is_some() + }) + { + shares.push(KeyShareEntry::new(component_group, component_share)); + } + } + + exts.key_shares = Some(shares); + } + + if let Some(cookie) = retryreq.and_then(|hrr| hrr.cookie.as_ref()) { + exts.cookie = Some(cookie.clone()); + } + + if supported_versions.tls13 { + // We could support PSK_KE here too. Such connections don't + // have forward secrecy, and are similar to TLS1.2 resumption. + exts.preshared_key_modes = Some(PskKeyExchangeModes { + psk: false, + psk_dhe: true, + }); + } + + input.hello.offered_cert_compression = + if supported_versions.tls13 && !config.cert_decompressors.is_empty() { + exts.certificate_compression_algorithms = Some( + config + .cert_decompressors + .iter() + .map(|dec| dec.algorithm()) + .collect(), + ); + true + } else { + false + }; + + if config + .client_auth_cert_resolver + .only_raw_public_keys() + { + exts.client_certificate_types = Some(vec![CertificateType::RawPublicKey]); + } + + if config + .verifier + .requires_raw_public_keys() + { + exts.server_certificate_types = Some(vec![CertificateType::RawPublicKey]); + } + + // If this is a second client hello we're constructing in response to an HRR, and + // we've rejected ECH or sent GREASE ECH, then we need to carry forward the + // exact same ECH extension we used in the first hello. + if matches!(cx.data.ech_status, EchStatus::Rejected | EchStatus::Grease) & retryreq.is_some() { + if let Some(prev_ech_ext) = input.prev_ech_ext.take() { + exts.encrypted_client_hello = Some(prev_ech_ext); + } + } + + // Do we have a SessionID or ticket cached for this host? + let tls13_session = prepare_resumption(&input.resuming, &mut exts, suite, cx, config); + + // Extensions MAY be randomized + // but they also need to keep the same order as the previous ClientHello + exts.order_seed = input.hello.extension_order_seed; + + let mut cipher_suites: Vec<_> = config + .provider + .cipher_suites + .iter() + .filter_map(|cs| match cs.usable_for_protocol(cx.common.protocol) { + true => Some(cs.suite()), + false => None, + }) + .collect(); + + if supported_versions.tls12 { + // We don't do renegotiation at all, in fact. + cipher_suites.push(CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + } + + let mut chp_payload = ClientHelloPayload { + client_version: ProtocolVersion::TLSv1_2, + random: input.random, + session_id: input.session_id, + cipher_suites, + compression_methods: vec![Compression::Null], + extensions: exts, + }; + + let ech_grease_ext = config + .ech_mode + .as_ref() + .and_then(|mode| match mode { + EchMode::Grease(cfg) => Some(cfg.grease_ext( + config.provider.secure_random, + input.server_name.clone(), + &chp_payload, + )), + _ => None, + }); + + match (cx.data.ech_status, &mut ech_state) { + // If we haven't offered ECH, or have offered ECH but got a non-rejecting HRR, then + // we need to replace the client hello payload with an ECH client hello payload. + (EchStatus::NotOffered | EchStatus::Offered, Some(ech_state)) => { + // Replace the client hello payload with an ECH client hello payload. + chp_payload = ech_state.ech_hello(chp_payload, retryreq, &tls13_session)?; + cx.data.ech_status = EchStatus::Offered; + // Store the ECH extension in case we need to carry it forward in a subsequent hello. + input.prev_ech_ext = chp_payload + .encrypted_client_hello + .clone(); + } + // If we haven't offered ECH, and have no ECH state, then consider whether to use GREASE + // ECH. + (EchStatus::NotOffered, None) => { + if let Some(grease_ext) = ech_grease_ext { + // Add the GREASE ECH extension. + let grease_ext = grease_ext?; + chp_payload.encrypted_client_hello = Some(grease_ext.clone()); + cx.data.ech_status = EchStatus::Grease; + // Store the GREASE ECH extension in case we need to carry it forward in a + // subsequent hello. + input.prev_ech_ext = Some(grease_ext); + } + } + _ => {} + } + + // Note what extensions we sent. + input.hello.sent_extensions = chp_payload.collect_used(); + + let mut chp = HandshakeMessagePayload(HandshakePayload::ClientHello(chp_payload)); + + let tls13_early_data_key_schedule = match (ech_state.as_mut(), tls13_session) { + // If we're performing ECH and resuming, then the PSK binder will have been dealt with + // separately, and we need to take the early_data_key_schedule computed for the inner hello. + (Some(ech_state), Some(tls13_session)) => ech_state + .early_data_key_schedule + .take() + .map(|schedule| (tls13_session.suite(), schedule)), + + // When we're not doing ECH and resuming, then the PSK binder need to be filled in as + // normal. + (_, Some(tls13_session)) => Some(( + tls13_session.suite(), + tls13::fill_in_psk_binder(&tls13_session, &transcript_buffer, &mut chp), + )), + + // No early key schedule in other cases. + _ => None, + }; + + let ch = Message { + version: match retryreq { + // <https://datatracker.ietf.org/doc/html/rfc8446#section-5.1>: + // "This value MUST be set to 0x0303 for all records generated + // by a TLS 1.3 implementation ..." + Some(_) => ProtocolVersion::TLSv1_2, + // "... other than an initial ClientHello (i.e., one not + // generated after a HelloRetryRequest), where it MAY also be + // 0x0301 for compatibility purposes" + // + // (retryreq == None means we're in the "initial ClientHello" case) + None => ProtocolVersion::TLSv1_0, + }, + payload: MessagePayload::handshake(chp), + }; + + if retryreq.is_some() { + // send dummy CCS to fool middleboxes prior + // to second client hello + tls13::emit_fake_ccs(&mut input.sent_tls13_fake_ccs, cx.common); + } + + trace!("Sending ClientHello {ch:#?}"); + + transcript_buffer.add_message(&ch); + cx.common.send_msg(ch, false); + + // Calculate the hash of ClientHello and use it to derive EarlyTrafficSecret + let early_data_key_schedule = + tls13_early_data_key_schedule.map(|(resuming_suite, schedule)| { + if !cx.data.early_data.is_enabled() { + return schedule; + } + + let (transcript_buffer, random) = match &ech_state { + // When using ECH the early data key schedule is derived based on the inner + // hello transcript and random. + Some(ech_state) => ( + &ech_state.inner_hello_transcript, + &ech_state.inner_hello_random.0, + ), + None => (&transcript_buffer, &input.random.0), + }; + + tls13::derive_early_traffic_secret( + &*config.key_log, + cx, + resuming_suite.common.hash_provider, + &schedule, + &mut input.sent_tls13_fake_ccs, + transcript_buffer, + random, + ); + schedule + }); + + let next = ExpectServerHello { + input, + transcript_buffer, + early_data_key_schedule, + offered_key_share: key_share, + suite, + ech_state, + }; + + Ok(if supported_versions.tls13 && retryreq.is_none() { + Box::new(ExpectServerHelloOrHelloRetryRequest { + next, + extra_exts: extra_exts.into_owned(), + }) + } else { + Box::new(next) + }) +} + +/// Prepares `exts` and `cx` with TLS 1.2 or TLS 1.3 session +/// resumption. +/// +/// - `suite` is `None` if this is the initial ClientHello, or +/// `Some` if we're retrying in response to +/// a HelloRetryRequest. +/// +/// This function will push onto `exts` to +/// +/// (a) request a new ticket if we don't have one, +/// (b) send our TLS 1.2 ticket after retrieving an 1.2 session, +/// (c) send a request for 1.3 early data if allowed and +/// (d) send a 1.3 preshared key if we have one. +/// +/// It returns the TLS 1.3 PSKs, if any, for further processing. +fn prepare_resumption<'a>( + resuming: &'a Option<persist::Retrieved<ClientSessionValue>>, + exts: &mut ClientExtensions<'_>, + suite: Option<SupportedCipherSuite>, + cx: &mut ClientContext<'_>, + config: &ClientConfig, +) -> Option<persist::Retrieved<&'a persist::Tls13ClientSessionValue>> { + // Check whether we're resuming with a non-empty ticket. + let resuming = match resuming { + Some(resuming) if !resuming.ticket().is_empty() => resuming, + _ => { + if config.supports_version(ProtocolVersion::TLSv1_2) + && config.resumption.tls12_resumption == Tls12Resumption::SessionIdOrTickets + { + // If we don't have a ticket, request one. + exts.session_ticket = Some(ClientSessionTicket::Request); + } + return None; + } + }; + + let Some(tls13) = resuming.map(|csv| csv.tls13()) else { + // TLS 1.2; send the ticket if we have support this protocol version + if config.supports_version(ProtocolVersion::TLSv1_2) + && config.resumption.tls12_resumption == Tls12Resumption::SessionIdOrTickets + { + exts.session_ticket = Some(ClientSessionTicket::Offer(Payload::new(resuming.ticket()))); + } + return None; // TLS 1.2, so nothing to return here + }; + + if !config.supports_version(ProtocolVersion::TLSv1_3) { + return None; + } + + // If the server selected TLS 1.2, we can't resume. + let suite = match suite { + Some(SupportedCipherSuite::Tls13(suite)) => Some(suite), + #[cfg(feature = "tls12")] + Some(SupportedCipherSuite::Tls12(_)) => return None, + None => None, + }; + + // If the selected cipher suite can't select from the session's, we can't resume. + if let Some(suite) = suite { + suite.can_resume_from(tls13.suite())?; + } + + tls13::prepare_resumption(config, cx, &tls13, exts, suite.is_some()); + Some(tls13) +} + +pub(super) fn process_alpn_protocol( + common: &mut CommonState, + offered_protocols: &[ProtocolName], + selected: Option<&ProtocolName>, +) -> Result<(), Error> { + common.alpn_protocol = selected.map(ToOwned::to_owned); + + if let Some(alpn_protocol) = &common.alpn_protocol { + if !offered_protocols.contains(alpn_protocol) { + return Err(common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::SelectedUnofferedApplicationProtocol, + )); + } + } + + // RFC 9001 says: "While ALPN only specifies that servers use this alert, QUIC clients MUST + // use error 0x0178 to terminate a connection when ALPN negotiation fails." We judge that + // the user intended to use ALPN (rather than some out-of-band protocol negotiation + // mechanism) if and only if any ALPN protocols were configured. This defends against badly-behaved + // servers which accept a connection that requires an application-layer protocol they do not + // understand. + if common.is_quic() && common.alpn_protocol.is_none() && !offered_protocols.is_empty() { + return Err(common.send_fatal_alert( + AlertDescription::NoApplicationProtocol, + Error::NoApplicationProtocol, + )); + } + + debug!( + "ALPN protocol is {:?}", + common + .alpn_protocol + .as_ref() + .map(|v| bs_debug::BsDebug(v.as_ref())) + ); + Ok(()) +} + +pub(super) fn process_server_cert_type_extension( + common: &mut CommonState, + config: &ClientConfig, + server_cert_extension: Option<&CertificateType>, +) -> Result<Option<(ExtensionType, CertificateType)>, Error> { + process_cert_type_extension( + common, + config + .verifier + .requires_raw_public_keys(), + server_cert_extension.copied(), + ExtensionType::ServerCertificateType, + ) +} + +pub(super) fn process_client_cert_type_extension( + common: &mut CommonState, + config: &ClientConfig, + client_cert_extension: Option<&CertificateType>, +) -> Result<Option<(ExtensionType, CertificateType)>, Error> { + process_cert_type_extension( + common, + config + .client_auth_cert_resolver + .only_raw_public_keys(), + client_cert_extension.copied(), + ExtensionType::ClientCertificateType, + ) +} + +impl State<ClientConnectionData> for ExpectServerHello { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> NextStateOrError<'m> + where + Self: 'm, + { + let server_hello = + require_handshake_msg!(m, HandshakeType::ServerHello, HandshakePayload::ServerHello)?; + trace!("We got ServerHello {server_hello:#?}"); + + use crate::ProtocolVersion::{TLSv1_2, TLSv1_3}; + let config = &self.input.config; + let tls13_supported = config.supports_version(TLSv1_3); + + let server_version = if server_hello.legacy_version == TLSv1_2 { + server_hello + .selected_version + .unwrap_or(server_hello.legacy_version) + } else { + server_hello.legacy_version + }; + + let version = match server_version { + TLSv1_3 if tls13_supported => TLSv1_3, + TLSv1_2 if config.supports_version(TLSv1_2) => { + if cx.data.early_data.is_enabled() && cx.common.early_traffic { + // The client must fail with a dedicated error code if the server + // responds with TLS 1.2 when offering 0-RTT. + return Err(PeerMisbehaved::OfferedEarlyDataWithOldProtocolVersion.into()); + } + + if server_hello.selected_version.is_some() { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::SelectedTls12UsingTls13VersionExtension, + ) + }); + } + + TLSv1_2 + } + _ => { + let reason = match server_version { + TLSv1_2 | TLSv1_3 => PeerIncompatible::ServerTlsVersionIsDisabledByOurConfig, + _ => PeerIncompatible::ServerDoesNotSupportTls12Or13, + }; + return Err(cx + .common + .send_fatal_alert(AlertDescription::ProtocolVersion, reason)); + } + }; + + if server_hello.compression_method != Compression::Null { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::SelectedUnofferedCompression, + ) + }); + } + + let allowed_unsolicited = [ExtensionType::RenegotiationInfo]; + if self + .input + .hello + .server_sent_unsolicited_extensions(server_hello, &allowed_unsolicited) + { + return Err(cx.common.send_fatal_alert( + AlertDescription::UnsupportedExtension, + PeerMisbehaved::UnsolicitedServerHelloExtension, + )); + } + + cx.common.negotiated_version = Some(version); + + // Extract ALPN protocol + if !cx.common.is_tls13() { + process_alpn_protocol( + cx.common, + &self.input.hello.alpn_protocols, + server_hello + .selected_protocol + .as_ref() + .map(|s| s.as_ref()), + )?; + } + + // If ECPointFormats extension is supplied by the server, it must contain + // Uncompressed. But it's allowed to be omitted. + if let Some(point_fmts) = &server_hello.ec_point_formats { + if !point_fmts.uncompressed { + return Err(cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerMisbehaved::ServerHelloMustOfferUncompressedEcPoints, + )); + } + } + + let suite = config + .find_cipher_suite(server_hello.cipher_suite) + .ok_or_else(|| { + cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerMisbehaved::SelectedUnofferedCipherSuite, + ) + })?; + + if version != suite.version().version { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::SelectedUnusableCipherSuiteForVersion, + ) + }); + } + + match self.suite { + Some(prev_suite) if prev_suite != suite => { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::SelectedDifferentCipherSuiteAfterRetry, + ) + }); + } + _ => { + debug!("Using ciphersuite {suite:?}"); + self.suite = Some(suite); + cx.common.suite = Some(suite); + } + } + + // Start our handshake hash, and input the server-hello. + let mut transcript = self + .transcript_buffer + .start_hash(suite.hash_provider()); + transcript.add_message(&m); + + let randoms = ConnectionRandoms::new(self.input.random, server_hello.random); + // For TLS1.3, start message encryption using + // handshake_traffic_secret. + match suite { + SupportedCipherSuite::Tls13(suite) => { + tls13::handle_server_hello( + cx, + server_hello, + randoms, + suite, + transcript, + self.early_data_key_schedule, + // We always send a key share when TLS 1.3 is enabled. + self.offered_key_share.unwrap(), + &m, + self.ech_state, + self.input, + ) + } + #[cfg(feature = "tls12")] + SupportedCipherSuite::Tls12(suite) => tls12::CompleteServerHelloHandling { + randoms, + transcript, + input: self.input, + } + .handle_server_hello(cx, suite, server_hello, tls13_supported), + } + } + + fn into_owned(self: Box<Self>) -> NextState<'static> { + self + } +} + +impl ExpectServerHelloOrHelloRetryRequest { + fn into_expect_server_hello(self) -> NextState<'static> { + Box::new(self.next) + } + + fn handle_hello_retry_request( + mut self, + cx: &mut ClientContext<'_>, + m: Message<'_>, + ) -> NextStateOrError<'static> { + let hrr = require_handshake_msg!( + m, + HandshakeType::HelloRetryRequest, + HandshakePayload::HelloRetryRequest + )?; + trace!("Got HRR {hrr:?}"); + + cx.common.check_aligned_handshake()?; + + // We always send a key share when TLS 1.3 is enabled. + let offered_key_share = self.next.offered_key_share.unwrap(); + + // A retry request is illegal if it contains no cookie and asks for + // retry of a group we already sent. + let config = &self.next.input.config; + + if let (None, Some(req_group)) = (&hrr.cookie, hrr.key_share) { + let offered_hybrid = offered_key_share + .hybrid_component() + .and_then(|(group_name, _)| { + config.find_kx_group(group_name, ProtocolVersion::TLSv1_3) + }) + .map(|skxg| skxg.name()); + + if req_group == offered_key_share.group() || Some(req_group) == offered_hybrid { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::IllegalHelloRetryRequestWithOfferedGroup, + ) + }); + } + } + + // Or has an empty cookie. + if let Some(cookie) = &hrr.cookie { + if cookie.0.is_empty() { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::IllegalHelloRetryRequestWithEmptyCookie, + ) + }); + } + } + + // Or asks us to change nothing. + if hrr.cookie.is_none() && hrr.key_share.is_none() { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::IllegalHelloRetryRequestWithNoChanges, + ) + }); + } + + // Or does not echo the session_id from our ClientHello: + // + // > the HelloRetryRequest has the same format as a ServerHello message, + // > and the legacy_version, legacy_session_id_echo, cipher_suite, and + // > legacy_compression_method fields have the same meaning + // <https://www.rfc-editor.org/rfc/rfc8446#section-4.1.4> + // + // and + // + // > A client which receives a legacy_session_id_echo field that does not + // > match what it sent in the ClientHello MUST abort the handshake with an + // > "illegal_parameter" alert. + // <https://www.rfc-editor.org/rfc/rfc8446#section-4.1.3> + if hrr.session_id != self.next.input.session_id { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::IllegalHelloRetryRequestWithWrongSessionId, + ) + }); + } + + // Or asks us to talk a protocol we didn't offer, or doesn't support HRR at all. + match hrr.supported_versions { + Some(ProtocolVersion::TLSv1_3) => { + cx.common.negotiated_version = Some(ProtocolVersion::TLSv1_3); + } + _ => { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::IllegalHelloRetryRequestWithUnsupportedVersion, + ) + }); + } + } + + // Or asks us to use a ciphersuite we didn't offer. + let Some(cs) = config.find_cipher_suite(hrr.cipher_suite) else { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::IllegalHelloRetryRequestWithUnofferedCipherSuite, + ) + }); + }; + + // Or offers ECH related extensions when we didn't offer ECH. + if cx.data.ech_status == EchStatus::NotOffered && hrr.encrypted_client_hello.is_some() { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::UnsupportedExtension, + PeerMisbehaved::IllegalHelloRetryRequestWithInvalidEch, + ) + }); + } + + // HRR selects the ciphersuite. + cx.common.suite = Some(cs); + cx.common.handshake_kind = Some(HandshakeKind::FullWithHelloRetryRequest); + + // If we offered ECH, we need to confirm that the server accepted it. + match (self.next.ech_state.as_ref(), cs.tls13()) { + (Some(ech_state), Some(tls13_cs)) => { + if !ech_state.confirm_hrr_acceptance(hrr, tls13_cs, cx.common)? { + // If the server did not confirm, then note the new ECH status but + // continue the handshake. We will abort with an ECH required error + // at the end. + cx.data.ech_status = EchStatus::Rejected; + } + } + (Some(_), None) => { + unreachable!("ECH state should only be set when TLS 1.3 was negotiated") + } + _ => {} + }; + + // This is the draft19 change where the transcript became a tree + let transcript = self + .next + .transcript_buffer + .start_hash(cs.hash_provider()); + let mut transcript_buffer = transcript.into_hrr_buffer(); + transcript_buffer.add_message(&m); + + // If we offered ECH and the server accepted, we also need to update the separate + // ECH transcript with the hello retry request message. + if let Some(ech_state) = self.next.ech_state.as_mut() { + ech_state.transcript_hrr_update(cs.hash_provider(), &m); + } + + // Early data is not allowed after HelloRetryrequest + if cx.data.early_data.is_enabled() { + cx.data.early_data.rejected(); + } + + let key_share = match hrr.key_share { + Some(group) if group != offered_key_share.group() => { + let Some(skxg) = config.find_kx_group(group, ProtocolVersion::TLSv1_3) else { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::IllegalHelloRetryRequestWithUnofferedNamedGroup, + )); + }; + + cx.common.kx_state = KxState::Start(skxg); + skxg.start()? + } + _ => offered_key_share, + }; + + emit_client_hello_for_retry( + transcript_buffer, + Some(hrr), + Some(key_share), + self.extra_exts, + Some(cs), + self.next.input, + cx, + self.next.ech_state, + ) + } +} + +impl State<ClientConnectionData> for ExpectServerHelloOrHelloRetryRequest { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::ServerHello(..)), + .. + } => self + .into_expect_server_hello() + .handle(cx, m), + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::HelloRetryRequest(..)), + .. + } => self.handle_hello_retry_request(cx, m), + payload => Err(inappropriate_handshake_message( + &payload, + &[ContentType::Handshake], + &[HandshakeType::ServerHello, HandshakeType::HelloRetryRequest], + )), + } + } + + fn into_owned(self: Box<Self>) -> NextState<'static> { + self + } +} + +fn process_cert_type_extension( + common: &mut CommonState, + client_expects: bool, + server_negotiated: Option<CertificateType>, + extension_type: ExtensionType, +) -> Result<Option<(ExtensionType, CertificateType)>, Error> { + match (client_expects, server_negotiated) { + (true, Some(CertificateType::RawPublicKey)) => { + Ok(Some((extension_type, CertificateType::RawPublicKey))) + } + (true, _) => Err(common.send_fatal_alert( + AlertDescription::HandshakeFailure, + Error::PeerIncompatible(PeerIncompatible::IncorrectCertificateTypeExtension), + )), + (_, Some(CertificateType::RawPublicKey)) => { + unreachable!("Caught by `PeerMisbehaved::UnsolicitedEncryptedExtension`") + } + (_, _) => Ok(None), + } +} + +pub(super) enum ClientSessionValue { + Tls13(persist::Tls13ClientSessionValue), + #[cfg(feature = "tls12")] + Tls12(persist::Tls12ClientSessionValue), +} + +impl ClientSessionValue { + fn retrieve( + server_name: &ServerName<'static>, + config: &ClientConfig, + cx: &mut ClientContext<'_>, + ) -> Option<persist::Retrieved<Self>> { + let found = config + .resumption + .store + .take_tls13_ticket(server_name) + .map(ClientSessionValue::Tls13) + .or_else(|| { + #[cfg(feature = "tls12")] + { + config + .resumption + .store + .tls12_session(server_name) + .map(ClientSessionValue::Tls12) + } + + #[cfg(not(feature = "tls12"))] + None + }) + .and_then(|resuming| { + resuming.compatible_config(&config.verifier, &config.client_auth_cert_resolver) + }) + .and_then(|resuming| { + let now = config + .current_time() + .map_err(|_err| debug!("Could not get current time: {_err}")) + .ok()?; + + let retrieved = persist::Retrieved::new(resuming, now); + match retrieved.has_expired() { + false => Some(retrieved), + true => None, + } + }) + .or_else(|| { + debug!("No cached session for {server_name:?}"); + None + }); + + if let Some(resuming) = &found { + if cx.common.is_quic() { + cx.common.quic.params = resuming + .tls13() + .map(|v| v.quic_params()); + } + } + + found + } + + fn common(&self) -> &persist::ClientSessionCommon { + match self { + Self::Tls13(inner) => &inner.common, + #[cfg(feature = "tls12")] + Self::Tls12(inner) => &inner.common, + } + } + + fn tls13(&self) -> Option<&persist::Tls13ClientSessionValue> { + match self { + Self::Tls13(v) => Some(v), + #[cfg(feature = "tls12")] + Self::Tls12(_) => None, + } + } + + fn compatible_config( + self, + server_cert_verifier: &Arc<dyn ServerCertVerifier>, + client_creds: &Arc<dyn ResolvesClientCert>, + ) -> Option<Self> { + match &self { + Self::Tls13(v) => v + .compatible_config(server_cert_verifier, client_creds) + .then_some(self), + #[cfg(feature = "tls12")] + Self::Tls12(v) => v + .compatible_config(server_cert_verifier, client_creds) + .then_some(self), + } + } +} + +impl Deref for ClientSessionValue { + type Target = persist::ClientSessionCommon; + + fn deref(&self) -> &Self::Target { + self.common() + } +} diff --git a/vendor/rustls/src/client/test.rs b/vendor/rustls/src/client/test.rs new file mode 100644 index 00000000..f4ea9580 --- /dev/null +++ b/vendor/rustls/src/client/test.rs @@ -0,0 +1,712 @@ +#![cfg(any(feature = "ring", feature = "aws_lc_rs"))] +use core::sync::atomic::{AtomicBool, Ordering}; +use std::prelude::v1::*; +use std::vec; + +use pki_types::{CertificateDer, ServerName}; + +use crate::client::{ClientConfig, ClientConnection, Resumption, Tls12Resumption}; +use crate::crypto::CryptoProvider; +use crate::enums::{CipherSuite, ProtocolVersion, SignatureScheme}; +use crate::msgs::base::PayloadU16; +use crate::msgs::codec::Reader; +use crate::msgs::enums::{Compression, NamedGroup}; +use crate::msgs::handshake::{ + ClientHelloPayload, HandshakeMessagePayload, HandshakePayload, HelloRetryRequest, Random, + ServerHelloPayload, SessionId, +}; +use crate::msgs::message::{Message, MessagePayload, OutboundOpaqueMessage}; +use crate::sync::Arc; +use crate::{Error, PeerIncompatible, PeerMisbehaved, RootCertStore}; + +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use std::sync::OnceLock; + + use super::super::*; + use crate::client::AlwaysResolvesClientRawPublicKeys; + use crate::crypto::cipher::MessageEncrypter; + use crate::crypto::tls13::OkmBlock; + use crate::enums::CertificateType; + use crate::msgs::base::PayloadU8; + use crate::msgs::enums::ECCurveType; + use crate::msgs::handshake::{ + CertificateChain, EcParameters, HelloRetryRequestExtensions, KeyShareEntry, + ServerEcdhParams, ServerExtensions, ServerKeyExchange, ServerKeyExchangeParams, + ServerKeyExchangePayload, + }; + use crate::msgs::message::PlainMessage; + use crate::pki_types::pem::PemObject; + use crate::pki_types::{PrivateKeyDer, UnixTime}; + use crate::sign::CertifiedKey; + use crate::tls13::key_schedule::{derive_traffic_iv, derive_traffic_key}; + use crate::verify::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; + use crate::{DigitallySignedStruct, DistinguishedName, KeyLog, version}; + + /// Tests that session_ticket(35) extension + /// is not sent if the client does not support TLS 1.2. + #[test] + fn test_no_session_ticket_request_on_tls_1_3() { + let mut config = + ClientConfig::builder_with_provider(super::provider::default_provider().into()) + .with_protocol_versions(&[&version::TLS13]) + .unwrap() + .with_root_certificates(roots()) + .with_no_client_auth(); + config.resumption = Resumption::in_memory_sessions(128) + .tls12_resumption(Tls12Resumption::SessionIdOrTickets); + let ch = client_hello_sent_for_config(config).unwrap(); + assert!(ch.extensions.session_ticket.is_none()); + } + + #[test] + fn test_no_renegotiation_scsv_on_tls_1_3() { + let ch = client_hello_sent_for_config( + ClientConfig::builder_with_provider(super::provider::default_provider().into()) + .with_protocol_versions(&[&version::TLS13]) + .unwrap() + .with_root_certificates(roots()) + .with_no_client_auth(), + ) + .unwrap(); + assert!( + !ch.cipher_suites + .contains(&CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV) + ); + } + + #[test] + fn test_client_does_not_offer_sha1() { + for version in crate::ALL_VERSIONS { + let config = + ClientConfig::builder_with_provider(super::provider::default_provider().into()) + .with_protocol_versions(&[version]) + .unwrap() + .with_root_certificates(roots()) + .with_no_client_auth(); + let ch = client_hello_sent_for_config(config).unwrap(); + assert!( + !ch.extensions + .signature_schemes + .as_ref() + .unwrap() + .contains(&SignatureScheme::RSA_PKCS1_SHA1), + "sha1 unexpectedly offered" + ); + } + } + + #[test] + fn test_client_rejects_hrr_with_varied_session_id() { + let config = + ClientConfig::builder_with_provider(super::provider::default_provider().into()) + .with_safe_default_protocol_versions() + .unwrap() + .with_root_certificates(roots()) + .with_no_client_auth(); + let mut conn = + ClientConnection::new(config.into(), ServerName::try_from("localhost").unwrap()) + .unwrap(); + let mut sent = Vec::new(); + conn.write_tls(&mut sent).unwrap(); + + // server replies with HRR, but does not echo `session_id` as required. + let hrr = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::HelloRetryRequest(HelloRetryRequest { + cipher_suite: CipherSuite::TLS13_AES_128_GCM_SHA256, + legacy_version: ProtocolVersion::TLSv1_2, + session_id: SessionId::empty(), + extensions: HelloRetryRequestExtensions { + cookie: Some(PayloadU16::new(vec![1, 2, 3, 4])), + ..HelloRetryRequestExtensions::default() + }, + }), + )), + }; + + conn.read_tls(&mut hrr.into_wire_bytes().as_slice()) + .unwrap(); + assert_eq!( + conn.process_new_packets().unwrap_err(), + PeerMisbehaved::IllegalHelloRetryRequestWithWrongSessionId.into() + ); + } + + #[cfg(feature = "tls12")] + #[test] + fn test_client_rejects_no_extended_master_secret_extension_when_require_ems_or_fips() { + let mut config = + ClientConfig::builder_with_provider(super::provider::default_provider().into()) + .with_safe_default_protocol_versions() + .unwrap() + .with_root_certificates(roots()) + .with_no_client_auth(); + if config.provider.fips() { + assert!(config.require_ems); + } else { + config.require_ems = true; + } + + let config = Arc::new(config); + let mut conn = + ClientConnection::new(config.clone(), ServerName::try_from("localhost").unwrap()) + .unwrap(); + let mut sent = Vec::new(); + conn.write_tls(&mut sent).unwrap(); + + let sh = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ServerHello(ServerHelloPayload { + random: Random::new(config.provider.secure_random).unwrap(), + compression_method: Compression::Null, + cipher_suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + legacy_version: ProtocolVersion::TLSv1_2, + session_id: SessionId::empty(), + extensions: Box::new(ServerExtensions::default()), + }), + )), + }; + conn.read_tls(&mut sh.into_wire_bytes().as_slice()) + .unwrap(); + + assert_eq!( + conn.process_new_packets(), + Err(PeerIncompatible::ExtendedMasterSecretExtensionRequired.into()) + ); + } + + #[test] + fn cas_extension_in_client_hello_if_server_verifier_requests_it() { + let cas_sending_server_verifier = + ServerVerifierWithAuthorityNames(vec![DistinguishedName::from(b"hello".to_vec())]); + + for (protocol_version, cas_extension_expected) in + [(&version::TLS12, false), (&version::TLS13, true)] + { + let client_hello = client_hello_sent_for_config( + ClientConfig::builder_with_provider(super::provider::default_provider().into()) + .with_protocol_versions(&[protocol_version]) + .unwrap() + .dangerous() + .with_custom_certificate_verifier(Arc::new(cas_sending_server_verifier.clone())) + .with_no_client_auth(), + ) + .unwrap(); + assert_eq!( + client_hello + .extensions + .certificate_authority_names + .is_some(), + cas_extension_expected + ); + } + } + + /// Regression test for <https://github.com/seanmonstar/reqwest/issues/2191> + #[cfg(feature = "tls12")] + #[test] + fn test_client_with_custom_verifier_can_accept_ecdsa_sha1_signatures() { + let verifier = Arc::new(ExpectSha1EcdsaVerifier::default()); + let config = ClientConfig::builder_with_provider(x25519_provider().into()) + .with_safe_default_protocol_versions() + .unwrap() + .dangerous() + .with_custom_certificate_verifier(verifier.clone()) + .with_no_client_auth(); + + let mut conn = + ClientConnection::new(config.into(), ServerName::try_from("localhost").unwrap()) + .unwrap(); + let mut sent = Vec::new(); + conn.write_tls(&mut sent).unwrap(); + + let sh = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ServerHello(ServerHelloPayload { + random: Random([0u8; 32]), + compression_method: Compression::Null, + cipher_suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + legacy_version: ProtocolVersion::TLSv1_2, + session_id: SessionId::empty(), + extensions: Box::new(ServerExtensions { + extended_master_secret_ack: Some(()), + ..ServerExtensions::default() + }), + }), + )), + }; + conn.read_tls(&mut sh.into_wire_bytes().as_slice()) + .unwrap(); + conn.process_new_packets().unwrap(); + + let cert = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::Certificate(CertificateChain(vec![CertificateDer::from( + &b"does not matter"[..], + )])), + )), + }; + conn.read_tls(&mut cert.into_wire_bytes().as_slice()) + .unwrap(); + conn.process_new_packets().unwrap(); + + let server_kx = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ServerKeyExchange(ServerKeyExchangePayload::Known( + ServerKeyExchange { + dss: DigitallySignedStruct::new( + SignatureScheme::ECDSA_SHA1_Legacy, + b"also does not matter".to_vec(), + ), + params: ServerKeyExchangeParams::Ecdh(ServerEcdhParams { + curve_params: EcParameters { + curve_type: ECCurveType::NamedCurve, + named_group: NamedGroup::X25519, + }, + public: PayloadU8::new(vec![0xab; 32]), + }), + }, + )), + )), + }; + conn.read_tls(&mut server_kx.into_wire_bytes().as_slice()) + .unwrap(); + conn.process_new_packets().unwrap(); + + let server_done = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ServerHelloDone, + )), + }; + conn.read_tls(&mut server_done.into_wire_bytes().as_slice()) + .unwrap(); + conn.process_new_packets().unwrap(); + + assert!( + verifier + .seen_sha1_signature + .load(Ordering::SeqCst) + ); + } + + #[derive(Debug, Default)] + struct ExpectSha1EcdsaVerifier { + seen_sha1_signature: AtomicBool, + } + + impl ServerCertVerifier for ExpectSha1EcdsaVerifier { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result<ServerCertVerified, Error> { + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + assert_eq!(dss.scheme, SignatureScheme::ECDSA_SHA1_Legacy); + self.seen_sha1_signature + .store(true, Ordering::SeqCst); + Ok(HandshakeSignatureValid::assertion()) + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + todo!() + } + + fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { + vec![SignatureScheme::ECDSA_SHA1_Legacy] + } + } + + #[test] + fn test_client_requiring_rpk_rejects_server_that_only_offers_x509_id_by_omission() { + assert_eq!( + client_requiring_rpk_receives_server_ee(ServerExtensions::default()), + Err(PeerIncompatible::IncorrectCertificateTypeExtension.into()) + ); + } + + #[test] + fn test_client_requiring_rpk_rejects_server_that_only_offers_x509_id() { + assert_eq!( + client_requiring_rpk_receives_server_ee(ServerExtensions { + server_certificate_type: Some(CertificateType::X509), + ..ServerExtensions::default() + }), + Err(PeerIncompatible::IncorrectCertificateTypeExtension.into()) + ); + } + + #[test] + fn test_client_requiring_rpk_rejects_server_that_only_demands_x509_by_omission() { + assert_eq!( + client_requiring_rpk_receives_server_ee(ServerExtensions { + server_certificate_type: Some(CertificateType::RawPublicKey), + ..ServerExtensions::default() + }), + Err(PeerIncompatible::IncorrectCertificateTypeExtension.into()) + ); + } + + #[test] + fn test_client_requiring_rpk_rejects_server_that_only_demands_x509() { + assert_eq!( + client_requiring_rpk_receives_server_ee(ServerExtensions { + client_certificate_type: Some(CertificateType::X509), + server_certificate_type: Some(CertificateType::RawPublicKey), + ..ServerExtensions::default() + }), + Err(PeerIncompatible::IncorrectCertificateTypeExtension.into()) + ); + } + + #[test] + fn test_client_requiring_rpk_accepts_rpk_server() { + assert_eq!( + client_requiring_rpk_receives_server_ee(ServerExtensions { + client_certificate_type: Some(CertificateType::RawPublicKey), + server_certificate_type: Some(CertificateType::RawPublicKey), + ..ServerExtensions::default() + }), + Ok(()) + ); + } + + fn client_requiring_rpk_receives_server_ee( + encrypted_extensions: ServerExtensions<'_>, + ) -> Result<(), Error> { + let fake_server_crypto = Arc::new(FakeServerCrypto::new()); + let mut conn = ClientConnection::new( + client_config_for_rpk(fake_server_crypto.clone()).into(), + ServerName::try_from("localhost").unwrap(), + ) + .unwrap(); + let mut sent = Vec::new(); + conn.write_tls(&mut sent).unwrap(); + + let sh = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ServerHello(ServerHelloPayload { + random: Random([0; 32]), + compression_method: Compression::Null, + cipher_suite: CipherSuite::TLS13_AES_128_GCM_SHA256, + legacy_version: ProtocolVersion::TLSv1_3, + session_id: SessionId::empty(), + extensions: Box::new(ServerExtensions { + key_share: Some(KeyShareEntry { + group: NamedGroup::X25519, + payload: PayloadU16::new(vec![0xaa; 32]), + }), + ..ServerExtensions::default() + }), + }), + )), + }; + conn.read_tls(&mut sh.into_wire_bytes().as_slice()) + .unwrap(); + conn.process_new_packets().unwrap(); + + let ee = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::EncryptedExtensions(Box::new(encrypted_extensions)), + )), + }; + + let mut encrypter = fake_server_crypto.server_handshake_encrypter(); + let enc_ee = encrypter + .encrypt(PlainMessage::from(ee).borrow_outbound(), 0) + .unwrap(); + conn.read_tls(&mut enc_ee.encode().as_slice()) + .unwrap(); + conn.process_new_packets().map(|_| ()) + } + + fn client_config_for_rpk(key_log: Arc<dyn KeyLog>) -> ClientConfig { + let mut config = ClientConfig::builder_with_provider(x25519_provider().into()) + .with_protocol_versions(&[&version::TLS13]) + .unwrap() + .dangerous() + .with_custom_certificate_verifier(Arc::new(ServerVerifierRequiringRpk)) + .with_client_cert_resolver(Arc::new(AlwaysResolvesClientRawPublicKeys::new(Arc::new( + client_certified_key(), + )))); + config.key_log = key_log; + config + } + + fn client_certified_key() -> CertifiedKey { + let key = super::provider::default_provider() + .key_provider + .load_private_key(client_key()) + .unwrap(); + let public_key_as_cert = vec![CertificateDer::from( + key.public_key() + .unwrap() + .as_ref() + .to_vec(), + )]; + CertifiedKey::new(public_key_as_cert, key) + } + + fn client_key() -> PrivateKeyDer<'static> { + PrivateKeyDer::from_pem_reader( + &mut include_bytes!("../../../test-ca/rsa-2048/client.key").as_slice(), + ) + .unwrap() + } + + fn x25519_provider() -> CryptoProvider { + // ensures X25519 is offered irrespective of cfg(feature = "fips"), which eases + // creation of fake server messages. + CryptoProvider { + kx_groups: vec![super::provider::kx_group::X25519], + ..super::provider::default_provider() + } + } + + #[derive(Clone, Debug)] + struct ServerVerifierWithAuthorityNames(Vec<DistinguishedName>); + + impl ServerCertVerifier for ServerVerifierWithAuthorityNames { + fn root_hint_subjects(&self) -> Option<&[DistinguishedName]> { + Some(self.0.as_slice()) + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result<ServerCertVerified, Error> { + unreachable!() + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + unreachable!() + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + unreachable!() + } + + fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { + vec![SignatureScheme::RSA_PKCS1_SHA1] + } + } + + #[derive(Debug)] + struct ServerVerifierRequiringRpk; + + impl ServerCertVerifier for ServerVerifierRequiringRpk { + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result<ServerCertVerified, Error> { + todo!() + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + todo!() + } + + #[cfg_attr(coverage_nightly, coverage(off))] + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + todo!() + } + + fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { + vec![SignatureScheme::RSA_PKCS1_SHA1] + } + + fn requires_raw_public_keys(&self) -> bool { + true + } + } + + #[derive(Debug)] + struct FakeServerCrypto { + server_handshake_secret: OnceLock<Vec<u8>>, + } + + impl FakeServerCrypto { + fn new() -> Self { + Self { + server_handshake_secret: OnceLock::new(), + } + } + + fn server_handshake_encrypter(&self) -> Box<dyn MessageEncrypter> { + let cipher_suite = super::provider::cipher_suite::TLS13_AES_128_GCM_SHA256 + .tls13() + .unwrap(); + + let secret = self + .server_handshake_secret + .get() + .unwrap(); + + let expander = cipher_suite + .hkdf_provider + .expander_for_okm(&OkmBlock::new(secret)); + + // Derive Encrypter + let key = derive_traffic_key(expander.as_ref(), cipher_suite.aead_alg); + let iv = derive_traffic_iv(expander.as_ref()); + cipher_suite.aead_alg.encrypter(key, iv) + } + } + + impl KeyLog for FakeServerCrypto { + fn will_log(&self, _label: &str) -> bool { + true + } + + fn log(&self, label: &str, _client_random: &[u8], secret: &[u8]) { + if label == "SERVER_HANDSHAKE_TRAFFIC_SECRET" { + self.server_handshake_secret + .set(secret.to_vec()) + .unwrap(); + } + } + } +} + +// invalid with fips, as we can't offer X25519 separately +#[cfg(all( + feature = "aws-lc-rs", + feature = "prefer-post-quantum", + not(feature = "fips") +))] +#[test] +fn hybrid_kx_component_share_offered_if_supported_separately() { + let ch = client_hello_sent_for_config( + ClientConfig::builder_with_provider(crate::crypto::aws_lc_rs::default_provider().into()) + .with_safe_default_protocol_versions() + .unwrap() + .with_root_certificates(roots()) + .with_no_client_auth(), + ) + .unwrap(); + + let key_shares = ch + .extensions + .key_shares + .as_ref() + .unwrap(); + assert_eq!(key_shares.len(), 2); + assert_eq!(key_shares[0].group, NamedGroup::X25519MLKEM768); + assert_eq!(key_shares[1].group, NamedGroup::X25519); +} + +#[cfg(feature = "aws-lc-rs")] +#[test] +fn hybrid_kx_component_share_not_offered_unless_supported_separately() { + use crate::crypto::aws_lc_rs; + let provider = CryptoProvider { + kx_groups: vec![aws_lc_rs::kx_group::X25519MLKEM768], + ..aws_lc_rs::default_provider() + }; + let ch = client_hello_sent_for_config( + ClientConfig::builder_with_provider(provider.into()) + .with_safe_default_protocol_versions() + .unwrap() + .with_root_certificates(roots()) + .with_no_client_auth(), + ) + .unwrap(); + + let key_shares = ch + .extensions + .key_shares + .as_ref() + .unwrap(); + assert_eq!(key_shares.len(), 1); + assert_eq!(key_shares[0].group, NamedGroup::X25519MLKEM768); +} + +fn client_hello_sent_for_config(config: ClientConfig) -> Result<ClientHelloPayload, Error> { + let mut conn = + ClientConnection::new(config.into(), ServerName::try_from("localhost").unwrap())?; + let mut bytes = Vec::new(); + conn.write_tls(&mut bytes).unwrap(); + + let message = OutboundOpaqueMessage::read(&mut Reader::init(&bytes)) + .unwrap() + .into_plain_message(); + + match Message::try_from(message).unwrap() { + Message { + payload: + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::ClientHello(ch)), + .. + }, + .. + } => Ok(ch), + other => panic!("unexpected message {other:?}"), + } +} + +fn roots() -> RootCertStore { + let mut r = RootCertStore::empty(); + r.add(CertificateDer::from_slice(include_bytes!( + "../../../test-ca/rsa-2048/ca.der" + ))) + .unwrap(); + r +} diff --git a/vendor/rustls/src/client/tls12.rs b/vendor/rustls/src/client/tls12.rs new file mode 100644 index 00000000..0fc5acee --- /dev/null +++ b/vendor/rustls/src/client/tls12.rs @@ -0,0 +1,1372 @@ +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; + +use pki_types::ServerName; +pub(super) use server_hello::CompleteServerHelloHandling; +use subtle::ConstantTimeEq; + +use super::client_conn::ClientConnectionData; +use super::hs::ClientContext; +use crate::ConnectionTrafficSecrets; +use crate::check::{inappropriate_handshake_message, inappropriate_message}; +use crate::client::common::{ClientAuthDetails, ServerCertDetails}; +use crate::client::{ClientConfig, hs}; +use crate::common_state::{CommonState, HandshakeKind, KxState, Side, State}; +use crate::conn::ConnectionRandoms; +use crate::conn::kernel::{Direction, KernelContext, KernelState}; +use crate::crypto::KeyExchangeAlgorithm; +use crate::enums::{AlertDescription, ContentType, HandshakeType, ProtocolVersion}; +use crate::error::{Error, InvalidMessage, PeerIncompatible, PeerMisbehaved}; +use crate::hash_hs::HandshakeHash; +use crate::log::{debug, trace, warn}; +use crate::msgs::base::{Payload, PayloadU8, PayloadU16}; +use crate::msgs::ccs::ChangeCipherSpecPayload; +use crate::msgs::handshake::{ + CertificateChain, ClientDhParams, ClientEcdhParams, ClientKeyExchangeParams, + HandshakeMessagePayload, HandshakePayload, NewSessionTicketPayload, + NewSessionTicketPayloadTls13, ServerKeyExchangeParams, SessionId, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist; +use crate::sign::Signer; +use crate::suites::{PartiallyExtractedSecrets, SupportedCipherSuite}; +use crate::sync::Arc; +use crate::tls12::{self, ConnectionSecrets, Tls12CipherSuite}; +use crate::verify::{self, DigitallySignedStruct}; + +mod server_hello { + use super::*; + use crate::client::hs::{ClientHelloInput, ClientSessionValue}; + use crate::msgs::handshake::ServerHelloPayload; + + pub(in crate::client) struct CompleteServerHelloHandling { + pub(in crate::client) randoms: ConnectionRandoms, + pub(in crate::client) transcript: HandshakeHash, + pub(in crate::client) input: ClientHelloInput, + } + + impl CompleteServerHelloHandling { + pub(in crate::client) fn handle_server_hello( + mut self, + cx: &mut ClientContext<'_>, + suite: &'static Tls12CipherSuite, + server_hello: &ServerHelloPayload, + tls13_supported: bool, + ) -> hs::NextStateOrError<'static> { + self.randoms + .server + .clone_from_slice(&server_hello.random.0[..]); + + // Look for TLS1.3 downgrade signal in server random + // both the server random and TLS12_DOWNGRADE_SENTINEL are + // public values and don't require constant time comparison + let has_downgrade_marker = self.randoms.server[24..] == tls12::DOWNGRADE_SENTINEL; + if tls13_supported && has_downgrade_marker { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::AttemptedDowngradeToTls12WhenTls13IsSupported, + ) + }); + } + + // If we didn't have an input session to resume, and we sent a session ID, + // that implies we sent a TLS 1.3 legacy_session_id for compatibility purposes. + // In this instance since we're now continuing a TLS 1.2 handshake the server + // should not have echoed it back: it's a randomly generated session ID it couldn't + // have known. + if self.input.resuming.is_none() + && !self.input.session_id.is_empty() + && self.input.session_id == server_hello.session_id + { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::ServerEchoedCompatibilitySessionId, + ) + }); + } + + let ClientHelloInput { + config, + server_name, + .. + } = self.input; + + let resuming_session = self + .input + .resuming + .and_then(|resuming| match resuming.value { + ClientSessionValue::Tls12(inner) => Some(inner), + ClientSessionValue::Tls13(_) => None, + }); + + // Doing EMS? + let using_ems = server_hello + .extended_master_secret_ack + .is_some(); + if config.require_ems && !using_ems { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::ExtendedMasterSecretExtensionRequired, + ) + }); + } + + // Might the server send a ticket? + let must_issue_new_ticket = if server_hello + .session_ticket_ack + .is_some() + { + debug!("Server supports tickets"); + true + } else { + false + }; + + // Might the server send a CertificateStatus between Certificate and + // ServerKeyExchange? + let may_send_cert_status = server_hello + .certificate_status_request_ack + .is_some(); + if may_send_cert_status { + debug!("Server may staple OCSP response"); + } + + // See if we're successfully resuming. + if let Some(resuming) = resuming_session { + if resuming.session_id == server_hello.session_id { + debug!("Server agreed to resume"); + + // Is the server telling lies about the ciphersuite? + if resuming.suite() != suite { + return Err(PeerMisbehaved::ResumptionOfferedWithVariedCipherSuite.into()); + } + + // And about EMS support? + if resuming.extended_ms() != using_ems { + return Err(PeerMisbehaved::ResumptionOfferedWithVariedEms.into()); + } + + let secrets = + ConnectionSecrets::new_resume(self.randoms, suite, resuming.secret()); + config.key_log.log( + "CLIENT_RANDOM", + &secrets.randoms.client, + &secrets.master_secret, + ); + cx.common + .start_encryption_tls12(&secrets, Side::Client); + + // Since we're resuming, we verified the certificate and + // proof of possession in the prior session. + cx.common.peer_certificates = Some( + resuming + .server_cert_chain() + .clone() + .into_owned(), + ); + cx.common.handshake_kind = Some(HandshakeKind::Resumed); + let cert_verified = verify::ServerCertVerified::assertion(); + let sig_verified = verify::HandshakeSignatureValid::assertion(); + + return if must_issue_new_ticket { + Ok(Box::new(ExpectNewTicket { + config, + secrets, + resuming_session: Some(resuming), + session_id: server_hello.session_id, + server_name, + using_ems, + transcript: self.transcript, + resuming: true, + cert_verified, + sig_verified, + })) + } else { + Ok(Box::new(ExpectCcs { + config, + secrets, + resuming_session: Some(resuming), + session_id: server_hello.session_id, + server_name, + using_ems, + transcript: self.transcript, + ticket: None, + resuming: true, + cert_verified, + sig_verified, + })) + }; + } + } + + cx.common.handshake_kind = Some(HandshakeKind::Full); + Ok(Box::new(ExpectCertificate { + config, + resuming_session: None, + session_id: server_hello.session_id, + server_name, + randoms: self.randoms, + using_ems, + transcript: self.transcript, + suite, + may_send_cert_status, + must_issue_new_ticket, + })) + } + } +} + +struct ExpectCertificate { + config: Arc<ClientConfig>, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + using_ems: bool, + transcript: HandshakeHash, + pub(super) suite: &'static Tls12CipherSuite, + may_send_cert_status: bool, + must_issue_new_ticket: bool, +} + +impl State<ClientConnectionData> for ExpectCertificate { + fn handle<'m>( + mut self: Box<Self>, + _cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + self.transcript.add_message(&m); + let server_cert_chain = require_handshake_msg_move!( + m, + HandshakeType::Certificate, + HandshakePayload::Certificate + )?; + + if self.may_send_cert_status { + Ok(Box::new(ExpectCertificateStatusOrServerKx { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert_chain, + must_issue_new_ticket: self.must_issue_new_ticket, + })) + } else { + let server_cert = ServerCertDetails::new(server_cert_chain, vec![]); + + Ok(Box::new(ExpectServerKx { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert, + must_issue_new_ticket: self.must_issue_new_ticket, + })) + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificateStatusOrServerKx<'m> { + config: Arc<ClientConfig>, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + using_ems: bool, + transcript: HandshakeHash, + suite: &'static Tls12CipherSuite, + server_cert_chain: CertificateChain<'m>, + must_issue_new_ticket: bool, +} + +impl State<ClientConnectionData> for ExpectCertificateStatusOrServerKx<'_> { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::ServerKeyExchange(..)), + .. + } => Box::new(ExpectServerKx { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert: ServerCertDetails::new(self.server_cert_chain, vec![]), + must_issue_new_ticket: self.must_issue_new_ticket, + }) + .handle(cx, m), + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CertificateStatus(..)), + .. + } => Box::new(ExpectCertificateStatus { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert_chain: self.server_cert_chain, + must_issue_new_ticket: self.must_issue_new_ticket, + }) + .handle(cx, m), + payload => Err(inappropriate_handshake_message( + &payload, + &[ContentType::Handshake], + &[ + HandshakeType::ServerKeyExchange, + HandshakeType::CertificateStatus, + ], + )), + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectCertificateStatusOrServerKx { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert_chain: self.server_cert_chain.into_owned(), + must_issue_new_ticket: self.must_issue_new_ticket, + }) + } +} + +struct ExpectCertificateStatus<'a> { + config: Arc<ClientConfig>, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + using_ems: bool, + transcript: HandshakeHash, + suite: &'static Tls12CipherSuite, + server_cert_chain: CertificateChain<'a>, + must_issue_new_ticket: bool, +} + +impl State<ClientConnectionData> for ExpectCertificateStatus<'_> { + fn handle<'m>( + mut self: Box<Self>, + _cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + self.transcript.add_message(&m); + let server_cert_ocsp_response = require_handshake_msg_move!( + m, + HandshakeType::CertificateStatus, + HandshakePayload::CertificateStatus + )? + .into_inner(); + + trace!( + "Server stapled OCSP response is {:?}", + &server_cert_ocsp_response + ); + + let server_cert = ServerCertDetails::new(self.server_cert_chain, server_cert_ocsp_response); + + Ok(Box::new(ExpectServerKx { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert, + must_issue_new_ticket: self.must_issue_new_ticket, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectCertificateStatus { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert_chain: self.server_cert_chain.into_owned(), + must_issue_new_ticket: self.must_issue_new_ticket, + }) + } +} + +struct ExpectServerKx<'a> { + config: Arc<ClientConfig>, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + using_ems: bool, + transcript: HandshakeHash, + suite: &'static Tls12CipherSuite, + server_cert: ServerCertDetails<'a>, + must_issue_new_ticket: bool, +} + +impl State<ClientConnectionData> for ExpectServerKx<'_> { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let opaque_kx = require_handshake_msg!( + m, + HandshakeType::ServerKeyExchange, + HandshakePayload::ServerKeyExchange + )?; + self.transcript.add_message(&m); + + let kx = opaque_kx + .unwrap_given_kxa(self.suite.kx) + .ok_or_else(|| { + cx.common.send_fatal_alert( + AlertDescription::DecodeError, + InvalidMessage::MissingKeyExchange, + ) + })?; + + // Save the signature and signed parameters for later verification. + let mut kx_params = Vec::new(); + kx.params.encode(&mut kx_params); + let server_kx = ServerKxDetails::new(kx_params, kx.dss); + + #[cfg_attr(not(feature = "logging"), allow(unused_variables))] + { + match &kx.params { + ServerKeyExchangeParams::Ecdh(ecdhe) => { + debug!("ECDHE curve is {:?}", ecdhe.curve_params) + } + ServerKeyExchangeParams::Dh(dhe) => { + debug!("DHE params are p = {:?}, g = {:?}", dhe.dh_p, dhe.dh_g) + } + } + } + + Ok(Box::new(ExpectServerDoneOrCertReq { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert: self.server_cert, + server_kx, + must_issue_new_ticket: self.must_issue_new_ticket, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectServerKx { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert: self.server_cert.into_owned(), + must_issue_new_ticket: self.must_issue_new_ticket, + }) + } +} + +fn emit_certificate( + transcript: &mut HandshakeHash, + cert_chain: CertificateChain<'static>, + common: &mut CommonState, +) { + let cert = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload(HandshakePayload::Certificate( + cert_chain, + ))), + }; + + transcript.add_message(&cert); + common.send_msg(cert, false); +} + +fn emit_client_kx( + transcript: &mut HandshakeHash, + kxa: KeyExchangeAlgorithm, + common: &mut CommonState, + pub_key: &[u8], +) { + let mut buf = Vec::new(); + match kxa { + KeyExchangeAlgorithm::ECDHE => ClientKeyExchangeParams::Ecdh(ClientEcdhParams { + public: PayloadU8::new(pub_key.to_vec()), + }), + KeyExchangeAlgorithm::DHE => ClientKeyExchangeParams::Dh(ClientDhParams { + public: PayloadU16::new(pub_key.to_vec()), + }), + } + .encode(&mut buf); + let pubkey = Payload::new(buf); + + let ckx = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientKeyExchange(pubkey), + )), + }; + + transcript.add_message(&ckx); + common.send_msg(ckx, false); +} + +fn emit_certverify( + transcript: &mut HandshakeHash, + signer: &dyn Signer, + common: &mut CommonState, +) -> Result<(), Error> { + let message = transcript + .take_handshake_buf() + .ok_or_else(|| Error::General("Expected transcript".to_owned()))?; + + let scheme = signer.scheme(); + let sig = signer.sign(&message)?; + let body = DigitallySignedStruct::new(scheme, sig); + + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::CertificateVerify(body), + )), + }; + + transcript.add_message(&m); + common.send_msg(m, false); + Ok(()) +} + +fn emit_ccs(common: &mut CommonState) { + let ccs = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}), + }; + + common.send_msg(ccs, false); +} + +fn emit_finished( + secrets: &ConnectionSecrets, + transcript: &mut HandshakeHash, + common: &mut CommonState, +) { + let vh = transcript.current_hash(); + let verify_data = secrets.client_verify_data(&vh); + let verify_data_payload = Payload::new(verify_data); + + let f = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload(HandshakePayload::Finished( + verify_data_payload, + ))), + }; + + transcript.add_message(&f); + common.send_msg(f, true); +} + +struct ServerKxDetails { + kx_params: Vec<u8>, + kx_sig: DigitallySignedStruct, +} + +impl ServerKxDetails { + fn new(params: Vec<u8>, sig: DigitallySignedStruct) -> Self { + Self { + kx_params: params, + kx_sig: sig, + } + } +} + +// --- Either a CertificateRequest, or a ServerHelloDone. --- +// Existence of the CertificateRequest tells us the server is asking for +// client auth. Otherwise we go straight to ServerHelloDone. +struct ExpectServerDoneOrCertReq<'a> { + config: Arc<ClientConfig>, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + using_ems: bool, + transcript: HandshakeHash, + suite: &'static Tls12CipherSuite, + server_cert: ServerCertDetails<'a>, + server_kx: ServerKxDetails, + must_issue_new_ticket: bool, +} + +impl State<ClientConnectionData> for ExpectServerDoneOrCertReq<'_> { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + if matches!( + m.payload, + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CertificateRequest(_)), + .. + } + ) { + Box::new(ExpectCertificateRequest { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert: self.server_cert, + server_kx: self.server_kx, + must_issue_new_ticket: self.must_issue_new_ticket, + }) + .handle(cx, m) + } else { + self.transcript.abandon_client_auth(); + + Box::new(ExpectServerDone { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert: self.server_cert, + server_kx: self.server_kx, + client_auth: None, + must_issue_new_ticket: self.must_issue_new_ticket, + }) + .handle(cx, m) + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectServerDoneOrCertReq { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert: self.server_cert.into_owned(), + server_kx: self.server_kx, + must_issue_new_ticket: self.must_issue_new_ticket, + }) + } +} + +struct ExpectCertificateRequest<'a> { + config: Arc<ClientConfig>, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + using_ems: bool, + transcript: HandshakeHash, + suite: &'static Tls12CipherSuite, + server_cert: ServerCertDetails<'a>, + server_kx: ServerKxDetails, + must_issue_new_ticket: bool, +} + +impl State<ClientConnectionData> for ExpectCertificateRequest<'_> { + fn handle<'m>( + mut self: Box<Self>, + _cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let certreq = require_handshake_msg!( + m, + HandshakeType::CertificateRequest, + HandshakePayload::CertificateRequest + )?; + self.transcript.add_message(&m); + debug!("Got CertificateRequest {certreq:?}"); + + // The RFC jovially describes the design here as 'somewhat complicated' + // and 'somewhat underspecified'. So thanks for that. + // + // We ignore certreq.certtypes as a result, since the information it contains + // is entirely duplicated in certreq.sigschemes. + + const NO_CONTEXT: Option<Vec<u8>> = None; // TLS 1.2 doesn't use a context. + let no_compression = None; // or compression + let client_auth = ClientAuthDetails::resolve( + self.config + .client_auth_cert_resolver + .as_ref(), + Some(&certreq.canames), + &certreq.sigschemes, + NO_CONTEXT, + no_compression, + ); + + Ok(Box::new(ExpectServerDone { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert: self.server_cert, + server_kx: self.server_kx, + client_auth: Some(client_auth), + must_issue_new_ticket: self.must_issue_new_ticket, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectCertificateRequest { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert: self.server_cert.into_owned(), + server_kx: self.server_kx, + must_issue_new_ticket: self.must_issue_new_ticket, + }) + } +} + +struct ExpectServerDone<'a> { + config: Arc<ClientConfig>, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + using_ems: bool, + transcript: HandshakeHash, + suite: &'static Tls12CipherSuite, + server_cert: ServerCertDetails<'a>, + server_kx: ServerKxDetails, + client_auth: Option<ClientAuthDetails>, + must_issue_new_ticket: bool, +} + +impl State<ClientConnectionData> for ExpectServerDone<'_> { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::ServerHelloDone), + .. + } => {} + payload => { + return Err(inappropriate_handshake_message( + &payload, + &[ContentType::Handshake], + &[HandshakeType::ServerHelloDone], + )); + } + } + + let mut st = *self; + st.transcript.add_message(&m); + + cx.common.check_aligned_handshake()?; + + trace!("Server cert is {:?}", st.server_cert.cert_chain); + debug!("Server DNS name is {:?}", st.server_name); + + let suite = st.suite; + + // 1. Verify the cert chain. + // 2. Verify that the top certificate signed their kx. + // 3. If doing client auth, send our Certificate. + // 4. Complete the key exchange: + // a) generate our kx pair + // b) emit a ClientKeyExchange containing it + // c) if doing client auth, emit a CertificateVerify + // d) derive the shared keys + // e) emit a CCS + // f) use the derived keys to start encryption + // 5. emit a Finished, our first encrypted message under the new keys. + + // 1. + let (end_entity, intermediates) = st + .server_cert + .cert_chain + .split_first() + .ok_or(Error::NoCertificatesPresented)?; + + let now = st.config.current_time()?; + + let cert_verified = st + .config + .verifier + .verify_server_cert( + end_entity, + intermediates, + &st.server_name, + &st.server_cert.ocsp_response, + now, + ) + .map_err(|err| { + cx.common + .send_cert_verify_error_alert(err) + })?; + + // 2. + // Build up the contents of the signed message. + // It's ClientHello.random || ServerHello.random || ServerKeyExchange.params + let sig_verified = { + let mut message = Vec::new(); + message.extend_from_slice(&st.randoms.client); + message.extend_from_slice(&st.randoms.server); + message.extend_from_slice(&st.server_kx.kx_params); + + // Check the signature is compatible with the ciphersuite. + let sig = &st.server_kx.kx_sig; + if !SupportedCipherSuite::from(suite) + .usable_for_signature_algorithm(sig.scheme.algorithm()) + { + warn!( + "peer signed kx with wrong algorithm (got {:?} expect {:?})", + sig.scheme.algorithm(), + suite.sign + ); + return Err(PeerMisbehaved::SignedKxWithWrongAlgorithm.into()); + } + + st.config + .verifier + .verify_tls12_signature(&message, end_entity, sig) + .map_err(|err| { + cx.common + .send_cert_verify_error_alert(err) + })? + }; + cx.common.peer_certificates = Some(st.server_cert.cert_chain.into_owned()); + + // 3. + if let Some(client_auth) = &st.client_auth { + let certs = match client_auth { + ClientAuthDetails::Empty { .. } => CertificateChain::default(), + ClientAuthDetails::Verify { certkey, .. } => CertificateChain(certkey.cert.clone()), + }; + emit_certificate(&mut st.transcript, certs, cx.common); + } + + // 4a. + let kx_params = tls12::decode_kx_params::<ServerKeyExchangeParams>( + st.suite.kx, + cx.common, + &st.server_kx.kx_params, + )?; + let maybe_skxg = match &kx_params { + ServerKeyExchangeParams::Ecdh(ecdh) => st + .config + .find_kx_group(ecdh.curve_params.named_group, ProtocolVersion::TLSv1_2), + ServerKeyExchangeParams::Dh(dh) => { + let ffdhe_group = dh.as_ffdhe_group(); + + st.config + .provider + .kx_groups + .iter() + .find(|kxg| kxg.ffdhe_group() == Some(ffdhe_group)) + .copied() + } + }; + let Some(skxg) = maybe_skxg else { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::SelectedUnofferedKxGroup, + )); + }; + cx.common.kx_state = KxState::Start(skxg); + let kx = skxg.start()?; + + // 4b. + let mut transcript = st.transcript; + emit_client_kx(&mut transcript, st.suite.kx, cx.common, kx.pub_key()); + // Note: EMS handshake hash only runs up to ClientKeyExchange. + let ems_seed = st + .using_ems + .then(|| transcript.current_hash()); + + // 4c. + if let Some(ClientAuthDetails::Verify { signer, .. }) = &st.client_auth { + emit_certverify(&mut transcript, signer.as_ref(), cx.common)?; + } + + // 4d. Derive secrets. + // An alert at this point will be sent in plaintext. That must happen + // prior to the CCS, or else the peer will try to decrypt it. + let secrets = ConnectionSecrets::from_key_exchange( + kx, + kx_params.pub_key(), + ems_seed, + st.randoms, + suite, + ) + .map_err(|err| { + cx.common + .send_fatal_alert(AlertDescription::IllegalParameter, err) + })?; + cx.common.kx_state.complete(); + + // 4e. CCS. We are definitely going to switch on encryption. + emit_ccs(cx.common); + + // 4f. Now commit secrets. + st.config.key_log.log( + "CLIENT_RANDOM", + &secrets.randoms.client, + &secrets.master_secret, + ); + cx.common + .start_encryption_tls12(&secrets, Side::Client); + cx.common + .record_layer + .start_encrypting(); + + // 5. + emit_finished(&secrets, &mut transcript, cx.common); + + if st.must_issue_new_ticket { + Ok(Box::new(ExpectNewTicket { + config: st.config, + secrets, + resuming_session: st.resuming_session, + session_id: st.session_id, + server_name: st.server_name, + using_ems: st.using_ems, + transcript, + resuming: false, + cert_verified, + sig_verified, + })) + } else { + Ok(Box::new(ExpectCcs { + config: st.config, + secrets, + resuming_session: st.resuming_session, + session_id: st.session_id, + server_name: st.server_name, + using_ems: st.using_ems, + transcript, + ticket: None, + resuming: false, + cert_verified, + sig_verified, + })) + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectServerDone { + config: self.config, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + randoms: self.randoms, + using_ems: self.using_ems, + transcript: self.transcript, + suite: self.suite, + server_cert: self.server_cert.into_owned(), + server_kx: self.server_kx, + client_auth: self.client_auth, + must_issue_new_ticket: self.must_issue_new_ticket, + }) + } +} + +struct ExpectNewTicket { + config: Arc<ClientConfig>, + secrets: ConnectionSecrets, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + using_ems: bool, + transcript: HandshakeHash, + resuming: bool, + cert_verified: verify::ServerCertVerified, + sig_verified: verify::HandshakeSignatureValid, +} + +impl State<ClientConnectionData> for ExpectNewTicket { + fn handle<'m>( + mut self: Box<Self>, + _cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + self.transcript.add_message(&m); + + let nst = require_handshake_msg_move!( + m, + HandshakeType::NewSessionTicket, + HandshakePayload::NewSessionTicket + )?; + + Ok(Box::new(ExpectCcs { + config: self.config, + secrets: self.secrets, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + using_ems: self.using_ems, + transcript: self.transcript, + ticket: Some(nst), + resuming: self.resuming, + cert_verified: self.cert_verified, + sig_verified: self.sig_verified, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// -- Waiting for their CCS -- +struct ExpectCcs { + config: Arc<ClientConfig>, + secrets: ConnectionSecrets, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + using_ems: bool, + transcript: HandshakeHash, + ticket: Option<NewSessionTicketPayload>, + resuming: bool, + cert_verified: verify::ServerCertVerified, + sig_verified: verify::HandshakeSignatureValid, +} + +impl State<ClientConnectionData> for ExpectCcs { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ChangeCipherSpec(..) => {} + payload => { + return Err(inappropriate_message( + &payload, + &[ContentType::ChangeCipherSpec], + )); + } + } + // CCS should not be received interleaved with fragmented handshake-level + // message. + cx.common.check_aligned_handshake()?; + + // Note: msgs layer validates trivial contents of CCS. + cx.common + .record_layer + .start_decrypting(); + + Ok(Box::new(ExpectFinished { + config: self.config, + secrets: self.secrets, + resuming_session: self.resuming_session, + session_id: self.session_id, + server_name: self.server_name, + using_ems: self.using_ems, + transcript: self.transcript, + ticket: self.ticket, + resuming: self.resuming, + cert_verified: self.cert_verified, + sig_verified: self.sig_verified, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectFinished { + config: Arc<ClientConfig>, + resuming_session: Option<persist::Tls12ClientSessionValue>, + session_id: SessionId, + server_name: ServerName<'static>, + using_ems: bool, + transcript: HandshakeHash, + ticket: Option<NewSessionTicketPayload>, + secrets: ConnectionSecrets, + resuming: bool, + cert_verified: verify::ServerCertVerified, + sig_verified: verify::HandshakeSignatureValid, +} + +impl ExpectFinished { + // -- Waiting for their finished -- + fn save_session(&mut self, cx: &ClientContext<'_>) { + // Save a ticket. If we got a new ticket, save that. Otherwise, save the + // original ticket again. + let (mut ticket, lifetime) = match self.ticket.take() { + Some(nst) => (nst.ticket, nst.lifetime_hint), + None => (Arc::new(PayloadU16::empty()), 0), + }; + + if ticket.0.is_empty() { + if let Some(resuming_session) = &mut self.resuming_session { + ticket = resuming_session.ticket(); + } + } + + if self.session_id.is_empty() && ticket.0.is_empty() { + debug!("Session not saved: server didn't allocate id or ticket"); + return; + } + + let Ok(now) = self.config.current_time() else { + debug!("Could not get current time"); + return; + }; + + let session_value = persist::Tls12ClientSessionValue::new( + self.secrets.suite(), + self.session_id, + ticket, + self.secrets.master_secret(), + cx.common + .peer_certificates + .clone() + .unwrap_or_default(), + &self.config.verifier, + &self.config.client_auth_cert_resolver, + now, + lifetime, + self.using_ems, + ); + + self.config + .resumption + .store + .set_tls12_session(self.server_name.clone(), session_value); + } +} + +impl State<ClientConnectionData> for ExpectFinished { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let mut st = *self; + let finished = + require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?; + + cx.common.check_aligned_handshake()?; + + // Work out what verify_data we expect. + let vh = st.transcript.current_hash(); + let expect_verify_data = st.secrets.server_verify_data(&vh); + + // Constant-time verification of this is relatively unimportant: they only + // get one chance. But it can't hurt. + let _fin_verified = + match ConstantTimeEq::ct_eq(&expect_verify_data[..], finished.bytes()).into() { + true => verify::FinishedMessageVerified::assertion(), + false => { + return Err(cx + .common + .send_fatal_alert(AlertDescription::DecryptError, Error::DecryptError)); + } + }; + + // Hash this message too. + st.transcript.add_message(&m); + + st.save_session(cx); + + if st.resuming { + emit_ccs(cx.common); + cx.common + .record_layer + .start_encrypting(); + emit_finished(&st.secrets, &mut st.transcript, cx.common); + } + + cx.common + .start_traffic(&mut cx.sendable_plaintext); + Ok(Box::new(ExpectTraffic { + secrets: st.secrets, + _cert_verified: st.cert_verified, + _sig_verified: st.sig_verified, + _fin_verified, + })) + } + + // we could not decrypt the encrypted handshake message with session resumption + // this might mean that the ticket was invalid for some reason, so we remove it + // from the store to restart a session from scratch + fn handle_decrypt_error(&self) { + if self.resuming { + self.config + .resumption + .store + .remove_tls12_session(&self.server_name); + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// -- Traffic transit state -- +struct ExpectTraffic { + secrets: ConnectionSecrets, + _cert_verified: verify::ServerCertVerified, + _sig_verified: verify::HandshakeSignatureValid, + _fin_verified: verify::FinishedMessageVerified, +} + +impl State<ClientConnectionData> for ExpectTraffic { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ApplicationData(payload) => cx + .common + .take_received_plaintext(payload), + payload => { + return Err(inappropriate_message( + &payload, + &[ContentType::ApplicationData], + )); + } + } + Ok(self) + } + + fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) -> Result<(), Error> { + self.secrets + .export_keying_material(output, label, context); + Ok(()) + } + + fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> { + self.secrets + .extract_secrets(Side::Client) + } + + fn into_external_state(self: Box<Self>) -> Result<Box<dyn KernelState + 'static>, Error> { + Ok(self) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +impl KernelState for ExpectTraffic { + fn update_secrets(&mut self, _: Direction) -> Result<ConnectionTrafficSecrets, Error> { + Err(Error::General( + "TLS 1.2 connections do not support traffic secret updates".into(), + )) + } + + fn handle_new_session_ticket( + &mut self, + _cx: &mut KernelContext<'_>, + _message: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + Err(Error::General( + "TLS 1.2 session tickets may not be sent once the handshake has completed".into(), + )) + } +} diff --git a/vendor/rustls/src/client/tls13.rs b/vendor/rustls/src/client/tls13.rs new file mode 100644 index 00000000..fe8495cc --- /dev/null +++ b/vendor/rustls/src/client/tls13.rs @@ -0,0 +1,1700 @@ +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; + +use pki_types::ServerName; +use subtle::ConstantTimeEq; + +use super::client_conn::ClientConnectionData; +use super::hs::{ClientContext, ClientHelloInput, ClientSessionValue}; +use crate::check::inappropriate_handshake_message; +use crate::client::common::{ClientAuthDetails, ClientHelloDetails, ServerCertDetails}; +use crate::client::ech::{self, EchState, EchStatus}; +use crate::client::{ClientConfig, ClientSessionStore, hs}; +use crate::common_state::{ + CommonState, HandshakeFlightTls13, HandshakeKind, KxState, Protocol, Side, State, +}; +use crate::conn::ConnectionRandoms; +use crate::conn::kernel::{Direction, KernelContext, KernelState}; +use crate::crypto::hash::Hash; +use crate::crypto::{ActiveKeyExchange, SharedSecret}; +use crate::enums::{ + AlertDescription, ContentType, HandshakeType, ProtocolVersion, SignatureScheme, +}; +use crate::error::{Error, InvalidMessage, PeerIncompatible, PeerMisbehaved}; +use crate::hash_hs::{HandshakeHash, HandshakeHashBuffer}; +use crate::log::{debug, trace, warn}; +use crate::msgs::base::{Payload, PayloadU8}; +use crate::msgs::ccs::ChangeCipherSpecPayload; +use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::enums::{ExtensionType, KeyUpdateRequest}; +use crate::msgs::handshake::{ + CERTIFICATE_MAX_SIZE_LIMIT, CertificatePayloadTls13, ClientExtensions, EchConfigPayload, + HandshakeMessagePayload, HandshakePayload, KeyShareEntry, NewSessionTicketPayloadTls13, + PresharedKeyBinder, PresharedKeyIdentity, PresharedKeyOffer, ServerExtensions, + ServerHelloPayload, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist::{self, Retrieved}; +use crate::sign::{CertifiedKey, Signer}; +use crate::suites::PartiallyExtractedSecrets; +use crate::sync::Arc; +use crate::tls13::key_schedule::{ + KeyScheduleEarly, KeyScheduleHandshake, KeySchedulePreHandshake, KeyScheduleResumption, + KeyScheduleTraffic, +}; +use crate::tls13::{ + Tls13CipherSuite, construct_client_verify_message, construct_server_verify_message, +}; +use crate::verify::{self, DigitallySignedStruct}; +use crate::{ConnectionTrafficSecrets, KeyLog, compress, crypto}; + +// Extensions we expect in plaintext in the ServerHello. +static ALLOWED_PLAINTEXT_EXTS: &[ExtensionType] = &[ + ExtensionType::KeyShare, + ExtensionType::PreSharedKey, + ExtensionType::SupportedVersions, +]; + +// Only the intersection of things we offer, and those disallowed +// in TLS1.3 +static DISALLOWED_TLS13_EXTS: &[ExtensionType] = &[ + ExtensionType::ECPointFormats, + ExtensionType::SessionTicket, + ExtensionType::RenegotiationInfo, + ExtensionType::ExtendedMasterSecret, +]; + +/// `early_data_key_schedule` is `Some` if we sent the +/// "early_data" extension to the server. +pub(super) fn handle_server_hello( + cx: &mut ClientContext<'_>, + server_hello: &ServerHelloPayload, + mut randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + mut transcript: HandshakeHash, + early_data_key_schedule: Option<KeyScheduleEarly>, + our_key_share: Box<dyn ActiveKeyExchange>, + server_hello_msg: &Message<'_>, + ech_state: Option<EchState>, + input: ClientHelloInput, +) -> hs::NextStateOrError<'static> { + validate_server_hello(cx.common, server_hello)?; + + let their_key_share = server_hello + .key_share + .as_ref() + .ok_or_else(|| { + cx.common.send_fatal_alert( + AlertDescription::MissingExtension, + PeerMisbehaved::MissingKeyShare, + ) + })?; + + let ClientHelloInput { + config, + resuming, + mut sent_tls13_fake_ccs, + mut hello, + server_name, + .. + } = input; + + let mut resuming_session = match resuming { + Some(Retrieved { + value: ClientSessionValue::Tls13(value), + .. + }) => Some(value), + _ => None, + }; + + let our_key_share = KeyExchangeChoice::new(&config, cx, our_key_share, their_key_share) + .map_err(|_| { + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::WrongGroupForKeyShare, + ) + })?; + + let key_schedule_pre_handshake = match (server_hello.preshared_key, early_data_key_schedule) { + (Some(selected_psk), Some(early_key_schedule)) => { + match &resuming_session { + Some(resuming) => { + let Some(resuming_suite) = suite.can_resume_from(resuming.suite()) else { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::ResumptionOfferedWithIncompatibleCipherSuite, + ) + }); + }; + + // If the server varies the suite here, we will have encrypted early data with + // the wrong suite. + if cx.data.early_data.is_enabled() && resuming_suite != suite { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::EarlyDataOfferedWithVariedCipherSuite, + ) + }); + } + + if selected_psk != 0 { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::SelectedInvalidPsk, + ) + }); + } + + debug!("Resuming using PSK"); + // The key schedule has been initialized and set in fill_in_psk_binder() + } + _ => { + return Err(PeerMisbehaved::SelectedUnofferedPsk.into()); + } + } + KeySchedulePreHandshake::from(early_key_schedule) + } + _ => { + debug!("Not resuming"); + // Discard the early data key schedule. + cx.data.early_data.rejected(); + cx.common.early_traffic = false; + resuming_session.take(); + KeySchedulePreHandshake::new(suite) + } + }; + + cx.common.kx_state.complete(); + let shared_secret = our_key_share + .complete(&their_key_share.payload.0) + .map_err(|err| { + cx.common + .send_fatal_alert(AlertDescription::IllegalParameter, err) + })?; + + let mut key_schedule = key_schedule_pre_handshake.into_handshake(shared_secret); + + // If we have ECH state, check that the server accepted our offer. + if let Some(ech_state) = ech_state { + let Message { + payload: + MessagePayload::Handshake { + encoded: server_hello_encoded, + .. + }, + .. + } = &server_hello_msg + else { + unreachable!("ServerHello is a handshake message"); + }; + cx.data.ech_status = match ech_state.confirm_acceptance( + &mut key_schedule, + server_hello, + server_hello_encoded, + suite.common.hash_provider, + )? { + // The server accepted our ECH offer, so complete the inner transcript with the + // server hello message, and switch the relevant state to the copies for the + // inner client hello. + Some(mut accepted) => { + accepted + .transcript + .add_message(server_hello_msg); + transcript = accepted.transcript; + randoms.client = accepted.random.0; + hello.sent_extensions = accepted.sent_extensions; + EchStatus::Accepted + } + // The server rejected our ECH offer. + None => EchStatus::Rejected, + }; + } + + // Remember what KX group the server liked for next time. + config + .resumption + .store + .set_kx_hint(server_name.clone(), their_key_share.group); + + // If we change keying when a subsequent handshake message is being joined, + // the two halves will have different record layer protections. Disallow this. + cx.common.check_aligned_handshake()?; + + let hash_at_client_recvd_server_hello = transcript.current_hash(); + let key_schedule = key_schedule.derive_client_handshake_secrets( + cx.data.early_data.is_enabled(), + hash_at_client_recvd_server_hello, + suite, + &*config.key_log, + &randoms.client, + cx.common, + ); + + emit_fake_ccs(&mut sent_tls13_fake_ccs, cx.common); + + Ok(Box::new(ExpectEncryptedExtensions { + config, + resuming_session, + server_name, + randoms, + suite, + transcript, + key_schedule, + hello, + })) +} + +enum KeyExchangeChoice { + Whole(Box<dyn ActiveKeyExchange>), + Component(Box<dyn ActiveKeyExchange>), +} + +impl KeyExchangeChoice { + /// Decide between `our_key_share` or `our_key_share.hybrid_component()` + /// based on the selection of the server expressed in `their_key_share`. + fn new( + config: &Arc<ClientConfig>, + cx: &mut ClientContext<'_>, + our_key_share: Box<dyn ActiveKeyExchange>, + their_key_share: &KeyShareEntry, + ) -> Result<Self, ()> { + if our_key_share.group() == their_key_share.group { + return Ok(Self::Whole(our_key_share)); + } + + let (component_group, _) = our_key_share + .hybrid_component() + .ok_or(())?; + + if component_group != their_key_share.group { + return Err(()); + } + + // correct the record for the benefit of accuracy of + // `negotiated_key_exchange_group()` + let actual_skxg = config + .find_kx_group(component_group, ProtocolVersion::TLSv1_3) + .ok_or(())?; + cx.common.kx_state = KxState::Start(actual_skxg); + + Ok(Self::Component(our_key_share)) + } + + fn complete(self, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> { + match self { + Self::Whole(akx) => akx.complete(peer_pub_key), + Self::Component(akx) => akx.complete_hybrid_component(peer_pub_key), + } + } +} + +fn validate_server_hello( + common: &mut CommonState, + server_hello: &ServerHelloPayload, +) -> Result<(), Error> { + if !server_hello.only_contains(ALLOWED_PLAINTEXT_EXTS) { + return Err(common.send_fatal_alert( + AlertDescription::UnsupportedExtension, + PeerMisbehaved::UnexpectedCleartextExtension, + )); + } + + Ok(()) +} + +pub(super) fn initial_key_share( + config: &ClientConfig, + server_name: &ServerName<'_>, + kx_state: &mut KxState, +) -> Result<Box<dyn ActiveKeyExchange>, Error> { + let group = config + .resumption + .store + .kx_hint(server_name) + .and_then(|group_name| config.find_kx_group(group_name, ProtocolVersion::TLSv1_3)) + .unwrap_or_else(|| { + config + .provider + .kx_groups + .iter() + .copied() + .next() + .expect("No kx groups configured") + }); + + *kx_state = KxState::Start(group); + group.start() +} + +/// This implements the horrifying TLS1.3 hack where PSK binders have a +/// data dependency on the message they are contained within. +pub(super) fn fill_in_psk_binder( + resuming: &persist::Tls13ClientSessionValue, + transcript: &HandshakeHashBuffer, + hmp: &mut HandshakeMessagePayload<'_>, +) -> KeyScheduleEarly { + // We need to know the hash function of the suite we're trying to resume into. + let suite = resuming.suite(); + let suite_hash = suite.common.hash_provider; + + // The binder is calculated over the clienthello, but doesn't include itself or its + // length, or the length of its container. + let binder_plaintext = hmp.encoding_for_binder_signing(); + let handshake_hash = transcript.hash_given(suite_hash, &binder_plaintext); + + // Run a fake key_schedule to simulate what the server will do if it chooses + // to resume. + let key_schedule = KeyScheduleEarly::new(suite, resuming.secret()); + let real_binder = key_schedule.resumption_psk_binder_key_and_sign_verify_data(&handshake_hash); + + if let HandshakePayload::ClientHello(ch) = &mut hmp.0 { + if let Some(PresharedKeyOffer { + binders, + identities, + }) = &mut ch.preshared_key_offer + { + // the caller of this function must have set up the desired identity, and a + // matching (dummy) binder; or else the binder we compute here will be incorrect. + // See `prepare_resumption()`. + debug_assert_eq!(identities.len(), 1); + debug_assert_eq!(binders.len(), 1); + debug_assert_eq!(binders[0].as_ref().len(), real_binder.as_ref().len()); + binders[0] = PresharedKeyBinder::from(real_binder.as_ref().to_vec()); + } + }; + + key_schedule +} + +pub(super) fn prepare_resumption( + config: &ClientConfig, + cx: &mut ClientContext<'_>, + resuming_session: &Retrieved<&persist::Tls13ClientSessionValue>, + exts: &mut ClientExtensions<'_>, + doing_retry: bool, +) { + let resuming_suite = resuming_session.suite(); + cx.common.suite = Some(resuming_suite.into()); + // The EarlyData extension MUST be supplied together with the + // PreSharedKey extension. + let max_early_data_size = resuming_session.max_early_data_size(); + if config.enable_early_data && max_early_data_size > 0 && !doing_retry { + cx.data + .early_data + .enable(max_early_data_size as usize); + exts.early_data_request = Some(()); + } + + // Finally, and only for TLS1.3 with a ticket resumption, include a binder + // for our ticket. This must go last. + // + // Include an empty binder. It gets filled in below because it depends on + // the message it's contained in (!!!). + let obfuscated_ticket_age = resuming_session.obfuscated_ticket_age(); + + let binder_len = resuming_suite + .common + .hash_provider + .output_len(); + let binder = vec![0u8; binder_len]; + + let psk_identity = + PresharedKeyIdentity::new(resuming_session.ticket().to_vec(), obfuscated_ticket_age); + let psk_offer = PresharedKeyOffer::new(psk_identity, binder); + exts.preshared_key_offer = Some(psk_offer); +} + +pub(super) fn derive_early_traffic_secret( + key_log: &dyn KeyLog, + cx: &mut ClientContext<'_>, + hash_alg: &'static dyn Hash, + early_key_schedule: &KeyScheduleEarly, + sent_tls13_fake_ccs: &mut bool, + transcript_buffer: &HandshakeHashBuffer, + client_random: &[u8; 32], +) { + // For middlebox compatibility + emit_fake_ccs(sent_tls13_fake_ccs, cx.common); + + let client_hello_hash = transcript_buffer.hash_given(hash_alg, &[]); + early_key_schedule.client_early_traffic_secret( + &client_hello_hash, + key_log, + client_random, + cx.common, + ); + + // Now the client can send encrypted early data + cx.common.early_traffic = true; + trace!("Starting early data traffic"); +} + +pub(super) fn emit_fake_ccs(sent_tls13_fake_ccs: &mut bool, common: &mut CommonState) { + if common.is_quic() { + return; + } + + if core::mem::replace(sent_tls13_fake_ccs, true) { + return; + } + + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}), + }; + common.send_msg(m, false); +} + +fn validate_encrypted_extensions( + common: &mut CommonState, + hello: &ClientHelloDetails, + exts: &ServerExtensions<'_>, +) -> Result<(), Error> { + if hello.server_sent_unsolicited_extensions(exts, &[]) { + return Err(common.send_fatal_alert( + AlertDescription::UnsupportedExtension, + PeerMisbehaved::UnsolicitedEncryptedExtension, + )); + } + + if exts.contains_any(ALLOWED_PLAINTEXT_EXTS) || exts.contains_any(DISALLOWED_TLS13_EXTS) { + return Err(common.send_fatal_alert( + AlertDescription::UnsupportedExtension, + PeerMisbehaved::DisallowedEncryptedExtension, + )); + } + + Ok(()) +} + +struct ExpectEncryptedExtensions { + config: Arc<ClientConfig>, + resuming_session: Option<persist::Tls13ClientSessionValue>, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + transcript: HandshakeHash, + key_schedule: KeyScheduleHandshake, + hello: ClientHelloDetails, +} + +impl State<ClientConnectionData> for ExpectEncryptedExtensions { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let exts = require_handshake_msg!( + m, + HandshakeType::EncryptedExtensions, + HandshakePayload::EncryptedExtensions + )?; + debug!("TLS1.3 encrypted extensions: {exts:?}"); + self.transcript.add_message(&m); + + validate_encrypted_extensions(cx.common, &self.hello, exts)?; + hs::process_alpn_protocol( + cx.common, + &self.hello.alpn_protocols, + exts.selected_protocol + .as_ref() + .map(|protocol| protocol.as_ref()), + )?; + hs::process_client_cert_type_extension( + cx.common, + &self.config, + exts.client_certificate_type.as_ref(), + )?; + hs::process_server_cert_type_extension( + cx.common, + &self.config, + exts.server_certificate_type.as_ref(), + )?; + + let ech_retry_configs = match (cx.data.ech_status, &exts.encrypted_client_hello_ack) { + // If we didn't offer ECH, or ECH was accepted, but the server sent an ECH encrypted + // extension with retry configs, we must error. + (EchStatus::NotOffered | EchStatus::Accepted, Some(_)) => { + return Err(cx.common.send_fatal_alert( + AlertDescription::UnsupportedExtension, + PeerMisbehaved::UnsolicitedEchExtension, + )); + } + // If we offered ECH, and it was rejected, store the retry configs (if any) from + // the server's ECH extension. We will return them in an error produced at the end + // of the handshake. + (EchStatus::Rejected, ext) => ext + .as_ref() + .map(|ext| ext.retry_configs.to_vec()), + _ => None, + }; + + // QUIC transport parameters + if cx.common.is_quic() { + match exts + .transport_parameters + .as_ref() + .or(exts.transport_parameters_draft.as_ref()) + { + Some(params) => cx.common.quic.params = Some(params.clone().into_vec()), + None => { + return Err(cx + .common + .missing_extension(PeerMisbehaved::MissingQuicTransportParameters)); + } + } + } + + match self.resuming_session { + Some(resuming_session) => { + let was_early_traffic = cx.common.early_traffic; + if was_early_traffic { + match exts.early_data_ack { + Some(()) => cx.data.early_data.accepted(), + None => { + cx.data.early_data.rejected(); + cx.common.early_traffic = false; + } + } + } + + if was_early_traffic && !cx.common.early_traffic { + // If no early traffic, set the encryption key for handshakes + self.key_schedule + .set_handshake_encrypter(cx.common); + } + + cx.common.peer_certificates = Some( + resuming_session + .server_cert_chain() + .clone(), + ); + cx.common.handshake_kind = Some(HandshakeKind::Resumed); + + // We *don't* reverify the certificate chain here: resumption is a + // continuation of the previous session in terms of security policy. + let cert_verified = verify::ServerCertVerified::assertion(); + let sig_verified = verify::HandshakeSignatureValid::assertion(); + Ok(Box::new(ExpectFinished { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: None, + cert_verified, + sig_verified, + ech_retry_configs, + })) + } + _ => { + if exts.early_data_ack.is_some() { + return Err(PeerMisbehaved::EarlyDataExtensionWithoutResumption.into()); + } + cx.common + .handshake_kind + .get_or_insert(HandshakeKind::Full); + + Ok(if self.hello.offered_cert_compression { + Box::new(ExpectCertificateOrCompressedCertificateOrCertReq { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + ech_retry_configs, + }) + } else { + Box::new(ExpectCertificateOrCertReq { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + ech_retry_configs, + }) + }) + } + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificateOrCompressedCertificateOrCertReq { + config: Arc<ClientConfig>, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + transcript: HandshakeHash, + key_schedule: KeyScheduleHandshake, + ech_retry_configs: Option<Vec<EchConfigPayload>>, +} + +impl State<ClientConnectionData> for ExpectCertificateOrCompressedCertificateOrCertReq { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CertificateTls13(..)), + .. + } => Box::new(ExpectCertificate { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: None, + message_already_in_transcript: false, + ech_retry_configs: self.ech_retry_configs, + }) + .handle(cx, m), + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CompressedCertificate(..)), + .. + } => Box::new(ExpectCompressedCertificate { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: None, + ech_retry_configs: self.ech_retry_configs, + }) + .handle(cx, m), + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CertificateRequestTls13(..)), + .. + } => Box::new(ExpectCertificateRequest { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + offered_cert_compression: true, + ech_retry_configs: self.ech_retry_configs, + }) + .handle(cx, m), + payload => Err(inappropriate_handshake_message( + &payload, + &[ContentType::Handshake], + &[ + HandshakeType::Certificate, + HandshakeType::CertificateRequest, + HandshakeType::CompressedCertificate, + ], + )), + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificateOrCompressedCertificate { + config: Arc<ClientConfig>, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + transcript: HandshakeHash, + key_schedule: KeyScheduleHandshake, + client_auth: Option<ClientAuthDetails>, + ech_retry_configs: Option<Vec<EchConfigPayload>>, +} + +impl State<ClientConnectionData> for ExpectCertificateOrCompressedCertificate { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CertificateTls13(..)), + .. + } => Box::new(ExpectCertificate { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: self.client_auth, + message_already_in_transcript: false, + ech_retry_configs: self.ech_retry_configs, + }) + .handle(cx, m), + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CompressedCertificate(..)), + .. + } => Box::new(ExpectCompressedCertificate { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: self.client_auth, + ech_retry_configs: self.ech_retry_configs, + }) + .handle(cx, m), + payload => Err(inappropriate_handshake_message( + &payload, + &[ContentType::Handshake], + &[ + HandshakeType::Certificate, + HandshakeType::CompressedCertificate, + ], + )), + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificateOrCertReq { + config: Arc<ClientConfig>, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + transcript: HandshakeHash, + key_schedule: KeyScheduleHandshake, + ech_retry_configs: Option<Vec<EchConfigPayload>>, +} + +impl State<ClientConnectionData> for ExpectCertificateOrCertReq { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CertificateTls13(..)), + .. + } => Box::new(ExpectCertificate { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: None, + message_already_in_transcript: false, + ech_retry_configs: self.ech_retry_configs, + }) + .handle(cx, m), + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CertificateRequestTls13(..)), + .. + } => Box::new(ExpectCertificateRequest { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + offered_cert_compression: false, + ech_retry_configs: self.ech_retry_configs, + }) + .handle(cx, m), + payload => Err(inappropriate_handshake_message( + &payload, + &[ContentType::Handshake], + &[ + HandshakeType::Certificate, + HandshakeType::CertificateRequest, + ], + )), + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// TLS1.3 version of CertificateRequest handling. We then move to expecting the server +// Certificate. Unfortunately the CertificateRequest type changed in an annoying way +// in TLS1.3. +struct ExpectCertificateRequest { + config: Arc<ClientConfig>, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + transcript: HandshakeHash, + key_schedule: KeyScheduleHandshake, + offered_cert_compression: bool, + ech_retry_configs: Option<Vec<EchConfigPayload>>, +} + +impl State<ClientConnectionData> for ExpectCertificateRequest { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let certreq = &require_handshake_msg!( + m, + HandshakeType::CertificateRequest, + HandshakePayload::CertificateRequestTls13 + )?; + self.transcript.add_message(&m); + debug!("Got CertificateRequest {certreq:?}"); + + // Fortunately the problems here in TLS1.2 and prior are corrected in + // TLS1.3. + + // Must be empty during handshake. + if !certreq.context.0.is_empty() { + warn!("Server sent non-empty certreq context"); + return Err(cx.common.send_fatal_alert( + AlertDescription::DecodeError, + InvalidMessage::InvalidCertRequest, + )); + } + + let compat_sigschemes = certreq + .extensions + .signature_algorithms + .as_deref() + .unwrap_or_default() + .iter() + .cloned() + .filter(SignatureScheme::supported_in_tls13) + .collect::<Vec<SignatureScheme>>(); + + if compat_sigschemes.is_empty() { + return Err(cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::NoCertificateRequestSignatureSchemesInCommon, + )); + } + + let compat_compressor = certreq + .extensions + .certificate_compression_algorithms + .as_deref() + .and_then(|offered| { + self.config + .cert_compressors + .iter() + .find(|compressor| offered.contains(&compressor.algorithm())) + }) + .cloned(); + + let client_auth = ClientAuthDetails::resolve( + self.config + .client_auth_cert_resolver + .as_ref(), + certreq + .extensions + .authority_names + .as_deref(), + &compat_sigschemes, + Some(certreq.context.0.clone()), + compat_compressor, + ); + + Ok(if self.offered_cert_compression { + Box::new(ExpectCertificateOrCompressedCertificate { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: Some(client_auth), + ech_retry_configs: self.ech_retry_configs, + }) + } else { + Box::new(ExpectCertificate { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: Some(client_auth), + message_already_in_transcript: false, + ech_retry_configs: self.ech_retry_configs, + }) + }) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCompressedCertificate { + config: Arc<ClientConfig>, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + transcript: HandshakeHash, + key_schedule: KeyScheduleHandshake, + client_auth: Option<ClientAuthDetails>, + ech_retry_configs: Option<Vec<EchConfigPayload>>, +} + +impl State<ClientConnectionData> for ExpectCompressedCertificate { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + self.transcript.add_message(&m); + let compressed_cert = require_handshake_msg_move!( + m, + HandshakeType::CompressedCertificate, + HandshakePayload::CompressedCertificate + )?; + + let selected_decompressor = self + .config + .cert_decompressors + .iter() + .find(|item| item.algorithm() == compressed_cert.alg); + + let Some(decompressor) = selected_decompressor else { + return Err(cx.common.send_fatal_alert( + AlertDescription::BadCertificate, + PeerMisbehaved::SelectedUnofferedCertCompression, + )); + }; + + if compressed_cert.uncompressed_len as usize > CERTIFICATE_MAX_SIZE_LIMIT { + return Err(cx.common.send_fatal_alert( + AlertDescription::BadCertificate, + InvalidMessage::MessageTooLarge, + )); + } + + let mut decompress_buffer = vec![0u8; compressed_cert.uncompressed_len as usize]; + if let Err(compress::DecompressionFailed) = + decompressor.decompress(compressed_cert.compressed.0.bytes(), &mut decompress_buffer) + { + return Err(cx.common.send_fatal_alert( + AlertDescription::BadCertificate, + PeerMisbehaved::InvalidCertCompression, + )); + } + + let cert_payload = + match CertificatePayloadTls13::read(&mut Reader::init(&decompress_buffer)) { + Ok(cm) => cm, + Err(err) => { + return Err(cx + .common + .send_fatal_alert(AlertDescription::BadCertificate, err)); + } + }; + trace!( + "Server certificate decompressed using {:?} ({} bytes -> {})", + compressed_cert.alg, + compressed_cert + .compressed + .0 + .bytes() + .len(), + compressed_cert.uncompressed_len, + ); + + let m = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::CertificateTls13(cert_payload.into_owned()), + )), + }; + + Box::new(ExpectCertificate { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: self.client_auth, + message_already_in_transcript: true, + ech_retry_configs: self.ech_retry_configs, + }) + .handle(cx, m) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificate { + config: Arc<ClientConfig>, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + transcript: HandshakeHash, + key_schedule: KeyScheduleHandshake, + client_auth: Option<ClientAuthDetails>, + message_already_in_transcript: bool, + ech_retry_configs: Option<Vec<EchConfigPayload>>, +} + +impl State<ClientConnectionData> for ExpectCertificate { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + if !self.message_already_in_transcript { + self.transcript.add_message(&m); + } + let cert_chain = require_handshake_msg_move!( + m, + HandshakeType::Certificate, + HandshakePayload::CertificateTls13 + )?; + + // This is only non-empty for client auth. + if !cert_chain.context.0.is_empty() { + return Err(cx.common.send_fatal_alert( + AlertDescription::DecodeError, + InvalidMessage::InvalidCertRequest, + )); + } + + let end_entity_ocsp = cert_chain.end_entity_ocsp().to_vec(); + let server_cert = ServerCertDetails::new( + cert_chain + .into_certificate_chain() + .into_owned(), + end_entity_ocsp, + ); + + Ok(Box::new(ExpectCertificateVerify { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + server_cert, + client_auth: self.client_auth, + ech_retry_configs: self.ech_retry_configs, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- TLS1.3 CertificateVerify --- +struct ExpectCertificateVerify<'a> { + config: Arc<ClientConfig>, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + transcript: HandshakeHash, + key_schedule: KeyScheduleHandshake, + server_cert: ServerCertDetails<'a>, + client_auth: Option<ClientAuthDetails>, + ech_retry_configs: Option<Vec<EchConfigPayload>>, +} + +impl State<ClientConnectionData> for ExpectCertificateVerify<'_> { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let cert_verify = require_handshake_msg!( + m, + HandshakeType::CertificateVerify, + HandshakePayload::CertificateVerify + )?; + + trace!("Server cert is {:?}", self.server_cert.cert_chain); + + // 1. Verify the certificate chain. + let (end_entity, intermediates) = self + .server_cert + .cert_chain + .split_first() + .ok_or(Error::NoCertificatesPresented)?; + + let now = self.config.current_time()?; + + let cert_verified = self + .config + .verifier + .verify_server_cert( + end_entity, + intermediates, + &self.server_name, + &self.server_cert.ocsp_response, + now, + ) + .map_err(|err| { + cx.common + .send_cert_verify_error_alert(err) + })?; + + // 2. Verify their signature on the handshake. + let handshake_hash = self.transcript.current_hash(); + let sig_verified = self + .config + .verifier + .verify_tls13_signature( + construct_server_verify_message(&handshake_hash).as_ref(), + end_entity, + cert_verify, + ) + .map_err(|err| { + cx.common + .send_cert_verify_error_alert(err) + })?; + + cx.common.peer_certificates = Some(self.server_cert.cert_chain.into_owned()); + self.transcript.add_message(&m); + + Ok(Box::new(ExpectFinished { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_auth: self.client_auth, + cert_verified, + sig_verified, + ech_retry_configs: self.ech_retry_configs, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectCertificateVerify { + config: self.config, + server_name: self.server_name, + randoms: self.randoms, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + server_cert: self.server_cert.into_owned(), + client_auth: self.client_auth, + ech_retry_configs: self.ech_retry_configs, + }) + } +} + +fn emit_compressed_certificate_tls13( + flight: &mut HandshakeFlightTls13<'_>, + certkey: &CertifiedKey, + auth_context: Option<Vec<u8>>, + compressor: &dyn compress::CertCompressor, + config: &ClientConfig, +) { + let mut cert_payload = CertificatePayloadTls13::new(certkey.cert.iter(), None); + cert_payload.context = PayloadU8::new(auth_context.clone().unwrap_or_default()); + + let Ok(compressed) = config + .cert_compression_cache + .compression_for(compressor, &cert_payload) + else { + return emit_certificate_tls13(flight, Some(certkey), auth_context); + }; + + flight.add(HandshakeMessagePayload( + HandshakePayload::CompressedCertificate(compressed.compressed_cert_payload()), + )); +} + +fn emit_certificate_tls13( + flight: &mut HandshakeFlightTls13<'_>, + certkey: Option<&CertifiedKey>, + auth_context: Option<Vec<u8>>, +) { + let certs = certkey + .map(|ck| ck.cert.as_ref()) + .unwrap_or(&[][..]); + let mut cert_payload = CertificatePayloadTls13::new(certs.iter(), None); + cert_payload.context = PayloadU8::new(auth_context.unwrap_or_default()); + + flight.add(HandshakeMessagePayload(HandshakePayload::CertificateTls13( + cert_payload, + ))); +} + +fn emit_certverify_tls13( + flight: &mut HandshakeFlightTls13<'_>, + signer: &dyn Signer, +) -> Result<(), Error> { + let message = construct_client_verify_message(&flight.transcript.current_hash()); + + let scheme = signer.scheme(); + let sig = signer.sign(message.as_ref())?; + let dss = DigitallySignedStruct::new(scheme, sig); + + flight.add(HandshakeMessagePayload( + HandshakePayload::CertificateVerify(dss), + )); + Ok(()) +} + +fn emit_finished_tls13(flight: &mut HandshakeFlightTls13<'_>, verify_data: &crypto::hmac::Tag) { + let verify_data_payload = Payload::new(verify_data.as_ref()); + + flight.add(HandshakeMessagePayload(HandshakePayload::Finished( + verify_data_payload, + ))); +} + +fn emit_end_of_early_data_tls13(transcript: &mut HandshakeHash, common: &mut CommonState) { + if common.is_quic() { + return; + } + + let m = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::EndOfEarlyData, + )), + }; + + transcript.add_message(&m); + common.send_msg(m, true); +} + +struct ExpectFinished { + config: Arc<ClientConfig>, + server_name: ServerName<'static>, + randoms: ConnectionRandoms, + suite: &'static Tls13CipherSuite, + transcript: HandshakeHash, + key_schedule: KeyScheduleHandshake, + client_auth: Option<ClientAuthDetails>, + cert_verified: verify::ServerCertVerified, + sig_verified: verify::HandshakeSignatureValid, + ech_retry_configs: Option<Vec<EchConfigPayload>>, +} + +impl State<ClientConnectionData> for ExpectFinished { + fn handle<'m>( + self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let mut st = *self; + let finished = + require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?; + + let handshake_hash = st.transcript.current_hash(); + let expect_verify_data = st + .key_schedule + .sign_server_finish(&handshake_hash); + + let fin = match ConstantTimeEq::ct_eq(expect_verify_data.as_ref(), finished.bytes()).into() + { + true => verify::FinishedMessageVerified::assertion(), + false => { + return Err(cx + .common + .send_fatal_alert(AlertDescription::DecryptError, Error::DecryptError)); + } + }; + + st.transcript.add_message(&m); + + let hash_after_handshake = st.transcript.current_hash(); + /* The EndOfEarlyData message to server is still encrypted with early data keys, + * but appears in the transcript after the server Finished. */ + if cx.common.early_traffic { + emit_end_of_early_data_tls13(&mut st.transcript, cx.common); + cx.common.early_traffic = false; + cx.data.early_data.finished(); + st.key_schedule + .set_handshake_encrypter(cx.common); + } + + let mut flight = HandshakeFlightTls13::new(&mut st.transcript); + + /* Send our authentication/finished messages. These are still encrypted + * with our handshake keys. */ + if let Some(client_auth) = st.client_auth { + match client_auth { + ClientAuthDetails::Empty { + auth_context_tls13: auth_context, + } => { + emit_certificate_tls13(&mut flight, None, auth_context); + } + ClientAuthDetails::Verify { + auth_context_tls13: auth_context, + .. + } if cx.data.ech_status == EchStatus::Rejected => { + // If ECH was offered, and rejected, we MUST respond with + // an empty certificate message. + emit_certificate_tls13(&mut flight, None, auth_context); + } + ClientAuthDetails::Verify { + certkey, + signer, + auth_context_tls13: auth_context, + compressor, + } => { + if let Some(compressor) = compressor { + emit_compressed_certificate_tls13( + &mut flight, + &certkey, + auth_context, + compressor, + &st.config, + ); + } else { + emit_certificate_tls13(&mut flight, Some(&certkey), auth_context); + } + emit_certverify_tls13(&mut flight, signer.as_ref())?; + } + } + } + + let (key_schedule_pre_finished, verify_data) = st + .key_schedule + .into_pre_finished_client_traffic( + hash_after_handshake, + flight.transcript.current_hash(), + &*st.config.key_log, + &st.randoms.client, + ); + + emit_finished_tls13(&mut flight, &verify_data); + flight.finish(cx.common); + + /* We're now sure this server supports TLS1.3. But if we run out of TLS1.3 tickets + * when connecting to it again, we definitely don't want to attempt a TLS1.2 resumption. */ + st.config + .resumption + .store + .remove_tls12_session(&st.server_name); + + /* Now move to our application traffic keys. */ + cx.common.check_aligned_handshake()?; + let (key_schedule, resumption) = + key_schedule_pre_finished.into_traffic(cx.common, st.transcript.current_hash()); + cx.common + .start_traffic(&mut cx.sendable_plaintext); + + // Now that we've reached the end of the normal handshake we must enforce ECH acceptance by + // sending an alert and returning an error (potentially with retry configs) if the server + // did not accept our ECH offer. + if cx.data.ech_status == EchStatus::Rejected { + return Err(ech::fatal_alert_required(st.ech_retry_configs, cx.common)); + } + + let st = ExpectTraffic { + config: st.config.clone(), + session_storage: st.config.resumption.store.clone(), + server_name: st.server_name, + suite: st.suite, + key_schedule, + resumption, + _cert_verified: st.cert_verified, + _sig_verified: st.sig_verified, + _fin_verified: fin, + }; + + Ok(match cx.common.is_quic() { + true => Box::new(ExpectQuicTraffic(st)), + false => Box::new(st), + }) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// -- Traffic transit state (TLS1.3) -- +// In this state we can be sent tickets, key updates, +// and application data. +struct ExpectTraffic { + config: Arc<ClientConfig>, + session_storage: Arc<dyn ClientSessionStore>, + server_name: ServerName<'static>, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTraffic, + resumption: KeyScheduleResumption, + _cert_verified: verify::ServerCertVerified, + _sig_verified: verify::HandshakeSignatureValid, + _fin_verified: verify::FinishedMessageVerified, +} + +impl ExpectTraffic { + fn handle_new_ticket_impl( + &mut self, + cx: &mut KernelContext<'_>, + nst: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + let secret = self + .resumption + .derive_ticket_psk(&nst.nonce.0); + + let now = self.config.current_time()?; + + #[allow(unused_mut)] + let mut value = persist::Tls13ClientSessionValue::new( + self.suite, + nst.ticket.clone(), + secret.as_ref(), + cx.peer_certificates + .cloned() + .unwrap_or_default(), + &self.config.verifier, + &self.config.client_auth_cert_resolver, + now, + nst.lifetime, + nst.age_add, + nst.extensions + .max_early_data_size + .unwrap_or_default(), + ); + + if cx.is_quic() { + if let Some(sz) = nst.extensions.max_early_data_size { + if sz != 0 && sz != 0xffff_ffff { + return Err(PeerMisbehaved::InvalidMaxEarlyDataSize.into()); + } + } + + if let Some(quic_params) = &cx.quic.params { + value.set_quic_params(quic_params); + } + } + + self.session_storage + .insert_tls13_ticket(self.server_name.clone(), value); + Ok(()) + } + + fn handle_new_ticket_tls13( + &mut self, + cx: &mut ClientContext<'_>, + nst: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + let mut kcx = KernelContext { + peer_certificates: cx.common.peer_certificates.as_ref(), + protocol: cx.common.protocol, + quic: &cx.common.quic, + }; + cx.common.tls13_tickets_received = cx + .common + .tls13_tickets_received + .saturating_add(1); + self.handle_new_ticket_impl(&mut kcx, nst) + } + + fn handle_key_update( + &mut self, + common: &mut CommonState, + key_update_request: &KeyUpdateRequest, + ) -> Result<(), Error> { + if let Protocol::Quic = common.protocol { + return Err(common.send_fatal_alert( + AlertDescription::UnexpectedMessage, + PeerMisbehaved::KeyUpdateReceivedInQuicConnection, + )); + } + + // Mustn't be interleaved with other handshake messages. + common.check_aligned_handshake()?; + + if common.should_update_key(key_update_request)? { + self.key_schedule + .update_encrypter_and_notify(common); + } + + // Update our read-side keys. + self.key_schedule + .update_decrypter(common); + Ok(()) + } +} + +impl State<ClientConnectionData> for ExpectTraffic { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ApplicationData(payload) => cx + .common + .take_received_plaintext(payload), + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::NewSessionTicketTls13(new_ticket)), + .. + } => self.handle_new_ticket_tls13(cx, &new_ticket)?, + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::KeyUpdate(key_update)), + .. + } => self.handle_key_update(cx.common, &key_update)?, + payload => { + return Err(inappropriate_handshake_message( + &payload, + &[ContentType::ApplicationData, ContentType::Handshake], + &[HandshakeType::NewSessionTicket, HandshakeType::KeyUpdate], + )); + } + } + + Ok(self) + } + + fn send_key_update_request(&mut self, common: &mut CommonState) -> Result<(), Error> { + self.key_schedule + .request_key_update_and_update_encrypter(common) + } + + fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) -> Result<(), Error> { + self.key_schedule + .export_keying_material(output, label, context) + } + + fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> { + self.key_schedule + .extract_secrets(Side::Client) + } + + fn into_external_state(self: Box<Self>) -> Result<Box<dyn KernelState + 'static>, Error> { + Ok(self) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +impl KernelState for ExpectTraffic { + fn update_secrets(&mut self, dir: Direction) -> Result<ConnectionTrafficSecrets, Error> { + self.key_schedule + .refresh_traffic_secret(match dir { + Direction::Transmit => Side::Client, + Direction::Receive => Side::Server, + }) + } + + fn handle_new_session_ticket( + &mut self, + cx: &mut KernelContext<'_>, + message: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + self.handle_new_ticket_impl(cx, message) + } +} + +struct ExpectQuicTraffic(ExpectTraffic); + +impl State<ClientConnectionData> for ExpectQuicTraffic { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ClientContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let nst = require_handshake_msg!( + m, + HandshakeType::NewSessionTicket, + HandshakePayload::NewSessionTicketTls13 + )?; + self.0 + .handle_new_ticket_tls13(cx, nst)?; + Ok(self) + } + + fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) -> Result<(), Error> { + self.0 + .export_keying_material(output, label, context) + } + + fn into_external_state(self: Box<Self>) -> Result<Box<dyn KernelState + 'static>, Error> { + Ok(self) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +impl KernelState for ExpectQuicTraffic { + fn update_secrets(&mut self, _: Direction) -> Result<ConnectionTrafficSecrets, Error> { + Err(Error::General( + "KeyUpdate is not supported for QUIC connections".into(), + )) + } + + fn handle_new_session_ticket( + &mut self, + cx: &mut KernelContext<'_>, + nst: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + self.0.handle_new_ticket_impl(cx, nst) + } +} diff --git a/vendor/rustls/src/common_state.rs b/vendor/rustls/src/common_state.rs new file mode 100644 index 00000000..4d47cf15 --- /dev/null +++ b/vendor/rustls/src/common_state.rs @@ -0,0 +1,1049 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; + +use pki_types::CertificateDer; + +use crate::conn::kernel::KernelState; +use crate::crypto::SupportedKxGroup; +use crate::enums::{AlertDescription, ContentType, HandshakeType, ProtocolVersion}; +use crate::error::{Error, InvalidMessage, PeerMisbehaved}; +use crate::hash_hs::HandshakeHash; +use crate::log::{debug, error, warn}; +use crate::msgs::alert::AlertMessagePayload; +use crate::msgs::base::Payload; +use crate::msgs::codec::Codec; +use crate::msgs::enums::{AlertLevel, KeyUpdateRequest}; +use crate::msgs::fragmenter::MessageFragmenter; +use crate::msgs::handshake::{CertificateChain, HandshakeMessagePayload, ProtocolName}; +use crate::msgs::message::{ + Message, MessagePayload, OutboundChunks, OutboundOpaqueMessage, OutboundPlainMessage, + PlainMessage, +}; +use crate::record_layer::PreEncryptAction; +use crate::suites::{PartiallyExtractedSecrets, SupportedCipherSuite}; +#[cfg(feature = "tls12")] +use crate::tls12::ConnectionSecrets; +use crate::unbuffered::{EncryptError, InsufficientSizeError}; +use crate::vecbuf::ChunkVecBuffer; +use crate::{quic, record_layer}; + +/// Connection state common to both client and server connections. +pub struct CommonState { + pub(crate) negotiated_version: Option<ProtocolVersion>, + pub(crate) handshake_kind: Option<HandshakeKind>, + pub(crate) side: Side, + pub(crate) record_layer: record_layer::RecordLayer, + pub(crate) suite: Option<SupportedCipherSuite>, + pub(crate) kx_state: KxState, + pub(crate) alpn_protocol: Option<ProtocolName>, + pub(crate) aligned_handshake: bool, + pub(crate) may_send_application_data: bool, + pub(crate) may_receive_application_data: bool, + pub(crate) early_traffic: bool, + sent_fatal_alert: bool, + /// If we signaled end of stream. + pub(crate) has_sent_close_notify: bool, + /// If the peer has signaled end of stream. + pub(crate) has_received_close_notify: bool, + #[cfg(feature = "std")] + pub(crate) has_seen_eof: bool, + pub(crate) peer_certificates: Option<CertificateChain<'static>>, + message_fragmenter: MessageFragmenter, + pub(crate) received_plaintext: ChunkVecBuffer, + pub(crate) sendable_tls: ChunkVecBuffer, + queued_key_update_message: Option<Vec<u8>>, + + /// Protocol whose key schedule should be used. Unused for TLS < 1.3. + pub(crate) protocol: Protocol, + pub(crate) quic: quic::Quic, + pub(crate) enable_secret_extraction: bool, + temper_counters: TemperCounters, + pub(crate) refresh_traffic_keys_pending: bool, + pub(crate) fips: bool, + pub(crate) tls13_tickets_received: u32, +} + +impl CommonState { + pub(crate) fn new(side: Side) -> Self { + Self { + negotiated_version: None, + handshake_kind: None, + side, + record_layer: record_layer::RecordLayer::new(), + suite: None, + kx_state: KxState::default(), + alpn_protocol: None, + aligned_handshake: true, + may_send_application_data: false, + may_receive_application_data: false, + early_traffic: false, + sent_fatal_alert: false, + has_sent_close_notify: false, + has_received_close_notify: false, + #[cfg(feature = "std")] + has_seen_eof: false, + peer_certificates: None, + message_fragmenter: MessageFragmenter::default(), + received_plaintext: ChunkVecBuffer::new(Some(DEFAULT_RECEIVED_PLAINTEXT_LIMIT)), + sendable_tls: ChunkVecBuffer::new(Some(DEFAULT_BUFFER_LIMIT)), + queued_key_update_message: None, + protocol: Protocol::Tcp, + quic: quic::Quic::default(), + enable_secret_extraction: false, + temper_counters: TemperCounters::default(), + refresh_traffic_keys_pending: false, + fips: false, + tls13_tickets_received: 0, + } + } + + /// Returns true if the caller should call [`Connection::write_tls`] as soon as possible. + /// + /// [`Connection::write_tls`]: crate::Connection::write_tls + pub fn wants_write(&self) -> bool { + !self.sendable_tls.is_empty() + } + + /// Returns true if the connection is currently performing the TLS handshake. + /// + /// During this time plaintext written to the connection is buffered in memory. After + /// [`Connection::process_new_packets()`] has been called, this might start to return `false` + /// while the final handshake packets still need to be extracted from the connection's buffers. + /// + /// [`Connection::process_new_packets()`]: crate::Connection::process_new_packets + pub fn is_handshaking(&self) -> bool { + !(self.may_send_application_data && self.may_receive_application_data) + } + + /// Retrieves the certificate chain or the raw public key used by the peer to authenticate. + /// + /// The order of the certificate chain is as it appears in the TLS + /// protocol: the first certificate relates to the peer, the + /// second certifies the first, the third certifies the second, and + /// so on. + /// + /// When using raw public keys, the first and only element is the raw public key. + /// + /// This is made available for both full and resumed handshakes. + /// + /// For clients, this is the certificate chain or the raw public key of the server. + /// + /// For servers, this is the certificate chain or the raw public key of the client, + /// if client authentication was completed. + /// + /// The return value is None until this value is available. + /// + /// Note: the return type of the 'certificate', when using raw public keys is `CertificateDer<'static>` + /// even though this should technically be a `SubjectPublicKeyInfoDer<'static>`. + /// This choice simplifies the API and ensures backwards compatibility. + pub fn peer_certificates(&self) -> Option<&[CertificateDer<'static>]> { + self.peer_certificates.as_deref() + } + + /// Retrieves the protocol agreed with the peer via ALPN. + /// + /// A return value of `None` after handshake completion + /// means no protocol was agreed (because no protocols + /// were offered or accepted by the peer). + pub fn alpn_protocol(&self) -> Option<&[u8]> { + self.get_alpn_protocol() + } + + /// Retrieves the ciphersuite agreed with the peer. + /// + /// This returns None until the ciphersuite is agreed. + pub fn negotiated_cipher_suite(&self) -> Option<SupportedCipherSuite> { + self.suite + } + + /// Retrieves the key exchange group agreed with the peer. + /// + /// This function may return `None` depending on the state of the connection, + /// the type of handshake, and the protocol version. + /// + /// If [`CommonState::is_handshaking()`] is true this function will return `None`. + /// Similarly, if the [`CommonState::handshake_kind()`] is [`HandshakeKind::Resumed`] + /// and the [`CommonState::protocol_version()`] is TLS 1.2, then no key exchange will have + /// occurred and this function will return `None`. + pub fn negotiated_key_exchange_group(&self) -> Option<&'static dyn SupportedKxGroup> { + match self.kx_state { + KxState::Complete(group) => Some(group), + _ => None, + } + } + + /// Retrieves the protocol version agreed with the peer. + /// + /// This returns `None` until the version is agreed. + pub fn protocol_version(&self) -> Option<ProtocolVersion> { + self.negotiated_version + } + + /// Which kind of handshake was performed. + /// + /// This tells you whether the handshake was a resumption or not. + /// + /// This will return `None` before it is known which sort of + /// handshake occurred. + pub fn handshake_kind(&self) -> Option<HandshakeKind> { + self.handshake_kind + } + + pub(crate) fn is_tls13(&self) -> bool { + matches!(self.negotiated_version, Some(ProtocolVersion::TLSv1_3)) + } + + pub(crate) fn process_main_protocol<Data>( + &mut self, + msg: Message<'_>, + mut state: Box<dyn State<Data>>, + data: &mut Data, + sendable_plaintext: Option<&mut ChunkVecBuffer>, + ) -> Result<Box<dyn State<Data>>, Error> { + // For TLS1.2, outside of the handshake, send rejection alerts for + // renegotiation requests. These can occur any time. + if self.may_receive_application_data && !self.is_tls13() { + let reject_ty = match self.side { + Side::Client => HandshakeType::HelloRequest, + Side::Server => HandshakeType::ClientHello, + }; + if msg.is_handshake_type(reject_ty) { + self.temper_counters + .received_renegotiation_request()?; + self.send_warning_alert(AlertDescription::NoRenegotiation); + return Ok(state); + } + } + + let mut cx = Context { + common: self, + data, + sendable_plaintext, + }; + match state.handle(&mut cx, msg) { + Ok(next) => { + state = next.into_owned(); + Ok(state) + } + Err(e @ Error::InappropriateMessage { .. }) + | Err(e @ Error::InappropriateHandshakeMessage { .. }) => { + Err(self.send_fatal_alert(AlertDescription::UnexpectedMessage, e)) + } + Err(e) => Err(e), + } + } + + pub(crate) fn write_plaintext( + &mut self, + payload: OutboundChunks<'_>, + outgoing_tls: &mut [u8], + ) -> Result<usize, EncryptError> { + if payload.is_empty() { + return Ok(0); + } + + let fragments = self + .message_fragmenter + .fragment_payload( + ContentType::ApplicationData, + ProtocolVersion::TLSv1_2, + payload.clone(), + ); + + for f in 0..fragments.len() { + match self + .record_layer + .pre_encrypt_action(f as u64) + { + PreEncryptAction::Nothing => {} + PreEncryptAction::RefreshOrClose => match self.negotiated_version { + Some(ProtocolVersion::TLSv1_3) => { + // driven by caller, as we don't have the `State` here + self.refresh_traffic_keys_pending = true; + } + _ => { + error!( + "traffic keys exhausted, closing connection to prevent security failure" + ); + self.send_close_notify(); + return Err(EncryptError::EncryptExhausted); + } + }, + PreEncryptAction::Refuse => { + return Err(EncryptError::EncryptExhausted); + } + } + } + + self.perhaps_write_key_update(); + + self.check_required_size(outgoing_tls, fragments)?; + + let fragments = self + .message_fragmenter + .fragment_payload( + ContentType::ApplicationData, + ProtocolVersion::TLSv1_2, + payload, + ); + + Ok(self.write_fragments(outgoing_tls, fragments)) + } + + // Changing the keys must not span any fragmented handshake + // messages. Otherwise the defragmented messages will have + // been protected with two different record layer protections, + // which is illegal. Not mentioned in RFC. + pub(crate) fn check_aligned_handshake(&mut self) -> Result<(), Error> { + if !self.aligned_handshake { + Err(self.send_fatal_alert( + AlertDescription::UnexpectedMessage, + PeerMisbehaved::KeyEpochWithPendingFragment, + )) + } else { + Ok(()) + } + } + + /// Fragment `m`, encrypt the fragments, and then queue + /// the encrypted fragments for sending. + pub(crate) fn send_msg_encrypt(&mut self, m: PlainMessage) { + let iter = self + .message_fragmenter + .fragment_message(&m); + for m in iter { + self.send_single_fragment(m); + } + } + + /// Like send_msg_encrypt, but operate on an appdata directly. + fn send_appdata_encrypt(&mut self, payload: OutboundChunks<'_>, limit: Limit) -> usize { + // Here, the limit on sendable_tls applies to encrypted data, + // but we're respecting it for plaintext data -- so we'll + // be out by whatever the cipher+record overhead is. That's a + // constant and predictable amount, so it's not a terrible issue. + let len = match limit { + #[cfg(feature = "std")] + Limit::Yes => self + .sendable_tls + .apply_limit(payload.len()), + Limit::No => payload.len(), + }; + + let iter = self + .message_fragmenter + .fragment_payload( + ContentType::ApplicationData, + ProtocolVersion::TLSv1_2, + payload.split_at(len).0, + ); + for m in iter { + self.send_single_fragment(m); + } + + len + } + + fn send_single_fragment(&mut self, m: OutboundPlainMessage<'_>) { + if m.typ == ContentType::Alert { + // Alerts are always sendable -- never quashed by a PreEncryptAction. + let em = self.record_layer.encrypt_outgoing(m); + self.queue_tls_message(em); + return; + } + + match self + .record_layer + .next_pre_encrypt_action() + { + PreEncryptAction::Nothing => {} + + // Close connection once we start to run out of + // sequence space. + PreEncryptAction::RefreshOrClose => { + match self.negotiated_version { + Some(ProtocolVersion::TLSv1_3) => { + // driven by caller, as we don't have the `State` here + self.refresh_traffic_keys_pending = true; + } + _ => { + error!( + "traffic keys exhausted, closing connection to prevent security failure" + ); + self.send_close_notify(); + return; + } + } + } + + // Refuse to wrap counter at all costs. This + // is basically untestable unfortunately. + PreEncryptAction::Refuse => { + return; + } + }; + + let em = self.record_layer.encrypt_outgoing(m); + self.queue_tls_message(em); + } + + fn send_plain_non_buffering(&mut self, payload: OutboundChunks<'_>, limit: Limit) -> usize { + debug_assert!(self.may_send_application_data); + debug_assert!(self.record_layer.is_encrypting()); + + if payload.is_empty() { + // Don't send empty fragments. + return 0; + } + + self.send_appdata_encrypt(payload, limit) + } + + /// Mark the connection as ready to send application data. + /// + /// Also flush `sendable_plaintext` if it is `Some`. + pub(crate) fn start_outgoing_traffic( + &mut self, + sendable_plaintext: &mut Option<&mut ChunkVecBuffer>, + ) { + self.may_send_application_data = true; + if let Some(sendable_plaintext) = sendable_plaintext { + self.flush_plaintext(sendable_plaintext); + } + } + + /// Mark the connection as ready to send and receive application data. + /// + /// Also flush `sendable_plaintext` if it is `Some`. + pub(crate) fn start_traffic(&mut self, sendable_plaintext: &mut Option<&mut ChunkVecBuffer>) { + self.may_receive_application_data = true; + self.start_outgoing_traffic(sendable_plaintext); + } + + /// Send any buffered plaintext. Plaintext is buffered if + /// written during handshake. + fn flush_plaintext(&mut self, sendable_plaintext: &mut ChunkVecBuffer) { + if !self.may_send_application_data { + return; + } + + while let Some(buf) = sendable_plaintext.pop() { + self.send_plain_non_buffering(buf.as_slice().into(), Limit::No); + } + } + + // Put m into sendable_tls for writing. + fn queue_tls_message(&mut self, m: OutboundOpaqueMessage) { + self.perhaps_write_key_update(); + self.sendable_tls.append(m.encode()); + } + + pub(crate) fn perhaps_write_key_update(&mut self) { + if let Some(message) = self.queued_key_update_message.take() { + self.sendable_tls.append(message); + } + } + + /// Send a raw TLS message, fragmenting it if needed. + pub(crate) fn send_msg(&mut self, m: Message<'_>, must_encrypt: bool) { + { + if let Protocol::Quic = self.protocol { + if let MessagePayload::Alert(alert) = m.payload { + self.quic.alert = Some(alert.description); + } else { + debug_assert!( + matches!( + m.payload, + MessagePayload::Handshake { .. } | MessagePayload::HandshakeFlight(_) + ), + "QUIC uses TLS for the cryptographic handshake only" + ); + let mut bytes = Vec::new(); + m.payload.encode(&mut bytes); + self.quic + .hs_queue + .push_back((must_encrypt, bytes)); + } + return; + } + } + if !must_encrypt { + let msg = &m.into(); + let iter = self + .message_fragmenter + .fragment_message(msg); + for m in iter { + self.queue_tls_message(m.to_unencrypted_opaque()); + } + } else { + self.send_msg_encrypt(m.into()); + } + } + + pub(crate) fn take_received_plaintext(&mut self, bytes: Payload<'_>) { + self.received_plaintext + .append(bytes.into_vec()); + } + + #[cfg(feature = "tls12")] + pub(crate) fn start_encryption_tls12(&mut self, secrets: &ConnectionSecrets, side: Side) { + let (dec, enc) = secrets.make_cipher_pair(side); + self.record_layer + .prepare_message_encrypter( + enc, + secrets + .suite() + .common + .confidentiality_limit, + ); + self.record_layer + .prepare_message_decrypter(dec); + } + + pub(crate) fn missing_extension(&mut self, why: PeerMisbehaved) -> Error { + self.send_fatal_alert(AlertDescription::MissingExtension, why) + } + + fn send_warning_alert(&mut self, desc: AlertDescription) { + warn!("Sending warning alert {desc:?}"); + self.send_warning_alert_no_log(desc); + } + + pub(crate) fn process_alert(&mut self, alert: &AlertMessagePayload) -> Result<(), Error> { + // Reject unknown AlertLevels. + if let AlertLevel::Unknown(_) = alert.level { + return Err(self.send_fatal_alert( + AlertDescription::IllegalParameter, + Error::AlertReceived(alert.description), + )); + } + + // If we get a CloseNotify, make a note to declare EOF to our + // caller. But do not treat unauthenticated alerts like this. + if self.may_receive_application_data && alert.description == AlertDescription::CloseNotify { + self.has_received_close_notify = true; + return Ok(()); + } + + // Warnings are nonfatal for TLS1.2, but outlawed in TLS1.3 + // (except, for no good reason, user_cancelled). + let err = Error::AlertReceived(alert.description); + if alert.level == AlertLevel::Warning { + self.temper_counters + .received_warning_alert()?; + if self.is_tls13() && alert.description != AlertDescription::UserCanceled { + return Err(self.send_fatal_alert(AlertDescription::DecodeError, err)); + } + + // Some implementations send pointless `user_canceled` alerts, don't log them + // in release mode (https://bugs.openjdk.org/browse/JDK-8323517). + if alert.description != AlertDescription::UserCanceled || cfg!(debug_assertions) { + warn!("TLS alert warning received: {alert:?}"); + } + + return Ok(()); + } + + Err(err) + } + + pub(crate) fn send_cert_verify_error_alert(&mut self, err: Error) -> Error { + self.send_fatal_alert( + match &err { + Error::InvalidCertificate(e) => e.clone().into(), + Error::PeerMisbehaved(_) => AlertDescription::IllegalParameter, + _ => AlertDescription::HandshakeFailure, + }, + err, + ) + } + + pub(crate) fn send_fatal_alert( + &mut self, + desc: AlertDescription, + err: impl Into<Error>, + ) -> Error { + debug_assert!(!self.sent_fatal_alert); + let m = Message::build_alert(AlertLevel::Fatal, desc); + self.send_msg(m, self.record_layer.is_encrypting()); + self.sent_fatal_alert = true; + err.into() + } + + /// Queues a `close_notify` warning alert to be sent in the next + /// [`Connection::write_tls`] call. This informs the peer that the + /// connection is being closed. + /// + /// Does nothing if any `close_notify` or fatal alert was already sent. + /// + /// [`Connection::write_tls`]: crate::Connection::write_tls + pub fn send_close_notify(&mut self) { + if self.sent_fatal_alert { + return; + } + debug!("Sending warning alert {:?}", AlertDescription::CloseNotify); + self.sent_fatal_alert = true; + self.has_sent_close_notify = true; + self.send_warning_alert_no_log(AlertDescription::CloseNotify); + } + + pub(crate) fn eager_send_close_notify( + &mut self, + outgoing_tls: &mut [u8], + ) -> Result<usize, EncryptError> { + self.send_close_notify(); + self.check_required_size(outgoing_tls, [].into_iter())?; + Ok(self.write_fragments(outgoing_tls, [].into_iter())) + } + + fn send_warning_alert_no_log(&mut self, desc: AlertDescription) { + let m = Message::build_alert(AlertLevel::Warning, desc); + self.send_msg(m, self.record_layer.is_encrypting()); + } + + fn check_required_size<'a>( + &self, + outgoing_tls: &mut [u8], + fragments: impl Iterator<Item = OutboundPlainMessage<'a>>, + ) -> Result<(), EncryptError> { + let mut required_size = self.sendable_tls.len(); + + for m in fragments { + required_size += m.encoded_len(&self.record_layer); + } + + if required_size > outgoing_tls.len() { + return Err(EncryptError::InsufficientSize(InsufficientSizeError { + required_size, + })); + } + + Ok(()) + } + + fn write_fragments<'a>( + &mut self, + outgoing_tls: &mut [u8], + fragments: impl Iterator<Item = OutboundPlainMessage<'a>>, + ) -> usize { + let mut written = 0; + + // Any pre-existing encrypted messages in `sendable_tls` must + // be output before encrypting any of the `fragments`. + while let Some(message) = self.sendable_tls.pop() { + let len = message.len(); + outgoing_tls[written..written + len].copy_from_slice(&message); + written += len; + } + + for m in fragments { + let em = self + .record_layer + .encrypt_outgoing(m) + .encode(); + + let len = em.len(); + outgoing_tls[written..written + len].copy_from_slice(&em); + written += len; + } + + written + } + + pub(crate) fn set_max_fragment_size(&mut self, new: Option<usize>) -> Result<(), Error> { + self.message_fragmenter + .set_max_fragment_size(new) + } + + pub(crate) fn get_alpn_protocol(&self) -> Option<&[u8]> { + self.alpn_protocol + .as_ref() + .map(AsRef::as_ref) + } + + /// Returns true if the caller should call [`Connection::read_tls`] as soon + /// as possible. + /// + /// If there is pending plaintext data to read with [`Connection::reader`], + /// this returns false. If your application respects this mechanism, + /// only one full TLS message will be buffered by rustls. + /// + /// [`Connection::reader`]: crate::Connection::reader + /// [`Connection::read_tls`]: crate::Connection::read_tls + pub fn wants_read(&self) -> bool { + // We want to read more data all the time, except when we have unprocessed plaintext. + // This provides back-pressure to the TCP buffers. We also don't want to read more after + // the peer has sent us a close notification. + // + // In the handshake case we don't have readable plaintext before the handshake has + // completed, but also don't want to read if we still have sendable tls. + self.received_plaintext.is_empty() + && !self.has_received_close_notify + && (self.may_send_application_data || self.sendable_tls.is_empty()) + } + + pub(crate) fn current_io_state(&self) -> IoState { + IoState { + tls_bytes_to_write: self.sendable_tls.len(), + plaintext_bytes_to_read: self.received_plaintext.len(), + peer_has_closed: self.has_received_close_notify, + } + } + + pub(crate) fn is_quic(&self) -> bool { + self.protocol == Protocol::Quic + } + + pub(crate) fn should_update_key( + &mut self, + key_update_request: &KeyUpdateRequest, + ) -> Result<bool, Error> { + self.temper_counters + .received_key_update_request()?; + + match key_update_request { + KeyUpdateRequest::UpdateNotRequested => Ok(false), + KeyUpdateRequest::UpdateRequested => Ok(self.queued_key_update_message.is_none()), + _ => Err(self.send_fatal_alert( + AlertDescription::IllegalParameter, + InvalidMessage::InvalidKeyUpdate, + )), + } + } + + pub(crate) fn enqueue_key_update_notification(&mut self) { + let message = PlainMessage::from(Message::build_key_update_notify()); + self.queued_key_update_message = Some( + self.record_layer + .encrypt_outgoing(message.borrow_outbound()) + .encode(), + ); + } + + pub(crate) fn received_tls13_change_cipher_spec(&mut self) -> Result<(), Error> { + self.temper_counters + .received_tls13_change_cipher_spec() + } +} + +#[cfg(feature = "std")] +impl CommonState { + /// Send plaintext application data, fragmenting and + /// encrypting it as it goes out. + /// + /// If internal buffers are too small, this function will not accept + /// all the data. + pub(crate) fn buffer_plaintext( + &mut self, + payload: OutboundChunks<'_>, + sendable_plaintext: &mut ChunkVecBuffer, + ) -> usize { + self.perhaps_write_key_update(); + self.send_plain(payload, Limit::Yes, sendable_plaintext) + } + + pub(crate) fn send_early_plaintext(&mut self, data: &[u8]) -> usize { + debug_assert!(self.early_traffic); + debug_assert!(self.record_layer.is_encrypting()); + + if data.is_empty() { + // Don't send empty fragments. + return 0; + } + + self.send_appdata_encrypt(data.into(), Limit::Yes) + } + + /// Encrypt and send some plaintext `data`. `limit` controls + /// whether the per-connection buffer limits apply. + /// + /// Returns the number of bytes written from `data`: this might + /// be less than `data.len()` if buffer limits were exceeded. + fn send_plain( + &mut self, + payload: OutboundChunks<'_>, + limit: Limit, + sendable_plaintext: &mut ChunkVecBuffer, + ) -> usize { + if !self.may_send_application_data { + // If we haven't completed handshaking, buffer + // plaintext to send once we do. + let len = match limit { + Limit::Yes => sendable_plaintext.append_limited_copy(payload), + Limit::No => sendable_plaintext.append(payload.to_vec()), + }; + return len; + } + + self.send_plain_non_buffering(payload, limit) + } +} + +/// Describes which sort of handshake happened. +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum HandshakeKind { + /// A full handshake. + /// + /// This is the typical TLS connection initiation process when resumption is + /// not yet unavailable, and the initial `ClientHello` was accepted by the server. + Full, + + /// A full TLS1.3 handshake, with an extra round-trip for a `HelloRetryRequest`. + /// + /// The server can respond with a `HelloRetryRequest` if the initial `ClientHello` + /// is unacceptable for several reasons, the most likely if no supported key + /// shares were offered by the client. + FullWithHelloRetryRequest, + + /// A resumed handshake. + /// + /// Resumed handshakes involve fewer round trips and less cryptography than + /// full ones, but can only happen when the peers have previously done a full + /// handshake together, and then remember data about it. + Resumed, +} + +/// Values of this structure are returned from [`Connection::process_new_packets`] +/// and tell the caller the current I/O state of the TLS connection. +/// +/// [`Connection::process_new_packets`]: crate::Connection::process_new_packets +#[derive(Debug, Eq, PartialEq)] +pub struct IoState { + tls_bytes_to_write: usize, + plaintext_bytes_to_read: usize, + peer_has_closed: bool, +} + +impl IoState { + /// How many bytes could be written by [`Connection::write_tls`] if called + /// right now. A non-zero value implies [`CommonState::wants_write`]. + /// + /// [`Connection::write_tls`]: crate::Connection::write_tls + pub fn tls_bytes_to_write(&self) -> usize { + self.tls_bytes_to_write + } + + /// How many plaintext bytes could be obtained via [`std::io::Read`] + /// without further I/O. + pub fn plaintext_bytes_to_read(&self) -> usize { + self.plaintext_bytes_to_read + } + + /// True if the peer has sent us a close_notify alert. This is + /// the TLS mechanism to securely half-close a TLS connection, + /// and signifies that the peer will not send any further data + /// on this connection. + /// + /// This is also signalled via returning `Ok(0)` from + /// [`std::io::Read`], after all the received bytes have been + /// retrieved. + pub fn peer_has_closed(&self) -> bool { + self.peer_has_closed + } +} + +pub(crate) trait State<Data>: Send + Sync { + fn handle<'m>( + self: Box<Self>, + cx: &mut Context<'_, Data>, + message: Message<'m>, + ) -> Result<Box<dyn State<Data> + 'm>, Error> + where + Self: 'm; + + fn export_keying_material( + &self, + _output: &mut [u8], + _label: &[u8], + _context: Option<&[u8]>, + ) -> Result<(), Error> { + Err(Error::HandshakeNotComplete) + } + + fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> { + Err(Error::HandshakeNotComplete) + } + + fn send_key_update_request(&mut self, _common: &mut CommonState) -> Result<(), Error> { + Err(Error::HandshakeNotComplete) + } + + fn handle_decrypt_error(&self) {} + + fn into_external_state(self: Box<Self>) -> Result<Box<dyn KernelState + 'static>, Error> { + Err(Error::HandshakeNotComplete) + } + + fn into_owned(self: Box<Self>) -> Box<dyn State<Data> + 'static>; +} + +pub(crate) struct Context<'a, Data> { + pub(crate) common: &'a mut CommonState, + pub(crate) data: &'a mut Data, + /// Buffered plaintext. This is `Some` if any plaintext was written during handshake and `None` + /// otherwise. + pub(crate) sendable_plaintext: Option<&'a mut ChunkVecBuffer>, +} + +/// Side of the connection. +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum Side { + /// A client initiates the connection. + Client, + /// A server waits for a client to connect. + Server, +} + +impl Side { + pub(crate) fn peer(&self) -> Self { + match self { + Self::Client => Self::Server, + Self::Server => Self::Client, + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub(crate) enum Protocol { + Tcp, + Quic, +} + +enum Limit { + #[cfg(feature = "std")] + Yes, + No, +} + +/// Tracking technically-allowed protocol actions +/// that we limit to avoid denial-of-service vectors. +struct TemperCounters { + allowed_warning_alerts: u8, + allowed_renegotiation_requests: u8, + allowed_key_update_requests: u8, + allowed_middlebox_ccs: u8, +} + +impl TemperCounters { + fn received_warning_alert(&mut self) -> Result<(), Error> { + match self.allowed_warning_alerts { + 0 => Err(PeerMisbehaved::TooManyWarningAlertsReceived.into()), + _ => { + self.allowed_warning_alerts -= 1; + Ok(()) + } + } + } + + fn received_renegotiation_request(&mut self) -> Result<(), Error> { + match self.allowed_renegotiation_requests { + 0 => Err(PeerMisbehaved::TooManyRenegotiationRequests.into()), + _ => { + self.allowed_renegotiation_requests -= 1; + Ok(()) + } + } + } + + fn received_key_update_request(&mut self) -> Result<(), Error> { + match self.allowed_key_update_requests { + 0 => Err(PeerMisbehaved::TooManyKeyUpdateRequests.into()), + _ => { + self.allowed_key_update_requests -= 1; + Ok(()) + } + } + } + + fn received_tls13_change_cipher_spec(&mut self) -> Result<(), Error> { + match self.allowed_middlebox_ccs { + 0 => Err(PeerMisbehaved::IllegalMiddleboxChangeCipherSpec.into()), + _ => { + self.allowed_middlebox_ccs -= 1; + Ok(()) + } + } + } +} + +impl Default for TemperCounters { + fn default() -> Self { + Self { + // cf. BoringSSL `kMaxWarningAlerts` + // <https://github.com/google/boringssl/blob/dec5989b793c56ad4dd32173bd2d8595ca78b398/ssl/tls_record.cc#L137-L139> + allowed_warning_alerts: 4, + + // we rebuff renegotiation requests with a `NoRenegotiation` warning alerts. + // a second request after this is fatal. + allowed_renegotiation_requests: 1, + + // cf. BoringSSL `kMaxKeyUpdates` + // <https://github.com/google/boringssl/blob/dec5989b793c56ad4dd32173bd2d8595ca78b398/ssl/tls13_both.cc#L35-L38> + allowed_key_update_requests: 32, + + // At most two CCS are allowed: one after each ClientHello (recall a second + // ClientHello happens after a HelloRetryRequest). + // + // note BoringSSL allows up to 32. + allowed_middlebox_ccs: 2, + } + } +} + +#[derive(Debug, Default)] +pub(crate) enum KxState { + #[default] + None, + Start(&'static dyn SupportedKxGroup), + Complete(&'static dyn SupportedKxGroup), +} + +impl KxState { + pub(crate) fn complete(&mut self) { + debug_assert!(matches!(self, Self::Start(_))); + if let Self::Start(group) = self { + *self = Self::Complete(*group); + } + } +} + +pub(crate) struct HandshakeFlight<'a, const TLS13: bool> { + pub(crate) transcript: &'a mut HandshakeHash, + body: Vec<u8>, +} + +impl<'a, const TLS13: bool> HandshakeFlight<'a, TLS13> { + pub(crate) fn new(transcript: &'a mut HandshakeHash) -> Self { + Self { + transcript, + body: Vec::new(), + } + } + + pub(crate) fn add(&mut self, hs: HandshakeMessagePayload<'_>) { + let start_len = self.body.len(); + hs.encode(&mut self.body); + self.transcript + .add(&self.body[start_len..]); + } + + pub(crate) fn finish(self, common: &mut CommonState) { + common.send_msg( + Message { + version: match TLS13 { + true => ProtocolVersion::TLSv1_3, + false => ProtocolVersion::TLSv1_2, + }, + payload: MessagePayload::HandshakeFlight(Payload::new(self.body)), + }, + TLS13, + ); + } +} + +#[cfg(feature = "tls12")] +pub(crate) type HandshakeFlightTls12<'a> = HandshakeFlight<'a, false>; +pub(crate) type HandshakeFlightTls13<'a> = HandshakeFlight<'a, true>; + +const DEFAULT_RECEIVED_PLAINTEXT_LIMIT: usize = 16 * 1024; +pub(crate) const DEFAULT_BUFFER_LIMIT: usize = 64 * 1024; diff --git a/vendor/rustls/src/compress.rs b/vendor/rustls/src/compress.rs new file mode 100644 index 00000000..d34cbaff --- /dev/null +++ b/vendor/rustls/src/compress.rs @@ -0,0 +1,677 @@ +//! Certificate compression and decompression support +//! +//! This crate supports compression and decompression everywhere +//! certificates are used, in accordance with [RFC8879][rfc8879]. +//! +//! Note that this is only supported for TLS1.3 connections. +//! +//! # Getting started +//! +//! Build this crate with the `brotli` and/or `zlib` crate features. This +//! adds dependencies on these crates. They are used by default if enabled. +//! +//! We especially recommend `brotli` as it has the widest deployment so far. +//! +//! # Custom compression/decompression implementations +//! +//! 1. Implement the [`CertCompressor`] and/or [`CertDecompressor`] traits +//! 2. Provide those to: +//! - [`ClientConfig::cert_compressors`][cc_cc] or [`ServerConfig::cert_compressors`][sc_cc]. +//! - [`ClientConfig::cert_decompressors`][cc_cd] or [`ServerConfig::cert_decompressors`][sc_cd]. +//! +//! These are used in these circumstances: +//! +//! | Peer | Client authentication | Server authentication | +//! | ---- | --------------------- | --------------------- | +//! | *Client* | [`ClientConfig::cert_compressors`][cc_cc] | [`ClientConfig::cert_decompressors`][cc_cd] | +//! | *Server* | [`ServerConfig::cert_decompressors`][sc_cd] | [`ServerConfig::cert_compressors`][sc_cc] | +//! +//! [rfc8879]: https://datatracker.ietf.org/doc/html/rfc8879 +//! [cc_cc]: crate::ClientConfig::cert_compressors +//! [sc_cc]: crate::ServerConfig::cert_compressors +//! [cc_cd]: crate::ClientConfig::cert_decompressors +//! [sc_cd]: crate::ServerConfig::cert_decompressors + +#[cfg(feature = "std")] +use alloc::collections::VecDeque; +use alloc::vec::Vec; +use core::fmt::Debug; +#[cfg(feature = "std")] +use std::sync::Mutex; + +use crate::enums::CertificateCompressionAlgorithm; +use crate::msgs::base::{Payload, PayloadU24}; +use crate::msgs::codec::Codec; +use crate::msgs::handshake::{CertificatePayloadTls13, CompressedCertificatePayload}; +use crate::sync::Arc; + +/// Returns the supported `CertDecompressor` implementations enabled +/// by crate features. +pub fn default_cert_decompressors() -> &'static [&'static dyn CertDecompressor] { + &[ + #[cfg(feature = "brotli")] + BROTLI_DECOMPRESSOR, + #[cfg(feature = "zlib")] + ZLIB_DECOMPRESSOR, + ] +} + +/// An available certificate decompression algorithm. +pub trait CertDecompressor: Debug + Send + Sync { + /// Decompress `input`, writing the result to `output`. + /// + /// `output` is sized to match the declared length of the decompressed data. + /// + /// `Err(DecompressionFailed)` should be returned if decompression produces more, or fewer + /// bytes than fit in `output`, or if the `input` is in any way malformed. + fn decompress(&self, input: &[u8], output: &mut [u8]) -> Result<(), DecompressionFailed>; + + /// Which algorithm this decompressor handles. + fn algorithm(&self) -> CertificateCompressionAlgorithm; +} + +/// Returns the supported `CertCompressor` implementations enabled +/// by crate features. +pub fn default_cert_compressors() -> &'static [&'static dyn CertCompressor] { + &[ + #[cfg(feature = "brotli")] + BROTLI_COMPRESSOR, + #[cfg(feature = "zlib")] + ZLIB_COMPRESSOR, + ] +} + +/// An available certificate compression algorithm. +pub trait CertCompressor: Debug + Send + Sync { + /// Compress `input`, returning the result. + /// + /// `input` is consumed by this function so (if the underlying implementation + /// supports it) the compression can be performed in-place. + /// + /// `level` is a hint as to how much effort to expend on the compression. + /// + /// `Err(CompressionFailed)` may be returned for any reason. + fn compress( + &self, + input: Vec<u8>, + level: CompressionLevel, + ) -> Result<Vec<u8>, CompressionFailed>; + + /// Which algorithm this compressor handles. + fn algorithm(&self) -> CertificateCompressionAlgorithm; +} + +/// A hint for how many resources to dedicate to a compression. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub enum CompressionLevel { + /// This compression is happening interactively during a handshake. + /// + /// Implementations may wish to choose a conservative compression level. + Interactive, + + /// The compression may be amortized over many connections. + /// + /// Implementations may wish to choose an aggressive compression level. + Amortized, +} + +/// A content-less error for when `CertDecompressor::decompress` fails. +#[derive(Debug)] +pub struct DecompressionFailed; + +/// A content-less error for when `CertCompressor::compress` fails. +#[derive(Debug)] +pub struct CompressionFailed; + +#[cfg(feature = "zlib")] +mod feat_zlib_rs { + use zlib_rs::c_api::Z_BEST_COMPRESSION; + use zlib_rs::{ReturnCode, deflate, inflate}; + + use super::*; + + /// A certificate decompressor for the Zlib algorithm using the `zlib-rs` crate. + pub const ZLIB_DECOMPRESSOR: &dyn CertDecompressor = &ZlibRsDecompressor; + + #[derive(Debug)] + struct ZlibRsDecompressor; + + impl CertDecompressor for ZlibRsDecompressor { + fn decompress(&self, input: &[u8], output: &mut [u8]) -> Result<(), DecompressionFailed> { + let output_len = output.len(); + match inflate::uncompress_slice(output, input, inflate::InflateConfig::default()) { + (output_filled, ReturnCode::Ok) if output_filled.len() == output_len => Ok(()), + (_, _) => Err(DecompressionFailed), + } + } + + fn algorithm(&self) -> CertificateCompressionAlgorithm { + CertificateCompressionAlgorithm::Zlib + } + } + + /// A certificate compressor for the Zlib algorithm using the `zlib-rs` crate. + pub const ZLIB_COMPRESSOR: &dyn CertCompressor = &ZlibRsCompressor; + + #[derive(Debug)] + struct ZlibRsCompressor; + + impl CertCompressor for ZlibRsCompressor { + fn compress( + &self, + input: Vec<u8>, + level: CompressionLevel, + ) -> Result<Vec<u8>, CompressionFailed> { + let mut output = alloc::vec![0u8; deflate::compress_bound(input.len())]; + let config = match level { + CompressionLevel::Interactive => deflate::DeflateConfig::default(), + CompressionLevel::Amortized => deflate::DeflateConfig::new(Z_BEST_COMPRESSION), + }; + let (output_filled, rc) = deflate::compress_slice(&mut output, &input, config); + if rc != ReturnCode::Ok { + return Err(CompressionFailed); + } + + let used = output_filled.len(); + output.truncate(used); + Ok(output) + } + + fn algorithm(&self) -> CertificateCompressionAlgorithm { + CertificateCompressionAlgorithm::Zlib + } + } +} + +#[cfg(feature = "zlib")] +pub use feat_zlib_rs::{ZLIB_COMPRESSOR, ZLIB_DECOMPRESSOR}; + +#[cfg(feature = "brotli")] +mod feat_brotli { + use std::io::{Cursor, Write}; + + use super::*; + + /// A certificate decompressor for the brotli algorithm using the `brotli` crate. + pub const BROTLI_DECOMPRESSOR: &dyn CertDecompressor = &BrotliDecompressor; + + #[derive(Debug)] + struct BrotliDecompressor; + + impl CertDecompressor for BrotliDecompressor { + fn decompress(&self, input: &[u8], output: &mut [u8]) -> Result<(), DecompressionFailed> { + let mut in_cursor = Cursor::new(input); + let mut out_cursor = Cursor::new(output); + + brotli::BrotliDecompress(&mut in_cursor, &mut out_cursor) + .map_err(|_| DecompressionFailed)?; + + if out_cursor.position() as usize != out_cursor.into_inner().len() { + return Err(DecompressionFailed); + } + + Ok(()) + } + + fn algorithm(&self) -> CertificateCompressionAlgorithm { + CertificateCompressionAlgorithm::Brotli + } + } + + /// A certificate compressor for the brotli algorithm using the `brotli` crate. + pub const BROTLI_COMPRESSOR: &dyn CertCompressor = &BrotliCompressor; + + #[derive(Debug)] + struct BrotliCompressor; + + impl CertCompressor for BrotliCompressor { + fn compress( + &self, + input: Vec<u8>, + level: CompressionLevel, + ) -> Result<Vec<u8>, CompressionFailed> { + let quality = match level { + CompressionLevel::Interactive => QUALITY_FAST, + CompressionLevel::Amortized => QUALITY_SLOW, + }; + let output = Cursor::new(Vec::with_capacity(input.len() / 2)); + let mut compressor = brotli::CompressorWriter::new(output, BUFFER_SIZE, quality, LGWIN); + compressor + .write_all(&input) + .map_err(|_| CompressionFailed)?; + Ok(compressor.into_inner().into_inner()) + } + + fn algorithm(&self) -> CertificateCompressionAlgorithm { + CertificateCompressionAlgorithm::Brotli + } + } + + /// Brotli buffer size. + /// + /// Chosen based on brotli `examples/compress.rs`. + const BUFFER_SIZE: usize = 4096; + + /// This is the default lgwin parameter, see `BrotliEncoderInitParams()` + const LGWIN: u32 = 22; + + /// Compression quality we use for interactive compressions. + /// See <https://blog.cloudflare.com/results-experimenting-brotli> for data. + const QUALITY_FAST: u32 = 4; + + /// Compression quality we use for offline compressions (the maximum). + const QUALITY_SLOW: u32 = 11; +} + +#[cfg(feature = "brotli")] +pub use feat_brotli::{BROTLI_COMPRESSOR, BROTLI_DECOMPRESSOR}; + +/// An LRU cache for compressions. +/// +/// The prospect of being able to reuse a given compression for many connections +/// means we can afford to spend more time on that compression (by passing +/// `CompressionLevel::Amortized` to the compressor). +#[derive(Debug)] +pub enum CompressionCache { + /// No caching happens, and compression happens each time using + /// `CompressionLevel::Interactive`. + Disabled, + + /// Compressions are stored in an LRU cache. + #[cfg(feature = "std")] + Enabled(CompressionCacheInner), +} + +/// Innards of an enabled CompressionCache. +/// +/// You cannot make one of these directly. Use [`CompressionCache::new`]. +#[cfg(feature = "std")] +#[derive(Debug)] +pub struct CompressionCacheInner { + /// Maximum size of underlying storage. + size: usize, + + /// LRU-order entries. + /// + /// First is least-used, last is most-used. + entries: Mutex<VecDeque<Arc<CompressionCacheEntry>>>, +} + +impl CompressionCache { + /// Make a `CompressionCache` that stores up to `size` compressed + /// certificate messages. + #[cfg(feature = "std")] + pub fn new(size: usize) -> Self { + if size == 0 { + return Self::Disabled; + } + + Self::Enabled(CompressionCacheInner { + size, + entries: Mutex::new(VecDeque::with_capacity(size)), + }) + } + + /// Return a `CompressionCacheEntry`, which is an owning + /// wrapper for a `CompressedCertificatePayload`. + /// + /// `compressor` is the compression function we have negotiated. + /// `original` is the uncompressed certificate message. + pub(crate) fn compression_for( + &self, + compressor: &dyn CertCompressor, + original: &CertificatePayloadTls13<'_>, + ) -> Result<Arc<CompressionCacheEntry>, CompressionFailed> { + match self { + Self::Disabled => Self::uncached_compression(compressor, original), + + #[cfg(feature = "std")] + Self::Enabled(_) => self.compression_for_impl(compressor, original), + } + } + + #[cfg(feature = "std")] + fn compression_for_impl( + &self, + compressor: &dyn CertCompressor, + original: &CertificatePayloadTls13<'_>, + ) -> Result<Arc<CompressionCacheEntry>, CompressionFailed> { + let (max_size, entries) = match self { + Self::Enabled(CompressionCacheInner { size, entries }) => (*size, entries), + _ => unreachable!(), + }; + + // context is a per-connection quantity, and included in the compressed data. + // it is not suitable for inclusion in the cache. + if !original.context.0.is_empty() { + return Self::uncached_compression(compressor, original); + } + + // cache probe: + let encoding = original.get_encoding(); + let algorithm = compressor.algorithm(); + + let mut cache = entries + .lock() + .map_err(|_| CompressionFailed)?; + for (i, item) in cache.iter().enumerate() { + if item.algorithm == algorithm && item.original == encoding { + // this item is now MRU + let item = cache.remove(i).unwrap(); + cache.push_back(item.clone()); + return Ok(item); + } + } + drop(cache); + + // do compression: + let uncompressed_len = encoding.len() as u32; + let compressed = compressor.compress(encoding.clone(), CompressionLevel::Amortized)?; + let new_entry = Arc::new(CompressionCacheEntry { + algorithm, + original: encoding, + compressed: CompressedCertificatePayload { + alg: algorithm, + uncompressed_len, + compressed: PayloadU24(Payload::new(compressed)), + }, + }); + + // insert into cache + let mut cache = entries + .lock() + .map_err(|_| CompressionFailed)?; + if cache.len() == max_size { + cache.pop_front(); + } + cache.push_back(new_entry.clone()); + Ok(new_entry) + } + + /// Compress `original` using `compressor` at `Interactive` level. + fn uncached_compression( + compressor: &dyn CertCompressor, + original: &CertificatePayloadTls13<'_>, + ) -> Result<Arc<CompressionCacheEntry>, CompressionFailed> { + let algorithm = compressor.algorithm(); + let encoding = original.get_encoding(); + let uncompressed_len = encoding.len() as u32; + let compressed = compressor.compress(encoding, CompressionLevel::Interactive)?; + + // this `CompressionCacheEntry` in fact never makes it into the cache, so + // `original` is left empty + Ok(Arc::new(CompressionCacheEntry { + algorithm, + original: Vec::new(), + compressed: CompressedCertificatePayload { + alg: algorithm, + uncompressed_len, + compressed: PayloadU24(Payload::new(compressed)), + }, + })) + } +} + +impl Default for CompressionCache { + fn default() -> Self { + #[cfg(feature = "std")] + { + // 4 entries allows 2 certificate chains times 2 compression algorithms + Self::new(4) + } + + #[cfg(not(feature = "std"))] + { + Self::Disabled + } + } +} + +#[cfg_attr(not(feature = "std"), allow(dead_code))] +#[derive(Debug)] +pub(crate) struct CompressionCacheEntry { + // cache key is algorithm + original: + algorithm: CertificateCompressionAlgorithm, + original: Vec<u8>, + + // cache value is compression result: + compressed: CompressedCertificatePayload<'static>, +} + +impl CompressionCacheEntry { + pub(crate) fn compressed_cert_payload(&self) -> CompressedCertificatePayload<'_> { + self.compressed.as_borrowed() + } +} + +#[cfg(all(test, any(feature = "brotli", feature = "zlib")))] +mod tests { + use std::{println, vec}; + + use super::*; + + #[test] + #[cfg(feature = "zlib")] + fn test_zlib() { + test_compressor(ZLIB_COMPRESSOR, ZLIB_DECOMPRESSOR); + } + + #[test] + #[cfg(feature = "brotli")] + fn test_brotli() { + test_compressor(BROTLI_COMPRESSOR, BROTLI_DECOMPRESSOR); + } + + fn test_compressor(comp: &dyn CertCompressor, decomp: &dyn CertDecompressor) { + assert_eq!(comp.algorithm(), decomp.algorithm()); + for sz in [16, 64, 512, 2048, 8192, 16384] { + test_trivial_pairwise(comp, decomp, sz); + } + test_decompress_wrong_len(comp, decomp); + test_decompress_garbage(decomp); + } + + fn test_trivial_pairwise( + comp: &dyn CertCompressor, + decomp: &dyn CertDecompressor, + plain_len: usize, + ) { + let original = vec![0u8; plain_len]; + + for level in [CompressionLevel::Interactive, CompressionLevel::Amortized] { + let compressed = comp + .compress(original.clone(), level) + .unwrap(); + println!( + "{:?} compressed trivial {} -> {} using {:?} level", + comp.algorithm(), + original.len(), + compressed.len(), + level + ); + let mut recovered = vec![0xffu8; plain_len]; + decomp + .decompress(&compressed, &mut recovered) + .unwrap(); + assert_eq!(original, recovered); + } + } + + fn test_decompress_wrong_len(comp: &dyn CertCompressor, decomp: &dyn CertDecompressor) { + let original = vec![0u8; 2048]; + let compressed = comp + .compress(original.clone(), CompressionLevel::Interactive) + .unwrap(); + println!("{compressed:?}"); + + // too big + let mut recovered = vec![0xffu8; original.len() + 1]; + decomp + .decompress(&compressed, &mut recovered) + .unwrap_err(); + + // too small + let mut recovered = vec![0xffu8; original.len() - 1]; + decomp + .decompress(&compressed, &mut recovered) + .unwrap_err(); + } + + fn test_decompress_garbage(decomp: &dyn CertDecompressor) { + let junk = [0u8; 1024]; + let mut recovered = vec![0u8; 512]; + decomp + .decompress(&junk, &mut recovered) + .unwrap_err(); + } + + #[test] + #[cfg(all(feature = "brotli", feature = "zlib"))] + fn test_cache_evicts_lru() { + use core::sync::atomic::{AtomicBool, Ordering}; + + use pki_types::CertificateDer; + + let cache = CompressionCache::default(); + + let cert = CertificateDer::from(vec![1]); + + let cert1 = CertificatePayloadTls13::new([&cert].into_iter(), Some(b"1")); + let cert2 = CertificatePayloadTls13::new([&cert].into_iter(), Some(b"2")); + let cert3 = CertificatePayloadTls13::new([&cert].into_iter(), Some(b"3")); + let cert4 = CertificatePayloadTls13::new([&cert].into_iter(), Some(b"4")); + + // insert zlib (1), (2), (3), (4) + + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), true), + &cert1, + ) + .unwrap(); + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), true), + &cert2, + ) + .unwrap(); + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), true), + &cert3, + ) + .unwrap(); + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), true), + &cert4, + ) + .unwrap(); + + // -- now full + + // insert brotli (1) evicts zlib (1) + cache + .compression_for( + &RequireCompress(BROTLI_COMPRESSOR, AtomicBool::default(), true), + &cert4, + ) + .unwrap(); + + // now zlib (2), (3), (4) and brotli (4) exist + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), false), + &cert2, + ) + .unwrap(); + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), false), + &cert3, + ) + .unwrap(); + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), false), + &cert4, + ) + .unwrap(); + cache + .compression_for( + &RequireCompress(BROTLI_COMPRESSOR, AtomicBool::default(), false), + &cert4, + ) + .unwrap(); + + // insert zlib (1) requires re-compression & evicts zlib (2) + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), true), + &cert1, + ) + .unwrap(); + + // now zlib (1), (3), (4) and brotli (4) exist + // query zlib (4), (3), (1) to demonstrate LRU tracks usage rather than insertion + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), false), + &cert4, + ) + .unwrap(); + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), false), + &cert3, + ) + .unwrap(); + cache + .compression_for( + &RequireCompress(ZLIB_COMPRESSOR, AtomicBool::default(), false), + &cert1, + ) + .unwrap(); + + // now brotli (4), zlib (4), (3), (1) + // insert brotli (1) evicting brotli (4) + cache + .compression_for( + &RequireCompress(BROTLI_COMPRESSOR, AtomicBool::default(), true), + &cert1, + ) + .unwrap(); + + // verify brotli (4) disappeared + cache + .compression_for( + &RequireCompress(BROTLI_COMPRESSOR, AtomicBool::default(), true), + &cert4, + ) + .unwrap(); + + #[derive(Debug)] + struct RequireCompress(&'static dyn CertCompressor, AtomicBool, bool); + + impl CertCompressor for RequireCompress { + fn compress( + &self, + input: Vec<u8>, + level: CompressionLevel, + ) -> Result<Vec<u8>, CompressionFailed> { + self.1.store(true, Ordering::SeqCst); + self.0.compress(input, level) + } + + fn algorithm(&self) -> CertificateCompressionAlgorithm { + self.0.algorithm() + } + } + + impl Drop for RequireCompress { + fn drop(&mut self) { + assert_eq!(self.1.load(Ordering::SeqCst), self.2); + } + } + } +} diff --git a/vendor/rustls/src/conn.rs b/vendor/rustls/src/conn.rs new file mode 100644 index 00000000..1a95079f --- /dev/null +++ b/vendor/rustls/src/conn.rs @@ -0,0 +1,1280 @@ +use alloc::boxed::Box; +use core::fmt::Debug; +use core::mem; +use core::ops::{Deref, DerefMut, Range}; +#[cfg(feature = "std")] +use std::io; + +use kernel::KernelConnection; + +use crate::common_state::{CommonState, Context, DEFAULT_BUFFER_LIMIT, IoState, State}; +use crate::enums::{AlertDescription, ContentType, ProtocolVersion}; +use crate::error::{Error, PeerMisbehaved}; +use crate::log::trace; +use crate::msgs::deframer::DeframerIter; +use crate::msgs::deframer::buffers::{BufferProgress, DeframerVecBuffer, Delocator, Locator}; +use crate::msgs::deframer::handshake::HandshakeDeframer; +use crate::msgs::handshake::Random; +use crate::msgs::message::{InboundPlainMessage, Message, MessagePayload}; +use crate::record_layer::Decrypted; +use crate::suites::ExtractedSecrets; +use crate::vecbuf::ChunkVecBuffer; + +// pub so that it can be re-exported from the crate root +pub mod kernel; +pub(crate) mod unbuffered; + +#[cfg(feature = "std")] +mod connection { + use alloc::vec::Vec; + use core::fmt::Debug; + use core::ops::{Deref, DerefMut}; + use std::io::{self, BufRead, Read}; + + use crate::ConnectionCommon; + use crate::common_state::{CommonState, IoState}; + use crate::error::Error; + use crate::msgs::message::OutboundChunks; + use crate::suites::ExtractedSecrets; + use crate::vecbuf::ChunkVecBuffer; + + /// A client or server connection. + #[derive(Debug)] + pub enum Connection { + /// A client connection + Client(crate::client::ClientConnection), + /// A server connection + Server(crate::server::ServerConnection), + } + + impl Connection { + /// Read TLS content from `rd`. + /// + /// See [`ConnectionCommon::read_tls()`] for more information. + pub fn read_tls(&mut self, rd: &mut dyn Read) -> Result<usize, io::Error> { + match self { + Self::Client(conn) => conn.read_tls(rd), + Self::Server(conn) => conn.read_tls(rd), + } + } + + /// Writes TLS messages to `wr`. + /// + /// See [`ConnectionCommon::write_tls()`] for more information. + pub fn write_tls(&mut self, wr: &mut dyn io::Write) -> Result<usize, io::Error> { + self.sendable_tls.write_to(wr) + } + + /// Returns an object that allows reading plaintext. + pub fn reader(&mut self) -> Reader<'_> { + match self { + Self::Client(conn) => conn.reader(), + Self::Server(conn) => conn.reader(), + } + } + + /// Returns an object that allows writing plaintext. + pub fn writer(&mut self) -> Writer<'_> { + match self { + Self::Client(conn) => Writer::new(&mut **conn), + Self::Server(conn) => Writer::new(&mut **conn), + } + } + + /// Processes any new packets read by a previous call to [`Connection::read_tls`]. + /// + /// See [`ConnectionCommon::process_new_packets()`] for more information. + pub fn process_new_packets(&mut self) -> Result<IoState, Error> { + match self { + Self::Client(conn) => conn.process_new_packets(), + Self::Server(conn) => conn.process_new_packets(), + } + } + + /// Derives key material from the agreed connection secrets. + /// + /// See [`ConnectionCommon::export_keying_material()`] for more information. + pub fn export_keying_material<T: AsMut<[u8]>>( + &self, + output: T, + label: &[u8], + context: Option<&[u8]>, + ) -> Result<T, Error> { + match self { + Self::Client(conn) => conn.export_keying_material(output, label, context), + Self::Server(conn) => conn.export_keying_material(output, label, context), + } + } + + /// This function uses `io` to complete any outstanding IO for this connection. + /// + /// See [`ConnectionCommon::complete_io()`] for more information. + pub fn complete_io<T>(&mut self, io: &mut T) -> Result<(usize, usize), io::Error> + where + Self: Sized, + T: Read + io::Write, + { + match self { + Self::Client(conn) => conn.complete_io(io), + Self::Server(conn) => conn.complete_io(io), + } + } + + /// Extract secrets, so they can be used when configuring kTLS, for example. + /// Should be used with care as it exposes secret key material. + pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + match self { + Self::Client(client) => client.dangerous_extract_secrets(), + Self::Server(server) => server.dangerous_extract_secrets(), + } + } + + /// Sets a limit on the internal buffers + /// + /// See [`ConnectionCommon::set_buffer_limit()`] for more information. + pub fn set_buffer_limit(&mut self, limit: Option<usize>) { + match self { + Self::Client(client) => client.set_buffer_limit(limit), + Self::Server(server) => server.set_buffer_limit(limit), + } + } + + /// Sends a TLS1.3 `key_update` message to refresh a connection's keys + /// + /// See [`ConnectionCommon::refresh_traffic_keys()`] for more information. + pub fn refresh_traffic_keys(&mut self) -> Result<(), Error> { + match self { + Self::Client(client) => client.refresh_traffic_keys(), + Self::Server(server) => server.refresh_traffic_keys(), + } + } + } + + impl Deref for Connection { + type Target = CommonState; + + fn deref(&self) -> &Self::Target { + match self { + Self::Client(conn) => &conn.core.common_state, + Self::Server(conn) => &conn.core.common_state, + } + } + } + + impl DerefMut for Connection { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + Self::Client(conn) => &mut conn.core.common_state, + Self::Server(conn) => &mut conn.core.common_state, + } + } + } + + /// A structure that implements [`std::io::Read`] for reading plaintext. + pub struct Reader<'a> { + pub(super) received_plaintext: &'a mut ChunkVecBuffer, + pub(super) has_received_close_notify: bool, + pub(super) has_seen_eof: bool, + } + + impl<'a> Reader<'a> { + /// Check the connection's state if no bytes are available for reading. + fn check_no_bytes_state(&self) -> io::Result<()> { + match (self.has_received_close_notify, self.has_seen_eof) { + // cleanly closed; don't care about TCP EOF: express this as Ok(0) + (true, _) => Ok(()), + // unclean closure + (false, true) => Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + UNEXPECTED_EOF_MESSAGE, + )), + // connection still going, but needs more data: signal `WouldBlock` so that + // the caller knows this + (false, false) => Err(io::ErrorKind::WouldBlock.into()), + } + } + + /// Obtain a chunk of plaintext data received from the peer over this TLS connection. + /// + /// This method consumes `self` so that it can return a slice whose lifetime is bounded by + /// the [`ConnectionCommon`] that created this `Reader`. + pub fn into_first_chunk(self) -> io::Result<&'a [u8]> { + match self.received_plaintext.chunk() { + Some(chunk) => Ok(chunk), + None => { + self.check_no_bytes_state()?; + Ok(&[]) + } + } + } + } + + impl Read for Reader<'_> { + /// Obtain plaintext data received from the peer over this TLS connection. + /// + /// If the peer closes the TLS session cleanly, this returns `Ok(0)` once all + /// the pending data has been read. No further data can be received on that + /// connection, so the underlying TCP connection should be half-closed too. + /// + /// If the peer closes the TLS session uncleanly (a TCP EOF without sending a + /// `close_notify` alert) this function returns a `std::io::Error` of type + /// `ErrorKind::UnexpectedEof` once any pending data has been read. + /// + /// Note that support for `close_notify` varies in peer TLS libraries: many do not + /// support it and uncleanly close the TCP connection (this might be + /// vulnerable to truncation attacks depending on the application protocol). + /// This means applications using rustls must both handle EOF + /// from this function, *and* unexpected EOF of the underlying TCP connection. + /// + /// If there are no bytes to read, this returns `Err(ErrorKind::WouldBlock.into())`. + /// + /// You may learn the number of bytes available at any time by inspecting + /// the return of [`Connection::process_new_packets`]. + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let len = self.received_plaintext.read(buf)?; + if len > 0 || buf.is_empty() { + return Ok(len); + } + + self.check_no_bytes_state() + .map(|()| len) + } + + /// Obtain plaintext data received from the peer over this TLS connection. + /// + /// If the peer closes the TLS session, this returns `Ok(())` without filling + /// any more of the buffer once all the pending data has been read. No further + /// data can be received on that connection, so the underlying TCP connection + /// should be half-closed too. + /// + /// If the peer closes the TLS session uncleanly (a TCP EOF without sending a + /// `close_notify` alert) this function returns a `std::io::Error` of type + /// `ErrorKind::UnexpectedEof` once any pending data has been read. + /// + /// Note that support for `close_notify` varies in peer TLS libraries: many do not + /// support it and uncleanly close the TCP connection (this might be + /// vulnerable to truncation attacks depending on the application protocol). + /// This means applications using rustls must both handle EOF + /// from this function, *and* unexpected EOF of the underlying TCP connection. + /// + /// If there are no bytes to read, this returns `Err(ErrorKind::WouldBlock.into())`. + /// + /// You may learn the number of bytes available at any time by inspecting + /// the return of [`Connection::process_new_packets`]. + #[cfg(read_buf)] + fn read_buf(&mut self, mut cursor: core::io::BorrowedCursor<'_>) -> io::Result<()> { + let before = cursor.written(); + self.received_plaintext + .read_buf(cursor.reborrow())?; + let len = cursor.written() - before; + if len > 0 || cursor.capacity() == 0 { + return Ok(()); + } + + self.check_no_bytes_state() + } + } + + impl BufRead for Reader<'_> { + /// Obtain a chunk of plaintext data received from the peer over this TLS connection. + /// This reads the same data as [`Reader::read()`], but returns a reference instead of + /// copying the data. + /// + /// The caller should call [`Reader::consume()`] afterward to advance the buffer. + /// + /// See [`Reader::into_first_chunk()`] for a version of this function that returns a + /// buffer with a longer lifetime. + fn fill_buf(&mut self) -> io::Result<&[u8]> { + Reader { + // reborrow + received_plaintext: self.received_plaintext, + ..*self + } + .into_first_chunk() + } + + fn consume(&mut self, amt: usize) { + self.received_plaintext + .consume_first_chunk(amt) + } + } + + const UNEXPECTED_EOF_MESSAGE: &str = "peer closed connection without sending TLS close_notify: \ +https://docs.rs/rustls/latest/rustls/manual/_03_howto/index.html#unexpected-eof"; + + /// A structure that implements [`std::io::Write`] for writing plaintext. + pub struct Writer<'a> { + sink: &'a mut dyn PlaintextSink, + } + + impl<'a> Writer<'a> { + /// Create a new Writer. + /// + /// This is not an external interface. Get one of these objects + /// from [`Connection::writer`]. + pub(crate) fn new(sink: &'a mut dyn PlaintextSink) -> Self { + Writer { sink } + } + } + + impl io::Write for Writer<'_> { + /// Send the plaintext `buf` to the peer, encrypting + /// and authenticating it. Once this function succeeds + /// you should call [`Connection::write_tls`] which will output the + /// corresponding TLS records. + /// + /// This function buffers plaintext sent before the + /// TLS handshake completes, and sends it as soon + /// as it can. See [`ConnectionCommon::set_buffer_limit`] to control + /// the size of this buffer. + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + self.sink.write(buf) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + self.sink.write_vectored(bufs) + } + + fn flush(&mut self) -> io::Result<()> { + self.sink.flush() + } + } + + /// Internal trait implemented by the [`ServerConnection`]/[`ClientConnection`] + /// allowing them to be the subject of a [`Writer`]. + /// + /// [`ServerConnection`]: crate::ServerConnection + /// [`ClientConnection`]: crate::ClientConnection + pub(crate) trait PlaintextSink { + fn write(&mut self, buf: &[u8]) -> io::Result<usize>; + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize>; + fn flush(&mut self) -> io::Result<()>; + } + + impl<T> PlaintextSink for ConnectionCommon<T> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + let len = self + .core + .common_state + .buffer_plaintext(buf.into(), &mut self.sendable_plaintext); + self.core.maybe_refresh_traffic_keys(); + Ok(len) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> { + let payload_owner: Vec<&[u8]>; + let payload = match bufs.len() { + 0 => return Ok(0), + 1 => OutboundChunks::Single(bufs[0].deref()), + _ => { + payload_owner = bufs + .iter() + .map(|io_slice| io_slice.deref()) + .collect(); + + OutboundChunks::new(&payload_owner) + } + }; + let len = self + .core + .common_state + .buffer_plaintext(payload, &mut self.sendable_plaintext); + self.core.maybe_refresh_traffic_keys(); + Ok(len) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + } +} + +#[cfg(feature = "std")] +pub use connection::{Connection, Reader, Writer}; + +#[derive(Debug)] +pub(crate) struct ConnectionRandoms { + pub(crate) client: [u8; 32], + pub(crate) server: [u8; 32], +} + +impl ConnectionRandoms { + pub(crate) fn new(client: Random, server: Random) -> Self { + Self { + client: client.0, + server: server.0, + } + } +} + +/// Interface shared by client and server connections. +pub struct ConnectionCommon<Data> { + pub(crate) core: ConnectionCore<Data>, + deframer_buffer: DeframerVecBuffer, + sendable_plaintext: ChunkVecBuffer, +} + +impl<Data> ConnectionCommon<Data> { + /// Processes any new packets read by a previous call to + /// [`Connection::read_tls`]. + /// + /// Errors from this function relate to TLS protocol errors, and + /// are fatal to the connection. Future calls after an error will do + /// no new work and will return the same error. After an error is + /// received from [`process_new_packets`], you should not call [`read_tls`] + /// any more (it will fill up buffers to no purpose). However, you + /// may call the other methods on the connection, including `write`, + /// `send_close_notify`, and `write_tls`. Most likely you will want to + /// call `write_tls` to send any alerts queued by the error and then + /// close the underlying connection. + /// + /// Success from this function comes with some sundry state data + /// about the connection. + /// + /// [`read_tls`]: Connection::read_tls + /// [`process_new_packets`]: Connection::process_new_packets + #[inline] + pub fn process_new_packets(&mut self) -> Result<IoState, Error> { + self.core + .process_new_packets(&mut self.deframer_buffer, &mut self.sendable_plaintext) + } + + /// Derives key material from the agreed connection secrets. + /// + /// This function fills in `output` with `output.len()` bytes of key + /// material derived from the master session secret using `label` + /// and `context` for diversification. Ownership of the buffer is taken + /// by the function and returned via the Ok result to ensure no key + /// material leaks if the function fails. + /// + /// See RFC5705 for more details on what this does and is for. + /// + /// For TLS1.3 connections, this function does not use the + /// "early" exporter at any point. + /// + /// This function fails if called prior to the handshake completing; + /// check with [`CommonState::is_handshaking`] first. + /// + /// This function fails if `output.len()` is zero. + #[inline] + pub fn export_keying_material<T: AsMut<[u8]>>( + &self, + output: T, + label: &[u8], + context: Option<&[u8]>, + ) -> Result<T, Error> { + self.core + .export_keying_material(output, label, context) + } + + /// Extract secrets, so they can be used when configuring kTLS, for example. + /// Should be used with care as it exposes secret key material. + pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + self.core.dangerous_extract_secrets() + } + + /// Sets a limit on the internal buffers used to buffer + /// unsent plaintext (prior to completing the TLS handshake) + /// and unsent TLS records. This limit acts only on application + /// data written through [`Connection::writer`]. + /// + /// By default the limit is 64KB. The limit can be set + /// at any time, even if the current buffer use is higher. + /// + /// [`None`] means no limit applies, and will mean that written + /// data is buffered without bound -- it is up to the application + /// to appropriately schedule its plaintext and TLS writes to bound + /// memory usage. + /// + /// For illustration: `Some(1)` means a limit of one byte applies: + /// [`Connection::writer`] will accept only one byte, encrypt it and + /// add a TLS header. Once this is sent via [`Connection::write_tls`], + /// another byte may be sent. + /// + /// # Internal write-direction buffering + /// rustls has two buffers whose size are bounded by this setting: + /// + /// ## Buffering of unsent plaintext data prior to handshake completion + /// + /// Calls to [`Connection::writer`] before or during the handshake + /// are buffered (up to the limit specified here). Once the + /// handshake completes this data is encrypted and the resulting + /// TLS records are added to the outgoing buffer. + /// + /// ## Buffering of outgoing TLS records + /// + /// This buffer is used to store TLS records that rustls needs to + /// send to the peer. It is used in these two circumstances: + /// + /// - by [`Connection::process_new_packets`] when a handshake or alert + /// TLS record needs to be sent. + /// - by [`Connection::writer`] post-handshake: the plaintext is + /// encrypted and the resulting TLS record is buffered. + /// + /// This buffer is emptied by [`Connection::write_tls`]. + /// + /// [`Connection::writer`]: crate::Connection::writer + /// [`Connection::write_tls`]: crate::Connection::write_tls + /// [`Connection::process_new_packets`]: crate::Connection::process_new_packets + pub fn set_buffer_limit(&mut self, limit: Option<usize>) { + self.sendable_plaintext.set_limit(limit); + self.sendable_tls.set_limit(limit); + } + + /// Sends a TLS1.3 `key_update` message to refresh a connection's keys. + /// + /// This call refreshes our encryption keys. Once the peer receives the message, + /// it refreshes _its_ encryption and decryption keys and sends a response. + /// Once we receive that response, we refresh our decryption keys to match. + /// At the end of this process, keys in both directions have been refreshed. + /// + /// Note that this process does not happen synchronously: this call just + /// arranges that the `key_update` message will be included in the next + /// `write_tls` output. + /// + /// This fails with `Error::HandshakeNotComplete` if called before the initial + /// handshake is complete, or if a version prior to TLS1.3 is negotiated. + /// + /// # Usage advice + /// Note that other implementations (including rustls) may enforce limits on + /// the number of `key_update` messages allowed on a given connection to prevent + /// denial of service. Therefore, this should be called sparingly. + /// + /// rustls implicitly and automatically refreshes traffic keys when needed + /// according to the selected cipher suite's cryptographic constraints. There + /// is therefore no need to call this manually to avoid cryptographic keys + /// "wearing out". + /// + /// The main reason to call this manually is to roll keys when it is known + /// a connection will be idle for a long period. + pub fn refresh_traffic_keys(&mut self) -> Result<(), Error> { + self.core.refresh_traffic_keys() + } +} + +#[cfg(feature = "std")] +impl<Data> ConnectionCommon<Data> { + /// Returns an object that allows reading plaintext. + pub fn reader(&mut self) -> Reader<'_> { + let common = &mut self.core.common_state; + Reader { + received_plaintext: &mut common.received_plaintext, + // Are we done? i.e., have we processed all received messages, and received a + // close_notify to indicate that no new messages will arrive? + has_received_close_notify: common.has_received_close_notify, + has_seen_eof: common.has_seen_eof, + } + } + + /// Returns an object that allows writing plaintext. + pub fn writer(&mut self) -> Writer<'_> { + Writer::new(self) + } + + /// This function uses `io` to complete any outstanding IO for + /// this connection. + /// + /// This is a convenience function which solely uses other parts + /// of the public API. + /// + /// What this means depends on the connection state: + /// + /// - If the connection [`is_handshaking`], then IO is performed until + /// the handshake is complete. + /// - Otherwise, if [`wants_write`] is true, [`write_tls`] is invoked + /// until it is all written. + /// - Otherwise, if [`wants_read`] is true, [`read_tls`] is invoked + /// once. + /// + /// The return value is the number of bytes read from and written + /// to `io`, respectively. + /// + /// This function will block if `io` blocks. + /// + /// Errors from TLS record handling (i.e., from [`process_new_packets`]) + /// are wrapped in an `io::ErrorKind::InvalidData`-kind error. + /// + /// [`is_handshaking`]: CommonState::is_handshaking + /// [`wants_read`]: CommonState::wants_read + /// [`wants_write`]: CommonState::wants_write + /// [`write_tls`]: ConnectionCommon::write_tls + /// [`read_tls`]: ConnectionCommon::read_tls + /// [`process_new_packets`]: ConnectionCommon::process_new_packets + pub fn complete_io<T>(&mut self, io: &mut T) -> Result<(usize, usize), io::Error> + where + Self: Sized, + T: io::Read + io::Write, + { + let mut eof = false; + let mut wrlen = 0; + let mut rdlen = 0; + + loop { + let until_handshaked = self.is_handshaking(); + + if !self.wants_write() && !self.wants_read() { + // We will make no further progress. + return Ok((rdlen, wrlen)); + } + + while self.wants_write() { + match self.write_tls(io)? { + 0 => { + io.flush()?; + return Ok((rdlen, wrlen)); // EOF. + } + n => wrlen += n, + } + } + io.flush()?; + + if !until_handshaked && wrlen > 0 { + return Ok((rdlen, wrlen)); + } + + while !eof && self.wants_read() { + let read_size = match self.read_tls(io) { + Ok(0) => { + eof = true; + Some(0) + } + Ok(n) => { + rdlen += n; + Some(n) + } + Err(err) if err.kind() == io::ErrorKind::Interrupted => None, // nothing to do + Err(err) => return Err(err), + }; + if read_size.is_some() { + break; + } + } + + match self.process_new_packets() { + Ok(_) => {} + Err(e) => { + // In case we have an alert to send describing this error, + // try a last-gasp write -- but don't predate the primary + // error. + let _ignored = self.write_tls(io); + let _ignored = io.flush(); + + return Err(io::Error::new(io::ErrorKind::InvalidData, e)); + } + }; + + // if we're doing IO until handshaked, and we believe we've finished handshaking, + // but process_new_packets() has queued TLS data to send, loop around again to write + // the queued messages. + if until_handshaked && !self.is_handshaking() && self.wants_write() { + continue; + } + + match (eof, until_handshaked, self.is_handshaking()) { + (_, true, false) => return Ok((rdlen, wrlen)), + (_, false, _) => return Ok((rdlen, wrlen)), + (true, true, true) => return Err(io::Error::from(io::ErrorKind::UnexpectedEof)), + (..) => {} + } + } + } + + /// Extract the first handshake message. + /// + /// This is a shortcut to the `process_new_packets()` -> `process_msg()` -> + /// `process_handshake_messages()` path, specialized for the first handshake message. + pub(crate) fn first_handshake_message(&mut self) -> Result<Option<Message<'static>>, Error> { + let mut buffer_progress = self.core.hs_deframer.progress(); + + let res = self + .core + .deframe( + None, + self.deframer_buffer.filled_mut(), + &mut buffer_progress, + ) + .map(|opt| opt.map(|pm| Message::try_from(pm).map(|m| m.into_owned()))); + + match res? { + Some(Ok(msg)) => { + self.deframer_buffer + .discard(buffer_progress.take_discard()); + Ok(Some(msg)) + } + Some(Err(err)) => Err(self.send_fatal_alert(AlertDescription::DecodeError, err)), + None => Ok(None), + } + } + + pub(crate) fn replace_state(&mut self, new: Box<dyn State<Data>>) { + self.core.state = Ok(new); + } + + /// Read TLS content from `rd` into the internal buffer. + /// + /// Due to the internal buffering, `rd` can supply TLS messages in arbitrary-sized chunks (like + /// a socket or pipe might). + /// + /// You should call [`process_new_packets()`] each time a call to this function succeeds in order + /// to empty the incoming TLS data buffer. + /// + /// This function returns `Ok(0)` when the underlying `rd` does so. This typically happens when + /// a socket is cleanly closed, or a file is at EOF. Errors may result from the IO done through + /// `rd`; additionally, errors of `ErrorKind::Other` are emitted to signal backpressure: + /// + /// * In order to empty the incoming TLS data buffer, you should call [`process_new_packets()`] + /// each time a call to this function succeeds. + /// * In order to empty the incoming plaintext data buffer, you should empty it through + /// the [`reader()`] after the call to [`process_new_packets()`]. + /// + /// This function also returns `Ok(0)` once a `close_notify` alert has been successfully + /// received. No additional data is ever read in this state. + /// + /// [`process_new_packets()`]: ConnectionCommon::process_new_packets + /// [`reader()`]: ConnectionCommon::reader + pub fn read_tls(&mut self, rd: &mut dyn io::Read) -> Result<usize, io::Error> { + if self.received_plaintext.is_full() { + return Err(io::Error::new( + io::ErrorKind::Other, + "received plaintext buffer full", + )); + } + + if self.has_received_close_notify { + return Ok(0); + } + + let res = self + .deframer_buffer + .read(rd, self.core.hs_deframer.is_active()); + if let Ok(0) = res { + self.has_seen_eof = true; + } + res + } + + /// Writes TLS messages to `wr`. + /// + /// On success, this function returns `Ok(n)` where `n` is a number of bytes written to `wr` + /// (after encoding and encryption). + /// + /// After this function returns, the connection buffer may not yet be fully flushed. The + /// [`CommonState::wants_write`] function can be used to check if the output buffer is empty. + pub fn write_tls(&mut self, wr: &mut dyn io::Write) -> Result<usize, io::Error> { + self.sendable_tls.write_to(wr) + } +} + +impl<'a, Data> From<&'a mut ConnectionCommon<Data>> for Context<'a, Data> { + fn from(conn: &'a mut ConnectionCommon<Data>) -> Self { + Self { + common: &mut conn.core.common_state, + data: &mut conn.core.data, + sendable_plaintext: Some(&mut conn.sendable_plaintext), + } + } +} + +impl<T> Deref for ConnectionCommon<T> { + type Target = CommonState; + + fn deref(&self) -> &Self::Target { + &self.core.common_state + } +} + +impl<T> DerefMut for ConnectionCommon<T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.core.common_state + } +} + +impl<Data> From<ConnectionCore<Data>> for ConnectionCommon<Data> { + fn from(core: ConnectionCore<Data>) -> Self { + Self { + core, + deframer_buffer: DeframerVecBuffer::default(), + sendable_plaintext: ChunkVecBuffer::new(Some(DEFAULT_BUFFER_LIMIT)), + } + } +} + +/// Interface shared by unbuffered client and server connections. +pub struct UnbufferedConnectionCommon<Data> { + pub(crate) core: ConnectionCore<Data>, + wants_write: bool, + emitted_peer_closed_state: bool, +} + +impl<Data> From<ConnectionCore<Data>> for UnbufferedConnectionCommon<Data> { + fn from(core: ConnectionCore<Data>) -> Self { + Self { + core, + wants_write: false, + emitted_peer_closed_state: false, + } + } +} + +impl<Data> UnbufferedConnectionCommon<Data> { + /// Extract secrets, so they can be used when configuring kTLS, for example. + /// Should be used with care as it exposes secret key material. + pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + self.core.dangerous_extract_secrets() + } +} + +impl<T> Deref for UnbufferedConnectionCommon<T> { + type Target = CommonState; + + fn deref(&self) -> &Self::Target { + &self.core.common_state + } +} + +pub(crate) struct ConnectionCore<Data> { + pub(crate) state: Result<Box<dyn State<Data>>, Error>, + pub(crate) data: Data, + pub(crate) common_state: CommonState, + pub(crate) hs_deframer: HandshakeDeframer, + + /// We limit consecutive empty fragments to avoid a route for the peer to send + /// us significant but fruitless traffic. + seen_consecutive_empty_fragments: u8, +} + +impl<Data> ConnectionCore<Data> { + pub(crate) fn new(state: Box<dyn State<Data>>, data: Data, common_state: CommonState) -> Self { + Self { + state: Ok(state), + data, + common_state, + hs_deframer: HandshakeDeframer::default(), + seen_consecutive_empty_fragments: 0, + } + } + + pub(crate) fn process_new_packets( + &mut self, + deframer_buffer: &mut DeframerVecBuffer, + sendable_plaintext: &mut ChunkVecBuffer, + ) -> Result<IoState, Error> { + let mut state = match mem::replace(&mut self.state, Err(Error::HandshakeNotComplete)) { + Ok(state) => state, + Err(e) => { + self.state = Err(e.clone()); + return Err(e); + } + }; + + let mut buffer_progress = self.hs_deframer.progress(); + + loop { + let res = self.deframe( + Some(&*state), + deframer_buffer.filled_mut(), + &mut buffer_progress, + ); + + let opt_msg = match res { + Ok(opt_msg) => opt_msg, + Err(e) => { + self.state = Err(e.clone()); + deframer_buffer.discard(buffer_progress.take_discard()); + return Err(e); + } + }; + + let Some(msg) = opt_msg else { + break; + }; + + match self.process_msg(msg, state, Some(sendable_plaintext)) { + Ok(new) => state = new, + Err(e) => { + self.state = Err(e.clone()); + deframer_buffer.discard(buffer_progress.take_discard()); + return Err(e); + } + } + + if self + .common_state + .has_received_close_notify + { + // "Any data received after a closure alert has been received MUST be ignored." + // -- <https://datatracker.ietf.org/doc/html/rfc8446#section-6.1> + // This is data that has already been accepted in `read_tls`. + buffer_progress.add_discard(deframer_buffer.filled().len()); + break; + } + + deframer_buffer.discard(buffer_progress.take_discard()); + } + + deframer_buffer.discard(buffer_progress.take_discard()); + self.state = Ok(state); + Ok(self.common_state.current_io_state()) + } + + /// Pull a message out of the deframer and send any messages that need to be sent as a result. + fn deframe<'b>( + &mut self, + state: Option<&dyn State<Data>>, + buffer: &'b mut [u8], + buffer_progress: &mut BufferProgress, + ) -> Result<Option<InboundPlainMessage<'b>>, Error> { + // before processing any more of `buffer`, return any extant messages from `hs_deframer` + if self.hs_deframer.has_message_ready() { + Ok(self.take_handshake_message(buffer, buffer_progress)) + } else { + self.process_more_input(state, buffer, buffer_progress) + } + } + + fn take_handshake_message<'b>( + &mut self, + buffer: &'b mut [u8], + buffer_progress: &mut BufferProgress, + ) -> Option<InboundPlainMessage<'b>> { + self.hs_deframer + .iter(buffer) + .next() + .map(|(message, discard)| { + buffer_progress.add_discard(discard); + message + }) + } + + fn process_more_input<'b>( + &mut self, + state: Option<&dyn State<Data>>, + buffer: &'b mut [u8], + buffer_progress: &mut BufferProgress, + ) -> Result<Option<InboundPlainMessage<'b>>, Error> { + let version_is_tls13 = matches!( + self.common_state.negotiated_version, + Some(ProtocolVersion::TLSv1_3) + ); + + let locator = Locator::new(buffer); + + loop { + let mut iter = DeframerIter::new(&mut buffer[buffer_progress.processed()..]); + + let (message, processed) = loop { + let message = match iter.next().transpose() { + Ok(Some(message)) => message, + Ok(None) => return Ok(None), + Err(err) => return Err(self.handle_deframe_error(err, state)), + }; + + let allowed_plaintext = match message.typ { + // CCS messages are always plaintext. + ContentType::ChangeCipherSpec => true, + // Alerts are allowed to be plaintext if-and-only-if: + // * The negotiated protocol version is TLS 1.3. - In TLS 1.2 it is unambiguous when + // keying changes based on the CCS message. Only TLS 1.3 requires these heuristics. + // * We have not yet decrypted any messages from the peer - if we have we don't + // expect any plaintext. + // * The payload size is indicative of a plaintext alert message. + ContentType::Alert + if version_is_tls13 + && !self + .common_state + .record_layer + .has_decrypted() + && message.payload.len() <= 2 => + { + true + } + // In other circumstances, we expect all messages to be encrypted. + _ => false, + }; + + if allowed_plaintext && !self.hs_deframer.is_active() { + break (message.into_plain_message(), iter.bytes_consumed()); + } + + let message = match self + .common_state + .record_layer + .decrypt_incoming(message) + { + // failed decryption during trial decryption is not allowed to be + // interleaved with partial handshake data. + Ok(None) if !self.hs_deframer.is_aligned() => { + return Err( + PeerMisbehaved::RejectedEarlyDataInterleavedWithHandshakeMessage.into(), + ); + } + + // failed decryption during trial decryption. + Ok(None) => continue, + + Ok(Some(message)) => message, + + Err(err) => return Err(self.handle_deframe_error(err, state)), + }; + + let Decrypted { + want_close_before_decrypt, + plaintext, + } = message; + + if want_close_before_decrypt { + self.common_state.send_close_notify(); + } + + break (plaintext, iter.bytes_consumed()); + }; + + if !self.hs_deframer.is_aligned() && message.typ != ContentType::Handshake { + // "Handshake messages MUST NOT be interleaved with other record + // types. That is, if a handshake message is split over two or more + // records, there MUST NOT be any other records between them." + // https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + return Err(PeerMisbehaved::MessageInterleavedWithHandshakeMessage.into()); + } + + match message.payload.len() { + 0 => { + if self.seen_consecutive_empty_fragments + == ALLOWED_CONSECUTIVE_EMPTY_FRAGMENTS_MAX + { + return Err(PeerMisbehaved::TooManyEmptyFragments.into()); + } + self.seen_consecutive_empty_fragments += 1; + } + _ => { + self.seen_consecutive_empty_fragments = 0; + } + }; + + buffer_progress.add_processed(processed); + + // do an end-run around the borrow checker, converting `message` (containing + // a borrowed slice) to an unborrowed one (containing a `Range` into the + // same buffer). the reborrow happens inside the branch that returns the + // message. + // + // is fixed by -Zpolonius + // https://github.com/rust-lang/rfcs/blob/master/text/2094-nll.md#problem-case-3-conditional-control-flow-across-functions + let unborrowed = InboundUnborrowedMessage::unborrow(&locator, message); + + if unborrowed.typ != ContentType::Handshake { + let message = unborrowed.reborrow(&Delocator::new(buffer)); + buffer_progress.add_discard(processed); + return Ok(Some(message)); + } + + let message = unborrowed.reborrow(&Delocator::new(buffer)); + self.hs_deframer + .input_message(message, &locator, buffer_progress.processed()); + self.hs_deframer.coalesce(buffer)?; + + self.common_state.aligned_handshake = self.hs_deframer.is_aligned(); + + if self.hs_deframer.has_message_ready() { + // trial decryption finishes with the first handshake message after it started. + self.common_state + .record_layer + .finish_trial_decryption(); + + return Ok(self.take_handshake_message(buffer, buffer_progress)); + } + } + } + + fn handle_deframe_error(&mut self, error: Error, state: Option<&dyn State<Data>>) -> Error { + match error { + error @ Error::InvalidMessage(_) => { + if self.common_state.is_quic() { + self.common_state.quic.alert = Some(AlertDescription::DecodeError); + error + } else { + self.common_state + .send_fatal_alert(AlertDescription::DecodeError, error) + } + } + Error::PeerSentOversizedRecord => self + .common_state + .send_fatal_alert(AlertDescription::RecordOverflow, error), + Error::DecryptError => { + if let Some(state) = state { + state.handle_decrypt_error(); + } + self.common_state + .send_fatal_alert(AlertDescription::BadRecordMac, error) + } + + error => error, + } + } + + fn process_msg( + &mut self, + msg: InboundPlainMessage<'_>, + state: Box<dyn State<Data>>, + sendable_plaintext: Option<&mut ChunkVecBuffer>, + ) -> Result<Box<dyn State<Data>>, Error> { + // Drop CCS messages during handshake in TLS1.3 + if msg.typ == ContentType::ChangeCipherSpec + && !self + .common_state + .may_receive_application_data + && self.common_state.is_tls13() + { + if !msg.is_valid_ccs() { + // "An implementation which receives any other change_cipher_spec value or + // which receives a protected change_cipher_spec record MUST abort the + // handshake with an "unexpected_message" alert." + return Err(self.common_state.send_fatal_alert( + AlertDescription::UnexpectedMessage, + PeerMisbehaved::IllegalMiddleboxChangeCipherSpec, + )); + } + + self.common_state + .received_tls13_change_cipher_spec()?; + trace!("Dropping CCS"); + return Ok(state); + } + + // Now we can fully parse the message payload. + let msg = match Message::try_from(msg) { + Ok(msg) => msg, + Err(err) => { + return Err(self + .common_state + .send_fatal_alert(AlertDescription::from(err), err)); + } + }; + + // For alerts, we have separate logic. + if let MessagePayload::Alert(alert) = &msg.payload { + self.common_state.process_alert(alert)?; + return Ok(state); + } + + self.common_state + .process_main_protocol(msg, state, &mut self.data, sendable_plaintext) + } + + pub(crate) fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + Ok(self + .dangerous_into_kernel_connection()? + .0) + } + + pub(crate) fn dangerous_into_kernel_connection( + self, + ) -> Result<(ExtractedSecrets, KernelConnection<Data>), Error> { + if !self + .common_state + .enable_secret_extraction + { + return Err(Error::General("Secret extraction is disabled".into())); + } + + if self.common_state.is_handshaking() { + return Err(Error::HandshakeNotComplete); + } + + if !self + .common_state + .sendable_tls + .is_empty() + { + return Err(Error::General( + "cannot convert into an KernelConnection while there are still buffered TLS records to send" + .into() + )); + } + + let state = self.state?; + + let record_layer = &self.common_state.record_layer; + let secrets = state.extract_secrets()?; + let secrets = ExtractedSecrets { + tx: (record_layer.write_seq(), secrets.tx), + rx: (record_layer.read_seq(), secrets.rx), + }; + + let state = state.into_external_state()?; + let external = KernelConnection::new(state, self.common_state)?; + + Ok((secrets, external)) + } + + pub(crate) fn export_keying_material<T: AsMut<[u8]>>( + &self, + mut output: T, + label: &[u8], + context: Option<&[u8]>, + ) -> Result<T, Error> { + if output.as_mut().is_empty() { + return Err(Error::General( + "export_keying_material with zero-length output".into(), + )); + } + + match self.state.as_ref() { + Ok(st) => st + .export_keying_material(output.as_mut(), label, context) + .map(|_| output), + Err(e) => Err(e.clone()), + } + } + + /// Trigger a `refresh_traffic_keys` if required by `CommonState`. + fn maybe_refresh_traffic_keys(&mut self) { + if mem::take( + &mut self + .common_state + .refresh_traffic_keys_pending, + ) { + let _ = self.refresh_traffic_keys(); + } + } + + fn refresh_traffic_keys(&mut self) -> Result<(), Error> { + match &mut self.state { + Ok(st) => st.send_key_update_request(&mut self.common_state), + Err(e) => Err(e.clone()), + } + } +} + +/// Data specific to the peer's side (client or server). +pub trait SideData: Debug {} + +/// An InboundPlainMessage which does not borrow its payload, but +/// references a range that can later be borrowed. +struct InboundUnborrowedMessage { + typ: ContentType, + version: ProtocolVersion, + bounds: Range<usize>, +} + +impl InboundUnborrowedMessage { + fn unborrow(locator: &Locator, msg: InboundPlainMessage<'_>) -> Self { + Self { + typ: msg.typ, + version: msg.version, + bounds: locator.locate(msg.payload), + } + } + + fn reborrow<'b>(self, delocator: &Delocator<'b>) -> InboundPlainMessage<'b> { + InboundPlainMessage { + typ: self.typ, + version: self.version, + payload: delocator.slice_from_range(&self.bounds), + } + } +} + +/// cf. BoringSSL's `kMaxEmptyRecords` +/// <https://github.com/google/boringssl/blob/dec5989b793c56ad4dd32173bd2d8595ca78b398/ssl/tls_record.cc#L124-L128> +const ALLOWED_CONSECUTIVE_EMPTY_FRAGMENTS_MAX: u8 = 32; diff --git a/vendor/rustls/src/conn/kernel.rs b/vendor/rustls/src/conn/kernel.rs new file mode 100644 index 00000000..b90fad39 --- /dev/null +++ b/vendor/rustls/src/conn/kernel.rs @@ -0,0 +1,268 @@ +//! Kernel connection API. +//! +//! This module gives you the bare minimum you need to implement a TLS connection +//! that does its own encryption and decryption while still using rustls to manage +//! connection secrets and session tickets. It is intended for use cases like kTLS +//! where you want to use rustls to establish the connection but want to use +//! something else to do the encryption/decryption after that. +//! +//! There are only two things that [`KernelConnection`] is able to do: +//! 1. Compute new traffic secrets when a key update occurs. +//! 2. Save received session tickets sent by a server peer. +//! +//! That's it. Everything else you will need to implement yourself. +//! +//! # Entry Point +//! The entry points into this API are +//! [`UnbufferedClientConnection::dangerous_into_kernel_connection`][client-into] +//! and +//! [`UnbufferedServerConnection::dangerous_into_kernel_connection`][server-into]. +//! +//! In order to actually create an [`KernelConnection`] all of the following +//! must be true: +//! - the connection must have completed its handshake, +//! - the connection must have no buffered TLS data waiting to be sent, and, +//! - the config used to create the connection must have `enable_extract_secrets` +//! set to true. +//! +//! This sounds fairly complicated to achieve at first glance. However, if you +//! drive an unbuffered connection through the handshake until it returns +//! [`WriteTraffic`] then it will end up in an appropriate state to convert +//! into an external connection. +//! +//! [client-into]: crate::client::UnbufferedClientConnection::dangerous_into_kernel_connection +//! [server-into]: crate::server::UnbufferedServerConnection::dangerous_into_kernel_connection +//! [`WriteTraffic`]: crate::unbuffered::ConnectionState::WriteTraffic +//! +//! # Cipher Suite Confidentiality Limits +//! Some cipher suites (notably AES-GCM) have vulnerabilities where they are no +//! longer secure once a certain number of messages have been sent. Normally, +//! rustls tracks how many messages have been written or read and will +//! automatically either refresh keys or emit an error when approaching the +//! confidentiality limit of the cipher suite. +//! +//! [`KernelConnection`] has no way to track this. It is the responsibility +//! of the user of the API to track approximately how many messages have been +//! sent and either refresh the traffic keys or abort the connection before the +//! confidentiality limit is reached. +//! +//! You can find the current confidentiality limit by looking at +//! [`CipherSuiteCommon::confidentiality_limit`] for the cipher suite selected +//! by the connection. +//! +//! [`CipherSuiteCommon::confidentiality_limit`]: crate::CipherSuiteCommon::confidentiality_limit +//! [`KernelConnection`]: crate::kernel::KernelConnection + +use alloc::boxed::Box; +use core::marker::PhantomData; + +use crate::client::ClientConnectionData; +use crate::common_state::Protocol; +use crate::msgs::codec::Codec; +use crate::msgs::handshake::{CertificateChain, NewSessionTicketPayloadTls13}; +use crate::quic::Quic; +use crate::{CommonState, ConnectionTrafficSecrets, Error, ProtocolVersion, SupportedCipherSuite}; + +/// A kernel connection. +/// +/// This does not directly wrap a kernel connection, rather it gives you the +/// minimal interfaces you need to implement a well-behaved TLS connection on +/// top of kTLS. +/// +/// See the [`crate::kernel`] module docs for more details. +pub struct KernelConnection<Data> { + state: Box<dyn KernelState>, + + peer_certificates: Option<CertificateChain<'static>>, + quic: Quic, + + negotiated_version: ProtocolVersion, + protocol: Protocol, + suite: SupportedCipherSuite, + + _data: PhantomData<Data>, +} + +impl<Data> KernelConnection<Data> { + pub(crate) fn new(state: Box<dyn KernelState>, common: CommonState) -> Result<Self, Error> { + Ok(Self { + state, + + peer_certificates: common.peer_certificates, + quic: common.quic, + negotiated_version: common + .negotiated_version + .ok_or(Error::HandshakeNotComplete)?, + protocol: common.protocol, + suite: common + .suite + .ok_or(Error::HandshakeNotComplete)?, + + _data: PhantomData, + }) + } + + /// Retrieves the ciphersuite agreed with the peer. + pub fn negotiated_cipher_suite(&self) -> SupportedCipherSuite { + self.suite + } + + /// Retrieves the protocol version agreed with the peer. + pub fn protocol_version(&self) -> ProtocolVersion { + self.negotiated_version + } + + /// Update the traffic secret used for encrypting messages sent to the peer. + /// + /// Returns the new traffic secret and initial sequence number to use. + /// + /// In order to use the new secret you should send a TLS 1.3 key update to + /// the peer and then use the new traffic secrets to encrypt any future + /// messages. + /// + /// Note that it is only possible to update the traffic secrets on a TLS 1.3 + /// connection. Attempting to do so on a non-TLS 1.3 connection will result + /// in an error. + pub fn update_tx_secret(&mut self) -> Result<(u64, ConnectionTrafficSecrets), Error> { + // The sequence number always starts at 0 after a key update. + self.state + .update_secrets(Direction::Transmit) + .map(|secret| (0, secret)) + } + + /// Update the traffic secret used for decrypting messages received from the + /// peer. + /// + /// Returns the new traffic secret and initial sequence number to use. + /// + /// You should call this method once you receive a TLS 1.3 key update message + /// from the peer. + /// + /// Note that it is only possible to update the traffic secrets on a TLS 1.3 + /// connection. Attempting to do so on a non-TLS 1.3 connection will result + /// in an error. + pub fn update_rx_secret(&mut self) -> Result<(u64, ConnectionTrafficSecrets), Error> { + // The sequence number always starts at 0 after a key update. + self.state + .update_secrets(Direction::Receive) + .map(|secret| (0, secret)) + } +} + +impl KernelConnection<ClientConnectionData> { + /// Handle a `new_session_ticket` message from the peer. + /// + /// This will register the session ticket within with rustls so that it can + /// be used to establish future TLS connections. + /// + /// # Getting the right payload + /// + /// This method expects to be passed the inner payload of the handshake + /// message. This means that you will need to parse the header of the + /// handshake message in order to determine the correct payload to pass in. + /// The message format is described in [RFC 8446 section 4][0]. `payload` + /// should not include the `msg_type` or `length` fields. + /// + /// Code to parse out the payload should look something like this + /// ```no_run + /// use rustls::{ContentType, HandshakeType}; + /// use rustls::kernel::KernelConnection; + /// use rustls::client::ClientConnectionData; + /// + /// # fn doctest(conn: &mut KernelConnection<ClientConnectionData>, typ: ContentType, message: &[u8]) -> Result<(), rustls::Error> { + /// let conn: &mut KernelConnection<ClientConnectionData> = // ... + /// # conn; + /// let typ: ContentType = // ... + /// # typ; + /// let mut message: &[u8] = // ... + /// # message; + /// + /// // Processing for other messages not included in this example + /// assert_eq!(typ, ContentType::Handshake); + /// + /// // There may be multiple handshake payloads within a single handshake message. + /// while !message.is_empty() { + /// let (typ, len, rest) = match message { + /// &[typ, a, b, c, ref rest @ ..] => ( + /// HandshakeType::from(typ), + /// u32::from_be_bytes([0, a, b, c]) as usize, + /// rest + /// ), + /// _ => panic!("error handling not included in this example") + /// }; + /// + /// // Processing for other messages not included in this example. + /// assert_eq!(typ, HandshakeType::NewSessionTicket); + /// assert!(rest.len() >= len, "invalid handshake message"); + /// + /// let (payload, rest) = rest.split_at(len); + /// message = rest; + /// + /// conn.handle_new_session_ticket(payload)?; + /// } + /// # Ok(()) + /// # } + /// ``` + /// + /// # Errors + /// This method will return an error if: + /// - This connection is not a TLS 1.3 connection (in TLS 1.2 session tickets + /// are sent as part of the handshake). + /// - The provided payload is not a valid `new_session_ticket` payload or has + /// extra unparsed trailing data. + /// - An error occurs while the connection updates the session ticket store. + /// + /// [0]: https://datatracker.ietf.org/doc/html/rfc8446#section-4 + pub fn handle_new_session_ticket(&mut self, payload: &[u8]) -> Result<(), Error> { + // We want to return a more specific error here first if this is called + // on a non-TLS 1.3 connection since a parsing error isn't the real issue + // here. + if self.protocol_version() != ProtocolVersion::TLSv1_3 { + return Err(Error::General( + "TLS 1.2 session tickets may not be sent once the handshake has completed".into(), + )); + } + + let nst = NewSessionTicketPayloadTls13::read_bytes(payload)?; + let mut cx = KernelContext { + peer_certificates: self.peer_certificates.as_ref(), + protocol: self.protocol, + quic: &self.quic, + }; + self.state + .handle_new_session_ticket(&mut cx, &nst) + } +} + +pub(crate) trait KernelState: Send + Sync { + /// Update the traffic secret for the specified direction on the connection. + fn update_secrets(&mut self, dir: Direction) -> Result<ConnectionTrafficSecrets, Error>; + + /// Handle a new session ticket. + /// + /// This will only ever be called for client connections, as [`KernelConnection`] + /// only exposes the relevant API for client connections. + fn handle_new_session_ticket( + &mut self, + cx: &mut KernelContext<'_>, + message: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error>; +} + +pub(crate) struct KernelContext<'a> { + pub(crate) peer_certificates: Option<&'a CertificateChain<'static>>, + pub(crate) protocol: Protocol, + pub(crate) quic: &'a Quic, +} + +impl KernelContext<'_> { + pub(crate) fn is_quic(&self) -> bool { + self.protocol == Protocol::Quic + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub(crate) enum Direction { + Transmit, + Receive, +} diff --git a/vendor/rustls/src/conn/unbuffered.rs b/vendor/rustls/src/conn/unbuffered.rs new file mode 100644 index 00000000..84fe3f09 --- /dev/null +++ b/vendor/rustls/src/conn/unbuffered.rs @@ -0,0 +1,613 @@ +//! Unbuffered connection API + +use alloc::vec::Vec; +use core::num::NonZeroUsize; +use core::{fmt, mem}; +#[cfg(feature = "std")] +use std::error::Error as StdError; + +use super::UnbufferedConnectionCommon; +use crate::Error; +use crate::client::ClientConnectionData; +use crate::msgs::deframer::buffers::DeframerSliceBuffer; +use crate::server::ServerConnectionData; + +impl UnbufferedConnectionCommon<ClientConnectionData> { + /// Processes the TLS records in `incoming_tls` buffer until a new [`UnbufferedStatus`] is + /// reached. + pub fn process_tls_records<'c, 'i>( + &'c mut self, + incoming_tls: &'i mut [u8], + ) -> UnbufferedStatus<'c, 'i, ClientConnectionData> { + self.process_tls_records_common(incoming_tls, |_| false, |_, _| unreachable!()) + } +} + +impl UnbufferedConnectionCommon<ServerConnectionData> { + /// Processes the TLS records in `incoming_tls` buffer until a new [`UnbufferedStatus`] is + /// reached. + pub fn process_tls_records<'c, 'i>( + &'c mut self, + incoming_tls: &'i mut [u8], + ) -> UnbufferedStatus<'c, 'i, ServerConnectionData> { + self.process_tls_records_common( + incoming_tls, + |conn| conn.peek_early_data().is_some(), + |conn, incoming_tls| ReadEarlyData::new(conn, incoming_tls).into(), + ) + } +} + +impl<Data> UnbufferedConnectionCommon<Data> { + fn process_tls_records_common<'c, 'i>( + &'c mut self, + incoming_tls: &'i mut [u8], + mut early_data_available: impl FnMut(&mut Self) -> bool, + early_data_state: impl FnOnce(&'c mut Self, &'i mut [u8]) -> ConnectionState<'c, 'i, Data>, + ) -> UnbufferedStatus<'c, 'i, Data> { + let mut buffer = DeframerSliceBuffer::new(incoming_tls); + let mut buffer_progress = self.core.hs_deframer.progress(); + + let (discard, state) = loop { + if early_data_available(self) { + break ( + buffer.pending_discard(), + early_data_state(self, incoming_tls), + ); + } + + if !self + .core + .common_state + .received_plaintext + .is_empty() + { + break ( + buffer.pending_discard(), + ReadTraffic::new(self, incoming_tls).into(), + ); + } + + if let Some(chunk) = self + .core + .common_state + .sendable_tls + .pop() + { + break ( + buffer.pending_discard(), + EncodeTlsData::new(self, chunk).into(), + ); + } + + let deframer_output = + match self + .core + .deframe(None, buffer.filled_mut(), &mut buffer_progress) + { + Err(err) => { + buffer.queue_discard(buffer_progress.take_discard()); + return UnbufferedStatus { + discard: buffer.pending_discard(), + state: Err(err), + }; + } + Ok(r) => r, + }; + + if let Some(msg) = deframer_output { + let mut state = + match mem::replace(&mut self.core.state, Err(Error::HandshakeNotComplete)) { + Ok(state) => state, + Err(e) => { + buffer.queue_discard(buffer_progress.take_discard()); + self.core.state = Err(e.clone()); + return UnbufferedStatus { + discard: buffer.pending_discard(), + state: Err(e), + }; + } + }; + + match self.core.process_msg(msg, state, None) { + Ok(new) => state = new, + + Err(e) => { + buffer.queue_discard(buffer_progress.take_discard()); + self.core.state = Err(e.clone()); + return UnbufferedStatus { + discard: buffer.pending_discard(), + state: Err(e), + }; + } + } + + buffer.queue_discard(buffer_progress.take_discard()); + + self.core.state = Ok(state); + } else if self.wants_write { + break ( + buffer.pending_discard(), + TransmitTlsData { conn: self }.into(), + ); + } else if self + .core + .common_state + .has_received_close_notify + && !self.emitted_peer_closed_state + { + self.emitted_peer_closed_state = true; + break (buffer.pending_discard(), ConnectionState::PeerClosed); + } else if self + .core + .common_state + .has_received_close_notify + && self + .core + .common_state + .has_sent_close_notify + { + break (buffer.pending_discard(), ConnectionState::Closed); + } else if self + .core + .common_state + .may_send_application_data + { + break ( + buffer.pending_discard(), + ConnectionState::WriteTraffic(WriteTraffic { conn: self }), + ); + } else { + break (buffer.pending_discard(), ConnectionState::BlockedHandshake); + } + }; + + UnbufferedStatus { + discard, + state: Ok(state), + } + } +} + +/// The current status of the `UnbufferedConnection*` +#[must_use] +#[derive(Debug)] +pub struct UnbufferedStatus<'c, 'i, Data> { + /// Number of bytes to discard + /// + /// After the `state` field of this object has been handled, `discard` bytes must be + /// removed from the *front* of the `incoming_tls` buffer that was passed to + /// the [`UnbufferedConnectionCommon::process_tls_records`] call that returned this object. + /// + /// This discard operation MUST happen *before* + /// [`UnbufferedConnectionCommon::process_tls_records`] is called again. + pub discard: usize, + + /// The current state of the handshake process + /// + /// This value MUST be handled prior to calling + /// [`UnbufferedConnectionCommon::process_tls_records`] again. See the documentation on the + /// variants of [`ConnectionState`] for more details. + pub state: Result<ConnectionState<'c, 'i, Data>, Error>, +} + +/// The state of the [`UnbufferedConnectionCommon`] object +#[non_exhaustive] // for forwards compatibility; to support caller-side certificate verification +pub enum ConnectionState<'c, 'i, Data> { + /// One, or more, application data records are available + /// + /// See [`ReadTraffic`] for more details on how to use the enclosed object to access + /// the received data. + ReadTraffic(ReadTraffic<'c, 'i, Data>), + + /// Connection has been cleanly closed by the peer. + /// + /// This state is encountered at most once by each connection -- it is + /// "edge" triggered, rather than "level" triggered. + /// + /// It delimits the data received from the peer, meaning you can be sure you + /// have received all the data the peer sent. + /// + /// No further application data will be received from the peer, so no further + /// `ReadTraffic` states will be produced. + /// + /// However, it is possible to _send_ further application data via `WriteTraffic` + /// states, or close the connection cleanly by calling + /// [`WriteTraffic::queue_close_notify()`]. + PeerClosed, + + /// Connection has been cleanly closed by both us and the peer. + /// + /// This is a terminal state. No other states will be produced for this + /// connection. + Closed, + + /// One, or more, early (RTT-0) data records are available + ReadEarlyData(ReadEarlyData<'c, 'i, Data>), + + /// A Handshake record is ready for encoding + /// + /// Call [`EncodeTlsData::encode`] on the enclosed object, providing an `outgoing_tls` + /// buffer to store the encoding + EncodeTlsData(EncodeTlsData<'c, Data>), + + /// Previously encoded handshake records need to be transmitted + /// + /// Transmit the contents of the `outgoing_tls` buffer that was passed to previous + /// [`EncodeTlsData::encode`] calls to the peer. + /// + /// After transmitting the contents, call [`TransmitTlsData::done`] on the enclosed object. + /// The transmitted contents MUST not be sent to the peer more than once so they SHOULD be + /// discarded at this point. + /// + /// At some stages of the handshake process, it's possible to send application-data alongside + /// handshake records. Call [`TransmitTlsData::may_encrypt_app_data`] on the enclosed + /// object to probe if that's allowed. + TransmitTlsData(TransmitTlsData<'c, Data>), + + /// More TLS data is needed to continue with the handshake + /// + /// Request more data from the peer and append the contents to the `incoming_tls` buffer that + /// was passed to [`UnbufferedConnectionCommon::process_tls_records`]. + BlockedHandshake, + + /// The handshake process has been completed. + /// + /// [`WriteTraffic::encrypt`] can be called on the enclosed object to encrypt application + /// data into an `outgoing_tls` buffer. Similarly, [`WriteTraffic::queue_close_notify`] can + /// be used to encrypt a close_notify alert message into a buffer to signal the peer that the + /// connection is being closed. Data written into `outgoing_buffer` by either method MAY be + /// transmitted to the peer during this state. + /// + /// Once this state has been reached, data MAY be requested from the peer and appended to an + /// `incoming_tls` buffer that will be passed to a future + /// [`UnbufferedConnectionCommon::process_tls_records`] invocation. When enough data has been + /// appended to `incoming_tls`, [`UnbufferedConnectionCommon::process_tls_records`] will yield + /// the [`ConnectionState::ReadTraffic`] state. + WriteTraffic(WriteTraffic<'c, Data>), +} + +impl<'c, 'i, Data> From<ReadTraffic<'c, 'i, Data>> for ConnectionState<'c, 'i, Data> { + fn from(v: ReadTraffic<'c, 'i, Data>) -> Self { + Self::ReadTraffic(v) + } +} + +impl<'c, 'i, Data> From<ReadEarlyData<'c, 'i, Data>> for ConnectionState<'c, 'i, Data> { + fn from(v: ReadEarlyData<'c, 'i, Data>) -> Self { + Self::ReadEarlyData(v) + } +} + +impl<'c, Data> From<EncodeTlsData<'c, Data>> for ConnectionState<'c, '_, Data> { + fn from(v: EncodeTlsData<'c, Data>) -> Self { + Self::EncodeTlsData(v) + } +} + +impl<'c, Data> From<TransmitTlsData<'c, Data>> for ConnectionState<'c, '_, Data> { + fn from(v: TransmitTlsData<'c, Data>) -> Self { + Self::TransmitTlsData(v) + } +} + +impl<Data> fmt::Debug for ConnectionState<'_, '_, Data> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ReadTraffic(..) => f.debug_tuple("ReadTraffic").finish(), + + Self::PeerClosed => write!(f, "PeerClosed"), + + Self::Closed => write!(f, "Closed"), + + Self::ReadEarlyData(..) => f.debug_tuple("ReadEarlyData").finish(), + + Self::EncodeTlsData(..) => f.debug_tuple("EncodeTlsData").finish(), + + Self::TransmitTlsData(..) => f + .debug_tuple("TransmitTlsData") + .finish(), + + Self::BlockedHandshake => f + .debug_tuple("BlockedHandshake") + .finish(), + + Self::WriteTraffic(..) => f.debug_tuple("WriteTraffic").finish(), + } + } +} + +/// Application data is available +pub struct ReadTraffic<'c, 'i, Data> { + conn: &'c mut UnbufferedConnectionCommon<Data>, + // for forwards compatibility; to support in-place decryption in the future + _incoming_tls: &'i mut [u8], + + // owner of the latest chunk obtained in `next_record`, as borrowed by + // `AppDataRecord` + chunk: Option<Vec<u8>>, +} + +impl<'c, 'i, Data> ReadTraffic<'c, 'i, Data> { + fn new(conn: &'c mut UnbufferedConnectionCommon<Data>, _incoming_tls: &'i mut [u8]) -> Self { + Self { + conn, + _incoming_tls, + chunk: None, + } + } + + /// Decrypts and returns the next available app-data record + // TODO deprecate in favor of `Iterator` implementation, which requires in-place decryption + pub fn next_record(&mut self) -> Option<Result<AppDataRecord<'_>, Error>> { + self.chunk = self + .conn + .core + .common_state + .received_plaintext + .pop(); + self.chunk.as_ref().map(|chunk| { + Ok(AppDataRecord { + discard: 0, + payload: chunk, + }) + }) + } + + /// Returns the payload size of the next app-data record *without* decrypting it + /// + /// Returns `None` if there are no more app-data records + pub fn peek_len(&self) -> Option<NonZeroUsize> { + self.conn + .core + .common_state + .received_plaintext + .peek() + .and_then(|ch| NonZeroUsize::new(ch.len())) + } +} + +/// Early application-data is available. +pub struct ReadEarlyData<'c, 'i, Data> { + conn: &'c mut UnbufferedConnectionCommon<Data>, + + // for forwards compatibility; to support in-place decryption in the future + _incoming_tls: &'i mut [u8], + + // owner of the latest chunk obtained in `next_record`, as borrowed by + // `AppDataRecord` + chunk: Option<Vec<u8>>, +} + +impl<'c, 'i> ReadEarlyData<'c, 'i, ServerConnectionData> { + fn new( + conn: &'c mut UnbufferedConnectionCommon<ServerConnectionData>, + _incoming_tls: &'i mut [u8], + ) -> Self { + Self { + conn, + _incoming_tls, + chunk: None, + } + } + + /// decrypts and returns the next available app-data record + // TODO deprecate in favor of `Iterator` implementation, which requires in-place decryption + pub fn next_record(&mut self) -> Option<Result<AppDataRecord<'_>, Error>> { + self.chunk = self.conn.pop_early_data(); + self.chunk.as_ref().map(|chunk| { + Ok(AppDataRecord { + discard: 0, + payload: chunk, + }) + }) + } + + /// returns the payload size of the next app-data record *without* decrypting it + /// + /// returns `None` if there are no more app-data records + pub fn peek_len(&self) -> Option<NonZeroUsize> { + self.conn + .peek_early_data() + .and_then(|ch| NonZeroUsize::new(ch.len())) + } +} + +/// A decrypted application-data record +pub struct AppDataRecord<'i> { + /// Number of additional bytes to discard + /// + /// This number MUST be added to the value of [`UnbufferedStatus.discard`] *prior* to the + /// discard operation. See [`UnbufferedStatus.discard`] for more details + pub discard: usize, + + /// The payload of the app-data record + pub payload: &'i [u8], +} + +/// Allows encrypting app-data +pub struct WriteTraffic<'c, Data> { + conn: &'c mut UnbufferedConnectionCommon<Data>, +} + +impl<Data> WriteTraffic<'_, Data> { + /// Encrypts `application_data` into the `outgoing_tls` buffer + /// + /// Returns the number of bytes that were written into `outgoing_tls`, or an error if + /// the provided buffer is too small. In the error case, `outgoing_tls` is not modified + pub fn encrypt( + &mut self, + application_data: &[u8], + outgoing_tls: &mut [u8], + ) -> Result<usize, EncryptError> { + self.conn + .core + .maybe_refresh_traffic_keys(); + self.conn + .core + .common_state + .write_plaintext(application_data.into(), outgoing_tls) + } + + /// Encrypts a close_notify warning alert in `outgoing_tls` + /// + /// Returns the number of bytes that were written into `outgoing_tls`, or an error if + /// the provided buffer is too small. In the error case, `outgoing_tls` is not modified + pub fn queue_close_notify(&mut self, outgoing_tls: &mut [u8]) -> Result<usize, EncryptError> { + self.conn + .core + .common_state + .eager_send_close_notify(outgoing_tls) + } + + /// Arranges for a TLS1.3 `key_update` to be sent. + /// + /// This consumes the `WriteTraffic` state: to actually send the message, + /// call [`UnbufferedConnectionCommon::process_tls_records`] again which will + /// return a `ConnectionState::EncodeTlsData` that emits the `key_update` + /// message. + /// + /// See [`ConnectionCommon::refresh_traffic_keys()`] for full documentation, + /// including why you might call this and in what circumstances it will fail. + /// + /// [`ConnectionCommon::refresh_traffic_keys()`]: crate::ConnectionCommon::refresh_traffic_keys + pub fn refresh_traffic_keys(self) -> Result<(), Error> { + self.conn.core.refresh_traffic_keys() + } +} + +/// A handshake record must be encoded +pub struct EncodeTlsData<'c, Data> { + conn: &'c mut UnbufferedConnectionCommon<Data>, + chunk: Option<Vec<u8>>, +} + +impl<'c, Data> EncodeTlsData<'c, Data> { + fn new(conn: &'c mut UnbufferedConnectionCommon<Data>, chunk: Vec<u8>) -> Self { + Self { + conn, + chunk: Some(chunk), + } + } + + /// Encodes a handshake record into the `outgoing_tls` buffer + /// + /// Returns the number of bytes that were written into `outgoing_tls`, or an error if + /// the provided buffer is too small. In the error case, `outgoing_tls` is not modified + pub fn encode(&mut self, outgoing_tls: &mut [u8]) -> Result<usize, EncodeError> { + let Some(chunk) = self.chunk.take() else { + return Err(EncodeError::AlreadyEncoded); + }; + + let required_size = chunk.len(); + + if required_size > outgoing_tls.len() { + self.chunk = Some(chunk); + Err(InsufficientSizeError { required_size }.into()) + } else { + let written = chunk.len(); + outgoing_tls[..written].copy_from_slice(&chunk); + + self.conn.wants_write = true; + + Ok(written) + } + } +} + +/// Previously encoded TLS data must be transmitted +pub struct TransmitTlsData<'c, Data> { + pub(crate) conn: &'c mut UnbufferedConnectionCommon<Data>, +} + +impl<Data> TransmitTlsData<'_, Data> { + /// Signals that the previously encoded TLS data has been transmitted + pub fn done(self) { + self.conn.wants_write = false; + } + + /// Returns an adapter that allows encrypting application data + /// + /// If allowed at this stage of the handshake process + pub fn may_encrypt_app_data(&mut self) -> Option<WriteTraffic<'_, Data>> { + if self + .conn + .core + .common_state + .may_send_application_data + { + Some(WriteTraffic { conn: self.conn }) + } else { + None + } + } +} + +/// Errors that may arise when encoding a handshake record +#[derive(Debug)] +pub enum EncodeError { + /// Provided buffer was too small + InsufficientSize(InsufficientSizeError), + + /// The handshake record has already been encoded; do not call `encode` again + AlreadyEncoded, +} + +impl From<InsufficientSizeError> for EncodeError { + fn from(v: InsufficientSizeError) -> Self { + Self::InsufficientSize(v) + } +} + +impl fmt::Display for EncodeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InsufficientSize(InsufficientSizeError { required_size }) => write!( + f, + "cannot encode due to insufficient size, {required_size} bytes are required" + ), + Self::AlreadyEncoded => "cannot encode, data has already been encoded".fmt(f), + } + } +} + +#[cfg(feature = "std")] +impl StdError for EncodeError {} + +/// Errors that may arise when encrypting application data +#[derive(Debug)] +pub enum EncryptError { + /// Provided buffer was too small + InsufficientSize(InsufficientSizeError), + + /// Encrypter has been exhausted + EncryptExhausted, +} + +impl From<InsufficientSizeError> for EncryptError { + fn from(v: InsufficientSizeError) -> Self { + Self::InsufficientSize(v) + } +} + +impl fmt::Display for EncryptError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InsufficientSize(InsufficientSizeError { required_size }) => write!( + f, + "cannot encrypt due to insufficient size, {required_size} bytes are required" + ), + Self::EncryptExhausted => f.write_str("encrypter has been exhausted"), + } + } +} + +#[cfg(feature = "std")] +impl StdError for EncryptError {} + +/// Provided buffer was too small +#[derive(Clone, Copy, Debug)] +pub struct InsufficientSizeError { + /// buffer must be at least this size + pub required_size: usize, +} diff --git a/vendor/rustls/src/crypto/aws_lc_rs/hpke.rs b/vendor/rustls/src/crypto/aws_lc_rs/hpke.rs new file mode 100644 index 00000000..de0e5d63 --- /dev/null +++ b/vendor/rustls/src/crypto/aws_lc_rs/hpke.rs @@ -0,0 +1,1191 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::fmt::{self, Debug, Formatter}; + +use aws_lc_rs::aead::{ + self, Aad, BoundKey, NONCE_LEN, Nonce, NonceSequence, OpeningKey, SealingKey, UnboundKey, +}; +use aws_lc_rs::agreement; +use aws_lc_rs::cipher::{AES_128_KEY_LEN, AES_256_KEY_LEN}; +use aws_lc_rs::digest::{SHA256_OUTPUT_LEN, SHA384_OUTPUT_LEN, SHA512_OUTPUT_LEN}; +use aws_lc_rs::encoding::{AsBigEndian, Curve25519SeedBin, EcPrivateKeyBin}; +use zeroize::Zeroize; + +use crate::crypto::aws_lc_rs::hmac::{HMAC_SHA256, HMAC_SHA384, HMAC_SHA512}; +use crate::crypto::aws_lc_rs::unspecified_err; +use crate::crypto::hpke::{ + EncapsulatedSecret, Hpke, HpkeOpener, HpkePrivateKey, HpkePublicKey, HpkeSealer, HpkeSuite, +}; +use crate::crypto::tls13::{HkdfExpander, HkdfPrkExtract, HkdfUsingHmac, expand}; +use crate::msgs::enums::{HpkeAead, HpkeKdf, HpkeKem}; +use crate::msgs::handshake::HpkeSymmetricCipherSuite; +#[cfg(feature = "std")] +use crate::sync::Arc; +use crate::{Error, OtherError}; + +/// Default [RFC 9180] Hybrid Public Key Encryption (HPKE) suites supported by aws-lc-rs cryptography. +pub static ALL_SUPPORTED_SUITES: &[&dyn Hpke] = &[ + DH_KEM_P256_HKDF_SHA256_AES_128, + DH_KEM_P256_HKDF_SHA256_AES_256, + #[cfg(not(feature = "fips"))] + DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305, + DH_KEM_P384_HKDF_SHA384_AES_128, + DH_KEM_P384_HKDF_SHA384_AES_256, + #[cfg(not(feature = "fips"))] + DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305, + DH_KEM_P521_HKDF_SHA512_AES_128, + DH_KEM_P521_HKDF_SHA512_AES_256, + #[cfg(not(feature = "fips"))] + DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305, + #[cfg(not(feature = "fips"))] + DH_KEM_X25519_HKDF_SHA256_AES_128, + #[cfg(not(feature = "fips"))] + DH_KEM_X25519_HKDF_SHA256_AES_256, + #[cfg(not(feature = "fips"))] + DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305, +]; + +/// HPKE suite using ECDH P-256 for agreement, HKDF SHA-256 for key derivation, and AEAD AES-128-GCM +/// for symmetric encryption. +pub static DH_KEM_P256_HKDF_SHA256_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA256_OUTPUT_LEN> = + &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_P256_HKDF_SHA256, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA256, + aead_id: HpkeAead::AES_128_GCM, + }, + }, + dh_kem: DH_KEM_P256_HKDF_SHA256, + hkdf: RING_HKDF_HMAC_SHA256, + aead: &aead::AES_128_GCM, + }; + +/// HPKE suite using ECDH P-256 for agreement, HKDF SHA-256 for key derivation and AEAD AES-256-GCM +/// for symmetric encryption. +pub static DH_KEM_P256_HKDF_SHA256_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA256_OUTPUT_LEN> = + &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_P256_HKDF_SHA256, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA256, + aead_id: HpkeAead::AES_256_GCM, + }, + }, + dh_kem: DH_KEM_P256_HKDF_SHA256, + hkdf: RING_HKDF_HMAC_SHA256, + aead: &aead::AES_256_GCM, + }; + +/// HPKE suite using ECDH P-256 for agreement, HKDF SHA-256 for key derivation, and AEAD +/// CHACHA20-POLY-1305 for symmetric encryption. +pub static DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305: &HpkeAwsLcRs< + CHACHA_KEY_LEN, + SHA256_OUTPUT_LEN, +> = &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_P256_HKDF_SHA256, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA256, + aead_id: HpkeAead::CHACHA20_POLY_1305, + }, + }, + dh_kem: DH_KEM_P256_HKDF_SHA256, + hkdf: RING_HKDF_HMAC_SHA256, + aead: &aead::CHACHA20_POLY1305, +}; + +/// HPKE suite using ECDH P-384 for agreement, HKDF SHA-384 for key derivation, and AEAD AES-128-GCM +/// for symmetric encryption. +pub static DH_KEM_P384_HKDF_SHA384_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA384_OUTPUT_LEN> = + &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_P384_HKDF_SHA384, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA384, + aead_id: HpkeAead::AES_128_GCM, + }, + }, + dh_kem: DH_KEM_P384_HKDF_SHA384, + hkdf: RING_HKDF_HMAC_SHA384, + aead: &aead::AES_128_GCM, + }; + +/// HPKE suite using ECDH P-384 for agreement, HKDF SHA-384 for key derivation, and AEAD AES-256-GCM +/// for symmetric encryption. +pub static DH_KEM_P384_HKDF_SHA384_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA384_OUTPUT_LEN> = + &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_P384_HKDF_SHA384, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA384, + aead_id: HpkeAead::AES_256_GCM, + }, + }, + dh_kem: DH_KEM_P384_HKDF_SHA384, + hkdf: RING_HKDF_HMAC_SHA384, + aead: &aead::AES_256_GCM, + }; + +/// HPKE suite using ECDH P-384 for agreement, HKDF SHA-384 for key derivation, and AEAD +/// CHACHA20-POLY-1305 for symmetric encryption. +pub static DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305: &HpkeAwsLcRs< + CHACHA_KEY_LEN, + SHA384_OUTPUT_LEN, +> = &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_P384_HKDF_SHA384, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA384, + aead_id: HpkeAead::CHACHA20_POLY_1305, + }, + }, + dh_kem: DH_KEM_P384_HKDF_SHA384, + hkdf: RING_HKDF_HMAC_SHA384, + aead: &aead::CHACHA20_POLY1305, +}; + +/// HPKE suite using ECDH P-521 for agreement, HKDF SHA-512 for key derivation, and AEAD AES-128-GCM +/// for symmetric encryption. +pub static DH_KEM_P521_HKDF_SHA512_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA512_OUTPUT_LEN> = + &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_P521_HKDF_SHA512, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA512, + aead_id: HpkeAead::AES_128_GCM, + }, + }, + dh_kem: DH_KEM_P521_HKDF_SHA512, + hkdf: RING_HKDF_HMAC_SHA512, + aead: &aead::AES_128_GCM, + }; + +/// HPKE suite using ECDH P-521 for agreement, HKDF SHA-512 for key derivation, and AEAD AES-256-GCM +/// for symmetric encryption. +pub static DH_KEM_P521_HKDF_SHA512_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA512_OUTPUT_LEN> = + &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_P521_HKDF_SHA512, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA512, + aead_id: HpkeAead::AES_256_GCM, + }, + }, + dh_kem: DH_KEM_P521_HKDF_SHA512, + hkdf: RING_HKDF_HMAC_SHA512, + aead: &aead::AES_256_GCM, + }; + +/// HPKE suite using ECDH P-521 for agreement, HKDF SHA-512 for key derivation, and AEAD +/// CHACHA20-POLY-1305 for symmetric encryption. +pub static DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305: &HpkeAwsLcRs< + CHACHA_KEY_LEN, + SHA512_OUTPUT_LEN, +> = &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_P521_HKDF_SHA512, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA512, + aead_id: HpkeAead::CHACHA20_POLY_1305, + }, + }, + dh_kem: DH_KEM_P521_HKDF_SHA512, + hkdf: RING_HKDF_HMAC_SHA512, + aead: &aead::CHACHA20_POLY1305, +}; + +/// HPKE suite using ECDH X25519 for agreement, HKDF SHA-256 for key derivation, and AEAD AES-128-GCM +/// for symmetric encryption. +pub static DH_KEM_X25519_HKDF_SHA256_AES_128: &HpkeAwsLcRs<AES_128_KEY_LEN, SHA256_OUTPUT_LEN> = + &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_X25519_HKDF_SHA256, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA256, + aead_id: HpkeAead::AES_128_GCM, + }, + }, + dh_kem: DH_KEM_X25519_HKDF_SHA256, + hkdf: RING_HKDF_HMAC_SHA256, + aead: &aead::AES_128_GCM, + }; + +/// HPKE suite using ECDH X25519 for agreement, HKDF SHA-256 for key derivation, and AEAD AES-256-GCM +/// for symmetric encryption. +pub static DH_KEM_X25519_HKDF_SHA256_AES_256: &HpkeAwsLcRs<AES_256_KEY_LEN, SHA256_OUTPUT_LEN> = + &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_X25519_HKDF_SHA256, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA256, + aead_id: HpkeAead::AES_256_GCM, + }, + }, + dh_kem: DH_KEM_X25519_HKDF_SHA256, + hkdf: RING_HKDF_HMAC_SHA256, + aead: &aead::AES_256_GCM, + }; + +/// HPKE suite using ECDH X25519 for agreement, HKDF SHA-256 for key derivation, and AEAD +/// CHACHA20-POLY-1305 for symmetric encryption. +pub static DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305: &HpkeAwsLcRs< + CHACHA_KEY_LEN, + SHA256_OUTPUT_LEN, +> = &HpkeAwsLcRs { + suite: HpkeSuite { + kem: HpkeKem::DHKEM_X25519_HKDF_SHA256, + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA256, + aead_id: HpkeAead::CHACHA20_POLY_1305, + }, + }, + dh_kem: DH_KEM_X25519_HKDF_SHA256, + hkdf: RING_HKDF_HMAC_SHA256, + aead: &aead::CHACHA20_POLY1305, +}; + +/// `HpkeAwsLcRs` holds the concrete instantiations of the algorithms specified by the [HpkeSuite]. +pub struct HpkeAwsLcRs<const KEY_SIZE: usize, const KDF_SIZE: usize> { + suite: HpkeSuite, + dh_kem: &'static DhKem<KDF_SIZE>, + hkdf: &'static dyn HkdfPrkExtract, + aead: &'static aead::Algorithm, +} + +impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> { + /// See [RFC 9180 §5.1 "Creating the Encryption Context"][0]. + /// + /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1 + fn key_schedule( + &self, + shared_secret: KemSharedSecret<KDF_SIZE>, + info: &[u8], + ) -> Result<KeySchedule<KEY_SIZE>, Error> { + // Note: we use an empty IKM for the `psk_id_hash` and `secret` labelled extractions because + // there is no PSK ID in base mode HPKE. + + let suite_id = LabeledSuiteId::Hpke(self.suite); + let psk_id_hash = labeled_extract_for_prk(self.hkdf, suite_id, None, Label::PskIdHash, &[]); + let info_hash = labeled_extract_for_prk(self.hkdf, suite_id, None, Label::InfoHash, info); + let key_schedule_context = [ + &[0][..], // base mode (0x00) + &psk_id_hash, + &info_hash, + ] + .concat(); + + let key = AeadKey(self.key_schedule_labeled_expand::<KEY_SIZE>( + &shared_secret, + &key_schedule_context, + Label::Key, + )); + + let base_nonce = self.key_schedule_labeled_expand::<NONCE_LEN>( + &shared_secret, + &key_schedule_context, + Label::BaseNonce, + ); + + Ok(KeySchedule { + aead: self.aead, + key, + base_nonce, + seq_num: 0, + }) + } + + fn key_schedule_labeled_expand<const L: usize>( + &self, + shared_secret: &KemSharedSecret<KDF_SIZE>, + key_schedule_context: &[u8], + label: Label, + ) -> [u8; L] { + let suite_id = LabeledSuiteId::Hpke(self.suite); + labeled_expand::<L>( + suite_id, + labeled_extract_for_expand( + self.hkdf, + suite_id, + Some(&shared_secret.0), + Label::Secret, + &[], + ), + label, + key_schedule_context, + ) + } +} + +impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Hpke for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> { + fn seal( + &self, + info: &[u8], + aad: &[u8], + plaintext: &[u8], + pub_key: &HpkePublicKey, + ) -> Result<(EncapsulatedSecret, Vec<u8>), Error> { + let (encap, mut sealer) = self.setup_sealer(info, pub_key)?; + Ok((encap, sealer.seal(aad, plaintext)?)) + } + + fn setup_sealer( + &self, + info: &[u8], + pub_key: &HpkePublicKey, + ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> { + let (encap, sealer) = Sealer::new(self, info, pub_key)?; + Ok((encap, Box::new(sealer))) + } + + fn open( + &self, + enc: &EncapsulatedSecret, + info: &[u8], + aad: &[u8], + ciphertext: &[u8], + secret_key: &HpkePrivateKey, + ) -> Result<Vec<u8>, Error> { + self.setup_opener(enc, info, secret_key)? + .open(aad, ciphertext) + } + + fn setup_opener( + &self, + enc: &EncapsulatedSecret, + info: &[u8], + secret_key: &HpkePrivateKey, + ) -> Result<Box<dyn HpkeOpener + 'static>, Error> { + Ok(Box::new(Opener::new(self, enc, info, secret_key)?)) + } + + fn fips(&self) -> bool { + matches!( + // We make a FIPS determination based on the suite's DH KEM and AEAD choice. + // We don't need to examine the KDF choice because all supported KDFs are FIPS + // compatible. + (self.suite.kem, self.suite.sym.aead_id), + ( + // Only the NIST "P-curve" DH KEMs are FIPS compatible. + HpkeKem::DHKEM_P256_HKDF_SHA256 + | HpkeKem::DHKEM_P384_HKDF_SHA384 + | HpkeKem::DHKEM_P521_HKDF_SHA512, + // Only the AES AEADs are FIPS compatible. + HpkeAead::AES_128_GCM | HpkeAead::AES_256_GCM, + ) + ) + } + + fn generate_key_pair(&self) -> Result<(HpkePublicKey, HpkePrivateKey), Error> { + (self.dh_kem.key_generator)() + } + + fn suite(&self) -> HpkeSuite { + self.suite + } +} + +impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + self.suite.fmt(f) + } +} + +/// Adapts a [KeySchedule] and [AeadKey] for the role of a [HpkeSealer]. +struct Sealer<const KEY_SIZE: usize, const KDF_SIZE: usize> { + key_schedule: KeySchedule<KEY_SIZE>, +} + +impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Sealer<KEY_SIZE, KDF_SIZE> { + /// See [RFC 9180 §5.1.1 "Encryption to a Public Key"][0]. + /// + /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.1 + fn new( + suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>, + info: &[u8], + pub_key: &HpkePublicKey, + ) -> Result<(EncapsulatedSecret, Self), Error> { + // def SetupBaseS(pkR, info): + // shared_secret, enc = Encap(pkR) + // return enc, KeyScheduleS(mode_base, shared_secret, info, + // default_psk, default_psk_id) + + let (shared_secret, enc) = suite.dh_kem.encap(pub_key)?; + let key_schedule = suite.key_schedule(shared_secret, info)?; + Ok((enc, Self { key_schedule })) + } + + /// A **test only** constructor that uses a pre-specified ephemeral agreement private key + /// instead of one that is randomly generated. + #[cfg(test)] + fn test_only_new( + suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>, + info: &[u8], + pub_key: &HpkePublicKey, + sk_e: &[u8], + ) -> Result<(EncapsulatedSecret, Self), Error> { + let (shared_secret, enc) = suite + .dh_kem + .test_only_encap(pub_key, sk_e)?; + let key_schedule = suite.key_schedule(shared_secret, info)?; + Ok((enc, Self { key_schedule })) + } +} + +impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeSealer for Sealer<KEY_SIZE, KDF_SIZE> { + fn seal(&mut self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error> { + // def ContextS.Seal(aad, pt): + // ct = Seal(self.key, self.ComputeNonce(self.seq), aad, pt) + // self.IncrementSeq() + // return ct + + let key = UnboundKey::new(self.key_schedule.aead, &self.key_schedule.key.0) + .map_err(unspecified_err)?; + let mut sealing_key = SealingKey::new(key, &mut self.key_schedule); + + let mut in_out_buffer = Vec::from(plaintext); + sealing_key + .seal_in_place_append_tag(Aad::from(aad), &mut in_out_buffer) + .map_err(unspecified_err)?; + + Ok(in_out_buffer) + } +} + +impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Sealer<KEY_SIZE, KDF_SIZE> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Sealer").finish() + } +} + +/// Adapts a [KeySchedule] and [AeadKey] for the role of a [HpkeOpener]. +struct Opener<const KEY_SIZE: usize, const KDF_SIZE: usize> { + key_schedule: KeySchedule<KEY_SIZE>, +} + +impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Opener<KEY_SIZE, KDF_SIZE> { + /// See [RFC 9180 §5.1.1 "Encryption to a Public Key"][0]. + /// + /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.1.1 + fn new( + suite: &HpkeAwsLcRs<KEY_SIZE, KDF_SIZE>, + enc: &EncapsulatedSecret, + info: &[u8], + secret_key: &HpkePrivateKey, + ) -> Result<Self, Error> { + // def SetupBaseR(enc, skR, info): + // shared_secret = Decap(enc, skR) + // return KeyScheduleR(mode_base, shared_secret, info, + // default_psk, default_psk_id) + Ok(Self { + key_schedule: suite.key_schedule(suite.dh_kem.decap(enc, secret_key)?, info)?, + }) + } +} + +impl<const KEY_SIZE: usize, const KDF_SIZE: usize> HpkeOpener for Opener<KEY_SIZE, KDF_SIZE> { + fn open(&mut self, aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Error> { + // def ContextR.Open(aad, ct): + // pt = Open(self.key, self.ComputeNonce(self.seq), aad, ct) + // if pt == OpenError: + // raise OpenError + // self.IncrementSeq() + // return pt + + let key = UnboundKey::new(self.key_schedule.aead, &self.key_schedule.key.0) + .map_err(unspecified_err)?; + let mut opening_key = OpeningKey::new(key, &mut self.key_schedule); + + let mut in_out_buffer = Vec::from(ciphertext); + let plaintext = opening_key + .open_in_place(Aad::from(aad), &mut in_out_buffer) + .map_err(unspecified_err)?; + + Ok(plaintext.to_vec()) + } +} + +impl<const KEY_SIZE: usize, const KDF_SIZE: usize> Debug for Opener<KEY_SIZE, KDF_SIZE> { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Opener").finish() + } +} + +/// A Diffie-Hellman (DH) based Key Encapsulation Mechanism (KEM). +/// +/// See [RFC 9180 §4.1 "DH-Based KEM (DHKEM)"][0]. +/// +/// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1 +struct DhKem<const KDF_SIZE: usize> { + id: HpkeKem, + agreement_algorithm: &'static agreement::Algorithm, + key_generator: + &'static (dyn Fn() -> Result<(HpkePublicKey, HpkePrivateKey), Error> + Send + Sync), + hkdf: &'static dyn HkdfPrkExtract, +} + +impl<const KDF_SIZE: usize> DhKem<KDF_SIZE> { + /// See [RFC 9180 §4.1 "DH-Based KEM (DHKEM)"][0]. + /// + /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1 + fn encap( + &self, + recipient: &HpkePublicKey, + ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> { + // def Encap(pkR): + // skE, pkE = GenerateKeyPair() + + let sk_e = + agreement::PrivateKey::generate(self.agreement_algorithm).map_err(unspecified_err)?; + self.encap_impl(recipient, sk_e) + } + + /// A test-only encap operation that uses a fixed `test_only_ske` instead of generating + /// one randomly. + #[cfg(test)] + fn test_only_encap( + &self, + recipient: &HpkePublicKey, + test_only_ske: &[u8], + ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> { + // For test contexts only, we accept a static sk_e as an argument. + let sk_e = agreement::PrivateKey::from_private_key(self.agreement_algorithm, test_only_ske) + .map_err(key_rejected_err)?; + self.encap_impl(recipient, sk_e) + } + + fn encap_impl( + &self, + recipient: &HpkePublicKey, + sk_e: agreement::PrivateKey, + ) -> Result<(KemSharedSecret<KDF_SIZE>, EncapsulatedSecret), Error> { + // def Encap(pkR): + // skE, pkE = GenerateKeyPair() + // dh = DH(skE, pkR) + // enc = SerializePublicKey(pkE) + // + // pkRm = SerializePublicKey(pkR) + // kem_context = concat(enc, pkRm) + // + // shared_secret = ExtractAndExpand(dh, kem_context) + // return shared_secret, enc + + let enc = sk_e + .compute_public_key() + .map_err(unspecified_err)?; + let pk_r = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &recipient.0); + let kem_context = [enc.as_ref(), pk_r.bytes()].concat(); + + let shared_secret = agreement::agree(&sk_e, &pk_r, aws_lc_rs::error::Unspecified, |dh| { + Ok(self.extract_and_expand(dh, &kem_context)) + }) + .map_err(unspecified_err)?; + + Ok(( + KemSharedSecret(shared_secret), + EncapsulatedSecret(enc.as_ref().into()), + )) + } + + /// See [RFC 9180 §4.1 "DH-Based KEM (DHKEM)"][0]. + /// + /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1 + fn decap( + &self, + enc: &EncapsulatedSecret, + recipient: &HpkePrivateKey, + ) -> Result<KemSharedSecret<KDF_SIZE>, Error> { + // def Decap(enc, skR): + // pkE = DeserializePublicKey(enc) + // dh = DH(skR, pkE) + // + // pkRm = SerializePublicKey(pk(skR)) + // kem_context = concat(enc, pkRm) + // + // shared_secret = ExtractAndExpand(dh, kem_context) + // return shared_secret + + let pk_e = agreement::UnparsedPublicKey::new(self.agreement_algorithm, &enc.0); + let sk_r = agreement::PrivateKey::from_private_key( + self.agreement_algorithm, + recipient.secret_bytes(), + ) + .map_err(key_rejected_err)?; + let pk_rm = sk_r + .compute_public_key() + .map_err(unspecified_err)?; + let kem_context = [&enc.0, pk_rm.as_ref()].concat(); + + let shared_secret = agreement::agree(&sk_r, &pk_e, aws_lc_rs::error::Unspecified, |dh| { + Ok(self.extract_and_expand(dh, &kem_context)) + }) + .map_err(unspecified_err)?; + + Ok(KemSharedSecret(shared_secret)) + } + + /// See [RFC 9180 §4.1 "DH-Based KEM (DHKEM)"][0]. + /// + /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4.1 + fn extract_and_expand(&self, dh: &[u8], kem_context: &[u8]) -> [u8; KDF_SIZE] { + // def ExtractAndExpand(dh, kem_context): + // eae_prk = LabeledExtract("", "eae_prk", dh) + // shared_secret = LabeledExpand(eae_prk, "shared_secret", + // kem_context, Nsecret) + // return shared_secret + + let suite_id = LabeledSuiteId::Kem(self.id); + labeled_expand( + suite_id, + labeled_extract_for_expand(self.hkdf, suite_id, None, Label::EaePrk, dh), + Label::SharedSecret, + kem_context, + ) + } +} + +static DH_KEM_P256_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem { + id: HpkeKem::DHKEM_P256_HKDF_SHA256, + agreement_algorithm: &agreement::ECDH_P256, + key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P256), + hkdf: RING_HKDF_HMAC_SHA256, +}; + +static DH_KEM_P384_HKDF_SHA384: &DhKem<SHA384_OUTPUT_LEN> = &DhKem { + id: HpkeKem::DHKEM_P384_HKDF_SHA384, + agreement_algorithm: &agreement::ECDH_P384, + key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P384), + hkdf: RING_HKDF_HMAC_SHA384, +}; + +static DH_KEM_P521_HKDF_SHA512: &DhKem<SHA512_OUTPUT_LEN> = &DhKem { + id: HpkeKem::DHKEM_P521_HKDF_SHA512, + agreement_algorithm: &agreement::ECDH_P521, + key_generator: &|| generate_p_curve_key_pair(&agreement::ECDH_P521), + hkdf: RING_HKDF_HMAC_SHA512, +}; + +static DH_KEM_X25519_HKDF_SHA256: &DhKem<SHA256_OUTPUT_LEN> = &DhKem { + id: HpkeKem::DHKEM_X25519_HKDF_SHA256, + agreement_algorithm: &agreement::X25519, + key_generator: &generate_x25519_key_pair, + hkdf: RING_HKDF_HMAC_SHA256, +}; + +/// Generate a NIST P-256, P-384 or P-512 key pair expressed as a raw big-endian fixed-length +/// integer. +/// +/// We must disambiguate the [`AsBigEndian`] trait in-use and this function uses +/// [`AsBigEndian<EcPrivateKeyBin>`], which does not support [`agreement::X25519`]. +/// For generating X25519 keys see [`generate_x25519_key_pair`]. +fn generate_p_curve_key_pair( + alg: &'static agreement::Algorithm, +) -> Result<(HpkePublicKey, HpkePrivateKey), Error> { + // We only initialize DH KEM instances that use this function as a key generator + // for non-X25519 algorithms. Debug assert this just in case since `AsBigEndian<EcPrivateKeyBin>` + // will panic for this algorithm. + debug_assert_ne!(alg, &agreement::X25519); + let (public_key, private_key) = generate_key_pair(alg)?; + let raw_private_key: EcPrivateKeyBin<'_> = private_key + .as_be_bytes() + .map_err(unspecified_err)?; + Ok(( + public_key, + HpkePrivateKey::from(raw_private_key.as_ref().to_vec()), + )) +} + +/// Generate a X25519 key pair expressed as a raw big-endian fixed-length +/// integer. +/// +/// We must disambiguate the [`AsBigEndian`] trait in-use and this function uses +/// [`AsBigEndian<Curve25519SeedBin>`], which only supports [`agreement::X25519`]. +/// For generating P-256, P-384 and P-512 keys see [`generate_p_curve_key_pair`]. +fn generate_x25519_key_pair() -> Result<(HpkePublicKey, HpkePrivateKey), Error> { + let (public_key, private_key) = generate_key_pair(&agreement::X25519)?; + let raw_private_key: Curve25519SeedBin<'_> = private_key + .as_be_bytes() + .map_err(unspecified_err)?; + Ok(( + public_key, + HpkePrivateKey::from(raw_private_key.as_ref().to_vec()), + )) +} + +fn generate_key_pair( + alg: &'static agreement::Algorithm, +) -> Result<(HpkePublicKey, agreement::PrivateKey), Error> { + let private_key = agreement::PrivateKey::generate(alg).map_err(unspecified_err)?; + let public_key = HpkePublicKey( + private_key + .compute_public_key() + .map_err(unspecified_err)? + .as_ref() + .to_vec(), + ); + Ok((public_key, private_key)) +} + +/// KeySchedule holds the derived AEAD key, base nonce, and seq number +/// common to both a [Sealer] and [Opener]. +struct KeySchedule<const KEY_SIZE: usize> { + aead: &'static aead::Algorithm, + key: AeadKey<KEY_SIZE>, + base_nonce: [u8; NONCE_LEN], + seq_num: u32, +} + +impl<const KEY_SIZE: usize> KeySchedule<KEY_SIZE> { + /// See [RFC 9180 §5.2 "Encryption and Decryption"][0]. + /// + /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2 + fn compute_nonce(&self) -> [u8; NONCE_LEN] { + // def Context<ROLE>.ComputeNonce(seq): + // seq_bytes = I2OSP(seq, Nn) + // return xor(self.base_nonce, seq_bytes) + + // Each new N-byte nonce is conceptually two parts: + // * N-4 bytes of the base nonce (0s in `nonce` to XOR in as-is). + // * 4 bytes derived from the sequence number XOR the base nonce. + let mut nonce = [0; NONCE_LEN]; + let seq_bytes = self.seq_num.to_be_bytes(); + nonce[NONCE_LEN - seq_bytes.len()..].copy_from_slice(&seq_bytes); + + for (n, &b) in nonce.iter_mut().zip(&self.base_nonce) { + *n ^= b; + } + + nonce + } + + /// See [RFC 9180 §5.2 "Encryption and Decryption"][0]. + /// + /// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-5.2 + fn increment_seq_num(&mut self) -> Result<(), aws_lc_rs::error::Unspecified> { + // def Context<ROLE>.IncrementSeq(): + // if self.seq >= (1 << (8*Nn)) - 1: + // raise MessageLimitReachedError + // self.seq += 1 + + // Determine the maximum sequence number using the AEAD nonce's length in bits. + // Do this as an u128 to prevent overflowing. + let max_seq_num = (1u128 << (NONCE_LEN * 8)) - 1; + + // Promote the u32 sequence number to an u128 and compare against the maximum allowed + // sequence number. + if u128::from(self.seq_num) >= max_seq_num { + return Err(aws_lc_rs::error::Unspecified); + } + + self.seq_num += 1; + Ok(()) + } +} + +impl<const KEY_SIZE: usize> NonceSequence for &mut KeySchedule<KEY_SIZE> { + fn advance(&mut self) -> Result<Nonce, aws_lc_rs::error::Unspecified> { + let nonce = self.compute_nonce(); + self.increment_seq_num()?; + Nonce::try_assume_unique_for_key(&nonce) + } +} + +/// See [RFC 9180 §4 "Cryptographic Dependencies"][0]. +/// +/// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4 +fn labeled_extract_for_expand( + hkdf: &'static dyn HkdfPrkExtract, + suite_id: LabeledSuiteId, + salt: Option<&[u8]>, + label: Label, + ikm: &[u8], +) -> Box<dyn HkdfExpander> { + // def LabeledExtract(salt, label, ikm): + // labeled_ikm = concat("HPKE-v1", suite_id, label, ikm) + // return Extract(salt, labeled_ikm) + + let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat(); + hkdf.extract_from_secret(salt, &labeled_ikm) +} + +/// See [RFC 9180 §4 "Cryptographic Dependencies"][0]. +/// +/// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4 +fn labeled_extract_for_prk( + hkdf: &'static dyn HkdfPrkExtract, + suite_id: LabeledSuiteId, + salt: Option<&[u8]>, + label: Label, + ikm: &[u8], +) -> Vec<u8> { + // def LabeledExtract(salt, label, ikm): + // labeled_ikm = concat("HPKE-v1", suite_id, label, ikm) + // return Extract(salt, labeled_ikm) + + let labeled_ikm = [&b"HPKE-v1"[..], &suite_id.encoded(), label.as_ref(), ikm].concat(); + hkdf.extract_prk_from_secret(salt, &labeled_ikm) +} + +/// See [RFC 9180 §4 "Cryptographic Dependencies"][0]. +/// +/// [0]: https://www.rfc-editor.org/rfc/rfc9180.html#section-4 +fn labeled_expand<const L: usize>( + suite_id: LabeledSuiteId, + expander: Box<dyn HkdfExpander>, + label: Label, + kem_context: &[u8], +) -> [u8; L] { + // def LabeledExpand(prk, label, info, L): + // labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id, + // label, info) + // return Expand(prk, labeled_info, L) + + let output_len = u16::to_be_bytes(L as u16); + let info = &[ + &output_len[..], + b"HPKE-v1", + &suite_id.encoded(), + label.as_ref(), + kem_context, + ]; + + expand(&*expander, info) +} + +/// Label describes the possible labels for use with [labeled_extract_for_expand] and [labeled_expand]. +#[derive(Debug)] +enum Label { + PskIdHash, + InfoHash, + Secret, + Key, + BaseNonce, + EaePrk, + SharedSecret, +} + +impl AsRef<[u8]> for Label { + fn as_ref(&self) -> &[u8] { + match self { + Self::PskIdHash => b"psk_id_hash", + Self::InfoHash => b"info_hash", + Self::Secret => b"secret", + Self::Key => b"key", + Self::BaseNonce => b"base_nonce", + Self::EaePrk => b"eae_prk", + Self::SharedSecret => b"shared_secret", + } + } +} + +/// LabeledSuiteId describes the possible suite ID values for use with [labeled_extract_for_expand] and +/// [labeled_expand]. +#[derive(Debug, Copy, Clone)] +enum LabeledSuiteId { + Hpke(HpkeSuite), + Kem(HpkeKem), +} + +impl LabeledSuiteId { + /// The suite ID encoding depends on the context of use. In the general HPKE context, + /// we use a "HPKE" prefix and encode the entire ciphersuite. In the KEM context we use a + /// "KEM" prefix and only encode the KEM ID. + /// + /// See the bottom of [RFC 9180 §4](https://www.rfc-editor.org/rfc/rfc9180.html#section-4) + /// for more information. + fn encoded(&self) -> Vec<u8> { + match self { + Self::Hpke(suite) => [ + &b"HPKE"[..], + &u16::from(suite.kem).to_be_bytes(), + &u16::from(suite.sym.kdf_id).to_be_bytes(), + &u16::from(suite.sym.aead_id).to_be_bytes(), + ] + .concat(), + Self::Kem(kem) => [&b"KEM"[..], &u16::from(*kem).to_be_bytes()].concat(), + } + } +} + +/// A newtype wrapper for an unbound AEAD key. +struct AeadKey<const KEY_LEN: usize>([u8; KEY_LEN]); + +impl<const KEY_LEN: usize> Drop for AeadKey<KEY_LEN> { + fn drop(&mut self) { + self.0.zeroize() + } +} + +/// A newtype wrapper for a DH KEM shared secret. +struct KemSharedSecret<const KDF_LEN: usize>([u8; KDF_LEN]); + +impl<const KDF_LEN: usize> Drop for KemSharedSecret<KDF_LEN> { + fn drop(&mut self) { + self.0.zeroize(); + } +} + +fn key_rejected_err(_e: aws_lc_rs::error::KeyRejected) -> Error { + #[cfg(feature = "std")] + { + Error::Other(OtherError(Arc::new(_e))) + } + #[cfg(not(feature = "std"))] + { + Error::Other(OtherError()) + } +} + +// The `cipher::chacha::KEY_LEN` const is not exported, so we copy it here: +// https://github.com/aws/aws-lc-rs/blob/0186ef7bb1a4d7e140bae8074a9871f49afedf1b/aws-lc-rs/src/cipher/chacha.rs#L13 +const CHACHA_KEY_LEN: usize = 32; + +static RING_HKDF_HMAC_SHA256: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA256); +static RING_HKDF_HMAC_SHA384: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA384); +static RING_HKDF_HMAC_SHA512: &HkdfUsingHmac<'static> = &HkdfUsingHmac(&HMAC_SHA512); + +#[cfg(test)] +mod tests { + use alloc::{format, vec}; + + use super::*; + + #[test] + fn smoke_test() { + for suite in ALL_SUPPORTED_SUITES { + _ = format!("{suite:?}"); // HpkeAwsLcRs suites should be Debug. + + // We should be able to generate a random keypair. + let (pk, sk) = suite.generate_key_pair().unwrap(); + + // Info value corresponds to the first RFC 9180 base mode test vector. + let info = &[ + 0x4f, 0x64, 0x65, 0x20, 0x6f, 0x6e, 0x20, 0x61, 0x20, 0x47, 0x72, 0x65, 0x63, 0x69, + 0x61, 0x6e, 0x20, 0x55, 0x72, 0x6e, + ][..]; + + // We should be able to set up a sealer. + let (enc, mut sealer) = suite.setup_sealer(info, &pk).unwrap(); + + _ = format!("{sealer:?}"); // Sealer should be Debug. + + // Setting up a sealer with an invalid public key should fail. + let bad_setup_res = suite.setup_sealer(info, &HpkePublicKey(vec![])); + assert!(matches!(bad_setup_res.unwrap_err(), Error::Other(_))); + + // We should be able to seal some plaintext. + let aad = &[0xC0, 0xFF, 0xEE]; + let pt = &[0xF0, 0x0D]; + let ct = sealer.seal(aad, pt).unwrap(); + + // We should be able to set up an opener. + let mut opener = suite + .setup_opener(&enc, info, &sk) + .unwrap(); + _ = format!("{opener:?}"); // Opener should be Debug. + + // Setting up an opener with an invalid private key should fail. + let bad_key_res = suite.setup_opener(&enc, info, &HpkePrivateKey::from(vec![])); + assert!(matches!(bad_key_res.unwrap_err(), Error::Other(_))); + + // Opening the plaintext should work with the correct opener and aad. + let pt_prime = opener.open(aad, &ct).unwrap(); + assert_eq!(pt_prime, pt); + + // Opening the plaintext with the correct opener and wrong aad should fail. + let open_res = opener.open(&[0x0], &ct); + assert!(matches!(open_res.unwrap_err(), Error::Other(_))); + + // Opening the plaintext with the wrong opener should fail. + let mut sk_rm_prime = sk.secret_bytes().to_vec(); + sk_rm_prime[10] ^= 0xFF; // Corrupt a byte of the private key. + let mut opener_two = suite + .setup_opener(&enc, info, &HpkePrivateKey::from(sk_rm_prime)) + .unwrap(); + let open_res = opener_two.open(aad, &ct); + assert!(matches!(open_res.unwrap_err(), Error::Other(_))); + } + } + + #[cfg(not(feature = "fips"))] // Ensure all supported suites are available to test. + #[test] + fn test_fips() { + let testcases: &[(&dyn Hpke, bool)] = &[ + // FIPS compatible. + (DH_KEM_P256_HKDF_SHA256_AES_128, true), + (DH_KEM_P256_HKDF_SHA256_AES_256, true), + (DH_KEM_P384_HKDF_SHA384_AES_128, true), + (DH_KEM_P384_HKDF_SHA384_AES_256, true), + (DH_KEM_P521_HKDF_SHA512_AES_128, true), + (DH_KEM_P521_HKDF_SHA512_AES_256, true), + // AEAD is not FIPS compatible. + (DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305, false), + (DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305, false), + (DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305, false), + // KEM is not FIPS compatible. + (DH_KEM_X25519_HKDF_SHA256_AES_128, false), + (DH_KEM_X25519_HKDF_SHA256_AES_256, false), + (DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305, false), + ]; + for (suite, expected) in testcases { + assert_eq!(suite.fips(), *expected); + } + } +} + +#[cfg(test)] +mod rfc_tests { + use alloc::string::String; + use std::fs::File; + use std::println; + + use serde::Deserialize; + + use super::*; + + /// Confirm open/seal operations work using the test vectors from [RFC 9180 Appendix A]. + /// + /// [RFC 9180 Appendix A]: https://www.rfc-editor.org/rfc/rfc9180#TestVectors + #[test] + fn check_test_vectors() { + for (idx, vec) in test_vectors().into_iter().enumerate() { + let Some(hpke) = vec.applicable() else { + println!("skipping inapplicable vector {idx}"); + continue; + }; + + println!("testing vector {idx}"); + let pk_r = HpkePublicKey(hex::decode(vec.pk_rm).unwrap()); + let sk_r = HpkePrivateKey::from(hex::decode(vec.sk_rm).unwrap()); + let sk_em = hex::decode(vec.sk_em).unwrap(); + let info = hex::decode(vec.info).unwrap(); + let expected_enc = hex::decode(vec.enc).unwrap(); + + let (enc, mut sealer) = hpke + .setup_test_sealer(&info, &pk_r, &sk_em) + .unwrap(); + assert_eq!(enc.0, expected_enc); + + let mut opener = hpke + .setup_opener(&enc, &info, &sk_r) + .unwrap(); + + for test_encryption in vec.encryptions { + let aad = hex::decode(test_encryption.aad).unwrap(); + let pt = hex::decode(test_encryption.pt).unwrap(); + let expected_ct = hex::decode(test_encryption.ct).unwrap(); + + let ciphertext = sealer.seal(&aad, &pt).unwrap(); + assert_eq!(ciphertext, expected_ct); + + let plaintext = opener.open(&aad, &ciphertext).unwrap(); + assert_eq!(plaintext, pt); + } + } + } + + trait TestHpke: Hpke { + fn setup_test_sealer( + &self, + info: &[u8], + pub_key: &HpkePublicKey, + sk_em: &[u8], + ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error>; + } + + impl<const KEY_SIZE: usize, const KDF_SIZE: usize> TestHpke for HpkeAwsLcRs<KEY_SIZE, KDF_SIZE> { + fn setup_test_sealer( + &self, + info: &[u8], + pub_key: &HpkePublicKey, + sk_em: &[u8], + ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error> { + let (encap, sealer) = Sealer::test_only_new(self, info, pub_key, sk_em)?; + Ok((encap, Box::new(sealer))) + } + } + + static TEST_SUITES: &[&dyn TestHpke] = &[ + DH_KEM_P256_HKDF_SHA256_AES_128, + DH_KEM_P256_HKDF_SHA256_AES_256, + #[cfg(not(feature = "fips"))] + DH_KEM_P256_HKDF_SHA256_CHACHA20_POLY1305, + DH_KEM_P384_HKDF_SHA384_AES_128, + DH_KEM_P384_HKDF_SHA384_AES_256, + #[cfg(not(feature = "fips"))] + DH_KEM_P384_HKDF_SHA384_CHACHA20_POLY1305, + DH_KEM_P521_HKDF_SHA512_AES_128, + DH_KEM_P521_HKDF_SHA512_AES_256, + #[cfg(not(feature = "fips"))] + DH_KEM_P521_HKDF_SHA512_CHACHA20_POLY1305, + #[cfg(not(feature = "fips"))] + DH_KEM_X25519_HKDF_SHA256_AES_128, + #[cfg(not(feature = "fips"))] + DH_KEM_X25519_HKDF_SHA256_AES_256, + #[cfg(not(feature = "fips"))] + DH_KEM_X25519_HKDF_SHA256_CHACHA20_POLY1305, + ]; + + #[derive(Deserialize, Debug)] + struct TestVector { + mode: u8, + kem_id: u16, + kdf_id: u16, + aead_id: u16, + info: String, + #[serde(rename(deserialize = "pkRm"))] + pk_rm: String, + #[serde(rename(deserialize = "skRm"))] + sk_rm: String, + #[serde(rename(deserialize = "skEm"))] + sk_em: String, + enc: String, + encryptions: Vec<TestEncryption>, + } + + #[derive(Deserialize, Debug)] + struct TestEncryption { + aad: String, + pt: String, + ct: String, + } + + impl TestVector { + fn suite(&self) -> HpkeSuite { + HpkeSuite { + kem: HpkeKem::from(self.kem_id), + sym: HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::from(self.kdf_id), + aead_id: HpkeAead::from(self.aead_id), + }, + } + } + + fn applicable(&self) -> Option<&'static dyn TestHpke> { + // Only base mode test vectors for supported suites are applicable. + if self.mode != 0 { + return None; + } + + Self::lookup_suite(self.suite(), TEST_SUITES) + } + + fn lookup_suite( + suite: HpkeSuite, + supported: &[&'static dyn TestHpke], + ) -> Option<&'static dyn TestHpke> { + supported + .iter() + .find(|s| s.suite() == suite) + .copied() + } + } + + fn test_vectors() -> Vec<TestVector> { + serde_json::from_reader( + &mut File::open("../rustls-provider-test/tests/rfc-9180-test-vectors.json") + .expect("failed to open test vectors data file"), + ) + .expect("failed to deserialize test vectors") + } +} diff --git a/vendor/rustls/src/crypto/aws_lc_rs/mod.rs b/vendor/rustls/src/crypto/aws_lc_rs/mod.rs new file mode 100644 index 00000000..1a7d2263 --- /dev/null +++ b/vendor/rustls/src/crypto/aws_lc_rs/mod.rs @@ -0,0 +1,318 @@ +use alloc::vec::Vec; + +// aws-lc-rs has a -- roughly -- ring-compatible API, so we just reuse all that +// glue here. The shared files should always use `super::ring_like` to access a +// ring-compatible crate, and `super::ring_shim` to bridge the gaps where they are +// small. +pub(crate) use aws_lc_rs as ring_like; +use pki_types::PrivateKeyDer; +use webpki::aws_lc_rs as webpki_algs; + +use crate::crypto::{CryptoProvider, KeyProvider, SecureRandom, SupportedKxGroup}; +use crate::enums::SignatureScheme; +use crate::rand::GetRandomFailed; +use crate::sign::SigningKey; +use crate::suites::SupportedCipherSuite; +use crate::sync::Arc; +use crate::webpki::WebPkiSupportedAlgorithms; +use crate::{Error, OtherError}; + +/// Hybrid public key encryption (HPKE). +pub mod hpke; +/// Post-quantum secure algorithms. +pub(crate) mod pq; +/// Using software keys for authentication. +pub mod sign; + +#[path = "../ring/hash.rs"] +pub(crate) mod hash; +#[path = "../ring/hmac.rs"] +pub(crate) mod hmac; +#[path = "../ring/kx.rs"] +pub(crate) mod kx; +#[path = "../ring/quic.rs"] +pub(crate) mod quic; +#[cfg(feature = "std")] +pub(crate) mod ticketer; +#[cfg(feature = "tls12")] +pub(crate) mod tls12; +pub(crate) mod tls13; + +/// A `CryptoProvider` backed by aws-lc-rs. +pub fn default_provider() -> CryptoProvider { + CryptoProvider { + cipher_suites: DEFAULT_CIPHER_SUITES.to_vec(), + kx_groups: default_kx_groups(), + signature_verification_algorithms: SUPPORTED_SIG_ALGS, + secure_random: &AwsLcRs, + key_provider: &AwsLcRs, + } +} + +fn default_kx_groups() -> Vec<&'static dyn SupportedKxGroup> { + #[cfg(feature = "fips")] + { + DEFAULT_KX_GROUPS + .iter() + .filter(|cs| cs.fips()) + .copied() + .collect() + } + #[cfg(not(feature = "fips"))] + { + DEFAULT_KX_GROUPS.to_vec() + } +} + +#[derive(Debug)] +struct AwsLcRs; + +impl SecureRandom for AwsLcRs { + fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed> { + use ring_like::rand::SecureRandom; + + ring_like::rand::SystemRandom::new() + .fill(buf) + .map_err(|_| GetRandomFailed) + } + + fn fips(&self) -> bool { + fips() + } +} + +impl KeyProvider for AwsLcRs { + fn load_private_key( + &self, + key_der: PrivateKeyDer<'static>, + ) -> Result<Arc<dyn SigningKey>, Error> { + sign::any_supported_type(&key_der) + } + + fn fips(&self) -> bool { + fips() + } +} + +/// The cipher suite configuration that an application should use by default. +/// +/// This will be [`ALL_CIPHER_SUITES`] sans any supported cipher suites that +/// shouldn't be enabled by most applications. +pub static DEFAULT_CIPHER_SUITES: &[SupportedCipherSuite] = &[ + // TLS1.3 suites + tls13::TLS13_AES_256_GCM_SHA384, + tls13::TLS13_AES_128_GCM_SHA256, + #[cfg(not(feature = "fips"))] + tls13::TLS13_CHACHA20_POLY1305_SHA256, + // TLS1.2 suites + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + #[cfg(all(feature = "tls12", not(feature = "fips")))] + tls12::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + #[cfg(all(feature = "tls12", not(feature = "fips")))] + tls12::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, +]; + +/// A list of all the cipher suites supported by the rustls aws-lc-rs provider. +pub static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = &[ + // TLS1.3 suites + tls13::TLS13_AES_256_GCM_SHA384, + tls13::TLS13_AES_128_GCM_SHA256, + tls13::TLS13_CHACHA20_POLY1305_SHA256, + // TLS1.2 suites + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, +]; + +/// All defined cipher suites supported by aws-lc-rs appear in this module. +pub mod cipher_suite { + #[cfg(feature = "tls12")] + pub use super::tls12::{ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + }; + pub use super::tls13::{ + TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, + }; +} + +/// A `WebPkiSupportedAlgorithms` value that reflects webpki's capabilities when +/// compiled against aws-lc-rs. +static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { + all: &[ + webpki_algs::ECDSA_P256_SHA256, + webpki_algs::ECDSA_P256_SHA384, + webpki_algs::ECDSA_P384_SHA256, + webpki_algs::ECDSA_P384_SHA384, + webpki_algs::ECDSA_P521_SHA256, + webpki_algs::ECDSA_P521_SHA384, + webpki_algs::ECDSA_P521_SHA512, + webpki_algs::ED25519, + webpki_algs::RSA_PSS_2048_8192_SHA256_LEGACY_KEY, + webpki_algs::RSA_PSS_2048_8192_SHA384_LEGACY_KEY, + webpki_algs::RSA_PSS_2048_8192_SHA512_LEGACY_KEY, + webpki_algs::RSA_PKCS1_2048_8192_SHA256, + webpki_algs::RSA_PKCS1_2048_8192_SHA384, + webpki_algs::RSA_PKCS1_2048_8192_SHA512, + webpki_algs::RSA_PKCS1_2048_8192_SHA256_ABSENT_PARAMS, + webpki_algs::RSA_PKCS1_2048_8192_SHA384_ABSENT_PARAMS, + webpki_algs::RSA_PKCS1_2048_8192_SHA512_ABSENT_PARAMS, + ], + mapping: &[ + // Note: for TLS1.2 the curve is not fixed by SignatureScheme. For TLS1.3 it is. + ( + SignatureScheme::ECDSA_NISTP384_SHA384, + &[ + webpki_algs::ECDSA_P384_SHA384, + webpki_algs::ECDSA_P256_SHA384, + webpki_algs::ECDSA_P521_SHA384, + ], + ), + ( + SignatureScheme::ECDSA_NISTP256_SHA256, + &[ + webpki_algs::ECDSA_P256_SHA256, + webpki_algs::ECDSA_P384_SHA256, + webpki_algs::ECDSA_P521_SHA256, + ], + ), + ( + SignatureScheme::ECDSA_NISTP521_SHA512, + &[webpki_algs::ECDSA_P521_SHA512], + ), + (SignatureScheme::ED25519, &[webpki_algs::ED25519]), + ( + SignatureScheme::RSA_PSS_SHA512, + &[webpki_algs::RSA_PSS_2048_8192_SHA512_LEGACY_KEY], + ), + ( + SignatureScheme::RSA_PSS_SHA384, + &[webpki_algs::RSA_PSS_2048_8192_SHA384_LEGACY_KEY], + ), + ( + SignatureScheme::RSA_PSS_SHA256, + &[webpki_algs::RSA_PSS_2048_8192_SHA256_LEGACY_KEY], + ), + ( + SignatureScheme::RSA_PKCS1_SHA512, + &[webpki_algs::RSA_PKCS1_2048_8192_SHA512], + ), + ( + SignatureScheme::RSA_PKCS1_SHA384, + &[webpki_algs::RSA_PKCS1_2048_8192_SHA384], + ), + ( + SignatureScheme::RSA_PKCS1_SHA256, + &[webpki_algs::RSA_PKCS1_2048_8192_SHA256], + ), + ], +}; + +/// All defined key exchange groups supported by aws-lc-rs appear in this module. +/// +/// [`ALL_KX_GROUPS`] is provided as an array of all of these values. +/// [`DEFAULT_KX_GROUPS`] is provided as an array of this provider's defaults. +pub mod kx_group { + pub use super::kx::{SECP256R1, SECP384R1, X25519}; + pub use super::pq::{MLKEM768, SECP256R1MLKEM768, X25519MLKEM768}; +} + +/// A list of the default key exchange groups supported by this provider. +/// +/// This does not contain MLKEM768; by default MLKEM768 is only offered +/// in hybrid with X25519. +pub static DEFAULT_KX_GROUPS: &[&dyn SupportedKxGroup] = &[ + #[cfg(feature = "prefer-post-quantum")] + kx_group::X25519MLKEM768, + kx_group::X25519, + kx_group::SECP256R1, + kx_group::SECP384R1, + #[cfg(not(feature = "prefer-post-quantum"))] + kx_group::X25519MLKEM768, +]; + +/// A list of all the key exchange groups supported by this provider. +pub static ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[ + #[cfg(feature = "prefer-post-quantum")] + kx_group::X25519MLKEM768, + #[cfg(feature = "prefer-post-quantum")] + kx_group::SECP256R1MLKEM768, + kx_group::X25519, + kx_group::SECP256R1, + kx_group::SECP384R1, + #[cfg(not(feature = "prefer-post-quantum"))] + kx_group::X25519MLKEM768, + #[cfg(not(feature = "prefer-post-quantum"))] + kx_group::SECP256R1MLKEM768, + kx_group::MLKEM768, +]; + +#[cfg(feature = "std")] +pub use ticketer::Ticketer; + +/// Compatibility shims between ring 0.16.x and 0.17.x API +mod ring_shim { + use super::ring_like; + use crate::crypto::SharedSecret; + + pub(super) fn agree_ephemeral( + priv_key: ring_like::agreement::EphemeralPrivateKey, + peer_key: &ring_like::agreement::UnparsedPublicKey<&[u8]>, + ) -> Result<SharedSecret, ()> { + ring_like::agreement::agree_ephemeral(priv_key, peer_key, (), |secret| { + Ok(SharedSecret::from(secret)) + }) + } +} + +/// Are we in FIPS mode? +pub(super) fn fips() -> bool { + aws_lc_rs::try_fips_mode().is_ok() +} + +pub(super) fn unspecified_err(_e: aws_lc_rs::error::Unspecified) -> Error { + #[cfg(feature = "std")] + { + Error::Other(OtherError(Arc::new(_e))) + } + #[cfg(not(feature = "std"))] + { + Error::Other(OtherError()) + } +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "fips")] + #[test] + fn default_suites_are_fips() { + assert!( + super::DEFAULT_CIPHER_SUITES + .iter() + .all(|scs| scs.fips()) + ); + } + + #[cfg(not(feature = "fips"))] + #[test] + fn default_suites() { + assert_eq!(super::DEFAULT_CIPHER_SUITES, super::ALL_CIPHER_SUITES); + } +} diff --git a/vendor/rustls/src/crypto/aws_lc_rs/pq/hybrid.rs b/vendor/rustls/src/crypto/aws_lc_rs/pq/hybrid.rs new file mode 100644 index 00000000..8e30d782 --- /dev/null +++ b/vendor/rustls/src/crypto/aws_lc_rs/pq/hybrid.rs @@ -0,0 +1,205 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; + +use super::INVALID_KEY_SHARE; +use crate::crypto::{ActiveKeyExchange, CompletedKeyExchange, SharedSecret, SupportedKxGroup}; +use crate::ffdhe_groups::FfdheGroup; +use crate::{Error, NamedGroup, ProtocolVersion}; + +/// A generalization of hybrid key exchange. +#[derive(Debug)] +pub(crate) struct Hybrid { + pub(crate) classical: &'static dyn SupportedKxGroup, + pub(crate) post_quantum: &'static dyn SupportedKxGroup, + pub(crate) name: NamedGroup, + pub(crate) layout: Layout, +} + +impl SupportedKxGroup for Hybrid { + fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> { + let classical = self.classical.start()?; + let post_quantum = self.post_quantum.start()?; + + let combined_pub_key = self + .layout + .concat(post_quantum.pub_key(), classical.pub_key()); + + Ok(Box::new(ActiveHybrid { + classical, + post_quantum, + name: self.name, + layout: self.layout, + combined_pub_key, + })) + } + + fn start_and_complete(&self, client_share: &[u8]) -> Result<CompletedKeyExchange, Error> { + let (post_quantum_share, classical_share) = self + .layout + .split_received_client_share(client_share) + .ok_or(INVALID_KEY_SHARE)?; + + let cl = self + .classical + .start_and_complete(classical_share)?; + let pq = self + .post_quantum + .start_and_complete(post_quantum_share)?; + + let combined_pub_key = self + .layout + .concat(&pq.pub_key, &cl.pub_key); + let secret = self + .layout + .concat(pq.secret.secret_bytes(), cl.secret.secret_bytes()); + + Ok(CompletedKeyExchange { + group: self.name, + pub_key: combined_pub_key, + secret: SharedSecret::from(secret), + }) + } + + fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> { + None + } + + fn name(&self) -> NamedGroup { + self.name + } + + fn fips(&self) -> bool { + // Behold! The Night Mare: SP800-56C rev 2: + // + // "In addition to the currently approved techniques for the generation of the + // shared secret Z as specified in SP 800-56A and SP 800-56B, this Recommendation + // permits the use of a "hybrid" shared secret of the form Z′ = Z || T, a + // concatenation consisting of a "standard" shared secret Z that was generated + // during the execution of a key-establishment scheme (as currently specified in + // [SP 800-56A] or [SP 800-56B])" + // + // NIST plan to adjust this and allow both orders: see + // <https://csrc.nist.gov/pubs/sp/800/227/ipd> (Jan 2025) lines 1070-1080. + // + // But, for now, we follow the SP800-56C logic: the element appearing first is the + // one that controls approval. + match self.layout.post_quantum_first { + true => self.post_quantum.fips(), + false => self.classical.fips(), + } + } + + fn usable_for_version(&self, version: ProtocolVersion) -> bool { + version == ProtocolVersion::TLSv1_3 + } +} + +struct ActiveHybrid { + classical: Box<dyn ActiveKeyExchange>, + post_quantum: Box<dyn ActiveKeyExchange>, + name: NamedGroup, + layout: Layout, + combined_pub_key: Vec<u8>, +} + +impl ActiveKeyExchange for ActiveHybrid { + fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> { + let (post_quantum_share, classical_share) = self + .layout + .split_received_server_share(peer_pub_key) + .ok_or(INVALID_KEY_SHARE)?; + + let cl = self + .classical + .complete(classical_share)?; + let pq = self + .post_quantum + .complete(post_quantum_share)?; + + let secret = self + .layout + .concat(pq.secret_bytes(), cl.secret_bytes()); + Ok(SharedSecret::from(secret)) + } + + /// Allow the classical computation to be offered and selected separately. + fn hybrid_component(&self) -> Option<(NamedGroup, &[u8])> { + Some((self.classical.group(), self.classical.pub_key())) + } + + fn complete_hybrid_component( + self: Box<Self>, + peer_pub_key: &[u8], + ) -> Result<SharedSecret, Error> { + self.classical.complete(peer_pub_key) + } + + fn pub_key(&self) -> &[u8] { + &self.combined_pub_key + } + + fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> { + None + } + + fn group(&self) -> NamedGroup { + self.name + } +} + +#[derive(Clone, Copy, Debug)] +pub(crate) struct Layout { + /// Length of classical key share. + pub(crate) classical_share_len: usize, + + /// Length of post-quantum key share sent by client + pub(crate) post_quantum_client_share_len: usize, + + /// Length of post-quantum key share sent by server + pub(crate) post_quantum_server_share_len: usize, + + /// Whether the post-quantum element comes first in shares and secrets. + /// + /// For dismal and unprincipled reasons, SECP256R1MLKEM768 has the + /// classical element first, while X25519MLKEM768 has it second. + pub(crate) post_quantum_first: bool, +} + +impl Layout { + fn split_received_client_share<'a>(&self, share: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> { + self.split(share, self.post_quantum_client_share_len) + } + + fn split_received_server_share<'a>(&self, share: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> { + self.split(share, self.post_quantum_server_share_len) + } + + /// Return the PQ and classical component of a key share. + fn split<'a>( + &self, + share: &'a [u8], + post_quantum_share_len: usize, + ) -> Option<(&'a [u8], &'a [u8])> { + if share.len() != self.classical_share_len + post_quantum_share_len { + return None; + } + + Some(match self.post_quantum_first { + true => { + let (first_share, second_share) = share.split_at(post_quantum_share_len); + (first_share, second_share) + } + false => { + let (first_share, second_share) = share.split_at(self.classical_share_len); + (second_share, first_share) + } + }) + } + + fn concat(&self, post_quantum: &[u8], classical: &[u8]) -> Vec<u8> { + match self.post_quantum_first { + true => [post_quantum, classical].concat(), + false => [classical, post_quantum].concat(), + } + } +} diff --git a/vendor/rustls/src/crypto/aws_lc_rs/pq/mlkem.rs b/vendor/rustls/src/crypto/aws_lc_rs/pq/mlkem.rs new file mode 100644 index 00000000..fa2be078 --- /dev/null +++ b/vendor/rustls/src/crypto/aws_lc_rs/pq/mlkem.rs @@ -0,0 +1,103 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; + +use aws_lc_rs::kem; + +use super::INVALID_KEY_SHARE; +use crate::crypto::{ActiveKeyExchange, CompletedKeyExchange, SharedSecret, SupportedKxGroup}; +use crate::ffdhe_groups::FfdheGroup; +use crate::{Error, NamedGroup, ProtocolVersion}; + +#[derive(Debug)] +pub(crate) struct MlKem768; + +impl SupportedKxGroup for MlKem768 { + fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> { + let decaps_key = kem::DecapsulationKey::generate(&kem::ML_KEM_768) + .map_err(|_| Error::General("key generation failed".into()))?; + + let pub_key_bytes = decaps_key + .encapsulation_key() + .and_then(|encaps_key| encaps_key.key_bytes()) + .map_err(|_| Error::General("encaps failed".into()))?; + + Ok(Box::new(Active { + decaps_key: Box::new(decaps_key), + encaps_key_bytes: Vec::from(pub_key_bytes.as_ref()), + })) + } + + fn start_and_complete(&self, client_share: &[u8]) -> Result<CompletedKeyExchange, Error> { + let encaps_key = kem::EncapsulationKey::new(&kem::ML_KEM_768, client_share) + .map_err(|_| INVALID_KEY_SHARE)?; + + let (ciphertext, shared_secret) = encaps_key + .encapsulate() + .map_err(|_| INVALID_KEY_SHARE)?; + + Ok(CompletedKeyExchange { + group: self.name(), + pub_key: Vec::from(ciphertext.as_ref()), + secret: SharedSecret::from(shared_secret.as_ref()), + }) + } + + fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> { + None + } + + fn name(&self) -> NamedGroup { + NamedGroup::MLKEM768 + } + + fn fips(&self) -> bool { + // AUDITORS: + // At the time of writing, the ML-KEM implementation in AWS-LC-FIPS module 3.0 + // is FIPS-pending. Some regulatory regimes (eg, FedRAMP rev 5 SC-13) allow + // use of implementations in this state, as if they are already approved. + // + // We follow this liberal interpretation, and say MlKem768 is FIPS-compliant + // if the underlying library is in FIPS mode. + // + // TODO: adjust the `fips()` function return type to allow more policies to + // be expressed, perhaps following something like + // <https://github.com/golang/go/issues/70200#issuecomment-2490017956> -- + // see <https://github.com/rustls/rustls/issues/2309> + super::super::fips() + } + + fn usable_for_version(&self, version: ProtocolVersion) -> bool { + version == ProtocolVersion::TLSv1_3 + } +} + +struct Active { + decaps_key: Box<kem::DecapsulationKey<kem::AlgorithmId>>, + encaps_key_bytes: Vec<u8>, +} + +impl ActiveKeyExchange for Active { + // The received 'peer_pub_key' is actually the ML-KEM ciphertext, + // which when decapsulated with our `decaps_key` produces the shared + // secret. + fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> { + let shared_secret = self + .decaps_key + .decapsulate(peer_pub_key.into()) + .map_err(|_| INVALID_KEY_SHARE)?; + + Ok(SharedSecret::from(shared_secret.as_ref())) + } + + fn pub_key(&self) -> &[u8] { + &self.encaps_key_bytes + } + + fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> { + None + } + + fn group(&self) -> NamedGroup { + NamedGroup::MLKEM768 + } +} diff --git a/vendor/rustls/src/crypto/aws_lc_rs/pq/mod.rs b/vendor/rustls/src/crypto/aws_lc_rs/pq/mod.rs new file mode 100644 index 00000000..a2252da5 --- /dev/null +++ b/vendor/rustls/src/crypto/aws_lc_rs/pq/mod.rs @@ -0,0 +1,48 @@ +use crate::crypto::SupportedKxGroup; +use crate::crypto::aws_lc_rs::kx_group; +use crate::{Error, NamedGroup, PeerMisbehaved}; + +mod hybrid; +mod mlkem; + +/// This is the [X25519MLKEM768] key exchange. +/// +/// [X25519MLKEM768]: <https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/> +pub static X25519MLKEM768: &dyn SupportedKxGroup = &hybrid::Hybrid { + classical: kx_group::X25519, + post_quantum: MLKEM768, + name: NamedGroup::X25519MLKEM768, + layout: hybrid::Layout { + classical_share_len: X25519_LEN, + post_quantum_client_share_len: MLKEM768_ENCAP_LEN, + post_quantum_server_share_len: MLKEM768_CIPHERTEXT_LEN, + post_quantum_first: true, + }, +}; + +/// This is the [SECP256R1MLKEM768] key exchange. +/// +/// [SECP256R1MLKEM768]: <https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/> +pub static SECP256R1MLKEM768: &dyn SupportedKxGroup = &hybrid::Hybrid { + classical: kx_group::SECP256R1, + post_quantum: MLKEM768, + name: NamedGroup::secp256r1MLKEM768, + layout: hybrid::Layout { + classical_share_len: SECP256R1_LEN, + post_quantum_client_share_len: MLKEM768_ENCAP_LEN, + post_quantum_server_share_len: MLKEM768_CIPHERTEXT_LEN, + post_quantum_first: false, + }, +}; + +/// This is the [MLKEM] key exchange. +/// +/// [MLKEM]: https://datatracker.ietf.org/doc/draft-connolly-tls-mlkem-key-agreement +pub static MLKEM768: &dyn SupportedKxGroup = &mlkem::MlKem768; + +const INVALID_KEY_SHARE: Error = Error::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare); + +const X25519_LEN: usize = 32; +const SECP256R1_LEN: usize = 65; +const MLKEM768_CIPHERTEXT_LEN: usize = 1088; +const MLKEM768_ENCAP_LEN: usize = 1184; diff --git a/vendor/rustls/src/crypto/aws_lc_rs/sign.rs b/vendor/rustls/src/crypto/aws_lc_rs/sign.rs new file mode 100644 index 00000000..0c874abf --- /dev/null +++ b/vendor/rustls/src/crypto/aws_lc_rs/sign.rs @@ -0,0 +1,876 @@ +#![allow(clippy::duplicate_mod)] + +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::vec::Vec; +use alloc::{format, vec}; +use core::fmt::{self, Debug, Formatter}; + +use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer, SubjectPublicKeyInfoDer, alg_id}; + +use super::ring_like::rand::SystemRandom; +use super::ring_like::signature::{self, EcdsaKeyPair, Ed25519KeyPair, KeyPair, RsaKeyPair}; +use crate::crypto::signer::{Signer, SigningKey, public_key_to_spki}; +use crate::enums::{SignatureAlgorithm, SignatureScheme}; +use crate::error::Error; +use crate::sync::Arc; + +/// Parse `der` as any supported key encoding/type, returning +/// the first which works. +pub fn any_supported_type(der: &PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>, Error> { + if let Ok(rsa) = RsaSigningKey::new(der) { + return Ok(Arc::new(rsa)); + } + + if let Ok(ecdsa) = any_ecdsa_type(der) { + return Ok(ecdsa); + } + + if let PrivateKeyDer::Pkcs8(pkcs8) = der { + if let Ok(eddsa) = any_eddsa_type(pkcs8) { + return Ok(eddsa); + } + } + + Err(Error::General( + "failed to parse private key as RSA, ECDSA, or EdDSA".into(), + )) +} + +/// Parse `der` as any ECDSA key type, returning the first which works. +/// +/// Both SEC1 (PEM section starting with 'BEGIN EC PRIVATE KEY') and PKCS8 +/// (PEM section starting with 'BEGIN PRIVATE KEY') encodings are supported. +pub fn any_ecdsa_type(der: &PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>, Error> { + if let Ok(ecdsa_p256) = EcdsaSigningKey::new( + der, + SignatureScheme::ECDSA_NISTP256_SHA256, + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + ) { + return Ok(Arc::new(ecdsa_p256)); + } + + if let Ok(ecdsa_p384) = EcdsaSigningKey::new( + der, + SignatureScheme::ECDSA_NISTP384_SHA384, + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + ) { + return Ok(Arc::new(ecdsa_p384)); + } + + if let Ok(ecdsa_p521) = EcdsaSigningKey::new( + der, + SignatureScheme::ECDSA_NISTP521_SHA512, + &signature::ECDSA_P521_SHA512_ASN1_SIGNING, + ) { + return Ok(Arc::new(ecdsa_p521)); + } + + Err(Error::General( + "failed to parse ECDSA private key as PKCS#8 or SEC1".into(), + )) +} + +/// Parse `der` as any EdDSA key type, returning the first which works. +/// +/// Note that, at the time of writing, Ed25519 does not have wide support +/// in browsers. It is also not supported by the WebPKI, because the +/// CA/Browser Forum Baseline Requirements do not support it for publicly +/// trusted certificates. +pub fn any_eddsa_type(der: &PrivatePkcs8KeyDer<'_>) -> Result<Arc<dyn SigningKey>, Error> { + // TODO: Add support for Ed448 + Ok(Arc::new(Ed25519SigningKey::new( + der, + SignatureScheme::ED25519, + )?)) +} + +/// A `SigningKey` for RSA-PKCS1 or RSA-PSS. +/// +/// This is used by the test suite, so it must be `pub`, but it isn't part of +/// the public, stable, API. +#[doc(hidden)] +pub struct RsaSigningKey { + key: Arc<RsaKeyPair>, +} + +static ALL_RSA_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA256, +]; + +impl RsaSigningKey { + /// Make a new `RsaSigningKey` from a DER encoding, in either + /// PKCS#1 or PKCS#8 format. + pub fn new(der: &PrivateKeyDer<'_>) -> Result<Self, Error> { + let key_pair = match der { + PrivateKeyDer::Pkcs1(pkcs1) => RsaKeyPair::from_der(pkcs1.secret_pkcs1_der()), + PrivateKeyDer::Pkcs8(pkcs8) => RsaKeyPair::from_pkcs8(pkcs8.secret_pkcs8_der()), + _ => { + return Err(Error::General( + "failed to parse RSA private key as either PKCS#1 or PKCS#8".into(), + )); + } + } + .map_err(|key_rejected| { + Error::General(format!("failed to parse RSA private key: {key_rejected}")) + })?; + + Ok(Self { + key: Arc::new(key_pair), + }) + } +} + +impl SigningKey for RsaSigningKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> { + ALL_RSA_SCHEMES + .iter() + .find(|scheme| offered.contains(scheme)) + .map(|scheme| RsaSigner::new(self.key.clone(), *scheme)) + } + + fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'_>> { + Some(public_key_to_spki( + &alg_id::RSA_ENCRYPTION, + self.key.public_key(), + )) + } + + fn algorithm(&self) -> SignatureAlgorithm { + SignatureAlgorithm::RSA + } +} + +impl Debug for RsaSigningKey { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("RsaSigningKey") + .field("algorithm", &self.algorithm()) + .finish() + } +} + +struct RsaSigner { + key: Arc<RsaKeyPair>, + scheme: SignatureScheme, + encoding: &'static dyn signature::RsaEncoding, +} + +impl RsaSigner { + fn new(key: Arc<RsaKeyPair>, scheme: SignatureScheme) -> Box<dyn Signer> { + let encoding: &dyn signature::RsaEncoding = match scheme { + SignatureScheme::RSA_PKCS1_SHA256 => &signature::RSA_PKCS1_SHA256, + SignatureScheme::RSA_PKCS1_SHA384 => &signature::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA512 => &signature::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PSS_SHA256 => &signature::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384 => &signature::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512 => &signature::RSA_PSS_SHA512, + _ => unreachable!(), + }; + + Box::new(Self { + key, + scheme, + encoding, + }) + } +} + +impl Signer for RsaSigner { + fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> { + let mut sig = vec![0; self.key.public_modulus_len()]; + + let rng = SystemRandom::new(); + self.key + .sign(self.encoding, &rng, message, &mut sig) + .map(|_| sig) + .map_err(|_| Error::General("signing failed".to_string())) + } + + fn scheme(&self) -> SignatureScheme { + self.scheme + } +} + +impl Debug for RsaSigner { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("RsaSigner") + .field("scheme", &self.scheme) + .finish() + } +} + +/// A SigningKey that uses exactly one TLS-level SignatureScheme +/// and one ring-level signature::SigningAlgorithm. +/// +/// Compare this to RsaSigningKey, which for a particular key is +/// willing to sign with several algorithms. This is quite poor +/// cryptography practice, but is necessary because a given RSA key +/// is expected to work in TLS1.2 (PKCS#1 signatures) and TLS1.3 +/// (PSS signatures) -- nobody is willing to obtain certificates for +/// different protocol versions. +/// +/// Currently this is only implemented for ECDSA keys. +struct EcdsaSigningKey { + key: Arc<EcdsaKeyPair>, + scheme: SignatureScheme, +} + +impl EcdsaSigningKey { + /// Make a new `ECDSASigningKey` from a DER encoding in PKCS#8 or SEC1 + /// format, expecting a key usable with precisely the given signature + /// scheme. + fn new( + der: &PrivateKeyDer<'_>, + scheme: SignatureScheme, + sigalg: &'static signature::EcdsaSigningAlgorithm, + ) -> Result<Self, ()> { + let key_pair = match der { + PrivateKeyDer::Sec1(sec1) => { + EcdsaKeyPair::from_private_key_der(sigalg, sec1.secret_sec1_der()) + .map_err(|_| ())? + } + PrivateKeyDer::Pkcs8(pkcs8) => { + EcdsaKeyPair::from_pkcs8(sigalg, pkcs8.secret_pkcs8_der()).map_err(|_| ())? + } + _ => return Err(()), + }; + + Ok(Self { + key: Arc::new(key_pair), + scheme, + }) + } +} + +impl SigningKey for EcdsaSigningKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> { + if offered.contains(&self.scheme) { + Some(Box::new(EcdsaSigner { + key: self.key.clone(), + scheme: self.scheme, + })) + } else { + None + } + } + + fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'_>> { + let id = match self.scheme { + SignatureScheme::ECDSA_NISTP256_SHA256 => alg_id::ECDSA_P256, + SignatureScheme::ECDSA_NISTP384_SHA384 => alg_id::ECDSA_P384, + SignatureScheme::ECDSA_NISTP521_SHA512 => alg_id::ECDSA_P521, + _ => unreachable!(), + }; + + Some(public_key_to_spki(&id, self.key.public_key())) + } + + fn algorithm(&self) -> SignatureAlgorithm { + self.scheme.algorithm() + } +} + +impl Debug for EcdsaSigningKey { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("EcdsaSigningKey") + .field("algorithm", &self.algorithm()) + .finish() + } +} + +struct EcdsaSigner { + key: Arc<EcdsaKeyPair>, + scheme: SignatureScheme, +} + +impl Signer for EcdsaSigner { + fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> { + let rng = SystemRandom::new(); + self.key + .sign(&rng, message) + .map_err(|_| Error::General("signing failed".into())) + .map(|sig| sig.as_ref().into()) + } + + fn scheme(&self) -> SignatureScheme { + self.scheme + } +} + +impl Debug for EcdsaSigner { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("EcdsaSigner") + .field("scheme", &self.scheme) + .finish() + } +} + +/// A SigningKey that uses exactly one TLS-level SignatureScheme +/// and one ring-level signature::SigningAlgorithm. +/// +/// Compare this to RsaSigningKey, which for a particular key is +/// willing to sign with several algorithms. This is quite poor +/// cryptography practice, but is necessary because a given RSA key +/// is expected to work in TLS1.2 (PKCS#1 signatures) and TLS1.3 +/// (PSS signatures) -- nobody is willing to obtain certificates for +/// different protocol versions. +/// +/// Currently this is only implemented for Ed25519 keys. +struct Ed25519SigningKey { + key: Arc<Ed25519KeyPair>, + scheme: SignatureScheme, +} + +impl Ed25519SigningKey { + /// Make a new `Ed25519SigningKey` from a DER encoding in PKCS#8 format, + /// expecting a key usable with precisely the given signature scheme. + fn new(der: &PrivatePkcs8KeyDer<'_>, scheme: SignatureScheme) -> Result<Self, Error> { + match Ed25519KeyPair::from_pkcs8_maybe_unchecked(der.secret_pkcs8_der()) { + Ok(key_pair) => Ok(Self { + key: Arc::new(key_pair), + scheme, + }), + Err(e) => Err(Error::General(format!( + "failed to parse Ed25519 private key: {e}" + ))), + } + } +} + +impl SigningKey for Ed25519SigningKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> { + if offered.contains(&self.scheme) { + Some(Box::new(Ed25519Signer { + key: self.key.clone(), + scheme: self.scheme, + })) + } else { + None + } + } + + fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'_>> { + Some(public_key_to_spki(&alg_id::ED25519, self.key.public_key())) + } + + fn algorithm(&self) -> SignatureAlgorithm { + self.scheme.algorithm() + } +} + +impl Debug for Ed25519SigningKey { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Ed25519SigningKey") + .field("algorithm", &self.algorithm()) + .finish() + } +} + +struct Ed25519Signer { + key: Arc<Ed25519KeyPair>, + scheme: SignatureScheme, +} + +impl Signer for Ed25519Signer { + fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> { + Ok(self.key.sign(message).as_ref().into()) + } + + fn scheme(&self) -> SignatureScheme { + self.scheme + } +} + +impl Debug for Ed25519Signer { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Ed25519Signer") + .field("scheme", &self.scheme) + .finish() + } +} + +#[cfg(test)] +mod tests { + use alloc::format; + + use pki_types::{PrivatePkcs1KeyDer, PrivateSec1KeyDer}; + + use super::*; + + #[test] + fn can_load_ecdsa_nistp256_pkcs8() { + let key = + PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/nistp256key.pkcs8.der")[..]); + assert!(any_eddsa_type(&key).is_err()); + let key = PrivateKeyDer::Pkcs8(key); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_load_ecdsa_nistp256_sec1() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp256key.der")[..], + )); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_sign_ecdsa_nistp256() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp256key.der")[..], + )); + + let k = any_supported_type(&key).unwrap(); + assert_eq!(format!("{k:?}"), "EcdsaSigningKey { algorithm: ECDSA }"); + assert_eq!(k.algorithm(), SignatureAlgorithm::ECDSA); + + assert!( + k.choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP384_SHA384]) + .is_none() + ); + let s = k + .choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .unwrap(); + assert_eq!( + format!("{s:?}"), + "EcdsaSigner { scheme: ECDSA_NISTP256_SHA256 }" + ); + assert_eq!(s.scheme(), SignatureScheme::ECDSA_NISTP256_SHA256); + // nb. signature is variable length and asn.1-encoded + assert!( + s.sign(b"hello") + .unwrap() + .starts_with(&[0x30]) + ); + } + + #[test] + fn can_load_ecdsa_nistp384_pkcs8() { + let key = + PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/nistp384key.pkcs8.der")[..]); + assert!(any_eddsa_type(&key).is_err()); + let key = PrivateKeyDer::Pkcs8(key); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_load_ecdsa_nistp384_sec1() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp384key.der")[..], + )); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_sign_ecdsa_nistp384() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp384key.der")[..], + )); + + let k = any_supported_type(&key).unwrap(); + assert_eq!(format!("{k:?}"), "EcdsaSigningKey { algorithm: ECDSA }"); + assert_eq!(k.algorithm(), SignatureAlgorithm::ECDSA); + + assert!( + k.choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .is_none() + ); + let s = k + .choose_scheme(&[SignatureScheme::ECDSA_NISTP384_SHA384]) + .unwrap(); + assert_eq!( + format!("{s:?}"), + "EcdsaSigner { scheme: ECDSA_NISTP384_SHA384 }" + ); + assert_eq!(s.scheme(), SignatureScheme::ECDSA_NISTP384_SHA384); + // nb. signature is variable length and asn.1-encoded + assert!( + s.sign(b"hello") + .unwrap() + .starts_with(&[0x30]) + ); + } + + #[test] + fn can_load_ecdsa_nistp521_pkcs8() { + let key = + PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/nistp521key.pkcs8.der")[..]); + assert!(any_eddsa_type(&key).is_err()); + let key = PrivateKeyDer::Pkcs8(key); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_load_ecdsa_nistp521_sec1() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp521key.der")[..], + )); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_sign_ecdsa_nistp521() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp521key.der")[..], + )); + + let k = any_supported_type(&key).unwrap(); + assert_eq!(format!("{k:?}"), "EcdsaSigningKey { algorithm: ECDSA }"); + assert_eq!(k.algorithm(), SignatureAlgorithm::ECDSA); + + assert!( + k.choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP384_SHA384]) + .is_none() + ); + let s = k + .choose_scheme(&[SignatureScheme::ECDSA_NISTP521_SHA512]) + .unwrap(); + assert_eq!( + format!("{s:?}"), + "EcdsaSigner { scheme: ECDSA_NISTP521_SHA512 }" + ); + assert_eq!(s.scheme(), SignatureScheme::ECDSA_NISTP521_SHA512); + // nb. signature is variable length and asn.1-encoded + assert!( + s.sign(b"hello") + .unwrap() + .starts_with(&[0x30]) + ); + } + + #[test] + fn can_load_eddsa_pkcs8() { + let key = PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/eddsakey.der")[..]); + assert!(any_eddsa_type(&key).is_ok()); + let key = PrivateKeyDer::Pkcs8(key); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_err()); + } + + #[test] + fn can_sign_eddsa() { + let key = PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/eddsakey.der")[..]); + + let k = any_eddsa_type(&key).unwrap(); + assert_eq!(format!("{k:?}"), "Ed25519SigningKey { algorithm: ED25519 }"); + assert_eq!(k.algorithm(), SignatureAlgorithm::ED25519); + + assert!( + k.choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .is_none() + ); + let s = k + .choose_scheme(&[SignatureScheme::ED25519]) + .unwrap(); + assert_eq!(format!("{s:?}"), "Ed25519Signer { scheme: ED25519 }"); + assert_eq!(s.scheme(), SignatureScheme::ED25519); + assert_eq!(s.sign(b"hello").unwrap().len(), 64); + } + + #[test] + fn can_load_rsa2048_pkcs8() { + let key = + PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..]); + assert!(any_eddsa_type(&key).is_err()); + let key = PrivateKeyDer::Pkcs8(key); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_err()); + } + + #[test] + fn can_load_rsa2048_pkcs1() { + let key = PrivateKeyDer::Pkcs1(PrivatePkcs1KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs1.der")[..], + )); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_err()); + } + + #[test] + fn can_sign_rsa2048() { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..], + )); + + let k = any_supported_type(&key).unwrap(); + assert_eq!(format!("{k:?}"), "RsaSigningKey { algorithm: RSA }"); + assert_eq!(k.algorithm(), SignatureAlgorithm::RSA); + + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ED25519]) + .is_none() + ); + + let s = k + .choose_scheme(&[SignatureScheme::RSA_PSS_SHA256]) + .unwrap(); + assert_eq!(format!("{s:?}"), "RsaSigner { scheme: RSA_PSS_SHA256 }"); + assert_eq!(s.scheme(), SignatureScheme::RSA_PSS_SHA256); + assert_eq!(s.sign(b"hello").unwrap().len(), 256); + + for scheme in &[ + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + ] { + k.choose_scheme(&[*scheme]).unwrap(); + } + } + + #[test] + fn cannot_load_invalid_pkcs8_encoding() { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(&b"invalid"[..])); + assert_eq!( + any_supported_type(&key).err(), + Some(Error::General( + "failed to parse private key as RSA, ECDSA, or EdDSA".into() + )) + ); + assert_eq!( + any_ecdsa_type(&key).err(), + Some(Error::General( + "failed to parse ECDSA private key as PKCS#8 or SEC1".into() + )) + ); + assert_eq!( + RsaSigningKey::new(&key).err(), + Some(Error::General( + "failed to parse RSA private key: InvalidEncoding".into() + )) + ); + } +} + +#[cfg(bench)] +mod benchmarks { + use super::{PrivateKeyDer, PrivatePkcs8KeyDer, SignatureScheme}; + + #[bench] + fn bench_rsa2048_pkcs1_sha256(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA256]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_rsa2048_pss_sha256(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::RSA_PSS_SHA256]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_eddsa(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/eddsakey.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::ED25519]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_ecdsa_p256_sha256(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp256key.pkcs8.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_ecdsa_p384_sha384(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp384key.pkcs8.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::ECDSA_NISTP384_SHA384]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_ecdsa_p521_sha512(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp521key.pkcs8.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::ECDSA_NISTP521_SHA512]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_load_and_validate_rsa2048(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..], + )); + + b.iter(|| { + test::black_box(super::any_supported_type(&key).unwrap()); + }); + } + + #[bench] + fn bench_load_and_validate_rsa4096(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa4096key.pkcs8.der")[..], + )); + + b.iter(|| { + test::black_box(super::any_supported_type(&key).unwrap()); + }); + } + + #[bench] + fn bench_load_and_validate_p256(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp256key.pkcs8.der")[..], + )); + + b.iter(|| { + test::black_box(super::any_ecdsa_type(&key).unwrap()); + }); + } + + #[bench] + fn bench_load_and_validate_p384(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp384key.pkcs8.der")[..], + )); + + b.iter(|| { + test::black_box(super::any_ecdsa_type(&key).unwrap()); + }); + } + + #[bench] + fn bench_load_and_validate_p521(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp521key.pkcs8.der")[..], + )); + + b.iter(|| { + test::black_box(super::any_ecdsa_type(&key).unwrap()); + }); + } + + #[bench] + fn bench_load_and_validate_eddsa(b: &mut test::Bencher) { + let key = PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/eddsakey.der")[..]); + + b.iter(|| { + test::black_box(super::any_eddsa_type(&key).unwrap()); + }); + } + + const SAMPLE_TLS13_MESSAGE: &[u8] = &[ + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, 0x2c, 0x20, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00, 0x04, 0xca, 0xc4, 0x48, 0x0e, 0x70, 0xf2, + 0x1b, 0xa9, 0x1c, 0x16, 0xca, 0x90, 0x48, 0xbe, 0x28, 0x2f, 0xc7, 0xf8, 0x9b, 0x87, 0x72, + 0x93, 0xda, 0x4d, 0x2f, 0x80, 0x80, 0x60, 0x1a, 0xd3, 0x08, 0xe2, 0xb7, 0x86, 0x14, 0x1b, + 0x54, 0xda, 0x9a, 0xc9, 0x6d, 0xe9, 0x66, 0xb4, 0x9f, 0xe2, 0x2c, + ]; +} diff --git a/vendor/rustls/src/crypto/aws_lc_rs/ticketer.rs b/vendor/rustls/src/crypto/aws_lc_rs/ticketer.rs new file mode 100644 index 00000000..44c7c21b --- /dev/null +++ b/vendor/rustls/src/crypto/aws_lc_rs/ticketer.rs @@ -0,0 +1,397 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::fmt; +use core::fmt::{Debug, Formatter}; +use core::sync::atomic::{AtomicUsize, Ordering}; + +use aws_lc_rs::cipher::{ + AES_256, AES_256_KEY_LEN, AES_CBC_IV_LEN, DecryptionContext, PaddedBlockDecryptingKey, + PaddedBlockEncryptingKey, UnboundCipherKey, +}; +use aws_lc_rs::{hmac, iv}; + +use super::ring_like::rand::{SecureRandom, SystemRandom}; +use super::unspecified_err; +use crate::error::Error; +#[cfg(debug_assertions)] +use crate::log::debug; +use crate::polyfill::try_split_at; +use crate::rand::GetRandomFailed; +use crate::server::ProducesTickets; +use crate::sync::Arc; + +/// A concrete, safe ticket creation mechanism. +pub struct Ticketer {} + +impl Ticketer { + /// Make the recommended `Ticketer`. This produces tickets + /// with a 12 hour life and randomly generated keys. + /// + /// The `Ticketer` uses the [RFC 5077 §4] "Recommended Ticket Construction", + /// using AES 256 for encryption and HMAC-SHA256 for ciphertext authentication. + /// + /// [RFC 5077 §4]: https://www.rfc-editor.org/rfc/rfc5077#section-4 + #[cfg(feature = "std")] + pub fn new() -> Result<Arc<dyn ProducesTickets>, Error> { + Ok(Arc::new(crate::ticketer::TicketRotator::new( + 6 * 60 * 60, + make_ticket_generator, + )?)) + } +} + +fn make_ticket_generator() -> Result<Box<dyn ProducesTickets>, GetRandomFailed> { + // NOTE(XXX): Unconditionally mapping errors to `GetRandomFailed` here is slightly + // misleading in some cases (e.g. failure to construct a padded block cipher encrypting key). + // However, we can't change the return type expected from a `TicketSwitcher` `generator` + // without breaking semver. + // Tracking in https://github.com/rustls/rustls/issues/2074 + Ok(Box::new( + Rfc5077Ticketer::new().map_err(|_| GetRandomFailed)?, + )) +} + +/// An RFC 5077 "Recommended Ticket Construction" implementation of a [`Ticketer`]. +struct Rfc5077Ticketer { + aes_encrypt_key: PaddedBlockEncryptingKey, + aes_decrypt_key: PaddedBlockDecryptingKey, + hmac_key: hmac::Key, + key_name: [u8; 16], + lifetime: u32, + maximum_ciphertext_len: AtomicUsize, +} + +impl Rfc5077Ticketer { + fn new() -> Result<Self, Error> { + let rand = SystemRandom::new(); + + // Generate a random AES 256 key to use for AES CBC encryption. + let mut aes_key = [0u8; AES_256_KEY_LEN]; + rand.fill(&mut aes_key) + .map_err(|_| GetRandomFailed)?; + + // Convert the raw AES 256 key bytes into encrypting and decrypting keys using CBC mode and + // PKCS#7 padding. We don't want to store just the raw key bytes as constructing the + // cipher keys has some setup overhead. We can't store just the `UnboundCipherKey` since + // constructing the padded encrypt/decrypt specific types consume the `UnboundCipherKey`. + let aes_encrypt_key = + UnboundCipherKey::new(&AES_256, &aes_key[..]).map_err(unspecified_err)?; + let aes_encrypt_key = + PaddedBlockEncryptingKey::cbc_pkcs7(aes_encrypt_key).map_err(unspecified_err)?; + + // Convert the raw AES 256 key bytes into a decrypting key using CBC PKCS#7 padding. + let aes_decrypt_key = + UnboundCipherKey::new(&AES_256, &aes_key[..]).map_err(unspecified_err)?; + let aes_decrypt_key = + PaddedBlockDecryptingKey::cbc_pkcs7(aes_decrypt_key).map_err(unspecified_err)?; + + // Generate a random HMAC SHA256 key to use for HMAC authentication. + let hmac_key = hmac::Key::generate(hmac::HMAC_SHA256, &rand).map_err(unspecified_err)?; + + // Generate a random key name. + let mut key_name = [0u8; 16]; + rand.fill(&mut key_name) + .map_err(|_| GetRandomFailed)?; + + Ok(Self { + aes_encrypt_key, + aes_decrypt_key, + hmac_key, + key_name, + lifetime: 60 * 60 * 12, + maximum_ciphertext_len: AtomicUsize::new(0), + }) + } +} + +impl ProducesTickets for Rfc5077Ticketer { + fn enabled(&self) -> bool { + true + } + + fn lifetime(&self) -> u32 { + self.lifetime + } + + /// Encrypt `message` and return the ciphertext. + fn encrypt(&self, message: &[u8]) -> Option<Vec<u8>> { + // Encrypt the ticket state - the cipher module handles generating a random IV of + // appropriate size, returning it in the `DecryptionContext`. + let mut encrypted_state = Vec::from(message); + let dec_ctx = self + .aes_encrypt_key + .encrypt(&mut encrypted_state) + .ok()?; + let iv: &[u8] = (&dec_ctx).try_into().ok()?; + + // Produce the MAC tag over the relevant context & encrypted state. + // Quoting RFC 5077: + // "The Message Authentication Code (MAC) is calculated using HMAC-SHA-256 over + // key_name (16 octets) and IV (16 octets), followed by the length of + // the encrypted_state field (2 octets) and its contents (variable + // length)." + let mut hmac_data = + Vec::with_capacity(self.key_name.len() + iv.len() + 2 + encrypted_state.len()); + hmac_data.extend(&self.key_name); + hmac_data.extend(iv); + hmac_data.extend( + u16::try_from(encrypted_state.len()) + .ok()? + .to_be_bytes(), + ); + hmac_data.extend(&encrypted_state); + let tag = hmac::sign(&self.hmac_key, &hmac_data); + let tag = tag.as_ref(); + + // Combine the context, the encrypted state, and the tag to produce the final ciphertext. + // Ciphertext structure is: + // key_name: [u8; 16] + // iv: [u8; 16] + // encrypted_state: [u8, _] + // mac tag: [u8; 32] + let mut ciphertext = + Vec::with_capacity(self.key_name.len() + iv.len() + encrypted_state.len() + tag.len()); + ciphertext.extend(self.key_name); + ciphertext.extend(iv); + ciphertext.extend(encrypted_state); + ciphertext.extend(tag); + + self.maximum_ciphertext_len + .fetch_max(ciphertext.len(), Ordering::SeqCst); + + Some(ciphertext) + } + + fn decrypt(&self, ciphertext: &[u8]) -> Option<Vec<u8>> { + if ciphertext.len() + > self + .maximum_ciphertext_len + .load(Ordering::SeqCst) + { + #[cfg(debug_assertions)] + debug!("rejected over-length ticket"); + return None; + } + + // Split off the key name from the remaining ciphertext. + let (alleged_key_name, ciphertext) = try_split_at(ciphertext, self.key_name.len())?; + + // Split off the IV from the remaining ciphertext. + let (iv, ciphertext) = try_split_at(ciphertext, AES_CBC_IV_LEN)?; + + // And finally, split the encrypted state from the tag. + let tag_len = self + .hmac_key + .algorithm() + .digest_algorithm() + .output_len(); + let (enc_state, mac) = try_split_at(ciphertext, ciphertext.len() - tag_len)?; + + // Reconstitute the HMAC data to verify the tag. + let mut hmac_data = + Vec::with_capacity(alleged_key_name.len() + iv.len() + 2 + enc_state.len()); + hmac_data.extend(alleged_key_name); + hmac_data.extend(iv); + hmac_data.extend( + u16::try_from(enc_state.len()) + .ok()? + .to_be_bytes(), + ); + hmac_data.extend(enc_state); + hmac::verify(&self.hmac_key, &hmac_data, mac).ok()?; + + // Convert the raw IV back into an appropriate decryption context. + let iv = iv::FixedLength::try_from(iv).ok()?; + let dec_context = DecryptionContext::Iv128(iv); + + // And finally, decrypt the encrypted state. + let mut out = Vec::from(enc_state); + let plaintext = self + .aes_decrypt_key + .decrypt(&mut out, dec_context) + .ok()?; + + Some(plaintext.into()) + } +} + +impl Debug for Rfc5077Ticketer { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // Note: we deliberately omit keys from the debug output. + f.debug_struct("Rfc5077Ticketer") + .field("lifetime", &self.lifetime) + .finish() + } +} + +#[cfg(test)] +mod tests { + use core::time::Duration; + + use pki_types::UnixTime; + + use super::*; + + #[test] + fn basic_pairwise_test() { + let t = Ticketer::new().unwrap(); + assert!(t.enabled()); + let cipher = t.encrypt(b"hello world").unwrap(); + let plain = t.decrypt(&cipher).unwrap(); + assert_eq!(plain, b"hello world"); + } + + #[test] + fn refuses_decrypt_before_encrypt() { + let t = Ticketer::new().unwrap(); + assert_eq!(t.decrypt(b"hello"), None); + } + + #[test] + fn refuses_decrypt_larger_than_largest_encryption() { + let t = Ticketer::new().unwrap(); + let mut cipher = t.encrypt(b"hello world").unwrap(); + assert_eq!(t.decrypt(&cipher), Some(b"hello world".to_vec())); + + // obviously this would never work anyway, but this + // and `cannot_decrypt_before_encrypt` exercise the + // first branch in `decrypt()` + cipher.push(0); + assert_eq!(t.decrypt(&cipher), None); + } + + #[test] + fn ticketrotator_switching_test() { + let t = Arc::new(crate::ticketer::TicketRotator::new(1, make_ticket_generator).unwrap()); + let now = UnixTime::now(); + let cipher1 = t.encrypt(b"ticket 1").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + { + // Trigger new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 10, + ))); + } + let cipher2 = t.encrypt(b"ticket 2").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + { + // Trigger new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 20, + ))); + } + let cipher3 = t.encrypt(b"ticket 3").unwrap(); + assert!(t.decrypt(&cipher1).is_none()); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3"); + } + + #[test] + fn ticketrotator_remains_usable_over_temporary_ticketer_creation_failure() { + let mut t = crate::ticketer::TicketRotator::new(1, make_ticket_generator).unwrap(); + let now = UnixTime::now(); + let cipher1 = t.encrypt(b"ticket 1").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + t.generator = fail_generator; + { + // Failed new ticketer; this means we still need to + // rotate. + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 10, + ))); + } + + // check post-failure encryption/decryption still works + let cipher2 = t.encrypt(b"ticket 2").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + + // do the rotation for real + t.generator = make_ticket_generator; + { + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 20, + ))); + } + let cipher3 = t.encrypt(b"ticket 3").unwrap(); + assert!(t.decrypt(&cipher1).is_some()); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3"); + } + + #[test] + fn ticketswitcher_switching_test() { + #[expect(deprecated)] + let t = Arc::new(crate::ticketer::TicketSwitcher::new(1, make_ticket_generator).unwrap()); + let now = UnixTime::now(); + let cipher1 = t.encrypt(b"ticket 1").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + { + // Trigger new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 10, + ))); + } + let cipher2 = t.encrypt(b"ticket 2").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + { + // Trigger new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 20, + ))); + } + let cipher3 = t.encrypt(b"ticket 3").unwrap(); + assert!(t.decrypt(&cipher1).is_none()); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3"); + } + + #[test] + fn ticketswitcher_recover_test() { + #[expect(deprecated)] + let mut t = crate::ticketer::TicketSwitcher::new(1, make_ticket_generator).unwrap(); + let now = UnixTime::now(); + let cipher1 = t.encrypt(b"ticket 1").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + t.generator = fail_generator; + { + // Failed new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 10, + ))); + } + t.generator = make_ticket_generator; + let cipher2 = t.encrypt(b"ticket 2").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + { + // recover + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 20, + ))); + } + let cipher3 = t.encrypt(b"ticket 3").unwrap(); + assert!(t.decrypt(&cipher1).is_none()); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3"); + } + + #[test] + fn rfc5077ticketer_is_debug_and_producestickets() { + use alloc::format; + + use super::*; + + let t = make_ticket_generator().unwrap(); + + assert_eq!(format!("{t:?}"), "Rfc5077Ticketer { lifetime: 43200 }"); + assert!(t.enabled()); + assert_eq!(t.lifetime(), 43200); + } + + fn fail_generator() -> Result<Box<dyn ProducesTickets>, GetRandomFailed> { + Err(GetRandomFailed) + } +} diff --git a/vendor/rustls/src/crypto/aws_lc_rs/tls12.rs b/vendor/rustls/src/crypto/aws_lc_rs/tls12.rs new file mode 100644 index 00000000..3845af58 --- /dev/null +++ b/vendor/rustls/src/crypto/aws_lc_rs/tls12.rs @@ -0,0 +1,465 @@ +use alloc::boxed::Box; + +use aws_lc_rs::{aead, tls_prf}; + +use crate::crypto::cipher::{ + AeadKey, InboundOpaqueMessage, Iv, KeyBlockShape, MessageDecrypter, MessageEncrypter, + NONCE_LEN, Nonce, Tls12AeadAlgorithm, UnsupportedOperationError, make_tls12_aad, +}; +use crate::crypto::tls12::Prf; +use crate::crypto::{ActiveKeyExchange, KeyExchangeAlgorithm}; +use crate::enums::{CipherSuite, SignatureScheme}; +use crate::error::Error; +use crate::msgs::fragmenter::MAX_FRAGMENT_LEN; +use crate::msgs::message::{ + InboundPlainMessage, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, +}; +use crate::suites::{CipherSuiteCommon, ConnectionTrafficSecrets, SupportedCipherSuite}; +use crate::tls12::Tls12CipherSuite; +use crate::version::TLS12; + +/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256. +pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &ChaCha20Poly1305, + prf_provider: &Tls12Prf(&tls_prf::P_SHA256), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +pub static TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_RSA_SCHEMES, + aead_alg: &ChaCha20Poly1305, + prf_provider: &Tls12Prf(&tls_prf::P_SHA256), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +pub static TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: 1 << 24, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_RSA_SCHEMES, + aead_alg: &AES128_GCM, + prf_provider: &Tls12Prf(&tls_prf::P_SHA256), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +pub static TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + hash_provider: &super::hash::SHA384, + confidentiality_limit: 1 << 24, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_RSA_SCHEMES, + aead_alg: &AES256_GCM, + prf_provider: &Tls12Prf(&tls_prf::P_SHA384), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +pub static TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: 1 << 24, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &AES128_GCM, + prf_provider: &Tls12Prf(&tls_prf::P_SHA256), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +pub static TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + hash_provider: &super::hash::SHA384, + confidentiality_limit: 1 << 24, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &AES256_GCM, + prf_provider: &Tls12Prf(&tls_prf::P_SHA384), + }); + +static TLS12_ECDSA_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::ED25519, + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::ECDSA_NISTP256_SHA256, +]; + +static TLS12_RSA_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA256, +]; + +pub(crate) static AES128_GCM: GcmAlgorithm = GcmAlgorithm(&aead::AES_128_GCM); +pub(crate) static AES256_GCM: GcmAlgorithm = GcmAlgorithm(&aead::AES_256_GCM); + +pub(crate) struct GcmAlgorithm(&'static aead::Algorithm); + +impl Tls12AeadAlgorithm for GcmAlgorithm { + fn decrypter(&self, dec_key: AeadKey, dec_iv: &[u8]) -> Box<dyn MessageDecrypter> { + // safety: see `encrypter()`. + let dec_key = + aead::TlsRecordOpeningKey::new(self.0, aead::TlsProtocolId::TLS12, dec_key.as_ref()) + .unwrap(); + + let mut ret = GcmMessageDecrypter { + dec_key, + dec_salt: [0u8; 4], + }; + + debug_assert_eq!(dec_iv.len(), 4); + ret.dec_salt.copy_from_slice(dec_iv); + Box::new(ret) + } + + fn encrypter( + &self, + enc_key: AeadKey, + write_iv: &[u8], + explicit: &[u8], + ) -> Box<dyn MessageEncrypter> { + // safety: `TlsRecordSealingKey::new` fails if + // - `enc_key`'s length is wrong for `algorithm`. But the length is defined by + // `algorithm.key_len()` in `key_block_shape()`, below. + // - `algorithm` is not supported: but `AES_128_GCM` and `AES_256_GCM` is. + // thus, this `unwrap()` is unreachable. + // + // `TlsProtocolId::TLS13` is deliberate: we reuse the nonce construction from + // RFC7905 and TLS13: a random starting point, XOR'd with the sequence number. This means + // `TlsProtocolId::TLS12` (which wants to see a plain sequence number) is unsuitable. + // + // The most important property is that nonce is unique per key, which is satisfied by + // this construction, even if the nonce is not monotonically increasing. + let enc_key = + aead::TlsRecordSealingKey::new(self.0, aead::TlsProtocolId::TLS13, enc_key.as_ref()) + .unwrap(); + let iv = gcm_iv(write_iv, explicit); + Box::new(GcmMessageEncrypter { enc_key, iv }) + } + + fn key_block_shape(&self) -> KeyBlockShape { + KeyBlockShape { + enc_key_len: self.0.key_len(), + fixed_iv_len: 4, + explicit_nonce_len: 8, + } + } + + fn extract_keys( + &self, + key: AeadKey, + write_iv: &[u8], + explicit: &[u8], + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + let iv = gcm_iv(write_iv, explicit); + Ok(match self.0.key_len() { + 16 => ConnectionTrafficSecrets::Aes128Gcm { key, iv }, + 32 => ConnectionTrafficSecrets::Aes256Gcm { key, iv }, + _ => unreachable!(), + }) + } + + fn fips(&self) -> bool { + super::fips() + } +} + +pub(crate) struct ChaCha20Poly1305; + +impl Tls12AeadAlgorithm for ChaCha20Poly1305 { + fn decrypter(&self, dec_key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter> { + let dec_key = aead::LessSafeKey::new( + aead::UnboundKey::new(&aead::CHACHA20_POLY1305, dec_key.as_ref()).unwrap(), + ); + Box::new(ChaCha20Poly1305MessageDecrypter { + dec_key, + dec_offset: Iv::copy(iv), + }) + } + + fn encrypter(&self, enc_key: AeadKey, enc_iv: &[u8], _: &[u8]) -> Box<dyn MessageEncrypter> { + let enc_key = aead::LessSafeKey::new( + aead::UnboundKey::new(&aead::CHACHA20_POLY1305, enc_key.as_ref()).unwrap(), + ); + Box::new(ChaCha20Poly1305MessageEncrypter { + enc_key, + enc_offset: Iv::copy(enc_iv), + }) + } + + fn key_block_shape(&self) -> KeyBlockShape { + KeyBlockShape { + enc_key_len: 32, + fixed_iv_len: 12, + explicit_nonce_len: 0, + } + } + + fn extract_keys( + &self, + key: AeadKey, + iv: &[u8], + _explicit: &[u8], + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + // This should always be true because KeyBlockShape and the Iv nonce len are in agreement. + debug_assert_eq!(aead::NONCE_LEN, iv.len()); + Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { + key, + iv: Iv::new(iv[..].try_into().unwrap()), + }) + } + + fn fips(&self) -> bool { + false // not FIPS approved + } +} + +/// A `MessageEncrypter` for AES-GCM AEAD ciphersuites. TLS 1.2 only. +struct GcmMessageEncrypter { + enc_key: aead::TlsRecordSealingKey, + iv: Iv, +} + +/// A `MessageDecrypter` for AES-GCM AEAD ciphersuites. TLS1.2 only. +struct GcmMessageDecrypter { + dec_key: aead::TlsRecordOpeningKey, + dec_salt: [u8; 4], +} + +const GCM_EXPLICIT_NONCE_LEN: usize = 8; +const GCM_OVERHEAD: usize = GCM_EXPLICIT_NONCE_LEN + 16; + +impl MessageDecrypter for GcmMessageDecrypter { + fn decrypt<'a>( + &mut self, + mut msg: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result<InboundPlainMessage<'a>, Error> { + let payload = &msg.payload; + if payload.len() < GCM_OVERHEAD { + return Err(Error::DecryptError); + } + + let nonce = { + let mut nonce = [0u8; 12]; + nonce[..4].copy_from_slice(&self.dec_salt); + nonce[4..].copy_from_slice(&payload[..8]); + aead::Nonce::assume_unique_for_key(nonce) + }; + + let aad = aead::Aad::from(make_tls12_aad( + seq, + msg.typ, + msg.version, + payload.len() - GCM_OVERHEAD, + )); + + let payload = &mut msg.payload; + let plain_len = self + .dec_key + .open_in_place(nonce, aad, &mut payload[GCM_EXPLICIT_NONCE_LEN..]) + .map_err(|_| Error::DecryptError)? + .len(); + + if plain_len > MAX_FRAGMENT_LEN { + return Err(Error::PeerSentOversizedRecord); + } + + Ok( + msg.into_plain_message_range( + GCM_EXPLICIT_NONCE_LEN..GCM_EXPLICIT_NONCE_LEN + plain_len, + ), + ) + } +} + +impl MessageEncrypter for GcmMessageEncrypter { + fn encrypt( + &mut self, + msg: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result<OutboundOpaqueMessage, Error> { + let total_len = self.encrypted_payload_len(msg.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, seq).0); + let aad = aead::Aad::from(make_tls12_aad(seq, msg.typ, msg.version, msg.payload.len())); + payload.extend_from_slice(&nonce.as_ref()[4..]); + payload.extend_from_chunks(&msg.payload); + + self.enc_key + .seal_in_place_separate_tag(nonce, aad, &mut payload.as_mut()[GCM_EXPLICIT_NONCE_LEN..]) + .map(|tag| payload.extend_from_slice(tag.as_ref())) + .map_err(|_| Error::EncryptError)?; + + Ok(OutboundOpaqueMessage::new(msg.typ, msg.version, payload)) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + GCM_EXPLICIT_NONCE_LEN + self.enc_key.algorithm().tag_len() + } +} + +/// The RFC7905/RFC7539 ChaCha20Poly1305 construction. +/// This implementation does the AAD construction required in TLS1.2. +/// TLS1.3 uses `TLS13MessageEncrypter`. +struct ChaCha20Poly1305MessageEncrypter { + enc_key: aead::LessSafeKey, + enc_offset: Iv, +} + +/// The RFC7905/RFC7539 ChaCha20Poly1305 construction. +/// This implementation does the AAD construction required in TLS1.2. +/// TLS1.3 uses `TLS13MessageDecrypter`. +struct ChaCha20Poly1305MessageDecrypter { + dec_key: aead::LessSafeKey, + dec_offset: Iv, +} + +const CHACHAPOLY1305_OVERHEAD: usize = 16; + +impl MessageDecrypter for ChaCha20Poly1305MessageDecrypter { + fn decrypt<'a>( + &mut self, + mut msg: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result<InboundPlainMessage<'a>, Error> { + let payload = &msg.payload; + + if payload.len() < CHACHAPOLY1305_OVERHEAD { + return Err(Error::DecryptError); + } + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.dec_offset, seq).0); + let aad = aead::Aad::from(make_tls12_aad( + seq, + msg.typ, + msg.version, + payload.len() - CHACHAPOLY1305_OVERHEAD, + )); + + let payload = &mut msg.payload; + let plain_len = self + .dec_key + .open_in_place(nonce, aad, payload) + .map_err(|_| Error::DecryptError)? + .len(); + + if plain_len > MAX_FRAGMENT_LEN { + return Err(Error::PeerSentOversizedRecord); + } + + payload.truncate(plain_len); + Ok(msg.into_plain_message()) + } +} + +impl MessageEncrypter for ChaCha20Poly1305MessageEncrypter { + fn encrypt( + &mut self, + msg: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result<OutboundOpaqueMessage, Error> { + let total_len = self.encrypted_payload_len(msg.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.enc_offset, seq).0); + let aad = aead::Aad::from(make_tls12_aad(seq, msg.typ, msg.version, msg.payload.len())); + payload.extend_from_chunks(&msg.payload); + + self.enc_key + .seal_in_place_append_tag(nonce, aad, &mut payload) + .map_err(|_| Error::EncryptError)?; + + Ok(OutboundOpaqueMessage::new(msg.typ, msg.version, payload)) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + self.enc_key.algorithm().tag_len() + } +} + +fn gcm_iv(write_iv: &[u8], explicit: &[u8]) -> Iv { + debug_assert_eq!(write_iv.len(), 4); + debug_assert_eq!(explicit.len(), 8); + + // The GCM nonce is constructed from a 32-bit 'salt' derived + // from the master-secret, and a 64-bit explicit part, + // with no specified construction. Thanks for that. + // + // We use the same construction as TLS1.3/ChaCha20Poly1305: + // a starting point extracted from the key block, xored with + // the sequence number. + let mut iv = [0; NONCE_LEN]; + iv[..4].copy_from_slice(write_iv); + iv[4..].copy_from_slice(explicit); + + Iv::new(iv) +} + +struct Tls12Prf(&'static tls_prf::Algorithm); + +impl Prf for Tls12Prf { + fn for_secret(&self, output: &mut [u8], secret: &[u8], label: &[u8], seed: &[u8]) { + // safety: + // - [1] is safe because our caller guarantees `secret` is non-empty; this is + // the only documented error case. + // - [2] is safe in practice because the only failure from `derive()` is due + // to zero `output.len()`; this is outlawed at higher levels + let derived = tls_prf::Secret::new(self.0, secret) + .unwrap() // [1] + .derive(label, seed, output.len()) + .unwrap(); // [2] + output.copy_from_slice(derived.as_ref()); + } + + fn for_key_exchange( + &self, + output: &mut [u8; 48], + kx: Box<dyn ActiveKeyExchange>, + peer_pub_key: &[u8], + label: &[u8], + seed: &[u8], + ) -> Result<(), Error> { + self.for_secret( + output, + kx.complete_for_tls_version(peer_pub_key, &TLS12)? + .secret_bytes(), + label, + seed, + ); + Ok(()) + } + + fn fips(&self) -> bool { + super::fips() + } +} diff --git a/vendor/rustls/src/crypto/aws_lc_rs/tls13.rs b/vendor/rustls/src/crypto/aws_lc_rs/tls13.rs new file mode 100644 index 00000000..dbc9d912 --- /dev/null +++ b/vendor/rustls/src/crypto/aws_lc_rs/tls13.rs @@ -0,0 +1,425 @@ +use alloc::boxed::Box; + +use aws_lc_rs::hkdf::KeyType; +use aws_lc_rs::{aead, hkdf, hmac}; + +use crate::crypto; +use crate::crypto::cipher::{ + AeadKey, InboundOpaqueMessage, Iv, MessageDecrypter, MessageEncrypter, Nonce, + Tls13AeadAlgorithm, UnsupportedOperationError, make_tls13_aad, +}; +use crate::crypto::tls13::{Hkdf, HkdfExpander, OkmBlock, OutputLengthError}; +use crate::enums::{CipherSuite, ContentType, ProtocolVersion}; +use crate::error::Error; +use crate::msgs::message::{ + InboundPlainMessage, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, +}; +use crate::suites::{CipherSuiteCommon, ConnectionTrafficSecrets, SupportedCipherSuite}; +use crate::tls13::Tls13CipherSuite; + +/// The TLS1.3 ciphersuite TLS_CHACHA20_POLY1305_SHA256 +pub static TLS13_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls13(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL); + +pub(crate) static TLS13_CHACHA20_POLY1305_SHA256_INTERNAL: &Tls13CipherSuite = &Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + hash_provider: &super::hash::SHA256, + // ref: <https://www.ietf.org/archive/id/draft-irtf-cfrg-aead-limits-08.html#section-5.2.1> + confidentiality_limit: u64::MAX, + }, + hkdf_provider: &AwsLcHkdf(hkdf::HKDF_SHA256, hmac::HMAC_SHA256), + aead_alg: &Chacha20Poly1305Aead(AeadAlgorithm(&aead::CHACHA20_POLY1305)), + quic: Some(&super::quic::KeyBuilder { + packet_alg: &aead::CHACHA20_POLY1305, + header_alg: &aead::quic::CHACHA20, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-6.6> + confidentiality_limit: u64::MAX, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-6.6> + integrity_limit: 1 << 36, + }), +}; + +/// The TLS1.3 ciphersuite TLS_AES_256_GCM_SHA384 +pub static TLS13_AES_256_GCM_SHA384: SupportedCipherSuite = + SupportedCipherSuite::Tls13(&Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_AES_256_GCM_SHA384, + hash_provider: &super::hash::SHA384, + confidentiality_limit: 1 << 24, + }, + hkdf_provider: &AwsLcHkdf(hkdf::HKDF_SHA384, hmac::HMAC_SHA384), + aead_alg: &Aes256GcmAead(AeadAlgorithm(&aead::AES_256_GCM)), + quic: Some(&super::quic::KeyBuilder { + packet_alg: &aead::AES_256_GCM, + header_alg: &aead::quic::AES_256, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-b.1.1> + confidentiality_limit: 1 << 23, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-b.1.2> + integrity_limit: 1 << 52, + }), + }); + +/// The TLS1.3 ciphersuite TLS_AES_128_GCM_SHA256 +pub static TLS13_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls13(TLS13_AES_128_GCM_SHA256_INTERNAL); + +pub(crate) static TLS13_AES_128_GCM_SHA256_INTERNAL: &Tls13CipherSuite = &Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_AES_128_GCM_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: 1 << 24, + }, + hkdf_provider: &AwsLcHkdf(hkdf::HKDF_SHA256, hmac::HMAC_SHA256), + aead_alg: &Aes128GcmAead(AeadAlgorithm(&aead::AES_128_GCM)), + quic: Some(&super::quic::KeyBuilder { + packet_alg: &aead::AES_128_GCM, + header_alg: &aead::quic::AES_128, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-b.1.1> + confidentiality_limit: 1 << 23, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-b.1.2> + integrity_limit: 1 << 52, + }), +}; + +struct Chacha20Poly1305Aead(AeadAlgorithm); + +impl Tls13AeadAlgorithm for Chacha20Poly1305Aead { + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> { + // safety: the caller arranges that `key` is `key_len()` in bytes, so this unwrap is safe. + Box::new(AeadMessageEncrypter { + enc_key: aead::LessSafeKey::new(aead::UnboundKey::new(self.0.0, key.as_ref()).unwrap()), + iv, + }) + } + + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> { + // safety: the caller arranges that `key` is `key_len()` in bytes, so this unwrap is safe. + Box::new(AeadMessageDecrypter { + dec_key: aead::LessSafeKey::new(aead::UnboundKey::new(self.0.0, key.as_ref()).unwrap()), + iv, + }) + } + + fn key_len(&self) -> usize { + self.0.key_len() + } + + fn extract_keys( + &self, + key: AeadKey, + iv: Iv, + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv }) + } + + fn fips(&self) -> bool { + false // not FIPS approved + } +} + +struct Aes256GcmAead(AeadAlgorithm); + +impl Tls13AeadAlgorithm for Aes256GcmAead { + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> { + self.0.encrypter(key, iv) + } + + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> { + self.0.decrypter(key, iv) + } + + fn key_len(&self) -> usize { + self.0.key_len() + } + + fn extract_keys( + &self, + key: AeadKey, + iv: Iv, + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + Ok(ConnectionTrafficSecrets::Aes256Gcm { key, iv }) + } + + fn fips(&self) -> bool { + super::fips() + } +} + +struct Aes128GcmAead(AeadAlgorithm); + +impl Tls13AeadAlgorithm for Aes128GcmAead { + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> { + self.0.encrypter(key, iv) + } + + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> { + self.0.decrypter(key, iv) + } + + fn key_len(&self) -> usize { + self.0.key_len() + } + + fn extract_keys( + &self, + key: AeadKey, + iv: Iv, + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + Ok(ConnectionTrafficSecrets::Aes128Gcm { key, iv }) + } + + fn fips(&self) -> bool { + super::fips() + } +} + +// common encrypter/decrypter/key_len items for above Tls13AeadAlgorithm impls +struct AeadAlgorithm(&'static aead::Algorithm); + +impl AeadAlgorithm { + // using aead::TlsRecordSealingKey + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> { + // safety: + // - the caller arranges that `key` is `key_len()` in bytes, so this unwrap is safe. + // - this function should only be used for `Algorithm::AES_128_GCM` or `Algorithm::AES_256_GCM` + Box::new(GcmMessageEncrypter { + enc_key: aead::TlsRecordSealingKey::new( + self.0, + aead::TlsProtocolId::TLS13, + key.as_ref(), + ) + .unwrap(), + iv, + }) + } + + // using aead::TlsRecordOpeningKey + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> { + // safety: + // - the caller arranges that `key` is `key_len()` in bytes, so this unwrap is safe. + // - this function should only be used for `Algorithm::AES_128_GCM` or `Algorithm::AES_256_GCM` + Box::new(GcmMessageDecrypter { + dec_key: aead::TlsRecordOpeningKey::new( + self.0, + aead::TlsProtocolId::TLS13, + key.as_ref(), + ) + .unwrap(), + iv, + }) + } + + fn key_len(&self) -> usize { + self.0.key_len() + } +} + +struct AeadMessageEncrypter { + enc_key: aead::LessSafeKey, + iv: Iv, +} + +struct AeadMessageDecrypter { + dec_key: aead::LessSafeKey, + iv: Iv, +} + +impl MessageEncrypter for AeadMessageEncrypter { + fn encrypt( + &mut self, + msg: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result<OutboundOpaqueMessage, Error> { + let total_len = self.encrypted_payload_len(msg.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, seq).0); + let aad = aead::Aad::from(make_tls13_aad(total_len)); + payload.extend_from_chunks(&msg.payload); + payload.extend_from_slice(&msg.typ.to_array()); + + self.enc_key + .seal_in_place_append_tag(nonce, aad, &mut payload) + .map_err(|_| Error::EncryptError)?; + + Ok(OutboundOpaqueMessage::new( + ContentType::ApplicationData, + // Note: all TLS 1.3 application data records use TLSv1_2 (0x0303) as the legacy record + // protocol version, see https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + ProtocolVersion::TLSv1_2, + payload, + )) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + 1 + self.enc_key.algorithm().tag_len() + } +} + +impl MessageDecrypter for AeadMessageDecrypter { + fn decrypt<'a>( + &mut self, + mut msg: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result<InboundPlainMessage<'a>, Error> { + let payload = &mut msg.payload; + if payload.len() < self.dec_key.algorithm().tag_len() { + return Err(Error::DecryptError); + } + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, seq).0); + let aad = aead::Aad::from(make_tls13_aad(payload.len())); + let plain_len = self + .dec_key + .open_in_place(nonce, aad, payload) + .map_err(|_| Error::DecryptError)? + .len(); + + payload.truncate(plain_len); + msg.into_tls13_unpadded_message() + } +} + +struct GcmMessageEncrypter { + enc_key: aead::TlsRecordSealingKey, + iv: Iv, +} + +impl MessageEncrypter for GcmMessageEncrypter { + fn encrypt( + &mut self, + msg: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result<OutboundOpaqueMessage, Error> { + let total_len = self.encrypted_payload_len(msg.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, seq).0); + let aad = aead::Aad::from(make_tls13_aad(total_len)); + payload.extend_from_chunks(&msg.payload); + payload.extend_from_slice(&msg.typ.to_array()); + + self.enc_key + .seal_in_place_append_tag(nonce, aad, &mut payload) + .map_err(|_| Error::EncryptError)?; + + Ok(OutboundOpaqueMessage::new( + ContentType::ApplicationData, + ProtocolVersion::TLSv1_2, + payload, + )) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + 1 + self.enc_key.algorithm().tag_len() + } +} + +struct GcmMessageDecrypter { + dec_key: aead::TlsRecordOpeningKey, + iv: Iv, +} + +impl MessageDecrypter for GcmMessageDecrypter { + fn decrypt<'a>( + &mut self, + mut msg: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result<InboundPlainMessage<'a>, Error> { + let payload = &mut msg.payload; + if payload.len() < self.dec_key.algorithm().tag_len() { + return Err(Error::DecryptError); + } + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, seq).0); + let aad = aead::Aad::from(make_tls13_aad(payload.len())); + let plain_len = self + .dec_key + .open_in_place(nonce, aad, payload) + .map_err(|_| Error::DecryptError)? + .len(); + + payload.truncate(plain_len); + msg.into_tls13_unpadded_message() + } +} + +struct AwsLcHkdf(hkdf::Algorithm, hmac::Algorithm); + +impl Hkdf for AwsLcHkdf { + fn extract_from_zero_ikm(&self, salt: Option<&[u8]>) -> Box<dyn HkdfExpander> { + let zeroes = [0u8; OkmBlock::MAX_LEN]; + let salt = match salt { + Some(salt) => salt, + None => &zeroes[..self.0.len()], + }; + Box::new(AwsLcHkdfExpander { + alg: self.0, + prk: hkdf::Salt::new(self.0, salt).extract(&zeroes[..self.0.len()]), + }) + } + + fn extract_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Box<dyn HkdfExpander> { + let zeroes = [0u8; OkmBlock::MAX_LEN]; + let salt = match salt { + Some(salt) => salt, + None => &zeroes[..self.0.len()], + }; + Box::new(AwsLcHkdfExpander { + alg: self.0, + prk: hkdf::Salt::new(self.0, salt).extract(secret), + }) + } + + fn expander_for_okm(&self, okm: &OkmBlock) -> Box<dyn HkdfExpander> { + Box::new(AwsLcHkdfExpander { + alg: self.0, + prk: hkdf::Prk::new_less_safe(self.0, okm.as_ref()), + }) + } + + fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> crypto::hmac::Tag { + crypto::hmac::Tag::new(hmac::sign(&hmac::Key::new(self.1, key.as_ref()), message).as_ref()) + } + + fn fips(&self) -> bool { + super::fips() + } +} + +struct AwsLcHkdfExpander { + alg: hkdf::Algorithm, + prk: hkdf::Prk, +} + +impl HkdfExpander for AwsLcHkdfExpander { + fn expand_slice(&self, info: &[&[u8]], output: &mut [u8]) -> Result<(), OutputLengthError> { + self.prk + .expand(info, Len(output.len())) + .and_then(|okm| okm.fill(output)) + .map_err(|_| OutputLengthError) + } + + fn expand_block(&self, info: &[&[u8]]) -> OkmBlock { + let mut buf = [0u8; OkmBlock::MAX_LEN]; + let output = &mut buf[..self.hash_len()]; + self.prk + .expand(info, Len(output.len())) + .and_then(|okm| okm.fill(output)) + .unwrap(); + OkmBlock::new(output) + } + + fn hash_len(&self) -> usize { + self.alg.len() + } +} + +struct Len(usize); + +impl KeyType for Len { + fn len(&self) -> usize { + self.0 + } +} diff --git a/vendor/rustls/src/crypto/cipher.rs b/vendor/rustls/src/crypto/cipher.rs new file mode 100644 index 00000000..64901a57 --- /dev/null +++ b/vendor/rustls/src/crypto/cipher.rs @@ -0,0 +1,350 @@ +use alloc::boxed::Box; +use alloc::string::ToString; +use core::fmt; + +use zeroize::Zeroize; + +use crate::enums::{ContentType, ProtocolVersion}; +use crate::error::Error; +use crate::msgs::codec; +pub use crate::msgs::message::{ + BorrowedPayload, InboundOpaqueMessage, InboundPlainMessage, OutboundChunks, + OutboundOpaqueMessage, OutboundPlainMessage, PlainMessage, PrefixedPayload, +}; +use crate::suites::ConnectionTrafficSecrets; + +/// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.3 cipher suite. +pub trait Tls13AeadAlgorithm: Send + Sync { + /// Build a `MessageEncrypter` for the given key/iv. + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter>; + + /// Build a `MessageDecrypter` for the given key/iv. + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter>; + + /// The length of key in bytes required by `encrypter()` and `decrypter()`. + fn key_len(&self) -> usize; + + /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item. + /// + /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported + /// variant of `ConnectionTrafficSecrets`. + fn extract_keys( + &self, + key: AeadKey, + iv: Iv, + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>; + + /// Return `true` if this is backed by a FIPS-approved implementation. + fn fips(&self) -> bool { + false + } +} + +/// Factory trait for building `MessageEncrypter` and `MessageDecrypter` for a TLS1.2 cipher suite. +pub trait Tls12AeadAlgorithm: Send + Sync + 'static { + /// Build a `MessageEncrypter` for the given key/iv and extra key block (which can be used for + /// improving explicit nonce size security, if needed). + /// + /// The length of `key` is set by [`KeyBlockShape::enc_key_len`]. + /// + /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`]. + /// + /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`]. + fn encrypter(&self, key: AeadKey, iv: &[u8], extra: &[u8]) -> Box<dyn MessageEncrypter>; + + /// Build a `MessageDecrypter` for the given key/iv. + /// + /// The length of `key` is set by [`KeyBlockShape::enc_key_len`]. + /// + /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`]. + fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter>; + + /// Return a `KeyBlockShape` that defines how large the `key_block` is and how it + /// is split up prior to calling `encrypter()`, `decrypter()` and/or `extract_keys()`. + fn key_block_shape(&self) -> KeyBlockShape; + + /// Convert the key material from `key`/`iv`, into a `ConnectionTrafficSecrets` item. + /// + /// The length of `key` is set by [`KeyBlockShape::enc_key_len`]. + /// + /// The length of `iv` is set by [`KeyBlockShape::fixed_iv_len`]. + /// + /// The length of `extra` is set by [`KeyBlockShape::explicit_nonce_len`]. + /// + /// May return [`UnsupportedOperationError`] if the AEAD algorithm is not a supported + /// variant of `ConnectionTrafficSecrets`. + fn extract_keys( + &self, + key: AeadKey, + iv: &[u8], + explicit: &[u8], + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError>; + + /// Return `true` if this is backed by a FIPS-approved implementation. + fn fips(&self) -> bool { + false + } +} + +/// An error indicating that the AEAD algorithm does not support the requested operation. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub struct UnsupportedOperationError; + +impl From<UnsupportedOperationError> for Error { + fn from(value: UnsupportedOperationError) -> Self { + Self::General(value.to_string()) + } +} + +impl fmt::Display for UnsupportedOperationError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "operation not supported") + } +} + +#[cfg(feature = "std")] +impl std::error::Error for UnsupportedOperationError {} + +/// How a TLS1.2 `key_block` is partitioned. +/// +/// Note: ciphersuites with non-zero `mac_key_length` are not currently supported. +pub struct KeyBlockShape { + /// How long keys are. + /// + /// `enc_key_length` terminology is from the standard ([RFC5246 A.6]). + /// + /// [RFC5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6> + pub enc_key_len: usize, + + /// How long the fixed part of the 'IV' is. + /// + /// `fixed_iv_length` terminology is from the standard ([RFC5246 A.6]). + /// + /// This isn't usually an IV, but we continue the + /// terminology misuse to match the standard. + /// + /// [RFC5246 A.6]: <https://www.rfc-editor.org/rfc/rfc5246#appendix-A.6> + pub fixed_iv_len: usize, + + /// This is a non-standard extension which extends the + /// key block to provide an initial explicit nonce offset, + /// in a deterministic and safe way. GCM needs this, + /// chacha20poly1305 works this way by design. + pub explicit_nonce_len: usize, +} + +/// Objects with this trait can decrypt TLS messages. +pub trait MessageDecrypter: Send + Sync { + /// Decrypt the given TLS message `msg`, using the sequence number + /// `seq` which can be used to derive a unique [`Nonce`]. + fn decrypt<'a>( + &mut self, + msg: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result<InboundPlainMessage<'a>, Error>; +} + +/// Objects with this trait can encrypt TLS messages. +pub trait MessageEncrypter: Send + Sync { + /// Encrypt the given TLS message `msg`, using the sequence number + /// `seq` which can be used to derive a unique [`Nonce`]. + fn encrypt( + &mut self, + msg: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result<OutboundOpaqueMessage, Error>; + + /// Return the length of the ciphertext that results from encrypting plaintext of + /// length `payload_len` + fn encrypted_payload_len(&self, payload_len: usize) -> usize; +} + +impl dyn MessageEncrypter { + pub(crate) fn invalid() -> Box<dyn MessageEncrypter> { + Box::new(InvalidMessageEncrypter {}) + } +} + +impl dyn MessageDecrypter { + pub(crate) fn invalid() -> Box<dyn MessageDecrypter> { + Box::new(InvalidMessageDecrypter {}) + } +} + +/// A write or read IV. +#[derive(Default)] +pub struct Iv([u8; NONCE_LEN]); + +impl Iv { + /// Create a new `Iv` from a byte array, of precisely `NONCE_LEN` bytes. + #[cfg(feature = "tls12")] + pub fn new(value: [u8; NONCE_LEN]) -> Self { + Self(value) + } + + /// Create a new `Iv` from a byte slice, of precisely `NONCE_LEN` bytes. + #[cfg(feature = "tls12")] + pub fn copy(value: &[u8]) -> Self { + debug_assert_eq!(value.len(), NONCE_LEN); + let mut iv = Self::new(Default::default()); + iv.0.copy_from_slice(value); + iv + } +} + +impl From<[u8; NONCE_LEN]> for Iv { + fn from(bytes: [u8; NONCE_LEN]) -> Self { + Self(bytes) + } +} + +impl AsRef<[u8]> for Iv { + fn as_ref(&self) -> &[u8] { + self.0.as_ref() + } +} + +/// A nonce. This is unique for all messages on a connection. +pub struct Nonce(pub [u8; NONCE_LEN]); + +impl Nonce { + /// Combine an `Iv` and sequence number to produce a unique nonce. + /// + /// This is `iv ^ seq` where `seq` is encoded as a 96-bit big-endian integer. + #[inline] + pub fn new(iv: &Iv, seq: u64) -> Self { + let mut nonce = Self([0u8; NONCE_LEN]); + codec::put_u64(seq, &mut nonce.0[4..]); + + nonce + .0 + .iter_mut() + .zip(iv.0.iter()) + .for_each(|(nonce, iv)| { + *nonce ^= *iv; + }); + + nonce + } +} + +/// Size of TLS nonces (incorrectly termed "IV" in standard) for all supported ciphersuites +/// (AES-GCM, Chacha20Poly1305) +pub const NONCE_LEN: usize = 12; + +/// Returns a TLS1.3 `additional_data` encoding. +/// +/// See RFC8446 s5.2 for the `additional_data` definition. +#[inline] +pub fn make_tls13_aad(payload_len: usize) -> [u8; 5] { + let version = ProtocolVersion::TLSv1_2.to_array(); + [ + ContentType::ApplicationData.into(), + // Note: this is `legacy_record_version`, i.e. TLS1.2 even for TLS1.3. + version[0], + version[1], + (payload_len >> 8) as u8, + (payload_len & 0xff) as u8, + ] +} + +/// Returns a TLS1.2 `additional_data` encoding. +/// +/// See RFC5246 s6.2.3.3 for the `additional_data` definition. +#[inline] +pub fn make_tls12_aad( + seq: u64, + typ: ContentType, + vers: ProtocolVersion, + len: usize, +) -> [u8; TLS12_AAD_SIZE] { + let mut out = [0; TLS12_AAD_SIZE]; + codec::put_u64(seq, &mut out[0..]); + out[8] = typ.into(); + codec::put_u16(vers.into(), &mut out[9..]); + codec::put_u16(len as u16, &mut out[11..]); + out +} + +const TLS12_AAD_SIZE: usize = 8 + 1 + 2 + 2; + +/// A key for an AEAD algorithm. +/// +/// This is a value type for a byte string up to `AeadKey::MAX_LEN` bytes in length. +pub struct AeadKey { + buf: [u8; Self::MAX_LEN], + used: usize, +} + +impl AeadKey { + #[cfg(feature = "tls12")] + pub(crate) fn new(buf: &[u8]) -> Self { + debug_assert!(buf.len() <= Self::MAX_LEN); + let mut key = Self::from([0u8; Self::MAX_LEN]); + key.buf[..buf.len()].copy_from_slice(buf); + key.used = buf.len(); + key + } + + pub(crate) fn with_length(self, len: usize) -> Self { + assert!(len <= self.used); + Self { + buf: self.buf, + used: len, + } + } + + /// Largest possible AEAD key in the ciphersuites we support. + pub(crate) const MAX_LEN: usize = 32; +} + +impl Drop for AeadKey { + fn drop(&mut self) { + self.buf.zeroize(); + } +} + +impl AsRef<[u8]> for AeadKey { + fn as_ref(&self) -> &[u8] { + &self.buf[..self.used] + } +} + +impl From<[u8; Self::MAX_LEN]> for AeadKey { + fn from(bytes: [u8; Self::MAX_LEN]) -> Self { + Self { + buf: bytes, + used: Self::MAX_LEN, + } + } +} + +/// A `MessageEncrypter` which doesn't work. +struct InvalidMessageEncrypter {} + +impl MessageEncrypter for InvalidMessageEncrypter { + fn encrypt( + &mut self, + _m: OutboundPlainMessage<'_>, + _seq: u64, + ) -> Result<OutboundOpaqueMessage, Error> { + Err(Error::EncryptError) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + } +} + +/// A `MessageDecrypter` which doesn't work. +struct InvalidMessageDecrypter {} + +impl MessageDecrypter for InvalidMessageDecrypter { + fn decrypt<'a>( + &mut self, + _m: InboundOpaqueMessage<'a>, + _seq: u64, + ) -> Result<InboundPlainMessage<'a>, Error> { + Err(Error::DecryptError) + } +} diff --git a/vendor/rustls/src/crypto/hash.rs b/vendor/rustls/src/crypto/hash.rs new file mode 100644 index 00000000..214dad4e --- /dev/null +++ b/vendor/rustls/src/crypto/hash.rs @@ -0,0 +1,81 @@ +use alloc::boxed::Box; + +pub use crate::msgs::enums::HashAlgorithm; + +/// Describes a single cryptographic hash function. +/// +/// This interface can do both one-shot and incremental hashing, using +/// [`Hash::hash()`] and [`Hash::start()`] respectively. +pub trait Hash: Send + Sync { + /// Start an incremental hash computation. + fn start(&self) -> Box<dyn Context>; + + /// Return the output of this hash function with input `data`. + fn hash(&self, data: &[u8]) -> Output; + + /// The length in bytes of this hash function's output. + fn output_len(&self) -> usize; + + /// Which hash function this is, eg, `HashAlgorithm::SHA256`. + fn algorithm(&self) -> HashAlgorithm; + + /// Return `true` if this is backed by a FIPS-approved implementation. + fn fips(&self) -> bool { + false + } +} + +/// A hash output, stored as a value. +pub struct Output { + buf: [u8; Self::MAX_LEN], + used: usize, +} + +impl Output { + /// Build a `hash::Output` from a slice of no more than `Output::MAX_LEN` bytes. + pub fn new(bytes: &[u8]) -> Self { + let mut output = Self { + buf: [0u8; Self::MAX_LEN], + used: bytes.len(), + }; + debug_assert!(bytes.len() <= Self::MAX_LEN); + output.buf[..bytes.len()].copy_from_slice(bytes); + output + } + + /// Maximum supported hash output size: supports up to SHA512. + pub const MAX_LEN: usize = 64; +} + +impl AsRef<[u8]> for Output { + fn as_ref(&self) -> &[u8] { + &self.buf[..self.used] + } +} + +/// How to incrementally compute a hash. +pub trait Context: Send + Sync { + /// Finish the computation, returning the resulting output. + /// + /// The computation remains valid, and more data can be added later with + /// [`Context::update()`]. + /// + /// Compare with [`Context::finish()`] which consumes the computation + /// and prevents any further data being added. This can be more efficient + /// because it avoids a hash context copy to apply Merkle-Damgård padding + /// (if required). + fn fork_finish(&self) -> Output; + + /// Fork the computation, producing another context that has the + /// same prefix as this one. + fn fork(&self) -> Box<dyn Context>; + + /// Terminate and finish the computation, returning the resulting output. + /// + /// Further data cannot be added after this, because the context is consumed. + /// Compare [`Context::fork_finish()`]. + fn finish(self: Box<Self>) -> Output; + + /// Add `data` to computation. + fn update(&mut self, data: &[u8]); +} diff --git a/vendor/rustls/src/crypto/hmac.rs b/vendor/rustls/src/crypto/hmac.rs new file mode 100644 index 00000000..6960b7e6 --- /dev/null +++ b/vendor/rustls/src/crypto/hmac.rs @@ -0,0 +1,73 @@ +use alloc::boxed::Box; + +use zeroize::Zeroize; + +/// A concrete HMAC implementation, for a single cryptographic hash function. +/// +/// You should have one object that implements this trait for HMAC-SHA256, another +/// for HMAC-SHA384, etc. +pub trait Hmac: Send + Sync { + /// Prepare to use `key` as a HMAC key. + fn with_key(&self, key: &[u8]) -> Box<dyn Key>; + + /// Give the length of the underlying hash function. In RFC2104 terminology this is `L`. + fn hash_output_len(&self) -> usize; + + /// Return `true` if this is backed by a FIPS-approved implementation. + fn fips(&self) -> bool { + false + } +} + +/// A HMAC tag, stored as a value. +#[derive(Clone)] +pub struct Tag { + buf: [u8; Self::MAX_LEN], + used: usize, +} + +impl Tag { + /// Build a tag by copying a byte slice. + /// + /// The slice can be up to [`Tag::MAX_LEN`] bytes in length. + pub fn new(bytes: &[u8]) -> Self { + let mut tag = Self { + buf: [0u8; Self::MAX_LEN], + used: bytes.len(), + }; + tag.buf[..bytes.len()].copy_from_slice(bytes); + tag + } + + /// Maximum supported HMAC tag size: supports up to SHA512. + pub const MAX_LEN: usize = 64; +} + +impl Drop for Tag { + fn drop(&mut self) { + self.buf.zeroize(); + } +} + +impl AsRef<[u8]> for Tag { + fn as_ref(&self) -> &[u8] { + &self.buf[..self.used] + } +} + +/// A HMAC key that is ready for use. +/// +/// The algorithm used is implicit in the `Hmac` object that produced the key. +pub trait Key: Send + Sync { + /// Calculates a tag over `data` -- a slice of byte slices. + fn sign(&self, data: &[&[u8]]) -> Tag { + self.sign_concat(&[], data, &[]) + } + + /// Calculates a tag over the concatenation of `first`, the items in `middle`, and `last`. + fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> Tag; + + /// Returns the length of the tag returned by a computation using + /// this key. + fn tag_len(&self) -> usize; +} diff --git a/vendor/rustls/src/crypto/hpke.rs b/vendor/rustls/src/crypto/hpke.rs new file mode 100644 index 00000000..62074150 --- /dev/null +++ b/vendor/rustls/src/crypto/hpke.rs @@ -0,0 +1,145 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::fmt::Debug; + +use zeroize::Zeroize; + +use crate::Error; +use crate::msgs::enums::HpkeKem; +use crate::msgs::handshake::HpkeSymmetricCipherSuite; + +/// An HPKE suite, specifying a key encapsulation mechanism and a symmetric cipher suite. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct HpkeSuite { + /// The choice of HPKE key encapsulation mechanism. + pub kem: HpkeKem, + + /// The choice of HPKE symmetric cipher suite. + /// + /// This combines a choice of authenticated encryption with additional data (AEAD) algorithm + /// and a key derivation function (KDF). + pub sym: HpkeSymmetricCipherSuite, +} + +/// An HPKE instance that can be used for base-mode single-shot encryption and decryption. +pub trait Hpke: Debug + Send + Sync { + /// Seal the provided `plaintext` to the recipient public key `pub_key` with application supplied + /// `info`, and additional data `aad`. + /// + /// Returns ciphertext that can be used with [Self::open] by the recipient to recover plaintext + /// using the same `info` and `aad` and the private key corresponding to `pub_key`. RFC 9180 + /// refers to `pub_key` as `pkR`. + fn seal( + &self, + info: &[u8], + aad: &[u8], + plaintext: &[u8], + pub_key: &HpkePublicKey, + ) -> Result<(EncapsulatedSecret, Vec<u8>), Error>; + + /// Set up a sealer context for the receiver public key `pub_key` with application supplied `info`. + /// + /// Returns both an encapsulated ciphertext and a sealer context that can be used to seal + /// messages to the recipient. RFC 9180 refers to `pub_key` as `pkR`. + fn setup_sealer( + &self, + info: &[u8], + pub_key: &HpkePublicKey, + ) -> Result<(EncapsulatedSecret, Box<dyn HpkeSealer + 'static>), Error>; + + /// Open the provided `ciphertext` using the encapsulated secret `enc`, with application + /// supplied `info`, and additional data `aad`. + /// + /// Returns plaintext if the `info` and `aad` match those used with [Self::seal], and + /// decryption with `secret_key` succeeds. RFC 9180 refers to `secret_key` as `skR`. + fn open( + &self, + enc: &EncapsulatedSecret, + info: &[u8], + aad: &[u8], + ciphertext: &[u8], + secret_key: &HpkePrivateKey, + ) -> Result<Vec<u8>, Error>; + + /// Set up an opener context for the secret key `secret_key` with application supplied `info`. + /// + /// Returns an opener context that can be used to open sealed messages encrypted to the + /// public key corresponding to `secret_key`. RFC 9180 refers to `secret_key` as `skR`. + fn setup_opener( + &self, + enc: &EncapsulatedSecret, + info: &[u8], + secret_key: &HpkePrivateKey, + ) -> Result<Box<dyn HpkeOpener + 'static>, Error>; + + /// Generate a new public key and private key pair compatible with this HPKE instance. + /// + /// Key pairs should be encoded as raw big endian fixed length integers sized based + /// on the suite's DH KEM algorithm. + fn generate_key_pair(&self) -> Result<(HpkePublicKey, HpkePrivateKey), Error>; + + /// Return whether the HPKE instance is FIPS compatible. + fn fips(&self) -> bool { + false + } + + /// Return the [HpkeSuite] that this HPKE instance supports. + fn suite(&self) -> HpkeSuite; +} + +/// An HPKE sealer context. +/// +/// This is a stateful object that can be used to seal messages for receipt by +/// a receiver. +pub trait HpkeSealer: Debug + Send + Sync + 'static { + /// Seal the provided `plaintext` with additional data `aad`, returning + /// ciphertext. + fn seal(&mut self, aad: &[u8], plaintext: &[u8]) -> Result<Vec<u8>, Error>; +} + +/// An HPKE opener context. +/// +/// This is a stateful object that can be used to open sealed messages sealed +/// by a sender. +pub trait HpkeOpener: Debug + Send + Sync + 'static { + /// Open the provided `ciphertext` with additional data `aad`, returning plaintext. + fn open(&mut self, aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, Error>; +} + +/// An HPKE public key. +#[derive(Clone, Debug)] +pub struct HpkePublicKey(pub Vec<u8>); + +/// An HPKE private key. +pub struct HpkePrivateKey(Vec<u8>); + +impl HpkePrivateKey { + /// Return the private key bytes. + pub fn secret_bytes(&self) -> &[u8] { + self.0.as_slice() + } +} + +impl From<Vec<u8>> for HpkePrivateKey { + fn from(bytes: Vec<u8>) -> Self { + Self(bytes) + } +} + +impl Drop for HpkePrivateKey { + fn drop(&mut self) { + self.0.zeroize(); + } +} + +/// An HPKE key pair, made of a matching public and private key. +pub struct HpkeKeyPair { + /// A HPKE public key. + pub public_key: HpkePublicKey, + /// A HPKE private key. + pub private_key: HpkePrivateKey, +} + +/// An encapsulated secret returned from setting up a sender or receiver context. +#[derive(Debug)] +pub struct EncapsulatedSecret(pub Vec<u8>); diff --git a/vendor/rustls/src/crypto/mod.rs b/vendor/rustls/src/crypto/mod.rs new file mode 100644 index 00000000..3ce026b9 --- /dev/null +++ b/vendor/rustls/src/crypto/mod.rs @@ -0,0 +1,756 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::fmt::Debug; + +use pki_types::PrivateKeyDer; +use zeroize::Zeroize; + +#[cfg(all(doc, feature = "tls12"))] +use crate::Tls12CipherSuite; +use crate::msgs::ffdhe_groups::FfdheGroup; +use crate::sign::SigningKey; +use crate::sync::Arc; +pub use crate::webpki::{ + WebPkiSupportedAlgorithms, verify_tls12_signature, verify_tls13_signature, + verify_tls13_signature_with_raw_key, +}; +#[cfg(doc)] +use crate::{ + ClientConfig, ConfigBuilder, ServerConfig, SupportedCipherSuite, Tls13CipherSuite, client, + crypto, server, sign, +}; +use crate::{Error, NamedGroup, ProtocolVersion, SupportedProtocolVersion, suites}; + +/// *ring* based CryptoProvider. +#[cfg(feature = "ring")] +pub mod ring; + +/// aws-lc-rs-based CryptoProvider. +#[cfg(feature = "aws_lc_rs")] +pub mod aws_lc_rs; + +/// TLS message encryption/decryption interfaces. +pub mod cipher; + +/// Hashing interfaces. +pub mod hash; + +/// HMAC interfaces. +pub mod hmac; + +#[cfg(feature = "tls12")] +/// Cryptography specific to TLS1.2. +pub mod tls12; + +/// Cryptography specific to TLS1.3. +pub mod tls13; + +/// Hybrid public key encryption (RFC 9180). +pub mod hpke; + +// Message signing interfaces. Re-exported under rustls::sign. Kept crate-internal here to +// avoid having two import paths to the same types. +pub(crate) mod signer; + +pub use crate::msgs::handshake::KeyExchangeAlgorithm; +pub use crate::rand::GetRandomFailed; +pub use crate::suites::CipherSuiteCommon; + +/// Controls core cryptography used by rustls. +/// +/// This crate comes with two built-in options, provided as +/// `CryptoProvider` structures: +/// +/// - [`crypto::aws_lc_rs::default_provider`]: (behind the `aws_lc_rs` crate feature, +/// which is enabled by default). This provider uses the [aws-lc-rs](https://github.com/aws/aws-lc-rs) +/// crate. The `fips` crate feature makes this option use FIPS140-3-approved cryptography. +/// - [`crypto::ring::default_provider`]: (behind the `ring` crate feature, which +/// is optional). This provider uses the [*ring*](https://github.com/briansmith/ring) +/// crate. +/// +/// This structure provides defaults. Everything in it can be overridden at +/// runtime by replacing field values as needed. +/// +/// # Using the per-process default `CryptoProvider` +/// +/// There is the concept of an implicit default provider, configured at run-time once in +/// a given process. +/// +/// It is used for functions like [`ClientConfig::builder()`] and [`ServerConfig::builder()`]. +/// +/// The intention is that an application can specify the [`CryptoProvider`] they wish to use +/// once, and have that apply to the variety of places where their application does TLS +/// (which may be wrapped inside other libraries). +/// They should do this by calling [`CryptoProvider::install_default()`] early on. +/// +/// To achieve this goal: +/// +/// - _libraries_ should use [`ClientConfig::builder()`]/[`ServerConfig::builder()`] +/// or otherwise rely on the [`CryptoProvider::get_default()`] provider. +/// - _applications_ should call [`CryptoProvider::install_default()`] early +/// in their `fn main()`. If _applications_ uses a custom provider based on the one built-in, +/// they can activate the `custom-provider` feature to ensure its usage. +/// +/// # Using a specific `CryptoProvider` +/// +/// Supply the provider when constructing your [`ClientConfig`] or [`ServerConfig`]: +/// +/// - [`ClientConfig::builder_with_provider()`] +/// - [`ServerConfig::builder_with_provider()`] +/// +/// When creating and configuring a webpki-backed client or server certificate verifier, a choice of +/// provider is also needed to start the configuration process: +/// +/// - [`client::WebPkiServerVerifier::builder_with_provider()`] +/// - [`server::WebPkiClientVerifier::builder_with_provider()`] +/// +/// If you install a custom provider and want to avoid any accidental use of a built-in provider, the feature +/// `custom-provider` can be activated to ensure your custom provider is used everywhere +/// and not a built-in one. This will disable any implicit use of a built-in provider. +/// +/// # Making a custom `CryptoProvider` +/// +/// Your goal will be to populate an instance of this `CryptoProvider` struct. +/// +/// ## Which elements are required? +/// +/// There is no requirement that the individual elements ([`SupportedCipherSuite`], [`SupportedKxGroup`], +/// [`SigningKey`], etc.) come from the same crate. It is allowed and expected that uninteresting +/// elements would be delegated back to one of the default providers (statically) or a parent +/// provider (dynamically). +/// +/// For example, if we want to make a provider that just overrides key loading in the config builder +/// API (with [`ConfigBuilder::with_single_cert`], etc.), it might look like this: +/// +/// ``` +/// # #[cfg(feature = "aws_lc_rs")] { +/// # use std::sync::Arc; +/// # mod fictious_hsm_api { pub fn load_private_key(key_der: pki_types::PrivateKeyDer<'static>) -> ! { unreachable!(); } } +/// use rustls::crypto::aws_lc_rs; +/// +/// pub fn provider() -> rustls::crypto::CryptoProvider { +/// rustls::crypto::CryptoProvider{ +/// key_provider: &HsmKeyLoader, +/// ..aws_lc_rs::default_provider() +/// } +/// } +/// +/// #[derive(Debug)] +/// struct HsmKeyLoader; +/// +/// impl rustls::crypto::KeyProvider for HsmKeyLoader { +/// fn load_private_key(&self, key_der: pki_types::PrivateKeyDer<'static>) -> Result<Arc<dyn rustls::sign::SigningKey>, rustls::Error> { +/// fictious_hsm_api::load_private_key(key_der) +/// } +/// } +/// # } +/// ``` +/// +/// ## References to the individual elements +/// +/// The elements are documented separately: +/// +/// - **Random** - see [`crypto::SecureRandom::fill()`]. +/// - **Cipher suites** - see [`SupportedCipherSuite`], [`Tls12CipherSuite`], and +/// [`Tls13CipherSuite`]. +/// - **Key exchange groups** - see [`crypto::SupportedKxGroup`]. +/// - **Signature verification algorithms** - see [`crypto::WebPkiSupportedAlgorithms`]. +/// - **Authentication key loading** - see [`crypto::KeyProvider::load_private_key()`] and +/// [`sign::SigningKey`]. +/// +/// # Example code +/// +/// See custom [`provider-example/`] for a full client and server example that uses +/// cryptography from the [`RustCrypto`] and [`dalek-cryptography`] projects. +/// +/// ```shell +/// $ cargo run --example client | head -3 +/// Current ciphersuite: TLS13_CHACHA20_POLY1305_SHA256 +/// HTTP/1.1 200 OK +/// Content-Type: text/html; charset=utf-8 +/// Content-Length: 19899 +/// ``` +/// +/// [`provider-example/`]: https://github.com/rustls/rustls/tree/main/provider-example/ +/// [`RustCrypto`]: https://github.com/RustCrypto +/// [`dalek-cryptography`]: https://github.com/dalek-cryptography +/// +/// # FIPS-approved cryptography +/// The `fips` crate feature enables use of the `aws-lc-rs` crate in FIPS mode. +/// +/// You can verify the configuration at runtime by checking +/// [`ServerConfig::fips()`]/[`ClientConfig::fips()`] return `true`. +#[derive(Debug, Clone)] +pub struct CryptoProvider { + /// List of supported ciphersuites, in preference order -- the first element + /// is the highest priority. + /// + /// The `SupportedCipherSuite` type carries both configuration and implementation. + /// + /// A valid `CryptoProvider` must ensure that all cipher suites are accompanied by at least + /// one matching key exchange group in [`CryptoProvider::kx_groups`]. + pub cipher_suites: Vec<suites::SupportedCipherSuite>, + + /// List of supported key exchange groups, in preference order -- the + /// first element is the highest priority. + /// + /// The first element in this list is the _default key share algorithm_, + /// and in TLS1.3 a key share for it is sent in the client hello. + /// + /// The `SupportedKxGroup` type carries both configuration and implementation. + pub kx_groups: Vec<&'static dyn SupportedKxGroup>, + + /// List of signature verification algorithms for use with webpki. + /// + /// These are used for both certificate chain verification and handshake signature verification. + /// + /// This is called by [`ConfigBuilder::with_root_certificates()`], + /// [`server::WebPkiClientVerifier::builder_with_provider()`] and + /// [`client::WebPkiServerVerifier::builder_with_provider()`]. + pub signature_verification_algorithms: WebPkiSupportedAlgorithms, + + /// Source of cryptographically secure random numbers. + pub secure_random: &'static dyn SecureRandom, + + /// Provider for loading private [`SigningKey`]s from [`PrivateKeyDer`]. + pub key_provider: &'static dyn KeyProvider, +} + +impl CryptoProvider { + /// Sets this `CryptoProvider` as the default for this process. + /// + /// This can be called successfully at most once in any process execution. + /// + /// Call this early in your process to configure which provider is used for + /// the provider. The configuration should happen before any use of + /// [`ClientConfig::builder()`] or [`ServerConfig::builder()`]. + pub fn install_default(self) -> Result<(), Arc<Self>> { + static_default::install_default(self) + } + + /// Returns the default `CryptoProvider` for this process. + /// + /// This will be `None` if no default has been set yet. + pub fn get_default() -> Option<&'static Arc<Self>> { + static_default::get_default() + } + + /// An internal function that: + /// + /// - gets the pre-installed default, or + /// - installs one `from_crate_features()`, or else + /// - panics about the need to call [`CryptoProvider::install_default()`] + pub(crate) fn get_default_or_install_from_crate_features() -> &'static Arc<Self> { + if let Some(provider) = Self::get_default() { + return provider; + } + + let provider = Self::from_crate_features() + .expect("no process-level CryptoProvider available -- call CryptoProvider::install_default() before this point"); + // Ignore the error resulting from us losing a race, and accept the outcome. + let _ = provider.install_default(); + Self::get_default().unwrap() + } + + /// Returns a provider named unambiguously by rustls crate features. + /// + /// This function returns `None` if the crate features are ambiguous (ie, specify two + /// providers), or specify no providers, or the feature `custom-provider` is activated. + /// In all cases the application should explicitly specify the provider to use + /// with [`CryptoProvider::install_default`]. + fn from_crate_features() -> Option<Self> { + #[cfg(all( + feature = "ring", + not(feature = "aws_lc_rs"), + not(feature = "custom-provider") + ))] + { + return Some(ring::default_provider()); + } + + #[cfg(all( + feature = "aws_lc_rs", + not(feature = "ring"), + not(feature = "custom-provider") + ))] + { + return Some(aws_lc_rs::default_provider()); + } + + #[allow(unreachable_code)] + None + } + + /// Returns `true` if this `CryptoProvider` is operating in FIPS mode. + /// + /// This covers only the cryptographic parts of FIPS approval. There are + /// also TLS protocol-level recommendations made by NIST. You should + /// prefer to call [`ClientConfig::fips()`] or [`ServerConfig::fips()`] + /// which take these into account. + pub fn fips(&self) -> bool { + let Self { + cipher_suites, + kx_groups, + signature_verification_algorithms, + secure_random, + key_provider, + } = self; + cipher_suites.iter().all(|cs| cs.fips()) + && kx_groups.iter().all(|kx| kx.fips()) + && signature_verification_algorithms.fips() + && secure_random.fips() + && key_provider.fips() + } +} + +/// A source of cryptographically secure randomness. +pub trait SecureRandom: Send + Sync + Debug { + /// Fill the given buffer with random bytes. + /// + /// The bytes must be sourced from a cryptographically secure random number + /// generator seeded with good quality, secret entropy. + /// + /// This is used for all randomness required by rustls, but not necessarily + /// randomness required by the underlying cryptography library. For example: + /// [`SupportedKxGroup::start()`] requires random material to generate + /// an ephemeral key exchange key, but this is not included in the interface with + /// rustls: it is assumed that the cryptography library provides for this itself. + fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed>; + + /// Return `true` if this is backed by a FIPS-approved implementation. + fn fips(&self) -> bool { + false + } +} + +/// A mechanism for loading private [`SigningKey`]s from [`PrivateKeyDer`]. +/// +/// This trait is intended to be used with private key material that is sourced from DER, +/// such as a private-key that may be present on-disk. It is not intended to be used with +/// keys held in hardware security modules (HSMs) or physical tokens. For these use-cases +/// see the Rustls manual section on [customizing private key usage]. +/// +/// [customizing private key usage]: <https://docs.rs/rustls/latest/rustls/manual/_03_howto/index.html#customising-private-key-usage> +pub trait KeyProvider: Send + Sync + Debug { + /// Decode and validate a private signing key from `key_der`. + /// + /// This is used by [`ConfigBuilder::with_client_auth_cert()`], [`ConfigBuilder::with_single_cert()`], + /// and [`ConfigBuilder::with_single_cert_with_ocsp()`]. The key types and formats supported by this + /// function directly defines the key types and formats supported in those APIs. + /// + /// Return an error if the key type encoding is not supported, or if the key fails validation. + fn load_private_key( + &self, + key_der: PrivateKeyDer<'static>, + ) -> Result<Arc<dyn SigningKey>, Error>; + + /// Return `true` if this is backed by a FIPS-approved implementation. + /// + /// If this returns `true`, that must be the case for all possible key types + /// supported by [`KeyProvider::load_private_key()`]. + fn fips(&self) -> bool { + false + } +} + +/// A supported key exchange group. +/// +/// This type carries both configuration and implementation. Specifically, +/// it has a TLS-level name expressed using the [`NamedGroup`] enum, and +/// a function which produces a [`ActiveKeyExchange`]. +/// +/// Compare with [`NamedGroup`], which carries solely a protocol identifier. +pub trait SupportedKxGroup: Send + Sync + Debug { + /// Start a key exchange. + /// + /// This will prepare an ephemeral secret key in the supported group, and a corresponding + /// public key. The key exchange can be completed by calling [ActiveKeyExchange#complete] + /// or discarded. + /// + /// # Errors + /// + /// This can fail if the random source fails during ephemeral key generation. + fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error>; + + /// Start and complete a key exchange, in one operation. + /// + /// The default implementation for this calls `start()` and then calls + /// `complete()` on the result. This is suitable for Diffie-Hellman-like + /// key exchange algorithms, where there is not a data dependency between + /// our key share (named "pub_key" in this API) and the peer's (`peer_pub_key`). + /// + /// If there is such a data dependency (like key encapsulation mechanisms), this + /// function should be implemented. + fn start_and_complete(&self, peer_pub_key: &[u8]) -> Result<CompletedKeyExchange, Error> { + let kx = self.start()?; + + Ok(CompletedKeyExchange { + group: kx.group(), + pub_key: kx.pub_key().to_vec(), + secret: kx.complete(peer_pub_key)?, + }) + } + + /// FFDHE group the `SupportedKxGroup` operates in. + /// + /// Return `None` if this group is not a FFDHE one. + /// + /// The default implementation calls `FfdheGroup::from_named_group`: this function + /// is extremely linker-unfriendly so it is recommended all key exchange implementers + /// provide this function. + /// + /// `rustls::ffdhe_groups` contains suitable values to return from this, + /// for example [`rustls::ffdhe_groups::FFDHE2048`][crate::ffdhe_groups::FFDHE2048]. + fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> { + #[allow(deprecated)] + FfdheGroup::from_named_group(self.name()) + } + + /// Named group the SupportedKxGroup operates in. + /// + /// If the `NamedGroup` enum does not have a name for the algorithm you are implementing, + /// you can use [`NamedGroup::Unknown`]. + fn name(&self) -> NamedGroup; + + /// Return `true` if this is backed by a FIPS-approved implementation. + fn fips(&self) -> bool { + false + } + + /// Return `true` if this should be offered/selected with the given version. + /// + /// The default implementation returns true for all versions. + fn usable_for_version(&self, _version: ProtocolVersion) -> bool { + true + } +} + +/// An in-progress key exchange originating from a [`SupportedKxGroup`]. +pub trait ActiveKeyExchange: Send + Sync { + /// Completes the key exchange, given the peer's public key. + /// + /// This method must return an error if `peer_pub_key` is invalid: either + /// mis-encoded, or an invalid public key (such as, but not limited to, being + /// in a small order subgroup). + /// + /// If the key exchange algorithm is FFDHE, the result must be left-padded with zeros, + /// as required by [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446#section-7.4.1) + /// (see [`complete_for_tls_version()`](Self::complete_for_tls_version) for more details). + /// + /// The shared secret is returned as a [`SharedSecret`] which can be constructed + /// from a `&[u8]`. + /// + /// This consumes and so terminates the [`ActiveKeyExchange`]. + fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error>; + + /// Completes the key exchange for the given TLS version, given the peer's public key. + /// + /// Note that finite-field Diffie–Hellman key exchange has different requirements for the derived + /// shared secret in TLS 1.2 and TLS 1.3 (ECDHE key exchange is the same in TLS 1.2 and TLS 1.3): + /// + /// In TLS 1.2, the calculated secret is required to be stripped of leading zeros + /// [(RFC 5246)](https://www.rfc-editor.org/rfc/rfc5246#section-8.1.2). + /// + /// In TLS 1.3, the calculated secret is required to be padded with leading zeros to be the same + /// byte-length as the group modulus [(RFC 8446)](https://www.rfc-editor.org/rfc/rfc8446#section-7.4.1). + /// + /// The default implementation of this method delegates to [`complete()`](Self::complete) assuming it is + /// implemented for TLS 1.3 (i.e., for FFDHE KX, removes padding as needed). Implementers of this trait + /// are encouraged to just implement [`complete()`](Self::complete) assuming TLS 1.3, and let the default + /// implementation of this method handle TLS 1.2-specific requirements. + /// + /// This method must return an error if `peer_pub_key` is invalid: either + /// mis-encoded, or an invalid public key (such as, but not limited to, being + /// in a small order subgroup). + /// + /// The shared secret is returned as a [`SharedSecret`] which can be constructed + /// from a `&[u8]`. + /// + /// This consumes and so terminates the [`ActiveKeyExchange`]. + fn complete_for_tls_version( + self: Box<Self>, + peer_pub_key: &[u8], + tls_version: &SupportedProtocolVersion, + ) -> Result<SharedSecret, Error> { + if tls_version.version != ProtocolVersion::TLSv1_2 { + return self.complete(peer_pub_key); + } + + let group = self.group(); + let mut complete_res = self.complete(peer_pub_key)?; + if group.key_exchange_algorithm() == KeyExchangeAlgorithm::DHE { + complete_res.strip_leading_zeros(); + } + Ok(complete_res) + } + + /// For hybrid key exchanges, returns the [`NamedGroup`] and key share + /// for the classical half of this key exchange. + /// + /// There is no requirement for a hybrid scheme (or any other!) to implement + /// `hybrid_component()`. It only enables an optimization; described below. + /// + /// "Hybrid" means a key exchange algorithm which is constructed from two + /// (or more) independent component algorithms. Usually one is post-quantum-secure, + /// and the other is "classical". See + /// <https://datatracker.ietf.org/doc/draft-ietf-tls-hybrid-design/11/> + /// + /// # Background + /// Rustls always sends a presumptive key share in its `ClientHello`, using + /// (absent any other information) the first item in [`CryptoProvider::kx_groups`]. + /// If the server accepts the client's selection, it can complete the handshake + /// using that key share. If not, the server sends a `HelloRetryRequest` instructing + /// the client to send a different key share instead. + /// + /// This request costs an extra round trip, and wastes the key exchange computation + /// (in [`SupportedKxGroup::start()`]) the client already did. We would + /// like to avoid those wastes if possible. + /// + /// It is early days for post-quantum-secure hybrid key exchange deployment. + /// This means (commonly) continuing to offer both the hybrid and classical + /// key exchanges, so the handshake can be completed without a `HelloRetryRequest` + /// for servers that support the offered hybrid or classical schemes. + /// + /// Implementing `hybrid_component()` enables two optimizations: + /// + /// 1. Sending both the hybrid and classical key shares in the `ClientHello`. + /// + /// 2. Performing the classical key exchange setup only once. This is important + /// because the classical key exchange setup is relatively expensive. + /// This optimization is permitted and described in + /// <https://www.ietf.org/archive/id/draft-ietf-tls-hybrid-design-11.html#section-3.2> + /// + /// Both of these only happen if the classical algorithm appears separately in + /// the client's [`CryptoProvider::kx_groups`], and if the hybrid algorithm appears + /// first in that list. + /// + /// # How it works + /// This function is only called by rustls for clients. It is called when + /// constructing the initial `ClientHello`. rustls follows these steps: + /// + /// 1. If the return value is `None`, nothing further happens. + /// 2. If the given [`NamedGroup`] does not appear in + /// [`CryptoProvider::kx_groups`], nothing further happens. + /// 3. The given key share is added to the `ClientHello`, after the hybrid entry. + /// + /// Then, one of three things may happen when the server replies to the `ClientHello`: + /// + /// 1. The server sends a `HelloRetryRequest`. Everything is thrown away and + /// we start again. + /// 2. The server agrees to our hybrid key exchange: rustls calls + /// [`ActiveKeyExchange::complete()`] consuming `self`. + /// 3. The server agrees to our classical key exchange: rustls calls + /// [`ActiveKeyExchange::complete_hybrid_component()`] which + /// discards the hybrid key data, and completes just the classical key exchange. + fn hybrid_component(&self) -> Option<(NamedGroup, &[u8])> { + None + } + + /// Completes the classical component of the key exchange, given the peer's public key. + /// + /// This is only called if `hybrid_component` returns `Some(_)`. + /// + /// This method must return an error if `peer_pub_key` is invalid: either + /// mis-encoded, or an invalid public key (such as, but not limited to, being + /// in a small order subgroup). + /// + /// The shared secret is returned as a [`SharedSecret`] which can be constructed + /// from a `&[u8]`. + /// + /// See the documentation on [`Self::hybrid_component()`] for explanation. + fn complete_hybrid_component( + self: Box<Self>, + _peer_pub_key: &[u8], + ) -> Result<SharedSecret, Error> { + unreachable!("only called if `hybrid_component()` implemented") + } + + /// Return the public key being used. + /// + /// For ECDHE, the encoding required is defined in + /// [RFC8446 section 4.2.8.2](https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.2). + /// + /// For FFDHE, the encoding required is defined in + /// [RFC8446 section 4.2.8.1](https://www.rfc-editor.org/rfc/rfc8446#section-4.2.8.1). + fn pub_key(&self) -> &[u8]; + + /// FFDHE group the `ActiveKeyExchange` is operating in. + /// + /// Return `None` if this group is not a FFDHE one. + /// + /// The default implementation calls `FfdheGroup::from_named_group`: this function + /// is extremely linker-unfriendly so it is recommended all key exchange implementers + /// provide this function. + /// + /// `rustls::ffdhe_groups` contains suitable values to return from this, + /// for example [`rustls::ffdhe_groups::FFDHE2048`][crate::ffdhe_groups::FFDHE2048]. + fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> { + #[allow(deprecated)] + FfdheGroup::from_named_group(self.group()) + } + + /// Return the group being used. + fn group(&self) -> NamedGroup; +} + +/// The result from [`SupportedKxGroup::start_and_complete()`]. +pub struct CompletedKeyExchange { + /// Which group was used. + pub group: NamedGroup, + + /// Our key share (sometimes a public key). + pub pub_key: Vec<u8>, + + /// The computed shared secret. + pub secret: SharedSecret, +} + +/// The result from [`ActiveKeyExchange::complete`] or [`ActiveKeyExchange::complete_hybrid_component`]. +pub struct SharedSecret { + buf: Vec<u8>, + offset: usize, +} + +impl SharedSecret { + /// Returns the shared secret as a slice of bytes. + pub fn secret_bytes(&self) -> &[u8] { + &self.buf[self.offset..] + } + + /// Removes leading zeros from `secret_bytes()` by adjusting the `offset`. + /// + /// This function does not re-allocate. + fn strip_leading_zeros(&mut self) { + let start = self + .secret_bytes() + .iter() + .enumerate() + .find(|(_i, x)| **x != 0) + .map(|(i, _x)| i) + .unwrap_or(self.secret_bytes().len()); + self.offset += start; + } +} + +impl Drop for SharedSecret { + fn drop(&mut self) { + self.buf.zeroize(); + } +} + +impl From<&[u8]> for SharedSecret { + fn from(source: &[u8]) -> Self { + Self { + buf: source.to_vec(), + offset: 0, + } + } +} + +impl From<Vec<u8>> for SharedSecret { + fn from(buf: Vec<u8>) -> Self { + Self { buf, offset: 0 } + } +} + +/// This function returns a [`CryptoProvider`] that uses +/// FIPS140-3-approved cryptography. +/// +/// Using this function expresses in your code that you require +/// FIPS-approved cryptography, and will not compile if you make +/// a mistake with cargo features. +/// +/// See our [FIPS documentation](crate::manual::_06_fips) for +/// more detail. +/// +/// Install this as the process-default provider, like: +/// +/// ```rust +/// # #[cfg(feature = "fips")] { +/// rustls::crypto::default_fips_provider().install_default() +/// .expect("default provider already set elsewhere"); +/// # } +/// ``` +/// +/// You can also use this explicitly, like: +/// +/// ```rust +/// # #[cfg(feature = "fips")] { +/// # let root_store = rustls::RootCertStore::empty(); +/// let config = rustls::ClientConfig::builder_with_provider( +/// rustls::crypto::default_fips_provider().into() +/// ) +/// .with_safe_default_protocol_versions() +/// .unwrap() +/// .with_root_certificates(root_store) +/// .with_no_client_auth(); +/// # } +/// ``` +#[cfg(all(feature = "aws_lc_rs", any(feature = "fips", docsrs)))] +#[cfg_attr(docsrs, doc(cfg(feature = "fips")))] +pub fn default_fips_provider() -> CryptoProvider { + aws_lc_rs::default_provider() +} + +mod static_default { + #[cfg(not(feature = "std"))] + use alloc::boxed::Box; + #[cfg(feature = "std")] + use std::sync::OnceLock; + + #[cfg(not(feature = "std"))] + use once_cell::race::OnceBox; + + use super::CryptoProvider; + use crate::sync::Arc; + + #[cfg(feature = "std")] + pub(crate) fn install_default( + default_provider: CryptoProvider, + ) -> Result<(), Arc<CryptoProvider>> { + PROCESS_DEFAULT_PROVIDER.set(Arc::new(default_provider)) + } + + #[cfg(not(feature = "std"))] + pub(crate) fn install_default( + default_provider: CryptoProvider, + ) -> Result<(), Arc<CryptoProvider>> { + PROCESS_DEFAULT_PROVIDER + .set(Box::new(Arc::new(default_provider))) + .map_err(|e| *e) + } + + pub(crate) fn get_default() -> Option<&'static Arc<CryptoProvider>> { + PROCESS_DEFAULT_PROVIDER.get() + } + + #[cfg(feature = "std")] + static PROCESS_DEFAULT_PROVIDER: OnceLock<Arc<CryptoProvider>> = OnceLock::new(); + #[cfg(not(feature = "std"))] + static PROCESS_DEFAULT_PROVIDER: OnceBox<Arc<CryptoProvider>> = OnceBox::new(); +} + +#[cfg(test)] +mod tests { + use std::vec; + + use super::SharedSecret; + + #[test] + fn test_shared_secret_strip_leading_zeros() { + let test_cases = [ + (vec![0, 1], vec![1]), + (vec![1], vec![1]), + (vec![1, 0, 2], vec![1, 0, 2]), + (vec![0, 0, 1, 2], vec![1, 2]), + (vec![0, 0, 0], vec![]), + (vec![], vec![]), + ]; + for (buf, expected) in test_cases { + let mut secret = SharedSecret::from(&buf[..]); + assert_eq!(secret.secret_bytes(), buf); + secret.strip_leading_zeros(); + assert_eq!(secret.secret_bytes(), expected); + } + } +} diff --git a/vendor/rustls/src/crypto/ring/hash.rs b/vendor/rustls/src/crypto/ring/hash.rs new file mode 100644 index 00000000..220dc536 --- /dev/null +++ b/vendor/rustls/src/crypto/ring/hash.rs @@ -0,0 +1,60 @@ +#![allow(clippy::duplicate_mod)] + +use alloc::boxed::Box; + +use super::ring_like::digest; +use crate::crypto; +use crate::msgs::enums::HashAlgorithm; + +pub(crate) static SHA256: Hash = Hash(&digest::SHA256, HashAlgorithm::SHA256); +pub(crate) static SHA384: Hash = Hash(&digest::SHA384, HashAlgorithm::SHA384); + +pub(crate) struct Hash(&'static digest::Algorithm, HashAlgorithm); + +impl crypto::hash::Hash for Hash { + fn start(&self) -> Box<dyn crypto::hash::Context> { + Box::new(Context(digest::Context::new(self.0))) + } + + fn hash(&self, bytes: &[u8]) -> crypto::hash::Output { + let mut ctx = digest::Context::new(self.0); + ctx.update(bytes); + convert(ctx.finish()) + } + + fn output_len(&self) -> usize { + self.0.output_len() + } + + fn algorithm(&self) -> HashAlgorithm { + self.1 + } + + fn fips(&self) -> bool { + super::fips() + } +} + +struct Context(digest::Context); + +impl crypto::hash::Context for Context { + fn fork_finish(&self) -> crypto::hash::Output { + convert(self.0.clone().finish()) + } + + fn fork(&self) -> Box<dyn crypto::hash::Context> { + Box::new(Self(self.0.clone())) + } + + fn finish(self: Box<Self>) -> crypto::hash::Output { + convert(self.0.finish()) + } + + fn update(&mut self, data: &[u8]) { + self.0.update(data); + } +} + +fn convert(val: digest::Digest) -> crypto::hash::Output { + crypto::hash::Output::new(val.as_ref()) +} diff --git a/vendor/rustls/src/crypto/ring/hmac.rs b/vendor/rustls/src/crypto/ring/hmac.rs new file mode 100644 index 00000000..7f30aba4 --- /dev/null +++ b/vendor/rustls/src/crypto/ring/hmac.rs @@ -0,0 +1,48 @@ +#![allow(clippy::duplicate_mod)] + +use alloc::boxed::Box; + +use super::ring_like; +use crate::crypto; + +pub(crate) static HMAC_SHA256: Hmac = Hmac(&ring_like::hmac::HMAC_SHA256); +pub(crate) static HMAC_SHA384: Hmac = Hmac(&ring_like::hmac::HMAC_SHA384); +#[allow(dead_code)] // Only used for TLS 1.2 prf test, and aws-lc-rs HPKE suites. +pub(crate) static HMAC_SHA512: Hmac = Hmac(&ring_like::hmac::HMAC_SHA512); + +pub(crate) struct Hmac(&'static ring_like::hmac::Algorithm); + +impl crypto::hmac::Hmac for Hmac { + fn with_key(&self, key: &[u8]) -> Box<dyn crypto::hmac::Key> { + Box::new(Key(ring_like::hmac::Key::new(*self.0, key))) + } + + fn hash_output_len(&self) -> usize { + self.0.digest_algorithm().output_len() + } + + fn fips(&self) -> bool { + super::fips() + } +} + +struct Key(ring_like::hmac::Key); + +impl crypto::hmac::Key for Key { + fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> crypto::hmac::Tag { + let mut ctx = ring_like::hmac::Context::with_key(&self.0); + ctx.update(first); + for d in middle { + ctx.update(d); + } + ctx.update(last); + crypto::hmac::Tag::new(ctx.sign().as_ref()) + } + + fn tag_len(&self) -> usize { + self.0 + .algorithm() + .digest_algorithm() + .output_len() + } +} diff --git a/vendor/rustls/src/crypto/ring/kx.rs b/vendor/rustls/src/crypto/ring/kx.rs new file mode 100644 index 00000000..6e0da227 --- /dev/null +++ b/vendor/rustls/src/crypto/ring/kx.rs @@ -0,0 +1,188 @@ +#![allow(clippy::duplicate_mod)] + +use alloc::boxed::Box; +use core::fmt; + +use super::ring_like::agreement; +use super::ring_like::rand::SystemRandom; +use crate::crypto::{ActiveKeyExchange, FfdheGroup, SharedSecret, SupportedKxGroup}; +use crate::error::{Error, PeerMisbehaved}; +use crate::msgs::enums::NamedGroup; +use crate::rand::GetRandomFailed; + +/// A key-exchange group supported by *ring*. +struct KxGroup { + /// The IANA "TLS Supported Groups" name of the group + name: NamedGroup, + + /// The corresponding ring agreement::Algorithm + agreement_algorithm: &'static agreement::Algorithm, + + /// Whether the algorithm is allowed by FIPS + /// + /// `SupportedKxGroup::fips()` is true if and only if the algorithm is allowed, + /// _and_ the implementation is FIPS-validated. + fips_allowed: bool, + + /// aws-lc-rs 1.9 and later accepts more formats of public keys than + /// just uncompressed. + /// + /// That is not compatible with TLS: + /// - TLS1.3 outlaws other encodings, + /// - TLS1.2 negotiates other encodings (we only offer uncompressed), and + /// defaults to uncompressed if negotiation is not done. + /// + /// This function should return `true` if the basic shape of its argument + /// is consistent with an uncompressed point encoding. It does not need + /// to verify that the point is on the curve (if the curve requires that + /// for security); aws-lc-rs/ring must do that. + pub_key_validator: fn(&[u8]) -> bool, +} + +impl SupportedKxGroup for KxGroup { + fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> { + let rng = SystemRandom::new(); + let priv_key = agreement::EphemeralPrivateKey::generate(self.agreement_algorithm, &rng) + .map_err(|_| GetRandomFailed)?; + + let pub_key = priv_key + .compute_public_key() + .map_err(|_| GetRandomFailed)?; + + Ok(Box::new(KeyExchange { + name: self.name, + agreement_algorithm: self.agreement_algorithm, + priv_key, + pub_key, + pub_key_validator: self.pub_key_validator, + })) + } + + fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> { + None + } + + fn name(&self) -> NamedGroup { + self.name + } + + fn fips(&self) -> bool { + self.fips_allowed && super::fips() + } +} + +impl fmt::Debug for KxGroup { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.name.fmt(f) + } +} + +/// Ephemeral ECDH on curve25519 (see RFC7748) +pub static X25519: &dyn SupportedKxGroup = &KxGroup { + name: NamedGroup::X25519, + agreement_algorithm: &agreement::X25519, + + // "Curves that are included in SP 800-186 but not included in SP 800-56Arev3 are + // not approved for key agreement. E.g., the ECDH X25519 and X448 key agreement + // schemes (defined in RFC 7748) that use Curve25519 and Curve448, respectively, + // are not compliant to SP 800-56Arev3." + // -- <https://csrc.nist.gov/csrc/media/Projects/cryptographic-module-validation-program/documents/fips%20140-3/FIPS%20140-3%20IG.pdf> + fips_allowed: false, + + pub_key_validator: |point: &[u8]| point.len() == 32, +}; + +/// Ephemeral ECDH on secp256r1 (aka NIST-P256) +pub static SECP256R1: &dyn SupportedKxGroup = &KxGroup { + name: NamedGroup::secp256r1, + agreement_algorithm: &agreement::ECDH_P256, + fips_allowed: true, + pub_key_validator: uncompressed_point, +}; + +/// Ephemeral ECDH on secp384r1 (aka NIST-P384) +pub static SECP384R1: &dyn SupportedKxGroup = &KxGroup { + name: NamedGroup::secp384r1, + agreement_algorithm: &agreement::ECDH_P384, + fips_allowed: true, + pub_key_validator: uncompressed_point, +}; + +fn uncompressed_point(point: &[u8]) -> bool { + // See `UncompressedPointRepresentation`, which is a retelling of + // SEC1 section 2.3.3 "Elliptic-Curve-Point-to-Octet-String Conversion" + // <https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.8.2> + matches!(point.first(), Some(0x04)) +} + +/// An in-progress key exchange. This has the algorithm, +/// our private key, and our public key. +struct KeyExchange { + name: NamedGroup, + agreement_algorithm: &'static agreement::Algorithm, + priv_key: agreement::EphemeralPrivateKey, + pub_key: agreement::PublicKey, + pub_key_validator: fn(&[u8]) -> bool, +} + +impl ActiveKeyExchange for KeyExchange { + /// Completes the key exchange, given the peer's public key. + fn complete(self: Box<Self>, peer: &[u8]) -> Result<SharedSecret, Error> { + if !(self.pub_key_validator)(peer) { + return Err(PeerMisbehaved::InvalidKeyShare.into()); + } + let peer_key = agreement::UnparsedPublicKey::new(self.agreement_algorithm, peer); + super::ring_shim::agree_ephemeral(self.priv_key, &peer_key) + .map_err(|_| PeerMisbehaved::InvalidKeyShare.into()) + } + + fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> { + None + } + + /// Return the group being used. + fn group(&self) -> NamedGroup { + self.name + } + + /// Return the public key being used. + fn pub_key(&self) -> &[u8] { + self.pub_key.as_ref() + } +} + +#[cfg(test)] +mod tests { + use std::format; + + #[test] + fn kxgroup_fmt_yields_name() { + assert_eq!("X25519", format!("{:?}", super::X25519)); + } +} + +#[cfg(bench)] +mod benchmarks { + #[bench] + fn bench_x25519(b: &mut test::Bencher) { + bench_any(b, super::X25519); + } + + #[bench] + fn bench_ecdh_p256(b: &mut test::Bencher) { + bench_any(b, super::SECP256R1); + } + + #[bench] + fn bench_ecdh_p384(b: &mut test::Bencher) { + bench_any(b, super::SECP384R1); + } + + fn bench_any(b: &mut test::Bencher, kxg: &dyn super::SupportedKxGroup) { + b.iter(|| { + let akx = kxg.start().unwrap(); + let pub_key = akx.pub_key().to_vec(); + test::black_box(akx.complete(&pub_key).unwrap()); + }); + } +} diff --git a/vendor/rustls/src/crypto/ring/mod.rs b/vendor/rustls/src/crypto/ring/mod.rs new file mode 100644 index 00000000..3ce99b15 --- /dev/null +++ b/vendor/rustls/src/crypto/ring/mod.rs @@ -0,0 +1,203 @@ +use pki_types::PrivateKeyDer; +pub(crate) use ring as ring_like; +use webpki::ring as webpki_algs; + +use crate::Error; +use crate::crypto::{CryptoProvider, KeyProvider, SecureRandom, SupportedKxGroup}; +use crate::enums::SignatureScheme; +use crate::rand::GetRandomFailed; +use crate::sign::SigningKey; +use crate::suites::SupportedCipherSuite; +use crate::sync::Arc; +use crate::webpki::WebPkiSupportedAlgorithms; + +/// Using software keys for authentication. +pub mod sign; + +pub(crate) mod hash; +#[cfg(any(test, feature = "tls12"))] +pub(crate) mod hmac; +pub(crate) mod kx; +pub(crate) mod quic; +#[cfg(feature = "std")] +pub(crate) mod ticketer; +#[cfg(feature = "tls12")] +pub(crate) mod tls12; +pub(crate) mod tls13; + +/// A `CryptoProvider` backed by the [*ring*] crate. +/// +/// [*ring*]: https://github.com/briansmith/ring +pub fn default_provider() -> CryptoProvider { + CryptoProvider { + cipher_suites: DEFAULT_CIPHER_SUITES.to_vec(), + kx_groups: DEFAULT_KX_GROUPS.to_vec(), + signature_verification_algorithms: SUPPORTED_SIG_ALGS, + secure_random: &Ring, + key_provider: &Ring, + } +} + +/// Default crypto provider. +#[derive(Debug)] +struct Ring; + +impl SecureRandom for Ring { + fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed> { + use ring_like::rand::SecureRandom; + + ring_like::rand::SystemRandom::new() + .fill(buf) + .map_err(|_| GetRandomFailed) + } +} + +impl KeyProvider for Ring { + fn load_private_key( + &self, + key_der: PrivateKeyDer<'static>, + ) -> Result<Arc<dyn SigningKey>, Error> { + sign::any_supported_type(&key_der) + } +} + +/// The cipher suite configuration that an application should use by default. +/// +/// This will be [`ALL_CIPHER_SUITES`] sans any supported cipher suites that +/// shouldn't be enabled by most applications. +pub static DEFAULT_CIPHER_SUITES: &[SupportedCipherSuite] = ALL_CIPHER_SUITES; + +/// A list of all the cipher suites supported by the rustls *ring* provider. +pub static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = &[ + // TLS1.3 suites + tls13::TLS13_AES_256_GCM_SHA384, + tls13::TLS13_AES_128_GCM_SHA256, + tls13::TLS13_CHACHA20_POLY1305_SHA256, + // TLS1.2 suites + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + #[cfg(feature = "tls12")] + tls12::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, +]; + +/// All defined cipher suites supported by *ring* appear in this module. +pub mod cipher_suite { + #[cfg(feature = "tls12")] + pub use super::tls12::{ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + }; + pub use super::tls13::{ + TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384, TLS13_CHACHA20_POLY1305_SHA256, + }; +} + +/// A `WebPkiSupportedAlgorithms` value that reflects webpki's capabilities when +/// compiled against *ring*. +static SUPPORTED_SIG_ALGS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { + all: &[ + webpki_algs::ECDSA_P256_SHA256, + webpki_algs::ECDSA_P256_SHA384, + webpki_algs::ECDSA_P384_SHA256, + webpki_algs::ECDSA_P384_SHA384, + webpki_algs::ED25519, + webpki_algs::RSA_PSS_2048_8192_SHA256_LEGACY_KEY, + webpki_algs::RSA_PSS_2048_8192_SHA384_LEGACY_KEY, + webpki_algs::RSA_PSS_2048_8192_SHA512_LEGACY_KEY, + webpki_algs::RSA_PKCS1_2048_8192_SHA256, + webpki_algs::RSA_PKCS1_2048_8192_SHA384, + webpki_algs::RSA_PKCS1_2048_8192_SHA512, + webpki_algs::RSA_PKCS1_2048_8192_SHA256_ABSENT_PARAMS, + webpki_algs::RSA_PKCS1_2048_8192_SHA384_ABSENT_PARAMS, + webpki_algs::RSA_PKCS1_2048_8192_SHA512_ABSENT_PARAMS, + ], + mapping: &[ + // Note: for TLS1.2 the curve is not fixed by SignatureScheme. For TLS1.3 it is. + ( + SignatureScheme::ECDSA_NISTP384_SHA384, + &[ + webpki_algs::ECDSA_P384_SHA384, + webpki_algs::ECDSA_P256_SHA384, + ], + ), + ( + SignatureScheme::ECDSA_NISTP256_SHA256, + &[ + webpki_algs::ECDSA_P256_SHA256, + webpki_algs::ECDSA_P384_SHA256, + ], + ), + (SignatureScheme::ED25519, &[webpki_algs::ED25519]), + ( + SignatureScheme::RSA_PSS_SHA512, + &[webpki_algs::RSA_PSS_2048_8192_SHA512_LEGACY_KEY], + ), + ( + SignatureScheme::RSA_PSS_SHA384, + &[webpki_algs::RSA_PSS_2048_8192_SHA384_LEGACY_KEY], + ), + ( + SignatureScheme::RSA_PSS_SHA256, + &[webpki_algs::RSA_PSS_2048_8192_SHA256_LEGACY_KEY], + ), + ( + SignatureScheme::RSA_PKCS1_SHA512, + &[webpki_algs::RSA_PKCS1_2048_8192_SHA512], + ), + ( + SignatureScheme::RSA_PKCS1_SHA384, + &[webpki_algs::RSA_PKCS1_2048_8192_SHA384], + ), + ( + SignatureScheme::RSA_PKCS1_SHA256, + &[webpki_algs::RSA_PKCS1_2048_8192_SHA256], + ), + ], +}; + +/// All defined key exchange groups supported by *ring* appear in this module. +/// +/// [`ALL_KX_GROUPS`] is provided as an array of all of these values. +/// [`DEFAULT_KX_GROUPS`] is provided as an array of this provider's defaults. +pub mod kx_group { + pub use super::kx::{SECP256R1, SECP384R1, X25519}; +} + +/// A list of the default key exchange groups supported by this provider. +pub static DEFAULT_KX_GROUPS: &[&dyn SupportedKxGroup] = ALL_KX_GROUPS; + +/// A list of all the key exchange groups supported by this provider. +pub static ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = + &[kx_group::X25519, kx_group::SECP256R1, kx_group::SECP384R1]; + +#[cfg(feature = "std")] +pub use ticketer::Ticketer; + +/// Compatibility shims between ring 0.16.x and 0.17.x API +mod ring_shim { + use super::ring_like; + use crate::crypto::SharedSecret; + + pub(super) fn agree_ephemeral( + priv_key: ring_like::agreement::EphemeralPrivateKey, + peer_key: &ring_like::agreement::UnparsedPublicKey<&[u8]>, + ) -> Result<SharedSecret, ()> { + ring_like::agreement::agree_ephemeral(priv_key, peer_key, |secret| { + SharedSecret::from(secret) + }) + .map_err(|_| ()) + } +} + +pub(super) fn fips() -> bool { + false +} diff --git a/vendor/rustls/src/crypto/ring/quic.rs b/vendor/rustls/src/crypto/ring/quic.rs new file mode 100644 index 00000000..3d0dc928 --- /dev/null +++ b/vendor/rustls/src/crypto/ring/quic.rs @@ -0,0 +1,416 @@ +#![allow(clippy::duplicate_mod)] + +use alloc::boxed::Box; + +use super::ring_like::aead; +use crate::crypto::cipher::{AeadKey, Iv, Nonce}; +use crate::error::Error; +use crate::quic; + +pub(crate) struct HeaderProtectionKey(aead::quic::HeaderProtectionKey); + +impl HeaderProtectionKey { + pub(crate) fn new(key: AeadKey, alg: &'static aead::quic::Algorithm) -> Self { + Self(aead::quic::HeaderProtectionKey::new(alg, key.as_ref()).unwrap()) + } + + fn xor_in_place( + &self, + sample: &[u8], + first: &mut u8, + packet_number: &mut [u8], + masked: bool, + ) -> Result<(), Error> { + // This implements "Header Protection Application" almost verbatim. + // <https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.1> + + let mask = self + .0 + .new_mask(sample) + .map_err(|_| Error::General("sample of invalid length".into()))?; + + // The `unwrap()` will not panic because `new_mask` returns a + // non-empty result. + let (first_mask, pn_mask) = mask.split_first().unwrap(); + + // It is OK for the `mask` to be longer than `packet_number`, + // but a valid `packet_number` will never be longer than `mask`. + if packet_number.len() > pn_mask.len() { + return Err(Error::General("packet number too long".into())); + } + + // Infallible from this point on. Before this point, `first` and + // `packet_number` are unchanged. + + const LONG_HEADER_FORM: u8 = 0x80; + let bits = match *first & LONG_HEADER_FORM == LONG_HEADER_FORM { + true => 0x0f, // Long header: 4 bits masked + false => 0x1f, // Short header: 5 bits masked + }; + + let first_plain = match masked { + // When unmasking, use the packet length bits after unmasking + true => *first ^ (first_mask & bits), + // When masking, use the packet length bits before masking + false => *first, + }; + let pn_len = (first_plain & 0x03) as usize + 1; + + *first ^= first_mask & bits; + for (dst, m) in packet_number + .iter_mut() + .zip(pn_mask) + .take(pn_len) + { + *dst ^= m; + } + + Ok(()) + } +} + +impl quic::HeaderProtectionKey for HeaderProtectionKey { + fn encrypt_in_place( + &self, + sample: &[u8], + first: &mut u8, + packet_number: &mut [u8], + ) -> Result<(), Error> { + self.xor_in_place(sample, first, packet_number, false) + } + + fn decrypt_in_place( + &self, + sample: &[u8], + first: &mut u8, + packet_number: &mut [u8], + ) -> Result<(), Error> { + self.xor_in_place(sample, first, packet_number, true) + } + + #[inline] + fn sample_len(&self) -> usize { + self.0.algorithm().sample_len() + } +} + +pub(crate) struct PacketKey { + /// Encrypts or decrypts a packet's payload + key: aead::LessSafeKey, + /// Computes unique nonces for each packet + iv: Iv, + /// Confidentiality limit (see [`quic::PacketKey::confidentiality_limit`]) + confidentiality_limit: u64, + /// Integrity limit (see [`quic::PacketKey::integrity_limit`]) + integrity_limit: u64, +} + +impl PacketKey { + pub(crate) fn new( + key: AeadKey, + iv: Iv, + confidentiality_limit: u64, + integrity_limit: u64, + aead_algorithm: &'static aead::Algorithm, + ) -> Self { + Self { + key: aead::LessSafeKey::new( + aead::UnboundKey::new(aead_algorithm, key.as_ref()).unwrap(), + ), + iv, + confidentiality_limit, + integrity_limit, + } + } +} + +impl quic::PacketKey for PacketKey { + fn encrypt_in_place( + &self, + packet_number: u64, + header: &[u8], + payload: &mut [u8], + ) -> Result<quic::Tag, Error> { + let aad = aead::Aad::from(header); + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, packet_number).0); + let tag = self + .key + .seal_in_place_separate_tag(nonce, aad, payload) + .map_err(|_| Error::EncryptError)?; + Ok(quic::Tag::from(tag.as_ref())) + } + + /// Decrypt a QUIC packet + /// + /// Takes the packet `header`, which is used as the additional authenticated data, and the + /// `payload`, which includes the authentication tag. + /// + /// If the return value is `Ok`, the decrypted payload can be found in `payload`, up to the + /// length found in the return value. + fn decrypt_in_place<'a>( + &self, + packet_number: u64, + header: &[u8], + payload: &'a mut [u8], + ) -> Result<&'a [u8], Error> { + let payload_len = payload.len(); + let aad = aead::Aad::from(header); + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, packet_number).0); + self.key + .open_in_place(nonce, aad, payload) + .map_err(|_| Error::DecryptError)?; + + let plain_len = payload_len - self.key.algorithm().tag_len(); + Ok(&payload[..plain_len]) + } + + /// Tag length for the underlying AEAD algorithm + #[inline] + fn tag_len(&self) -> usize { + self.key.algorithm().tag_len() + } + + /// Confidentiality limit (see [`quic::PacketKey::confidentiality_limit`]) + fn confidentiality_limit(&self) -> u64 { + self.confidentiality_limit + } + + /// Integrity limit (see [`quic::PacketKey::integrity_limit`]) + fn integrity_limit(&self) -> u64 { + self.integrity_limit + } +} + +pub(crate) struct KeyBuilder { + pub(crate) packet_alg: &'static aead::Algorithm, + pub(crate) header_alg: &'static aead::quic::Algorithm, + pub(crate) confidentiality_limit: u64, + pub(crate) integrity_limit: u64, +} + +impl quic::Algorithm for KeyBuilder { + fn packet_key(&self, key: AeadKey, iv: Iv) -> Box<dyn quic::PacketKey> { + Box::new(PacketKey::new( + key, + iv, + self.confidentiality_limit, + self.integrity_limit, + self.packet_alg, + )) + } + + fn header_protection_key(&self, key: AeadKey) -> Box<dyn quic::HeaderProtectionKey> { + Box::new(HeaderProtectionKey::new(key, self.header_alg)) + } + + fn aead_key_len(&self) -> usize { + self.packet_alg.key_len() + } + + fn fips(&self) -> bool { + super::fips() + } +} + +#[cfg(test)] +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use std::dbg; + + use super::provider::tls13::{ + TLS13_AES_128_GCM_SHA256_INTERNAL, TLS13_CHACHA20_POLY1305_SHA256_INTERNAL, + }; + use crate::common_state::Side; + use crate::crypto::tls13::OkmBlock; + use crate::quic::*; + + fn test_short_packet(version: Version, expected: &[u8]) { + const PN: u64 = 654360564; + const SECRET: &[u8] = &[ + 0x9a, 0xc3, 0x12, 0xa7, 0xf8, 0x77, 0x46, 0x8e, 0xbe, 0x69, 0x42, 0x27, 0x48, 0xad, + 0x00, 0xa1, 0x54, 0x43, 0xf1, 0x82, 0x03, 0xa0, 0x7d, 0x60, 0x60, 0xf6, 0x88, 0xf3, + 0x0f, 0x21, 0x63, 0x2b, + ]; + + let secret = OkmBlock::new(SECRET); + let builder = KeyBuilder::new( + &secret, + version, + TLS13_CHACHA20_POLY1305_SHA256_INTERNAL + .quic + .unwrap(), + TLS13_CHACHA20_POLY1305_SHA256_INTERNAL.hkdf_provider, + ); + let packet = builder.packet_key(); + let hpk = builder.header_protection_key(); + + const PLAIN: &[u8] = &[0x42, 0x00, 0xbf, 0xf4, 0x01]; + + let mut buf = PLAIN.to_vec(); + let (header, payload) = buf.split_at_mut(4); + let tag = packet + .encrypt_in_place(PN, header, payload) + .unwrap(); + buf.extend(tag.as_ref()); + + let pn_offset = 1; + let (header, sample) = buf.split_at_mut(pn_offset + 4); + let (first, rest) = header.split_at_mut(1); + let sample = &sample[..hpk.sample_len()]; + hpk.encrypt_in_place(sample, &mut first[0], dbg!(rest)) + .unwrap(); + + assert_eq!(&buf, expected); + + let (header, sample) = buf.split_at_mut(pn_offset + 4); + let (first, rest) = header.split_at_mut(1); + let sample = &sample[..hpk.sample_len()]; + hpk.decrypt_in_place(sample, &mut first[0], rest) + .unwrap(); + + let (header, payload_tag) = buf.split_at_mut(4); + let plain = packet + .decrypt_in_place(PN, header, payload_tag) + .unwrap(); + + assert_eq!(plain, &PLAIN[4..]); + } + + #[test] + fn short_packet_header_protection() { + // https://www.rfc-editor.org/rfc/rfc9001.html#name-chacha20-poly1305-short-hea + test_short_packet( + Version::V1, + &[ + 0x4c, 0xfe, 0x41, 0x89, 0x65, 0x5e, 0x5c, 0xd5, 0x5c, 0x41, 0xf6, 0x90, 0x80, 0x57, + 0x5d, 0x79, 0x99, 0xc2, 0x5a, 0x5b, 0xfb, + ], + ); + } + + #[test] + fn key_update_test_vector() { + fn equal_okm(x: &OkmBlock, y: &OkmBlock) -> bool { + x.as_ref() == y.as_ref() + } + + let mut secrets = Secrets::new( + // Constant dummy values for reproducibility + OkmBlock::new( + &[ + 0xb8, 0x76, 0x77, 0x08, 0xf8, 0x77, 0x23, 0x58, 0xa6, 0xea, 0x9f, 0xc4, 0x3e, + 0x4a, 0xdd, 0x2c, 0x96, 0x1b, 0x3f, 0x52, 0x87, 0xa6, 0xd1, 0x46, 0x7e, 0xe0, + 0xae, 0xab, 0x33, 0x72, 0x4d, 0xbf, + ][..], + ), + OkmBlock::new( + &[ + 0x42, 0xdc, 0x97, 0x21, 0x40, 0xe0, 0xf2, 0xe3, 0x98, 0x45, 0xb7, 0x67, 0x61, + 0x34, 0x39, 0xdc, 0x67, 0x58, 0xca, 0x43, 0x25, 0x9b, 0x87, 0x85, 0x06, 0x82, + 0x4e, 0xb1, 0xe4, 0x38, 0xd8, 0x55, + ][..], + ), + TLS13_AES_128_GCM_SHA256_INTERNAL, + TLS13_AES_128_GCM_SHA256_INTERNAL + .quic + .unwrap(), + Side::Client, + Version::V1, + ); + secrets.update(); + + assert!(equal_okm( + &secrets.client, + &OkmBlock::new( + &[ + 0x42, 0xca, 0xc8, 0xc9, 0x1c, 0xd5, 0xeb, 0x40, 0x68, 0x2e, 0x43, 0x2e, 0xdf, + 0x2d, 0x2b, 0xe9, 0xf4, 0x1a, 0x52, 0xca, 0x6b, 0x22, 0xd8, 0xe6, 0xcd, 0xb1, + 0xe8, 0xac, 0xa9, 0x6, 0x1f, 0xce + ][..] + ) + )); + assert!(equal_okm( + &secrets.server, + &OkmBlock::new( + &[ + 0xeb, 0x7f, 0x5e, 0x2a, 0x12, 0x3f, 0x40, 0x7d, 0xb4, 0x99, 0xe3, 0x61, 0xca, + 0xe5, 0x90, 0xd4, 0xd9, 0x92, 0xe1, 0x4b, 0x7a, 0xce, 0x3, 0xc2, 0x44, 0xe0, + 0x42, 0x21, 0x15, 0xb6, 0xd3, 0x8a + ][..] + ) + )); + } + + #[test] + fn short_packet_header_protection_v2() { + // https://www.ietf.org/archive/id/draft-ietf-quic-v2-10.html#name-chacha20-poly1305-short-head + test_short_packet( + Version::V2, + &[ + 0x55, 0x58, 0xb1, 0xc6, 0x0a, 0xe7, 0xb6, 0xb9, 0x32, 0xbc, 0x27, 0xd7, 0x86, 0xf4, + 0xbc, 0x2b, 0xb2, 0x0f, 0x21, 0x62, 0xba, + ], + ); + } + + #[test] + fn initial_test_vector_v2() { + // https://www.ietf.org/archive/id/draft-ietf-quic-v2-10.html#name-sample-packet-protection-2 + let icid = [0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08]; + let server = Keys::initial( + Version::V2, + TLS13_AES_128_GCM_SHA256_INTERNAL, + TLS13_AES_128_GCM_SHA256_INTERNAL + .quic + .unwrap(), + &icid, + Side::Server, + ); + let mut server_payload = [ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x40, 0x5a, 0x02, 0x00, 0x00, 0x56, 0x03, + 0x03, 0xee, 0xfc, 0xe7, 0xf7, 0xb3, 0x7b, 0xa1, 0xd1, 0x63, 0x2e, 0x96, 0x67, 0x78, + 0x25, 0xdd, 0xf7, 0x39, 0x88, 0xcf, 0xc7, 0x98, 0x25, 0xdf, 0x56, 0x6d, 0xc5, 0x43, + 0x0b, 0x9a, 0x04, 0x5a, 0x12, 0x00, 0x13, 0x01, 0x00, 0x00, 0x2e, 0x00, 0x33, 0x00, + 0x24, 0x00, 0x1d, 0x00, 0x20, 0x9d, 0x3c, 0x94, 0x0d, 0x89, 0x69, 0x0b, 0x84, 0xd0, + 0x8a, 0x60, 0x99, 0x3c, 0x14, 0x4e, 0xca, 0x68, 0x4d, 0x10, 0x81, 0x28, 0x7c, 0x83, + 0x4d, 0x53, 0x11, 0xbc, 0xf3, 0x2b, 0xb9, 0xda, 0x1a, 0x00, 0x2b, 0x00, 0x02, 0x03, + 0x04, + ]; + let mut server_header = [ + 0xd1, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, + 0xb5, 0x00, 0x40, 0x75, 0x00, 0x01, + ]; + let tag = server + .local + .packet + .encrypt_in_place(1, &server_header, &mut server_payload) + .unwrap(); + let (first, rest) = server_header.split_at_mut(1); + let rest_len = rest.len(); + server + .local + .header + .encrypt_in_place( + &server_payload[2..18], + &mut first[0], + &mut rest[rest_len - 2..], + ) + .unwrap(); + let mut server_packet = server_header.to_vec(); + server_packet.extend(server_payload); + server_packet.extend(tag.as_ref()); + let expected_server_packet = [ + 0xdc, 0x6b, 0x33, 0x43, 0xcf, 0x00, 0x08, 0xf0, 0x67, 0xa5, 0x50, 0x2a, 0x42, 0x62, + 0xb5, 0x00, 0x40, 0x75, 0xd9, 0x2f, 0xaa, 0xf1, 0x6f, 0x05, 0xd8, 0xa4, 0x39, 0x8c, + 0x47, 0x08, 0x96, 0x98, 0xba, 0xee, 0xa2, 0x6b, 0x91, 0xeb, 0x76, 0x1d, 0x9b, 0x89, + 0x23, 0x7b, 0xbf, 0x87, 0x26, 0x30, 0x17, 0x91, 0x53, 0x58, 0x23, 0x00, 0x35, 0xf7, + 0xfd, 0x39, 0x45, 0xd8, 0x89, 0x65, 0xcf, 0x17, 0xf9, 0xaf, 0x6e, 0x16, 0x88, 0x6c, + 0x61, 0xbf, 0xc7, 0x03, 0x10, 0x6f, 0xba, 0xf3, 0xcb, 0x4c, 0xfa, 0x52, 0x38, 0x2d, + 0xd1, 0x6a, 0x39, 0x3e, 0x42, 0x75, 0x75, 0x07, 0x69, 0x80, 0x75, 0xb2, 0xc9, 0x84, + 0xc7, 0x07, 0xf0, 0xa0, 0x81, 0x2d, 0x8c, 0xd5, 0xa6, 0x88, 0x1e, 0xaf, 0x21, 0xce, + 0xda, 0x98, 0xf4, 0xbd, 0x23, 0xf6, 0xfe, 0x1a, 0x3e, 0x2c, 0x43, 0xed, 0xd9, 0xce, + 0x7c, 0xa8, 0x4b, 0xed, 0x85, 0x21, 0xe2, 0xe1, 0x40, + ]; + assert_eq!(server_packet[..], expected_server_packet[..]); + } +} diff --git a/vendor/rustls/src/crypto/ring/sign.rs b/vendor/rustls/src/crypto/ring/sign.rs new file mode 100644 index 00000000..ad318270 --- /dev/null +++ b/vendor/rustls/src/crypto/ring/sign.rs @@ -0,0 +1,822 @@ +#![allow(clippy::duplicate_mod)] + +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::vec::Vec; +use alloc::{format, vec}; +use core::fmt::{self, Debug, Formatter}; + +use pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer, SubjectPublicKeyInfoDer, alg_id}; + +use super::ring_like::rand::{SecureRandom, SystemRandom}; +use super::ring_like::signature::{self, EcdsaKeyPair, Ed25519KeyPair, KeyPair, RsaKeyPair}; +use crate::crypto::signer::{Signer, SigningKey, public_key_to_spki}; +use crate::enums::{SignatureAlgorithm, SignatureScheme}; +use crate::error::Error; +use crate::sync::Arc; +use crate::x509::{wrap_concat_in_sequence, wrap_in_octet_string}; + +/// Parse `der` as any supported key encoding/type, returning +/// the first which works. +pub fn any_supported_type(der: &PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>, Error> { + if let Ok(rsa) = RsaSigningKey::new(der) { + return Ok(Arc::new(rsa)); + } + + if let Ok(ecdsa) = any_ecdsa_type(der) { + return Ok(ecdsa); + } + + if let PrivateKeyDer::Pkcs8(pkcs8) = der { + if let Ok(eddsa) = any_eddsa_type(pkcs8) { + return Ok(eddsa); + } + } + + Err(Error::General( + "failed to parse private key as RSA, ECDSA, or EdDSA".into(), + )) +} + +/// Parse `der` as any ECDSA key type, returning the first which works. +/// +/// Both SEC1 (PEM section starting with 'BEGIN EC PRIVATE KEY') and PKCS8 +/// (PEM section starting with 'BEGIN PRIVATE KEY') encodings are supported. +pub fn any_ecdsa_type(der: &PrivateKeyDer<'_>) -> Result<Arc<dyn SigningKey>, Error> { + if let Ok(ecdsa_p256) = EcdsaSigningKey::new( + der, + SignatureScheme::ECDSA_NISTP256_SHA256, + &signature::ECDSA_P256_SHA256_ASN1_SIGNING, + ) { + return Ok(Arc::new(ecdsa_p256)); + } + + if let Ok(ecdsa_p384) = EcdsaSigningKey::new( + der, + SignatureScheme::ECDSA_NISTP384_SHA384, + &signature::ECDSA_P384_SHA384_ASN1_SIGNING, + ) { + return Ok(Arc::new(ecdsa_p384)); + } + + Err(Error::General( + "failed to parse ECDSA private key as PKCS#8 or SEC1".into(), + )) +} + +/// Parse `der` as any EdDSA key type, returning the first which works. +/// +/// Note that, at the time of writing, Ed25519 does not have wide support +/// in browsers. It is also not supported by the WebPKI, because the +/// CA/Browser Forum Baseline Requirements do not support it for publicly +/// trusted certificates. +pub fn any_eddsa_type(der: &PrivatePkcs8KeyDer<'_>) -> Result<Arc<dyn SigningKey>, Error> { + // TODO: Add support for Ed448 + Ok(Arc::new(Ed25519SigningKey::new( + der, + SignatureScheme::ED25519, + )?)) +} + +/// A `SigningKey` for RSA-PKCS1 or RSA-PSS. +/// +/// This is used by the test suite, so it must be `pub`, but it isn't part of +/// the public, stable, API. +#[doc(hidden)] +pub struct RsaSigningKey { + key: Arc<RsaKeyPair>, +} + +static ALL_RSA_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA256, +]; + +impl RsaSigningKey { + /// Make a new `RsaSigningKey` from a DER encoding, in either + /// PKCS#1 or PKCS#8 format. + pub fn new(der: &PrivateKeyDer<'_>) -> Result<Self, Error> { + let key_pair = match der { + PrivateKeyDer::Pkcs1(pkcs1) => RsaKeyPair::from_der(pkcs1.secret_pkcs1_der()), + PrivateKeyDer::Pkcs8(pkcs8) => RsaKeyPair::from_pkcs8(pkcs8.secret_pkcs8_der()), + _ => { + return Err(Error::General( + "failed to parse RSA private key as either PKCS#1 or PKCS#8".into(), + )); + } + } + .map_err(|key_rejected| { + Error::General(format!("failed to parse RSA private key: {key_rejected}")) + })?; + + Ok(Self { + key: Arc::new(key_pair), + }) + } +} + +impl SigningKey for RsaSigningKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> { + ALL_RSA_SCHEMES + .iter() + .find(|scheme| offered.contains(scheme)) + .map(|scheme| RsaSigner::new(self.key.clone(), *scheme)) + } + + fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'_>> { + Some(public_key_to_spki( + &alg_id::RSA_ENCRYPTION, + self.key.public_key(), + )) + } + + fn algorithm(&self) -> SignatureAlgorithm { + SignatureAlgorithm::RSA + } +} + +impl Debug for RsaSigningKey { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("RsaSigningKey") + .field("algorithm", &self.algorithm()) + .finish() + } +} + +struct RsaSigner { + key: Arc<RsaKeyPair>, + scheme: SignatureScheme, + encoding: &'static dyn signature::RsaEncoding, +} + +impl RsaSigner { + fn new(key: Arc<RsaKeyPair>, scheme: SignatureScheme) -> Box<dyn Signer> { + let encoding: &dyn signature::RsaEncoding = match scheme { + SignatureScheme::RSA_PKCS1_SHA256 => &signature::RSA_PKCS1_SHA256, + SignatureScheme::RSA_PKCS1_SHA384 => &signature::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA512 => &signature::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PSS_SHA256 => &signature::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384 => &signature::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512 => &signature::RSA_PSS_SHA512, + _ => unreachable!(), + }; + + Box::new(Self { + key, + scheme, + encoding, + }) + } +} + +impl Signer for RsaSigner { + fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> { + let mut sig = vec![0; self.key.public().modulus_len()]; + + let rng = SystemRandom::new(); + self.key + .sign(self.encoding, &rng, message, &mut sig) + .map(|_| sig) + .map_err(|_| Error::General("signing failed".to_string())) + } + + fn scheme(&self) -> SignatureScheme { + self.scheme + } +} + +impl Debug for RsaSigner { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("RsaSigner") + .field("scheme", &self.scheme) + .finish() + } +} + +/// A SigningKey that uses exactly one TLS-level SignatureScheme +/// and one ring-level signature::SigningAlgorithm. +/// +/// Compare this to RsaSigningKey, which for a particular key is +/// willing to sign with several algorithms. This is quite poor +/// cryptography practice, but is necessary because a given RSA key +/// is expected to work in TLS1.2 (PKCS#1 signatures) and TLS1.3 +/// (PSS signatures) -- nobody is willing to obtain certificates for +/// different protocol versions. +/// +/// Currently this is only implemented for ECDSA keys. +struct EcdsaSigningKey { + key: Arc<EcdsaKeyPair>, + scheme: SignatureScheme, +} + +impl EcdsaSigningKey { + /// Make a new `ECDSASigningKey` from a DER encoding in PKCS#8 or SEC1 + /// format, expecting a key usable with precisely the given signature + /// scheme. + fn new( + der: &PrivateKeyDer<'_>, + scheme: SignatureScheme, + sigalg: &'static signature::EcdsaSigningAlgorithm, + ) -> Result<Self, ()> { + let rng = SystemRandom::new(); + let key_pair = match der { + PrivateKeyDer::Sec1(sec1) => { + Self::convert_sec1_to_pkcs8(scheme, sigalg, sec1.secret_sec1_der(), &rng)? + } + PrivateKeyDer::Pkcs8(pkcs8) => { + EcdsaKeyPair::from_pkcs8(sigalg, pkcs8.secret_pkcs8_der(), &rng).map_err(|_| ())? + } + _ => return Err(()), + }; + + Ok(Self { + key: Arc::new(key_pair), + scheme, + }) + } + + /// Convert a SEC1 encoding to PKCS8, and ask ring to parse it. This + /// can be removed once <https://github.com/briansmith/ring/pull/1456> + /// (or equivalent) is landed. + fn convert_sec1_to_pkcs8( + scheme: SignatureScheme, + sigalg: &'static signature::EcdsaSigningAlgorithm, + maybe_sec1_der: &[u8], + rng: &dyn SecureRandom, + ) -> Result<EcdsaKeyPair, ()> { + let pkcs8_prefix = match scheme { + SignatureScheme::ECDSA_NISTP256_SHA256 => &PKCS8_PREFIX_ECDSA_NISTP256, + SignatureScheme::ECDSA_NISTP384_SHA384 => &PKCS8_PREFIX_ECDSA_NISTP384, + _ => unreachable!(), // all callers are in this file + }; + + let sec1_wrap = wrap_in_octet_string(maybe_sec1_der); + let pkcs8 = wrap_concat_in_sequence(pkcs8_prefix, &sec1_wrap); + + EcdsaKeyPair::from_pkcs8(sigalg, &pkcs8, rng).map_err(|_| ()) + } +} + +// This is (line-by-line): +// - INTEGER Version = 0 +// - SEQUENCE (privateKeyAlgorithm) +// - id-ecPublicKey OID +// - prime256v1 OID +const PKCS8_PREFIX_ECDSA_NISTP256: &[u8] = b"\x02\x01\x00\ + \x30\x13\ + \x06\x07\x2a\x86\x48\xce\x3d\x02\x01\ + \x06\x08\x2a\x86\x48\xce\x3d\x03\x01\x07"; + +// This is (line-by-line): +// - INTEGER Version = 0 +// - SEQUENCE (privateKeyAlgorithm) +// - id-ecPublicKey OID +// - secp384r1 OID +const PKCS8_PREFIX_ECDSA_NISTP384: &[u8] = b"\x02\x01\x00\ + \x30\x10\ + \x06\x07\x2a\x86\x48\xce\x3d\x02\x01\ + \x06\x05\x2b\x81\x04\x00\x22"; + +impl SigningKey for EcdsaSigningKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> { + if offered.contains(&self.scheme) { + Some(Box::new(EcdsaSigner { + key: self.key.clone(), + scheme: self.scheme, + })) + } else { + None + } + } + + fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'_>> { + let id = match self.scheme { + SignatureScheme::ECDSA_NISTP256_SHA256 => alg_id::ECDSA_P256, + SignatureScheme::ECDSA_NISTP384_SHA384 => alg_id::ECDSA_P384, + _ => unreachable!(), + }; + + Some(public_key_to_spki(&id, self.key.public_key())) + } + + fn algorithm(&self) -> SignatureAlgorithm { + self.scheme.algorithm() + } +} + +impl Debug for EcdsaSigningKey { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("EcdsaSigningKey") + .field("algorithm", &self.algorithm()) + .finish() + } +} + +struct EcdsaSigner { + key: Arc<EcdsaKeyPair>, + scheme: SignatureScheme, +} + +impl Signer for EcdsaSigner { + fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> { + let rng = SystemRandom::new(); + self.key + .sign(&rng, message) + .map_err(|_| Error::General("signing failed".into())) + .map(|sig| sig.as_ref().into()) + } + + fn scheme(&self) -> SignatureScheme { + self.scheme + } +} + +impl Debug for EcdsaSigner { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("EcdsaSigner") + .field("scheme", &self.scheme) + .finish() + } +} + +/// A SigningKey that uses exactly one TLS-level SignatureScheme +/// and one ring-level signature::SigningAlgorithm. +/// +/// Compare this to RsaSigningKey, which for a particular key is +/// willing to sign with several algorithms. This is quite poor +/// cryptography practice, but is necessary because a given RSA key +/// is expected to work in TLS1.2 (PKCS#1 signatures) and TLS1.3 +/// (PSS signatures) -- nobody is willing to obtain certificates for +/// different protocol versions. +/// +/// Currently this is only implemented for Ed25519 keys. +struct Ed25519SigningKey { + key: Arc<Ed25519KeyPair>, + scheme: SignatureScheme, +} + +impl Ed25519SigningKey { + /// Make a new `Ed25519SigningKey` from a DER encoding in PKCS#8 format, + /// expecting a key usable with precisely the given signature scheme. + fn new(der: &PrivatePkcs8KeyDer<'_>, scheme: SignatureScheme) -> Result<Self, Error> { + match Ed25519KeyPair::from_pkcs8_maybe_unchecked(der.secret_pkcs8_der()) { + Ok(key_pair) => Ok(Self { + key: Arc::new(key_pair), + scheme, + }), + Err(e) => Err(Error::General(format!( + "failed to parse Ed25519 private key: {e}" + ))), + } + } +} + +impl SigningKey for Ed25519SigningKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>> { + if offered.contains(&self.scheme) { + Some(Box::new(Ed25519Signer { + key: self.key.clone(), + scheme: self.scheme, + })) + } else { + None + } + } + + fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'_>> { + Some(public_key_to_spki(&alg_id::ED25519, self.key.public_key())) + } + + fn algorithm(&self) -> SignatureAlgorithm { + self.scheme.algorithm() + } +} + +impl Debug for Ed25519SigningKey { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Ed25519SigningKey") + .field("algorithm", &self.algorithm()) + .finish() + } +} + +struct Ed25519Signer { + key: Arc<Ed25519KeyPair>, + scheme: SignatureScheme, +} + +impl Signer for Ed25519Signer { + fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error> { + Ok(self.key.sign(message).as_ref().into()) + } + + fn scheme(&self) -> SignatureScheme { + self.scheme + } +} + +impl Debug for Ed25519Signer { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Ed25519Signer") + .field("scheme", &self.scheme) + .finish() + } +} + +#[cfg(test)] +mod tests { + use alloc::format; + + use pki_types::{PrivatePkcs1KeyDer, PrivateSec1KeyDer}; + + use super::*; + + #[test] + fn can_load_ecdsa_nistp256_pkcs8() { + let key = + PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/nistp256key.pkcs8.der")[..]); + assert!(any_eddsa_type(&key).is_err()); + let key = PrivateKeyDer::Pkcs8(key); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_load_ecdsa_nistp256_sec1() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp256key.der")[..], + )); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_sign_ecdsa_nistp256() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp256key.der")[..], + )); + + let k = any_supported_type(&key).unwrap(); + assert_eq!(format!("{k:?}"), "EcdsaSigningKey { algorithm: ECDSA }"); + assert_eq!(k.algorithm(), SignatureAlgorithm::ECDSA); + + assert!( + k.choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP384_SHA384]) + .is_none() + ); + let s = k + .choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .unwrap(); + assert_eq!( + format!("{s:?}"), + "EcdsaSigner { scheme: ECDSA_NISTP256_SHA256 }" + ); + assert_eq!(s.scheme(), SignatureScheme::ECDSA_NISTP256_SHA256); + // nb. signature is variable length and asn.1-encoded + assert!( + s.sign(b"hello") + .unwrap() + .starts_with(&[0x30]) + ); + } + + #[test] + fn can_load_ecdsa_nistp384_pkcs8() { + let key = + PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/nistp384key.pkcs8.der")[..]); + assert!(any_eddsa_type(&key).is_err()); + let key = PrivateKeyDer::Pkcs8(key); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_load_ecdsa_nistp384_sec1() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp384key.der")[..], + )); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_ok()); + } + + #[test] + fn can_sign_ecdsa_nistp384() { + let key = PrivateKeyDer::Sec1(PrivateSec1KeyDer::from( + &include_bytes!("../../testdata/nistp384key.der")[..], + )); + + let k = any_supported_type(&key).unwrap(); + assert_eq!(format!("{k:?}"), "EcdsaSigningKey { algorithm: ECDSA }"); + assert_eq!(k.algorithm(), SignatureAlgorithm::ECDSA); + + assert!( + k.choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .is_none() + ); + let s = k + .choose_scheme(&[SignatureScheme::ECDSA_NISTP384_SHA384]) + .unwrap(); + assert_eq!( + format!("{s:?}"), + "EcdsaSigner { scheme: ECDSA_NISTP384_SHA384 }" + ); + assert_eq!(s.scheme(), SignatureScheme::ECDSA_NISTP384_SHA384); + // nb. signature is variable length and asn.1-encoded + assert!( + s.sign(b"hello") + .unwrap() + .starts_with(&[0x30]) + ); + } + + #[test] + fn can_load_eddsa_pkcs8() { + let key = PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/eddsakey.der")[..]); + assert!(any_eddsa_type(&key).is_ok()); + let key = PrivateKeyDer::Pkcs8(key); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_err()); + } + + #[test] + fn can_sign_eddsa() { + let key = PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/eddsakey.der")[..]); + + let k = any_eddsa_type(&key).unwrap(); + assert_eq!(format!("{k:?}"), "Ed25519SigningKey { algorithm: ED25519 }"); + assert_eq!(k.algorithm(), SignatureAlgorithm::ED25519); + + assert!( + k.choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .is_none() + ); + let s = k + .choose_scheme(&[SignatureScheme::ED25519]) + .unwrap(); + assert_eq!(format!("{s:?}"), "Ed25519Signer { scheme: ED25519 }"); + assert_eq!(s.scheme(), SignatureScheme::ED25519); + assert_eq!(s.sign(b"hello").unwrap().len(), 64); + } + + #[test] + fn can_load_rsa2048_pkcs8() { + let key = + PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..]); + assert!(any_eddsa_type(&key).is_err()); + let key = PrivateKeyDer::Pkcs8(key); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_err()); + } + + #[test] + fn can_load_rsa2048_pkcs1() { + let key = PrivateKeyDer::Pkcs1(PrivatePkcs1KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs1.der")[..], + )); + assert!(any_supported_type(&key).is_ok()); + assert!(any_ecdsa_type(&key).is_err()); + } + + #[test] + fn can_sign_rsa2048() { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..], + )); + + let k = any_supported_type(&key).unwrap(); + assert_eq!(format!("{k:?}"), "RsaSigningKey { algorithm: RSA }"); + assert_eq!(k.algorithm(), SignatureAlgorithm::RSA); + + assert!( + k.choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .is_none() + ); + assert!( + k.choose_scheme(&[SignatureScheme::ED25519]) + .is_none() + ); + + let s = k + .choose_scheme(&[SignatureScheme::RSA_PSS_SHA256]) + .unwrap(); + assert_eq!(format!("{s:?}"), "RsaSigner { scheme: RSA_PSS_SHA256 }"); + assert_eq!(s.scheme(), SignatureScheme::RSA_PSS_SHA256); + assert_eq!(s.sign(b"hello").unwrap().len(), 256); + + for scheme in &[ + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + ] { + k.choose_scheme(&[*scheme]).unwrap(); + } + } + + #[test] + fn cannot_load_invalid_pkcs8_encoding() { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(&b"invalid"[..])); + assert_eq!( + any_supported_type(&key).err(), + Some(Error::General( + "failed to parse private key as RSA, ECDSA, or EdDSA".into() + )) + ); + assert_eq!( + any_ecdsa_type(&key).err(), + Some(Error::General( + "failed to parse ECDSA private key as PKCS#8 or SEC1".into() + )) + ); + assert_eq!( + RsaSigningKey::new(&key).err(), + Some(Error::General( + "failed to parse RSA private key: InvalidEncoding".into() + )) + ); + } +} + +#[cfg(bench)] +mod benchmarks { + use super::{PrivateKeyDer, PrivatePkcs8KeyDer, SignatureScheme}; + + #[bench] + fn bench_rsa2048_pkcs1_sha256(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::RSA_PKCS1_SHA256]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_rsa2048_pss_sha256(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::RSA_PSS_SHA256]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_eddsa(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/eddsakey.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::ED25519]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_ecdsa_p256_sha256(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp256key.pkcs8.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::ECDSA_NISTP256_SHA256]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_ecdsa_p384_sha384(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp384key.pkcs8.der")[..], + )); + let sk = super::any_supported_type(&key).unwrap(); + let signer = sk + .choose_scheme(&[SignatureScheme::ECDSA_NISTP384_SHA384]) + .unwrap(); + + b.iter(|| { + test::black_box( + signer + .sign(SAMPLE_TLS13_MESSAGE) + .unwrap(), + ); + }); + } + + #[bench] + fn bench_load_and_validate_rsa2048(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa2048key.pkcs8.der")[..], + )); + + b.iter(|| { + test::black_box(super::any_supported_type(&key).unwrap()); + }); + } + + #[bench] + fn bench_load_and_validate_rsa4096(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/rsa4096key.pkcs8.der")[..], + )); + + b.iter(|| { + test::black_box(super::any_supported_type(&key).unwrap()); + }); + } + + #[bench] + fn bench_load_and_validate_p256(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp256key.pkcs8.der")[..], + )); + + b.iter(|| { + test::black_box(super::any_ecdsa_type(&key).unwrap()); + }); + } + + #[bench] + fn bench_load_and_validate_p384(b: &mut test::Bencher) { + let key = PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from( + &include_bytes!("../../testdata/nistp384key.pkcs8.der")[..], + )); + + b.iter(|| { + test::black_box(super::any_ecdsa_type(&key).unwrap()); + }); + } + + #[bench] + fn bench_load_and_validate_eddsa(b: &mut test::Bencher) { + let key = PrivatePkcs8KeyDer::from(&include_bytes!("../../testdata/eddsakey.der")[..]); + + b.iter(|| { + test::black_box(super::any_eddsa_type(&key).unwrap()); + }); + } + + const SAMPLE_TLS13_MESSAGE: &[u8] = &[ + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x54, 0x4c, 0x53, 0x20, 0x31, 0x2e, 0x33, 0x2c, 0x20, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x65, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x00, 0x04, 0xca, 0xc4, 0x48, 0x0e, 0x70, 0xf2, + 0x1b, 0xa9, 0x1c, 0x16, 0xca, 0x90, 0x48, 0xbe, 0x28, 0x2f, 0xc7, 0xf8, 0x9b, 0x87, 0x72, + 0x93, 0xda, 0x4d, 0x2f, 0x80, 0x80, 0x60, 0x1a, 0xd3, 0x08, 0xe2, 0xb7, 0x86, 0x14, 0x1b, + 0x54, 0xda, 0x9a, 0xc9, 0x6d, 0xe9, 0x66, 0xb4, 0x9f, 0xe2, 0x2c, + ]; +} diff --git a/vendor/rustls/src/crypto/ring/ticketer.rs b/vendor/rustls/src/crypto/ring/ticketer.rs new file mode 100644 index 00000000..45dcc27a --- /dev/null +++ b/vendor/rustls/src/crypto/ring/ticketer.rs @@ -0,0 +1,366 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::fmt; +use core::fmt::{Debug, Formatter}; +use core::sync::atomic::{AtomicUsize, Ordering}; + +use subtle::ConstantTimeEq; + +use super::ring_like::aead; +use super::ring_like::rand::{SecureRandom, SystemRandom}; +use crate::error::Error; +#[cfg(debug_assertions)] +use crate::log::debug; +use crate::polyfill::try_split_at; +use crate::rand::GetRandomFailed; +use crate::server::ProducesTickets; +use crate::sync::Arc; + +/// A concrete, safe ticket creation mechanism. +pub struct Ticketer {} + +impl Ticketer { + /// Make the recommended `Ticketer`. This produces tickets + /// with a 12 hour life and randomly generated keys. + /// + /// The encryption mechanism used is Chacha20Poly1305. + #[cfg(feature = "std")] + pub fn new() -> Result<Arc<dyn ProducesTickets>, Error> { + Ok(Arc::new(crate::ticketer::TicketRotator::new( + 6 * 60 * 60, + make_ticket_generator, + )?)) + } +} + +fn make_ticket_generator() -> Result<Box<dyn ProducesTickets>, GetRandomFailed> { + Ok(Box::new(AeadTicketer::new()?)) +} + +/// This is a `ProducesTickets` implementation which uses +/// any *ring* `aead::Algorithm` to encrypt and authentication +/// the ticket payload. It does not enforce any lifetime +/// constraint. +struct AeadTicketer { + alg: &'static aead::Algorithm, + key: aead::LessSafeKey, + key_name: [u8; 16], + lifetime: u32, + + /// Tracks the largest ciphertext produced by `encrypt`, and + /// uses it to early-reject `decrypt` queries that are too long. + /// + /// Accepting excessively long ciphertexts means a "Partitioning + /// Oracle Attack" (see <https://eprint.iacr.org/2020/1491.pdf>) + /// can be more efficient, though also note that these are thought + /// to be cryptographically hard if the key is full-entropy (as it + /// is here). + maximum_ciphertext_len: AtomicUsize, +} + +impl AeadTicketer { + fn new() -> Result<Self, GetRandomFailed> { + let mut key = [0u8; 32]; + SystemRandom::new() + .fill(&mut key) + .map_err(|_| GetRandomFailed)?; + + let key = aead::UnboundKey::new(TICKETER_AEAD, &key).unwrap(); + + let mut key_name = [0u8; 16]; + SystemRandom::new() + .fill(&mut key_name) + .map_err(|_| GetRandomFailed)?; + + Ok(Self { + alg: TICKETER_AEAD, + key: aead::LessSafeKey::new(key), + key_name, + lifetime: 60 * 60 * 12, + maximum_ciphertext_len: AtomicUsize::new(0), + }) + } +} + +impl ProducesTickets for AeadTicketer { + fn enabled(&self) -> bool { + true + } + + fn lifetime(&self) -> u32 { + self.lifetime + } + + /// Encrypt `message` and return the ciphertext. + fn encrypt(&self, message: &[u8]) -> Option<Vec<u8>> { + // Random nonce, because a counter is a privacy leak. + let mut nonce_buf = [0u8; 12]; + SystemRandom::new() + .fill(&mut nonce_buf) + .ok()?; + let nonce = aead::Nonce::assume_unique_for_key(nonce_buf); + let aad = aead::Aad::from(self.key_name); + + // ciphertext structure is: + // key_name: [u8; 16] + // nonce: [u8; 12] + // message: [u8, _] + // tag: [u8; 16] + + let mut ciphertext = Vec::with_capacity( + self.key_name.len() + nonce_buf.len() + message.len() + self.key.algorithm().tag_len(), + ); + ciphertext.extend(self.key_name); + ciphertext.extend(nonce_buf); + ciphertext.extend(message); + let ciphertext = self + .key + .seal_in_place_separate_tag( + nonce, + aad, + &mut ciphertext[self.key_name.len() + nonce_buf.len()..], + ) + .map(|tag| { + ciphertext.extend(tag.as_ref()); + ciphertext + }) + .ok()?; + + self.maximum_ciphertext_len + .fetch_max(ciphertext.len(), Ordering::SeqCst); + Some(ciphertext) + } + + /// Decrypt `ciphertext` and recover the original message. + fn decrypt(&self, ciphertext: &[u8]) -> Option<Vec<u8>> { + if ciphertext.len() + > self + .maximum_ciphertext_len + .load(Ordering::SeqCst) + { + #[cfg(debug_assertions)] + debug!("rejected over-length ticket"); + return None; + } + + let (alleged_key_name, ciphertext) = try_split_at(ciphertext, self.key_name.len())?; + + let (nonce, ciphertext) = try_split_at(ciphertext, self.alg.nonce_len())?; + + // checking the key_name is the expected one, *and* then putting it into the + // additionally authenticated data is duplicative. this check quickly rejects + // tickets for a different ticketer (see `TicketSwitcher`), while including it + // in the AAD ensures it is authenticated independent of that check and that + // any attempted attack on the integrity such as [^1] must happen for each + // `key_label`, not over a population of potential keys. this approach + // is overall similar to [^2]. + // + // [^1]: https://eprint.iacr.org/2020/1491.pdf + // [^2]: "Authenticated Encryption with Key Identification", fig 6 + // <https://eprint.iacr.org/2022/1680.pdf> + if ConstantTimeEq::ct_ne(&self.key_name[..], alleged_key_name).into() { + #[cfg(debug_assertions)] + debug!("rejected ticket with wrong ticket_name"); + return None; + } + + // This won't fail since `nonce` has the required length. + let nonce = aead::Nonce::try_assume_unique_for_key(nonce).ok()?; + + let mut out = Vec::from(ciphertext); + + let plain_len = self + .key + .open_in_place(nonce, aead::Aad::from(alleged_key_name), &mut out) + .ok()? + .len(); + out.truncate(plain_len); + + Some(out) + } +} + +impl Debug for AeadTicketer { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + // Note: we deliberately omit the key from the debug output. + f.debug_struct("AeadTicketer") + .field("alg", &self.alg) + .field("lifetime", &self.lifetime) + .finish() + } +} + +static TICKETER_AEAD: &aead::Algorithm = &aead::CHACHA20_POLY1305; + +#[cfg(test)] +mod tests { + use core::time::Duration; + + use pki_types::UnixTime; + + use super::*; + + #[test] + fn basic_pairwise_test() { + let t = Ticketer::new().unwrap(); + assert!(t.enabled()); + let cipher = t.encrypt(b"hello world").unwrap(); + let plain = t.decrypt(&cipher).unwrap(); + assert_eq!(plain, b"hello world"); + } + + #[test] + fn refuses_decrypt_before_encrypt() { + let t = Ticketer::new().unwrap(); + assert_eq!(t.decrypt(b"hello"), None); + } + + #[test] + fn refuses_decrypt_larger_than_largest_encryption() { + let t = Ticketer::new().unwrap(); + let mut cipher = t.encrypt(b"hello world").unwrap(); + assert_eq!(t.decrypt(&cipher), Some(b"hello world".to_vec())); + + // obviously this would never work anyway, but this + // and `cannot_decrypt_before_encrypt` exercise the + // first branch in `decrypt()` + cipher.push(0); + assert_eq!(t.decrypt(&cipher), None); + } + + #[test] + fn ticketrotator_switching_test() { + let t = Arc::new(crate::ticketer::TicketRotator::new(1, make_ticket_generator).unwrap()); + let now = UnixTime::now(); + let cipher1 = t.encrypt(b"ticket 1").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + { + // Trigger new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 10, + ))); + } + let cipher2 = t.encrypt(b"ticket 2").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + { + // Trigger new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 20, + ))); + } + let cipher3 = t.encrypt(b"ticket 3").unwrap(); + assert!(t.decrypt(&cipher1).is_none()); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3"); + } + + #[test] + fn ticketrotator_remains_usable_over_temporary_ticketer_creation_failure() { + let mut t = crate::ticketer::TicketRotator::new(1, make_ticket_generator).unwrap(); + let now = UnixTime::now(); + let cipher1 = t.encrypt(b"ticket 1").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + t.generator = fail_generator; + { + // Failed new ticketer; this means we still need to + // rotate. + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 10, + ))); + } + + // check post-failure encryption/decryption still works + let cipher2 = t.encrypt(b"ticket 2").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + + // do the rotation for real + t.generator = make_ticket_generator; + { + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 20, + ))); + } + let cipher3 = t.encrypt(b"ticket 3").unwrap(); + assert!(t.decrypt(&cipher1).is_some()); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3"); + } + + #[test] + fn ticketswitcher_switching_test() { + #[expect(deprecated)] + let t = Arc::new(crate::ticketer::TicketSwitcher::new(1, make_ticket_generator).unwrap()); + let now = UnixTime::now(); + let cipher1 = t.encrypt(b"ticket 1").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + { + // Trigger new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 10, + ))); + } + let cipher2 = t.encrypt(b"ticket 2").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + { + // Trigger new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 20, + ))); + } + let cipher3 = t.encrypt(b"ticket 3").unwrap(); + assert!(t.decrypt(&cipher1).is_none()); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3"); + } + + #[test] + fn ticketswitcher_recover_test() { + #[expect(deprecated)] + let mut t = crate::ticketer::TicketSwitcher::new(1, make_ticket_generator).unwrap(); + let now = UnixTime::now(); + let cipher1 = t.encrypt(b"ticket 1").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + t.generator = fail_generator; + { + // Failed new ticketer + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 10, + ))); + } + t.generator = make_ticket_generator; + let cipher2 = t.encrypt(b"ticket 2").unwrap(); + assert_eq!(t.decrypt(&cipher1).unwrap(), b"ticket 1"); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + { + // recover + t.maybe_roll(UnixTime::since_unix_epoch(Duration::from_secs( + now.as_secs() + 20, + ))); + } + let cipher3 = t.encrypt(b"ticket 3").unwrap(); + assert!(t.decrypt(&cipher1).is_none()); + assert_eq!(t.decrypt(&cipher2).unwrap(), b"ticket 2"); + assert_eq!(t.decrypt(&cipher3).unwrap(), b"ticket 3"); + } + + #[test] + fn aeadticketer_is_debug_and_producestickets() { + use alloc::format; + + use super::*; + + let t = make_ticket_generator().unwrap(); + + let expect = format!("AeadTicketer {{ alg: {TICKETER_AEAD:?}, lifetime: 43200 }}"); + assert_eq!(format!("{t:?}"), expect); + assert!(t.enabled()); + assert_eq!(t.lifetime(), 43200); + } + + fn fail_generator() -> Result<Box<dyn ProducesTickets>, GetRandomFailed> { + Err(GetRandomFailed) + } +} diff --git a/vendor/rustls/src/crypto/ring/tls12.rs b/vendor/rustls/src/crypto/ring/tls12.rs new file mode 100644 index 00000000..63dfae8b --- /dev/null +++ b/vendor/rustls/src/crypto/ring/tls12.rs @@ -0,0 +1,406 @@ +use alloc::boxed::Box; + +use super::ring_like::aead; +use crate::crypto::KeyExchangeAlgorithm; +use crate::crypto::cipher::{ + AeadKey, InboundOpaqueMessage, Iv, KeyBlockShape, MessageDecrypter, MessageEncrypter, + NONCE_LEN, Nonce, Tls12AeadAlgorithm, UnsupportedOperationError, make_tls12_aad, +}; +use crate::crypto::tls12::PrfUsingHmac; +use crate::enums::{CipherSuite, SignatureScheme}; +use crate::error::Error; +use crate::msgs::fragmenter::MAX_FRAGMENT_LEN; +use crate::msgs::message::{ + InboundPlainMessage, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, +}; +use crate::suites::{CipherSuiteCommon, ConnectionTrafficSecrets, SupportedCipherSuite}; +use crate::tls12::Tls12CipherSuite; + +/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256. +pub static TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &ChaCha20Poly1305, + prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 +pub static TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_RSA_SCHEMES, + aead_alg: &ChaCha20Poly1305, + prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +pub static TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: 1 << 24, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_RSA_SCHEMES, + aead_alg: &AES128_GCM, + prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +pub static TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + hash_provider: &super::hash::SHA384, + confidentiality_limit: 1 << 24, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_RSA_SCHEMES, + aead_alg: &AES256_GCM, + prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA384), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +pub static TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: 1 << 24, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &AES128_GCM, + prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA256), + }); + +/// The TLS1.2 ciphersuite TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +pub static TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + hash_provider: &super::hash::SHA384, + confidentiality_limit: 1 << 24, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &AES256_GCM, + prf_provider: &PrfUsingHmac(&super::hmac::HMAC_SHA384), + }); + +static TLS12_ECDSA_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::ED25519, + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::ECDSA_NISTP256_SHA256, +]; + +static TLS12_RSA_SCHEMES: &[SignatureScheme] = &[ + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::RSA_PKCS1_SHA256, +]; + +pub(crate) static AES128_GCM: GcmAlgorithm = GcmAlgorithm(&aead::AES_128_GCM); +pub(crate) static AES256_GCM: GcmAlgorithm = GcmAlgorithm(&aead::AES_256_GCM); + +pub(crate) struct GcmAlgorithm(&'static aead::Algorithm); + +impl Tls12AeadAlgorithm for GcmAlgorithm { + fn decrypter(&self, dec_key: AeadKey, dec_iv: &[u8]) -> Box<dyn MessageDecrypter> { + let dec_key = + aead::LessSafeKey::new(aead::UnboundKey::new(self.0, dec_key.as_ref()).unwrap()); + + let mut ret = GcmMessageDecrypter { + dec_key, + dec_salt: [0u8; 4], + }; + + debug_assert_eq!(dec_iv.len(), 4); + ret.dec_salt.copy_from_slice(dec_iv); + Box::new(ret) + } + + fn encrypter( + &self, + enc_key: AeadKey, + write_iv: &[u8], + explicit: &[u8], + ) -> Box<dyn MessageEncrypter> { + let enc_key = + aead::LessSafeKey::new(aead::UnboundKey::new(self.0, enc_key.as_ref()).unwrap()); + let iv = gcm_iv(write_iv, explicit); + Box::new(GcmMessageEncrypter { enc_key, iv }) + } + + fn key_block_shape(&self) -> KeyBlockShape { + KeyBlockShape { + enc_key_len: self.0.key_len(), + fixed_iv_len: 4, + explicit_nonce_len: 8, + } + } + + fn extract_keys( + &self, + key: AeadKey, + write_iv: &[u8], + explicit: &[u8], + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + let iv = gcm_iv(write_iv, explicit); + Ok(match self.0.key_len() { + 16 => ConnectionTrafficSecrets::Aes128Gcm { key, iv }, + 32 => ConnectionTrafficSecrets::Aes256Gcm { key, iv }, + _ => unreachable!(), + }) + } + + fn fips(&self) -> bool { + super::fips() + } +} + +pub(crate) struct ChaCha20Poly1305; + +impl Tls12AeadAlgorithm for ChaCha20Poly1305 { + fn decrypter(&self, dec_key: AeadKey, iv: &[u8]) -> Box<dyn MessageDecrypter> { + let dec_key = aead::LessSafeKey::new( + aead::UnboundKey::new(&aead::CHACHA20_POLY1305, dec_key.as_ref()).unwrap(), + ); + Box::new(ChaCha20Poly1305MessageDecrypter { + dec_key, + dec_offset: Iv::copy(iv), + }) + } + + fn encrypter(&self, enc_key: AeadKey, enc_iv: &[u8], _: &[u8]) -> Box<dyn MessageEncrypter> { + let enc_key = aead::LessSafeKey::new( + aead::UnboundKey::new(&aead::CHACHA20_POLY1305, enc_key.as_ref()).unwrap(), + ); + Box::new(ChaCha20Poly1305MessageEncrypter { + enc_key, + enc_offset: Iv::copy(enc_iv), + }) + } + + fn key_block_shape(&self) -> KeyBlockShape { + KeyBlockShape { + enc_key_len: 32, + fixed_iv_len: 12, + explicit_nonce_len: 0, + } + } + + fn extract_keys( + &self, + key: AeadKey, + iv: &[u8], + _explicit: &[u8], + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + // This should always be true because KeyBlockShape and the Iv nonce len are in agreement. + debug_assert_eq!(aead::NONCE_LEN, iv.len()); + Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { + key, + iv: Iv::new(iv[..].try_into().unwrap()), + }) + } + + fn fips(&self) -> bool { + false // not fips approved + } +} + +/// A `MessageEncrypter` for AES-GCM AEAD ciphersuites. TLS 1.2 only. +struct GcmMessageEncrypter { + enc_key: aead::LessSafeKey, + iv: Iv, +} + +/// A `MessageDecrypter` for AES-GCM AEAD ciphersuites. TLS1.2 only. +struct GcmMessageDecrypter { + dec_key: aead::LessSafeKey, + dec_salt: [u8; 4], +} + +const GCM_EXPLICIT_NONCE_LEN: usize = 8; +const GCM_OVERHEAD: usize = GCM_EXPLICIT_NONCE_LEN + 16; + +impl MessageDecrypter for GcmMessageDecrypter { + fn decrypt<'a>( + &mut self, + mut msg: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result<InboundPlainMessage<'a>, Error> { + let payload = &msg.payload; + if payload.len() < GCM_OVERHEAD { + return Err(Error::DecryptError); + } + + let nonce = { + let mut nonce = [0u8; 12]; + nonce[..4].copy_from_slice(&self.dec_salt); + nonce[4..].copy_from_slice(&payload[..8]); + aead::Nonce::assume_unique_for_key(nonce) + }; + + let aad = aead::Aad::from(make_tls12_aad( + seq, + msg.typ, + msg.version, + payload.len() - GCM_OVERHEAD, + )); + + let payload = &mut msg.payload; + let plain_len = self + .dec_key + .open_within(nonce, aad, payload, GCM_EXPLICIT_NONCE_LEN..) + .map_err(|_| Error::DecryptError)? + .len(); + + if plain_len > MAX_FRAGMENT_LEN { + return Err(Error::PeerSentOversizedRecord); + } + + payload.truncate(plain_len); + Ok(msg.into_plain_message()) + } +} + +impl MessageEncrypter for GcmMessageEncrypter { + fn encrypt( + &mut self, + msg: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result<OutboundOpaqueMessage, Error> { + let total_len = self.encrypted_payload_len(msg.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, seq).0); + let aad = aead::Aad::from(make_tls12_aad(seq, msg.typ, msg.version, msg.payload.len())); + payload.extend_from_slice(&nonce.as_ref()[4..]); + payload.extend_from_chunks(&msg.payload); + + self.enc_key + .seal_in_place_separate_tag(nonce, aad, &mut payload.as_mut()[GCM_EXPLICIT_NONCE_LEN..]) + .map(|tag| payload.extend_from_slice(tag.as_ref())) + .map_err(|_| Error::EncryptError)?; + + Ok(OutboundOpaqueMessage::new(msg.typ, msg.version, payload)) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + GCM_EXPLICIT_NONCE_LEN + self.enc_key.algorithm().tag_len() + } +} + +/// The RFC7905/RFC7539 ChaCha20Poly1305 construction. +/// This implementation does the AAD construction required in TLS1.2. +/// TLS1.3 uses `TLS13MessageEncrypter`. +struct ChaCha20Poly1305MessageEncrypter { + enc_key: aead::LessSafeKey, + enc_offset: Iv, +} + +/// The RFC7905/RFC7539 ChaCha20Poly1305 construction. +/// This implementation does the AAD construction required in TLS1.2. +/// TLS1.3 uses `TLS13MessageDecrypter`. +struct ChaCha20Poly1305MessageDecrypter { + dec_key: aead::LessSafeKey, + dec_offset: Iv, +} + +const CHACHAPOLY1305_OVERHEAD: usize = 16; + +impl MessageDecrypter for ChaCha20Poly1305MessageDecrypter { + fn decrypt<'a>( + &mut self, + mut msg: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result<InboundPlainMessage<'a>, Error> { + let payload = &msg.payload; + + if payload.len() < CHACHAPOLY1305_OVERHEAD { + return Err(Error::DecryptError); + } + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.dec_offset, seq).0); + let aad = aead::Aad::from(make_tls12_aad( + seq, + msg.typ, + msg.version, + payload.len() - CHACHAPOLY1305_OVERHEAD, + )); + + let payload = &mut msg.payload; + let plain_len = self + .dec_key + .open_in_place(nonce, aad, payload) + .map_err(|_| Error::DecryptError)? + .len(); + + if plain_len > MAX_FRAGMENT_LEN { + return Err(Error::PeerSentOversizedRecord); + } + + payload.truncate(plain_len); + Ok(msg.into_plain_message()) + } +} + +impl MessageEncrypter for ChaCha20Poly1305MessageEncrypter { + fn encrypt( + &mut self, + msg: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result<OutboundOpaqueMessage, Error> { + let total_len = self.encrypted_payload_len(msg.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.enc_offset, seq).0); + let aad = aead::Aad::from(make_tls12_aad(seq, msg.typ, msg.version, msg.payload.len())); + payload.extend_from_chunks(&msg.payload); + + self.enc_key + .seal_in_place_append_tag(nonce, aad, &mut payload) + .map_err(|_| Error::EncryptError)?; + + Ok(OutboundOpaqueMessage::new(msg.typ, msg.version, payload)) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + self.enc_key.algorithm().tag_len() + } +} + +fn gcm_iv(write_iv: &[u8], explicit: &[u8]) -> Iv { + debug_assert_eq!(write_iv.len(), 4); + debug_assert_eq!(explicit.len(), 8); + + // The GCM nonce is constructed from a 32-bit 'salt' derived + // from the master-secret, and a 64-bit explicit part, + // with no specified construction. Thanks for that. + // + // We use the same construction as TLS1.3/ChaCha20Poly1305: + // a starting point extracted from the key block, xored with + // the sequence number. + let mut iv = [0; NONCE_LEN]; + iv[..4].copy_from_slice(write_iv); + iv[4..].copy_from_slice(explicit); + + Iv::new(iv) +} diff --git a/vendor/rustls/src/crypto/ring/tls13.rs b/vendor/rustls/src/crypto/ring/tls13.rs new file mode 100644 index 00000000..ef488b62 --- /dev/null +++ b/vendor/rustls/src/crypto/ring/tls13.rs @@ -0,0 +1,336 @@ +use alloc::boxed::Box; + +use super::ring_like::hkdf::KeyType; +use super::ring_like::{aead, hkdf, hmac}; +use crate::crypto; +use crate::crypto::cipher::{ + AeadKey, InboundOpaqueMessage, Iv, MessageDecrypter, MessageEncrypter, Nonce, + Tls13AeadAlgorithm, UnsupportedOperationError, make_tls13_aad, +}; +use crate::crypto::tls13::{Hkdf, HkdfExpander, OkmBlock, OutputLengthError}; +use crate::enums::{CipherSuite, ContentType, ProtocolVersion}; +use crate::error::Error; +use crate::msgs::message::{ + InboundPlainMessage, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, +}; +use crate::suites::{CipherSuiteCommon, ConnectionTrafficSecrets, SupportedCipherSuite}; +use crate::tls13::Tls13CipherSuite; + +/// The TLS1.3 ciphersuite TLS_CHACHA20_POLY1305_SHA256 +pub static TLS13_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls13(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL); + +pub(crate) static TLS13_CHACHA20_POLY1305_SHA256_INTERNAL: &Tls13CipherSuite = &Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + hash_provider: &super::hash::SHA256, + // ref: <https://www.ietf.org/archive/id/draft-irtf-cfrg-aead-limits-08.html#section-5.2.1> + confidentiality_limit: u64::MAX, + }, + hkdf_provider: &RingHkdf(hkdf::HKDF_SHA256, hmac::HMAC_SHA256), + aead_alg: &Chacha20Poly1305Aead(AeadAlgorithm(&aead::CHACHA20_POLY1305)), + quic: Some(&super::quic::KeyBuilder { + packet_alg: &aead::CHACHA20_POLY1305, + header_alg: &aead::quic::CHACHA20, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-6.6> + confidentiality_limit: u64::MAX, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-6.6> + integrity_limit: 1 << 36, + }), +}; + +/// The TLS1.3 ciphersuite TLS_AES_256_GCM_SHA384 +pub static TLS13_AES_256_GCM_SHA384: SupportedCipherSuite = + SupportedCipherSuite::Tls13(&Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_AES_256_GCM_SHA384, + hash_provider: &super::hash::SHA384, + confidentiality_limit: 1 << 24, + }, + hkdf_provider: &RingHkdf(hkdf::HKDF_SHA384, hmac::HMAC_SHA384), + aead_alg: &Aes256GcmAead(AeadAlgorithm(&aead::AES_256_GCM)), + quic: Some(&super::quic::KeyBuilder { + packet_alg: &aead::AES_256_GCM, + header_alg: &aead::quic::AES_256, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-b.1.1> + confidentiality_limit: 1 << 23, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-b.1.2> + integrity_limit: 1 << 52, + }), + }); + +/// The TLS1.3 ciphersuite TLS_AES_128_GCM_SHA256 +pub static TLS13_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls13(TLS13_AES_128_GCM_SHA256_INTERNAL); + +pub(crate) static TLS13_AES_128_GCM_SHA256_INTERNAL: &Tls13CipherSuite = &Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_AES_128_GCM_SHA256, + hash_provider: &super::hash::SHA256, + confidentiality_limit: 1 << 24, + }, + hkdf_provider: &RingHkdf(hkdf::HKDF_SHA256, hmac::HMAC_SHA256), + aead_alg: &Aes128GcmAead(AeadAlgorithm(&aead::AES_128_GCM)), + quic: Some(&super::quic::KeyBuilder { + packet_alg: &aead::AES_128_GCM, + header_alg: &aead::quic::AES_128, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-b.1.1> + confidentiality_limit: 1 << 23, + // ref: <https://datatracker.ietf.org/doc/html/rfc9001#section-b.1.2> + integrity_limit: 1 << 52, + }), +}; + +struct Chacha20Poly1305Aead(AeadAlgorithm); + +impl Tls13AeadAlgorithm for Chacha20Poly1305Aead { + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> { + self.0.encrypter(key, iv) + } + + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> { + self.0.decrypter(key, iv) + } + + fn key_len(&self) -> usize { + self.0.key_len() + } + + fn extract_keys( + &self, + key: AeadKey, + iv: Iv, + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv }) + } + + fn fips(&self) -> bool { + false // chacha20poly1305 not FIPS approved + } +} + +struct Aes256GcmAead(AeadAlgorithm); + +impl Tls13AeadAlgorithm for Aes256GcmAead { + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> { + self.0.encrypter(key, iv) + } + + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> { + self.0.decrypter(key, iv) + } + + fn key_len(&self) -> usize { + self.0.key_len() + } + + fn extract_keys( + &self, + key: AeadKey, + iv: Iv, + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + Ok(ConnectionTrafficSecrets::Aes256Gcm { key, iv }) + } + + fn fips(&self) -> bool { + super::fips() + } +} + +struct Aes128GcmAead(AeadAlgorithm); + +impl Tls13AeadAlgorithm for Aes128GcmAead { + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> { + self.0.encrypter(key, iv) + } + + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> { + self.0.decrypter(key, iv) + } + + fn key_len(&self) -> usize { + self.0.key_len() + } + + fn extract_keys( + &self, + key: AeadKey, + iv: Iv, + ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> { + Ok(ConnectionTrafficSecrets::Aes128Gcm { key, iv }) + } + + fn fips(&self) -> bool { + super::fips() + } +} + +// common encrypter/decrypter/key_len items for above Tls13AeadAlgorithm impls +struct AeadAlgorithm(&'static aead::Algorithm); + +impl AeadAlgorithm { + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> { + // safety: the caller arranges that `key` is `key_len()` in bytes, so this unwrap is safe. + Box::new(Tls13MessageEncrypter { + enc_key: aead::LessSafeKey::new(aead::UnboundKey::new(self.0, key.as_ref()).unwrap()), + iv, + }) + } + + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> { + // safety: the caller arranges that `key` is `key_len()` in bytes, so this unwrap is safe. + Box::new(Tls13MessageDecrypter { + dec_key: aead::LessSafeKey::new(aead::UnboundKey::new(self.0, key.as_ref()).unwrap()), + iv, + }) + } + + fn key_len(&self) -> usize { + self.0.key_len() + } +} + +struct Tls13MessageEncrypter { + enc_key: aead::LessSafeKey, + iv: Iv, +} + +struct Tls13MessageDecrypter { + dec_key: aead::LessSafeKey, + iv: Iv, +} + +impl MessageEncrypter for Tls13MessageEncrypter { + fn encrypt( + &mut self, + msg: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result<OutboundOpaqueMessage, Error> { + let total_len = self.encrypted_payload_len(msg.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, seq).0); + let aad = aead::Aad::from(make_tls13_aad(total_len)); + payload.extend_from_chunks(&msg.payload); + payload.extend_from_slice(&msg.typ.to_array()); + + self.enc_key + .seal_in_place_append_tag(nonce, aad, &mut payload) + .map_err(|_| Error::EncryptError)?; + + Ok(OutboundOpaqueMessage::new( + ContentType::ApplicationData, + // Note: all TLS 1.3 application data records use TLSv1_2 (0x0303) as the legacy record + // protocol version, see https://www.rfc-editor.org/rfc/rfc8446#section-5.1 + ProtocolVersion::TLSv1_2, + payload, + )) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + 1 + self.enc_key.algorithm().tag_len() + } +} + +impl MessageDecrypter for Tls13MessageDecrypter { + fn decrypt<'a>( + &mut self, + mut msg: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result<InboundPlainMessage<'a>, Error> { + let payload = &mut msg.payload; + if payload.len() < self.dec_key.algorithm().tag_len() { + return Err(Error::DecryptError); + } + + let nonce = aead::Nonce::assume_unique_for_key(Nonce::new(&self.iv, seq).0); + let aad = aead::Aad::from(make_tls13_aad(payload.len())); + let plain_len = self + .dec_key + .open_in_place(nonce, aad, payload) + .map_err(|_| Error::DecryptError)? + .len(); + + payload.truncate(plain_len); + msg.into_tls13_unpadded_message() + } +} + +struct RingHkdf(hkdf::Algorithm, hmac::Algorithm); + +impl Hkdf for RingHkdf { + fn extract_from_zero_ikm(&self, salt: Option<&[u8]>) -> Box<dyn HkdfExpander> { + let zeroes = [0u8; OkmBlock::MAX_LEN]; + let salt = match salt { + Some(salt) => salt, + None => &zeroes[..self.0.len()], + }; + Box::new(RingHkdfExpander { + alg: self.0, + prk: hkdf::Salt::new(self.0, salt).extract(&zeroes[..self.0.len()]), + }) + } + + fn extract_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Box<dyn HkdfExpander> { + let zeroes = [0u8; OkmBlock::MAX_LEN]; + let salt = match salt { + Some(salt) => salt, + None => &zeroes[..self.0.len()], + }; + Box::new(RingHkdfExpander { + alg: self.0, + prk: hkdf::Salt::new(self.0, salt).extract(secret), + }) + } + + fn expander_for_okm(&self, okm: &OkmBlock) -> Box<dyn HkdfExpander> { + Box::new(RingHkdfExpander { + alg: self.0, + prk: hkdf::Prk::new_less_safe(self.0, okm.as_ref()), + }) + } + + fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> crypto::hmac::Tag { + crypto::hmac::Tag::new(hmac::sign(&hmac::Key::new(self.1, key.as_ref()), message).as_ref()) + } + + fn fips(&self) -> bool { + super::fips() + } +} + +struct RingHkdfExpander { + alg: hkdf::Algorithm, + prk: hkdf::Prk, +} + +impl HkdfExpander for RingHkdfExpander { + fn expand_slice(&self, info: &[&[u8]], output: &mut [u8]) -> Result<(), OutputLengthError> { + self.prk + .expand(info, Len(output.len())) + .and_then(|okm| okm.fill(output)) + .map_err(|_| OutputLengthError) + } + + fn expand_block(&self, info: &[&[u8]]) -> OkmBlock { + let mut buf = [0u8; OkmBlock::MAX_LEN]; + let output = &mut buf[..self.hash_len()]; + self.prk + .expand(info, Len(output.len())) + .and_then(|okm| okm.fill(output)) + .unwrap(); + OkmBlock::new(output) + } + + fn hash_len(&self) -> usize { + self.alg.len() + } +} + +struct Len(usize); + +impl KeyType for Len { + fn len(&self) -> usize { + self.0 + } +} diff --git a/vendor/rustls/src/crypto/signer.rs b/vendor/rustls/src/crypto/signer.rs new file mode 100644 index 00000000..8028e74b --- /dev/null +++ b/vendor/rustls/src/crypto/signer.rs @@ -0,0 +1,233 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::fmt::Debug; + +use pki_types::{AlgorithmIdentifier, CertificateDer, PrivateKeyDer, SubjectPublicKeyInfoDer}; + +use super::CryptoProvider; +use crate::client::ResolvesClientCert; +use crate::enums::{SignatureAlgorithm, SignatureScheme}; +use crate::error::{Error, InconsistentKeys}; +use crate::server::{ClientHello, ParsedCertificate, ResolvesServerCert}; +use crate::sync::Arc; +use crate::x509; + +/// An abstract signing key. +/// +/// This interface is used by rustls to use a private signing key +/// for authentication. This includes server and client authentication. +/// +/// Objects of this type are always used within Rustls as +/// `Arc<dyn SigningKey>`. There are no concrete public structs in Rustls +/// that implement this trait. +/// +/// There are two main ways to get a signing key: +/// +/// - [`KeyProvider::load_private_key()`], or +/// - some other method outside of the `KeyProvider` extension trait, +/// for instance: +/// - [`crypto::ring::sign::any_ecdsa_type()`] +/// - [`crypto::ring::sign::any_eddsa_type()`] +/// - [`crypto::ring::sign::any_supported_type()`] +/// - [`crypto::aws_lc_rs::sign::any_ecdsa_type()`] +/// - [`crypto::aws_lc_rs::sign::any_eddsa_type()`] +/// - [`crypto::aws_lc_rs::sign::any_supported_type()`] +/// +/// The `KeyProvider` method `load_private_key()` is called under the hood by +/// [`ConfigBuilder::with_single_cert()`], +/// [`ConfigBuilder::with_client_auth_cert()`], and +/// [`ConfigBuilder::with_single_cert_with_ocsp()`]. +/// +/// A signing key created outside of the `KeyProvider` extension trait can be used +/// to create a [`CertifiedKey`], which in turn can be used to create a +/// [`ResolvesServerCertUsingSni`]. Alternately, a `CertifiedKey` can be returned from a +/// custom implementation of the [`ResolvesServerCert`] or [`ResolvesClientCert`] traits. +/// +/// [`KeyProvider::load_private_key()`]: crate::crypto::KeyProvider::load_private_key +/// [`ConfigBuilder::with_single_cert()`]: crate::ConfigBuilder::with_single_cert +/// [`ConfigBuilder::with_single_cert_with_ocsp()`]: crate::ConfigBuilder::with_single_cert_with_ocsp +/// [`ConfigBuilder::with_client_auth_cert()`]: crate::ConfigBuilder::with_client_auth_cert +/// [`crypto::ring::sign::any_ecdsa_type()`]: crate::crypto::ring::sign::any_ecdsa_type +/// [`crypto::ring::sign::any_eddsa_type()`]: crate::crypto::ring::sign::any_eddsa_type +/// [`crypto::ring::sign::any_supported_type()`]: crate::crypto::ring::sign::any_supported_type +/// [`crypto::aws_lc_rs::sign::any_ecdsa_type()`]: crate::crypto::aws_lc_rs::sign::any_ecdsa_type +/// [`crypto::aws_lc_rs::sign::any_eddsa_type()`]: crate::crypto::aws_lc_rs::sign::any_eddsa_type +/// [`crypto::aws_lc_rs::sign::any_supported_type()`]: crate::crypto::aws_lc_rs::sign::any_supported_type +/// [`ResolvesServerCertUsingSni`]: crate::server::ResolvesServerCertUsingSni +/// [`ResolvesServerCert`]: crate::server::ResolvesServerCert +/// [`ResolvesClientCert`]: crate::client::ResolvesClientCert +pub trait SigningKey: Debug + Send + Sync { + /// Choose a `SignatureScheme` from those offered. + /// + /// Expresses the choice by returning something that implements `Signer`, + /// using the chosen scheme. + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option<Box<dyn Signer>>; + + /// Get the RFC 5280-compliant SubjectPublicKeyInfo (SPKI) of this [`SigningKey`] if available. + fn public_key(&self) -> Option<SubjectPublicKeyInfoDer<'_>> { + // Opt-out by default + None + } + + /// What kind of key we have. + fn algorithm(&self) -> SignatureAlgorithm; +} + +/// A thing that can sign a message. +pub trait Signer: Debug + Send + Sync { + /// Signs `message` using the selected scheme. + /// + /// `message` is not hashed; the implementer must hash it using the hash function + /// implicit in [`Self::scheme()`]. + /// + /// The returned signature format is also defined by [`Self::scheme()`]. + fn sign(&self, message: &[u8]) -> Result<Vec<u8>, Error>; + + /// Reveals which scheme will be used when you call [`Self::sign()`]. + fn scheme(&self) -> SignatureScheme; +} + +/// Server certificate resolver which always resolves to the same certificate and key. +/// +/// For use with [`ConfigBuilder::with_cert_resolver()`]. +/// +/// [`ConfigBuilder::with_cert_resolver()`]: crate::ConfigBuilder::with_cert_resolver +#[derive(Debug)] +pub struct SingleCertAndKey(Arc<CertifiedKey>); + +impl From<CertifiedKey> for SingleCertAndKey { + fn from(certified_key: CertifiedKey) -> Self { + Self(Arc::new(certified_key)) + } +} + +impl From<Arc<CertifiedKey>> for SingleCertAndKey { + fn from(certified_key: Arc<CertifiedKey>) -> Self { + Self(certified_key) + } +} + +impl ResolvesClientCert for SingleCertAndKey { + fn resolve( + &self, + _root_hint_subjects: &[&[u8]], + _sigschemes: &[SignatureScheme], + ) -> Option<Arc<CertifiedKey>> { + Some(self.0.clone()) + } + + fn has_certs(&self) -> bool { + true + } +} + +impl ResolvesServerCert for SingleCertAndKey { + fn resolve(&self, _client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> { + Some(self.0.clone()) + } +} + +/// A packaged-together certificate chain, matching `SigningKey` and +/// optional stapled OCSP response. +/// +/// Note: this struct is also used to represent an [RFC 7250] raw public key, +/// when the client/server is configured to use raw public keys instead of +/// certificates. +/// +/// [RFC 7250]: https://tools.ietf.org/html/rfc7250 +#[derive(Clone, Debug)] +pub struct CertifiedKey { + /// The certificate chain or raw public key. + pub cert: Vec<CertificateDer<'static>>, + + /// The certified key. + pub key: Arc<dyn SigningKey>, + + /// An optional OCSP response from the certificate issuer, + /// attesting to its continued validity. + pub ocsp: Option<Vec<u8>>, +} + +impl CertifiedKey { + /// Create a new `CertifiedKey` from a certificate chain and DER-encoded private key. + /// + /// Attempt to parse the private key with the given [`CryptoProvider`]'s [`KeyProvider`] and + /// verify that it matches the public key in the first certificate of the `cert_chain` + /// if possible. + /// + /// [`KeyProvider`]: crate::crypto::KeyProvider + pub fn from_der( + cert_chain: Vec<CertificateDer<'static>>, + key: PrivateKeyDer<'static>, + provider: &CryptoProvider, + ) -> Result<Self, Error> { + let private_key = provider + .key_provider + .load_private_key(key)?; + + let certified_key = Self::new(cert_chain, private_key); + match certified_key.keys_match() { + // Don't treat unknown consistency as an error + Ok(()) | Err(Error::InconsistentKeys(InconsistentKeys::Unknown)) => Ok(certified_key), + Err(err) => Err(err), + } + } + + /// Make a new CertifiedKey, with the given chain and key. + /// + /// The cert chain must not be empty. The first certificate in the chain + /// must be the end-entity certificate. + pub fn new(cert: Vec<CertificateDer<'static>>, key: Arc<dyn SigningKey>) -> Self { + Self { + cert, + key, + ocsp: None, + } + } + + /// Verify the consistency of this [`CertifiedKey`]'s public and private keys. + /// This is done by performing a comparison of SubjectPublicKeyInfo bytes. + pub fn keys_match(&self) -> Result<(), Error> { + let Some(key_spki) = self.key.public_key() else { + return Err(InconsistentKeys::Unknown.into()); + }; + + let cert = ParsedCertificate::try_from(self.end_entity_cert()?)?; + match key_spki == cert.subject_public_key_info() { + true => Ok(()), + false => Err(InconsistentKeys::KeyMismatch.into()), + } + } + + /// The end-entity certificate. + pub fn end_entity_cert(&self) -> Result<&CertificateDer<'_>, Error> { + self.cert + .first() + .ok_or(Error::NoCertificatesPresented) + } +} + +#[cfg_attr(not(any(feature = "aws_lc_rs", feature = "ring")), allow(dead_code))] +pub(crate) fn public_key_to_spki( + alg_id: &AlgorithmIdentifier, + public_key: impl AsRef<[u8]>, +) -> SubjectPublicKeyInfoDer<'static> { + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + // + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + // + // note that the `pki_types::AlgorithmIdentifier` type is the + // concatenation of `algorithm` and `parameters`, but misses the + // outer `Sequence`. + + let mut spki_inner = x509::wrap_in_sequence(alg_id.as_ref()); + spki_inner.extend(&x509::wrap_in_bit_string(public_key.as_ref())); + + let spki = x509::wrap_in_sequence(&spki_inner); + + SubjectPublicKeyInfoDer::from(spki) +} diff --git a/vendor/rustls/src/crypto/tls12.rs b/vendor/rustls/src/crypto/tls12.rs new file mode 100644 index 00000000..7128de4b --- /dev/null +++ b/vendor/rustls/src/crypto/tls12.rs @@ -0,0 +1,169 @@ +use alloc::boxed::Box; + +use super::{ActiveKeyExchange, hmac}; +use crate::error::Error; +use crate::version::TLS12; + +/// Implements [`Prf`] using a [`hmac::Hmac`]. +pub struct PrfUsingHmac<'a>(pub &'a dyn hmac::Hmac); + +impl Prf for PrfUsingHmac<'_> { + fn for_key_exchange( + &self, + output: &mut [u8; 48], + kx: Box<dyn ActiveKeyExchange>, + peer_pub_key: &[u8], + label: &[u8], + seed: &[u8], + ) -> Result<(), Error> { + prf( + output, + self.0 + .with_key( + kx.complete_for_tls_version(peer_pub_key, &TLS12)? + .secret_bytes(), + ) + .as_ref(), + label, + seed, + ); + Ok(()) + } + + fn for_secret(&self, output: &mut [u8], secret: &[u8], label: &[u8], seed: &[u8]) { + prf(output, self.0.with_key(secret).as_ref(), label, seed); + } +} + +/// An instantiation of the TLS1.2 PRF with a specific, implicit hash function. +/// +/// See the definition in [RFC5246 section 5](https://www.rfc-editor.org/rfc/rfc5246#section-5). +/// +/// See [`PrfUsingHmac`] as a route to implementing this trait with just +/// an implementation of [`hmac::Hmac`]. +pub trait Prf: Send + Sync { + /// Computes `PRF(secret, label, seed)` using the secret from a completed key exchange. + /// + /// Completes the given key exchange, and then uses the resulting shared secret + /// to compute the PRF, writing the result into `output`. + /// + /// The caller guarantees that `label`, `seed` are non-empty. The caller makes no + /// guarantees about the contents of `peer_pub_key`. It must be validated by + /// [`ActiveKeyExchange::complete`]. + fn for_key_exchange( + &self, + output: &mut [u8; 48], + kx: Box<dyn ActiveKeyExchange>, + peer_pub_key: &[u8], + label: &[u8], + seed: &[u8], + ) -> Result<(), Error>; + + /// Computes `PRF(secret, label, seed)`, writing the result into `output`. + /// + /// The caller guarantees that `secret`, `label`, and `seed` are non-empty. + fn for_secret(&self, output: &mut [u8], secret: &[u8], label: &[u8], seed: &[u8]); + + /// Return `true` if this is backed by a FIPS-approved implementation. + fn fips(&self) -> bool { + false + } +} + +pub(crate) fn prf(out: &mut [u8], hmac_key: &dyn hmac::Key, label: &[u8], seed: &[u8]) { + // A(1) + let mut current_a = hmac_key.sign(&[label, seed]); + + let chunk_size = hmac_key.tag_len(); + for chunk in out.chunks_mut(chunk_size) { + // P_hash[i] = HMAC_hash(secret, A(i) + seed) + let p_term = hmac_key.sign(&[current_a.as_ref(), label, seed]); + chunk.copy_from_slice(&p_term.as_ref()[..chunk.len()]); + + // A(i+1) = HMAC_hash(secret, A(i)) + current_a = hmac_key.sign(&[current_a.as_ref()]); + } +} + +#[cfg(all(test, feature = "ring"))] +mod tests { + use crate::crypto::hmac::Hmac; + // nb: crypto::aws_lc_rs provider doesn't provide (or need) hmac, + // so cannot be used for this test. + use crate::crypto::ring::hmac; + + // Below known answer tests come from https://mailarchive.ietf.org/arch/msg/tls/fzVCzk-z3FShgGJ6DOXqM1ydxms/ + + #[test] + fn check_sha256() { + let secret = b"\x9b\xbe\x43\x6b\xa9\x40\xf0\x17\xb1\x76\x52\x84\x9a\x71\xdb\x35"; + let seed = b"\xa0\xba\x9f\x93\x6c\xda\x31\x18\x27\xa6\xf7\x96\xff\xd5\x19\x8c"; + let label = b"test label"; + let expect = include_bytes!("../testdata/prf-result.1.bin"); + let mut output = [0u8; 100]; + + super::prf( + &mut output, + &*hmac::HMAC_SHA256.with_key(secret), + label, + seed, + ); + assert_eq!(expect.len(), output.len()); + assert_eq!(expect.to_vec(), output.to_vec()); + } + + #[test] + fn check_sha512() { + let secret = b"\xb0\x32\x35\x23\xc1\x85\x35\x99\x58\x4d\x88\x56\x8b\xbb\x05\xeb"; + let seed = b"\xd4\x64\x0e\x12\xe4\xbc\xdb\xfb\x43\x7f\x03\xe6\xae\x41\x8e\xe5"; + let label = b"test label"; + let expect = include_bytes!("../testdata/prf-result.2.bin"); + let mut output = [0u8; 196]; + + super::prf( + &mut output, + &*hmac::HMAC_SHA512.with_key(secret), + label, + seed, + ); + assert_eq!(expect.len(), output.len()); + assert_eq!(expect.to_vec(), output.to_vec()); + } + + #[test] + fn check_sha384() { + let secret = b"\xb8\x0b\x73\x3d\x6c\xee\xfc\xdc\x71\x56\x6e\xa4\x8e\x55\x67\xdf"; + let seed = b"\xcd\x66\x5c\xf6\xa8\x44\x7d\xd6\xff\x8b\x27\x55\x5e\xdb\x74\x65"; + let label = b"test label"; + let expect = include_bytes!("../testdata/prf-result.3.bin"); + let mut output = [0u8; 148]; + + super::prf( + &mut output, + &*hmac::HMAC_SHA384.with_key(secret), + label, + seed, + ); + assert_eq!(expect.len(), output.len()); + assert_eq!(expect.to_vec(), output.to_vec()); + } +} + +#[cfg(all(bench, feature = "ring"))] +mod benchmarks { + #[bench] + fn bench_sha256(b: &mut test::Bencher) { + use crate::crypto::hmac::Hmac; + use crate::crypto::ring::hmac; + + let label = &b"extended master secret"[..]; + let seed = [0u8; 32]; + let key = &b"secret"[..]; + + b.iter(|| { + let mut out = [0u8; 48]; + super::prf(&mut out, &*hmac::HMAC_SHA256.with_key(key), &label, &seed); + test::black_box(out); + }); + } +} diff --git a/vendor/rustls/src/crypto/tls13.rs b/vendor/rustls/src/crypto/tls13.rs new file mode 100644 index 00000000..50ecaa89 --- /dev/null +++ b/vendor/rustls/src/crypto/tls13.rs @@ -0,0 +1,406 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; + +use zeroize::Zeroize; + +use super::{ActiveKeyExchange, hmac}; +use crate::error::Error; +use crate::version::TLS13; + +/// Implementation of `HkdfExpander` via `hmac::Key`. +pub struct HkdfExpanderUsingHmac(Box<dyn hmac::Key>); + +impl HkdfExpanderUsingHmac { + fn expand_unchecked(&self, info: &[&[u8]], output: &mut [u8]) { + let mut term = hmac::Tag::new(b""); + + for (n, chunk) in output + .chunks_mut(self.0.tag_len()) + .enumerate() + { + term = self + .0 + .sign_concat(term.as_ref(), info, &[(n + 1) as u8]); + chunk.copy_from_slice(&term.as_ref()[..chunk.len()]); + } + } +} + +impl HkdfExpander for HkdfExpanderUsingHmac { + fn expand_slice(&self, info: &[&[u8]], output: &mut [u8]) -> Result<(), OutputLengthError> { + if output.len() > 255 * self.0.tag_len() { + return Err(OutputLengthError); + } + + self.expand_unchecked(info, output); + Ok(()) + } + + fn expand_block(&self, info: &[&[u8]]) -> OkmBlock { + let mut tag = [0u8; hmac::Tag::MAX_LEN]; + let reduced_tag = &mut tag[..self.0.tag_len()]; + self.expand_unchecked(info, reduced_tag); + OkmBlock::new(reduced_tag) + } + + fn hash_len(&self) -> usize { + self.0.tag_len() + } +} + +/// Implementation of `Hkdf` (and thence `HkdfExpander`) via `hmac::Hmac`. +pub struct HkdfUsingHmac<'a>(pub &'a dyn hmac::Hmac); + +impl Hkdf for HkdfUsingHmac<'_> { + fn extract_from_zero_ikm(&self, salt: Option<&[u8]>) -> Box<dyn HkdfExpander> { + let zeroes = [0u8; hmac::Tag::MAX_LEN]; + Box::new(HkdfExpanderUsingHmac(self.0.with_key( + &self.extract_prk_from_secret(salt, &zeroes[..self.0.hash_output_len()]), + ))) + } + + fn extract_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Box<dyn HkdfExpander> { + Box::new(HkdfExpanderUsingHmac( + self.0 + .with_key(&self.extract_prk_from_secret(salt, secret)), + )) + } + + fn expander_for_okm(&self, okm: &OkmBlock) -> Box<dyn HkdfExpander> { + Box::new(HkdfExpanderUsingHmac(self.0.with_key(okm.as_ref()))) + } + + fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> hmac::Tag { + self.0 + .with_key(key.as_ref()) + .sign(&[message]) + } +} + +impl HkdfPrkExtract for HkdfUsingHmac<'_> { + fn extract_prk_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Vec<u8> { + let zeroes = [0u8; hmac::Tag::MAX_LEN]; + let salt = match salt { + Some(salt) => salt, + None => &zeroes[..self.0.hash_output_len()], + }; + self.0 + .with_key(salt) + .sign(&[secret]) + .as_ref() + .to_vec() + } +} + +/// Implementation of `HKDF-Expand` with an implicitly stored and immutable `PRK`. +pub trait HkdfExpander: Send + Sync { + /// `HKDF-Expand(PRK, info, L)` into a slice. + /// + /// Where: + /// + /// - `PRK` is the implicit key material represented by this instance. + /// - `L` is `output.len()`. + /// - `info` is a slice of byte slices, which should be processed sequentially + /// (or concatenated if that is not possible). + /// + /// Returns `Err(OutputLengthError)` if `L` is larger than `255 * HashLen`. + /// Otherwise, writes to `output`. + fn expand_slice(&self, info: &[&[u8]], output: &mut [u8]) -> Result<(), OutputLengthError>; + + /// `HKDF-Expand(PRK, info, L=HashLen)` returned as a value. + /// + /// - `PRK` is the implicit key material represented by this instance. + /// - `L := HashLen`. + /// - `info` is a slice of byte slices, which should be processed sequentially + /// (or concatenated if that is not possible). + /// + /// This is infallible, because by definition `OkmBlock` is always exactly + /// `HashLen` bytes long. + fn expand_block(&self, info: &[&[u8]]) -> OkmBlock; + + /// Return what `HashLen` is for this instance. + /// + /// This must be no larger than [`OkmBlock::MAX_LEN`]. + fn hash_len(&self) -> usize; +} + +/// A HKDF implementation oriented to the needs of TLS1.3. +/// +/// See [RFC5869](https://datatracker.ietf.org/doc/html/rfc5869) for the terminology +/// used in this definition. +/// +/// You can use [`HkdfUsingHmac`] which implements this trait on top of an implementation +/// of [`hmac::Hmac`]. +pub trait Hkdf: Send + Sync { + /// `HKDF-Extract(salt, 0_HashLen)` + /// + /// `0_HashLen` is a string of `HashLen` zero bytes. + /// + /// A `salt` of `None` should be treated as a sequence of `HashLen` zero bytes. + fn extract_from_zero_ikm(&self, salt: Option<&[u8]>) -> Box<dyn HkdfExpander>; + + /// `HKDF-Extract(salt, secret)` + /// + /// A `salt` of `None` should be treated as a sequence of `HashLen` zero bytes. + fn extract_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Box<dyn HkdfExpander>; + + /// `HKDF-Extract(salt, shared_secret)` where `shared_secret` is the result of a key exchange. + /// + /// Custom implementations should complete the key exchange by calling + /// `kx.complete(peer_pub_key)` and then using this as the input keying material to + /// `HKDF-Extract`. + /// + /// A `salt` of `None` should be treated as a sequence of `HashLen` zero bytes. + fn extract_from_kx_shared_secret( + &self, + salt: Option<&[u8]>, + kx: Box<dyn ActiveKeyExchange>, + peer_pub_key: &[u8], + ) -> Result<Box<dyn HkdfExpander>, Error> { + Ok(self.extract_from_secret( + salt, + kx.complete_for_tls_version(peer_pub_key, &TLS13)? + .secret_bytes(), + )) + } + + /// Build a `HkdfExpander` using `okm` as the secret PRK. + fn expander_for_okm(&self, okm: &OkmBlock) -> Box<dyn HkdfExpander>; + + /// Signs `message` using `key` viewed as a HMAC key. + /// + /// This should use the same hash function as the HKDF functions in this + /// trait. + /// + /// See [RFC2104](https://datatracker.ietf.org/doc/html/rfc2104) for the + /// definition of HMAC. + fn hmac_sign(&self, key: &OkmBlock, message: &[u8]) -> hmac::Tag; + + /// Return `true` if this is backed by a FIPS-approved implementation. + fn fips(&self) -> bool { + false + } +} + +/// An extended HKDF implementation that supports directly extracting a pseudo-random key (PRK). +/// +/// The base [`Hkdf`] trait is tailored to the needs of TLS 1.3, where all extracted PRKs +/// are expanded as-is, and so can be safely encapsulated without exposing the caller +/// to the key material. +/// +/// In other contexts (for example, hybrid public key encryption (HPKE)) it may be necessary +/// to use the extracted PRK directly for purposes other than an immediate expansion. +/// This trait can be implemented to offer this functionality when it is required. +pub(crate) trait HkdfPrkExtract: Hkdf { + /// `HKDF-Extract(salt, secret)` + /// + /// A `salt` of `None` should be treated as a sequence of `HashLen` zero bytes. + /// + /// In most cases you should prefer [`Hkdf::extract_from_secret`] and using the + /// returned [HkdfExpander] instead of handling the PRK directly. + fn extract_prk_from_secret(&self, salt: Option<&[u8]>, secret: &[u8]) -> Vec<u8>; +} + +/// `HKDF-Expand(PRK, info, L)` to construct any type from a byte array. +/// +/// - `PRK` is the implicit key material represented by this instance. +/// - `L := N`; N is the size of the byte array. +/// - `info` is a slice of byte slices, which should be processed sequentially +/// (or concatenated if that is not possible). +/// +/// This is infallible, because the set of types (and therefore their length) is known +/// at compile time. +pub fn expand<T, const N: usize>(expander: &dyn HkdfExpander, info: &[&[u8]]) -> T +where + T: From<[u8; N]>, +{ + let mut output = [0u8; N]; + expander + .expand_slice(info, &mut output) + .expect("expand type parameter T is too large"); + T::from(output) +} + +/// Output key material from HKDF, as a value type. +#[derive(Clone)] +pub struct OkmBlock { + buf: [u8; Self::MAX_LEN], + used: usize, +} + +impl OkmBlock { + /// Build a single OKM block by copying a byte slice. + /// + /// The slice can be up to [`OkmBlock::MAX_LEN`] bytes in length. + pub fn new(bytes: &[u8]) -> Self { + let mut tag = Self { + buf: [0u8; Self::MAX_LEN], + used: bytes.len(), + }; + tag.buf[..bytes.len()].copy_from_slice(bytes); + tag + } + + /// Maximum supported HMAC tag size: supports up to SHA512. + pub const MAX_LEN: usize = 64; +} + +impl Drop for OkmBlock { + fn drop(&mut self) { + self.buf.zeroize(); + } +} + +impl AsRef<[u8]> for OkmBlock { + fn as_ref(&self) -> &[u8] { + &self.buf[..self.used] + } +} + +/// An error type used for `HkdfExpander::expand_slice` when +/// the slice exceeds the maximum HKDF output length. +#[derive(Debug)] +pub struct OutputLengthError; + +#[cfg(all(test, feature = "ring"))] +mod tests { + use std::prelude::v1::*; + + use super::{Hkdf, HkdfUsingHmac, expand}; + // nb: crypto::aws_lc_rs provider doesn't provide (or need) hmac, + // so cannot be used for this test. + use crate::crypto::ring::hmac; + + struct ByteArray<const N: usize>([u8; N]); + + impl<const N: usize> From<[u8; N]> for ByteArray<N> { + fn from(array: [u8; N]) -> Self { + Self(array) + } + } + + /// Test cases from appendix A in the RFC, minus cases requiring SHA1. + + #[test] + fn test_case_1() { + let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256); + let ikm = &[0x0b; 22]; + let salt = &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, + ]; + let info: &[&[u8]] = &[ + &[0xf0, 0xf1, 0xf2], + &[0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9], + ]; + + let output: ByteArray<42> = expand( + hkdf.extract_from_secret(Some(salt), ikm) + .as_ref(), + info, + ); + + assert_eq!( + &output.0, + &[ + 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, + 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, + 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65 + ] + ); + } + + #[test] + fn test_case_2() { + let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256); + let ikm: Vec<u8> = (0x00u8..=0x4f).collect(); + let salt: Vec<u8> = (0x60u8..=0xaf).collect(); + let info: Vec<u8> = (0xb0u8..=0xff).collect(); + + let output: ByteArray<82> = expand( + hkdf.extract_from_secret(Some(&salt), &ikm) + .as_ref(), + &[&info], + ); + + assert_eq!( + &output.0, + &[ + 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, 0xf7, 0x8c, 0x59, 0x6a, + 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, + 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, + 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, + 0x36, 0x77, 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, + 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, 0x1d, 0x87 + ] + ); + } + + #[test] + fn test_case_3() { + let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256); + let ikm = &[0x0b; 22]; + let salt = &[]; + let info = &[]; + + let output: ByteArray<42> = expand( + hkdf.extract_from_secret(Some(salt), ikm) + .as_ref(), + info, + ); + + assert_eq!( + &output.0, + &[ + 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, + 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, + 0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8 + ] + ); + } + + #[test] + fn test_salt_not_provided() { + // can't use test case 7, because we don't have (or want) SHA1. + // + // this output is generated with cryptography.io: + // + // >>> hkdf.HKDF(algorithm=hashes.SHA384(), length=96, salt=None, info=b"hello").derive(b"\x0b" * 40) + + let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA384); + let ikm = &[0x0b; 40]; + let info = &[&b"hel"[..], &b"lo"[..]]; + + let output: ByteArray<96> = expand( + hkdf.extract_from_secret(None, ikm) + .as_ref(), + info, + ); + + assert_eq!( + &output.0, + &[ + 0xd5, 0x45, 0xdd, 0x3a, 0xff, 0x5b, 0x19, 0x46, 0xd4, 0x86, 0xfd, 0xb8, 0xd8, 0x88, + 0x2e, 0xe0, 0x1c, 0xc1, 0xa5, 0x48, 0xb6, 0x05, 0x75, 0xe4, 0xd7, 0x5d, 0x0f, 0x5f, + 0x23, 0x40, 0xee, 0x6c, 0x9e, 0x7c, 0x65, 0xd0, 0xee, 0x79, 0xdb, 0xb2, 0x07, 0x1d, + 0x66, 0xa5, 0x50, 0xc4, 0x8a, 0xa3, 0x93, 0x86, 0x8b, 0x7c, 0x69, 0x41, 0x6b, 0x3e, + 0x61, 0x44, 0x98, 0xb8, 0xc2, 0xfc, 0x82, 0x82, 0xae, 0xcd, 0x46, 0xcf, 0xb1, 0x47, + 0xdc, 0xd0, 0x69, 0x0d, 0x19, 0xad, 0xe6, 0x6c, 0x70, 0xfe, 0x87, 0x92, 0x04, 0xb6, + 0x82, 0x2d, 0x97, 0x7e, 0x46, 0x80, 0x4c, 0xe5, 0x76, 0x72, 0xb4, 0xb8 + ] + ); + } + + #[test] + fn test_output_length_bounds() { + let hkdf = HkdfUsingHmac(&hmac::HMAC_SHA256); + let ikm = &[]; + let info = &[]; + + let mut output = [0u8; 32 * 255 + 1]; + assert!( + hkdf.extract_from_secret(None, ikm) + .expand_slice(info, &mut output) + .is_err() + ); + } +} diff --git a/vendor/rustls/src/enums.rs b/vendor/rustls/src/enums.rs new file mode 100644 index 00000000..f59ca3f9 --- /dev/null +++ b/vendor/rustls/src/enums.rs @@ -0,0 +1,691 @@ +#![allow(non_camel_case_types)] +#![allow(missing_docs)] +use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::enums::HashAlgorithm; + +enum_builder! { + /// The `AlertDescription` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum AlertDescription { + CloseNotify => 0x00, + UnexpectedMessage => 0x0a, + BadRecordMac => 0x14, + DecryptionFailed => 0x15, + RecordOverflow => 0x16, + DecompressionFailure => 0x1e, + HandshakeFailure => 0x28, + NoCertificate => 0x29, + BadCertificate => 0x2a, + UnsupportedCertificate => 0x2b, + CertificateRevoked => 0x2c, + CertificateExpired => 0x2d, + CertificateUnknown => 0x2e, + IllegalParameter => 0x2f, + UnknownCA => 0x30, + AccessDenied => 0x31, + DecodeError => 0x32, + DecryptError => 0x33, + ExportRestriction => 0x3c, + ProtocolVersion => 0x46, + InsufficientSecurity => 0x47, + InternalError => 0x50, + InappropriateFallback => 0x56, + UserCanceled => 0x5a, + NoRenegotiation => 0x64, + MissingExtension => 0x6d, + UnsupportedExtension => 0x6e, + CertificateUnobtainable => 0x6f, + UnrecognisedName => 0x70, + BadCertificateStatusResponse => 0x71, + BadCertificateHashValue => 0x72, + UnknownPSKIdentity => 0x73, + CertificateRequired => 0x74, + NoApplicationProtocol => 0x78, + EncryptedClientHelloRequired => 0x79, // https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#section-11.2 + } +} + +enum_builder! { + /// The `HandshakeType` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum HandshakeType { + HelloRequest => 0x00, + ClientHello => 0x01, + ServerHello => 0x02, + HelloVerifyRequest => 0x03, + NewSessionTicket => 0x04, + EndOfEarlyData => 0x05, + HelloRetryRequest => 0x06, + EncryptedExtensions => 0x08, + Certificate => 0x0b, + ServerKeyExchange => 0x0c, + CertificateRequest => 0x0d, + ServerHelloDone => 0x0e, + CertificateVerify => 0x0f, + ClientKeyExchange => 0x10, + Finished => 0x14, + CertificateURL => 0x15, + CertificateStatus => 0x16, + KeyUpdate => 0x18, + CompressedCertificate => 0x19, + MessageHash => 0xfe, + } +} + +enum_builder! { + /// The `ContentType` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum ContentType { + ChangeCipherSpec => 0x14, + Alert => 0x15, + Handshake => 0x16, + ApplicationData => 0x17, + Heartbeat => 0x18, + } +} + +enum_builder! { + /// The `ProtocolVersion` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u16)] + pub enum ProtocolVersion { + SSLv2 => 0x0002, + SSLv3 => 0x0300, + TLSv1_0 => 0x0301, + TLSv1_1 => 0x0302, + TLSv1_2 => 0x0303, + TLSv1_3 => 0x0304, + DTLSv1_0 => 0xFEFF, + DTLSv1_2 => 0xFEFD, + DTLSv1_3 => 0xFEFC, + } +} + +enum_builder! { + /// The `CipherSuite` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u16)] + pub enum CipherSuite { + TLS_NULL_WITH_NULL_NULL => 0x0000, + TLS_PSK_WITH_AES_128_GCM_SHA256 => 0x00a8, + TLS_PSK_WITH_AES_256_GCM_SHA384 => 0x00a9, + TLS_EMPTY_RENEGOTIATION_INFO_SCSV => 0x00ff, + TLS13_AES_128_GCM_SHA256 => 0x1301, + TLS13_AES_256_GCM_SHA384 => 0x1302, + TLS13_CHACHA20_POLY1305_SHA256 => 0x1303, + TLS13_AES_128_CCM_SHA256 => 0x1304, + TLS13_AES_128_CCM_8_SHA256 => 0x1305, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA => 0xc009, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA => 0xc00a, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA => 0xc013, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA => 0xc014, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 => 0xc023, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 => 0xc024, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 => 0xc027, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 => 0xc028, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 => 0xc02b, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 => 0xc02c, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 => 0xc02f, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 => 0xc030, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => 0xcca8, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 => 0xcca9, + + !Debug: + TLS_RSA_WITH_NULL_MD5 => 0x0001, + TLS_RSA_WITH_NULL_SHA => 0x0002, + TLS_RSA_EXPORT_WITH_RC4_40_MD5 => 0x0003, + TLS_RSA_WITH_RC4_128_MD5 => 0x0004, + TLS_RSA_WITH_RC4_128_SHA => 0x0005, + TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 => 0x0006, + TLS_RSA_WITH_IDEA_CBC_SHA => 0x0007, + TLS_RSA_EXPORT_WITH_DES40_CBC_SHA => 0x0008, + TLS_RSA_WITH_DES_CBC_SHA => 0x0009, + TLS_RSA_WITH_3DES_EDE_CBC_SHA => 0x000a, + TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA => 0x000b, + TLS_DH_DSS_WITH_DES_CBC_SHA => 0x000c, + TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA => 0x000d, + TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA => 0x000e, + TLS_DH_RSA_WITH_DES_CBC_SHA => 0x000f, + TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA => 0x0010, + TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA => 0x0011, + TLS_DHE_DSS_WITH_DES_CBC_SHA => 0x0012, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA => 0x0013, + TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA => 0x0014, + TLS_DHE_RSA_WITH_DES_CBC_SHA => 0x0015, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA => 0x0016, + TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 => 0x0017, + TLS_DH_anon_WITH_RC4_128_MD5 => 0x0018, + TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA => 0x0019, + TLS_DH_anon_WITH_DES_CBC_SHA => 0x001a, + TLS_DH_anon_WITH_3DES_EDE_CBC_SHA => 0x001b, + SSL_FORTEZZA_KEA_WITH_NULL_SHA => 0x001c, + SSL_FORTEZZA_KEA_WITH_FORTEZZA_CBC_SHA => 0x001d, + TLS_KRB5_WITH_DES_CBC_SHA_or_SSL_FORTEZZA_KEA_WITH_RC4_128_SHA => 0x001e, + TLS_KRB5_WITH_3DES_EDE_CBC_SHA => 0x001f, + TLS_KRB5_WITH_RC4_128_SHA => 0x0020, + TLS_KRB5_WITH_IDEA_CBC_SHA => 0x0021, + TLS_KRB5_WITH_DES_CBC_MD5 => 0x0022, + TLS_KRB5_WITH_3DES_EDE_CBC_MD5 => 0x0023, + TLS_KRB5_WITH_RC4_128_MD5 => 0x0024, + TLS_KRB5_WITH_IDEA_CBC_MD5 => 0x0025, + TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA => 0x0026, + TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA => 0x0027, + TLS_KRB5_EXPORT_WITH_RC4_40_SHA => 0x0028, + TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 => 0x0029, + TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 => 0x002a, + TLS_KRB5_EXPORT_WITH_RC4_40_MD5 => 0x002b, + TLS_PSK_WITH_NULL_SHA => 0x002c, + TLS_DHE_PSK_WITH_NULL_SHA => 0x002d, + TLS_RSA_PSK_WITH_NULL_SHA => 0x002e, + TLS_RSA_WITH_AES_128_CBC_SHA => 0x002f, + TLS_DH_DSS_WITH_AES_128_CBC_SHA => 0x0030, + TLS_DH_RSA_WITH_AES_128_CBC_SHA => 0x0031, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA => 0x0032, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA => 0x0033, + TLS_DH_anon_WITH_AES_128_CBC_SHA => 0x0034, + TLS_RSA_WITH_AES_256_CBC_SHA => 0x0035, + TLS_DH_DSS_WITH_AES_256_CBC_SHA => 0x0036, + TLS_DH_RSA_WITH_AES_256_CBC_SHA => 0x0037, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA => 0x0038, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA => 0x0039, + TLS_DH_anon_WITH_AES_256_CBC_SHA => 0x003a, + TLS_RSA_WITH_NULL_SHA256 => 0x003b, + TLS_RSA_WITH_AES_128_CBC_SHA256 => 0x003c, + TLS_RSA_WITH_AES_256_CBC_SHA256 => 0x003d, + TLS_DH_DSS_WITH_AES_128_CBC_SHA256 => 0x003e, + TLS_DH_RSA_WITH_AES_128_CBC_SHA256 => 0x003f, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 => 0x0040, + TLS_RSA_WITH_CAMELLIA_128_CBC_SHA => 0x0041, + TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA => 0x0042, + TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA => 0x0043, + TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA => 0x0044, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA => 0x0045, + TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA => 0x0046, + TLS_ECDH_ECDSA_WITH_NULL_SHA_draft => 0x0047, + TLS_ECDH_ECDSA_WITH_RC4_128_SHA_draft => 0x0048, + TLS_ECDH_ECDSA_WITH_DES_CBC_SHA_draft => 0x0049, + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA_draft => 0x004a, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA_draft => 0x004b, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA_draft => 0x004c, + TLS_ECDH_ECNRA_WITH_DES_CBC_SHA_draft => 0x004d, + TLS_ECDH_ECNRA_WITH_3DES_EDE_CBC_SHA_draft => 0x004e, + TLS_ECMQV_ECDSA_NULL_SHA_draft => 0x004f, + TLS_ECMQV_ECDSA_WITH_RC4_128_SHA_draft => 0x0050, + TLS_ECMQV_ECDSA_WITH_DES_CBC_SHA_draft => 0x0051, + TLS_ECMQV_ECDSA_WITH_3DES_EDE_CBC_SHA_draft => 0x0052, + TLS_ECMQV_ECNRA_NULL_SHA_draft => 0x0053, + TLS_ECMQV_ECNRA_WITH_RC4_128_SHA_draft => 0x0054, + TLS_ECMQV_ECNRA_WITH_DES_CBC_SHA_draft => 0x0055, + TLS_ECMQV_ECNRA_WITH_3DES_EDE_CBC_SHA_draft => 0x0056, + TLS_ECDH_anon_NULL_WITH_SHA_draft => 0x0057, + TLS_ECDH_anon_WITH_RC4_128_SHA_draft => 0x0058, + TLS_ECDH_anon_WITH_DES_CBC_SHA_draft => 0x0059, + TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA_draft => 0x005a, + TLS_ECDH_anon_EXPORT_WITH_DES40_CBC_SHA_draft => 0x005b, + TLS_ECDH_anon_EXPORT_WITH_RC4_40_SHA_draft => 0x005c, + TLS_RSA_EXPORT1024_WITH_RC4_56_MD5 => 0x0060, + TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5 => 0x0061, + TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA => 0x0062, + TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA => 0x0063, + TLS_RSA_EXPORT1024_WITH_RC4_56_SHA => 0x0064, + TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA => 0x0065, + TLS_DHE_DSS_WITH_RC4_128_SHA => 0x0066, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 => 0x0067, + TLS_DH_DSS_WITH_AES_256_CBC_SHA256 => 0x0068, + TLS_DH_RSA_WITH_AES_256_CBC_SHA256 => 0x0069, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 => 0x006a, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 => 0x006b, + TLS_DH_anon_WITH_AES_128_CBC_SHA256 => 0x006c, + TLS_DH_anon_WITH_AES_256_CBC_SHA256 => 0x006d, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_RMD => 0x0072, + TLS_DHE_DSS_WITH_AES_128_CBC_RMD => 0x0073, + TLS_DHE_DSS_WITH_AES_256_CBC_RMD => 0x0074, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_RMD => 0x0077, + TLS_DHE_RSA_WITH_AES_128_CBC_RMD => 0x0078, + TLS_DHE_RSA_WITH_AES_256_CBC_RMD => 0x0079, + TLS_RSA_WITH_3DES_EDE_CBC_RMD => 0x007c, + TLS_RSA_WITH_AES_128_CBC_RMD => 0x007d, + TLS_RSA_WITH_AES_256_CBC_RMD => 0x007e, + TLS_GOSTR341094_WITH_28147_CNT_IMIT => 0x0080, + TLS_GOSTR341001_WITH_28147_CNT_IMIT => 0x0081, + TLS_GOSTR341094_WITH_NULL_GOSTR3411 => 0x0082, + TLS_GOSTR341001_WITH_NULL_GOSTR3411 => 0x0083, + TLS_RSA_WITH_CAMELLIA_256_CBC_SHA => 0x0084, + TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA => 0x0085, + TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA => 0x0086, + TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA => 0x0087, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA => 0x0088, + TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA => 0x0089, + TLS_PSK_WITH_RC4_128_SHA => 0x008a, + TLS_PSK_WITH_3DES_EDE_CBC_SHA => 0x008b, + TLS_PSK_WITH_AES_128_CBC_SHA => 0x008c, + TLS_PSK_WITH_AES_256_CBC_SHA => 0x008d, + TLS_DHE_PSK_WITH_RC4_128_SHA => 0x008e, + TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA => 0x008f, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA => 0x0090, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA => 0x0091, + TLS_RSA_PSK_WITH_RC4_128_SHA => 0x0092, + TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA => 0x0093, + TLS_RSA_PSK_WITH_AES_128_CBC_SHA => 0x0094, + TLS_RSA_PSK_WITH_AES_256_CBC_SHA => 0x0095, + TLS_RSA_WITH_SEED_CBC_SHA => 0x0096, + TLS_DH_DSS_WITH_SEED_CBC_SHA => 0x0097, + TLS_DH_RSA_WITH_SEED_CBC_SHA => 0x0098, + TLS_DHE_DSS_WITH_SEED_CBC_SHA => 0x0099, + TLS_DHE_RSA_WITH_SEED_CBC_SHA => 0x009a, + TLS_DH_anon_WITH_SEED_CBC_SHA => 0x009b, + TLS_RSA_WITH_AES_128_GCM_SHA256 => 0x009c, + TLS_RSA_WITH_AES_256_GCM_SHA384 => 0x009d, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 => 0x009e, + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 => 0x009f, + TLS_DH_RSA_WITH_AES_128_GCM_SHA256 => 0x00a0, + TLS_DH_RSA_WITH_AES_256_GCM_SHA384 => 0x00a1, + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 => 0x00a2, + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 => 0x00a3, + TLS_DH_DSS_WITH_AES_128_GCM_SHA256 => 0x00a4, + TLS_DH_DSS_WITH_AES_256_GCM_SHA384 => 0x00a5, + TLS_DH_anon_WITH_AES_128_GCM_SHA256 => 0x00a6, + TLS_DH_anon_WITH_AES_256_GCM_SHA384 => 0x00a7, + TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 => 0x00aa, + TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 => 0x00ab, + TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 => 0x00ac, + TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 => 0x00ad, + TLS_PSK_WITH_AES_128_CBC_SHA256 => 0x00ae, + TLS_PSK_WITH_AES_256_CBC_SHA384 => 0x00af, + TLS_PSK_WITH_NULL_SHA256 => 0x00b0, + TLS_PSK_WITH_NULL_SHA384 => 0x00b1, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 => 0x00b2, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 => 0x00b3, + TLS_DHE_PSK_WITH_NULL_SHA256 => 0x00b4, + TLS_DHE_PSK_WITH_NULL_SHA384 => 0x00b5, + TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 => 0x00b6, + TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 => 0x00b7, + TLS_RSA_PSK_WITH_NULL_SHA256 => 0x00b8, + TLS_RSA_PSK_WITH_NULL_SHA384 => 0x00b9, + TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0x00ba, + TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 => 0x00bb, + TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0x00bc, + TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 => 0x00bd, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0x00be, + TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 => 0x00bf, + TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c0, + TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c1, + TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c2, + TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c3, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c4, + TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 => 0x00c5, + TLS_ECDH_ECDSA_WITH_NULL_SHA => 0xc001, + TLS_ECDH_ECDSA_WITH_RC4_128_SHA => 0xc002, + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA => 0xc003, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA => 0xc004, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA => 0xc005, + TLS_ECDHE_ECDSA_WITH_NULL_SHA => 0xc006, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA => 0xc007, + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA => 0xc008, + TLS_ECDH_RSA_WITH_NULL_SHA => 0xc00b, + TLS_ECDH_RSA_WITH_RC4_128_SHA => 0xc00c, + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA => 0xc00d, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA => 0xc00e, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA => 0xc00f, + TLS_ECDHE_RSA_WITH_NULL_SHA => 0xc010, + TLS_ECDHE_RSA_WITH_RC4_128_SHA => 0xc011, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA => 0xc012, + TLS_ECDH_anon_WITH_NULL_SHA => 0xc015, + TLS_ECDH_anon_WITH_RC4_128_SHA => 0xc016, + TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA => 0xc017, + TLS_ECDH_anon_WITH_AES_128_CBC_SHA => 0xc018, + TLS_ECDH_anon_WITH_AES_256_CBC_SHA => 0xc019, + TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA => 0xc01a, + TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA => 0xc01b, + TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA => 0xc01c, + TLS_SRP_SHA_WITH_AES_128_CBC_SHA => 0xc01d, + TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA => 0xc01e, + TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA => 0xc01f, + TLS_SRP_SHA_WITH_AES_256_CBC_SHA => 0xc020, + TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA => 0xc021, + TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA => 0xc022, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 => 0xc025, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 => 0xc026, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 => 0xc029, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 => 0xc02a, + TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 => 0xc02d, + TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 => 0xc02e, + TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 => 0xc031, + TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 => 0xc032, + TLS_ECDHE_PSK_WITH_RC4_128_SHA => 0xc033, + TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA => 0xc034, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA => 0xc035, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA => 0xc036, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 => 0xc037, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 => 0xc038, + TLS_ECDHE_PSK_WITH_NULL_SHA => 0xc039, + TLS_ECDHE_PSK_WITH_NULL_SHA256 => 0xc03a, + TLS_ECDHE_PSK_WITH_NULL_SHA384 => 0xc03b, + TLS_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc03c, + TLS_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc03d, + TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 => 0xc03e, + TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 => 0xc03f, + TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc040, + TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc041, + TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 => 0xc042, + TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 => 0xc043, + TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc044, + TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc045, + TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 => 0xc046, + TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 => 0xc047, + TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 => 0xc048, + TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 => 0xc049, + TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 => 0xc04a, + TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 => 0xc04b, + TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc04c, + TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc04d, + TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 => 0xc04e, + TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 => 0xc04f, + TLS_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc050, + TLS_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc051, + TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc052, + TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc053, + TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc054, + TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc055, + TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 => 0xc056, + TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 => 0xc057, + TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 => 0xc058, + TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 => 0xc059, + TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 => 0xc05a, + TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 => 0xc05b, + TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 => 0xc05c, + TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 => 0xc05d, + TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 => 0xc05e, + TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 => 0xc05f, + TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc060, + TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc061, + TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 => 0xc062, + TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 => 0xc063, + TLS_PSK_WITH_ARIA_128_CBC_SHA256 => 0xc064, + TLS_PSK_WITH_ARIA_256_CBC_SHA384 => 0xc065, + TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 => 0xc066, + TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 => 0xc067, + TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 => 0xc068, + TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 => 0xc069, + TLS_PSK_WITH_ARIA_128_GCM_SHA256 => 0xc06a, + TLS_PSK_WITH_ARIA_256_GCM_SHA384 => 0xc06b, + TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 => 0xc06c, + TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 => 0xc06d, + TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 => 0xc06e, + TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 => 0xc06f, + TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 => 0xc070, + TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 => 0xc071, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 => 0xc072, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 => 0xc073, + TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 => 0xc074, + TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 => 0xc075, + TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0xc076, + TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 => 0xc077, + TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 => 0xc078, + TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 => 0xc079, + TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc07a, + TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc07b, + TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc07c, + TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc07d, + TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc07e, + TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc07f, + TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 => 0xc080, + TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 => 0xc081, + TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 => 0xc082, + TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 => 0xc083, + TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 => 0xc084, + TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 => 0xc085, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc086, + TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc087, + TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc088, + TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc089, + TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc08a, + TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc08b, + TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 => 0xc08c, + TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 => 0xc08d, + TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 => 0xc08e, + TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 => 0xc08f, + TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 => 0xc090, + TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 => 0xc091, + TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 => 0xc092, + TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 => 0xc093, + TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 => 0xc094, + TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 => 0xc095, + TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 => 0xc096, + TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 => 0xc097, + TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 => 0xc098, + TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 => 0xc099, + TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 => 0xc09a, + TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 => 0xc09b, + TLS_RSA_WITH_AES_128_CCM => 0xc09c, + TLS_RSA_WITH_AES_256_CCM => 0xc09d, + TLS_DHE_RSA_WITH_AES_128_CCM => 0xc09e, + TLS_DHE_RSA_WITH_AES_256_CCM => 0xc09f, + TLS_RSA_WITH_AES_128_CCM_8 => 0xc0a0, + TLS_RSA_WITH_AES_256_CCM_8 => 0xc0a1, + TLS_DHE_RSA_WITH_AES_128_CCM_8 => 0xc0a2, + TLS_DHE_RSA_WITH_AES_256_CCM_8 => 0xc0a3, + TLS_PSK_WITH_AES_128_CCM => 0xc0a4, + TLS_PSK_WITH_AES_256_CCM => 0xc0a5, + TLS_DHE_PSK_WITH_AES_128_CCM => 0xc0a6, + TLS_DHE_PSK_WITH_AES_256_CCM => 0xc0a7, + TLS_PSK_WITH_AES_128_CCM_8 => 0xc0a8, + TLS_PSK_WITH_AES_256_CCM_8 => 0xc0a9, + TLS_PSK_DHE_WITH_AES_128_CCM_8 => 0xc0aa, + TLS_PSK_DHE_WITH_AES_256_CCM_8 => 0xc0ab, + TLS_ECDHE_ECDSA_WITH_AES_128_CCM => 0xc0ac, + TLS_ECDHE_ECDSA_WITH_AES_256_CCM => 0xc0ad, + TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 => 0xc0ae, + TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 => 0xc0af, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => 0xccaa, + TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 => 0xccab, + TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 => 0xccac, + TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 => 0xccad, + TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 => 0xccae, + SSL_RSA_FIPS_WITH_DES_CBC_SHA => 0xfefe, + SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA => 0xfeff, + } +} + +enum_builder! { + /// The `SignatureScheme` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u16)] + pub enum SignatureScheme { + RSA_PKCS1_SHA1 => 0x0201, + ECDSA_SHA1_Legacy => 0x0203, + RSA_PKCS1_SHA256 => 0x0401, + ECDSA_NISTP256_SHA256 => 0x0403, + RSA_PKCS1_SHA384 => 0x0501, + ECDSA_NISTP384_SHA384 => 0x0503, + RSA_PKCS1_SHA512 => 0x0601, + ECDSA_NISTP521_SHA512 => 0x0603, + RSA_PSS_SHA256 => 0x0804, + RSA_PSS_SHA384 => 0x0805, + RSA_PSS_SHA512 => 0x0806, + ED25519 => 0x0807, + ED448 => 0x0808, + // https://datatracker.ietf.org/doc/html/draft-ietf-tls-mldsa-00#name-iana-considerations + ML_DSA_44 => 0x0904, + ML_DSA_65 => 0x0905, + ML_DSA_87 => 0x0906, + } +} + +impl SignatureScheme { + pub(crate) fn algorithm(&self) -> SignatureAlgorithm { + match *self { + Self::RSA_PKCS1_SHA1 + | Self::RSA_PKCS1_SHA256 + | Self::RSA_PKCS1_SHA384 + | Self::RSA_PKCS1_SHA512 + | Self::RSA_PSS_SHA256 + | Self::RSA_PSS_SHA384 + | Self::RSA_PSS_SHA512 => SignatureAlgorithm::RSA, + Self::ECDSA_SHA1_Legacy + | Self::ECDSA_NISTP256_SHA256 + | Self::ECDSA_NISTP384_SHA384 + | Self::ECDSA_NISTP521_SHA512 => SignatureAlgorithm::ECDSA, + Self::ED25519 => SignatureAlgorithm::ED25519, + Self::ED448 => SignatureAlgorithm::ED448, + _ => SignatureAlgorithm::Unknown(0), + } + } + + /// Whether a particular `SignatureScheme` is allowed for TLS protocol signatures + /// in TLS1.3. + /// + /// This prevents (eg) RSA_PKCS1_SHA256 being offered or accepted, even if our + /// verifier supports it for other protocol versions. + /// + /// See RFC8446 s4.2.3: <https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.3> + /// + /// This is a denylist so that newly-allocated `SignatureScheme`s values are + /// allowed in TLS1.3 by default. + pub(crate) fn supported_in_tls13(&self) -> bool { + let [hash, sign] = self.to_array(); + + // This covers both disallowing SHA1 items in `SignatureScheme`, and + // old hash functions. See the section beginning "Legacy algorithms:" + // and item starting "In TLS 1.2, the extension contained hash/signature + // pairs" in RFC8446 section 4.2.3. + match HashAlgorithm::from(hash) { + HashAlgorithm::NONE + | HashAlgorithm::MD5 + | HashAlgorithm::SHA1 + | HashAlgorithm::SHA224 => return false, + _ => (), + }; + + // RSA-PKCS1 is also disallowed for TLS1.3, see the section beginning + // "RSASSA-PKCS1-v1_5 algorithms:" in RFC8446 section 4.2.3. + // + // (nb. SignatureAlgorithm::RSA is RSA-PKCS1, and does not cover RSA-PSS + // or RSAE-PSS.) + // + // This also covers the outlawing of DSA mentioned elsewhere in 4.2.3. + !matches!( + SignatureAlgorithm::from(sign), + SignatureAlgorithm::Anonymous | SignatureAlgorithm::RSA | SignatureAlgorithm::DSA + ) + } +} + +enum_builder! { + /// The `SignatureAlgorithm` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum SignatureAlgorithm { + Anonymous => 0x00, + RSA => 0x01, + DSA => 0x02, + ECDSA => 0x03, + ED25519 => 0x07, + ED448 => 0x08, + } +} + +enum_builder! { + /// The "TLS Certificate Compression Algorithm IDs" TLS protocol enum. + /// Values in this enum are taken from [RFC8879]. + /// + /// [RFC8879]: https://www.rfc-editor.org/rfc/rfc8879.html#section-7.3 + #[repr(u16)] + pub enum CertificateCompressionAlgorithm { + Zlib => 1, + Brotli => 2, + Zstd => 3, + } +} + +enum_builder! { + /// The `CertificateType` enum sent in the cert_type extensions. + /// Values in this enum are taken from the various RFCs covering TLS, and are listed by IANA. + /// + /// [RFC 6091 Section 5]: <https://datatracker.ietf.org/doc/html/rfc6091#section-5> + /// [RFC 7250 Section 7]: <https://datatracker.ietf.org/doc/html/rfc7250#section-7> + #[repr(u8)] + pub enum CertificateType { + X509 => 0x00, + RawPublicKey => 0x02, + } +} + +enum_builder! { + /// The type of Encrypted Client Hello (`EchClientHelloType`). + /// + /// Specified in [draft-ietf-tls-esni Section 5]. + /// + /// [draft-ietf-tls-esni Section 5]: <https://www.ietf.org/archive/id/draft-ietf-tls-esni-18.html#section-5> + #[repr(u8)] + pub enum EchClientHelloType { + ClientHelloOuter => 0, + ClientHelloInner => 1 + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::msgs::enums::tests::{test_enum8, test_enum16}; + + #[test] + fn test_enums() { + test_enum8::<SignatureAlgorithm>(SignatureAlgorithm::Anonymous, SignatureAlgorithm::ECDSA); + test_enum8::<ContentType>(ContentType::ChangeCipherSpec, ContentType::Heartbeat); + test_enum8::<HandshakeType>(HandshakeType::HelloRequest, HandshakeType::MessageHash); + test_enum8::<AlertDescription>( + AlertDescription::CloseNotify, + AlertDescription::NoApplicationProtocol, + ); + test_enum16::<CertificateCompressionAlgorithm>( + CertificateCompressionAlgorithm::Zlib, + CertificateCompressionAlgorithm::Zstd, + ); + test_enum8::<CertificateType>(CertificateType::X509, CertificateType::RawPublicKey); + } + + #[test] + fn tls13_signature_restrictions() { + // rsa-pkcs1 denied + assert!(!SignatureScheme::RSA_PKCS1_SHA1.supported_in_tls13()); + assert!(!SignatureScheme::RSA_PKCS1_SHA256.supported_in_tls13()); + assert!(!SignatureScheme::RSA_PKCS1_SHA384.supported_in_tls13()); + assert!(!SignatureScheme::RSA_PKCS1_SHA512.supported_in_tls13()); + + // dsa denied + assert!(!SignatureScheme::from(0x0201).supported_in_tls13()); + assert!(!SignatureScheme::from(0x0202).supported_in_tls13()); + assert!(!SignatureScheme::from(0x0203).supported_in_tls13()); + assert!(!SignatureScheme::from(0x0204).supported_in_tls13()); + assert!(!SignatureScheme::from(0x0205).supported_in_tls13()); + assert!(!SignatureScheme::from(0x0206).supported_in_tls13()); + + // common + assert!(SignatureScheme::ED25519.supported_in_tls13()); + assert!(SignatureScheme::ED448.supported_in_tls13()); + assert!(SignatureScheme::RSA_PSS_SHA256.supported_in_tls13()); + assert!(SignatureScheme::RSA_PSS_SHA384.supported_in_tls13()); + assert!(SignatureScheme::RSA_PSS_SHA512.supported_in_tls13()); + + // rsa_pss_rsae_* + assert!(SignatureScheme::from(0x0804).supported_in_tls13()); + assert!(SignatureScheme::from(0x0805).supported_in_tls13()); + assert!(SignatureScheme::from(0x0806).supported_in_tls13()); + + // ecdsa_brainpool* + assert!(SignatureScheme::from(0x081a).supported_in_tls13()); + assert!(SignatureScheme::from(0x081b).supported_in_tls13()); + assert!(SignatureScheme::from(0x081c).supported_in_tls13()); + } +} diff --git a/vendor/rustls/src/error.rs b/vendor/rustls/src/error.rs new file mode 100644 index 00000000..3aaf3bb3 --- /dev/null +++ b/vendor/rustls/src/error.rs @@ -0,0 +1,1387 @@ +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; +#[cfg(feature = "std")] +use std::time::SystemTimeError; + +use pki_types::{AlgorithmIdentifier, ServerName, UnixTime}; +use webpki::KeyUsage; + +use crate::enums::{AlertDescription, ContentType, HandshakeType}; +use crate::msgs::handshake::{EchConfigPayload, KeyExchangeAlgorithm}; +use crate::rand; + +/// rustls reports protocol errors using this type. +#[non_exhaustive] +#[derive(Debug, PartialEq, Clone)] +pub enum Error { + /// We received a TLS message that isn't valid right now. + /// `expect_types` lists the message types we can expect right now. + /// `got_type` is the type we found. This error is typically + /// caused by a buggy TLS stack (the peer or this one), a broken + /// network, or an attack. + InappropriateMessage { + /// Which types we expected + expect_types: Vec<ContentType>, + /// What type we received + got_type: ContentType, + }, + + /// We received a TLS handshake message that isn't valid right now. + /// `expect_types` lists the handshake message types we can expect + /// right now. `got_type` is the type we found. + InappropriateHandshakeMessage { + /// Which handshake type we expected + expect_types: Vec<HandshakeType>, + /// What handshake type we received + got_type: HandshakeType, + }, + + /// An error occurred while handling Encrypted Client Hello (ECH). + InvalidEncryptedClientHello(EncryptedClientHelloError), + + /// The peer sent us a TLS message with invalid contents. + InvalidMessage(InvalidMessage), + + /// The peer didn't give us any certificates. + NoCertificatesPresented, + + /// The certificate verifier doesn't support the given type of name. + UnsupportedNameType, + + /// We couldn't decrypt a message. This is invariably fatal. + DecryptError, + + /// We couldn't encrypt a message because it was larger than the allowed message size. + /// This should never happen if the application is using valid record sizes. + EncryptError, + + /// The peer doesn't support a protocol version/feature we require. + /// The parameter gives a hint as to what version/feature it is. + PeerIncompatible(PeerIncompatible), + + /// The peer deviated from the standard TLS protocol. + /// The parameter gives a hint where. + PeerMisbehaved(PeerMisbehaved), + + /// We received a fatal alert. This means the peer is unhappy. + AlertReceived(AlertDescription), + + /// We saw an invalid certificate. + /// + /// The contained error is from the certificate validation trait + /// implementation. + InvalidCertificate(CertificateError), + + /// A provided certificate revocation list (CRL) was invalid. + InvalidCertRevocationList(CertRevocationListError), + + /// A catch-all error for unlikely errors. + General(String), + + /// We failed to figure out what time it currently is. + FailedToGetCurrentTime, + + /// We failed to acquire random bytes from the system. + FailedToGetRandomBytes, + + /// This function doesn't work until the TLS handshake + /// is complete. + HandshakeNotComplete, + + /// The peer sent an oversized record/fragment. + PeerSentOversizedRecord, + + /// An incoming connection did not support any known application protocol. + NoApplicationProtocol, + + /// The `max_fragment_size` value supplied in configuration was too small, + /// or too large. + BadMaxFragmentSize, + + /// Specific failure cases from [`keys_match`] or a [`crate::crypto::signer::SigningKey`] that cannot produce a corresponding public key. + /// + /// [`keys_match`]: crate::crypto::signer::CertifiedKey::keys_match + InconsistentKeys(InconsistentKeys), + + /// Any other error. + /// + /// This variant should only be used when the error is not better described by a more + /// specific variant. For example, if a custom crypto provider returns a + /// provider specific error. + /// + /// Enums holding this variant will never compare equal to each other. + Other(OtherError), +} + +/// Specific failure cases from [`keys_match`] or a [`crate::crypto::signer::SigningKey`] that cannot produce a corresponding public key. +/// +/// [`keys_match`]: crate::crypto::signer::CertifiedKey::keys_match +#[non_exhaustive] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum InconsistentKeys { + /// The public key returned by the [`SigningKey`] does not match the public key information in the certificate. + /// + /// [`SigningKey`]: crate::crypto::signer::SigningKey + KeyMismatch, + + /// The [`SigningKey`] cannot produce its corresponding public key. + /// + /// [`SigningKey`]: crate::crypto::signer::SigningKey + Unknown, +} + +impl From<InconsistentKeys> for Error { + #[inline] + fn from(e: InconsistentKeys) -> Self { + Self::InconsistentKeys(e) + } +} + +/// A corrupt TLS message payload that resulted in an error. +#[non_exhaustive] +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum InvalidMessage { + /// A certificate payload exceeded rustls's 64KB limit + CertificatePayloadTooLarge, + /// An advertised message was larger then expected. + HandshakePayloadTooLarge, + /// The peer sent us a syntactically incorrect ChangeCipherSpec payload. + InvalidCcs, + /// An unknown content type was encountered during message decoding. + InvalidContentType, + /// A peer sent an invalid certificate status type + InvalidCertificateStatusType, + /// Context was incorrectly attached to a certificate request during a handshake. + InvalidCertRequest, + /// A peer's DH params could not be decoded + InvalidDhParams, + /// A message was zero-length when its record kind forbids it. + InvalidEmptyPayload, + /// A peer sent an unexpected key update request. + InvalidKeyUpdate, + /// A peer's server name could not be decoded + InvalidServerName, + /// A TLS message payload was larger then allowed by the specification. + MessageTooLarge, + /// Message is shorter than the expected length + MessageTooShort, + /// Missing data for the named handshake payload value + MissingData(&'static str), + /// A peer did not advertise its supported key exchange groups. + MissingKeyExchange, + /// A peer sent an empty list of signature schemes + NoSignatureSchemes, + /// Trailing data found for the named handshake payload value + TrailingData(&'static str), + /// A peer sent an unexpected message type. + UnexpectedMessage(&'static str), + /// An unknown TLS protocol was encountered during message decoding. + UnknownProtocolVersion, + /// A peer sent a non-null compression method. + UnsupportedCompression, + /// A peer sent an unknown elliptic curve type. + UnsupportedCurveType, + /// A peer sent an unsupported key exchange algorithm. + UnsupportedKeyExchangeAlgorithm(KeyExchangeAlgorithm), + /// A server sent an empty ticket + EmptyTicketValue, + /// A peer sent an empty list of items, but a non-empty list is required. + /// + /// The argument names the context. + IllegalEmptyList(&'static str), + /// A peer sent an empty value, but a non-empty value is required. + IllegalEmptyValue, + /// A peer sent a message where a given extension type was repeated + DuplicateExtension(u16), + /// A peer sent a message with a PSK offer extension in wrong position + PreSharedKeyIsNotFinalExtension, + /// A server sent a HelloRetryRequest with an unknown extension + UnknownHelloRetryRequestExtension, + /// The peer sent a TLS1.3 Certificate with an unknown extension + UnknownCertificateExtension, +} + +impl From<InvalidMessage> for Error { + #[inline] + fn from(e: InvalidMessage) -> Self { + Self::InvalidMessage(e) + } +} + +impl From<InvalidMessage> for AlertDescription { + fn from(e: InvalidMessage) -> Self { + match e { + InvalidMessage::PreSharedKeyIsNotFinalExtension => Self::IllegalParameter, + InvalidMessage::DuplicateExtension(_) => Self::IllegalParameter, + InvalidMessage::UnknownHelloRetryRequestExtension => Self::UnsupportedExtension, + _ => Self::DecodeError, + } + } +} + +#[non_exhaustive] +#[allow(missing_docs)] +#[derive(Debug, PartialEq, Clone)] +/// The set of cases where we failed to make a connection because we thought +/// the peer was misbehaving. +/// +/// This is `non_exhaustive`: we might add or stop using items here in minor +/// versions. We also don't document what they mean. Generally a user of +/// rustls shouldn't vary its behaviour on these error codes, and there is +/// nothing it can do to improve matters. +/// +/// Please file a bug against rustls if you see `Error::PeerMisbehaved` in +/// the wild. +pub enum PeerMisbehaved { + AttemptedDowngradeToTls12WhenTls13IsSupported, + BadCertChainExtensions, + DisallowedEncryptedExtension, + DuplicateClientHelloExtensions, + DuplicateEncryptedExtensions, + DuplicateHelloRetryRequestExtensions, + DuplicateNewSessionTicketExtensions, + DuplicateServerHelloExtensions, + DuplicateServerNameTypes, + EarlyDataAttemptedInSecondClientHello, + EarlyDataExtensionWithoutResumption, + EarlyDataOfferedWithVariedCipherSuite, + HandshakeHashVariedAfterRetry, + IllegalHelloRetryRequestWithEmptyCookie, + IllegalHelloRetryRequestWithNoChanges, + IllegalHelloRetryRequestWithOfferedGroup, + IllegalHelloRetryRequestWithUnofferedCipherSuite, + IllegalHelloRetryRequestWithUnofferedNamedGroup, + IllegalHelloRetryRequestWithUnsupportedVersion, + IllegalHelloRetryRequestWithWrongSessionId, + IllegalHelloRetryRequestWithInvalidEch, + IllegalMiddleboxChangeCipherSpec, + IllegalTlsInnerPlaintext, + IncorrectBinder, + InvalidCertCompression, + InvalidMaxEarlyDataSize, + InvalidKeyShare, + KeyEpochWithPendingFragment, + KeyUpdateReceivedInQuicConnection, + MessageInterleavedWithHandshakeMessage, + MissingBinderInPskExtension, + MissingKeyShare, + MissingPskModesExtension, + MissingQuicTransportParameters, + OfferedDuplicateCertificateCompressions, + OfferedDuplicateKeyShares, + OfferedEarlyDataWithOldProtocolVersion, + OfferedEmptyApplicationProtocol, + OfferedIncorrectCompressions, + PskExtensionMustBeLast, + PskExtensionWithMismatchedIdsAndBinders, + RefusedToFollowHelloRetryRequest, + RejectedEarlyDataInterleavedWithHandshakeMessage, + ResumptionAttemptedWithVariedEms, + ResumptionOfferedWithVariedCipherSuite, + ResumptionOfferedWithVariedEms, + ResumptionOfferedWithIncompatibleCipherSuite, + SelectedDifferentCipherSuiteAfterRetry, + SelectedInvalidPsk, + SelectedTls12UsingTls13VersionExtension, + SelectedUnofferedApplicationProtocol, + SelectedUnofferedCertCompression, + SelectedUnofferedCipherSuite, + SelectedUnofferedCompression, + SelectedUnofferedKxGroup, + SelectedUnofferedPsk, + SelectedUnusableCipherSuiteForVersion, + ServerEchoedCompatibilitySessionId, + ServerHelloMustOfferUncompressedEcPoints, + ServerNameDifferedOnRetry, + ServerNameMustContainOneHostName, + SignedKxWithWrongAlgorithm, + SignedHandshakeWithUnadvertisedSigScheme, + TooManyEmptyFragments, + TooManyKeyUpdateRequests, + TooManyRenegotiationRequests, + TooManyWarningAlertsReceived, + TooMuchEarlyDataReceived, + UnexpectedCleartextExtension, + UnsolicitedCertExtension, + UnsolicitedEncryptedExtension, + UnsolicitedSctList, + UnsolicitedServerHelloExtension, + WrongGroupForKeyShare, + UnsolicitedEchExtension, +} + +impl From<PeerMisbehaved> for Error { + #[inline] + fn from(e: PeerMisbehaved) -> Self { + Self::PeerMisbehaved(e) + } +} + +#[non_exhaustive] +#[allow(missing_docs)] +#[derive(Debug, PartialEq, Clone)] +/// The set of cases where we failed to make a connection because a peer +/// doesn't support a TLS version/feature we require. +/// +/// This is `non_exhaustive`: we might add or stop using items here in minor +/// versions. +pub enum PeerIncompatible { + EcPointsExtensionRequired, + ExtendedMasterSecretExtensionRequired, + IncorrectCertificateTypeExtension, + KeyShareExtensionRequired, + NamedGroupsExtensionRequired, + NoCertificateRequestSignatureSchemesInCommon, + NoCipherSuitesInCommon, + NoEcPointFormatsInCommon, + NoKxGroupsInCommon, + NoSignatureSchemesInCommon, + NullCompressionRequired, + ServerDoesNotSupportTls12Or13, + ServerSentHelloRetryRequestWithUnknownExtension, + ServerTlsVersionIsDisabledByOurConfig, + SignatureAlgorithmsExtensionRequired, + SupportedVersionsExtensionRequired, + Tls12NotOffered, + Tls12NotOfferedOrEnabled, + Tls13RequiredForQuic, + UncompressedEcPointsRequired, + UnsolicitedCertificateTypeExtension, + ServerRejectedEncryptedClientHello(Option<Vec<EchConfigPayload>>), +} + +impl From<PeerIncompatible> for Error { + #[inline] + fn from(e: PeerIncompatible) -> Self { + Self::PeerIncompatible(e) + } +} + +#[non_exhaustive] +#[derive(Debug, Clone)] +/// The ways in which certificate validators can express errors. +/// +/// Note that the rustls TLS protocol code interprets specifically these +/// error codes to send specific TLS alerts. Therefore, if a +/// custom certificate validator uses incorrect errors the library as +/// a whole will send alerts that do not match the standard (this is usually +/// a minor issue, but could be misleading). +pub enum CertificateError { + /// The certificate is not correctly encoded. + BadEncoding, + + /// The current time is after the `notAfter` time in the certificate. + Expired, + + /// The current time is after the `notAfter` time in the certificate. + /// + /// This variant is semantically the same as `Expired`, but includes + /// extra data to improve error reports. + ExpiredContext { + /// The validation time. + time: UnixTime, + /// The `notAfter` time of the certificate. + not_after: UnixTime, + }, + + /// The current time is before the `notBefore` time in the certificate. + NotValidYet, + + /// The current time is before the `notBefore` time in the certificate. + /// + /// This variant is semantically the same as `NotValidYet`, but includes + /// extra data to improve error reports. + NotValidYetContext { + /// The validation time. + time: UnixTime, + /// The `notBefore` time of the certificate. + not_before: UnixTime, + }, + + /// The certificate has been revoked. + Revoked, + + /// The certificate contains an extension marked critical, but it was + /// not processed by the certificate validator. + UnhandledCriticalExtension, + + /// The certificate chain is not issued by a known root certificate. + UnknownIssuer, + + /// The certificate's revocation status could not be determined. + UnknownRevocationStatus, + + /// The certificate's revocation status could not be determined, because the CRL is expired. + ExpiredRevocationList, + + /// The certificate's revocation status could not be determined, because the CRL is expired. + /// + /// This variant is semantically the same as `ExpiredRevocationList`, but includes + /// extra data to improve error reports. + ExpiredRevocationListContext { + /// The validation time. + time: UnixTime, + /// The nextUpdate time of the CRL. + next_update: UnixTime, + }, + + /// A certificate is not correctly signed by the key of its alleged + /// issuer. + BadSignature, + + /// A signature inside a certificate or on a handshake was made with an unsupported algorithm. + #[deprecated( + since = "0.23.29", + note = "use `UnsupportedSignatureAlgorithmContext` instead" + )] + UnsupportedSignatureAlgorithm, + + /// A signature inside a certificate or on a handshake was made with an unsupported algorithm. + UnsupportedSignatureAlgorithmContext { + /// The signature algorithm OID that was unsupported. + signature_algorithm_id: Vec<u8>, + /// Supported algorithms that were available for signature verification. + supported_algorithms: Vec<AlgorithmIdentifier>, + }, + + /// A signature was made with an algorithm that doesn't match the relevant public key. + UnsupportedSignatureAlgorithmForPublicKeyContext { + /// The signature algorithm OID. + signature_algorithm_id: Vec<u8>, + /// The public key algorithm OID. + public_key_algorithm_id: Vec<u8>, + }, + + /// The subject names in an end-entity certificate do not include + /// the expected name. + NotValidForName, + + /// The subject names in an end-entity certificate do not include + /// the expected name. + /// + /// This variant is semantically the same as `NotValidForName`, but includes + /// extra data to improve error reports. + NotValidForNameContext { + /// Expected server name. + expected: ServerName<'static>, + + /// The names presented in the end entity certificate. + /// + /// These are the subject names as present in the leaf certificate and may contain DNS names + /// with or without a wildcard label as well as IP address names. + presented: Vec<String>, + }, + + /// The certificate is being used for a different purpose than allowed. + InvalidPurpose, + + /// The certificate is being used for a different purpose than allowed. + /// + /// This variant is semantically the same as `InvalidPurpose`, but includes + /// extra data to improve error reports. + InvalidPurposeContext { + /// Extended key purpose that was required by the application. + required: ExtendedKeyPurpose, + /// Extended key purposes that were presented in the peer's certificate. + presented: Vec<ExtendedKeyPurpose>, + }, + + /// The OCSP response provided to the verifier was invalid. + /// + /// This should be returned from [`ServerCertVerifier::verify_server_cert()`] + /// when a verifier checks its `ocsp_response` parameter and finds it invalid. + /// + /// This maps to [`AlertDescription::BadCertificateStatusResponse`]. + /// + /// [`ServerCertVerifier::verify_server_cert()`]: crate::client::danger::ServerCertVerifier::verify_server_cert + InvalidOcspResponse, + + /// The certificate is valid, but the handshake is rejected for other + /// reasons. + ApplicationVerificationFailure, + + /// Any other error. + /// + /// This can be used by custom verifiers to expose the underlying error + /// (where they are not better described by the more specific errors + /// above). + /// + /// It is also used by the default verifier in case its error is + /// not covered by the above common cases. + /// + /// Enums holding this variant will never compare equal to each other. + Other(OtherError), +} + +impl PartialEq<Self> for CertificateError { + fn eq(&self, other: &Self) -> bool { + use CertificateError::*; + #[allow(clippy::match_like_matches_macro)] + match (self, other) { + (BadEncoding, BadEncoding) => true, + (Expired, Expired) => true, + ( + ExpiredContext { + time: left_time, + not_after: left_not_after, + }, + ExpiredContext { + time: right_time, + not_after: right_not_after, + }, + ) => (left_time, left_not_after) == (right_time, right_not_after), + (NotValidYet, NotValidYet) => true, + ( + NotValidYetContext { + time: left_time, + not_before: left_not_before, + }, + NotValidYetContext { + time: right_time, + not_before: right_not_before, + }, + ) => (left_time, left_not_before) == (right_time, right_not_before), + (Revoked, Revoked) => true, + (UnhandledCriticalExtension, UnhandledCriticalExtension) => true, + (UnknownIssuer, UnknownIssuer) => true, + (BadSignature, BadSignature) => true, + #[allow(deprecated)] + (UnsupportedSignatureAlgorithm, UnsupportedSignatureAlgorithm) => true, + ( + UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: left_signature_algorithm_id, + supported_algorithms: left_supported_algorithms, + }, + UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: right_signature_algorithm_id, + supported_algorithms: right_supported_algorithms, + }, + ) => { + (left_signature_algorithm_id, left_supported_algorithms) + == (right_signature_algorithm_id, right_supported_algorithms) + } + ( + UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: left_signature_algorithm_id, + public_key_algorithm_id: left_public_key_algorithm_id, + }, + UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: right_signature_algorithm_id, + public_key_algorithm_id: right_public_key_algorithm_id, + }, + ) => { + (left_signature_algorithm_id, left_public_key_algorithm_id) + == (right_signature_algorithm_id, right_public_key_algorithm_id) + } + (NotValidForName, NotValidForName) => true, + ( + NotValidForNameContext { + expected: left_expected, + presented: left_presented, + }, + NotValidForNameContext { + expected: right_expected, + presented: right_presented, + }, + ) => (left_expected, left_presented) == (right_expected, right_presented), + (InvalidPurpose, InvalidPurpose) => true, + ( + InvalidPurposeContext { + required: left_required, + presented: left_presented, + }, + InvalidPurposeContext { + required: right_required, + presented: right_presented, + }, + ) => (left_required, left_presented) == (right_required, right_presented), + (InvalidOcspResponse, InvalidOcspResponse) => true, + (ApplicationVerificationFailure, ApplicationVerificationFailure) => true, + (UnknownRevocationStatus, UnknownRevocationStatus) => true, + (ExpiredRevocationList, ExpiredRevocationList) => true, + ( + ExpiredRevocationListContext { + time: left_time, + next_update: left_next_update, + }, + ExpiredRevocationListContext { + time: right_time, + next_update: right_next_update, + }, + ) => (left_time, left_next_update) == (right_time, right_next_update), + _ => false, + } + } +} + +// The following mapping are heavily referenced in: +// * [OpenSSL Implementation](https://github.com/openssl/openssl/blob/45bb98bfa223efd3258f445ad443f878011450f0/ssl/statem/statem_lib.c#L1434) +// * [BoringSSL Implementation](https://github.com/google/boringssl/blob/583c60bd4bf76d61b2634a58bcda99a92de106cb/ssl/ssl_x509.cc#L1323) +impl From<CertificateError> for AlertDescription { + fn from(e: CertificateError) -> Self { + use CertificateError::*; + match e { + BadEncoding + | UnhandledCriticalExtension + | NotValidForName + | NotValidForNameContext { .. } => Self::BadCertificate, + // RFC 5246/RFC 8446 + // certificate_expired + // A certificate has expired or **is not currently valid**. + Expired | ExpiredContext { .. } | NotValidYet | NotValidYetContext { .. } => { + Self::CertificateExpired + } + Revoked => Self::CertificateRevoked, + // OpenSSL, BoringSSL and AWS-LC all generate an Unknown CA alert for + // the case where revocation status can not be determined, so we do the same here. + UnknownIssuer + | UnknownRevocationStatus + | ExpiredRevocationList + | ExpiredRevocationListContext { .. } => Self::UnknownCA, + InvalidOcspResponse => Self::BadCertificateStatusResponse, + #[allow(deprecated)] + BadSignature + | UnsupportedSignatureAlgorithm + | UnsupportedSignatureAlgorithmContext { .. } + | UnsupportedSignatureAlgorithmForPublicKeyContext { .. } => Self::DecryptError, + InvalidPurpose | InvalidPurposeContext { .. } => Self::UnsupportedCertificate, + ApplicationVerificationFailure => Self::AccessDenied, + // RFC 5246/RFC 8446 + // certificate_unknown + // Some other (unspecified) issue arose in processing the + // certificate, rendering it unacceptable. + Other(..) => Self::CertificateUnknown, + } + } +} + +impl fmt::Display for CertificateError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + #[cfg(feature = "std")] + Self::NotValidForNameContext { + expected, + presented, + } => { + write!( + f, + "certificate not valid for name {:?}; certificate ", + expected.to_str() + )?; + + match presented.as_slice() { + &[] => write!( + f, + "is not valid for any names (according to its subjectAltName extension)" + ), + [one] => write!(f, "is only valid for {one}"), + many => { + write!(f, "is only valid for ")?; + + let n = many.len(); + let all_but_last = &many[..n - 1]; + let last = &many[n - 1]; + + for (i, name) in all_but_last.iter().enumerate() { + write!(f, "{name}")?; + if i < n - 2 { + write!(f, ", ")?; + } + } + write!(f, " or {last}") + } + } + } + + Self::ExpiredContext { time, not_after } => write!( + f, + "certificate expired: verification time {} (UNIX), \ + but certificate is not valid after {} \ + ({} seconds ago)", + time.as_secs(), + not_after.as_secs(), + time.as_secs() + .saturating_sub(not_after.as_secs()) + ), + + Self::NotValidYetContext { time, not_before } => write!( + f, + "certificate not valid yet: verification time {} (UNIX), \ + but certificate is not valid before {} \ + ({} seconds in future)", + time.as_secs(), + not_before.as_secs(), + not_before + .as_secs() + .saturating_sub(time.as_secs()) + ), + + Self::ExpiredRevocationListContext { time, next_update } => write!( + f, + "certificate revocation list expired: \ + verification time {} (UNIX), \ + but CRL is not valid after {} \ + ({} seconds ago)", + time.as_secs(), + next_update.as_secs(), + time.as_secs() + .saturating_sub(next_update.as_secs()) + ), + + Self::InvalidPurposeContext { + required, + presented, + } => { + write!( + f, + "certificate does not allow extended key usage for {required}, allows " + )?; + for (i, eku) in presented.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{eku}")?; + } + Ok(()) + } + + other => write!(f, "{other:?}"), + } + } +} + +impl From<CertificateError> for Error { + #[inline] + fn from(e: CertificateError) -> Self { + Self::InvalidCertificate(e) + } +} + +/// Extended Key Usage (EKU) purpose values. +/// +/// These are usually represented as OID values in the certificate's extension (if present), but +/// we represent the values that are most relevant to rustls as named enum variants. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum ExtendedKeyPurpose { + /// Client authentication + ClientAuth, + /// Server authentication + ServerAuth, + /// Other EKU values + /// + /// Represented here as a `Vec<usize>` for human readability. + Other(Vec<usize>), +} + +impl ExtendedKeyPurpose { + pub(crate) fn for_values(values: impl Iterator<Item = usize>) -> Self { + let values = values.collect::<Vec<_>>(); + match &*values { + KeyUsage::CLIENT_AUTH_REPR => Self::ClientAuth, + KeyUsage::SERVER_AUTH_REPR => Self::ServerAuth, + _ => Self::Other(values), + } + } +} + +impl fmt::Display for ExtendedKeyPurpose { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ClientAuth => write!(f, "client authentication"), + Self::ServerAuth => write!(f, "server authentication"), + Self::Other(values) => { + for (i, value) in values.iter().enumerate() { + if i > 0 { + write!(f, ", ")?; + } + write!(f, "{value}")?; + } + Ok(()) + } + } + } +} + +#[non_exhaustive] +#[derive(Debug, Clone)] +/// The ways in which a certificate revocation list (CRL) can be invalid. +pub enum CertRevocationListError { + /// The CRL had a bad signature from its issuer. + BadSignature, + + /// The CRL had an unsupported signature from its issuer. + #[deprecated( + since = "0.23.29", + note = "use `UnsupportedSignatureAlgorithmContext` instead" + )] + UnsupportedSignatureAlgorithm, + + /// A signature inside a certificate or on a handshake was made with an unsupported algorithm. + UnsupportedSignatureAlgorithmContext { + /// The signature algorithm OID that was unsupported. + signature_algorithm_id: Vec<u8>, + /// Supported algorithms that were available for signature verification. + supported_algorithms: Vec<AlgorithmIdentifier>, + }, + + /// A signature was made with an algorithm that doesn't match the relevant public key. + UnsupportedSignatureAlgorithmForPublicKeyContext { + /// The signature algorithm OID. + signature_algorithm_id: Vec<u8>, + /// The public key algorithm OID. + public_key_algorithm_id: Vec<u8>, + }, + + /// The CRL contained an invalid CRL number. + InvalidCrlNumber, + + /// The CRL contained a revoked certificate with an invalid serial number. + InvalidRevokedCertSerialNumber, + + /// The CRL issuer does not specify the cRLSign key usage. + IssuerInvalidForCrl, + + /// The CRL is invalid for some other reason. + /// + /// Enums holding this variant will never compare equal to each other. + Other(OtherError), + + /// The CRL is not correctly encoded. + ParseError, + + /// The CRL is not a v2 X.509 CRL. + UnsupportedCrlVersion, + + /// The CRL, or a revoked certificate in the CRL, contained an unsupported critical extension. + UnsupportedCriticalExtension, + + /// The CRL is an unsupported delta CRL, containing only changes relative to another CRL. + UnsupportedDeltaCrl, + + /// The CRL is an unsupported indirect CRL, containing revoked certificates issued by a CA + /// other than the issuer of the CRL. + UnsupportedIndirectCrl, + + /// The CRL contained a revoked certificate with an unsupported revocation reason. + /// See RFC 5280 Section 5.3.1[^1] for a list of supported revocation reasons. + /// + /// [^1]: <https://www.rfc-editor.org/rfc/rfc5280#section-5.3.1> + UnsupportedRevocationReason, +} + +impl PartialEq<Self> for CertRevocationListError { + fn eq(&self, other: &Self) -> bool { + use CertRevocationListError::*; + #[allow(clippy::match_like_matches_macro)] + match (self, other) { + (BadSignature, BadSignature) => true, + #[allow(deprecated)] + (UnsupportedSignatureAlgorithm, UnsupportedSignatureAlgorithm) => true, + ( + UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: left_signature_algorithm_id, + supported_algorithms: left_supported_algorithms, + }, + UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: right_signature_algorithm_id, + supported_algorithms: right_supported_algorithms, + }, + ) => { + (left_signature_algorithm_id, left_supported_algorithms) + == (right_signature_algorithm_id, right_supported_algorithms) + } + ( + UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: left_signature_algorithm_id, + public_key_algorithm_id: left_public_key_algorithm_id, + }, + UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: right_signature_algorithm_id, + public_key_algorithm_id: right_public_key_algorithm_id, + }, + ) => { + (left_signature_algorithm_id, left_public_key_algorithm_id) + == (right_signature_algorithm_id, right_public_key_algorithm_id) + } + (InvalidCrlNumber, InvalidCrlNumber) => true, + (InvalidRevokedCertSerialNumber, InvalidRevokedCertSerialNumber) => true, + (IssuerInvalidForCrl, IssuerInvalidForCrl) => true, + (ParseError, ParseError) => true, + (UnsupportedCrlVersion, UnsupportedCrlVersion) => true, + (UnsupportedCriticalExtension, UnsupportedCriticalExtension) => true, + (UnsupportedDeltaCrl, UnsupportedDeltaCrl) => true, + (UnsupportedIndirectCrl, UnsupportedIndirectCrl) => true, + (UnsupportedRevocationReason, UnsupportedRevocationReason) => true, + _ => false, + } + } +} + +impl From<CertRevocationListError> for Error { + #[inline] + fn from(e: CertRevocationListError) -> Self { + Self::InvalidCertRevocationList(e) + } +} + +#[non_exhaustive] +#[derive(Debug, Clone, Eq, PartialEq)] +/// An error that occurred while handling Encrypted Client Hello (ECH). +pub enum EncryptedClientHelloError { + /// The provided ECH configuration list was invalid. + InvalidConfigList, + /// No compatible ECH configuration. + NoCompatibleConfig, + /// The client configuration has server name indication (SNI) disabled. + SniRequired, +} + +impl From<EncryptedClientHelloError> for Error { + #[inline] + fn from(e: EncryptedClientHelloError) -> Self { + Self::InvalidEncryptedClientHello(e) + } +} + +fn join<T: fmt::Debug>(items: &[T]) -> String { + items + .iter() + .map(|x| format!("{x:?}")) + .collect::<Vec<String>>() + .join(" or ") +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::InappropriateMessage { + expect_types, + got_type, + } => write!( + f, + "received unexpected message: got {:?} when expecting {}", + got_type, + join::<ContentType>(expect_types) + ), + Self::InappropriateHandshakeMessage { + expect_types, + got_type, + } => write!( + f, + "received unexpected handshake message: got {:?} when expecting {}", + got_type, + join::<HandshakeType>(expect_types) + ), + Self::InvalidMessage(typ) => { + write!(f, "received corrupt message of type {typ:?}") + } + Self::PeerIncompatible(why) => write!(f, "peer is incompatible: {why:?}"), + Self::PeerMisbehaved(why) => write!(f, "peer misbehaved: {why:?}"), + Self::AlertReceived(alert) => write!(f, "received fatal alert: {alert:?}"), + Self::InvalidCertificate(err) => { + write!(f, "invalid peer certificate: {err}") + } + Self::InvalidCertRevocationList(err) => { + write!(f, "invalid certificate revocation list: {err:?}") + } + Self::NoCertificatesPresented => write!(f, "peer sent no certificates"), + Self::UnsupportedNameType => write!(f, "presented server name type wasn't supported"), + Self::DecryptError => write!(f, "cannot decrypt peer's message"), + Self::InvalidEncryptedClientHello(err) => { + write!(f, "encrypted client hello failure: {err:?}") + } + Self::EncryptError => write!(f, "cannot encrypt message"), + Self::PeerSentOversizedRecord => write!(f, "peer sent excess record size"), + Self::HandshakeNotComplete => write!(f, "handshake not complete"), + Self::NoApplicationProtocol => write!(f, "peer doesn't support any known protocol"), + Self::FailedToGetCurrentTime => write!(f, "failed to get current time"), + Self::FailedToGetRandomBytes => write!(f, "failed to get random bytes"), + Self::BadMaxFragmentSize => { + write!(f, "the supplied max_fragment_size was too small or large") + } + Self::InconsistentKeys(why) => { + write!(f, "keys may not be consistent: {why:?}") + } + Self::General(err) => write!(f, "unexpected error: {err}"), + Self::Other(err) => write!(f, "other error: {err}"), + } + } +} + +#[cfg(feature = "std")] +impl From<SystemTimeError> for Error { + #[inline] + fn from(_: SystemTimeError) -> Self { + Self::FailedToGetCurrentTime + } +} + +#[cfg(feature = "std")] +impl std::error::Error for Error {} + +impl From<rand::GetRandomFailed> for Error { + fn from(_: rand::GetRandomFailed) -> Self { + Self::FailedToGetRandomBytes + } +} + +mod other_error { + use core::fmt; + #[cfg(feature = "std")] + use std::error::Error as StdError; + + use super::Error; + #[cfg(feature = "std")] + use crate::sync::Arc; + + /// Any other error that cannot be expressed by a more specific [`Error`] variant. + /// + /// For example, an `OtherError` could be produced by a custom crypto provider + /// exposing a provider specific error. + /// + /// Enums holding this type will never compare equal to each other. + #[derive(Debug, Clone)] + pub struct OtherError(#[cfg(feature = "std")] pub Arc<dyn StdError + Send + Sync>); + + impl PartialEq<Self> for OtherError { + fn eq(&self, _other: &Self) -> bool { + false + } + } + + impl From<OtherError> for Error { + fn from(value: OtherError) -> Self { + Self::Other(value) + } + } + + impl fmt::Display for OtherError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[cfg(feature = "std")] + { + write!(f, "{}", self.0) + } + #[cfg(not(feature = "std"))] + { + f.write_str("no further information available") + } + } + } + + #[cfg(feature = "std")] + impl StdError for OtherError { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + Some(self.0.as_ref()) + } + } +} + +pub use other_error::OtherError; + +#[cfg(test)] +mod tests { + use core::time::Duration; + use std::prelude::v1::*; + use std::{println, vec}; + + use pki_types::ServerName; + + use super::{ + CertRevocationListError, Error, InconsistentKeys, InvalidMessage, OtherError, UnixTime, + }; + #[cfg(feature = "std")] + use crate::sync::Arc; + + #[test] + fn certificate_error_equality() { + use super::CertificateError::*; + assert_eq!(BadEncoding, BadEncoding); + assert_eq!(Expired, Expired); + let context = ExpiredContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(1234)), + not_after: UnixTime::since_unix_epoch(Duration::from_secs(123)), + }; + assert_eq!(context, context); + assert_ne!( + context, + ExpiredContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(12345)), + not_after: UnixTime::since_unix_epoch(Duration::from_secs(123)), + } + ); + assert_ne!( + context, + ExpiredContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(1234)), + not_after: UnixTime::since_unix_epoch(Duration::from_secs(1234)), + } + ); + assert_eq!(NotValidYet, NotValidYet); + let context = NotValidYetContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(123)), + not_before: UnixTime::since_unix_epoch(Duration::from_secs(1234)), + }; + assert_eq!(context, context); + assert_ne!( + context, + NotValidYetContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(1234)), + not_before: UnixTime::since_unix_epoch(Duration::from_secs(1234)), + } + ); + assert_ne!( + context, + NotValidYetContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(123)), + not_before: UnixTime::since_unix_epoch(Duration::from_secs(12345)), + } + ); + assert_eq!(Revoked, Revoked); + assert_eq!(UnhandledCriticalExtension, UnhandledCriticalExtension); + assert_eq!(UnknownIssuer, UnknownIssuer); + assert_eq!(ExpiredRevocationList, ExpiredRevocationList); + assert_eq!(UnknownRevocationStatus, UnknownRevocationStatus); + let context = ExpiredRevocationListContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(1234)), + next_update: UnixTime::since_unix_epoch(Duration::from_secs(123)), + }; + assert_eq!(context, context); + assert_ne!( + context, + ExpiredRevocationListContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(12345)), + next_update: UnixTime::since_unix_epoch(Duration::from_secs(123)), + } + ); + assert_ne!( + context, + ExpiredRevocationListContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(1234)), + next_update: UnixTime::since_unix_epoch(Duration::from_secs(1234)), + } + ); + assert_eq!(BadSignature, BadSignature); + #[allow(deprecated)] + { + assert_eq!(UnsupportedSignatureAlgorithm, UnsupportedSignatureAlgorithm); + } + assert_eq!( + UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: vec![1, 2, 3], + supported_algorithms: vec![] + }, + UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: vec![1, 2, 3], + supported_algorithms: vec![] + } + ); + assert_eq!( + UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: vec![1, 2, 3], + public_key_algorithm_id: vec![4, 5, 6] + }, + UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: vec![1, 2, 3], + public_key_algorithm_id: vec![4, 5, 6] + } + ); + assert_eq!(NotValidForName, NotValidForName); + let context = NotValidForNameContext { + expected: ServerName::try_from("example.com") + .unwrap() + .to_owned(), + presented: vec!["other.com".into()], + }; + assert_eq!(context, context); + assert_ne!( + context, + NotValidForNameContext { + expected: ServerName::try_from("example.com") + .unwrap() + .to_owned(), + presented: vec![] + } + ); + assert_ne!( + context, + NotValidForNameContext { + expected: ServerName::try_from("huh.com") + .unwrap() + .to_owned(), + presented: vec!["other.com".into()], + } + ); + assert_eq!(InvalidPurpose, InvalidPurpose); + assert_eq!( + ApplicationVerificationFailure, + ApplicationVerificationFailure + ); + assert_eq!(InvalidOcspResponse, InvalidOcspResponse); + let other = Other(OtherError( + #[cfg(feature = "std")] + Arc::from(Box::from("")), + )); + assert_ne!(other, other); + assert_ne!(BadEncoding, Expired); + } + + #[test] + fn crl_error_equality() { + use super::CertRevocationListError::*; + assert_eq!(BadSignature, BadSignature); + #[allow(deprecated)] + { + assert_eq!(UnsupportedSignatureAlgorithm, UnsupportedSignatureAlgorithm); + } + assert_eq!( + UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: vec![1, 2, 3], + supported_algorithms: vec![] + }, + UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: vec![1, 2, 3], + supported_algorithms: vec![] + } + ); + assert_eq!( + UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: vec![1, 2, 3], + public_key_algorithm_id: vec![4, 5, 6] + }, + UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: vec![1, 2, 3], + public_key_algorithm_id: vec![4, 5, 6] + } + ); + assert_eq!(InvalidCrlNumber, InvalidCrlNumber); + assert_eq!( + InvalidRevokedCertSerialNumber, + InvalidRevokedCertSerialNumber + ); + assert_eq!(IssuerInvalidForCrl, IssuerInvalidForCrl); + assert_eq!(ParseError, ParseError); + assert_eq!(UnsupportedCriticalExtension, UnsupportedCriticalExtension); + assert_eq!(UnsupportedCrlVersion, UnsupportedCrlVersion); + assert_eq!(UnsupportedDeltaCrl, UnsupportedDeltaCrl); + assert_eq!(UnsupportedIndirectCrl, UnsupportedIndirectCrl); + assert_eq!(UnsupportedRevocationReason, UnsupportedRevocationReason); + let other = Other(OtherError( + #[cfg(feature = "std")] + Arc::from(Box::from("")), + )); + assert_ne!(other, other); + assert_ne!(BadSignature, InvalidCrlNumber); + } + + #[test] + #[cfg(feature = "std")] + fn other_error_equality() { + let other_error = OtherError(Arc::from(Box::from(""))); + assert_ne!(other_error, other_error); + let other: Error = other_error.into(); + assert_ne!(other, other); + } + + #[test] + fn smoke() { + use crate::enums::{AlertDescription, ContentType, HandshakeType}; + + let all = vec![ + Error::InappropriateMessage { + expect_types: vec![ContentType::Alert], + got_type: ContentType::Handshake, + }, + Error::InappropriateHandshakeMessage { + expect_types: vec![HandshakeType::ClientHello, HandshakeType::Finished], + got_type: HandshakeType::ServerHello, + }, + Error::InvalidMessage(InvalidMessage::InvalidCcs), + Error::NoCertificatesPresented, + Error::DecryptError, + super::PeerIncompatible::Tls12NotOffered.into(), + super::PeerMisbehaved::UnsolicitedCertExtension.into(), + Error::AlertReceived(AlertDescription::ExportRestriction), + super::CertificateError::Expired.into(), + super::CertificateError::NotValidForNameContext { + expected: ServerName::try_from("example.com") + .unwrap() + .to_owned(), + presented: vec![], + } + .into(), + super::CertificateError::NotValidForNameContext { + expected: ServerName::try_from("example.com") + .unwrap() + .to_owned(), + presented: vec!["DnsName(\"hello.com\")".into()], + } + .into(), + super::CertificateError::NotValidForNameContext { + expected: ServerName::try_from("example.com") + .unwrap() + .to_owned(), + presented: vec![ + "DnsName(\"hello.com\")".into(), + "DnsName(\"goodbye.com\")".into(), + ], + } + .into(), + super::CertificateError::NotValidYetContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(300)), + not_before: UnixTime::since_unix_epoch(Duration::from_secs(320)), + } + .into(), + super::CertificateError::ExpiredContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(320)), + not_after: UnixTime::since_unix_epoch(Duration::from_secs(300)), + } + .into(), + super::CertificateError::ExpiredRevocationListContext { + time: UnixTime::since_unix_epoch(Duration::from_secs(320)), + next_update: UnixTime::since_unix_epoch(Duration::from_secs(300)), + } + .into(), + super::CertificateError::InvalidOcspResponse.into(), + Error::General("undocumented error".to_string()), + Error::FailedToGetCurrentTime, + Error::FailedToGetRandomBytes, + Error::HandshakeNotComplete, + Error::PeerSentOversizedRecord, + Error::NoApplicationProtocol, + Error::BadMaxFragmentSize, + Error::InconsistentKeys(InconsistentKeys::KeyMismatch), + Error::InconsistentKeys(InconsistentKeys::Unknown), + Error::InvalidCertRevocationList(CertRevocationListError::BadSignature), + Error::Other(OtherError( + #[cfg(feature = "std")] + Arc::from(Box::from("")), + )), + ]; + + for err in all { + println!("{err:?}:"); + println!(" fmt '{err}'"); + } + } + + #[test] + fn rand_error_mapping() { + use super::rand; + let err: Error = rand::GetRandomFailed.into(); + assert_eq!(err, Error::FailedToGetRandomBytes); + } + + #[cfg(feature = "std")] + #[test] + fn time_error_mapping() { + use std::time::SystemTime; + + let time_error = SystemTime::UNIX_EPOCH + .duration_since(SystemTime::now()) + .unwrap_err(); + let err: Error = time_error.into(); + assert_eq!(err, Error::FailedToGetCurrentTime); + } +} diff --git a/vendor/rustls/src/hash_hs.rs b/vendor/rustls/src/hash_hs.rs new file mode 100644 index 00000000..8b4edc68 --- /dev/null +++ b/vendor/rustls/src/hash_hs.rs @@ -0,0 +1,347 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::mem; + +use crate::crypto::hash; +use crate::msgs::codec::Codec; +use crate::msgs::enums::HashAlgorithm; +use crate::msgs::handshake::HandshakeMessagePayload; +use crate::msgs::message::{Message, MessagePayload}; + +/// Early stage buffering of handshake payloads. +/// +/// Before we know the hash algorithm to use to verify the handshake, we just buffer the messages. +/// During the handshake, we may restart the transcript due to a HelloRetryRequest, reverting +/// from the `HandshakeHash` to a `HandshakeHashBuffer` again. +#[derive(Clone)] +pub(crate) struct HandshakeHashBuffer { + buffer: Vec<u8>, + client_auth_enabled: bool, +} + +impl HandshakeHashBuffer { + pub(crate) fn new() -> Self { + Self { + buffer: Vec::new(), + client_auth_enabled: false, + } + } + + /// We might be doing client auth, so need to keep a full + /// log of the handshake. + pub(crate) fn set_client_auth_enabled(&mut self) { + self.client_auth_enabled = true; + } + + /// Hash/buffer a handshake message. + pub(crate) fn add_message(&mut self, m: &Message<'_>) { + match &m.payload { + MessagePayload::Handshake { encoded, .. } => self.add_raw(encoded.bytes()), + MessagePayload::HandshakeFlight(payload) => self.add_raw(payload.bytes()), + _ => {} + }; + } + + /// Hash or buffer a byte slice. + fn add_raw(&mut self, buf: &[u8]) { + self.buffer.extend_from_slice(buf); + } + + /// Get the hash value if we were to hash `extra` too. + pub(crate) fn hash_given( + &self, + provider: &'static dyn hash::Hash, + extra: &[u8], + ) -> hash::Output { + let mut ctx = provider.start(); + ctx.update(&self.buffer); + ctx.update(extra); + ctx.finish() + } + + /// We now know what hash function the verify_data will use. + pub(crate) fn start_hash(self, provider: &'static dyn hash::Hash) -> HandshakeHash { + let mut ctx = provider.start(); + ctx.update(&self.buffer); + HandshakeHash { + provider, + ctx, + client_auth: match self.client_auth_enabled { + true => Some(self.buffer), + false => None, + }, + } + } +} + +/// This deals with keeping a running hash of the handshake +/// payloads. This is computed by buffering initially. Once +/// we know what hash function we need to use we switch to +/// incremental hashing. +/// +/// For client auth, we also need to buffer all the messages. +/// This is disabled in cases where client auth is not possible. +pub(crate) struct HandshakeHash { + provider: &'static dyn hash::Hash, + ctx: Box<dyn hash::Context>, + + /// buffer for client-auth. + client_auth: Option<Vec<u8>>, +} + +impl HandshakeHash { + /// We decided not to do client auth after all, so discard + /// the transcript. + pub(crate) fn abandon_client_auth(&mut self) { + self.client_auth = None; + } + + /// Hash/buffer a handshake message. + pub(crate) fn add_message(&mut self, m: &Message<'_>) -> &mut Self { + match &m.payload { + MessagePayload::Handshake { encoded, .. } => self.add_raw(encoded.bytes()), + MessagePayload::HandshakeFlight(payload) => self.add_raw(payload.bytes()), + _ => self, + } + } + + /// Hash/buffer an encoded handshake message. + pub(crate) fn add(&mut self, bytes: &[u8]) { + self.add_raw(bytes); + } + + /// Hash or buffer a byte slice. + fn add_raw(&mut self, buf: &[u8]) -> &mut Self { + self.ctx.update(buf); + + if let Some(buffer) = &mut self.client_auth { + buffer.extend_from_slice(buf); + } + + self + } + + /// Get the hash value if we were to hash `extra` too, + /// using hash function `hash`. + pub(crate) fn hash_given(&self, extra: &[u8]) -> hash::Output { + let mut ctx = self.ctx.fork(); + ctx.update(extra); + ctx.finish() + } + + pub(crate) fn into_hrr_buffer(self) -> HandshakeHashBuffer { + let old_hash = self.ctx.finish(); + let old_handshake_hash_msg = + HandshakeMessagePayload::build_handshake_hash(old_hash.as_ref()); + + HandshakeHashBuffer { + client_auth_enabled: self.client_auth.is_some(), + buffer: old_handshake_hash_msg.get_encoding(), + } + } + + /// Take the current hash value, and encapsulate it in a + /// 'handshake_hash' handshake message. Start this hash + /// again, with that message at the front. + pub(crate) fn rollup_for_hrr(&mut self) { + let ctx = &mut self.ctx; + + let old_ctx = mem::replace(ctx, self.provider.start()); + let old_hash = old_ctx.finish(); + let old_handshake_hash_msg = + HandshakeMessagePayload::build_handshake_hash(old_hash.as_ref()); + + self.add_raw(&old_handshake_hash_msg.get_encoding()); + } + + /// Get the current hash value. + pub(crate) fn current_hash(&self) -> hash::Output { + self.ctx.fork_finish() + } + + /// Takes this object's buffer containing all handshake messages + /// so far. This method only works once; it resets the buffer + /// to empty. + #[cfg(feature = "tls12")] + pub(crate) fn take_handshake_buf(&mut self) -> Option<Vec<u8>> { + self.client_auth.take() + } + + /// The hashing algorithm + pub(crate) fn algorithm(&self) -> HashAlgorithm { + self.provider.algorithm() + } +} + +impl Clone for HandshakeHash { + fn clone(&self) -> Self { + Self { + provider: self.provider, + ctx: self.ctx.fork(), + client_auth: self.client_auth.clone(), + } + } +} + +#[cfg(test)] +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use super::provider::hash::SHA256; + use super::*; + use crate::crypto::hash::Hash; + use crate::enums::ProtocolVersion; + use crate::msgs::base::Payload; + use crate::msgs::handshake::{HandshakeMessagePayload, HandshakePayload}; + + #[test] + fn hashes_correctly() { + let mut hhb = HandshakeHashBuffer::new(); + hhb.add_raw(b"hello"); + assert_eq!(hhb.buffer.len(), 5); + let mut hh = hhb.start_hash(&SHA256); + assert!(hh.client_auth.is_none()); + hh.add_raw(b"world"); + let h = hh.current_hash(); + let h = h.as_ref(); + assert_eq!(h[0], 0x93); + assert_eq!(h[1], 0x6a); + assert_eq!(h[2], 0x18); + assert_eq!(h[3], 0x5c); + } + + #[test] + fn hashes_message_types() { + // handshake protocol encoding of 0x0e 00 00 00 + let server_hello_done_message = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ServerHelloDone, + )), + }; + + let app_data_ignored = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::ApplicationData(Payload::Borrowed(b"hello")), + }; + + let end_of_early_data_flight = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::HandshakeFlight(Payload::Borrowed(b"\x05\x00\x00\x00")), + }; + + // buffered mode + let mut hhb = HandshakeHashBuffer::new(); + hhb.add_message(&server_hello_done_message); + hhb.add_message(&app_data_ignored); + hhb.add_message(&end_of_early_data_flight); + assert_eq!( + hhb.start_hash(&SHA256) + .current_hash() + .as_ref(), + SHA256 + .hash(b"\x0e\x00\x00\x00\x05\x00\x00\x00") + .as_ref() + ); + + // non-buffered mode + let mut hh = HandshakeHashBuffer::new().start_hash(&SHA256); + hh.add_message(&server_hello_done_message); + hh.add_message(&app_data_ignored); + hh.add_message(&end_of_early_data_flight); + assert_eq!( + hh.current_hash().as_ref(), + SHA256 + .hash(b"\x0e\x00\x00\x00\x05\x00\x00\x00") + .as_ref() + ); + } + + #[cfg(feature = "tls12")] + #[test] + fn buffers_correctly() { + let mut hhb = HandshakeHashBuffer::new(); + hhb.set_client_auth_enabled(); + hhb.add_raw(b"hello"); + assert_eq!(hhb.buffer.len(), 5); + let mut hh = hhb.start_hash(&SHA256); + assert_eq!( + hh.client_auth + .as_ref() + .map(|buf| buf.len()), + Some(5) + ); + hh.add_raw(b"world"); + assert_eq!( + hh.client_auth + .as_ref() + .map(|buf| buf.len()), + Some(10) + ); + let h = hh.current_hash(); + let h = h.as_ref(); + assert_eq!(h[0], 0x93); + assert_eq!(h[1], 0x6a); + assert_eq!(h[2], 0x18); + assert_eq!(h[3], 0x5c); + let buf = hh.take_handshake_buf(); + assert_eq!(Some(b"helloworld".to_vec()), buf); + } + + #[test] + fn abandon() { + let mut hhb = HandshakeHashBuffer::new(); + hhb.set_client_auth_enabled(); + hhb.add_raw(b"hello"); + assert_eq!(hhb.buffer.len(), 5); + let mut hh = hhb.start_hash(&SHA256); + assert_eq!( + hh.client_auth + .as_ref() + .map(|buf| buf.len()), + Some(5) + ); + hh.abandon_client_auth(); + assert_eq!(hh.client_auth, None); + hh.add_raw(b"world"); + assert_eq!(hh.client_auth, None); + let h = hh.current_hash(); + let h = h.as_ref(); + assert_eq!(h[0], 0x93); + assert_eq!(h[1], 0x6a); + assert_eq!(h[2], 0x18); + assert_eq!(h[3], 0x5c); + } + + #[test] + fn clones_correctly() { + let mut hhb = HandshakeHashBuffer::new(); + hhb.set_client_auth_enabled(); + hhb.add_raw(b"hello"); + assert_eq!(hhb.buffer.len(), 5); + + // Cloning the HHB should result in the same buffer and client auth state. + let mut hhb_prime = hhb.clone(); + assert_eq!(hhb_prime.buffer, hhb.buffer); + assert!(hhb_prime.client_auth_enabled); + + // Updating the HHB clone shouldn't affect the original. + hhb_prime.add_raw(b"world"); + assert_eq!(hhb_prime.buffer.len(), 10); + assert_ne!(hhb.buffer, hhb_prime.buffer); + + let hh = hhb.start_hash(&SHA256); + let hh_hash = hh.current_hash(); + let hh_hash = hh_hash.as_ref(); + + // Cloning the HH should result in the same current hash. + let mut hh_prime = hh.clone(); + let hh_prime_hash = hh_prime.current_hash(); + let hh_prime_hash = hh_prime_hash.as_ref(); + assert_eq!(hh_hash, hh_prime_hash); + + // Updating the HH clone shouldn't affect the original. + hh_prime.add_raw(b"goodbye"); + assert_eq!(hh.current_hash().as_ref(), hh_hash); + assert_ne!(hh_prime.current_hash().as_ref(), hh_hash); + } +} diff --git a/vendor/rustls/src/key_log.rs b/vendor/rustls/src/key_log.rs new file mode 100644 index 00000000..0ffcccfe --- /dev/null +++ b/vendor/rustls/src/key_log.rs @@ -0,0 +1,61 @@ +use core::fmt::Debug; + +#[cfg(all(doc, feature = "std"))] +use crate::KeyLogFile; + +/// This trait represents the ability to do something useful +/// with key material, such as logging it to a file for debugging. +/// +/// Naturally, secrets passed over the interface are *extremely* +/// sensitive and can break the security of past, present and +/// future sessions. +/// +/// You'll likely want some interior mutability in your +/// implementation to make this useful. +/// +/// See [`KeyLogFile`] that implements the standard +/// `SSLKEYLOGFILE` environment variable behaviour. +pub trait KeyLog: Debug + Send + Sync { + /// Log the given `secret`. `client_random` is provided for + /// session identification. `label` describes precisely what + /// `secret` means: + /// + /// - `CLIENT_RANDOM`: `secret` is the master secret for a TLSv1.2 session. + /// - `CLIENT_EARLY_TRAFFIC_SECRET`: `secret` encrypts early data + /// transmitted by a client + /// - `SERVER_HANDSHAKE_TRAFFIC_SECRET`: `secret` encrypts + /// handshake messages from the server during a TLSv1.3 handshake. + /// - `CLIENT_HANDSHAKE_TRAFFIC_SECRET`: `secret` encrypts + /// handshake messages from the client during a TLSv1.3 handshake. + /// - `SERVER_TRAFFIC_SECRET_0`: `secret` encrypts post-handshake data + /// from the server in a TLSv1.3 session. + /// - `CLIENT_TRAFFIC_SECRET_0`: `secret` encrypts post-handshake data + /// from the client in a TLSv1.3 session. + /// - `EXPORTER_SECRET`: `secret` is the post-handshake exporter secret + /// in a TLSv1.3 session. + /// + /// These strings are selected to match the NSS key log format: + /// <https://nss-crypto.org/reference/security/nss/legacy/key_log_format/index.html> + fn log(&self, label: &str, client_random: &[u8], secret: &[u8]); + + /// Indicates whether the secret with label `label` will be logged. + /// + /// If `will_log` returns true then `log` will be called with the secret. + /// Otherwise, `log` will not be called for the secret. This is a + /// performance optimization. + fn will_log(&self, _label: &str) -> bool { + true + } +} + +/// KeyLog that does exactly nothing. +#[derive(Debug)] +pub struct NoKeyLog; + +impl KeyLog for NoKeyLog { + fn log(&self, _: &str, _: &[u8], _: &[u8]) {} + #[inline] + fn will_log(&self, _label: &str) -> bool { + false + } +} diff --git a/vendor/rustls/src/key_log_file.rs b/vendor/rustls/src/key_log_file.rs new file mode 100644 index 00000000..45f29da5 --- /dev/null +++ b/vendor/rustls/src/key_log_file.rs @@ -0,0 +1,162 @@ +use alloc::vec::Vec; +use core::fmt::{Debug, Formatter}; +use std::env::var_os; +use std::ffi::OsString; +use std::fs::{File, OpenOptions}; +use std::io; +use std::io::Write; +use std::sync::Mutex; + +use crate::KeyLog; +use crate::log::warn; + +// Internal mutable state for KeyLogFile +struct KeyLogFileInner { + file: Option<File>, + buf: Vec<u8>, +} + +impl KeyLogFileInner { + fn new(var: Option<OsString>) -> Self { + let Some(path) = &var else { + return Self { + file: None, + buf: Vec::new(), + }; + }; + + #[cfg_attr(not(feature = "logging"), allow(unused_variables))] + let file = match OpenOptions::new() + .append(true) + .create(true) + .open(path) + { + Ok(f) => Some(f), + Err(e) => { + warn!("unable to create key log file {path:?}: {e}"); + None + } + }; + + Self { + file, + buf: Vec::new(), + } + } + + fn try_write(&mut self, label: &str, client_random: &[u8], secret: &[u8]) -> io::Result<()> { + let Some(file) = &mut self.file else { + return Ok(()); + }; + + self.buf.truncate(0); + write!(self.buf, "{label} ")?; + for b in client_random.iter() { + write!(self.buf, "{b:02x}")?; + } + write!(self.buf, " ")?; + for b in secret.iter() { + write!(self.buf, "{b:02x}")?; + } + writeln!(self.buf)?; + file.write_all(&self.buf) + } +} + +impl Debug for KeyLogFileInner { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("KeyLogFileInner") + // Note: we omit self.buf deliberately as it may contain key data. + .field("file", &self.file) + .finish() + } +} + +/// [`KeyLog`] implementation that opens a file whose name is +/// given by the `SSLKEYLOGFILE` environment variable, and writes +/// keys into it. +/// +/// If `SSLKEYLOGFILE` is not set, this does nothing. +/// +/// If such a file cannot be opened, or cannot be written then +/// this does nothing but logs errors at warning-level. +pub struct KeyLogFile(Mutex<KeyLogFileInner>); + +impl KeyLogFile { + /// Makes a new `KeyLogFile`. The environment variable is + /// inspected and the named file is opened during this call. + pub fn new() -> Self { + let var = var_os("SSLKEYLOGFILE"); + Self(Mutex::new(KeyLogFileInner::new(var))) + } +} + +impl KeyLog for KeyLogFile { + fn log(&self, label: &str, client_random: &[u8], secret: &[u8]) { + #[cfg_attr(not(feature = "logging"), allow(unused_variables))] + match self + .0 + .lock() + .unwrap() + .try_write(label, client_random, secret) + { + Ok(()) => {} + Err(e) => { + warn!("error writing to key log file: {e}"); + } + } + } +} + +impl Debug for KeyLogFile { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self.0.try_lock() { + Ok(key_log_file) => write!(f, "{key_log_file:?}"), + Err(_) => write!(f, "KeyLogFile {{ <locked> }}"), + } + } +} + +#[cfg(all(test, target_os = "linux"))] +mod tests { + use super::*; + + fn init() { + let _ = env_logger::builder() + .is_test(true) + .try_init(); + } + + #[test] + fn test_env_var_is_not_set() { + init(); + let mut inner = KeyLogFileInner::new(None); + assert!( + inner + .try_write("label", b"random", b"secret") + .is_ok() + ); + } + + #[test] + fn test_env_var_cannot_be_opened() { + init(); + let mut inner = KeyLogFileInner::new(Some("/dev/does-not-exist".into())); + assert!( + inner + .try_write("label", b"random", b"secret") + .is_ok() + ); + } + + #[test] + fn test_env_var_cannot_be_written() { + init(); + let mut inner = KeyLogFileInner::new(Some("/dev/full".into())); + assert!( + inner + .try_write("label", b"random", b"secret") + .is_err() + ); + } +} diff --git a/vendor/rustls/src/lib.rs b/vendor/rustls/src/lib.rs new file mode 100644 index 00000000..845f4e94 --- /dev/null +++ b/vendor/rustls/src/lib.rs @@ -0,0 +1,715 @@ +//! # Rustls - a modern TLS library +//! +//! Rustls is a TLS library that aims to provide a good level of cryptographic security, +//! requires no configuration to achieve that security, and provides no unsafe features or +//! obsolete cryptography by default. +//! +//! Rustls implements TLS1.2 and TLS1.3 for both clients and servers. See [the full +//! list of protocol features](manual::_04_features). +//! +//! ### Platform support +//! +//! While Rustls itself is platform independent, by default it uses [`aws-lc-rs`] for implementing +//! the cryptography in TLS. See [the aws-lc-rs FAQ][aws-lc-rs-platforms-faq] for more details of the +//! platform/architecture support constraints in aws-lc-rs. +//! +//! [`ring`] is also available via the `ring` crate feature: see +//! [the supported `ring` target platforms][ring-target-platforms]. +//! +//! By providing a custom instance of the [`crypto::CryptoProvider`] struct, you +//! can replace all cryptography dependencies of rustls. This is a route to being portable +//! to a wider set of architectures and environments, or compliance requirements. See the +//! [`crypto::CryptoProvider`] documentation for more details. +//! +//! Specifying `default-features = false` when depending on rustls will remove the implicit +//! dependency on aws-lc-rs. +//! +//! Rustls requires Rust 1.71 or later. It has an optional dependency on zlib-rs which requires 1.75 or later. +//! +//! [ring-target-platforms]: https://github.com/briansmith/ring/blob/2e8363b433fa3b3962c877d9ed2e9145612f3160/include/ring-core/target.h#L18-L64 +//! [`crypto::CryptoProvider`]: crate::crypto::CryptoProvider +//! [`ring`]: https://crates.io/crates/ring +//! [aws-lc-rs-platforms-faq]: https://aws.github.io/aws-lc-rs/faq.html#can-i-run-aws-lc-rs-on-x-platform-or-architecture +//! [`aws-lc-rs`]: https://crates.io/crates/aws-lc-rs +//! +//! ### Cryptography providers +//! +//! Since Rustls 0.22 it has been possible to choose the provider of the cryptographic primitives +//! that Rustls uses. This may be appealing if you have specific platform, compliance or feature +//! requirements that aren't met by the default provider, [`aws-lc-rs`]. +//! +//! Users that wish to customize the provider in use can do so when constructing `ClientConfig` +//! and `ServerConfig` instances using the `with_crypto_provider` method on the respective config +//! builder types. See the [`crypto::CryptoProvider`] documentation for more details. +//! +//! #### Built-in providers +//! +//! Rustls ships with two built-in providers controlled by associated crate features: +//! +//! * [`aws-lc-rs`] - enabled by default, available with the `aws_lc_rs` crate feature enabled. +//! * [`ring`] - available with the `ring` crate feature enabled. +//! +//! See the documentation for [`crypto::CryptoProvider`] for details on how providers are +//! selected. +//! +//! #### Third-party providers +//! +//! The community has also started developing third-party providers for Rustls: +//! +//! * [`boring-rustls-provider`] - a work-in-progress provider that uses [`boringssl`] for +//! cryptography. +//! * [`rustls-graviola`] - a provider that uses [`graviola`] for cryptography. +//! * [`rustls-mbedtls-provider`] - a provider that uses [`mbedtls`] for cryptography. +//! * [`rustls-openssl`] - a provider that uses [OpenSSL] for cryptography. +//! * [`rustls-rustcrypto`] - an experimental provider that uses the crypto primitives +//! from [`RustCrypto`] for cryptography. +//! * [`rustls-symcrypt`] - a provider that uses Microsoft's [SymCrypt] library. +//! * [`rustls-wolfcrypt-provider`] - a work-in-progress provider that uses [`wolfCrypt`] for cryptography. +//! +//! [`rustls-graviola`]: https://crates.io/crates/rustls-graviola +//! [`graviola`]: https://github.com/ctz/graviola +//! [`rustls-mbedtls-provider`]: https://github.com/fortanix/rustls-mbedtls-provider +//! [`mbedtls`]: https://github.com/Mbed-TLS/mbedtls +//! [`rustls-openssl`]: https://github.com/tofay/rustls-openssl +//! [OpenSSL]: https://openssl-library.org/ +//! [`rustls-symcrypt`]: https://github.com/microsoft/rustls-symcrypt +//! [SymCrypt]: https://github.com/microsoft/SymCrypt +//! [`boring-rustls-provider`]: https://github.com/janrueth/boring-rustls-provider +//! [`boringssl`]: https://github.com/google/boringssl +//! [`rustls-rustcrypto`]: https://github.com/RustCrypto/rustls-rustcrypto +//! [`RustCrypto`]: https://github.com/RustCrypto +//! [`rustls-wolfcrypt-provider`]: https://github.com/wolfSSL/rustls-wolfcrypt-provider +//! [`wolfCrypt`]: https://www.wolfssl.com/products/wolfcrypt +//! +//! #### Custom provider +//! +//! We also provide a simple example of writing your own provider in the [custom provider example]. +//! This example implements a minimal provider using parts of the [`RustCrypto`] ecosystem. +//! +//! See the [Making a custom CryptoProvider] section of the documentation for more information +//! on this topic. +//! +//! [custom provider example]: https://github.com/rustls/rustls/tree/main/provider-example/ +//! [`RustCrypto`]: https://github.com/RustCrypto +//! [Making a custom CryptoProvider]: https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html#making-a-custom-cryptoprovider +//! +//! ## Design overview +//! +//! Rustls is a low-level library. If your goal is to make HTTPS connections you may prefer +//! to use a library built on top of Rustls like [hyper] or [ureq]. +//! +//! [hyper]: https://crates.io/crates/hyper +//! [ureq]: https://crates.io/crates/ureq +//! +//! ### Rustls does not take care of network IO +//! It doesn't make or accept TCP connections, or do DNS, or read or write files. +//! +//! Our [examples] directory contains demos that show how to handle I/O using the +//! [`stream::Stream`] helper, as well as more complex asynchronous I/O using [`mio`]. +//! If you're already using Tokio for an async runtime you may prefer to use [`tokio-rustls`] instead +//! of interacting with rustls directly. +//! +//! [examples]: https://github.com/rustls/rustls/tree/main/examples +//! [`tokio-rustls`]: https://github.com/rustls/tokio-rustls +//! +//! ### Rustls provides encrypted pipes +//! These are the [`ServerConnection`] and [`ClientConnection`] types. You supply raw TLS traffic +//! on the left (via the [`read_tls()`] and [`write_tls()`] methods) and then read/write the +//! plaintext on the right: +//! +//! [`read_tls()`]: Connection::read_tls +//! [`write_tls()`]: Connection::read_tls +//! +//! ```text +//! TLS Plaintext +//! === ========= +//! read_tls() +-----------------------+ reader() as io::Read +//! | | +//! +---------> ClientConnection +---------> +//! | or | +//! <---------+ ServerConnection <---------+ +//! | | +//! write_tls() +-----------------------+ writer() as io::Write +//! ``` +//! +//! ### Rustls takes care of server certificate verification +//! You do not need to provide anything other than a set of root certificates to trust. +//! Certificate verification cannot be turned off or disabled in the main API. +//! +//! ## Getting started +//! This is the minimum you need to do to make a TLS client connection. +//! +//! First we load some root certificates. These are used to authenticate the server. +//! The simplest way is to depend on the [`webpki_roots`] crate which contains +//! the Mozilla set of root certificates. +//! +//! ```rust,no_run +//! # #[cfg(feature = "aws-lc-rs")] { +//! let root_store = rustls::RootCertStore::from_iter( +//! webpki_roots::TLS_SERVER_ROOTS +//! .iter() +//! .cloned(), +//! ); +//! # } +//! ``` +//! +//! [`webpki_roots`]: https://crates.io/crates/webpki-roots +//! +//! Next, we make a `ClientConfig`. You're likely to make one of these per process, +//! and use it for all connections made by that process. +//! +//! ```rust,no_run +//! # #[cfg(feature = "aws_lc_rs")] { +//! # let root_store: rustls::RootCertStore = panic!(); +//! let config = rustls::ClientConfig::builder() +//! .with_root_certificates(root_store) +//! .with_no_client_auth(); +//! # } +//! ``` +//! +//! Now we can make a connection. You need to provide the server's hostname so we +//! know what to expect to find in the server's certificate. +//! +//! ```rust +//! # #[cfg(feature = "aws_lc_rs")] { +//! # use rustls; +//! # use webpki; +//! # use std::sync::Arc; +//! # rustls::crypto::aws_lc_rs::default_provider().install_default(); +//! # let root_store = rustls::RootCertStore::from_iter( +//! # webpki_roots::TLS_SERVER_ROOTS +//! # .iter() +//! # .cloned(), +//! # ); +//! # let config = rustls::ClientConfig::builder() +//! # .with_root_certificates(root_store) +//! # .with_no_client_auth(); +//! let rc_config = Arc::new(config); +//! let example_com = "example.com".try_into().unwrap(); +//! let mut client = rustls::ClientConnection::new(rc_config, example_com); +//! # } +//! ``` +//! +//! Now you should do appropriate IO for the `client` object. If `client.wants_read()` yields +//! true, you should call `client.read_tls()` when the underlying connection has data. +//! Likewise, if `client.wants_write()` yields true, you should call `client.write_tls()` +//! when the underlying connection is able to send data. You should continue doing this +//! as long as the connection is valid. +//! +//! The return types of `read_tls()` and `write_tls()` only tell you if the IO worked. No +//! parsing or processing of the TLS messages is done. After each `read_tls()` you should +//! therefore call `client.process_new_packets()` which parses and processes the messages. +//! Any error returned from `process_new_packets` is fatal to the connection, and will tell you +//! why. For example, if the server's certificate is expired `process_new_packets` will +//! return `Err(InvalidCertificate(Expired))`. From this point on, +//! `process_new_packets` will not do any new work and will return that error continually. +//! +//! You can extract newly received data by calling `client.reader()` (which implements the +//! `io::Read` trait). You can send data to the peer by calling `client.writer()` (which +//! implements `io::Write` trait). Note that `client.writer().write()` buffers data you +//! send if the TLS connection is not yet established: this is useful for writing (say) a +//! HTTP request, but this is buffered so avoid large amounts of data. +//! +//! The following code uses a fictional socket IO API for illustration, and does not handle +//! errors. +//! +//! ```rust,no_run +//! # #[cfg(feature = "aws_lc_rs")] { +//! # let mut client = rustls::ClientConnection::new(panic!(), panic!()).unwrap(); +//! # struct Socket { } +//! # impl Socket { +//! # fn ready_for_write(&self) -> bool { false } +//! # fn ready_for_read(&self) -> bool { false } +//! # fn wait_for_something_to_happen(&self) { } +//! # } +//! # +//! # use std::io::{Read, Write, Result}; +//! # impl Read for Socket { +//! # fn read(&mut self, buf: &mut [u8]) -> Result<usize> { panic!() } +//! # } +//! # impl Write for Socket { +//! # fn write(&mut self, buf: &[u8]) -> Result<usize> { panic!() } +//! # fn flush(&mut self) -> Result<()> { panic!() } +//! # } +//! # +//! # fn connect(_address: &str, _port: u16) -> Socket { +//! # panic!(); +//! # } +//! use std::io; +//! use rustls::Connection; +//! +//! client.writer().write(b"GET / HTTP/1.0\r\n\r\n").unwrap(); +//! let mut socket = connect("example.com", 443); +//! loop { +//! if client.wants_read() && socket.ready_for_read() { +//! client.read_tls(&mut socket).unwrap(); +//! client.process_new_packets().unwrap(); +//! +//! let mut plaintext = Vec::new(); +//! client.reader().read_to_end(&mut plaintext).unwrap(); +//! io::stdout().write(&plaintext).unwrap(); +//! } +//! +//! if client.wants_write() && socket.ready_for_write() { +//! client.write_tls(&mut socket).unwrap(); +//! } +//! +//! socket.wait_for_something_to_happen(); +//! } +//! # } +//! ``` +//! +//! # Examples +//! +//! You can find several client and server examples of varying complexity in the [examples] +//! directory, including [`tlsserver-mio`](https://github.com/rustls/rustls/blob/main/examples/src/bin/tlsserver-mio.rs) +//! and [`tlsclient-mio`](https://github.com/rustls/rustls/blob/main/examples/src/bin/tlsclient-mio.rs) +//! \- full worked examples using [`mio`]. +//! +//! [`mio`]: https://docs.rs/mio/latest/mio/ +//! +//! # Manual +//! +//! The [rustls manual](crate::manual) explains design decisions and includes how-to guidance. +//! +//! # Crate features +//! Here's a list of what features are exposed by the rustls crate and what +//! they mean. +//! +//! - `std` (enabled by default): enable the high-level (buffered) Connection API and other functionality +//! which relies on the `std` library. +//! +//! - `aws_lc_rs` (enabled by default): makes the rustls crate depend on the [`aws-lc-rs`] crate. +//! Use `rustls::crypto::aws_lc_rs::default_provider().install_default()` to +//! use it as the default `CryptoProvider`, or provide it explicitly +//! when making a `ClientConfig` or `ServerConfig`. +//! +//! Note that aws-lc-rs has additional build-time dependencies like cmake. +//! See [the documentation](https://aws.github.io/aws-lc-rs/requirements/index.html) for details. +//! +//! - `ring`: makes the rustls crate depend on the *ring* crate for cryptography. +//! Use `rustls::crypto::ring::default_provider().install_default()` to +//! use it as the default `CryptoProvider`, or provide it explicitly +//! when making a `ClientConfig` or `ServerConfig`. +//! +//! - `fips`: enable support for FIPS140-3-approved cryptography, via the [`aws-lc-rs`] crate. +//! This feature enables the `aws_lc_rs` crate feature, which makes the rustls crate depend +//! on [aws-lc-rs](https://github.com/aws/aws-lc-rs). It also changes the default +//! for [`ServerConfig::require_ems`] and [`ClientConfig::require_ems`]. +//! +//! See [manual::_06_fips] for more details. +//! +//! - `prefer-post-quantum` (enabled by default): for the [`aws-lc-rs`]-backed provider, +//! prioritizes post-quantum secure key exchange by default (using X25519MLKEM768). +//! This feature merely alters the order of `rustls::crypto::aws_lc_rs::DEFAULT_KX_GROUPS`. +//! See [the manual][x25519mlkem768-manual] for more details. +//! +//! - `custom-provider`: disables implicit use of built-in providers (`aws-lc-rs` or `ring`). This forces +//! applications to manually install one, for instance, when using a custom `CryptoProvider`. +//! +//! - `tls12` (enabled by default): enable support for TLS version 1.2. Note that, due to the +//! additive nature of Cargo features and because it is enabled by default, other crates +//! in your dependency graph could re-enable it for your application. If you want to disable +//! TLS 1.2 for security reasons, consider explicitly enabling TLS 1.3 only in the config +//! builder API. +//! +//! - `logging` (enabled by default): make the rustls crate depend on the `log` crate. +//! rustls outputs interesting protocol-level messages at `trace!` and `debug!` level, +//! and protocol-level errors at `warn!` and `error!` level. The log messages do not +//! contain secret key data, and so are safe to archive without affecting session security. +//! +//! - `read_buf`: when building with Rust Nightly, adds support for the unstable +//! `std::io::ReadBuf` and related APIs. This reduces costs from initializing +//! buffers. Will do nothing on non-Nightly releases. +//! +//! - `brotli`: uses the `brotli` crate for RFC8879 certificate compression support. +//! +//! - `zlib`: uses the `zlib-rs` crate for RFC8879 certificate compression support. +//! +//! [x25519mlkem768-manual]: manual::_05_defaults#about-the-post-quantum-secure-key-exchange-x25519mlkem768 + +// Require docs for public APIs, deny unsafe code, etc. +#![forbid(unsafe_code, unused_must_use)] +#![cfg_attr(not(any(read_buf, bench, coverage_nightly)), forbid(unstable_features))] +#![warn( + clippy::alloc_instead_of_core, + clippy::manual_let_else, + clippy::std_instead_of_core, + clippy::use_self, + clippy::upper_case_acronyms, + elided_lifetimes_in_paths, + missing_docs, + trivial_casts, + trivial_numeric_casts, + unreachable_pub, + unused_import_braces, + unused_extern_crates, + unused_qualifications +)] +// Relax these clippy lints: +// - ptr_arg: this triggers on references to type aliases that are Vec +// underneath. +// - too_many_arguments: some things just need a lot of state, wrapping it +// doesn't necessarily make it easier to follow what's going on +// - new_ret_no_self: we sometimes return `Arc<Self>`, which seems fine +// - single_component_path_imports: our top-level `use log` import causes +// a false positive, https://github.com/rust-lang/rust-clippy/issues/5210 +// - new_without_default: for internal constructors, the indirection is not +// helpful +#![allow( + clippy::too_many_arguments, + clippy::new_ret_no_self, + clippy::ptr_arg, + clippy::single_component_path_imports, + clippy::new_without_default +)] +// Enable documentation for all features on docs.rs +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] +// Enable coverage() attr for nightly coverage builds, see +// <https://github.com/rust-lang/rust/issues/84605> +// (`coverage_nightly` is a cfg set by `cargo-llvm-cov`) +#![cfg_attr(coverage_nightly, feature(coverage_attribute))] +// XXX: Because of https://github.com/rust-lang/rust/issues/54726, we cannot +// write `#![rustversion::attr(nightly, feature(read_buf))]` here. Instead, +// build.rs set `read_buf` for (only) Rust Nightly to get the same effect. +// +// All the other conditional logic in the crate could use +// `#[rustversion::nightly]` instead of `#[cfg(read_buf)]`; `#[cfg(read_buf)]` +// is used to avoid needing `rustversion` to be compiled twice during +// cross-compiling. +#![cfg_attr(read_buf, feature(read_buf))] +#![cfg_attr(read_buf, feature(core_io_borrowed_buf))] +#![cfg_attr(bench, feature(test))] +#![no_std] + +extern crate alloc; +// This `extern crate` plus the `#![no_std]` attribute changes the default prelude from +// `std::prelude` to `core::prelude`. That forces one to _explicitly_ import (`use`) everything that +// is in `std::prelude` but not in `core::prelude`. This helps maintain no-std support as even +// developers that are not interested in, or aware of, no-std support and / or that never run +// `cargo build --no-default-features` locally will get errors when they rely on `std::prelude` API. +#[cfg(any(feature = "std", test))] +extern crate std; + +#[cfg(doc)] +use crate::crypto::CryptoProvider; + +// Import `test` sysroot crate for `Bencher` definitions. +#[cfg(bench)] +#[allow(unused_extern_crates)] +extern crate test; + +// log for logging (optional). +#[cfg(feature = "logging")] +use log; + +#[cfg(not(feature = "logging"))] +mod log { + macro_rules! trace ( ($($tt:tt)*) => {{}} ); + macro_rules! debug ( ($($tt:tt)*) => {{}} ); + macro_rules! error ( ($($tt:tt)*) => {{}} ); + macro_rules! _warn ( ($($tt:tt)*) => {{}} ); + pub(crate) use {_warn as warn, debug, error, trace}; +} + +#[cfg(test)] +#[macro_use] +mod test_macros; + +/// This internal `sync` module aliases the `Arc` implementation to allow downstream forks +/// of rustls targeting architectures without atomic pointers to replace the implementation +/// with another implementation such as `portable_atomic_util::Arc` in one central location. +mod sync { + #[allow(clippy::disallowed_types)] + pub(crate) type Arc<T> = alloc::sync::Arc<T>; + #[allow(clippy::disallowed_types)] + pub(crate) type Weak<T> = alloc::sync::Weak<T>; +} + +#[macro_use] +mod msgs; +mod common_state; +pub mod compress; +mod conn; +/// Crypto provider interface. +pub mod crypto; +mod error; +mod hash_hs; +#[cfg(any(feature = "std", feature = "hashbrown"))] +mod limited_cache; +mod rand; +mod record_layer; +#[cfg(feature = "std")] +mod stream; +#[cfg(feature = "tls12")] +mod tls12; +mod tls13; +mod vecbuf; +mod verify; +#[cfg(test)] +mod verifybench; +mod x509; +#[macro_use] +mod check; +#[cfg(feature = "logging")] +mod bs_debug; +mod builder; +mod enums; +mod key_log; +#[cfg(feature = "std")] +mod key_log_file; +mod suites; +mod versions; +mod webpki; + +/// Internal classes that are used in integration tests. +/// The contents of this section DO NOT form part of the stable interface. +#[allow(missing_docs)] +#[doc(hidden)] +pub mod internal { + /// Low-level TLS message parsing and encoding functions. + pub mod msgs { + pub mod base { + pub use crate::msgs::base::{Payload, PayloadU16}; + } + pub mod codec { + pub use crate::msgs::codec::{Codec, Reader}; + } + pub mod enums { + pub use crate::msgs::enums::{ + AlertLevel, EchVersion, ExtensionType, HpkeAead, HpkeKdf, HpkeKem, + }; + } + pub mod fragmenter { + pub use crate::msgs::fragmenter::MessageFragmenter; + } + pub mod handshake { + pub use crate::msgs::handshake::{ + EchConfigContents, EchConfigPayload, HpkeKeyConfig, HpkeSymmetricCipherSuite, + }; + } + pub mod message { + pub use crate::msgs::message::{ + Message, MessagePayload, OutboundOpaqueMessage, PlainMessage, + }; + } + pub mod persist { + pub use crate::msgs::persist::ServerSessionValue; + } + } + + pub use crate::tls13::key_schedule::{derive_traffic_iv, derive_traffic_key}; + + pub mod fuzzing { + pub use crate::msgs::deframer::fuzz_deframer; + } +} + +/// Unbuffered connection API +/// +/// This is an alternative to the [`crate::ConnectionCommon`] API that does not internally buffer +/// TLS nor plaintext data. Instead those buffers are managed by the API user so they have +/// control over when and how to allocate, resize and dispose of them. +/// +/// This API is lower level than the `ConnectionCommon` API and is built around a state machine +/// interface where the API user must handle each state to advance and complete the +/// handshake process. +/// +/// Like the `ConnectionCommon` API, no IO happens internally so all IO must be handled by the API +/// user. Unlike the `ConnectionCommon` API, this API does not make use of the [`std::io::Read`] and +/// [`std::io::Write`] traits so it's usable in no-std context. +/// +/// The entry points into this API are [`crate::client::UnbufferedClientConnection::new`], +/// [`crate::server::UnbufferedServerConnection::new`] and +/// [`unbuffered::UnbufferedConnectionCommon::process_tls_records`]. The state machine API is +/// documented in [`unbuffered::ConnectionState`]. +/// +/// # Examples +/// +/// [`unbuffered-client`] and [`unbuffered-server`] are examples that fully exercise the API in +/// std, non-async context. +/// +/// [`unbuffered-client`]: https://github.com/rustls/rustls/blob/main/examples/src/bin/unbuffered-client.rs +/// [`unbuffered-server`]: https://github.com/rustls/rustls/blob/main/examples/src/bin/unbuffered-server.rs +pub mod unbuffered { + pub use crate::conn::UnbufferedConnectionCommon; + pub use crate::conn::unbuffered::{ + AppDataRecord, ConnectionState, EncodeError, EncodeTlsData, EncryptError, + InsufficientSizeError, ReadEarlyData, ReadTraffic, TransmitTlsData, UnbufferedStatus, + WriteTraffic, + }; +} + +// The public interface is: +pub use crate::builder::{ConfigBuilder, ConfigSide, WantsVerifier, WantsVersions}; +pub use crate::common_state::{CommonState, HandshakeKind, IoState, Side}; +#[cfg(feature = "std")] +pub use crate::conn::{Connection, Reader, Writer}; +pub use crate::conn::{ConnectionCommon, SideData, kernel}; +pub use crate::enums::{ + AlertDescription, CertificateCompressionAlgorithm, CipherSuite, ContentType, HandshakeType, + ProtocolVersion, SignatureAlgorithm, SignatureScheme, +}; +pub use crate::error::{ + CertRevocationListError, CertificateError, EncryptedClientHelloError, Error, + ExtendedKeyPurpose, InconsistentKeys, InvalidMessage, OtherError, PeerIncompatible, + PeerMisbehaved, +}; +pub use crate::key_log::{KeyLog, NoKeyLog}; +#[cfg(feature = "std")] +pub use crate::key_log_file::KeyLogFile; +pub use crate::msgs::enums::NamedGroup; +pub use crate::msgs::ffdhe_groups; +pub use crate::msgs::handshake::DistinguishedName; +#[cfg(feature = "std")] +pub use crate::stream::{Stream, StreamOwned}; +pub use crate::suites::{ + CipherSuiteCommon, ConnectionTrafficSecrets, ExtractedSecrets, SupportedCipherSuite, +}; +#[cfg(feature = "std")] +pub use crate::ticketer::TicketRotator; +#[cfg(any(feature = "std", feature = "hashbrown"))] // < XXX: incorrect feature gate +pub use crate::ticketer::TicketSwitcher; +#[cfg(feature = "tls12")] +pub use crate::tls12::Tls12CipherSuite; +pub use crate::tls13::Tls13CipherSuite; +pub use crate::verify::DigitallySignedStruct; +pub use crate::versions::{ALL_VERSIONS, DEFAULT_VERSIONS, SupportedProtocolVersion}; +pub use crate::webpki::RootCertStore; + +/// Items for use in a client. +pub mod client { + pub(super) mod builder; + mod client_conn; + mod common; + mod ech; + pub(super) mod handy; + mod hs; + #[cfg(test)] + mod test; + #[cfg(feature = "tls12")] + mod tls12; + mod tls13; + + pub use builder::WantsClientCert; + pub use client_conn::{ + ClientConfig, ClientConnectionData, ClientSessionStore, EarlyDataError, ResolvesClientCert, + Resumption, Tls12Resumption, UnbufferedClientConnection, + }; + #[cfg(feature = "std")] + pub use client_conn::{ClientConnection, WriteEarlyData}; + pub use ech::{EchConfig, EchGreaseConfig, EchMode, EchStatus}; + pub use handy::AlwaysResolvesClientRawPublicKeys; + #[cfg(any(feature = "std", feature = "hashbrown"))] + pub use handy::ClientSessionMemoryCache; + + /// Dangerous configuration that should be audited and used with extreme care. + pub mod danger { + pub use super::builder::danger::DangerousClientConfigBuilder; + pub use super::client_conn::danger::DangerousClientConfig; + pub use crate::verify::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; + } + + pub use crate::msgs::persist::{Tls12ClientSessionValue, Tls13ClientSessionValue}; + pub use crate::webpki::{ + ServerCertVerifierBuilder, VerifierBuilderError, WebPkiServerVerifier, + verify_server_cert_signed_by_trust_anchor, verify_server_name, + }; +} + +pub use client::ClientConfig; +#[cfg(feature = "std")] +pub use client::ClientConnection; + +/// Items for use in a server. +pub mod server { + pub(crate) mod builder; + mod common; + pub(crate) mod handy; + mod hs; + mod server_conn; + #[cfg(test)] + mod test; + #[cfg(feature = "tls12")] + mod tls12; + mod tls13; + + pub use builder::WantsServerCert; + #[cfg(any(feature = "std", feature = "hashbrown"))] + pub use handy::ResolvesServerCertUsingSni; + #[cfg(any(feature = "std", feature = "hashbrown"))] + pub use handy::ServerSessionMemoryCache; + pub use handy::{AlwaysResolvesServerRawPublicKeys, NoServerSessionStorage}; + pub use server_conn::{ + Accepted, ClientHello, ProducesTickets, ResolvesServerCert, ServerConfig, + ServerConnectionData, StoresServerSessions, UnbufferedServerConnection, + }; + #[cfg(feature = "std")] + pub use server_conn::{AcceptedAlert, Acceptor, ReadEarlyData, ServerConnection}; + + pub use crate::enums::CertificateType; + pub use crate::verify::NoClientAuth; + pub use crate::webpki::{ + ClientCertVerifierBuilder, ParsedCertificate, VerifierBuilderError, WebPkiClientVerifier, + }; + + /// Dangerous configuration that should be audited and used with extreme care. + pub mod danger { + pub use crate::verify::{ClientCertVerified, ClientCertVerifier}; + } +} + +pub use server::ServerConfig; +#[cfg(feature = "std")] +pub use server::ServerConnection; + +/// All defined protocol versions appear in this module. +/// +/// ALL_VERSIONS is a provided as an array of all of these values. +pub mod version { + #[cfg(feature = "tls12")] + pub use crate::versions::TLS12; + pub use crate::versions::TLS13; +} + +/// Re-exports the contents of the [rustls-pki-types](https://docs.rs/rustls-pki-types) crate for easy access +pub mod pki_types { + #[doc(no_inline)] + pub use pki_types::*; +} + +/// Message signing interfaces. +pub mod sign { + pub use crate::crypto::signer::{CertifiedKey, Signer, SigningKey, SingleCertAndKey}; +} + +/// APIs for implementing QUIC TLS +pub mod quic; + +#[cfg(any(feature = "std", feature = "hashbrown"))] // < XXX: incorrect feature gate +/// APIs for implementing TLS tickets +pub mod ticketer; + +/// This is the rustls manual. +pub mod manual; + +pub mod time_provider; + +/// APIs abstracting over locking primitives. +pub mod lock; + +/// Polyfills for features that are not yet stabilized or available with current MSRV. +pub(crate) mod polyfill; + +#[cfg(any(feature = "std", feature = "hashbrown"))] +mod hash_map { + #[cfg(feature = "std")] + pub(crate) use std::collections::HashMap; + #[cfg(feature = "std")] + pub(crate) use std::collections::hash_map::Entry; + + #[cfg(all(not(feature = "std"), feature = "hashbrown"))] + pub(crate) use hashbrown::HashMap; + #[cfg(all(not(feature = "std"), feature = "hashbrown"))] + pub(crate) use hashbrown::hash_map::Entry; +} diff --git a/vendor/rustls/src/limited_cache.rs b/vendor/rustls/src/limited_cache.rs new file mode 100644 index 00000000..6ae8bf94 --- /dev/null +++ b/vendor/rustls/src/limited_cache.rs @@ -0,0 +1,250 @@ +use alloc::collections::VecDeque; +use core::borrow::Borrow; +use core::hash::Hash; + +use crate::hash_map::{Entry, HashMap}; + +/// A HashMap-alike, which never gets larger than a specified +/// capacity, and evicts the oldest insertion to maintain this. +/// +/// The requested capacity may be rounded up by the underlying +/// collections. This implementation uses all the allocated +/// storage. +/// +/// This is inefficient: it stores keys twice. +pub(crate) struct LimitedCache<K: Clone + Hash + Eq, V> { + map: HashMap<K, V>, + + // first item is the oldest key + oldest: VecDeque<K>, +} + +impl<K, V> LimitedCache<K, V> +where + K: Eq + Hash + Clone + core::fmt::Debug, + V: Default, +{ + pub(crate) fn get_or_insert_default_and_edit(&mut self, k: K, edit: impl FnOnce(&mut V)) { + let inserted_new_item = match self.map.entry(k) { + Entry::Occupied(value) => { + edit(value.into_mut()); + false + } + entry @ Entry::Vacant(_) => { + self.oldest + .push_back(entry.key().clone()); + edit(entry.or_insert_with(V::default)); + true + } + }; + + // ensure next insertion does not require a realloc + if inserted_new_item && self.oldest.capacity() == self.oldest.len() { + if let Some(oldest_key) = self.oldest.pop_front() { + self.map.remove(&oldest_key); + } + } + } + + pub(crate) fn get_mut<Q: Hash + Eq + ?Sized>(&mut self, k: &Q) -> Option<&mut V> + where + K: Borrow<Q>, + { + self.map.get_mut(k) + } +} + +impl<K, V> LimitedCache<K, V> +where + K: Eq + Hash + Clone + core::fmt::Debug, + V: Default, +{ + /// Create a new LimitedCache with the given rough capacity. + pub(crate) fn new(capacity_order_of_magnitude: usize) -> Self { + Self { + map: HashMap::with_capacity(capacity_order_of_magnitude), + oldest: VecDeque::with_capacity(capacity_order_of_magnitude), + } + } + + pub(crate) fn insert(&mut self, k: K, v: V) { + let inserted_new_item = match self.map.entry(k) { + Entry::Occupied(mut old) => { + // Note: does not freshen entry in `oldest` + old.insert(v); + false + } + + entry @ Entry::Vacant(_) => { + self.oldest + .push_back(entry.key().clone()); + entry.or_insert(v); + true + } + }; + + // ensure next insertion does not require a realloc + if inserted_new_item && self.oldest.capacity() == self.oldest.len() { + if let Some(oldest_key) = self.oldest.pop_front() { + self.map.remove(&oldest_key); + } + } + } + + pub(crate) fn get<Q: Hash + Eq + ?Sized>(&self, k: &Q) -> Option<&V> + where + K: Borrow<Q>, + { + self.map.get(k) + } + + pub(crate) fn remove<Q: Hash + Eq + ?Sized>(&mut self, k: &Q) -> Option<V> + where + K: Borrow<Q>, + { + let value = self.map.remove(k)?; + + // O(N) search, followed by O(N) removal + if let Some(index) = self + .oldest + .iter() + .position(|item| item.borrow() == k) + { + self.oldest.remove(index); + } + + Some(value) + } +} + +#[cfg(test)] +mod tests { + use std::prelude::v1::*; + + type Test = super::LimitedCache<String, usize>; + + #[test] + fn test_updates_existing_item() { + let mut t = Test::new(3); + t.insert("abc".into(), 1); + t.insert("abc".into(), 2); + assert_eq!(t.get("abc"), Some(&2)); + } + + #[test] + fn test_evicts_oldest_item() { + let mut t = Test::new(3); + t.insert("abc".into(), 1); + t.insert("def".into(), 2); + t.insert("ghi".into(), 3); + + assert_eq!(t.get("abc"), None); + assert_eq!(t.get("def"), Some(&2)); + assert_eq!(t.get("ghi"), Some(&3)); + } + + #[test] + fn test_evicts_second_oldest_item_if_first_removed() { + let mut t = Test::new(3); + t.insert("abc".into(), 1); + t.insert("def".into(), 2); + + assert_eq!(t.remove("abc"), Some(1)); + + t.insert("ghi".into(), 3); + t.insert("jkl".into(), 4); + + assert_eq!(t.get("abc"), None); + assert_eq!(t.get("def"), None); + assert_eq!(t.get("ghi"), Some(&3)); + assert_eq!(t.get("jkl"), Some(&4)); + } + + #[test] + fn test_evicts_after_second_oldest_item_removed() { + let mut t = Test::new(3); + t.insert("abc".into(), 1); + t.insert("def".into(), 2); + + assert_eq!(t.remove("def"), Some(2)); + assert_eq!(t.get("abc"), Some(&1)); + + t.insert("ghi".into(), 3); + t.insert("jkl".into(), 4); + + assert_eq!(t.get("abc"), None); + assert_eq!(t.get("def"), None); + assert_eq!(t.get("ghi"), Some(&3)); + assert_eq!(t.get("jkl"), Some(&4)); + } + + #[test] + fn test_removes_all_items() { + let mut t = Test::new(3); + t.insert("abc".into(), 1); + t.insert("def".into(), 2); + + assert_eq!(t.remove("def"), Some(2)); + assert_eq!(t.remove("abc"), Some(1)); + + t.insert("ghi".into(), 3); + t.insert("jkl".into(), 4); + t.insert("mno".into(), 5); + + assert_eq!(t.get("abc"), None); + assert_eq!(t.get("def"), None); + assert_eq!(t.get("ghi"), None); + assert_eq!(t.get("jkl"), Some(&4)); + assert_eq!(t.get("mno"), Some(&5)); + } + + #[test] + fn test_inserts_many_items() { + let mut t = Test::new(3); + + for _ in 0..10000 { + t.insert("abc".into(), 1); + t.insert("def".into(), 2); + t.insert("ghi".into(), 3); + } + } + + #[test] + fn test_get_or_insert_default_and_edit_evicts_old_items_to_meet_capacity() { + let mut t = Test::new(3); + + t.get_or_insert_default_and_edit("abc".into(), |v| *v += 1); + t.get_or_insert_default_and_edit("def".into(), |v| *v += 2); + + // evicts "abc" + t.get_or_insert_default_and_edit("ghi".into(), |v| *v += 3); + assert_eq!(t.get("abc"), None); + + // evicts "def" + t.get_or_insert_default_and_edit("jkl".into(), |v| *v += 4); + assert_eq!(t.get("def"), None); + + // evicts "ghi" + t.get_or_insert_default_and_edit("abc".into(), |v| *v += 5); + assert_eq!(t.get("ghi"), None); + + // evicts "jkl" + t.get_or_insert_default_and_edit("def".into(), |v| *v += 6); + + assert_eq!(t.get("abc"), Some(&5)); + assert_eq!(t.get("def"), Some(&6)); + assert_eq!(t.get("ghi"), None); + assert_eq!(t.get("jkl"), None); + } + + #[test] + fn test_get_or_insert_default_and_edit_edits_existing_item() { + let mut t = Test::new(3); + + t.get_or_insert_default_and_edit("abc".into(), |v| *v += 1); + t.get_or_insert_default_and_edit("abc".into(), |v| *v += 2); + t.get_or_insert_default_and_edit("abc".into(), |v| *v += 3); + + assert_eq!(t.get("abc"), Some(&6)); + } +} diff --git a/vendor/rustls/src/lock.rs b/vendor/rustls/src/lock.rs new file mode 100644 index 00000000..b632b2c5 --- /dev/null +++ b/vendor/rustls/src/lock.rs @@ -0,0 +1,89 @@ +#[cfg(not(feature = "std"))] +pub use no_std_lock::*; +#[cfg(feature = "std")] +pub use std_lock::*; + +#[cfg(feature = "std")] +mod std_lock { + use std::sync::Mutex as StdMutex; + pub use std::sync::MutexGuard; + + /// A wrapper around [`std::sync::Mutex`]. + #[derive(Debug)] + pub struct Mutex<T> { + inner: StdMutex<T>, + } + + impl<T> Mutex<T> { + /// Creates a new mutex in an unlocked state ready for use. + pub fn new(data: T) -> Self { + Self { + inner: StdMutex::new(data), + } + } + + /// Acquires the mutex, blocking the current thread until it is able to do so. + /// + /// This will return `None` in the case the mutex is poisoned. + #[inline] + pub fn lock(&self) -> Option<MutexGuard<'_, T>> { + self.inner.lock().ok() + } + } +} + +#[cfg(not(feature = "std"))] +mod no_std_lock { + use alloc::boxed::Box; + use core::fmt::Debug; + use core::ops::DerefMut; + + use crate::sync::Arc; + + /// A no-std compatible wrapper around [`Lock`]. + #[derive(Debug)] + pub struct Mutex<T> { + inner: Arc<dyn Lock<T>>, + } + + impl<T: Send + 'static> Mutex<T> { + /// Creates a new mutex in an unlocked state ready for use. + pub fn new<M>(val: T) -> Self + where + M: MakeMutex, + T: Send + 'static, + { + Self { + inner: M::make_mutex(val), + } + } + + /// Acquires the mutex, blocking the current thread until it is able to do so. + /// + /// This will return `None` in the case the mutex is poisoned. + #[inline] + pub fn lock(&self) -> Option<MutexGuard<'_, T>> { + self.inner.lock().ok() + } + } + + /// A lock protecting shared data. + pub trait Lock<T>: Debug + Send + Sync { + /// Acquire the lock. + fn lock(&self) -> Result<MutexGuard<'_, T>, Poisoned>; + } + + /// A lock builder. + pub trait MakeMutex { + /// Create a new mutex. + fn make_mutex<T>(value: T) -> Arc<dyn Lock<T>> + where + T: Send + 'static; + } + + /// A no-std compatible mutex guard. + pub type MutexGuard<'a, T> = Box<dyn DerefMut<Target = T> + 'a>; + + /// A marker type used to indicate `Lock::lock` failed due to a poisoned lock. + pub struct Poisoned; +} diff --git a/vendor/rustls/src/manual/defaults.rs b/vendor/rustls/src/manual/defaults.rs new file mode 100644 index 00000000..f6f70dc0 --- /dev/null +++ b/vendor/rustls/src/manual/defaults.rs @@ -0,0 +1,71 @@ +/*! + +## Rationale for defaults + +### Why is AES-256 preferred over AES-128? + +This is a trade-off between: + +1. classical security level: searching a 2^128 key space is as implausible as 2^256. +2. post-quantum security level: the difference is more meaningful, and AES-256 seems like the conservative choice. +3. performance: AES-256 is around 40% slower than AES-128, though hardware acceleration typically narrows this gap. + +The choice is frankly quite marginal. + +### Why is AES-GCM preferred over chacha20-poly1305? + +Hardware support for accelerating AES-GCM is widespread, and hardware-accelerated AES-GCM +is quicker than un-accelerated chacha20-poly1305. + +However, if you know your application will run on a platform without that, you should +_definitely_ change the default order to prefer chacha20-poly1305: both the performance and +the implementation security will be improved. We think this is an uncommon case. + +### Why is x25519 preferred for key exchange over nistp256? + +Both provide roughly the same classical security level, but x25519 has better performance and +it's _much_ more likely that both peers will have good quality implementations. + +### About the post-quantum-secure key exchange `X25519MLKEM768` + +[`X25519MLKEM768`] -- a hybrid[^1], post-quantum-secure[^2] key exchange +algorithm -- is available when using the aws-lc-rs provider. + +The `prefer-post-quantum` crate feature makes `X25519MLKEM768` the +highest-priority key exchange algorithm. Otherwise, it is available but +not highest-priority. + +[X25519MLKEM768] is pre-standardization, but is now widely deployed, +for example, by [Chrome] and [Cloudflare]. + +You may see unexpected connection failures (such as [tldr.fail]) +-- [please report these to us][interop-bug]. + +The two components of this key exchange are well regarded: +X25519 alone is already used by default by rustls, and tends to have +higher quality implementations than other elliptic curves. +ML-KEM-768 was standardized by NIST in [FIPS203]. + +[`MLKEM768`] is available separately, but is not currently enabled +by default out of conservatism. + +[^1]: meaning: a construction that runs a classical and post-quantum + key exchange, and uses the output of both together. This is a hedge + against the post-quantum half being broken. + +[^2]: a "post-quantum-secure" algorithm is one posited to be invulnerable + to attack using a cryptographically-relevant quantum computer. In contrast, + classical algorithms would be broken by such a computer. Note that such computers + do not currently exist, and may never exist, but current traffic could be captured + now and attacked later. + +[X25519MLKEM768]: <https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/> +[`X25519MLKEM768`]: crate::crypto::aws_lc_rs::kx_group::X25519MLKEM768 +[`MLKEM768`]: crate::crypto::aws_lc_rs::kx_group::MLKEM768 +[FIPS203]: <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf> +[Chrome]: <https://security.googleblog.com/2024/09/a-new-path-for-kyber-on-web.html> +[Cloudflare]: <https://blog.cloudflare.com/pq-2024/#ml-kem-768-and-x25519> +[interop-bug]: <https://github.com/rustls/rustls/issues/new?assignees=&labels=&projects=&template=bug_report.md&title=> +[tldr.fail]: <https://tldr.fail/> + +*/ diff --git a/vendor/rustls/src/manual/features.rs b/vendor/rustls/src/manual/features.rs new file mode 100644 index 00000000..69daf48a --- /dev/null +++ b/vendor/rustls/src/manual/features.rs @@ -0,0 +1,100 @@ +/*! + +The below list reflects the support provided with the default crate features. +Items marked with an asterisk `*` can be extended or altered via public +APIs ([`CryptoProvider`] for example). + +[`CryptoProvider`]: crate::crypto::CryptoProvider + +## Current features + +* TLS1.2 and TLS1.3 +* ECDSA, Ed25519 or RSA server authentication by clients `*` +* ECDSA, Ed25519[^1] or RSA server authentication by servers `*` +* Forward secrecy using ECDHE; with curve25519, nistp256 or nistp384 curves `*` +* Post-quantum hybrid key exchange with [X25519MLKEM768](https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/) [^2] `*` +* AES128-GCM and AES256-GCM bulk encryption, with safe nonces `*` +* ChaCha20-Poly1305 bulk encryption ([RFC7905](https://tools.ietf.org/html/rfc7905)) `*` +* ALPN support +* SNI support +* Tunable fragment size to make TLS messages match size of underlying transport +* Optional use of vectored IO to minimise system calls +* TLS1.2 session resumption +* TLS1.2 resumption via tickets ([RFC5077](https://tools.ietf.org/html/rfc5077)) +* TLS1.3 resumption via tickets or session storage +* TLS1.3 0-RTT data +* Server and optional client authentication +* Extended master secret support ([RFC7627](https://tools.ietf.org/html/rfc7627)) +* Exporters ([RFC5705](https://tools.ietf.org/html/rfc5705)) +* OCSP stapling by servers +* [RFC7250](https://tools.ietf.org/html/rfc7250) raw public keys for TLS1.3 +* [RFC8879](https://tools.ietf.org/html/rfc8879) certificate compression by clients + and servers `*` +* Client-side Encrypted client hello (ECH) + ([draft-ietf-tls-esni](https://datatracker.ietf.org/doc/draft-ietf-tls-esni/)). + +[^1]: Note that, at the time of writing, Ed25519 does not have wide support + in browsers. It is also not supported by the WebPKI, because the + CA/Browser Forum Baseline Requirements do not support it for publicly + trusted certificates. +[^2]: See [the documentation][crate::manual::_05_defaults#about-the-post-quantum-secure-key-exchange-x25519mlkem768] + +## Non-features + +For reasons explained in the other sections of this manual, rustls does not +and will not support: + +* SSL1, SSL2, SSL3, TLS1 or TLS1.1 +* RC4 +* DES or triple DES +* EXPORT ciphersuites +* MAC-then-encrypt ciphersuites +* Ciphersuites without forward secrecy +* Renegotiation +* Kerberos +* TLS 1.2 protocol compression +* Discrete-log Diffie-Hellman `*` +* Automatic protocol version downgrade +* Using CA certificates directly to authenticate a server/client (often called "self-signed + certificates"). _Rustls' default certificate verifier does not support using a trust anchor as + both a CA certificate and an end-entity certificate in order to limit complexity and risk in + path building. While dangerous, all authentication can be turned off if required -- + see the [example code](https://github.com/rustls/rustls/blob/v/0.23.23/examples/src/bin/tlsclient-mio.rs#L338)_ `*` + +### About "custom extensions" + +OpenSSL allows an application to add arbitrary TLS extensions (via +the `SSL_CTX_add_custom_ext` function and associated APIs). We don't +support this, with the following rationale: + +Such an API is limited to extensions that are quite narrow in scope: +they cannot change the meaning of standard messages, or introduce new +messages, or make any changes to the connection's cryptography. + +However, there is no reasonable way to technically limit that API to +that set of extensions. That makes the API pretty unsafe (in the +TLS and cryptography sense, not memory safety sense). This could +cause security or interop failures. + +Instead, we suggest that potential users of that API consider: + +- whether their use can fit in standard extensions such as ALPN, + or [ALPS][alps][^3]. +- if not, whether they can fit in a more general extension, and define + and standardize that in the [IETF TLSWG][tlswg]. + +Note the above is not a guarantee or offer that rustls will implement +any specific extensions that are standardized by the IETF TLSWG. +It is a non-goal of this project to implement absolutely everything. + +For experimentation and pre-standardization testing, we suggest +forking rustls. + +See also: [Go's position on such an API][golang]. + +[alps]: https://datatracker.ietf.org/doc/html/draft-vvv-tls-alps +[golang]: https://github.com/golang/go/issues/51497 +[tlswg]: https://datatracker.ietf.org/wg/tls/charter/ +[^3]: rustls does not currently implement ALPS, but it is something we + would consider once standardised and deployed. +*/ diff --git a/vendor/rustls/src/manual/fips.rs b/vendor/rustls/src/manual/fips.rs new file mode 100644 index 00000000..203a759e --- /dev/null +++ b/vendor/rustls/src/manual/fips.rs @@ -0,0 +1,62 @@ +/*! # Using rustls with FIPS-approved cryptography + +To use FIPS-approved cryptography with rustls, you should take +these actions: + +## 1. Enable the `fips` crate feature for rustls. + +Use: + +```toml +rustls = { version = "0.23", features = [ "fips" ] } +``` + +## 2. Use the FIPS `CryptoProvider` + +This is [`default_fips_provider()`]: + +```rust,ignore +rustls::crypto::default_fips_provider() + .install_default() + .expect("default provider already set elsewhere"); +``` + +This snippet makes use of the process-default provider, +and that assumes all your uses of rustls use that. +See [`CryptoProvider`] documentation for other ways to +specify which `CryptoProvider` to use. + +## 3. Validate the FIPS status of your `ClientConfig`/`ServerConfig` at run-time + +See [`ClientConfig::fips()`] or [`ServerConfig::fips()`]. + +You could, for example: + +```rust,ignore +# let client_config = unreachable!(); +assert!(client_config.fips()); +``` + +But maybe your application has an error handling +or health-check strategy better than panicking. + +# aws-lc-rs FIPS approval status + +This is covered by [FIPS 140-3 certificate #4816][cert-4816]. +See [the security policy][policy-4816] for precisely which +environments and functions this certificate covers. + +Later releases of aws-lc-rs may be covered by later certificates, +or be pending certification. + +For the most up-to-date details see the latest documentation +for the [`aws-lc-fips-sys`] crate. + +[`aws-lc-fips-sys`]: https://crates.io/crates/aws-lc-fips-sys +[`default_fips_provider()`]: crate::crypto::default_fips_provider +[`CryptoProvider`]: crate::crypto::CryptoProvider +[`ClientConfig::fips()`]: crate::client::ClientConfig::fips +[`ServerConfig::fips()`]: crate::server::ServerConfig::fips +[cert-4816]: https://csrc.nist.gov/projects/cryptographic-module-validation-program/certificate/4816 +[policy-4816]: https://csrc.nist.gov/CSRC/media/projects/cryptographic-module-validation-program/documents/security-policies/140sp4816.pdf +*/ diff --git a/vendor/rustls/src/manual/howto.rs b/vendor/rustls/src/manual/howto.rs new file mode 100644 index 00000000..6c00c695 --- /dev/null +++ b/vendor/rustls/src/manual/howto.rs @@ -0,0 +1,124 @@ +/*! # Customising private key usage + +By default rustls supports PKCS#8-format[^1] RSA or ECDSA keys, plus PKCS#1-format RSA keys. + +However, if your private key resides in a HSM, or in another process, or perhaps +another machine, rustls has some extension points to support this: + +The main trait you must implement is [`sign::SigningKey`][signing_key]. The primary method here +is [`choose_scheme()`][choose_scheme] where you are given a set of [`SignatureScheme`s][sig_scheme] the client says +it supports: you must choose one (or return `None` -- this aborts the handshake). Having +done that, you return an implementation of the [`sign::Signer`][signer] trait. +The [`sign()`][sign_method] performs the signature and returns it. + +(Unfortunately this is currently designed for keys with low latency access, like in a +PKCS#11 provider, Microsoft CryptoAPI, etc. so is blocking rather than asynchronous. +It's a TODO to make these and other extension points async.) + +Once you have these two pieces, configuring a server to use them involves, briefly: + +- packaging your [`sign::SigningKey`][signing_key] with the matching certificate chain into a [`sign::CertifiedKey`][certified_key] +- making a [`ResolvesServerCertUsingSni`][cert_using_sni] and feeding in your [`sign::CertifiedKey`][certified_key] for all SNI hostnames you want to use it for, +- setting that as your `ServerConfig`'s [`cert_resolver`][cert_resolver] + +For a complete example of implementing a custom [`sign::SigningKey`][signing_key] and +[`sign::Signer`][signer] see the [`signer` module in the `rustls-cng` crate][rustls-cng-signer]. + +[signing_key]: crate::crypto::signer::SigningKey +[choose_scheme]: crate::crypto::signer::SigningKey::choose_scheme +[sig_scheme]: crate::SignatureScheme +[signer]: crate::crypto::signer::Signer +[sign_method]: crate::crypto::signer::Signer::sign +[certified_key]: crate::crypto::signer::CertifiedKey +[cert_using_sni]: crate::server::ResolvesServerCertUsingSni +[cert_resolver]: crate::ServerConfig::cert_resolver +[rustls-cng-signer]: https://github.com/rustls/rustls-cng/blob/dev/src/signer.rs + +[^1]: For PKCS#8 it does not support password encryption -- there's not a meaningful threat + model addressed by this, and the encryption supported is typically extremely poor. + +# Unexpected EOF + +TLS has a `close_notify` mechanism to prevent truncation attacks[^2]. +According to the TLS RFCs, each party is required to send a `close_notify` message before +closing the write side of the connection. However, some implementations don't send it. +So long as the application layer protocol (for instance HTTP/2) has message length framing +and can reject truncated messages, this is not a security problem. + +Rustls treats an EOF without `close_notify` as an error of type `std::io::Error` with +`ErrorKind::UnexpectedEof`. In some situations it's appropriate for the application to handle +this error the same way it would handle a normal EOF (a read returning `Ok(0)`). In particular +if `UnexpectedEof` occurs on an idle connection it is appropriate to treat it the same way as a +clean shutdown. And if an application always uses messages with length framing (in other words, +messages are never delimited by the close of the TCP connection), it can unconditionally +ignore `UnexpectedEof` errors from rustls. + +[^2]: <https://datatracker.ietf.org/doc/html/rfc8446#section-6.1> + +# Debugging + +If you encounter a bug with Rustls it can be helpful to collect up as much diagnostic +information as possible. + +## Collecting logs + +If your bug reproduces with one of the [Rustls examples] you can use the +[`RUST_LOG`] environment variable to increase the log verbosity. If you're using +your own application, you may need to configure it with a logging backend +like `env_logger`. + +Consider reproducing your bug with `RUST_LOG=rustls=trace` and sharing the result +in a [GitHub gist]. + +[Rustls examples]: https://github.com/rustls/rustls/tree/main/examples +[`RUST_LOG`]: https://docs.rs/env_logger/latest/env_logger/#enabling-logging +[`env_logger`]: https://docs.rs/env_logger/ +[GitHub gist]: https://docs.github.com/en/get-started/writing-on-github/editing-and-sharing-content-with-gists/creating-gists + +## Taking a packet capture + +When logs aren't enough taking a packet capture ("pcap") is another helpful tool. +The details of how to accomplish this vary by operating system/context. + +### tcpdump + +As one example, on Linux using [`tcpdump`] is often easiest. + +If you know the IP address of the remote server your bug demonstrates with you +could take a short packet capture with this command (after replacing +`XX.XX.XX.XX` with the correct IP address): + +```bash +sudo tcpdump -i any tcp and dst host XX.XX.XX.XX -C5 -w rustls.pcap +``` + +The `-i any` captures on any network interface. The `tcp and dst host XX.XX.XX.XX` +portion target the capture to TCP traffic to the specified IP address. The `-C5` +argument limits the capture to at most 5MB. Lastly the `-w` argument writes the +capture to `rustls.pcap`. + +Another approach is to use `tcp and port XXXX` instead of `tcp and dst host XX.XX.XX.XX` +to capture all traffic to a specific port instead of a specific host server. + +[`tcpdump`]: https://www.redhat.com/en/blog/introduction-using-tcpdump-linux-command-line + +### SSLKEYLOGFILE + +If the bug you are reporting happens after data is encrypted you may also wish to +share the secret keys required to decrypt the post-handshake traffic. + +If you're using one of the [Rustls examples] you can set the `SSLKEYLOGFILE` environment +variable to a path where secrets will be written. E.g. `SSLKEYLOGFILE=rustls.pcap.keys`. + +If you're using your own application you may need to customize the Rustls `ClientConfig` +or `ServerConfig`'s `key_log` setting like the example applications do. + +With the file from `SSLKEYLOGFILE` it is possible to use [Wireshark] or another tool to +decrypt the post-handshake messages, following [these instructions][curl-sslkeylogfile]. + +Remember this allows plaintext decryption and should only be done in testing contexts +where no sensitive data (API keys, etc) are being shared. + +[Wireshark]: https://www.wireshark.org/download.html +[curl-sslkeylogfile]: https://everything.curl.dev/usingcurl/tls/sslkeylogfile.html +*/ diff --git a/vendor/rustls/src/manual/implvulns.rs b/vendor/rustls/src/manual/implvulns.rs new file mode 100644 index 00000000..a073350c --- /dev/null +++ b/vendor/rustls/src/manual/implvulns.rs @@ -0,0 +1,104 @@ +/*! # A review of TLS Implementation Vulnerabilities + +An important part of engineering involves studying and learning from the mistakes of the past. +It would be tremendously unfortunate to spend effort re-discovering and re-fixing the same +vulnerabilities that were discovered in the past. + +## Memory safety + +Being written entirely in the safe-subset of Rust immediately offers us freedom from the entire +class of memory safety vulnerabilities. There are too many to exhaustively list, and there will +certainly be more in the future. + +Examples: + +- Heartbleed [CVE-2014-0160](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-0160) (OpenSSL) +- Memory corruption in ASN.1 decoder [CVE-2016-2108](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2108) (OpenSSL) +- Buffer overflow in read_server_hello [CVE-2014-3466](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3466) (GnuTLS) + +## `goto fail` + +This is the name of a vulnerability in Apple Secure Transport [CVE-2014-1266](https://nvd.nist.gov/vuln/detail/CVE-2014-1266). +This boiled down to the following code, which validates the server's signature on the key exchange: + +```c + if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0) + goto fail; + if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) + goto fail; +> goto fail; + if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0) + goto fail; +``` + +The marked line was duplicated, likely accidentally during a merge. This meant +the remaining part of the function (including the actual signature validation) +was unconditionally skipped. + +Ultimately the one countermeasure to this type of bug is basic testing: that a +valid signature returns success, and that an invalid one does not. rustls +has such testing, but this is really table stakes for security code. + +Further than this, though, we could consider that the *lack* of an error from +this function is a poor indicator that the signature was valid. rustls, instead, +has zero-size and non-copyable types that indicate a particular signature validation +has been performed. These types can be thought of as *capabilities* originated only +by designated signature verification functions -- such functions can then be a focus +of manual code review. Like capabilities, values of these types are otherwise unforgeable, +and are communicable only by Rust's move semantics. + +Values of these types are threaded through the protocol state machine, leading to terminal +states that look like: + +```ignore +struct ExpectTraffic { + (...) + _cert_verified: verify::ServerCertVerified, + _sig_verified: verify::HandshakeSignatureValid, + _fin_verified: verify::FinishedMessageVerified, +} +``` + +Since this state requires a value of these types, it will be a compile-time error to +reach that state without performing the requisite security-critical operations. + +This approach is not infallible, but it has zero runtime cost. + +## State machine attacks: EarlyCCS and SMACK/SKIP/FREAK + +EarlyCCS [CVE-2014-0224](https://nvd.nist.gov/vuln/detail/CVE-2014-0224) was a vulnerability in OpenSSL +found in 2014. The TLS `ChangeCipherSpec` message would be processed at inappropriate times, leading +to data being encrypted with the wrong keys (specifically, keys which were not secret). This resulted +from OpenSSL taking a *reactive* strategy to incoming messages ("when I get a message X, I should do Y") +which allows it to diverge from the proper state machine under attacker control. + +[SMACK](https://mitls.org/pages/attacks/SMACK) is a similar suite of vulnerabilities found in JSSE, +CyaSSL, OpenSSL, Mono and axTLS. "SKIP-TLS" demonstrated that some implementations allowed handshake +messages (and in one case, the entire handshake!) to be skipped leading to breaks in security. "FREAK" +found that some implementations incorrectly allowed export-only state transitions (i.e., transitions that +were only valid when an export ciphersuite was in use). + +rustls represents its protocol state machine carefully to avoid these defects. We model the handshake, +CCS and application data subprotocols in the same single state machine. Each state in this machine is +represented with a single struct, and transitions are modelled as functions that consume the current state +plus one TLS message[^1] and return a struct representing the next state. These functions fully validate +the message type before further operations. + +A sample sequence for a full TLSv1.2 handshake by a client looks like: + +- `hs::ExpectServerHello` (Note: ClientHello is logically sent before this state); transition to `tls12::ExpectCertificate` +- `tls12::ExpectCertificate`; transition to `tls12::ExpectServerKX` +- `tls12::ExpectServerKX`; transition to `tls12::ExpectServerDoneOrCertReq` +- `tls12::ExpectServerDoneOrCertReq`; delegates to `tls12::ExpectCertificateRequest` or `tls12::ExpectServerDone` depending on incoming message. + - `tls12::ExpectServerDone`; transition to `tls12::ExpectCCS` +- `tls12::ExpectCCS`; transition to `tls12::ExpectFinished` +- `tls12::ExpectFinished`; transition to `tls12::ExpectTraffic` +- `tls12::ExpectTraffic`; terminal state; transitions to `tls12::ExpectTraffic` + +In the future we plan to formally prove that all possible transitions modelled in this system of types +are correct with respect to the standard(s). At the moment we rely merely on exhaustive testing. + +[^1]: a logical TLS message: post-decryption, post-fragmentation. + + +*/ diff --git a/vendor/rustls/src/manual/mod.rs b/vendor/rustls/src/manual/mod.rs new file mode 100644 index 00000000..0e4fddce --- /dev/null +++ b/vendor/rustls/src/manual/mod.rs @@ -0,0 +1,34 @@ +/*! + +This documentation primarily aims to explain design decisions taken in rustls. + +It does this from a few aspects: how rustls attempts to avoid construction errors +that occurred in other TLS libraries, how rustls attempts to avoid past TLS +protocol vulnerabilities, and assorted advice for achieving common tasks with rustls. +*/ +#![allow(non_snake_case)] + +/// This section discusses vulnerabilities in other TLS implementations, theorising their +/// root cause and how we aim to avoid them in rustls. +#[path = "implvulns.rs"] +pub mod _01_impl_vulnerabilities; + +/// This section discusses vulnerabilities and design errors in the TLS protocol. +#[path = "tlsvulns.rs"] +pub mod _02_tls_vulnerabilities; + +/// This section collects together goal-oriented documentation. +#[path = "howto.rs"] +pub mod _03_howto; + +/// This section documents rustls itself: what protocol features are and are not implemented. +#[path = "features.rs"] +pub mod _04_features; + +/// This section provides rationale for the defaults in rustls. +#[path = "defaults.rs"] +pub mod _05_defaults; + +/// This section provides guidance on using rustls with FIPS-approved cryptography. +#[path = "fips.rs"] +pub mod _06_fips; diff --git a/vendor/rustls/src/manual/tlsvulns.rs b/vendor/rustls/src/manual/tlsvulns.rs new file mode 100644 index 00000000..6d2220e3 --- /dev/null +++ b/vendor/rustls/src/manual/tlsvulns.rs @@ -0,0 +1,175 @@ +/*! # A review of protocol vulnerabilities + +## CBC MAC-then-encrypt ciphersuites + +Back in 2000 [Bellare and Namprempre](https://eprint.iacr.org/2000/025) discussed how to make authenticated +encryption by composing separate encryption and authentication primitives. That paper included this table: + +| Composition Method | Privacy | | | Integrity | | +|--------------------|---------|-|-|-----------|-| +|| IND-CPA | IND-CCA | NM-CPA | INT-PTXT | INT-CTXT | +| Encrypt-and-MAC | insecure | insecure | insecure | secure | insecure | +| MAC-then-encrypt | secure | insecure | insecure | secure | insecure | +| Encrypt-then-MAC | secure | secure | secure | secure | secure | + +One may assume from this fairly clear result that encrypt-and-MAC and MAC-then-encrypt compositions would be quickly abandoned +in favour of the remaining proven-secure option. But that didn't happen, not in TLSv1.1 (2006) nor in TLSv1.2 (2008). Worse, +both RFCs included incorrect advice on countermeasures for implementers, suggesting that the flaw was "not believed to be large +enough to be exploitable". + +[Lucky 13](http://www.isg.rhul.ac.uk/tls/Lucky13.html) (2013) exploited this flaw and affected all implementations, including +those written [after discovery](https://aws.amazon.com/blogs/security/s2n-and-lucky-13/). OpenSSL even had a +[memory safety vulnerability in the fix for Lucky 13](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2107), which +gives a flavour of the kind of complexity required to remove the side channel. + +rustls does not implement CBC MAC-then-encrypt ciphersuites for these reasons. TLSv1.3 removed support for these +ciphersuites in 2018. + +There are some further rejected options worth mentioning: [RFC7366](https://tools.ietf.org/html/rfc7366) defines +Encrypt-then-MAC for TLS, but unfortunately cannot be negotiated without also supporting MAC-then-encrypt +(clients cannot express "I offer CBC, but only EtM and not MtE"). + +## RSA PKCS#1 encryption + +"RSA key exchange" in TLS involves the client choosing a large random value and encrypting it using the server's +public key. This has two overall problems: + +1. It provides no _forward secrecy_: later compromise of the server's private key breaks confidentiality of + *all* past sessions using that key. This is a crucial property in the presence of software that is often + [poor at keeping a secret](http://heartbleed.com/). +2. The padding used in practice in TLS ("PKCS#1", or fully "RSAES-PKCS1-v1_5") has been known to be broken since + [1998](http://archiv.infsec.ethz.ch/education/fs08/secsem/bleichenbacher98.pdf). + +In a similar pattern to the MAC-then-encrypt problem discussed above, TLSv1.0 (1999), TLSv1.1 (2006) and TLSv1.2 (2008) +continued to specify use of PKCS#1 encryption, again with incrementally more complex and incorrect advice on countermeasures. + +[ROBOT](https://robotattack.org/) (2018) showed that implementations were still vulnerable to these attacks twenty years later. +[The Marvin Attack](https://people.redhat.com/~hkario/marvin/) (2023) demonstrated the same a further five years later. + +rustls does not support RSA key exchange. TLSv1.3 also removed support. + +## BEAST + +[BEAST](https://vnhacker.blogspot.com/2011/09/beast.html) ([CVE-2011-3389](https://nvd.nist.gov/vuln/detail/CVE-2011-3389)) +was demonstrated in 2011 by Thai Duong and Juliano Rizzo, +and was another vulnerability in CBC-based ciphersuites in SSLv3.0 and TLSv1.0. CBC mode is vulnerable to adaptive +chosen-plaintext attacks if the IV is predictable. In the case of these protocol versions, the IV was the previous +block of ciphertext (as if the entire TLS session was one CBC ciphertext, albeit revealed incrementally). This was +obviously predictable, since it was published on the wire. + +OpenSSL contained a countermeasure for this problem from 2002 onwards: it encrypts an empty message before each real +one, so that the IV used in the real message is unpredictable. This was turned off by default due to bugs in IE6. + +TLSv1.1 fix this vulnerability, but not any of the other deficiencies of CBC mode (see above). + +rustls does not support these ciphersuites. + +## CRIME + +In 2002 [John Kelsey](https://www.iacr.org/cryptodb/archive/2002/FSE/3091/3091.pdf) discussed the length side channel +as applied to compression of combined secret and attacker-chosen strings. + +Compression continued to be an option in TLSv1.1 (2006) and in TLSv1.2 (2008). Support in libraries was widespread. + +[CRIME](https://en.wikipedia.org/wiki/CRIME) ([CVE-2012-4929](https://nvd.nist.gov/vuln/detail/CVE-2012-4929)) +was demonstrated in 2012, again by Thai Duong and Juliano Rizzo. It attacked several protocols offering transparent +compression of application data, allowing quick adaptive chosen-plaintext attacks against secret values like cookies. + +rustls does not implement compression. TLSv1.3 also removed support. + +## Logjam / FREAK + +Way back when SSL was first being born, circa 1995, the US government considered cryptography a munition requiring +export control. SSL contained specific ciphersuites with dramatically small key sizes that were not subject +to export control. These controls were dropped in 2000. + +Since the "export-grade" ciphersuites no longer fulfilled any purpose, and because they were actively harmful to users, +one may have expected software support to disappear quickly. This did not happen. + +In 2015 [the FREAK attack](https://mitls.org/pages/attacks/SMACK#freak) ([CVE-2015-0204](https://nvd.nist.gov/vuln/detail/CVE-2015-0204)) +and [the Logjam attack](https://weakdh.org/) ([CVE-2015-4000](https://nvd.nist.gov/vuln/detail/CVE-2015-4000)) both +demonstrated total breaks of security in the presence of servers that accepted export ciphersuites. FREAK factored +512-bit RSA keys, while Logjam optimised solving discrete logs in the 512-bit group used by many different servers. + +Naturally, rustls does not implement any of these ciphersuites. + +## SWEET32 + +Block ciphers are vulnerable to birthday attacks, where the probability of repeating a block increases dramatically +once a particular key has been used for many blocks. For block ciphers with 64-bit blocks, this becomes probable +once a given key encrypts the order of 32GB of data. + +[Sweet32](https://sweet32.info/) ([CVE-2016-2183](https://nvd.nist.gov/vuln/detail/CVE-2016-2183)) attacked this fact +in the context of TLS support for 3DES, breaking confidentiality by analysing a large amount of attacker-induced traffic +in one session. + +rustls does not support any 64-bit block ciphers. + +## DROWN + +[DROWN](https://drownattack.com/) ([CVE-2016-0800](https://nvd.nist.gov/vuln/detail/CVE-2016-0800)) is a cross-protocol +attack that breaks the security of TLSv1.2 and earlier (when used with RSA key exchange) by using SSLv2. It is required +that the server uses the same key for both protocol versions. + +rustls naturally does not support SSLv2, but most importantly does not support RSA key exchange for TLSv1.2. + +## Poodle + +[POODLE](https://cdn1.vox-cdn.com/uploads/chorus_asset/file/2354994/ssl-poodle.0.pdf) ([CVE-2014-3566](https://nvd.nist.gov/vuln/detail/CVE-2014-3566)) +is an attack against CBC mode ciphersuites in SSLv3. This was possible in most cases because some clients willingly +downgraded to SSLv3 after failed handshakes for later versions. + +rustls does not support CBC mode ciphersuites, or SSLv3. Note that rustls does not need to implement `TLS_FALLBACK_SCSV` +introduced as a countermeasure because it contains no ability to downgrade from TLS 1.2 to earlier protocol versions, +and TLS 1.3 has protocol-level downgrade protection based on the [ServerHello server random value](https://www.rfc-editor.org/rfc/rfc8446#section-4.1.3). + +## GCM nonces + +[RFC5288](https://tools.ietf.org/html/rfc5288) introduced GCM-based ciphersuites for use in TLS. Unfortunately +the design was poor; it reused design for an unrelated security setting proposed in RFC5116. + +GCM is a typical nonce-based AEAD: it requires a unique (but not necessarily unpredictable) 96-bit nonce for each encryption +with a given key. The design specified by RFC5288 left two-thirds of the nonce construction up to implementations: + +- wasting 8 bytes per TLS ciphertext, +- meaning correct operation cannot be tested for (e.g., in protocol-level test vectors). + +There were no trade-offs here: TLS has a 64-bit sequence number that is not allowed to wrap and would make an ideal nonce. + +As a result, a [2016 study](https://eprint.iacr.org/2016/475.pdf) found: + +- implementations from IBM, A10 and Citrix used randomly-chosen nonces, which are unlikely to be unique over long connections, +- an implementation from Radware used the same nonce for the first two messages. + +rustls uses a counter from a random starting point for GCM nonces. TLSv1.3 and the Chacha20-Poly1305 TLSv1.2 ciphersuite +standardise this method. + +## Renegotiation + +In 2009 Marsh Ray and Steve Dispensa [discovered](https://kryptera.se/Renegotiating%20TLS.pdf) that the renegotiation +feature of all versions of TLS allows a MitM to splice a request of their choice onto the front of the client's real HTTP +request. A countermeasure was proposed and widely implemented to bind renegotiations to their previous negotiations; +unfortunately this was insufficient. + +rustls does not support renegotiation in TLSv1.2. TLSv1.3 also no longer supports renegotiation. + +## 3SHAKE + +[3SHAKE](https://www.mitls.org/pages/attacks/3SHAKE) (2014) described a complex attack that broke the "Secure Renegotiation" extension +introduced as a countermeasure to the previous protocol flaw. + +rustls does not support renegotiation for TLSv1.2 connections, or RSA key exchange, and both are required for this attack +to work. rustls implements the "Extended Master Secret" (RFC7627) extension for TLSv1.2 which was standardised as a countermeasure. + +TLSv1.3 no longer supports renegotiation and RSA key exchange. It also effectively incorporates the improvements made in RFC7627. + +## KCI + +[This vulnerability](https://kcitls.org/) makes use of TLS ciphersuites (those offering static DH) which were standardised +yet not widely used. However, they were implemented by libraries, and as a result enabled for various clients. It coupled +this with misconfigured certificates (on services including facebook.com) which allowed their misuse to MitM connections. + +rustls does not support static DH/EC-DH ciphersuites. We assert that it is misissuance to sign an EC certificate +with the keyUsage extension allowing both signatures and key exchange. That it isn't is probably a failure +of CAB Forum baseline requirements. +*/ diff --git a/vendor/rustls/src/msgs/alert.rs b/vendor/rustls/src/msgs/alert.rs new file mode 100644 index 00000000..e66f64cf --- /dev/null +++ b/vendor/rustls/src/msgs/alert.rs @@ -0,0 +1,26 @@ +use alloc::vec::Vec; + +use crate::enums::AlertDescription; +use crate::error::InvalidMessage; +use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::enums::AlertLevel; + +#[derive(Debug)] +pub struct AlertMessagePayload { + pub level: AlertLevel, + pub description: AlertDescription, +} + +impl Codec<'_> for AlertMessagePayload { + fn encode(&self, bytes: &mut Vec<u8>) { + self.level.encode(bytes); + self.description.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let level = AlertLevel::read(r)?; + let description = AlertDescription::read(r)?; + r.expect_empty("AlertMessagePayload") + .map(|_| Self { level, description }) + } +} diff --git a/vendor/rustls/src/msgs/base.rs b/vendor/rustls/src/msgs/base.rs new file mode 100644 index 00000000..4204714d --- /dev/null +++ b/vendor/rustls/src/msgs/base.rs @@ -0,0 +1,238 @@ +use alloc::vec::Vec; +use core::fmt; +use core::marker::PhantomData; + +use pki_types::CertificateDer; +use zeroize::Zeroize; + +use crate::error::InvalidMessage; +use crate::msgs::codec; +use crate::msgs::codec::{Codec, Reader}; + +/// An externally length'd payload +#[derive(Clone, Eq, PartialEq)] +pub enum Payload<'a> { + Borrowed(&'a [u8]), + Owned(Vec<u8>), +} + +impl<'a> Codec<'a> for Payload<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + bytes.extend_from_slice(self.bytes()); + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + Ok(Self::read(r)) + } +} + +impl<'a> Payload<'a> { + pub fn bytes(&self) -> &[u8] { + match self { + Self::Borrowed(bytes) => bytes, + Self::Owned(bytes) => bytes, + } + } + + pub fn into_owned(self) -> Payload<'static> { + Payload::Owned(self.into_vec()) + } + + pub fn into_vec(self) -> Vec<u8> { + match self { + Self::Borrowed(bytes) => bytes.to_vec(), + Self::Owned(bytes) => bytes, + } + } + + pub fn read(r: &mut Reader<'a>) -> Self { + Self::Borrowed(r.rest()) + } +} + +impl Payload<'static> { + pub fn new(bytes: impl Into<Vec<u8>>) -> Self { + Self::Owned(bytes.into()) + } + + pub fn empty() -> Self { + Self::Borrowed(&[]) + } +} + +impl<'a> Codec<'a> for CertificateDer<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + codec::u24(self.as_ref().len() as u32).encode(bytes); + bytes.extend(self.as_ref()); + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let len = codec::u24::read(r)?.0 as usize; + let mut sub = r.sub(len)?; + let body = sub.rest(); + Ok(Self::from(body)) + } +} + +impl fmt::Debug for Payload<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + hex(f, self.bytes()) + } +} + +/// An arbitrary, unknown-content, u24-length-prefixed payload +#[derive(Clone, Eq, PartialEq)] +pub(crate) struct PayloadU24<'a>(pub(crate) Payload<'a>); + +impl PayloadU24<'_> { + pub(crate) fn into_owned(self) -> PayloadU24<'static> { + PayloadU24(self.0.into_owned()) + } +} + +impl<'a> Codec<'a> for PayloadU24<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + let inner = self.0.bytes(); + codec::u24(inner.len() as u32).encode(bytes); + bytes.extend_from_slice(inner); + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let len = codec::u24::read(r)?.0 as usize; + let mut sub = r.sub(len)?; + Ok(Self(Payload::read(&mut sub))) + } +} + +impl fmt::Debug for PayloadU24<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +/// An arbitrary, unknown-content, u16-length-prefixed payload +/// +/// The `C` type parameter controls whether decoded values may +/// be empty. +#[derive(Clone, Eq, PartialEq)] +pub struct PayloadU16<C: Cardinality = MaybeEmpty>(pub(crate) Vec<u8>, PhantomData<C>); + +impl<C: Cardinality> PayloadU16<C> { + pub fn new(bytes: Vec<u8>) -> Self { + debug_assert!(bytes.len() >= C::MIN); + Self(bytes, PhantomData) + } +} + +impl PayloadU16<MaybeEmpty> { + pub(crate) fn empty() -> Self { + Self::new(Vec::new()) + } +} + +impl<C: Cardinality> Codec<'_> for PayloadU16<C> { + fn encode(&self, bytes: &mut Vec<u8>) { + debug_assert!(self.0.len() >= C::MIN); + (self.0.len() as u16).encode(bytes); + bytes.extend_from_slice(&self.0); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let len = u16::read(r)? as usize; + if len < C::MIN { + return Err(InvalidMessage::IllegalEmptyValue); + } + let mut sub = r.sub(len)?; + let body = sub.rest().to_vec(); + Ok(Self(body, PhantomData)) + } +} + +impl<C: Cardinality> fmt::Debug for PayloadU16<C> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + hex(f, &self.0) + } +} + +/// An arbitrary, unknown-content, u8-length-prefixed payload +/// +/// `C` controls the minimum length accepted when decoding. +#[derive(Clone, Eq, PartialEq)] +pub(crate) struct PayloadU8<C: Cardinality = MaybeEmpty>(pub(crate) Vec<u8>, PhantomData<C>); + +impl<C: Cardinality> PayloadU8<C> { + pub(crate) fn encode_slice(slice: &[u8], bytes: &mut Vec<u8>) { + (slice.len() as u8).encode(bytes); + bytes.extend_from_slice(slice); + } + + pub(crate) fn new(bytes: Vec<u8>) -> Self { + debug_assert!(bytes.len() >= C::MIN); + Self(bytes, PhantomData) + } +} + +impl PayloadU8<MaybeEmpty> { + pub(crate) fn empty() -> Self { + Self(Vec::new(), PhantomData) + } +} + +impl<C: Cardinality> Codec<'_> for PayloadU8<C> { + fn encode(&self, bytes: &mut Vec<u8>) { + debug_assert!(self.0.len() >= C::MIN); + (self.0.len() as u8).encode(bytes); + bytes.extend_from_slice(&self.0); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let len = u8::read(r)? as usize; + if len < C::MIN { + return Err(InvalidMessage::IllegalEmptyValue); + } + let mut sub = r.sub(len)?; + let body = sub.rest().to_vec(); + Ok(Self(body, PhantomData)) + } +} + +impl<C: Cardinality> Zeroize for PayloadU8<C> { + fn zeroize(&mut self) { + self.0.zeroize(); + } +} + +impl<C: Cardinality> fmt::Debug for PayloadU8<C> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + hex(f, &self.0) + } +} + +pub trait Cardinality: Clone + Eq + PartialEq { + const MIN: usize; +} + +#[derive(Clone, Eq, PartialEq)] +pub struct MaybeEmpty; + +impl Cardinality for MaybeEmpty { + const MIN: usize = 0; +} + +#[derive(Clone, Eq, PartialEq)] +pub struct NonEmpty; + +impl Cardinality for NonEmpty { + const MIN: usize = 1; +} + +// Format an iterator of u8 into a hex string +pub(super) fn hex<'a>( + f: &mut fmt::Formatter<'_>, + payload: impl IntoIterator<Item = &'a u8>, +) -> fmt::Result { + for b in payload { + write!(f, "{b:02x}")?; + } + Ok(()) +} diff --git a/vendor/rustls/src/msgs/ccs.rs b/vendor/rustls/src/msgs/ccs.rs new file mode 100644 index 00000000..ac2c9e6a --- /dev/null +++ b/vendor/rustls/src/msgs/ccs.rs @@ -0,0 +1,23 @@ +use alloc::vec::Vec; + +use crate::error::InvalidMessage; +use crate::msgs::codec::{Codec, Reader}; + +#[derive(Debug)] +pub struct ChangeCipherSpecPayload; + +impl Codec<'_> for ChangeCipherSpecPayload { + fn encode(&self, bytes: &mut Vec<u8>) { + 1u8.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let typ = u8::read(r)?; + if typ != 1 { + return Err(InvalidMessage::InvalidCcs); + } + + r.expect_empty("ChangeCipherSpecPayload") + .map(|_| Self {}) + } +} diff --git a/vendor/rustls/src/msgs/codec.rs b/vendor/rustls/src/msgs/codec.rs new file mode 100644 index 00000000..bd62cd3a --- /dev/null +++ b/vendor/rustls/src/msgs/codec.rs @@ -0,0 +1,404 @@ +use alloc::vec::Vec; +use core::fmt::Debug; +use core::marker::PhantomData; + +use crate::error::InvalidMessage; + +/// Wrapper over a slice of bytes that allows reading chunks from +/// with the current position state held using a cursor. +/// +/// A new reader for a sub section of the buffer can be created +/// using the `sub` function or a section of a certain length can +/// be obtained using the `take` function +pub struct Reader<'a> { + /// The underlying buffer storing the readers content + buffer: &'a [u8], + /// Stores the current reading position for the buffer + cursor: usize, +} + +impl<'a> Reader<'a> { + /// Creates a new Reader of the provided `bytes` slice with + /// the initial cursor position of zero. + pub fn init(bytes: &'a [u8]) -> Self { + Reader { + buffer: bytes, + cursor: 0, + } + } + + /// Attempts to create a new Reader on a sub section of this + /// readers bytes by taking a slice of the provided `length` + /// will return None if there is not enough bytes + pub fn sub(&mut self, length: usize) -> Result<Self, InvalidMessage> { + match self.take(length) { + Some(bytes) => Ok(Reader::init(bytes)), + None => Err(InvalidMessage::MessageTooShort), + } + } + + /// Borrows a slice of all the remaining bytes + /// that appear after the cursor position. + /// + /// Moves the cursor to the end of the buffer length. + pub fn rest(&mut self) -> &'a [u8] { + let rest = &self.buffer[self.cursor..]; + self.cursor = self.buffer.len(); + rest + } + + /// Attempts to borrow a slice of bytes from the current + /// cursor position of `length` if there is not enough + /// bytes remaining after the cursor to take the length + /// then None is returned instead. + pub fn take(&mut self, length: usize) -> Option<&'a [u8]> { + if self.left() < length { + return None; + } + let current = self.cursor; + self.cursor += length; + Some(&self.buffer[current..current + length]) + } + + /// Used to check whether the reader has any content left + /// after the cursor (cursor has not reached end of buffer) + pub fn any_left(&self) -> bool { + self.cursor < self.buffer.len() + } + + pub fn expect_empty(&self, name: &'static str) -> Result<(), InvalidMessage> { + match self.any_left() { + true => Err(InvalidMessage::TrailingData(name)), + false => Ok(()), + } + } + + /// Returns the cursor position which is also the number + /// of bytes that have been read from the buffer. + pub fn used(&self) -> usize { + self.cursor + } + + /// Returns the number of bytes that are still able to be + /// read (The number of remaining takes) + pub fn left(&self) -> usize { + self.buffer.len() - self.cursor + } +} + +/// Trait for implementing encoding and decoding functionality +/// on something. +pub trait Codec<'a>: Debug + Sized { + /// Function for encoding itself by appending itself to + /// the provided vec of bytes. + fn encode(&self, bytes: &mut Vec<u8>); + + /// Function for decoding itself from the provided reader + /// will return Some if the decoding was successful or + /// None if it was not. + fn read(_: &mut Reader<'a>) -> Result<Self, InvalidMessage>; + + /// Convenience function for encoding the implementation + /// into a vec and returning it + fn get_encoding(&self) -> Vec<u8> { + let mut bytes = Vec::new(); + self.encode(&mut bytes); + bytes + } + + /// Function for wrapping a call to the read function in + /// a Reader for the slice of bytes provided + /// + /// Returns `Err(InvalidMessage::ExcessData(_))` if + /// `Self::read` does not read the entirety of `bytes`. + fn read_bytes(bytes: &'a [u8]) -> Result<Self, InvalidMessage> { + let mut reader = Reader::init(bytes); + Self::read(&mut reader).and_then(|r| { + reader.expect_empty("read_bytes")?; + Ok(r) + }) + } +} + +impl Codec<'_> for u8 { + fn encode(&self, bytes: &mut Vec<u8>) { + bytes.push(*self); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + match r.take(1) { + Some(&[byte]) => Ok(byte), + _ => Err(InvalidMessage::MissingData("u8")), + } + } +} + +pub(crate) fn put_u16(v: u16, out: &mut [u8]) { + let out: &mut [u8; 2] = (&mut out[..2]).try_into().unwrap(); + *out = u16::to_be_bytes(v); +} + +impl Codec<'_> for u16 { + fn encode(&self, bytes: &mut Vec<u8>) { + let mut b16 = [0u8; 2]; + put_u16(*self, &mut b16); + bytes.extend_from_slice(&b16); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + match r.take(2) { + Some(&[b1, b2]) => Ok(Self::from_be_bytes([b1, b2])), + _ => Err(InvalidMessage::MissingData("u16")), + } + } +} + +// Make a distinct type for u24, even though it's a u32 underneath +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone)] +pub struct u24(pub u32); + +#[cfg(any(target_pointer_width = "32", target_pointer_width = "64"))] +impl From<u24> for usize { + #[inline] + fn from(v: u24) -> Self { + v.0 as Self + } +} + +impl Codec<'_> for u24 { + fn encode(&self, bytes: &mut Vec<u8>) { + let be_bytes = u32::to_be_bytes(self.0); + bytes.extend_from_slice(&be_bytes[1..]); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + match r.take(3) { + Some(&[a, b, c]) => Ok(Self(u32::from_be_bytes([0, a, b, c]))), + _ => Err(InvalidMessage::MissingData("u24")), + } + } +} + +impl Codec<'_> for u32 { + fn encode(&self, bytes: &mut Vec<u8>) { + bytes.extend(Self::to_be_bytes(*self)); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + match r.take(4) { + Some(&[a, b, c, d]) => Ok(Self::from_be_bytes([a, b, c, d])), + _ => Err(InvalidMessage::MissingData("u32")), + } + } +} + +pub(crate) fn put_u64(v: u64, bytes: &mut [u8]) { + let bytes: &mut [u8; 8] = (&mut bytes[..8]).try_into().unwrap(); + *bytes = u64::to_be_bytes(v); +} + +impl Codec<'_> for u64 { + fn encode(&self, bytes: &mut Vec<u8>) { + let mut b64 = [0u8; 8]; + put_u64(*self, &mut b64); + bytes.extend_from_slice(&b64); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + match r.take(8) { + Some(&[a, b, c, d, e, f, g, h]) => Ok(Self::from_be_bytes([a, b, c, d, e, f, g, h])), + _ => Err(InvalidMessage::MissingData("u64")), + } + } +} + +/// Implement `Codec` for lists of elements that implement `TlsListElement`. +/// +/// `TlsListElement` provides the size of the length prefix for the list. +impl<'a, T: Codec<'a> + TlsListElement + Debug> Codec<'a> for Vec<T> { + fn encode(&self, bytes: &mut Vec<u8>) { + let nest = LengthPrefixedBuffer::new(T::SIZE_LEN, bytes); + + for i in self { + i.encode(nest.buf); + } + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let mut ret = Self::new(); + for item in TlsListIter::<T>::new(r)? { + ret.push(item?); + } + + Ok(ret) + } +} + +/// An iterator over a vector of `TlsListElements`. +/// +/// All uses _MUST_ exhaust the iterator, as errors may be delayed +/// until the last element. +pub(crate) struct TlsListIter<'a, T: Codec<'a> + TlsListElement + Debug> { + sub: Reader<'a>, + _t: PhantomData<T>, +} + +impl<'a, T: Codec<'a> + TlsListElement + Debug> TlsListIter<'a, T> { + pub(crate) fn new(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let len = T::SIZE_LEN.read(r)?; + let sub = r.sub(len)?; + Ok(Self { + sub, + _t: PhantomData, + }) + } +} + +impl<'a, T: Codec<'a> + TlsListElement + Debug> Iterator for TlsListIter<'a, T> { + type Item = Result<T, InvalidMessage>; + + fn next(&mut self) -> Option<Self::Item> { + match self.sub.any_left() { + true => Some(T::read(&mut self.sub)), + false => None, + } + } +} + +impl Codec<'_> for () { + fn encode(&self, _: &mut Vec<u8>) {} + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + r.expect_empty("Empty") + } +} + +/// A trait for types that can be encoded and decoded in a list. +/// +/// This trait is used to implement `Codec` for `Vec<T>`. Lists in the TLS wire format are +/// prefixed with a length, the size of which depends on the type of the list elements. +/// As such, the `Codec` implementation for `Vec<T>` requires an implementation of this trait +/// for its element type `T`. +pub(crate) trait TlsListElement { + const SIZE_LEN: ListLength; +} + +/// The length of the length prefix for a list. +/// +/// The types that appear in lists are limited to three kinds of length prefixes: +/// 1, 2, and 3 bytes. For the latter kind, we require a `TlsListElement` implementer +/// to specify a maximum length and error if the actual length is larger. +pub(crate) enum ListLength { + /// U8 but non-empty + NonZeroU8 { empty_error: InvalidMessage }, + + /// U16, perhaps empty + U16, + + /// U16 but non-empty + NonZeroU16 { empty_error: InvalidMessage }, + + /// U24 with imposed upper bound + U24 { max: usize, error: InvalidMessage }, +} + +impl ListLength { + pub(crate) fn read(&self, r: &mut Reader<'_>) -> Result<usize, InvalidMessage> { + Ok(match self { + Self::NonZeroU8 { empty_error } => match usize::from(u8::read(r)?) { + 0 => return Err(*empty_error), + len => len, + }, + Self::U16 => usize::from(u16::read(r)?), + Self::NonZeroU16 { empty_error } => match usize::from(u16::read(r)?) { + 0 => return Err(*empty_error), + len => len, + }, + Self::U24 { max, error } => match usize::from(u24::read(r)?) { + len if len > *max => return Err(*error), + len => len, + }, + }) + } +} + +/// Tracks encoding a length-delimited structure in a single pass. +pub(crate) struct LengthPrefixedBuffer<'a> { + pub(crate) buf: &'a mut Vec<u8>, + len_offset: usize, + size_len: ListLength, +} + +impl<'a> LengthPrefixedBuffer<'a> { + /// Inserts a dummy length into `buf`, and remembers where it went. + /// + /// After this, the body of the length-delimited structure should be appended to `LengthPrefixedBuffer::buf`. + /// The length header is corrected in `LengthPrefixedBuffer::drop`. + pub(crate) fn new(size_len: ListLength, buf: &'a mut Vec<u8>) -> Self { + let len_offset = buf.len(); + buf.extend(match size_len { + ListLength::NonZeroU8 { .. } => &[0xff][..], + ListLength::U16 | ListLength::NonZeroU16 { .. } => &[0xff, 0xff], + ListLength::U24 { .. } => &[0xff, 0xff, 0xff], + }); + + Self { + buf, + len_offset, + size_len, + } + } +} + +impl Drop for LengthPrefixedBuffer<'_> { + /// Goes back and corrects the length previously inserted at the start of the structure. + fn drop(&mut self) { + match self.size_len { + ListLength::NonZeroU8 { .. } => { + let len = self.buf.len() - self.len_offset - 1; + debug_assert!(len <= 0xff); + self.buf[self.len_offset] = len as u8; + } + ListLength::U16 | ListLength::NonZeroU16 { .. } => { + let len = self.buf.len() - self.len_offset - 2; + debug_assert!(len <= 0xffff); + let out: &mut [u8; 2] = (&mut self.buf[self.len_offset..self.len_offset + 2]) + .try_into() + .unwrap(); + *out = u16::to_be_bytes(len as u16); + } + ListLength::U24 { .. } => { + let len = self.buf.len() - self.len_offset - 3; + debug_assert!(len <= 0xff_ffff); + let len_bytes = u32::to_be_bytes(len as u32); + let out: &mut [u8; 3] = (&mut self.buf[self.len_offset..self.len_offset + 3]) + .try_into() + .unwrap(); + out.copy_from_slice(&len_bytes[1..]); + } + } + } +} + +#[cfg(test)] +mod tests { + use std::prelude::v1::*; + use std::vec; + + use super::*; + + #[test] + fn interrupted_length_prefixed_buffer_leaves_maximum_length() { + let mut buf = Vec::new(); + let nested = LengthPrefixedBuffer::new(ListLength::U16, &mut buf); + nested.buf.push(0xaa); + assert_eq!(nested.buf, &vec![0xff, 0xff, 0xaa]); + // <- if the buffer is accidentally read here, there is no possibility + // that the contents of the length-prefixed buffer are interpreted + // as a subsequent encoding (perhaps allowing injection of a different + // extension) + drop(nested); + assert_eq!(buf, vec![0x00, 0x01, 0xaa]); + } +} diff --git a/vendor/rustls/src/msgs/deframer/buffers.rs b/vendor/rustls/src/msgs/deframer/buffers.rs new file mode 100644 index 00000000..bf098740 --- /dev/null +++ b/vendor/rustls/src/msgs/deframer/buffers.rs @@ -0,0 +1,296 @@ +use alloc::vec::Vec; +use core::mem; +use core::ops::Range; +#[cfg(feature = "std")] +use std::io; + +#[cfg(feature = "std")] +use crate::msgs::message::MAX_WIRE_SIZE; + +/// Conversion from a slice within a larger buffer into +/// a `Range` offset within. +#[derive(Debug)] +pub(crate) struct Locator { + bounds: Range<*const u8>, +} + +impl Locator { + #[inline] + pub(crate) fn new(slice: &[u8]) -> Self { + Self { + bounds: slice.as_ptr_range(), + } + } + + #[inline] + pub(crate) fn locate(&self, slice: &[u8]) -> Range<usize> { + let bounds = slice.as_ptr_range(); + debug_assert!(self.fully_contains(slice)); + let start = bounds.start as usize - self.bounds.start as usize; + let len = bounds.end as usize - bounds.start as usize; + Range { + start, + end: start + len, + } + } + + #[inline] + pub(crate) fn fully_contains(&self, slice: &[u8]) -> bool { + let bounds = slice.as_ptr_range(); + bounds.start >= self.bounds.start && bounds.end <= self.bounds.end + } +} + +/// Conversion from a `Range` offset to the original slice. +pub(crate) struct Delocator<'b> { + slice: &'b [u8], +} + +impl<'b> Delocator<'b> { + #[inline] + pub(crate) fn new(slice: &'b [u8]) -> Self { + Self { slice } + } + + #[inline] + pub(crate) fn slice_from_range(&'_ self, range: &Range<usize>) -> &'b [u8] { + // safety: this unwrap is safe so long as `range` came from `locate()` + // for the same buffer + self.slice.get(range.clone()).unwrap() + } + + #[inline] + pub(crate) fn locator(self) -> Locator { + Locator::new(self.slice) + } +} + +/// Reordering the underlying buffer based on ranges. +pub(crate) struct Coalescer<'b> { + slice: &'b mut [u8], +} + +impl<'b> Coalescer<'b> { + #[inline] + pub(crate) fn new(slice: &'b mut [u8]) -> Self { + Self { slice } + } + + #[inline] + pub(crate) fn copy_within(&mut self, from: Range<usize>, to: Range<usize>) { + debug_assert!(from.len() == to.len()); + debug_assert!(self.slice.get(from.clone()).is_some()); + debug_assert!(self.slice.get(to.clone()).is_some()); + self.slice.copy_within(from, to.start); + } + + #[inline] + pub(crate) fn delocator(self) -> Delocator<'b> { + Delocator::new(self.slice) + } +} + +/// Accounting structure tracking progress in parsing a single buffer. +#[derive(Clone, Debug)] +pub(crate) struct BufferProgress { + /// Prefix of the buffer that has been processed so far. + /// + /// `processed` may exceed `discard`, that means we have parsed + /// some buffer, but are still using it. This happens due to + /// in-place decryption of incoming records, and in-place + /// reassembly of handshake messages. + /// + /// 0 <= processed <= len + processed: usize, + + /// Prefix of the buffer that can be removed. + /// + /// If `discard` exceeds `processed`, that means we are ignoring + /// data without processing it. + /// + /// 0 <= discard <= len + discard: usize, +} + +impl BufferProgress { + pub(super) fn new(processed: usize) -> Self { + Self { + processed, + discard: 0, + } + } + + #[inline] + pub(crate) fn add_discard(&mut self, discard: usize) { + self.discard += discard; + } + + #[inline] + pub(crate) fn add_processed(&mut self, processed: usize) { + self.processed += processed; + } + + #[inline] + pub(crate) fn take_discard(&mut self) -> usize { + // the caller is about to discard `discard` bytes + // from the front of the buffer. adjust `processed` + // down by the same amount. + self.processed = self + .processed + .saturating_sub(self.discard); + mem::take(&mut self.discard) + } + + #[inline] + pub(crate) fn processed(&self) -> usize { + self.processed + } +} + +#[derive(Default, Debug)] +pub(crate) struct DeframerVecBuffer { + /// Buffer of data read from the socket, in the process of being parsed into messages. + /// + /// For buffer size management, checkout out the [`DeframerVecBuffer::prepare_read()`] method. + buf: Vec<u8>, + + /// What size prefix of `buf` is used. + used: usize, +} + +impl DeframerVecBuffer { + /// Discard `taken` bytes from the start of our buffer. + pub(crate) fn discard(&mut self, taken: usize) { + #[allow(clippy::comparison_chain)] + if taken < self.used { + /* Before: + * +----------+----------+----------+ + * | taken | pending |xxxxxxxxxx| + * +----------+----------+----------+ + * 0 ^ taken ^ self.used + * + * After: + * +----------+----------+----------+ + * | pending |xxxxxxxxxxxxxxxxxxxxx| + * +----------+----------+----------+ + * 0 ^ self.used + */ + + self.buf + .copy_within(taken..self.used, 0); + self.used -= taken; + } else if taken >= self.used { + self.used = 0; + } + } + + pub(crate) fn filled_mut(&mut self) -> &mut [u8] { + &mut self.buf[..self.used] + } + + pub(crate) fn filled(&self) -> &[u8] { + &self.buf[..self.used] + } +} + +#[cfg(feature = "std")] +impl DeframerVecBuffer { + /// Read some bytes from `rd`, and add them to the buffer. + pub(crate) fn read(&mut self, rd: &mut dyn io::Read, in_handshake: bool) -> io::Result<usize> { + if let Err(err) = self.prepare_read(in_handshake) { + return Err(io::Error::new(io::ErrorKind::InvalidData, err)); + } + + // Try to do the largest reads possible. Note that if + // we get a message with a length field out of range here, + // we do a zero length read. That looks like an EOF to + // the next layer up, which is fine. + let new_bytes = rd.read(&mut self.buf[self.used..])?; + self.used += new_bytes; + Ok(new_bytes) + } + + /// Resize the internal `buf` if necessary for reading more bytes. + fn prepare_read(&mut self, is_joining_hs: bool) -> Result<(), &'static str> { + /// TLS allows for handshake messages of up to 16MB. We + /// restrict that to 64KB to limit potential for denial-of- + /// service. + const MAX_HANDSHAKE_SIZE: u32 = 0xffff; + + const READ_SIZE: usize = 4096; + + // We allow a maximum of 64k of buffered data for handshake messages only. Enforce this + // by varying the maximum allowed buffer size here based on whether a prefix of a + // handshake payload is currently being buffered. Given that the first read of such a + // payload will only ever be 4k bytes, the next time we come around here we allow a + // larger buffer size. Once the large message and any following handshake messages in + // the same flight have been consumed, `pop()` will call `discard()` to reset `used`. + // At this point, the buffer resizing logic below should reduce the buffer size. + let allow_max = match is_joining_hs { + true => MAX_HANDSHAKE_SIZE as usize, + false => MAX_WIRE_SIZE, + }; + + if self.used >= allow_max { + return Err("message buffer full"); + } + + // If we can and need to increase the buffer size to allow a 4k read, do so. After + // dealing with a large handshake message (exceeding `OutboundOpaqueMessage::MAX_WIRE_SIZE`), + // make sure to reduce the buffer size again (large messages should be rare). + // Also, reduce the buffer size if there are neither full nor partial messages in it, + // which usually means that the other side suspended sending data. + let need_capacity = Ord::min(allow_max, self.used + READ_SIZE); + if need_capacity > self.buf.len() { + self.buf.resize(need_capacity, 0); + } else if self.used == 0 || self.buf.len() > allow_max { + self.buf.resize(need_capacity, 0); + self.buf.shrink_to(need_capacity); + } + + Ok(()) + } + + /// Append `bytes` to the end of this buffer. + /// + /// Return a `Range` saying where it went. + pub(crate) fn extend(&mut self, bytes: &[u8]) -> Range<usize> { + let len = bytes.len(); + let start = self.used; + let end = start + len; + if self.buf.len() < end { + self.buf.resize(end, 0); + } + self.buf[start..end].copy_from_slice(bytes); + self.used += len; + Range { start, end } + } +} + +/// A borrowed version of [`DeframerVecBuffer`] that tracks discard operations +#[derive(Debug)] +pub(crate) struct DeframerSliceBuffer<'a> { + // a fully initialized buffer that will be deframed + buf: &'a mut [u8], + // number of bytes to discard from the front of `buf` at a later time + discard: usize, +} + +impl<'a> DeframerSliceBuffer<'a> { + pub(crate) fn new(buf: &'a mut [u8]) -> Self { + Self { buf, discard: 0 } + } + + /// Tracks a pending discard operation of `num_bytes` + pub(crate) fn queue_discard(&mut self, num_bytes: usize) { + self.discard += num_bytes; + } + + pub(crate) fn pending_discard(&self) -> usize { + self.discard + } + + pub(crate) fn filled_mut(&mut self) -> &mut [u8] { + &mut self.buf[self.discard..] + } +} diff --git a/vendor/rustls/src/msgs/deframer/handshake.rs b/vendor/rustls/src/msgs/deframer/handshake.rs new file mode 100644 index 00000000..0d91f0ea --- /dev/null +++ b/vendor/rustls/src/msgs/deframer/handshake.rs @@ -0,0 +1,525 @@ +use alloc::vec::Vec; +use core::mem; +use core::ops::Range; + +use super::buffers::{BufferProgress, Coalescer, Delocator, Locator}; +use crate::error::InvalidMessage; +use crate::msgs::codec::{Codec, u24}; +use crate::msgs::message::InboundPlainMessage; +use crate::{ContentType, ProtocolVersion}; + +#[derive(Debug)] +pub(crate) struct HandshakeDeframer { + /// Spans covering individual handshake payloads, in order of receipt. + spans: Vec<FragmentSpan>, + + /// Discard value, tracking the rightmost extent of the last message + /// in `spans`. + outer_discard: usize, +} + +impl HandshakeDeframer { + /// Accepts a message into the deframer. + /// + /// `containing_buffer` allows mapping the message payload to its position + /// in the input buffer, and thereby avoid retaining a borrow on the input + /// buffer. + /// + /// That is required because our processing of handshake messages requires + /// them to be contiguous (and avoiding that would mean supporting gather-based + /// parsing in a large number of places, including `core`, `webpki`, and the + /// `CryptoProvider` interface). `coalesce()` arranges for that to happen, but + /// to do so it needs to move the fragments together in the original buffer. + /// This would not be possible if the messages were borrowing from that buffer. + /// + /// `outer_discard` is the rightmost extent of the original message. + pub(crate) fn input_message( + &mut self, + msg: InboundPlainMessage<'_>, + containing_buffer: &Locator, + outer_discard: usize, + ) { + debug_assert_eq!(msg.typ, ContentType::Handshake); + debug_assert!(containing_buffer.fully_contains(msg.payload)); + debug_assert!(self.outer_discard <= outer_discard); + + self.outer_discard = outer_discard; + + // if our last span is incomplete, we can blindly add this as a new span -- + // no need to attempt parsing it with `DissectHandshakeIter`. + // + // `coalesce()` will later move this new message to be contiguous with + // `_last_incomplete`, and reparse the result. + // + // we cannot merge these processes, because `coalesce` mutates the underlying + // buffer, and `msg` borrows it. + if let Some(_last_incomplete) = self + .spans + .last() + .filter(|span| !span.is_complete()) + { + self.spans.push(FragmentSpan { + version: msg.version, + size: None, + bounds: containing_buffer.locate(msg.payload), + }); + return; + } + + // otherwise, we can expect `msg` to contain a handshake header introducing + // a new message (and perhaps several of them.) + for span in DissectHandshakeIter::new(msg, containing_buffer) { + self.spans.push(span); + } + } + + /// Returns a `BufferProgress` that skips over unprocessed handshake data. + pub(crate) fn progress(&self) -> BufferProgress { + BufferProgress::new(self.outer_discard) + } + + /// Do we have a message ready? ie, would `iter().next()` return `Some`? + pub(crate) fn has_message_ready(&self) -> bool { + match self.spans.first() { + Some(span) => span.is_complete(), + None => false, + } + } + + /// Do we have any message data, partial or otherwise? + pub(crate) fn is_active(&self) -> bool { + !self.spans.is_empty() + } + + /// We are "aligned" if there is no partial fragment of a handshake + /// message. + pub(crate) fn is_aligned(&self) -> bool { + self.spans + .iter() + .all(|span| span.is_complete()) + } + + /// Iterate over the complete messages. + pub(crate) fn iter<'a, 'b>(&'a mut self, containing_buffer: &'b [u8]) -> HandshakeIter<'a, 'b> { + HandshakeIter { + deframer: self, + containing_buffer: Delocator::new(containing_buffer), + index: 0, + } + } + + /// Coalesce the handshake portions of the given buffer, + /// if needed. + /// + /// This does nothing if there is nothing to do. + /// + /// In a normal TLS stream, handshake messages need not be contiguous. + /// For example, each handshake message could be delivered in its own + /// outer TLS message. This would mean the handshake messages are + /// separated by the outer TLS message headers, and likely also + /// separated by encryption overhead (any explicit nonce in front, + /// any padding and authentication tag afterwards). + /// + /// For a toy example of one handshake message in two fragments, and: + /// + /// - the letter `h` for handshake header octets + /// - the letter `H` for handshake payload octets + /// - the letter `x` for octets in the buffer ignored by this code, + /// + /// the buffer and `spans` data structure could look like: + /// + /// ```text + /// 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 + /// x x x x x h h h h H H H x x x x x H H H H H H x x x + /// '------------' '----------' + /// | | + /// spans = [ { bounds = (5, 12), | + /// size = Some(9), .. }, | + /// { bounds = (17, 23), .. } ] + /// ``` + /// + /// In this case, `requires_coalesce` returns `Some(0)`. Then + /// `coalesce_one` moves the second range leftwards: + /// + /// ```text + /// 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 + /// x x x x x h h h h H H H x x x x x H H H H H H x x x + /// '----------' + /// ^ '----------' + /// | v + /// '--<---<--' + /// copy_within(from = (17, 23), + /// to = (12, 18)) + /// ``` + /// + /// Leaving the buffer and spans: + /// + /// ```text + /// 0 1 2 3 4 5 6 7 8 9 a b c d e f 0 1 2 3 4 5 6 7 8 9 + /// x x x x x h h h h H H H H H H H H H x x x x x x x x + /// '------------------------' + /// | + /// spans = [ { bounds = (5, 18), size = Some(9), .. } ] + /// ``` + pub(crate) fn coalesce(&mut self, containing_buffer: &mut [u8]) -> Result<(), InvalidMessage> { + // Strategy: while there is work to do, scan `spans` + // for a pair where the first is not complete. move + // the second down towards the first, then reparse the contents. + while let Some(i) = self.requires_coalesce() { + self.coalesce_one(i, Coalescer::new(containing_buffer)); + } + + // check resulting spans pass our imposed length limit + match self + .spans + .iter() + .any(|span| span.size.unwrap_or_default() > MAX_HANDSHAKE_SIZE) + { + true => Err(InvalidMessage::HandshakePayloadTooLarge), + false => Ok(()), + } + } + + /// Within `containing_buffer`, move `span[index+1]` to be contiguous + /// with `span[index]`. + fn coalesce_one(&mut self, index: usize, mut containing_buffer: Coalescer<'_>) { + let second = self.spans.remove(index + 1); + let mut first = self.spans.remove(index); + + // move the entirety of `second` to be contiguous with `first` + let len = second.bounds.len(); + let target = Range { + start: first.bounds.end, + end: first.bounds.end + len, + }; + + containing_buffer.copy_within(second.bounds, target); + let delocator = containing_buffer.delocator(); + + // now adjust `first` to cover both + first.bounds.end += len; + + // finally, attempt to re-dissect `first` + let msg = InboundPlainMessage { + typ: ContentType::Handshake, + version: first.version, + payload: delocator.slice_from_range(&first.bounds), + }; + + for (i, span) in DissectHandshakeIter::new(msg, &delocator.locator()).enumerate() { + self.spans.insert(index + i, span); + } + } + + /// We require coalescing if any span except the last is not complete. + /// + /// Returns an index into `spans` for the first non-complete span: + /// this will never be the last item. + fn requires_coalesce(&self) -> Option<usize> { + self.spans + .split_last() + .and_then(|(_last, elements)| { + elements + .iter() + .enumerate() + .find_map(|(i, span)| (!span.is_complete()).then_some(i)) + }) + } +} + +impl Default for HandshakeDeframer { + fn default() -> Self { + Self { + // capacity: a typical upper limit on handshake messages in + // a single flight + spans: Vec::with_capacity(16), + outer_discard: 0, + } + } +} + +struct DissectHandshakeIter<'a, 'b> { + version: ProtocolVersion, + payload: &'b [u8], + containing_buffer: &'a Locator, +} + +impl<'a, 'b> DissectHandshakeIter<'a, 'b> { + fn new(msg: InboundPlainMessage<'b>, containing_buffer: &'a Locator) -> Self { + Self { + version: msg.version, + payload: msg.payload, + containing_buffer, + } + } +} + +impl Iterator for DissectHandshakeIter<'_, '_> { + type Item = FragmentSpan; + + fn next(&mut self) -> Option<Self::Item> { + if self.payload.is_empty() { + return None; + } + + // If there is not enough data to have a header the length is unknown + if self.payload.len() < HANDSHAKE_HEADER_LEN { + let buf = mem::take(&mut self.payload); + let bounds = self.containing_buffer.locate(buf); + return Some(FragmentSpan { + version: self.version, + size: None, + bounds: bounds.clone(), + }); + } + + let (header, rest) = mem::take(&mut self.payload).split_at(HANDSHAKE_HEADER_LEN); + + // safety: header[1..] is exactly 3 bytes, so `u24::read_bytes` cannot fail + let size = u24::read_bytes(&header[1..]) + .unwrap() + .into(); + + let available = if size < rest.len() { + self.payload = &rest[size..]; + size + } else { + rest.len() + }; + + let mut bounds = self.containing_buffer.locate(header); + bounds.end += available; + Some(FragmentSpan { + version: self.version, + size: Some(size), + bounds: bounds.clone(), + }) + } +} + +pub(crate) struct HandshakeIter<'a, 'b> { + deframer: &'a mut HandshakeDeframer, + containing_buffer: Delocator<'b>, + index: usize, +} + +impl<'b> Iterator for HandshakeIter<'_, 'b> { + type Item = (InboundPlainMessage<'b>, usize); + + fn next(&mut self) -> Option<Self::Item> { + let next_span = self.deframer.spans.get(self.index)?; + + if !next_span.is_complete() { + return None; + } + + // if this is the last handshake message, then we'll end + // up with an empty `spans` and can discard the remainder + // of the input buffer. + let discard = if self.deframer.spans.len() - 1 == self.index { + mem::take(&mut self.deframer.outer_discard) + } else { + 0 + }; + + self.index += 1; + Some(( + InboundPlainMessage { + typ: ContentType::Handshake, + version: next_span.version, + payload: self + .containing_buffer + .slice_from_range(&next_span.bounds), + }, + discard, + )) + } +} + +impl Drop for HandshakeIter<'_, '_> { + fn drop(&mut self) { + self.deframer.spans.drain(..self.index); + } +} + +#[derive(Debug)] +struct FragmentSpan { + /// version taken from containing message. + version: ProtocolVersion, + + /// size of the handshake message body (excluding header) + /// + /// `None` means the size is unknown, because `bounds` is not + /// large enough to encompass a whole header. + size: Option<usize>, + + /// bounds of the handshake message, including header + bounds: Range<usize>, +} + +impl FragmentSpan { + /// A `FragmentSpan` is "complete" if its size is known, and its + /// bounds exactly encompasses one handshake message. + fn is_complete(&self) -> bool { + match self.size { + Some(sz) => sz + HANDSHAKE_HEADER_LEN == self.bounds.len(), + None => false, + } + } +} + +const HANDSHAKE_HEADER_LEN: usize = 1 + 3; + +/// TLS allows for handshake messages of up to 16MB. We +/// restrict that to 64KB to limit potential for denial-of- +/// service. +const MAX_HANDSHAKE_SIZE: usize = 0xffff; + +#[cfg(test)] +mod tests { + use std::vec; + + use super::*; + use crate::msgs::deframer::DeframerIter; + + fn add_bytes(hs: &mut HandshakeDeframer, slice: &[u8], within: &[u8]) { + let msg = InboundPlainMessage { + typ: ContentType::Handshake, + version: ProtocolVersion::TLSv1_3, + payload: slice, + }; + let locator = Locator::new(within); + let discard = locator.locate(slice).end; + hs.input_message(msg, &locator, discard); + } + + #[test] + fn coalesce() { + let mut input = vec![0, 0, 0, 0x21, 0, 0, 0, 0, 0x01, 0xff, 0x00, 0x01]; + let mut hs = HandshakeDeframer::default(); + + add_bytes(&mut hs, &input[3..4], &input); + assert_eq!(hs.requires_coalesce(), None); + add_bytes(&mut hs, &input[4..6], &input); + assert_eq!(hs.requires_coalesce(), Some(0)); + add_bytes(&mut hs, &input[8..10], &input); + assert_eq!(hs.requires_coalesce(), Some(0)); + + std::println!("before: {hs:?}"); + hs.coalesce(&mut input).unwrap(); + std::println!("after: {hs:?}"); + + let (msg, discard) = hs.iter(&input).next().unwrap(); + std::println!("msg {msg:?} discard {discard:?}"); + assert_eq!(msg.typ, ContentType::Handshake); + assert_eq!(msg.version, ProtocolVersion::TLSv1_3); + assert_eq!(msg.payload, &[0x21, 0x00, 0x00, 0x01, 0xff]); + + input.drain(..discard); + assert_eq!(input, &[0, 1]); + } + + #[test] + fn append() { + let mut input = vec![0, 0, 0, 0x21, 0, 0, 5, 0, 0, 1, 2, 3, 4, 5, 0]; + let mut hs = HandshakeDeframer::default(); + + add_bytes(&mut hs, &input[3..7], &input); + add_bytes(&mut hs, &input[9..14], &input); + assert_eq!(hs.spans.len(), 2); + + hs.coalesce(&mut input).unwrap(); + assert_eq!(hs.spans.len(), 1); + + let (msg, discard) = std::dbg!(hs.iter(&input).next().unwrap()); + assert_eq!(msg.typ, ContentType::Handshake); + assert_eq!(msg.version, ProtocolVersion::TLSv1_3); + assert_eq!(msg.payload, &[0x21, 0x00, 0x00, 0x05, 1, 2, 3, 4, 5]); + + input.drain(..discard); + assert_eq!(input, &[0]); + } + + #[test] + fn coalesce_rejects_excess_size_message() { + const X: u8 = 0xff; + let mut input = vec![0x21, 0x01, 0x00, X, 0x00, 0xab, X]; + let mut hs = HandshakeDeframer::default(); + + // split header over multiple messages, which motivates doing + // this check in `coalesce()` + add_bytes(&mut hs, &input[0..3], &input); + add_bytes(&mut hs, &input[4..6], &input); + + assert_eq!( + hs.coalesce(&mut input), + Err(InvalidMessage::HandshakePayloadTooLarge) + ); + } + + #[test] + fn iter_only_returns_full_messages() { + let input = [0, 0, 0, 0x21, 0, 0, 1, 0xab, 0x21, 0, 0, 1]; + + let mut hs = HandshakeDeframer::default(); + + add_bytes(&mut hs, &input[3..8], &input); + add_bytes(&mut hs, &input[8..12], &input); + + let mut iter = hs.iter(&input); + let (msg, discard) = iter.next().unwrap(); + assert!(iter.next().is_none()); + + assert_eq!(msg.typ, ContentType::Handshake); + assert_eq!(msg.version, ProtocolVersion::TLSv1_3); + assert_eq!(msg.payload, &[0x21, 0x00, 0x00, 0x01, 0xab]); + assert_eq!(discard, 0); + } + + #[test] + fn handshake_flight() { + // intended to be a realistic example + let mut input = include_bytes!("../../testdata/handshake-test.1.bin").to_vec(); + let locator = Locator::new(&input); + + let mut hs = HandshakeDeframer::default(); + + let mut iter = DeframerIter::new(&mut input[..]); + + while let Some(message) = iter.next() { + let plain = message.unwrap().into_plain_message(); + std::println!("message {plain:?}"); + + hs.input_message(plain, &locator, iter.bytes_consumed()); + } + + hs.coalesce(&mut input[..]).unwrap(); + + let mut iter = hs.iter(&input[..]); + for _ in 0..4 { + let (msg, discard) = iter.next().unwrap(); + assert!(matches!( + msg, + InboundPlainMessage { + typ: ContentType::Handshake, + .. + } + )); + assert_eq!(discard, 0); + } + + let (msg, discard) = iter.next().unwrap(); + assert!(matches!( + msg, + InboundPlainMessage { + typ: ContentType::Handshake, + .. + } + )); + assert_eq!(discard, 4280); + drop(iter); + + input.drain(0..discard); + assert!(input.is_empty()); + } +} diff --git a/vendor/rustls/src/msgs/deframer/mod.rs b/vendor/rustls/src/msgs/deframer/mod.rs new file mode 100644 index 00000000..4122b525 --- /dev/null +++ b/vendor/rustls/src/msgs/deframer/mod.rs @@ -0,0 +1,238 @@ +use core::mem; + +use crate::error::{Error, InvalidMessage}; +use crate::msgs::codec::Reader; +use crate::msgs::message::{ + HEADER_SIZE, InboundOpaqueMessage, MessageError, read_opaque_message_header, +}; + +pub(crate) mod buffers; +pub(crate) mod handshake; + +/// A deframer of TLS wire messages. +/// +/// Returns `Some(Ok(_))` containing each `InboundOpaqueMessage` deframed +/// from the buffer. +/// +/// Returns `None` if no further messages can be deframed from the +/// buffer. More data is required for further progress. +/// +/// Returns `Some(Err(_))` if the peer is not talking TLS, but some +/// other protocol. The caller should abort the connection, because +/// the deframer cannot recover. +/// +/// Call `bytes_consumed()` to learn how many bytes the iterator has +/// processed from the front of the original buffer. This is only updated +/// when a message is successfully deframed (ie. `Some(Ok(_))` is returned). +pub(crate) struct DeframerIter<'a> { + buf: &'a mut [u8], + consumed: usize, +} + +impl<'a> DeframerIter<'a> { + /// Make a new `DeframerIter` + pub(crate) fn new(buf: &'a mut [u8]) -> Self { + Self { buf, consumed: 0 } + } + + /// How many bytes were processed successfully from the front + /// of the buffer passed to `new()`? + pub(crate) fn bytes_consumed(&self) -> usize { + self.consumed + } +} + +impl<'a> Iterator for DeframerIter<'a> { + type Item = Result<InboundOpaqueMessage<'a>, Error>; + + fn next(&mut self) -> Option<Self::Item> { + let mut reader = Reader::init(self.buf); + + let (typ, version, len) = match read_opaque_message_header(&mut reader) { + Ok(header) => header, + Err(err) => { + let err = match err { + MessageError::TooShortForHeader | MessageError::TooShortForLength => { + return None; + } + MessageError::InvalidEmptyPayload => InvalidMessage::InvalidEmptyPayload, + MessageError::MessageTooLarge => InvalidMessage::MessageTooLarge, + MessageError::InvalidContentType => InvalidMessage::InvalidContentType, + MessageError::UnknownProtocolVersion => InvalidMessage::UnknownProtocolVersion, + }; + return Some(Err(err.into())); + } + }; + + let end = HEADER_SIZE + len as usize; + + self.buf.get(HEADER_SIZE..end)?; + + // we now have a TLS header and body on the front of `self.buf`. remove + // it from the front. + let (consumed, remainder) = mem::take(&mut self.buf).split_at_mut(end); + self.buf = remainder; + self.consumed += end; + + Some(Ok(InboundOpaqueMessage::new( + typ, + version, + &mut consumed[HEADER_SIZE..], + ))) + } +} + +pub fn fuzz_deframer(data: &[u8]) { + let mut buf = data.to_vec(); + let mut iter = DeframerIter::new(&mut buf); + + for message in iter.by_ref() { + if message.is_err() { + break; + } + } + + assert!(iter.bytes_consumed() <= buf.len()); +} + +#[cfg(feature = "std")] +#[cfg(test)] +mod tests { + use alloc::vec::Vec; + use std::prelude::v1::*; + + use super::*; + use crate::ContentType; + + #[test] + fn iterator_empty_before_header_received() { + assert!( + DeframerIter::new(&mut []) + .next() + .is_none() + ); + assert!( + DeframerIter::new(&mut [0x16]) + .next() + .is_none() + ); + assert!( + DeframerIter::new(&mut [0x16, 0x03]) + .next() + .is_none() + ); + assert!( + DeframerIter::new(&mut [0x16, 0x03, 0x03]) + .next() + .is_none() + ); + assert!( + DeframerIter::new(&mut [0x16, 0x03, 0x03, 0x00]) + .next() + .is_none() + ); + assert!( + DeframerIter::new(&mut [0x16, 0x03, 0x03, 0x00, 0x01]) + .next() + .is_none() + ); + } + + #[test] + fn iterate_one_message() { + let mut buffer = [0x17, 0x03, 0x03, 0x00, 0x01, 0x00]; + let mut iter = DeframerIter::new(&mut buffer); + assert_eq!( + iter.next().unwrap().unwrap().typ, + ContentType::ApplicationData + ); + assert_eq!(iter.bytes_consumed(), 6); + assert!(iter.next().is_none()); + } + + #[test] + fn iterate_two_messages() { + let mut buffer = [ + 0x16, 0x03, 0x03, 0x00, 0x01, 0x00, 0x17, 0x03, 0x03, 0x00, 0x01, 0x00, + ]; + let mut iter = DeframerIter::new(&mut buffer); + assert_eq!(iter.next().unwrap().unwrap().typ, ContentType::Handshake); + assert_eq!(iter.bytes_consumed(), 6); + assert_eq!( + iter.next().unwrap().unwrap().typ, + ContentType::ApplicationData + ); + assert_eq!(iter.bytes_consumed(), 12); + assert!(iter.next().is_none()); + } + + #[test] + fn iterator_invalid_protocol_version_rejected() { + let mut buffer = include_bytes!("../../testdata/deframer-invalid-version.bin").to_vec(); + let mut iter = DeframerIter::new(&mut buffer); + assert_eq!( + iter.next().unwrap().err(), + Some(Error::InvalidMessage( + InvalidMessage::UnknownProtocolVersion + )) + ); + } + + #[test] + fn iterator_invalid_content_type_rejected() { + let mut buffer = include_bytes!("../../testdata/deframer-invalid-contenttype.bin").to_vec(); + let mut iter = DeframerIter::new(&mut buffer); + assert_eq!( + iter.next().unwrap().err(), + Some(Error::InvalidMessage(InvalidMessage::InvalidContentType)) + ); + } + + #[test] + fn iterator_excess_message_length_rejected() { + let mut buffer = include_bytes!("../../testdata/deframer-invalid-length.bin").to_vec(); + let mut iter = DeframerIter::new(&mut buffer); + assert_eq!( + iter.next().unwrap().err(), + Some(Error::InvalidMessage(InvalidMessage::MessageTooLarge)) + ); + } + + #[test] + fn iterator_zero_message_length_rejected() { + let mut buffer = include_bytes!("../../testdata/deframer-invalid-empty.bin").to_vec(); + let mut iter = DeframerIter::new(&mut buffer); + assert_eq!( + iter.next().unwrap().err(), + Some(Error::InvalidMessage(InvalidMessage::InvalidEmptyPayload)) + ); + } + + #[test] + fn iterator_over_many_messages() { + let client_hello = include_bytes!("../../testdata/deframer-test.1.bin"); + let mut buffer = Vec::with_capacity(3 * client_hello.len()); + buffer.extend(client_hello); + buffer.extend(client_hello); + buffer.extend(client_hello); + let mut iter = DeframerIter::new(&mut buffer); + let mut count = 0; + + for message in iter.by_ref() { + let message = message.unwrap(); + assert_eq!(ContentType::Handshake, message.typ); + count += 1; + } + + assert_eq!(count, 3); + assert_eq!(client_hello.len() * 3, iter.bytes_consumed()); + } + + #[test] + fn exercise_fuzz_deframer() { + fuzz_deframer(&[0xff, 0xff, 0xff, 0xff, 0xff]); + for prefix in 0..7 { + fuzz_deframer(&[0x16, 0x03, 0x03, 0x00, 0x01, 0xff][..prefix]); + } + } +} diff --git a/vendor/rustls/src/msgs/enums.rs b/vendor/rustls/src/msgs/enums.rs new file mode 100644 index 00000000..6eaa2b32 --- /dev/null +++ b/vendor/rustls/src/msgs/enums.rs @@ -0,0 +1,510 @@ +#![allow(clippy::upper_case_acronyms)] +#![allow(non_camel_case_types)] +use crate::crypto::{KeyExchangeAlgorithm, hash}; +use crate::msgs::codec::{Codec, Reader}; + +enum_builder! { + /// The `HashAlgorithm` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum HashAlgorithm { + NONE => 0x00, + MD5 => 0x01, + SHA1 => 0x02, + SHA224 => 0x03, + SHA256 => 0x04, + SHA384 => 0x05, + SHA512 => 0x06, + } +} + +impl HashAlgorithm { + /// Returns the hash of the empty input. + /// + /// This returns `None` for some hash algorithms, so the caller + /// should be prepared to do the computation themselves in this case. + pub(crate) fn hash_for_empty_input(&self) -> Option<hash::Output> { + match self { + Self::SHA256 => Some(hash::Output::new( + b"\xe3\xb0\xc4\x42\x98\xfc\x1c\x14\ + \x9a\xfb\xf4\xc8\x99\x6f\xb9\x24\ + \x27\xae\x41\xe4\x64\x9b\x93\x4c\ + \xa4\x95\x99\x1b\x78\x52\xb8\x55", + )), + Self::SHA384 => Some(hash::Output::new( + b"\x38\xb0\x60\xa7\x51\xac\x96\x38\ + \x4c\xd9\x32\x7e\xb1\xb1\xe3\x6a\ + \x21\xfd\xb7\x11\x14\xbe\x07\x43\ + \x4c\x0c\xc7\xbf\x63\xf6\xe1\xda\ + \x27\x4e\xde\xbf\xe7\x6f\x65\xfb\ + \xd5\x1a\xd2\xf1\x48\x98\xb9\x5b", + )), + _ => None, + } + } +} + +enum_builder! { + /// The `ClientCertificateType` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub(crate) enum ClientCertificateType { + RSASign => 0x01, + DSSSign => 0x02, + RSAFixedDH => 0x03, + DSSFixedDH => 0x04, + RSAEphemeralDH => 0x05, + DSSEphemeralDH => 0x06, + FortezzaDMS => 0x14, + ECDSASign => 0x40, + RSAFixedECDH => 0x41, + ECDSAFixedECDH => 0x42, + } +} + +enum_builder! { + /// The `Compression` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum Compression { + Null => 0x00, + Deflate => 0x01, + LSZ => 0x40, + } +} + +enum_builder! { + /// The `AlertLevel` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum AlertLevel { + Warning => 0x01, + Fatal => 0x02, + } +} + +enum_builder! { + /// The `HeartbeatMessageType` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub(crate) enum HeartbeatMessageType { + Request => 0x01, + Response => 0x02, + } +} + +enum_builder! { + /// The `ExtensionType` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u16)] + pub enum ExtensionType { + ServerName => 0x0000, + MaxFragmentLength => 0x0001, + ClientCertificateUrl => 0x0002, + TrustedCAKeys => 0x0003, + TruncatedHMAC => 0x0004, + StatusRequest => 0x0005, + UserMapping => 0x0006, + ClientAuthz => 0x0007, + ServerAuthz => 0x0008, + CertificateType => 0x0009, + EllipticCurves => 0x000a, + ECPointFormats => 0x000b, + SRP => 0x000c, + SignatureAlgorithms => 0x000d, + UseSRTP => 0x000e, + Heartbeat => 0x000f, + ALProtocolNegotiation => 0x0010, + SCT => 0x0012, + ClientCertificateType => 0x0013, + ServerCertificateType => 0x0014, + Padding => 0x0015, + ExtendedMasterSecret => 0x0017, + CompressCertificate => 0x001b, + SessionTicket => 0x0023, + PreSharedKey => 0x0029, + EarlyData => 0x002a, + SupportedVersions => 0x002b, + Cookie => 0x002c, + PSKKeyExchangeModes => 0x002d, + TicketEarlyDataInfo => 0x002e, + CertificateAuthorities => 0x002f, + OIDFilters => 0x0030, + PostHandshakeAuth => 0x0031, + SignatureAlgorithmsCert => 0x0032, + KeyShare => 0x0033, + TransportParameters => 0x0039, + NextProtocolNegotiation => 0x3374, + ChannelId => 0x754f, + RenegotiationInfo => 0xff01, + TransportParametersDraft => 0xffa5, + EncryptedClientHello => 0xfe0d, // https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#section-11.1 + EncryptedClientHelloOuterExtensions => 0xfd00, // https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#section-5.1 + } +} + +impl ExtensionType { + /// Returns true if the extension type can be compressed in an "inner" client hello for ECH. + /// + /// This function should only return true for extension types where the inner hello and outer + /// hello extensions values will always be identical. Extensions that may be identical + /// sometimes (e.g. server name, cert compression methods), but not always, SHOULD NOT be + /// compressed. + /// + /// See [draft-ietf-esni-18 §5](https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#section-5) + /// and [draft-ietf-esni-18 §10.5](https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#section-10.5) + /// for more information. + pub(crate) fn ech_compress(&self) -> bool { + // We match which extensions we will compress with BoringSSL and Go's stdlib. + matches!( + self, + Self::StatusRequest + | Self::EllipticCurves + | Self::SignatureAlgorithms + | Self::SignatureAlgorithmsCert + | Self::ALProtocolNegotiation + | Self::SupportedVersions + | Self::Cookie + | Self::KeyShare + | Self::PSKKeyExchangeModes + ) + } +} + +enum_builder! { + /// The `ServerNameType` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub(crate) enum ServerNameType { + HostName => 0x00, + } +} + +enum_builder! { + /// The `NamedCurve` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + /// + /// This enum is used for recognizing elliptic curve parameters advertised + /// by a peer during a TLS handshake. It is **not** a list of curves that + /// Rustls supports. See [`crate::crypto::ring::kx_group`] for the list of supported + /// elliptic curve groups. + #[repr(u16)] + pub(crate) enum NamedCurve { + sect163k1 => 0x0001, + sect163r1 => 0x0002, + sect163r2 => 0x0003, + sect193r1 => 0x0004, + sect193r2 => 0x0005, + sect233k1 => 0x0006, + sect233r1 => 0x0007, + sect239k1 => 0x0008, + sect283k1 => 0x0009, + sect283r1 => 0x000a, + sect409k1 => 0x000b, + sect409r1 => 0x000c, + sect571k1 => 0x000d, + sect571r1 => 0x000e, + secp160k1 => 0x000f, + secp160r1 => 0x0010, + secp160r2 => 0x0011, + secp192k1 => 0x0012, + secp192r1 => 0x0013, + secp224k1 => 0x0014, + secp224r1 => 0x0015, + secp256k1 => 0x0016, + secp256r1 => 0x0017, + secp384r1 => 0x0018, + secp521r1 => 0x0019, + brainpoolp256r1 => 0x001a, + brainpoolp384r1 => 0x001b, + brainpoolp512r1 => 0x001c, + X25519 => 0x001d, + X448 => 0x001e, + arbitrary_explicit_prime_curves => 0xff01, + arbitrary_explicit_char2_curves => 0xff02, + } +} + +enum_builder! { + /// The `NamedGroup` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u16)] + pub enum NamedGroup { + secp256r1 => 0x0017, + secp384r1 => 0x0018, + secp521r1 => 0x0019, + X25519 => 0x001d, + X448 => 0x001e, + FFDHE2048 => 0x0100, + FFDHE3072 => 0x0101, + FFDHE4096 => 0x0102, + FFDHE6144 => 0x0103, + FFDHE8192 => 0x0104, + MLKEM512 => 0x0200, + MLKEM768 => 0x0201, + MLKEM1024 => 0x0202, + secp256r1MLKEM768 => 0x11eb, + X25519MLKEM768 => 0x11ec, + } +} + +impl NamedGroup { + /// Return the key exchange algorithm associated with this `NamedGroup` + pub fn key_exchange_algorithm(self) -> KeyExchangeAlgorithm { + match u16::from(self) { + x if (0x100..0x200).contains(&x) => KeyExchangeAlgorithm::DHE, + _ => KeyExchangeAlgorithm::ECDHE, + } + } +} + +enum_builder! { + /// The `ECPointFormat` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum ECPointFormat { + Uncompressed => 0x00, + ANSIX962CompressedPrime => 0x01, + ANSIX962CompressedChar2 => 0x02, + } +} + +enum_builder! { + /// The `HeartbeatMode` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub(crate) enum HeartbeatMode { + PeerAllowedToSend => 0x01, + PeerNotAllowedToSend => 0x02, + } +} + +enum_builder! { + /// The `ECCurveType` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub(crate) enum ECCurveType { + ExplicitPrime => 0x01, + ExplicitChar2 => 0x02, + NamedCurve => 0x03, + } +} + +enum_builder! { + /// The `PskKeyExchangeMode` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum PskKeyExchangeMode { + PSK_KE => 0x00, + PSK_DHE_KE => 0x01, + } +} + +enum_builder! { + /// The `KeyUpdateRequest` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum KeyUpdateRequest { + UpdateNotRequested => 0x00, + UpdateRequested => 0x01, + } +} + +enum_builder! { + /// The `CertificateStatusType` TLS protocol enum. Values in this enum are taken + /// from the various RFCs covering TLS, and are listed by IANA. + /// The `Unknown` item is used when processing unrecognised ordinals. + #[repr(u8)] + pub enum CertificateStatusType { + OCSP => 0x01, + } +} + +enum_builder! { + /// The Key Encapsulation Mechanism (`Kem`) type for HPKE operations. + /// Listed by IANA, as specified in [RFC 9180 Section 7.1] + /// + /// [RFC 9180 Section 7.1]: <https://datatracker.ietf.org/doc/html/rfc9180#kemid-values> + #[repr(u16)] + pub enum HpkeKem { + DHKEM_P256_HKDF_SHA256 => 0x0010, + DHKEM_P384_HKDF_SHA384 => 0x0011, + DHKEM_P521_HKDF_SHA512 => 0x0012, + DHKEM_X25519_HKDF_SHA256 => 0x0020, + DHKEM_X448_HKDF_SHA512 => 0x0021, + } +} + +enum_builder! { + /// The Key Derivation Function (`Kdf`) type for HPKE operations. + /// Listed by IANA, as specified in [RFC 9180 Section 7.2] + /// + /// [RFC 9180 Section 7.2]: <https://datatracker.ietf.org/doc/html/rfc9180#name-key-derivation-functions-kd> + #[repr(u16)] + pub enum HpkeKdf { + HKDF_SHA256 => 0x0001, + HKDF_SHA384 => 0x0002, + HKDF_SHA512 => 0x0003, + } +} + +impl Default for HpkeKdf { + // TODO(XXX): revisit the default configuration. This is just what Cloudflare ships right now. + fn default() -> Self { + Self::HKDF_SHA256 + } +} + +enum_builder! { + /// The Authenticated Encryption with Associated Data (`Aead`) type for HPKE operations. + /// Listed by IANA, as specified in [RFC 9180 Section 7.3] + /// + /// [RFC 9180 Section 7.3]: <https://datatracker.ietf.org/doc/html/rfc9180#name-authenticated-encryption-wi> + #[repr(u16)] + pub enum HpkeAead { + AES_128_GCM => 0x0001, + AES_256_GCM => 0x0002, + CHACHA20_POLY_1305 => 0x0003, + EXPORT_ONLY => 0xFFFF, + } +} + +impl HpkeAead { + /// Returns the length of the tag for the AEAD algorithm, or none if the AEAD is EXPORT_ONLY. + pub(crate) fn tag_len(&self) -> Option<usize> { + match self { + // See RFC 9180 Section 7.3, column `Nt`, the length in bytes of the authentication tag + // for the algorithm. + // https://www.rfc-editor.org/rfc/rfc9180.html#section-7.3 + Self::AES_128_GCM | Self::AES_256_GCM | Self::CHACHA20_POLY_1305 => Some(16), + _ => None, + } + } +} + +impl Default for HpkeAead { + // TODO(XXX): revisit the default configuration. This is just what Cloudflare ships right now. + fn default() -> Self { + Self::AES_128_GCM + } +} + +enum_builder! { + /// The Encrypted Client Hello protocol version (`EchVersion`). + /// + /// Specified in [draft-ietf-tls-esni Section 4]. + /// TODO(XXX): Update reference once RFC is published. + /// + /// [draft-ietf-tls-esni Section 4]: <https://www.ietf.org/archive/id/draft-ietf-tls-esni-17.html#section-4> + #[repr(u16)] + pub enum EchVersion { + V18 => 0xfe0d, + } +} + +#[cfg(test)] +pub(crate) mod tests { + // These tests are intended to provide coverage and + // check panic-safety of relatively unused values. + + use std::prelude::v1::*; + + use super::*; + + #[test] + fn test_enums() { + test_enum8::<HashAlgorithm>(HashAlgorithm::NONE, HashAlgorithm::SHA512); + test_enum8::<ClientCertificateType>( + ClientCertificateType::RSASign, + ClientCertificateType::ECDSAFixedECDH, + ); + test_enum8::<Compression>(Compression::Null, Compression::LSZ); + test_enum8::<AlertLevel>(AlertLevel::Warning, AlertLevel::Fatal); + test_enum8::<HeartbeatMessageType>( + HeartbeatMessageType::Request, + HeartbeatMessageType::Response, + ); + test_enum16::<ExtensionType>(ExtensionType::ServerName, ExtensionType::RenegotiationInfo); + test_enum8::<ServerNameType>(ServerNameType::HostName, ServerNameType::HostName); + test_enum16::<NamedCurve>( + NamedCurve::sect163k1, + NamedCurve::arbitrary_explicit_char2_curves, + ); + test_enum16::<NamedGroup>(NamedGroup::secp256r1, NamedGroup::FFDHE8192); + test_enum8::<ECPointFormat>( + ECPointFormat::Uncompressed, + ECPointFormat::ANSIX962CompressedChar2, + ); + test_enum8::<HeartbeatMode>( + HeartbeatMode::PeerAllowedToSend, + HeartbeatMode::PeerNotAllowedToSend, + ); + test_enum8::<ECCurveType>(ECCurveType::ExplicitPrime, ECCurveType::NamedCurve); + test_enum8::<PskKeyExchangeMode>( + PskKeyExchangeMode::PSK_KE, + PskKeyExchangeMode::PSK_DHE_KE, + ); + test_enum8::<KeyUpdateRequest>( + KeyUpdateRequest::UpdateNotRequested, + KeyUpdateRequest::UpdateRequested, + ); + test_enum8::<CertificateStatusType>( + CertificateStatusType::OCSP, + CertificateStatusType::OCSP, + ); + } + + pub(crate) fn test_enum8<T: for<'a> Codec<'a>>(first: T, last: T) { + let first_v = get8(&first); + let last_v = get8(&last); + + for val in first_v..last_v + 1 { + let mut buf = Vec::new(); + val.encode(&mut buf); + assert_eq!(buf.len(), 1); + + let t = T::read_bytes(&buf).unwrap(); + assert_eq!(val, get8(&t)); + } + } + + pub(crate) fn test_enum16<T: for<'a> Codec<'a>>(first: T, last: T) { + let first_v = get16(&first); + let last_v = get16(&last); + + for val in first_v..last_v + 1 { + let mut buf = Vec::new(); + val.encode(&mut buf); + assert_eq!(buf.len(), 2); + + let t = T::read_bytes(&buf).unwrap(); + assert_eq!(val, get16(&t)); + } + } + + fn get8<T: for<'a> Codec<'a>>(enum_value: &T) -> u8 { + let enc = enum_value.get_encoding(); + assert_eq!(enc.len(), 1); + enc[0] + } + + fn get16<T: for<'a> Codec<'a>>(enum_value: &T) -> u16 { + let enc = enum_value.get_encoding(); + assert_eq!(enc.len(), 2); + (enc[0] as u16 >> 8) | (enc[1] as u16) + } +} diff --git a/vendor/rustls/src/msgs/ffdhe_groups.rs b/vendor/rustls/src/msgs/ffdhe_groups.rs new file mode 100644 index 00000000..ac105b59 --- /dev/null +++ b/vendor/rustls/src/msgs/ffdhe_groups.rs @@ -0,0 +1,323 @@ +//! This module contains parameters for FFDHE named groups as defined +//! in [RFC 7919 Appendix A](https://datatracker.ietf.org/doc/html/rfc7919#appendix-A). + +use crate::NamedGroup; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// Parameters of an FFDHE group, with Big-endian byte order +pub struct FfdheGroup<'a> { + pub p: &'a [u8], + pub g: &'a [u8], +} + +impl FfdheGroup<'static> { + /// Return the `FfdheGroup` corresponding to the provided `NamedGroup` + /// if it is indeed an FFDHE group + #[deprecated( + since = "0.23.13", + note = "This function is linker-unfriendly. Use `SupportedKxGroup::ffdhe_group()` instead" + )] + pub fn from_named_group(named_group: NamedGroup) -> Option<Self> { + match named_group { + NamedGroup::FFDHE2048 => Some(FFDHE2048), + NamedGroup::FFDHE3072 => Some(FFDHE3072), + NamedGroup::FFDHE4096 => Some(FFDHE4096), + NamedGroup::FFDHE6144 => Some(FFDHE6144), + NamedGroup::FFDHE8192 => Some(FFDHE8192), + _ => None, + } + } +} + +impl<'a> FfdheGroup<'a> { + /// Return the `NamedGroup` for the `FfdheGroup` if it represents one. + #[deprecated( + since = "0.23.13", + note = "This function is linker-unfriendly. Use `SupportedKxGroup::name()` instead" + )] + pub fn named_group(&self) -> Option<NamedGroup> { + match *self { + FFDHE2048 => Some(NamedGroup::FFDHE2048), + FFDHE3072 => Some(NamedGroup::FFDHE3072), + FFDHE4096 => Some(NamedGroup::FFDHE4096), + FFDHE6144 => Some(NamedGroup::FFDHE6144), + FFDHE8192 => Some(NamedGroup::FFDHE8192), + _ => None, + } + } + + /// Construct an `FfdheGroup` from the given `p` and `g`, trimming any potential leading zeros. + pub fn from_params_trimming_leading_zeros(p: &'a [u8], g: &'a [u8]) -> Self { + fn trim_leading_zeros(buf: &[u8]) -> &[u8] { + for start in 0..buf.len() { + if buf[start] != 0 { + return &buf[start..]; + } + } + &[] + } + + FfdheGroup { + p: trim_leading_zeros(p), + g: trim_leading_zeros(g), + } + } +} + +/// FFDHE2048 group defined in [RFC 7919 Appendix A.1] +/// +/// [RFC 7919 Appendix A.1]: https://datatracker.ietf.org/doc/html/rfc7919#appendix-A.1 +pub const FFDHE2048: FfdheGroup<'static> = FfdheGroup { + p: &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, + 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, + 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, + 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, + 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, + 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, + 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, + 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, + 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, + 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, + 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, + 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, + 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, + 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, + 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, + 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x28, 0x5c, 0x97, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, + ], + g: &[2], +}; + +/// FFDHE3072 group defined in [RFC 7919 Appendix A.2] +/// +/// [RFC 7919 Appendix A.2]: https://datatracker.ietf.org/doc/html/rfc7919#appendix-A.2 +pub const FFDHE3072: FfdheGroup<'static> = FfdheGroup { + p: &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, + 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, + 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, + 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, + 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, + 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, + 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, + 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, + 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, + 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, + 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, + 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, + 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, + 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, + 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, + 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, + 0x5b, 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, + 0xc9, 0x07, 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, + 0x86, 0xd9, 0x1c, 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, + 0xbc, 0x43, 0x79, 0x44, 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, + 0x1e, 0x71, 0xf5, 0x4b, 0xff, 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, + 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, + 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, + 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0xc6, 0x2e, + 0x37, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + ], + g: &[2], +}; + +/// FFDHE4096 group defined in [RFC 7919 Appendix A.3] +/// +/// [RFC 7919 Appendix A.3]: https://datatracker.ietf.org/doc/html/rfc7919#appendix-A.3 +pub const FFDHE4096: FfdheGroup<'static> = FfdheGroup { + p: &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, + 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, + 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, + 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, + 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, + 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, + 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, + 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, + 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, + 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, + 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, + 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, + 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, + 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, + 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, + 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, + 0x5b, 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, + 0xc9, 0x07, 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, + 0x86, 0xd9, 0x1c, 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, + 0xbc, 0x43, 0x79, 0x44, 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, + 0x1e, 0x71, 0xf5, 0x4b, 0xff, 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, + 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, + 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, + 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0x9e, 0x1e, + 0xf1, 0x6e, 0x6f, 0x52, 0xc3, 0x16, 0x4d, 0xf4, 0xfb, 0x79, 0x30, 0xe9, 0xe4, 0xe5, 0x88, + 0x57, 0xb6, 0xac, 0x7d, 0x5f, 0x42, 0xd6, 0x9f, 0x6d, 0x18, 0x77, 0x63, 0xcf, 0x1d, 0x55, + 0x03, 0x40, 0x04, 0x87, 0xf5, 0x5b, 0xa5, 0x7e, 0x31, 0xcc, 0x7a, 0x71, 0x35, 0xc8, 0x86, + 0xef, 0xb4, 0x31, 0x8a, 0xed, 0x6a, 0x1e, 0x01, 0x2d, 0x9e, 0x68, 0x32, 0xa9, 0x07, 0x60, + 0x0a, 0x91, 0x81, 0x30, 0xc4, 0x6d, 0xc7, 0x78, 0xf9, 0x71, 0xad, 0x00, 0x38, 0x09, 0x29, + 0x99, 0xa3, 0x33, 0xcb, 0x8b, 0x7a, 0x1a, 0x1d, 0xb9, 0x3d, 0x71, 0x40, 0x00, 0x3c, 0x2a, + 0x4e, 0xce, 0xa9, 0xf9, 0x8d, 0x0a, 0xcc, 0x0a, 0x82, 0x91, 0xcd, 0xce, 0xc9, 0x7d, 0xcf, + 0x8e, 0xc9, 0xb5, 0x5a, 0x7f, 0x88, 0xa4, 0x6b, 0x4d, 0xb5, 0xa8, 0x51, 0xf4, 0x41, 0x82, + 0xe1, 0xc6, 0x8a, 0x00, 0x7e, 0x5e, 0x65, 0x5f, 0x6a, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, + ], + g: &[2], +}; + +/// FFDHE6144 group defined in [RFC 7919 Appendix A.4] +/// +/// [RFC 7919 Appendix A.4]: https://datatracker.ietf.org/doc/html/rfc7919#appendix-A.4 +pub const FFDHE6144: FfdheGroup<'static> = FfdheGroup { + p: &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, + 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, + 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, + 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, + 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, + 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, + 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, + 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, + 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, + 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, + 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, + 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, + 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, + 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, + 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, + 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, + 0x5b, 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, + 0xc9, 0x07, 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, + 0x86, 0xd9, 0x1c, 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, + 0xbc, 0x43, 0x79, 0x44, 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, + 0x1e, 0x71, 0xf5, 0x4b, 0xff, 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, + 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, + 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, + 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0x9e, 0x1e, + 0xf1, 0x6e, 0x6f, 0x52, 0xc3, 0x16, 0x4d, 0xf4, 0xfb, 0x79, 0x30, 0xe9, 0xe4, 0xe5, 0x88, + 0x57, 0xb6, 0xac, 0x7d, 0x5f, 0x42, 0xd6, 0x9f, 0x6d, 0x18, 0x77, 0x63, 0xcf, 0x1d, 0x55, + 0x03, 0x40, 0x04, 0x87, 0xf5, 0x5b, 0xa5, 0x7e, 0x31, 0xcc, 0x7a, 0x71, 0x35, 0xc8, 0x86, + 0xef, 0xb4, 0x31, 0x8a, 0xed, 0x6a, 0x1e, 0x01, 0x2d, 0x9e, 0x68, 0x32, 0xa9, 0x07, 0x60, + 0x0a, 0x91, 0x81, 0x30, 0xc4, 0x6d, 0xc7, 0x78, 0xf9, 0x71, 0xad, 0x00, 0x38, 0x09, 0x29, + 0x99, 0xa3, 0x33, 0xcb, 0x8b, 0x7a, 0x1a, 0x1d, 0xb9, 0x3d, 0x71, 0x40, 0x00, 0x3c, 0x2a, + 0x4e, 0xce, 0xa9, 0xf9, 0x8d, 0x0a, 0xcc, 0x0a, 0x82, 0x91, 0xcd, 0xce, 0xc9, 0x7d, 0xcf, + 0x8e, 0xc9, 0xb5, 0x5a, 0x7f, 0x88, 0xa4, 0x6b, 0x4d, 0xb5, 0xa8, 0x51, 0xf4, 0x41, 0x82, + 0xe1, 0xc6, 0x8a, 0x00, 0x7e, 0x5e, 0x0d, 0xd9, 0x02, 0x0b, 0xfd, 0x64, 0xb6, 0x45, 0x03, + 0x6c, 0x7a, 0x4e, 0x67, 0x7d, 0x2c, 0x38, 0x53, 0x2a, 0x3a, 0x23, 0xba, 0x44, 0x42, 0xca, + 0xf5, 0x3e, 0xa6, 0x3b, 0xb4, 0x54, 0x32, 0x9b, 0x76, 0x24, 0xc8, 0x91, 0x7b, 0xdd, 0x64, + 0xb1, 0xc0, 0xfd, 0x4c, 0xb3, 0x8e, 0x8c, 0x33, 0x4c, 0x70, 0x1c, 0x3a, 0xcd, 0xad, 0x06, + 0x57, 0xfc, 0xcf, 0xec, 0x71, 0x9b, 0x1f, 0x5c, 0x3e, 0x4e, 0x46, 0x04, 0x1f, 0x38, 0x81, + 0x47, 0xfb, 0x4c, 0xfd, 0xb4, 0x77, 0xa5, 0x24, 0x71, 0xf7, 0xa9, 0xa9, 0x69, 0x10, 0xb8, + 0x55, 0x32, 0x2e, 0xdb, 0x63, 0x40, 0xd8, 0xa0, 0x0e, 0xf0, 0x92, 0x35, 0x05, 0x11, 0xe3, + 0x0a, 0xbe, 0xc1, 0xff, 0xf9, 0xe3, 0xa2, 0x6e, 0x7f, 0xb2, 0x9f, 0x8c, 0x18, 0x30, 0x23, + 0xc3, 0x58, 0x7e, 0x38, 0xda, 0x00, 0x77, 0xd9, 0xb4, 0x76, 0x3e, 0x4e, 0x4b, 0x94, 0xb2, + 0xbb, 0xc1, 0x94, 0xc6, 0x65, 0x1e, 0x77, 0xca, 0xf9, 0x92, 0xee, 0xaa, 0xc0, 0x23, 0x2a, + 0x28, 0x1b, 0xf6, 0xb3, 0xa7, 0x39, 0xc1, 0x22, 0x61, 0x16, 0x82, 0x0a, 0xe8, 0xdb, 0x58, + 0x47, 0xa6, 0x7c, 0xbe, 0xf9, 0xc9, 0x09, 0x1b, 0x46, 0x2d, 0x53, 0x8c, 0xd7, 0x2b, 0x03, + 0x74, 0x6a, 0xe7, 0x7f, 0x5e, 0x62, 0x29, 0x2c, 0x31, 0x15, 0x62, 0xa8, 0x46, 0x50, 0x5d, + 0xc8, 0x2d, 0xb8, 0x54, 0x33, 0x8a, 0xe4, 0x9f, 0x52, 0x35, 0xc9, 0x5b, 0x91, 0x17, 0x8c, + 0xcf, 0x2d, 0xd5, 0xca, 0xce, 0xf4, 0x03, 0xec, 0x9d, 0x18, 0x10, 0xc6, 0x27, 0x2b, 0x04, + 0x5b, 0x3b, 0x71, 0xf9, 0xdc, 0x6b, 0x80, 0xd6, 0x3f, 0xdd, 0x4a, 0x8e, 0x9a, 0xdb, 0x1e, + 0x69, 0x62, 0xa6, 0x95, 0x26, 0xd4, 0x31, 0x61, 0xc1, 0xa4, 0x1d, 0x57, 0x0d, 0x79, 0x38, + 0xda, 0xd4, 0xa4, 0x0e, 0x32, 0x9c, 0xd0, 0xe4, 0x0e, 0x65, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, + ], + g: &[2], +}; + +/// FFDHE8192 group defined in [RFC 7919 Appendix A.5] +/// +/// [RFC 7919 Appendix A.5]: https://datatracker.ietf.org/doc/html/rfc7919#appendix-A.5 +pub const FFDHE8192: FfdheGroup<'static> = FfdheGroup { + p: &[ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xad, 0xf8, 0x54, 0x58, 0xa2, 0xbb, 0x4a, + 0x9a, 0xaf, 0xdc, 0x56, 0x20, 0x27, 0x3d, 0x3c, 0xf1, 0xd8, 0xb9, 0xc5, 0x83, 0xce, 0x2d, + 0x36, 0x95, 0xa9, 0xe1, 0x36, 0x41, 0x14, 0x64, 0x33, 0xfb, 0xcc, 0x93, 0x9d, 0xce, 0x24, + 0x9b, 0x3e, 0xf9, 0x7d, 0x2f, 0xe3, 0x63, 0x63, 0x0c, 0x75, 0xd8, 0xf6, 0x81, 0xb2, 0x02, + 0xae, 0xc4, 0x61, 0x7a, 0xd3, 0xdf, 0x1e, 0xd5, 0xd5, 0xfd, 0x65, 0x61, 0x24, 0x33, 0xf5, + 0x1f, 0x5f, 0x06, 0x6e, 0xd0, 0x85, 0x63, 0x65, 0x55, 0x3d, 0xed, 0x1a, 0xf3, 0xb5, 0x57, + 0x13, 0x5e, 0x7f, 0x57, 0xc9, 0x35, 0x98, 0x4f, 0x0c, 0x70, 0xe0, 0xe6, 0x8b, 0x77, 0xe2, + 0xa6, 0x89, 0xda, 0xf3, 0xef, 0xe8, 0x72, 0x1d, 0xf1, 0x58, 0xa1, 0x36, 0xad, 0xe7, 0x35, + 0x30, 0xac, 0xca, 0x4f, 0x48, 0x3a, 0x79, 0x7a, 0xbc, 0x0a, 0xb1, 0x82, 0xb3, 0x24, 0xfb, + 0x61, 0xd1, 0x08, 0xa9, 0x4b, 0xb2, 0xc8, 0xe3, 0xfb, 0xb9, 0x6a, 0xda, 0xb7, 0x60, 0xd7, + 0xf4, 0x68, 0x1d, 0x4f, 0x42, 0xa3, 0xde, 0x39, 0x4d, 0xf4, 0xae, 0x56, 0xed, 0xe7, 0x63, + 0x72, 0xbb, 0x19, 0x0b, 0x07, 0xa7, 0xc8, 0xee, 0x0a, 0x6d, 0x70, 0x9e, 0x02, 0xfc, 0xe1, + 0xcd, 0xf7, 0xe2, 0xec, 0xc0, 0x34, 0x04, 0xcd, 0x28, 0x34, 0x2f, 0x61, 0x91, 0x72, 0xfe, + 0x9c, 0xe9, 0x85, 0x83, 0xff, 0x8e, 0x4f, 0x12, 0x32, 0xee, 0xf2, 0x81, 0x83, 0xc3, 0xfe, + 0x3b, 0x1b, 0x4c, 0x6f, 0xad, 0x73, 0x3b, 0xb5, 0xfc, 0xbc, 0x2e, 0xc2, 0x20, 0x05, 0xc5, + 0x8e, 0xf1, 0x83, 0x7d, 0x16, 0x83, 0xb2, 0xc6, 0xf3, 0x4a, 0x26, 0xc1, 0xb2, 0xef, 0xfa, + 0x88, 0x6b, 0x42, 0x38, 0x61, 0x1f, 0xcf, 0xdc, 0xde, 0x35, 0x5b, 0x3b, 0x65, 0x19, 0x03, + 0x5b, 0xbc, 0x34, 0xf4, 0xde, 0xf9, 0x9c, 0x02, 0x38, 0x61, 0xb4, 0x6f, 0xc9, 0xd6, 0xe6, + 0xc9, 0x07, 0x7a, 0xd9, 0x1d, 0x26, 0x91, 0xf7, 0xf7, 0xee, 0x59, 0x8c, 0xb0, 0xfa, 0xc1, + 0x86, 0xd9, 0x1c, 0xae, 0xfe, 0x13, 0x09, 0x85, 0x13, 0x92, 0x70, 0xb4, 0x13, 0x0c, 0x93, + 0xbc, 0x43, 0x79, 0x44, 0xf4, 0xfd, 0x44, 0x52, 0xe2, 0xd7, 0x4d, 0xd3, 0x64, 0xf2, 0xe2, + 0x1e, 0x71, 0xf5, 0x4b, 0xff, 0x5c, 0xae, 0x82, 0xab, 0x9c, 0x9d, 0xf6, 0x9e, 0xe8, 0x6d, + 0x2b, 0xc5, 0x22, 0x36, 0x3a, 0x0d, 0xab, 0xc5, 0x21, 0x97, 0x9b, 0x0d, 0xea, 0xda, 0x1d, + 0xbf, 0x9a, 0x42, 0xd5, 0xc4, 0x48, 0x4e, 0x0a, 0xbc, 0xd0, 0x6b, 0xfa, 0x53, 0xdd, 0xef, + 0x3c, 0x1b, 0x20, 0xee, 0x3f, 0xd5, 0x9d, 0x7c, 0x25, 0xe4, 0x1d, 0x2b, 0x66, 0x9e, 0x1e, + 0xf1, 0x6e, 0x6f, 0x52, 0xc3, 0x16, 0x4d, 0xf4, 0xfb, 0x79, 0x30, 0xe9, 0xe4, 0xe5, 0x88, + 0x57, 0xb6, 0xac, 0x7d, 0x5f, 0x42, 0xd6, 0x9f, 0x6d, 0x18, 0x77, 0x63, 0xcf, 0x1d, 0x55, + 0x03, 0x40, 0x04, 0x87, 0xf5, 0x5b, 0xa5, 0x7e, 0x31, 0xcc, 0x7a, 0x71, 0x35, 0xc8, 0x86, + 0xef, 0xb4, 0x31, 0x8a, 0xed, 0x6a, 0x1e, 0x01, 0x2d, 0x9e, 0x68, 0x32, 0xa9, 0x07, 0x60, + 0x0a, 0x91, 0x81, 0x30, 0xc4, 0x6d, 0xc7, 0x78, 0xf9, 0x71, 0xad, 0x00, 0x38, 0x09, 0x29, + 0x99, 0xa3, 0x33, 0xcb, 0x8b, 0x7a, 0x1a, 0x1d, 0xb9, 0x3d, 0x71, 0x40, 0x00, 0x3c, 0x2a, + 0x4e, 0xce, 0xa9, 0xf9, 0x8d, 0x0a, 0xcc, 0x0a, 0x82, 0x91, 0xcd, 0xce, 0xc9, 0x7d, 0xcf, + 0x8e, 0xc9, 0xb5, 0x5a, 0x7f, 0x88, 0xa4, 0x6b, 0x4d, 0xb5, 0xa8, 0x51, 0xf4, 0x41, 0x82, + 0xe1, 0xc6, 0x8a, 0x00, 0x7e, 0x5e, 0x0d, 0xd9, 0x02, 0x0b, 0xfd, 0x64, 0xb6, 0x45, 0x03, + 0x6c, 0x7a, 0x4e, 0x67, 0x7d, 0x2c, 0x38, 0x53, 0x2a, 0x3a, 0x23, 0xba, 0x44, 0x42, 0xca, + 0xf5, 0x3e, 0xa6, 0x3b, 0xb4, 0x54, 0x32, 0x9b, 0x76, 0x24, 0xc8, 0x91, 0x7b, 0xdd, 0x64, + 0xb1, 0xc0, 0xfd, 0x4c, 0xb3, 0x8e, 0x8c, 0x33, 0x4c, 0x70, 0x1c, 0x3a, 0xcd, 0xad, 0x06, + 0x57, 0xfc, 0xcf, 0xec, 0x71, 0x9b, 0x1f, 0x5c, 0x3e, 0x4e, 0x46, 0x04, 0x1f, 0x38, 0x81, + 0x47, 0xfb, 0x4c, 0xfd, 0xb4, 0x77, 0xa5, 0x24, 0x71, 0xf7, 0xa9, 0xa9, 0x69, 0x10, 0xb8, + 0x55, 0x32, 0x2e, 0xdb, 0x63, 0x40, 0xd8, 0xa0, 0x0e, 0xf0, 0x92, 0x35, 0x05, 0x11, 0xe3, + 0x0a, 0xbe, 0xc1, 0xff, 0xf9, 0xe3, 0xa2, 0x6e, 0x7f, 0xb2, 0x9f, 0x8c, 0x18, 0x30, 0x23, + 0xc3, 0x58, 0x7e, 0x38, 0xda, 0x00, 0x77, 0xd9, 0xb4, 0x76, 0x3e, 0x4e, 0x4b, 0x94, 0xb2, + 0xbb, 0xc1, 0x94, 0xc6, 0x65, 0x1e, 0x77, 0xca, 0xf9, 0x92, 0xee, 0xaa, 0xc0, 0x23, 0x2a, + 0x28, 0x1b, 0xf6, 0xb3, 0xa7, 0x39, 0xc1, 0x22, 0x61, 0x16, 0x82, 0x0a, 0xe8, 0xdb, 0x58, + 0x47, 0xa6, 0x7c, 0xbe, 0xf9, 0xc9, 0x09, 0x1b, 0x46, 0x2d, 0x53, 0x8c, 0xd7, 0x2b, 0x03, + 0x74, 0x6a, 0xe7, 0x7f, 0x5e, 0x62, 0x29, 0x2c, 0x31, 0x15, 0x62, 0xa8, 0x46, 0x50, 0x5d, + 0xc8, 0x2d, 0xb8, 0x54, 0x33, 0x8a, 0xe4, 0x9f, 0x52, 0x35, 0xc9, 0x5b, 0x91, 0x17, 0x8c, + 0xcf, 0x2d, 0xd5, 0xca, 0xce, 0xf4, 0x03, 0xec, 0x9d, 0x18, 0x10, 0xc6, 0x27, 0x2b, 0x04, + 0x5b, 0x3b, 0x71, 0xf9, 0xdc, 0x6b, 0x80, 0xd6, 0x3f, 0xdd, 0x4a, 0x8e, 0x9a, 0xdb, 0x1e, + 0x69, 0x62, 0xa6, 0x95, 0x26, 0xd4, 0x31, 0x61, 0xc1, 0xa4, 0x1d, 0x57, 0x0d, 0x79, 0x38, + 0xda, 0xd4, 0xa4, 0x0e, 0x32, 0x9c, 0xcf, 0xf4, 0x6a, 0xaa, 0x36, 0xad, 0x00, 0x4c, 0xf6, + 0x00, 0xc8, 0x38, 0x1e, 0x42, 0x5a, 0x31, 0xd9, 0x51, 0xae, 0x64, 0xfd, 0xb2, 0x3f, 0xce, + 0xc9, 0x50, 0x9d, 0x43, 0x68, 0x7f, 0xeb, 0x69, 0xed, 0xd1, 0xcc, 0x5e, 0x0b, 0x8c, 0xc3, + 0xbd, 0xf6, 0x4b, 0x10, 0xef, 0x86, 0xb6, 0x31, 0x42, 0xa3, 0xab, 0x88, 0x29, 0x55, 0x5b, + 0x2f, 0x74, 0x7c, 0x93, 0x26, 0x65, 0xcb, 0x2c, 0x0f, 0x1c, 0xc0, 0x1b, 0xd7, 0x02, 0x29, + 0x38, 0x88, 0x39, 0xd2, 0xaf, 0x05, 0xe4, 0x54, 0x50, 0x4a, 0xc7, 0x8b, 0x75, 0x82, 0x82, + 0x28, 0x46, 0xc0, 0xba, 0x35, 0xc3, 0x5f, 0x5c, 0x59, 0x16, 0x0c, 0xc0, 0x46, 0xfd, 0x82, + 0x51, 0x54, 0x1f, 0xc6, 0x8c, 0x9c, 0x86, 0xb0, 0x22, 0xbb, 0x70, 0x99, 0x87, 0x6a, 0x46, + 0x0e, 0x74, 0x51, 0xa8, 0xa9, 0x31, 0x09, 0x70, 0x3f, 0xee, 0x1c, 0x21, 0x7e, 0x6c, 0x38, + 0x26, 0xe5, 0x2c, 0x51, 0xaa, 0x69, 0x1e, 0x0e, 0x42, 0x3c, 0xfc, 0x99, 0xe9, 0xe3, 0x16, + 0x50, 0xc1, 0x21, 0x7b, 0x62, 0x48, 0x16, 0xcd, 0xad, 0x9a, 0x95, 0xf9, 0xd5, 0xb8, 0x01, + 0x94, 0x88, 0xd9, 0xc0, 0xa0, 0xa1, 0xfe, 0x30, 0x75, 0xa5, 0x77, 0xe2, 0x31, 0x83, 0xf8, + 0x1d, 0x4a, 0x3f, 0x2f, 0xa4, 0x57, 0x1e, 0xfc, 0x8c, 0xe0, 0xba, 0x8a, 0x4f, 0xe8, 0xb6, + 0x85, 0x5d, 0xfe, 0x72, 0xb0, 0xa6, 0x6e, 0xde, 0xd2, 0xfb, 0xab, 0xfb, 0xe5, 0x8a, 0x30, + 0xfa, 0xfa, 0xbe, 0x1c, 0x5d, 0x71, 0xa8, 0x7e, 0x2f, 0x74, 0x1e, 0xf8, 0xc1, 0xfe, 0x86, + 0xfe, 0xa6, 0xbb, 0xfd, 0xe5, 0x30, 0x67, 0x7f, 0x0d, 0x97, 0xd1, 0x1d, 0x49, 0xf7, 0xa8, + 0x44, 0x3d, 0x08, 0x22, 0xe5, 0x06, 0xa9, 0xf4, 0x61, 0x4e, 0x01, 0x1e, 0x2a, 0x94, 0x83, + 0x8f, 0xf8, 0x8c, 0xd6, 0x8c, 0x8b, 0xb7, 0xc5, 0xc6, 0x42, 0x4c, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, + ], + g: &[2], +}; + +#[test] +fn named_group_ffdhe_group_roundtrip() { + use NamedGroup::*; + let ffdhe_groups = [FFDHE2048, FFDHE3072, FFDHE4096, FFDHE6144, FFDHE8192]; + for g in ffdhe_groups { + #[allow(deprecated)] + let roundtrip = FfdheGroup::from_named_group(g) + .unwrap() + .named_group(); + assert_eq!(roundtrip, Some(g)); + } +} diff --git a/vendor/rustls/src/msgs/fragmenter.rs b/vendor/rustls/src/msgs/fragmenter.rs new file mode 100644 index 00000000..48636777 --- /dev/null +++ b/vendor/rustls/src/msgs/fragmenter.rs @@ -0,0 +1,232 @@ +use crate::Error; +use crate::enums::{ContentType, ProtocolVersion}; +use crate::msgs::message::{OutboundChunks, OutboundPlainMessage, PlainMessage}; +pub(crate) const MAX_FRAGMENT_LEN: usize = 16384; +pub(crate) const PACKET_OVERHEAD: usize = 1 + 2 + 2; +pub(crate) const MAX_FRAGMENT_SIZE: usize = MAX_FRAGMENT_LEN + PACKET_OVERHEAD; + +pub struct MessageFragmenter { + max_frag: usize, +} + +impl Default for MessageFragmenter { + fn default() -> Self { + Self { + max_frag: MAX_FRAGMENT_LEN, + } + } +} + +impl MessageFragmenter { + /// Take `msg` and fragment it into new messages with the same type and version. + /// + /// Each returned message size is no more than `max_frag`. + /// + /// Return an iterator across those messages. + /// + /// Payloads are borrowed from `msg`. + pub fn fragment_message<'a>( + &self, + msg: &'a PlainMessage, + ) -> impl Iterator<Item = OutboundPlainMessage<'a>> + 'a { + self.fragment_payload(msg.typ, msg.version, msg.payload.bytes().into()) + } + + /// Take `payload` and fragment it into new messages with given type and version. + /// + /// Each returned message size is no more than `max_frag`. + /// + /// Return an iterator across those messages. + /// + /// Payloads are borrowed from `payload`. + pub(crate) fn fragment_payload<'a>( + &self, + typ: ContentType, + version: ProtocolVersion, + payload: OutboundChunks<'a>, + ) -> impl ExactSizeIterator<Item = OutboundPlainMessage<'a>> { + Chunker::new(payload, self.max_frag).map(move |payload| OutboundPlainMessage { + typ, + version, + payload, + }) + } + + /// Set the maximum fragment size that will be produced. + /// + /// This includes overhead. A `max_fragment_size` of 10 will produce TLS fragments + /// up to 10 bytes long. + /// + /// A `max_fragment_size` of `None` sets the highest allowable fragment size. + /// + /// Returns BadMaxFragmentSize if the size is smaller than 32 or larger than 16389. + pub fn set_max_fragment_size(&mut self, max_fragment_size: Option<usize>) -> Result<(), Error> { + self.max_frag = match max_fragment_size { + Some(sz @ 32..=MAX_FRAGMENT_SIZE) => sz - PACKET_OVERHEAD, + None => MAX_FRAGMENT_LEN, + _ => return Err(Error::BadMaxFragmentSize), + }; + Ok(()) + } +} + +/// An iterator over borrowed fragments of a payload +struct Chunker<'a> { + payload: OutboundChunks<'a>, + limit: usize, +} + +impl<'a> Chunker<'a> { + fn new(payload: OutboundChunks<'a>, limit: usize) -> Self { + Self { payload, limit } + } +} + +impl<'a> Iterator for Chunker<'a> { + type Item = OutboundChunks<'a>; + + fn next(&mut self) -> Option<Self::Item> { + if self.payload.is_empty() { + return None; + } + + let (before, after) = self.payload.split_at(self.limit); + self.payload = after; + Some(before) + } +} + +impl ExactSizeIterator for Chunker<'_> { + fn len(&self) -> usize { + (self.payload.len() + self.limit - 1) / self.limit + } +} + +#[cfg(test)] +mod tests { + use std::prelude::v1::*; + use std::vec; + + use super::{MessageFragmenter, PACKET_OVERHEAD}; + use crate::enums::{ContentType, ProtocolVersion}; + use crate::msgs::base::Payload; + use crate::msgs::message::{OutboundChunks, OutboundPlainMessage, PlainMessage}; + + fn msg_eq( + m: &OutboundPlainMessage<'_>, + total_len: usize, + typ: &ContentType, + version: &ProtocolVersion, + bytes: &[u8], + ) { + assert_eq!(&m.typ, typ); + assert_eq!(&m.version, version); + assert_eq!(m.payload.to_vec(), bytes); + + let buf = m.to_unencrypted_opaque().encode(); + + assert_eq!(total_len, buf.len()); + } + + #[test] + fn smoke() { + let typ = ContentType::Handshake; + let version = ProtocolVersion::TLSv1_2; + let data: Vec<u8> = (1..70u8).collect(); + let m = PlainMessage { + typ, + version, + payload: Payload::new(data), + }; + + let mut frag = MessageFragmenter::default(); + frag.set_max_fragment_size(Some(32)) + .unwrap(); + let q = frag + .fragment_message(&m) + .collect::<Vec<_>>(); + assert_eq!(q.len(), 3); + msg_eq( + &q[0], + 32, + &typ, + &version, + &[ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, + ], + ); + msg_eq( + &q[1], + 32, + &typ, + &version, + &[ + 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, + ], + ); + msg_eq( + &q[2], + 20, + &typ, + &version, + &[55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69], + ); + } + + #[test] + fn non_fragment() { + let m = PlainMessage { + typ: ContentType::Handshake, + version: ProtocolVersion::TLSv1_2, + payload: Payload::new(b"\x01\x02\x03\x04\x05\x06\x07\x08".to_vec()), + }; + + let mut frag = MessageFragmenter::default(); + frag.set_max_fragment_size(Some(32)) + .unwrap(); + let q = frag + .fragment_message(&m) + .collect::<Vec<_>>(); + assert_eq!(q.len(), 1); + msg_eq( + &q[0], + PACKET_OVERHEAD + 8, + &ContentType::Handshake, + &ProtocolVersion::TLSv1_2, + b"\x01\x02\x03\x04\x05\x06\x07\x08", + ); + } + + #[test] + fn fragment_multiple_slices() { + let typ = ContentType::Handshake; + let version = ProtocolVersion::TLSv1_2; + let payload_owner: Vec<&[u8]> = vec![&[b'a'; 8], &[b'b'; 12], &[b'c'; 32], &[b'd'; 20]]; + let borrowed_payload = OutboundChunks::new(&payload_owner); + let mut frag = MessageFragmenter::default(); + frag.set_max_fragment_size(Some(37)) // 32 + packet overhead + .unwrap(); + + let fragments = frag + .fragment_payload(typ, version, borrowed_payload) + .collect::<Vec<_>>(); + assert_eq!(fragments.len(), 3); + msg_eq( + &fragments[0], + 37, + &typ, + &version, + b"aaaaaaaabbbbbbbbbbbbcccccccccccc", + ); + msg_eq( + &fragments[1], + 37, + &typ, + &version, + b"ccccccccccccccccccccdddddddddddd", + ); + msg_eq(&fragments[2], 13, &typ, &version, b"dddddddd"); + } +} diff --git a/vendor/rustls/src/msgs/handshake.rs b/vendor/rustls/src/msgs/handshake.rs new file mode 100644 index 00000000..093f2d12 --- /dev/null +++ b/vendor/rustls/src/msgs/handshake.rs @@ -0,0 +1,3210 @@ +use alloc::boxed::Box; +use alloc::collections::BTreeSet; +#[cfg(feature = "logging")] +use alloc::string::String; +use alloc::vec; +use alloc::vec::Vec; +use core::ops::{Deref, DerefMut}; +use core::{fmt, iter}; + +use pki_types::{CertificateDer, DnsName}; + +#[cfg(feature = "tls12")] +use crate::crypto::ActiveKeyExchange; +use crate::crypto::SecureRandom; +use crate::enums::{ + CertificateCompressionAlgorithm, CertificateType, CipherSuite, EchClientHelloType, + HandshakeType, ProtocolVersion, SignatureScheme, +}; +use crate::error::InvalidMessage; +#[cfg(feature = "tls12")] +use crate::ffdhe_groups::FfdheGroup; +use crate::log::warn; +use crate::msgs::base::{MaybeEmpty, NonEmpty, Payload, PayloadU8, PayloadU16, PayloadU24}; +use crate::msgs::codec::{ + self, Codec, LengthPrefixedBuffer, ListLength, Reader, TlsListElement, TlsListIter, +}; +use crate::msgs::enums::{ + CertificateStatusType, ClientCertificateType, Compression, ECCurveType, ECPointFormat, + EchVersion, ExtensionType, HpkeAead, HpkeKdf, HpkeKem, KeyUpdateRequest, NamedGroup, + PskKeyExchangeMode, ServerNameType, +}; +use crate::rand; +use crate::sync::Arc; +use crate::verify::DigitallySignedStruct; +use crate::x509::wrap_in_sequence; + +/// Create a newtype wrapper around a given type. +/// +/// This is used to create newtypes for the various TLS message types which is used to wrap +/// the `PayloadU8` or `PayloadU16` types. This is typically used for types where we don't need +/// anything other than access to the underlying bytes. +macro_rules! wrapped_payload( + ($(#[$comment:meta])* $vis:vis struct $name:ident, $inner:ident$(<$inner_ty:ty>)?,) => { + $(#[$comment])* + #[derive(Clone, Debug)] + $vis struct $name($inner$(<$inner_ty>)?); + + impl From<Vec<u8>> for $name { + fn from(v: Vec<u8>) -> Self { + Self($inner::new(v)) + } + } + + impl AsRef<[u8]> for $name { + fn as_ref(&self) -> &[u8] { + self.0.0.as_slice() + } + } + + impl Codec<'_> for $name { + fn encode(&self, bytes: &mut Vec<u8>) { + self.0.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self($inner::read(r)?)) + } + } + } +); + +#[derive(Clone, Copy, Eq, PartialEq)] +pub(crate) struct Random(pub(crate) [u8; 32]); + +impl fmt::Debug for Random { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + super::base::hex(f, &self.0) + } +} + +static HELLO_RETRY_REQUEST_RANDOM: Random = Random([ + 0xcf, 0x21, 0xad, 0x74, 0xe5, 0x9a, 0x61, 0x11, 0xbe, 0x1d, 0x8c, 0x02, 0x1e, 0x65, 0xb8, 0x91, + 0xc2, 0xa2, 0x11, 0x16, 0x7a, 0xbb, 0x8c, 0x5e, 0x07, 0x9e, 0x09, 0xe2, 0xc8, 0xa8, 0x33, 0x9c, +]); + +static ZERO_RANDOM: Random = Random([0u8; 32]); + +impl Codec<'_> for Random { + fn encode(&self, bytes: &mut Vec<u8>) { + bytes.extend_from_slice(&self.0); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let Some(bytes) = r.take(32) else { + return Err(InvalidMessage::MissingData("Random")); + }; + + let mut opaque = [0; 32]; + opaque.clone_from_slice(bytes); + Ok(Self(opaque)) + } +} + +impl Random { + pub(crate) fn new(secure_random: &dyn SecureRandom) -> Result<Self, rand::GetRandomFailed> { + let mut data = [0u8; 32]; + secure_random.fill(&mut data)?; + Ok(Self(data)) + } +} + +impl From<[u8; 32]> for Random { + #[inline] + fn from(bytes: [u8; 32]) -> Self { + Self(bytes) + } +} + +#[derive(Copy, Clone)] +pub(crate) struct SessionId { + len: usize, + data: [u8; 32], +} + +impl fmt::Debug for SessionId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + super::base::hex(f, &self.data[..self.len]) + } +} + +impl PartialEq for SessionId { + fn eq(&self, other: &Self) -> bool { + if self.len != other.len { + return false; + } + + let mut diff = 0u8; + for i in 0..self.len { + diff |= self.data[i] ^ other.data[i]; + } + + diff == 0u8 + } +} + +impl Codec<'_> for SessionId { + fn encode(&self, bytes: &mut Vec<u8>) { + debug_assert!(self.len <= 32); + bytes.push(self.len as u8); + bytes.extend_from_slice(self.as_ref()); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let len = u8::read(r)? as usize; + if len > 32 { + return Err(InvalidMessage::TrailingData("SessionID")); + } + + let Some(bytes) = r.take(len) else { + return Err(InvalidMessage::MissingData("SessionID")); + }; + + let mut out = [0u8; 32]; + out[..len].clone_from_slice(&bytes[..len]); + Ok(Self { data: out, len }) + } +} + +impl SessionId { + pub(crate) fn random(secure_random: &dyn SecureRandom) -> Result<Self, rand::GetRandomFailed> { + let mut data = [0u8; 32]; + secure_random.fill(&mut data)?; + Ok(Self { data, len: 32 }) + } + + pub(crate) fn empty() -> Self { + Self { + data: [0u8; 32], + len: 0, + } + } + + #[cfg(feature = "tls12")] + pub(crate) fn is_empty(&self) -> bool { + self.len == 0 + } +} + +impl AsRef<[u8]> for SessionId { + fn as_ref(&self) -> &[u8] { + &self.data[..self.len] + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct UnknownExtension { + pub(crate) typ: ExtensionType, + pub(crate) payload: Payload<'static>, +} + +impl UnknownExtension { + fn encode(&self, bytes: &mut Vec<u8>) { + self.payload.encode(bytes); + } + + fn read(typ: ExtensionType, r: &mut Reader<'_>) -> Self { + let payload = Payload::read(r).into_owned(); + Self { typ, payload } + } +} + +#[derive(Clone, Copy, Debug)] +pub(crate) struct SupportedEcPointFormats { + pub(crate) uncompressed: bool, +} + +impl Codec<'_> for SupportedEcPointFormats { + fn encode(&self, bytes: &mut Vec<u8>) { + let inner = LengthPrefixedBuffer::new(ECPointFormat::SIZE_LEN, bytes); + + if self.uncompressed { + ECPointFormat::Uncompressed.encode(inner.buf); + } + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let mut uncompressed = false; + + for pf in TlsListIter::<ECPointFormat>::new(r)? { + if let ECPointFormat::Uncompressed = pf? { + uncompressed = true; + } + } + + Ok(Self { uncompressed }) + } +} + +impl Default for SupportedEcPointFormats { + fn default() -> Self { + Self { uncompressed: true } + } +} + +/// RFC8422: `ECPointFormat ec_point_format_list<1..2^8-1>` +impl TlsListElement for ECPointFormat { + const SIZE_LEN: ListLength = ListLength::NonZeroU8 { + empty_error: InvalidMessage::IllegalEmptyList("ECPointFormats"), + }; +} + +/// RFC8422: `NamedCurve named_curve_list<2..2^16-1>` +impl TlsListElement for NamedGroup { + const SIZE_LEN: ListLength = ListLength::NonZeroU16 { + empty_error: InvalidMessage::IllegalEmptyList("NamedGroups"), + }; +} + +/// RFC8446: `SignatureScheme supported_signature_algorithms<2..2^16-2>;` +impl TlsListElement for SignatureScheme { + const SIZE_LEN: ListLength = ListLength::NonZeroU16 { + empty_error: InvalidMessage::NoSignatureSchemes, + }; +} + +#[derive(Clone, Debug)] +pub(crate) enum ServerNamePayload<'a> { + /// A successfully decoded value: + SingleDnsName(DnsName<'a>), + + /// A DNS name which was actually an IP address + IpAddress, + + /// A successfully decoded, but syntactically-invalid value. + Invalid, +} + +impl ServerNamePayload<'_> { + fn into_owned(self) -> ServerNamePayload<'static> { + match self { + Self::SingleDnsName(d) => ServerNamePayload::SingleDnsName(d.to_owned()), + Self::IpAddress => ServerNamePayload::IpAddress, + Self::Invalid => ServerNamePayload::Invalid, + } + } + + /// RFC6066: `ServerName server_name_list<1..2^16-1>` + const SIZE_LEN: ListLength = ListLength::NonZeroU16 { + empty_error: InvalidMessage::IllegalEmptyList("ServerNames"), + }; +} + +/// Simplified encoding/decoding for a `ServerName` extension payload to/from `DnsName` +/// +/// This is possible because: +/// +/// - the spec (RFC6066) disallows multiple names for a given name type +/// - name types other than ServerNameType::HostName are not defined, and they and +/// any data that follows them cannot be skipped over. +impl<'a> Codec<'a> for ServerNamePayload<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + let server_name_list = LengthPrefixedBuffer::new(Self::SIZE_LEN, bytes); + + let ServerNamePayload::SingleDnsName(dns_name) = self else { + return; + }; + + ServerNameType::HostName.encode(server_name_list.buf); + let name_slice = dns_name.as_ref().as_bytes(); + (name_slice.len() as u16).encode(server_name_list.buf); + server_name_list + .buf + .extend_from_slice(name_slice); + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let mut found = None; + + let len = Self::SIZE_LEN.read(r)?; + let mut sub = r.sub(len)?; + + while sub.any_left() { + let typ = ServerNameType::read(&mut sub)?; + + let payload = match typ { + ServerNameType::HostName => HostNamePayload::read(&mut sub)?, + _ => { + // Consume remainder of extension bytes. Since the length of the item + // is an unknown encoding, we cannot continue. + sub.rest(); + break; + } + }; + + // "The ServerNameList MUST NOT contain more than one name of + // the same name_type." - RFC6066 + if found.is_some() { + warn!("Illegal SNI extension: duplicate host_name received"); + return Err(InvalidMessage::InvalidServerName); + } + + found = match payload { + HostNamePayload::HostName(dns_name) => { + Some(Self::SingleDnsName(dns_name.to_owned())) + } + + HostNamePayload::IpAddress(_invalid) => { + warn!( + "Illegal SNI extension: ignoring IP address presented as hostname ({_invalid:?})" + ); + Some(Self::IpAddress) + } + + HostNamePayload::Invalid(_invalid) => { + warn!( + "Illegal SNI hostname received {:?}", + String::from_utf8_lossy(&_invalid.0) + ); + Some(Self::Invalid) + } + }; + } + + Ok(found.unwrap_or(Self::Invalid)) + } +} + +impl<'a> From<&DnsName<'a>> for ServerNamePayload<'static> { + fn from(value: &DnsName<'a>) -> Self { + Self::SingleDnsName(trim_hostname_trailing_dot_for_sni(value)) + } +} + +#[derive(Clone, Debug)] +pub(crate) enum HostNamePayload { + HostName(DnsName<'static>), + IpAddress(PayloadU16<NonEmpty>), + Invalid(PayloadU16<NonEmpty>), +} + +impl HostNamePayload { + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + use pki_types::ServerName; + let raw = PayloadU16::<NonEmpty>::read(r)?; + + match ServerName::try_from(raw.0.as_slice()) { + Ok(ServerName::DnsName(d)) => Ok(Self::HostName(d.to_owned())), + Ok(ServerName::IpAddress(_)) => Ok(Self::IpAddress(raw)), + Ok(_) | Err(_) => Ok(Self::Invalid(raw)), + } + } +} + +wrapped_payload!( + /// RFC7301: `opaque ProtocolName<1..2^8-1>;` + pub(crate) struct ProtocolName, PayloadU8<NonEmpty>, +); + +impl PartialEq for ProtocolName { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } +} + +impl Deref for ProtocolName { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.as_ref() + } +} + +/// RFC7301: `ProtocolName protocol_name_list<2..2^16-1>` +impl TlsListElement for ProtocolName { + const SIZE_LEN: ListLength = ListLength::NonZeroU16 { + empty_error: InvalidMessage::IllegalEmptyList("ProtocolNames"), + }; +} + +/// RFC7301 encodes a single protocol name as `Vec<ProtocolName>` +#[derive(Clone, Debug)] +pub(crate) struct SingleProtocolName(ProtocolName); + +impl SingleProtocolName { + pub(crate) fn new(single: ProtocolName) -> Self { + Self(single) + } + + const SIZE_LEN: ListLength = ListLength::NonZeroU16 { + empty_error: InvalidMessage::IllegalEmptyList("ProtocolNames"), + }; +} + +impl Codec<'_> for SingleProtocolName { + fn encode(&self, bytes: &mut Vec<u8>) { + let body = LengthPrefixedBuffer::new(Self::SIZE_LEN, bytes); + self.0.encode(body.buf); + } + + fn read(reader: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let len = Self::SIZE_LEN.read(reader)?; + let mut sub = reader.sub(len)?; + + let item = ProtocolName::read(&mut sub)?; + + if sub.any_left() { + Err(InvalidMessage::TrailingData("SingleProtocolName")) + } else { + Ok(Self(item)) + } + } +} + +impl AsRef<ProtocolName> for SingleProtocolName { + fn as_ref(&self) -> &ProtocolName { + &self.0 + } +} + +// --- TLS 1.3 Key shares --- +#[derive(Clone, Debug)] +pub(crate) struct KeyShareEntry { + pub(crate) group: NamedGroup, + /// RFC8446: `opaque key_exchange<1..2^16-1>;` + pub(crate) payload: PayloadU16<NonEmpty>, +} + +impl KeyShareEntry { + pub(crate) fn new(group: NamedGroup, payload: impl Into<Vec<u8>>) -> Self { + Self { + group, + payload: PayloadU16::new(payload.into()), + } + } +} + +impl Codec<'_> for KeyShareEntry { + fn encode(&self, bytes: &mut Vec<u8>) { + self.group.encode(bytes); + self.payload.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let group = NamedGroup::read(r)?; + let payload = PayloadU16::read(r)?; + + Ok(Self { group, payload }) + } +} + +// --- TLS 1.3 PresharedKey offers --- +#[derive(Clone, Debug)] +pub(crate) struct PresharedKeyIdentity { + /// RFC8446: `opaque identity<1..2^16-1>;` + pub(crate) identity: PayloadU16<NonEmpty>, + pub(crate) obfuscated_ticket_age: u32, +} + +impl PresharedKeyIdentity { + pub(crate) fn new(id: Vec<u8>, age: u32) -> Self { + Self { + identity: PayloadU16::new(id), + obfuscated_ticket_age: age, + } + } +} + +impl Codec<'_> for PresharedKeyIdentity { + fn encode(&self, bytes: &mut Vec<u8>) { + self.identity.encode(bytes); + self.obfuscated_ticket_age.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + identity: PayloadU16::read(r)?, + obfuscated_ticket_age: u32::read(r)?, + }) + } +} + +/// RFC8446: `PskIdentity identities<7..2^16-1>;` +impl TlsListElement for PresharedKeyIdentity { + const SIZE_LEN: ListLength = ListLength::NonZeroU16 { + empty_error: InvalidMessage::IllegalEmptyList("PskIdentities"), + }; +} + +wrapped_payload!( + /// RFC8446: `opaque PskBinderEntry<32..255>;` + pub(crate) struct PresharedKeyBinder, PayloadU8<NonEmpty>, +); + +/// RFC8446: `PskBinderEntry binders<33..2^16-1>;` +impl TlsListElement for PresharedKeyBinder { + const SIZE_LEN: ListLength = ListLength::NonZeroU16 { + empty_error: InvalidMessage::IllegalEmptyList("PskBinders"), + }; +} + +#[derive(Clone, Debug)] +pub(crate) struct PresharedKeyOffer { + pub(crate) identities: Vec<PresharedKeyIdentity>, + pub(crate) binders: Vec<PresharedKeyBinder>, +} + +impl PresharedKeyOffer { + /// Make a new one with one entry. + pub(crate) fn new(id: PresharedKeyIdentity, binder: Vec<u8>) -> Self { + Self { + identities: vec![id], + binders: vec![PresharedKeyBinder::from(binder)], + } + } +} + +impl Codec<'_> for PresharedKeyOffer { + fn encode(&self, bytes: &mut Vec<u8>) { + self.identities.encode(bytes); + self.binders.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + identities: Vec::read(r)?, + binders: Vec::read(r)?, + }) + } +} + +// --- RFC6066 certificate status request --- +wrapped_payload!(pub(crate) struct ResponderId, PayloadU16,); + +/// RFC6066: `ResponderID responder_id_list<0..2^16-1>;` +impl TlsListElement for ResponderId { + const SIZE_LEN: ListLength = ListLength::U16; +} + +#[derive(Clone, Debug)] +pub(crate) struct OcspCertificateStatusRequest { + pub(crate) responder_ids: Vec<ResponderId>, + pub(crate) extensions: PayloadU16, +} + +impl Codec<'_> for OcspCertificateStatusRequest { + fn encode(&self, bytes: &mut Vec<u8>) { + CertificateStatusType::OCSP.encode(bytes); + self.responder_ids.encode(bytes); + self.extensions.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + responder_ids: Vec::read(r)?, + extensions: PayloadU16::read(r)?, + }) + } +} + +#[derive(Clone, Debug)] +pub(crate) enum CertificateStatusRequest { + Ocsp(OcspCertificateStatusRequest), + Unknown((CertificateStatusType, Payload<'static>)), +} + +impl Codec<'_> for CertificateStatusRequest { + fn encode(&self, bytes: &mut Vec<u8>) { + match self { + Self::Ocsp(r) => r.encode(bytes), + Self::Unknown((typ, payload)) => { + typ.encode(bytes); + payload.encode(bytes); + } + } + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let typ = CertificateStatusType::read(r)?; + + match typ { + CertificateStatusType::OCSP => { + let ocsp_req = OcspCertificateStatusRequest::read(r)?; + Ok(Self::Ocsp(ocsp_req)) + } + _ => { + let data = Payload::read(r).into_owned(); + Ok(Self::Unknown((typ, data))) + } + } + } +} + +impl CertificateStatusRequest { + pub(crate) fn build_ocsp() -> Self { + let ocsp = OcspCertificateStatusRequest { + responder_ids: Vec::new(), + extensions: PayloadU16::empty(), + }; + Self::Ocsp(ocsp) + } +} + +// --- + +/// RFC8446: `PskKeyExchangeMode ke_modes<1..255>;` +#[derive(Clone, Copy, Debug, Default)] +pub(crate) struct PskKeyExchangeModes { + pub(crate) psk_dhe: bool, + pub(crate) psk: bool, +} + +impl Codec<'_> for PskKeyExchangeModes { + fn encode(&self, bytes: &mut Vec<u8>) { + let inner = LengthPrefixedBuffer::new(PskKeyExchangeMode::SIZE_LEN, bytes); + if self.psk_dhe { + PskKeyExchangeMode::PSK_DHE_KE.encode(inner.buf); + } + if self.psk { + PskKeyExchangeMode::PSK_KE.encode(inner.buf); + } + } + + fn read(reader: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let mut psk_dhe = false; + let mut psk = false; + + for ke in TlsListIter::<PskKeyExchangeMode>::new(reader)? { + match ke? { + PskKeyExchangeMode::PSK_DHE_KE => psk_dhe = true, + PskKeyExchangeMode::PSK_KE => psk = true, + _ => continue, + }; + } + + Ok(Self { psk_dhe, psk }) + } +} + +impl TlsListElement for PskKeyExchangeMode { + const SIZE_LEN: ListLength = ListLength::NonZeroU8 { + empty_error: InvalidMessage::IllegalEmptyList("PskKeyExchangeModes"), + }; +} + +/// RFC8446: `KeyShareEntry client_shares<0..2^16-1>;` +impl TlsListElement for KeyShareEntry { + const SIZE_LEN: ListLength = ListLength::U16; +} + +/// The body of the `SupportedVersions` extension when it appears in a +/// `ClientHello` +/// +/// This is documented as a preference-order vector, but we (as a server) +/// ignore the preference of the client. +/// +/// RFC8446: `ProtocolVersion versions<2..254>;` +#[derive(Clone, Copy, Debug, Default)] +pub(crate) struct SupportedProtocolVersions { + pub(crate) tls13: bool, + pub(crate) tls12: bool, +} + +impl SupportedProtocolVersions { + /// Return true if `filter` returns true for any enabled version. + pub(crate) fn any(&self, filter: impl Fn(ProtocolVersion) -> bool) -> bool { + if self.tls13 && filter(ProtocolVersion::TLSv1_3) { + return true; + } + if self.tls12 && filter(ProtocolVersion::TLSv1_2) { + return true; + } + false + } + + const LIST_LENGTH: ListLength = ListLength::NonZeroU8 { + empty_error: InvalidMessage::IllegalEmptyList("ProtocolVersions"), + }; +} + +impl Codec<'_> for SupportedProtocolVersions { + fn encode(&self, bytes: &mut Vec<u8>) { + let inner = LengthPrefixedBuffer::new(Self::LIST_LENGTH, bytes); + if self.tls13 { + ProtocolVersion::TLSv1_3.encode(inner.buf); + } + if self.tls12 { + ProtocolVersion::TLSv1_2.encode(inner.buf); + } + } + + fn read(reader: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let mut tls12 = false; + let mut tls13 = false; + + for pv in TlsListIter::<ProtocolVersion>::new(reader)? { + match pv? { + ProtocolVersion::TLSv1_3 => tls13 = true, + ProtocolVersion::TLSv1_2 => tls12 = true, + _ => continue, + }; + } + + Ok(Self { tls13, tls12 }) + } +} + +impl TlsListElement for ProtocolVersion { + const SIZE_LEN: ListLength = ListLength::NonZeroU8 { + empty_error: InvalidMessage::IllegalEmptyList("ProtocolVersions"), + }; +} + +/// RFC7250: `CertificateType client_certificate_types<1..2^8-1>;` +/// +/// Ditto `CertificateType server_certificate_types<1..2^8-1>;` +impl TlsListElement for CertificateType { + const SIZE_LEN: ListLength = ListLength::NonZeroU8 { + empty_error: InvalidMessage::IllegalEmptyList("CertificateTypes"), + }; +} + +/// RFC8879: `CertificateCompressionAlgorithm algorithms<2..2^8-2>;` +impl TlsListElement for CertificateCompressionAlgorithm { + const SIZE_LEN: ListLength = ListLength::NonZeroU8 { + empty_error: InvalidMessage::IllegalEmptyList("CertificateCompressionAlgorithms"), + }; +} + +/// A precursor to `ClientExtensions`, allowing customisation. +/// +/// This is smaller than `ClientExtensions`, as it only contains the extensions +/// we need to vary between different protocols (eg, TCP-TLS versus QUIC). +#[derive(Clone, Default)] +pub(crate) struct ClientExtensionsInput<'a> { + /// QUIC transport parameters + pub(crate) transport_parameters: Option<TransportParameters<'a>>, + + /// ALPN protocols + pub(crate) protocols: Option<Vec<ProtocolName>>, +} + +impl ClientExtensionsInput<'_> { + pub(crate) fn from_alpn(alpn_protocols: Vec<Vec<u8>>) -> ClientExtensionsInput<'static> { + let protocols = match alpn_protocols.is_empty() { + true => None, + false => Some( + alpn_protocols + .into_iter() + .map(ProtocolName::from) + .collect::<Vec<_>>(), + ), + }; + + ClientExtensionsInput { + transport_parameters: None, + protocols, + } + } + + pub(crate) fn into_owned(self) -> ClientExtensionsInput<'static> { + let Self { + transport_parameters, + protocols, + } = self; + ClientExtensionsInput { + transport_parameters: transport_parameters.map(|x| x.into_owned()), + protocols, + } + } +} + +#[derive(Clone)] +pub(crate) enum TransportParameters<'a> { + /// QUIC transport parameters (RFC9001 prior to draft 33) + QuicDraft(Payload<'a>), + + /// QUIC transport parameters (RFC9001) + Quic(Payload<'a>), +} + +impl TransportParameters<'_> { + pub(crate) fn into_owned(self) -> TransportParameters<'static> { + match self { + Self::QuicDraft(v) => TransportParameters::QuicDraft(v.into_owned()), + Self::Quic(v) => TransportParameters::Quic(v.into_owned()), + } + } +} + +extension_struct! { + /// A representation of extensions present in a `ClientHello` message + /// + /// All extensions are optional (by definition) so are represented with `Option<T>`. + /// + /// Some extensions have an empty value and are represented with Option<()>. + /// + /// Unknown extensions are dropped during parsing. + pub(crate) struct ClientExtensions<'a> { + /// Requested server name indication (RFC6066) + ExtensionType::ServerName => + pub(crate) server_name: Option<ServerNamePayload<'a>>, + + /// Certificate status is requested (RFC6066) + ExtensionType::StatusRequest => + pub(crate) certificate_status_request: Option<CertificateStatusRequest>, + + /// Supported groups (RFC4492/RFC8446) + ExtensionType::EllipticCurves => + pub(crate) named_groups: Option<Vec<NamedGroup>>, + + /// Supported EC point formats (RFC4492) + ExtensionType::ECPointFormats => + pub(crate) ec_point_formats: Option<SupportedEcPointFormats>, + + /// Supported signature schemes (RFC5246/RFC8446) + ExtensionType::SignatureAlgorithms => + pub(crate) signature_schemes: Option<Vec<SignatureScheme>>, + + /// Offered ALPN protocols (RFC6066) + ExtensionType::ALProtocolNegotiation => + pub(crate) protocols: Option<Vec<ProtocolName>>, + + /// Available client certificate types (RFC7250) + ExtensionType::ClientCertificateType => + pub(crate) client_certificate_types: Option<Vec<CertificateType>>, + + /// Acceptable server certificate types (RFC7250) + ExtensionType::ServerCertificateType => + pub(crate) server_certificate_types: Option<Vec<CertificateType>>, + + /// Extended master secret is requested (RFC7627) + ExtensionType::ExtendedMasterSecret => + pub(crate) extended_master_secret_request: Option<()>, + + /// Offered certificate compression methods (RFC8879) + ExtensionType::CompressCertificate => + pub(crate) certificate_compression_algorithms: Option<Vec<CertificateCompressionAlgorithm>>, + + /// Session ticket offer or request (RFC5077/RFC8446) + ExtensionType::SessionTicket => + pub(crate) session_ticket: Option<ClientSessionTicket>, + + /// Offered preshared keys (RFC8446) + ExtensionType::PreSharedKey => + pub(crate) preshared_key_offer: Option<PresharedKeyOffer>, + + /// Early data is requested (RFC8446) + ExtensionType::EarlyData => + pub(crate) early_data_request: Option<()>, + + /// Supported TLS versions (RFC8446) + ExtensionType::SupportedVersions => + pub(crate) supported_versions: Option<SupportedProtocolVersions>, + + /// Stateless HelloRetryRequest cookie (RFC8446) + ExtensionType::Cookie => + pub(crate) cookie: Option<PayloadU16<NonEmpty>>, + + /// Offered preshared key modes (RFC8446) + ExtensionType::PSKKeyExchangeModes => + pub(crate) preshared_key_modes: Option<PskKeyExchangeModes>, + + /// Certificate authority names (RFC8446) + ExtensionType::CertificateAuthorities => + pub(crate) certificate_authority_names: Option<Vec<DistinguishedName>>, + + /// Offered key exchange shares (RFC8446) + ExtensionType::KeyShare => + pub(crate) key_shares: Option<Vec<KeyShareEntry>>, + + /// QUIC transport parameters (RFC9001) + ExtensionType::TransportParameters => + pub(crate) transport_parameters: Option<Payload<'a>>, + + /// Secure renegotiation (RFC5746) + ExtensionType::RenegotiationInfo => + pub(crate) renegotiation_info: Option<PayloadU8>, + + /// QUIC transport parameters (RFC9001 prior to draft 33) + ExtensionType::TransportParametersDraft => + pub(crate) transport_parameters_draft: Option<Payload<'a>>, + + /// Encrypted inner client hello (draft-ietf-tls-esni) + ExtensionType::EncryptedClientHello => + pub(crate) encrypted_client_hello: Option<EncryptedClientHello>, + + /// Encrypted client hello outer extensions (draft-ietf-tls-esni) + ExtensionType::EncryptedClientHelloOuterExtensions => + pub(crate) encrypted_client_hello_outer: Option<Vec<ExtensionType>>, + } + { + /// Order randomization seed. + pub(crate) order_seed: u16, + + /// Extensions that must appear contiguously. + pub(crate) contiguous_extensions: Vec<ExtensionType>, + } +} + +impl ClientExtensions<'_> { + pub(crate) fn into_owned(self) -> ClientExtensions<'static> { + let Self { + server_name, + certificate_status_request, + named_groups, + ec_point_formats, + signature_schemes, + protocols, + client_certificate_types, + server_certificate_types, + extended_master_secret_request, + certificate_compression_algorithms, + session_ticket, + preshared_key_offer, + early_data_request, + supported_versions, + cookie, + preshared_key_modes, + certificate_authority_names, + key_shares, + transport_parameters, + renegotiation_info, + transport_parameters_draft, + encrypted_client_hello, + encrypted_client_hello_outer, + order_seed, + contiguous_extensions, + } = self; + ClientExtensions { + server_name: server_name.map(|x| x.into_owned()), + certificate_status_request, + named_groups, + ec_point_formats, + signature_schemes, + protocols, + client_certificate_types, + server_certificate_types, + extended_master_secret_request, + certificate_compression_algorithms, + session_ticket, + preshared_key_offer, + early_data_request, + supported_versions, + cookie, + preshared_key_modes, + certificate_authority_names, + key_shares, + transport_parameters: transport_parameters.map(|x| x.into_owned()), + renegotiation_info, + transport_parameters_draft: transport_parameters_draft.map(|x| x.into_owned()), + encrypted_client_hello, + encrypted_client_hello_outer, + order_seed, + contiguous_extensions, + } + } + + pub(crate) fn used_extensions_in_encoding_order(&self) -> Vec<ExtensionType> { + let mut exts = self.order_insensitive_extensions_in_random_order(); + exts.extend(&self.contiguous_extensions); + + if self + .encrypted_client_hello_outer + .is_some() + { + exts.push(ExtensionType::EncryptedClientHelloOuterExtensions); + } + if self.encrypted_client_hello.is_some() { + exts.push(ExtensionType::EncryptedClientHello); + } + if self.preshared_key_offer.is_some() { + exts.push(ExtensionType::PreSharedKey); + } + exts + } + + /// Returns extensions which don't need a specific order, in randomized order. + /// + /// Extensions are encoded in three portions: + /// + /// - First, extensions not otherwise dealt with by other cases. + /// These are encoded in random order, controlled by `self.order_seed`, + /// and this is the set of extensions returned by this function. + /// + /// - Second, extensions named in `self.contiguous_extensions`, in the order + /// given by that field. + /// + /// - Lastly, any ECH and PSK extensions (in that order). These + /// are required to be last by the standard. + fn order_insensitive_extensions_in_random_order(&self) -> Vec<ExtensionType> { + let mut order = self.collect_used(); + + // Remove extensions which have specific order requirements. + order.retain(|ext| { + !(matches!( + ext, + ExtensionType::PreSharedKey + | ExtensionType::EncryptedClientHello + | ExtensionType::EncryptedClientHelloOuterExtensions + ) || self.contiguous_extensions.contains(ext)) + }); + + order.sort_by_cached_key(|new_ext| { + let seed = ((self.order_seed as u32) << 16) | (u16::from(*new_ext) as u32); + low_quality_integer_hash(seed) + }); + + order + } +} + +impl<'a> Codec<'a> for ClientExtensions<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + let order = self.used_extensions_in_encoding_order(); + + if order.is_empty() { + return; + } + + let body = LengthPrefixedBuffer::new(ListLength::U16, bytes); + for item in order { + self.encode_one(item, body.buf); + } + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let mut out = Self::default(); + + // extensions length can be absent if no extensions + if !r.any_left() { + return Ok(out); + } + + let mut checker = DuplicateExtensionChecker::new(); + + let len = usize::from(u16::read(r)?); + let mut sub = r.sub(len)?; + + while sub.any_left() { + let typ = out.read_one(&mut sub, |unknown| checker.check(unknown))?; + + // PreSharedKey offer must come last + if typ == ExtensionType::PreSharedKey && sub.any_left() { + return Err(InvalidMessage::PreSharedKeyIsNotFinalExtension); + } + } + + Ok(out) + } +} + +fn trim_hostname_trailing_dot_for_sni(dns_name: &DnsName<'_>) -> DnsName<'static> { + let dns_name_str = dns_name.as_ref(); + + // RFC6066: "The hostname is represented as a byte string using + // ASCII encoding without a trailing dot" + if dns_name_str.ends_with('.') { + let trimmed = &dns_name_str[0..dns_name_str.len() - 1]; + DnsName::try_from(trimmed) + .unwrap() + .to_owned() + } else { + dns_name.to_owned() + } +} + +#[derive(Clone, Debug)] +pub(crate) enum ClientSessionTicket { + Request, + Offer(Payload<'static>), +} + +impl<'a> Codec<'a> for ClientSessionTicket { + fn encode(&self, bytes: &mut Vec<u8>) { + match self { + Self::Request => (), + Self::Offer(p) => p.encode(bytes), + } + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + Ok(match r.left() { + 0 => Self::Request, + _ => Self::Offer(Payload::read(r).into_owned()), + }) + } +} + +#[derive(Default)] +pub(crate) struct ServerExtensionsInput<'a> { + /// QUIC transport parameters + pub(crate) transport_parameters: Option<TransportParameters<'a>>, +} + +extension_struct! { + pub(crate) struct ServerExtensions<'a> { + /// Supported EC point formats (RFC4492) + ExtensionType::ECPointFormats => + pub(crate) ec_point_formats: Option<SupportedEcPointFormats>, + + /// Server name indication acknowledgement (RFC6066) + ExtensionType::ServerName => + pub(crate) server_name_ack: Option<()>, + + /// Session ticket acknowledgement (RFC5077) + ExtensionType::SessionTicket => + pub(crate) session_ticket_ack: Option<()>, + + ExtensionType::RenegotiationInfo => + pub(crate) renegotiation_info: Option<PayloadU8>, + + /// Selected ALPN protocol (RFC7301) + ExtensionType::ALProtocolNegotiation => + pub(crate) selected_protocol: Option<SingleProtocolName>, + + /// Key exchange server share (RFC8446) + ExtensionType::KeyShare => + pub(crate) key_share: Option<KeyShareEntry>, + + /// Selected preshared key index (RFC8446) + ExtensionType::PreSharedKey => + pub(crate) preshared_key: Option<u16>, + + /// Required client certificate type (RFC7250) + ExtensionType::ClientCertificateType => + pub(crate) client_certificate_type: Option<CertificateType>, + + /// Selected server certificate type (RFC7250) + ExtensionType::ServerCertificateType => + pub(crate) server_certificate_type: Option<CertificateType>, + + /// Extended master secret is in use (RFC7627) + ExtensionType::ExtendedMasterSecret => + pub(crate) extended_master_secret_ack: Option<()>, + + /// Certificate status acknowledgement (RFC6066) + ExtensionType::StatusRequest => + pub(crate) certificate_status_request_ack: Option<()>, + + /// Selected TLS version (RFC8446) + ExtensionType::SupportedVersions => + pub(crate) selected_version: Option<ProtocolVersion>, + + /// QUIC transport parameters (RFC9001) + ExtensionType::TransportParameters => + pub(crate) transport_parameters: Option<Payload<'a>>, + + /// QUIC transport parameters (RFC9001 prior to draft 33) + ExtensionType::TransportParametersDraft => + pub(crate) transport_parameters_draft: Option<Payload<'a>>, + + /// Early data is accepted (RFC8446) + ExtensionType::EarlyData => + pub(crate) early_data_ack: Option<()>, + + /// Encrypted inner client hello response (draft-ietf-tls-esni) + ExtensionType::EncryptedClientHello => + pub(crate) encrypted_client_hello_ack: Option<ServerEncryptedClientHello>, + } + { + pub(crate) unknown_extensions: BTreeSet<u16>, + } +} + +impl ServerExtensions<'_> { + fn into_owned(self) -> ServerExtensions<'static> { + let Self { + ec_point_formats, + server_name_ack, + session_ticket_ack, + renegotiation_info, + selected_protocol, + key_share, + preshared_key, + client_certificate_type, + server_certificate_type, + extended_master_secret_ack, + certificate_status_request_ack, + selected_version, + transport_parameters, + transport_parameters_draft, + early_data_ack, + encrypted_client_hello_ack, + unknown_extensions, + } = self; + ServerExtensions { + ec_point_formats, + server_name_ack, + session_ticket_ack, + renegotiation_info, + selected_protocol, + key_share, + preshared_key, + client_certificate_type, + server_certificate_type, + extended_master_secret_ack, + certificate_status_request_ack, + selected_version, + transport_parameters: transport_parameters.map(|x| x.into_owned()), + transport_parameters_draft: transport_parameters_draft.map(|x| x.into_owned()), + early_data_ack, + encrypted_client_hello_ack, + unknown_extensions, + } + } +} + +impl<'a> Codec<'a> for ServerExtensions<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + let extensions = LengthPrefixedBuffer::new(ListLength::U16, bytes); + + for ext in Self::ALL_EXTENSIONS { + self.encode_one(*ext, extensions.buf); + } + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let mut out = Self::default(); + let mut checker = DuplicateExtensionChecker::new(); + + let len = usize::from(u16::read(r)?); + let mut sub = r.sub(len)?; + + while sub.any_left() { + out.read_one(&mut sub, |unknown| checker.check(unknown))?; + } + + out.unknown_extensions = checker.0; + Ok(out) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct ClientHelloPayload { + pub(crate) client_version: ProtocolVersion, + pub(crate) random: Random, + pub(crate) session_id: SessionId, + pub(crate) cipher_suites: Vec<CipherSuite>, + pub(crate) compression_methods: Vec<Compression>, + pub(crate) extensions: Box<ClientExtensions<'static>>, +} + +impl ClientHelloPayload { + pub(crate) fn ech_inner_encoding(&self, to_compress: Vec<ExtensionType>) -> Vec<u8> { + let mut bytes = Vec::new(); + self.payload_encode(&mut bytes, Encoding::EchInnerHello { to_compress }); + bytes + } + + pub(crate) fn payload_encode(&self, bytes: &mut Vec<u8>, purpose: Encoding) { + self.client_version.encode(bytes); + self.random.encode(bytes); + + match purpose { + // SessionID is required to be empty in the encoded inner client hello. + Encoding::EchInnerHello { .. } => SessionId::empty().encode(bytes), + _ => self.session_id.encode(bytes), + } + + self.cipher_suites.encode(bytes); + self.compression_methods.encode(bytes); + + let to_compress = match purpose { + Encoding::EchInnerHello { to_compress } if !to_compress.is_empty() => to_compress, + _ => { + self.extensions.encode(bytes); + return; + } + }; + + let mut compressed = self.extensions.clone(); + + // First, eliminate the full-fat versions of the extensions + for e in &to_compress { + compressed.clear(*e); + } + + // Replace with the marker noting which extensions were elided. + compressed.encrypted_client_hello_outer = Some(to_compress); + + // And encode as normal. + compressed.encode(bytes); + } + + pub(crate) fn has_keyshare_extension_with_duplicates(&self) -> bool { + self.key_shares + .as_ref() + .map(|entries| { + has_duplicates::<_, _, u16>( + entries + .iter() + .map(|kse| u16::from(kse.group)), + ) + }) + .unwrap_or_default() + } + + pub(crate) fn has_certificate_compression_extension_with_duplicates(&self) -> bool { + if let Some(algs) = &self.certificate_compression_algorithms { + has_duplicates::<_, _, u16>(algs.iter().cloned()) + } else { + false + } + } +} + +impl Codec<'_> for ClientHelloPayload { + fn encode(&self, bytes: &mut Vec<u8>) { + self.payload_encode(bytes, Encoding::Standard) + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let ret = Self { + client_version: ProtocolVersion::read(r)?, + random: Random::read(r)?, + session_id: SessionId::read(r)?, + cipher_suites: Vec::read(r)?, + compression_methods: Vec::read(r)?, + extensions: Box::new(ClientExtensions::read(r)?.into_owned()), + }; + + match r.any_left() { + true => Err(InvalidMessage::TrailingData("ClientHelloPayload")), + false => Ok(ret), + } + } +} + +impl Deref for ClientHelloPayload { + type Target = ClientExtensions<'static>; + fn deref(&self) -> &Self::Target { + &self.extensions + } +} + +impl DerefMut for ClientHelloPayload { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.extensions + } +} + +/// RFC8446: `CipherSuite cipher_suites<2..2^16-2>;` +impl TlsListElement for CipherSuite { + const SIZE_LEN: ListLength = ListLength::NonZeroU16 { + empty_error: InvalidMessage::IllegalEmptyList("CipherSuites"), + }; +} + +/// RFC5246: `CompressionMethod compression_methods<1..2^8-1>;` +impl TlsListElement for Compression { + const SIZE_LEN: ListLength = ListLength::NonZeroU8 { + empty_error: InvalidMessage::IllegalEmptyList("Compressions"), + }; +} + +/// draft-ietf-tls-esni-17: `ExtensionType OuterExtensions<2..254>;` +impl TlsListElement for ExtensionType { + const SIZE_LEN: ListLength = ListLength::NonZeroU8 { + empty_error: InvalidMessage::IllegalEmptyList("ExtensionTypes"), + }; +} + +extension_struct! { + /// A representation of extensions present in a `HelloRetryRequest` message + pub(crate) struct HelloRetryRequestExtensions<'a> { + ExtensionType::KeyShare => + pub(crate) key_share: Option<NamedGroup>, + + ExtensionType::Cookie => + pub(crate) cookie: Option<PayloadU16<NonEmpty>>, + + ExtensionType::SupportedVersions => + pub(crate) supported_versions: Option<ProtocolVersion>, + + ExtensionType::EncryptedClientHello => + pub(crate) encrypted_client_hello: Option<Payload<'a>>, + } + { + /// Records decoding order of records, and controls encoding order. + pub(crate) order: Option<Vec<ExtensionType>>, + } +} + +impl HelloRetryRequestExtensions<'_> { + fn into_owned(self) -> HelloRetryRequestExtensions<'static> { + let Self { + key_share, + cookie, + supported_versions, + encrypted_client_hello, + order, + } = self; + HelloRetryRequestExtensions { + key_share, + cookie, + supported_versions, + encrypted_client_hello: encrypted_client_hello.map(|x| x.into_owned()), + order, + } + } +} + +impl<'a> Codec<'a> for HelloRetryRequestExtensions<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + let extensions = LengthPrefixedBuffer::new(ListLength::U16, bytes); + + for ext in self + .order + .as_deref() + .unwrap_or(Self::ALL_EXTENSIONS) + { + self.encode_one(*ext, extensions.buf); + } + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let mut out = Self::default(); + + // we must record order, so re-encoding round trips. this is needed, + // unfortunately, for ECH HRR confirmation + let mut order = vec![]; + + let len = usize::from(u16::read(r)?); + let mut sub = r.sub(len)?; + + while sub.any_left() { + let typ = out.read_one(&mut sub, |_unk| { + Err(InvalidMessage::UnknownHelloRetryRequestExtension) + })?; + + order.push(typ); + } + + out.order = Some(order); + Ok(out) + } +} + +#[derive(Clone, Debug)] +pub(crate) struct HelloRetryRequest { + pub(crate) legacy_version: ProtocolVersion, + pub(crate) session_id: SessionId, + pub(crate) cipher_suite: CipherSuite, + pub(crate) extensions: HelloRetryRequestExtensions<'static>, +} + +impl Codec<'_> for HelloRetryRequest { + fn encode(&self, bytes: &mut Vec<u8>) { + self.payload_encode(bytes, Encoding::Standard) + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let session_id = SessionId::read(r)?; + let cipher_suite = CipherSuite::read(r)?; + let compression = Compression::read(r)?; + + if compression != Compression::Null { + return Err(InvalidMessage::UnsupportedCompression); + } + + Ok(Self { + legacy_version: ProtocolVersion::Unknown(0), + session_id, + cipher_suite, + extensions: HelloRetryRequestExtensions::read(r)?.into_owned(), + }) + } +} + +impl HelloRetryRequest { + fn payload_encode(&self, bytes: &mut Vec<u8>, purpose: Encoding) { + self.legacy_version.encode(bytes); + HELLO_RETRY_REQUEST_RANDOM.encode(bytes); + self.session_id.encode(bytes); + self.cipher_suite.encode(bytes); + Compression::Null.encode(bytes); + + match purpose { + // For the purpose of ECH confirmation, the Encrypted Client Hello extension + // must have its payload replaced by 8 zero bytes. + // + // See draft-ietf-tls-esni-18 7.2.1: + // <https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#name-sending-helloretryrequest-2> + Encoding::EchConfirmation + if self + .extensions + .encrypted_client_hello + .is_some() => + { + let hrr_confirmation = [0u8; 8]; + HelloRetryRequestExtensions { + encrypted_client_hello: Some(Payload::Borrowed(&hrr_confirmation)), + ..self.extensions.clone() + } + .encode(bytes); + } + _ => self.extensions.encode(bytes), + } + } +} + +impl Deref for HelloRetryRequest { + type Target = HelloRetryRequestExtensions<'static>; + fn deref(&self) -> &Self::Target { + &self.extensions + } +} + +impl DerefMut for HelloRetryRequest { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.extensions + } +} + +#[derive(Clone, Debug)] +pub(crate) struct ServerHelloPayload { + pub(crate) legacy_version: ProtocolVersion, + pub(crate) random: Random, + pub(crate) session_id: SessionId, + pub(crate) cipher_suite: CipherSuite, + pub(crate) compression_method: Compression, + pub(crate) extensions: Box<ServerExtensions<'static>>, +} + +impl Codec<'_> for ServerHelloPayload { + fn encode(&self, bytes: &mut Vec<u8>) { + self.payload_encode(bytes, Encoding::Standard) + } + + // minus version and random, which have already been read. + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let session_id = SessionId::read(r)?; + let suite = CipherSuite::read(r)?; + let compression = Compression::read(r)?; + + // RFC5246: + // "The presence of extensions can be detected by determining whether + // there are bytes following the compression_method field at the end of + // the ServerHello." + let extensions = Box::new( + if r.any_left() { + ServerExtensions::read(r)? + } else { + ServerExtensions::default() + } + .into_owned(), + ); + + let ret = Self { + legacy_version: ProtocolVersion::Unknown(0), + random: ZERO_RANDOM, + session_id, + cipher_suite: suite, + compression_method: compression, + extensions, + }; + + r.expect_empty("ServerHelloPayload") + .map(|_| ret) + } +} + +impl ServerHelloPayload { + fn payload_encode(&self, bytes: &mut Vec<u8>, encoding: Encoding) { + debug_assert!( + !matches!(encoding, Encoding::EchConfirmation), + "we cannot compute an ECH confirmation on a received ServerHello" + ); + + self.legacy_version.encode(bytes); + self.random.encode(bytes); + self.session_id.encode(bytes); + self.cipher_suite.encode(bytes); + self.compression_method.encode(bytes); + self.extensions.encode(bytes); + } +} + +impl Deref for ServerHelloPayload { + type Target = ServerExtensions<'static>; + fn deref(&self) -> &Self::Target { + &self.extensions + } +} + +impl DerefMut for ServerHelloPayload { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.extensions + } +} + +#[derive(Clone, Default, Debug)] +pub(crate) struct CertificateChain<'a>(pub(crate) Vec<CertificateDer<'a>>); + +impl CertificateChain<'_> { + pub(crate) fn into_owned(self) -> CertificateChain<'static> { + CertificateChain( + self.0 + .into_iter() + .map(|c| c.into_owned()) + .collect(), + ) + } +} + +impl<'a> Codec<'a> for CertificateChain<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + Vec::encode(&self.0, bytes) + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + Vec::read(r).map(Self) + } +} + +impl<'a> Deref for CertificateChain<'a> { + type Target = [CertificateDer<'a>]; + + fn deref(&self) -> &[CertificateDer<'a>] { + &self.0 + } +} + +impl TlsListElement for CertificateDer<'_> { + const SIZE_LEN: ListLength = ListLength::U24 { + max: CERTIFICATE_MAX_SIZE_LIMIT, + error: InvalidMessage::CertificatePayloadTooLarge, + }; +} + +/// TLS has a 16MB size limit on any handshake message, +/// plus a 16MB limit on any given certificate. +/// +/// We contract that to 64KB to limit the amount of memory allocation +/// that is directly controllable by the peer. +pub(crate) const CERTIFICATE_MAX_SIZE_LIMIT: usize = 0x1_0000; + +extension_struct! { + pub(crate) struct CertificateExtensions<'a> { + ExtensionType::StatusRequest => + pub(crate) status: Option<CertificateStatus<'a>>, + } +} + +impl CertificateExtensions<'_> { + fn into_owned(self) -> CertificateExtensions<'static> { + CertificateExtensions { + status: self.status.map(|s| s.into_owned()), + } + } +} + +impl<'a> Codec<'a> for CertificateExtensions<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + let extensions = LengthPrefixedBuffer::new(ListLength::U16, bytes); + + for ext in Self::ALL_EXTENSIONS { + self.encode_one(*ext, extensions.buf); + } + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let mut out = Self::default(); + + let len = usize::from(u16::read(r)?); + let mut sub = r.sub(len)?; + + while sub.any_left() { + out.read_one(&mut sub, |_unk| { + Err(InvalidMessage::UnknownCertificateExtension) + })?; + } + + Ok(out) + } +} + +#[derive(Debug)] +pub(crate) struct CertificateEntry<'a> { + pub(crate) cert: CertificateDer<'a>, + pub(crate) extensions: CertificateExtensions<'a>, +} + +impl<'a> Codec<'a> for CertificateEntry<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + self.cert.encode(bytes); + self.extensions.encode(bytes); + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + Ok(Self { + cert: CertificateDer::read(r)?, + extensions: CertificateExtensions::read(r)?.into_owned(), + }) + } +} + +impl<'a> CertificateEntry<'a> { + pub(crate) fn new(cert: CertificateDer<'a>) -> Self { + Self { + cert, + extensions: CertificateExtensions::default(), + } + } + + pub(crate) fn into_owned(self) -> CertificateEntry<'static> { + CertificateEntry { + cert: self.cert.into_owned(), + extensions: self.extensions.into_owned(), + } + } +} + +impl TlsListElement for CertificateEntry<'_> { + const SIZE_LEN: ListLength = ListLength::U24 { + max: CERTIFICATE_MAX_SIZE_LIMIT, + error: InvalidMessage::CertificatePayloadTooLarge, + }; +} + +#[derive(Debug)] +pub(crate) struct CertificatePayloadTls13<'a> { + pub(crate) context: PayloadU8, + pub(crate) entries: Vec<CertificateEntry<'a>>, +} + +impl<'a> Codec<'a> for CertificatePayloadTls13<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + self.context.encode(bytes); + self.entries.encode(bytes); + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + Ok(Self { + context: PayloadU8::read(r)?, + entries: Vec::read(r)?, + }) + } +} + +impl<'a> CertificatePayloadTls13<'a> { + pub(crate) fn new( + certs: impl Iterator<Item = &'a CertificateDer<'a>>, + ocsp_response: Option<&'a [u8]>, + ) -> Self { + Self { + context: PayloadU8::empty(), + entries: certs + // zip certificate iterator with `ocsp_response` followed by + // an infinite-length iterator of `None`. + .zip( + ocsp_response + .into_iter() + .map(Some) + .chain(iter::repeat(None)), + ) + .map(|(cert, ocsp)| { + let mut e = CertificateEntry::new(cert.clone()); + if let Some(ocsp) = ocsp { + e.extensions.status = Some(CertificateStatus::new(ocsp)); + } + e + }) + .collect(), + } + } + + pub(crate) fn into_owned(self) -> CertificatePayloadTls13<'static> { + CertificatePayloadTls13 { + context: self.context, + entries: self + .entries + .into_iter() + .map(CertificateEntry::into_owned) + .collect(), + } + } + + pub(crate) fn end_entity_ocsp(&self) -> Vec<u8> { + let Some(entry) = self.entries.first() else { + return vec![]; + }; + entry + .extensions + .status + .as_ref() + .map(|status| { + status + .ocsp_response + .0 + .clone() + .into_vec() + }) + .unwrap_or_default() + } + + pub(crate) fn into_certificate_chain(self) -> CertificateChain<'a> { + CertificateChain( + self.entries + .into_iter() + .map(|e| e.cert) + .collect(), + ) + } +} + +/// Describes supported key exchange mechanisms. +#[derive(Clone, Copy, Debug, PartialEq)] +#[non_exhaustive] +pub enum KeyExchangeAlgorithm { + /// Diffie-Hellman Key exchange (with only known parameters as defined in [RFC 7919]). + /// + /// [RFC 7919]: https://datatracker.ietf.org/doc/html/rfc7919 + DHE, + /// Key exchange performed via elliptic curve Diffie-Hellman. + ECDHE, +} + +pub(crate) static ALL_KEY_EXCHANGE_ALGORITHMS: &[KeyExchangeAlgorithm] = + &[KeyExchangeAlgorithm::ECDHE, KeyExchangeAlgorithm::DHE]; + +// We don't support arbitrary curves. It's a terrible +// idea and unnecessary attack surface. Please, +// get a grip. +#[derive(Debug)] +pub(crate) struct EcParameters { + pub(crate) curve_type: ECCurveType, + pub(crate) named_group: NamedGroup, +} + +impl Codec<'_> for EcParameters { + fn encode(&self, bytes: &mut Vec<u8>) { + self.curve_type.encode(bytes); + self.named_group.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let ct = ECCurveType::read(r)?; + if ct != ECCurveType::NamedCurve { + return Err(InvalidMessage::UnsupportedCurveType); + } + + let grp = NamedGroup::read(r)?; + + Ok(Self { + curve_type: ct, + named_group: grp, + }) + } +} + +#[cfg(feature = "tls12")] +pub(crate) trait KxDecode<'a>: fmt::Debug + Sized { + /// Decode a key exchange message given the key_exchange `algo` + fn decode(r: &mut Reader<'a>, algo: KeyExchangeAlgorithm) -> Result<Self, InvalidMessage>; +} + +#[cfg(feature = "tls12")] +#[derive(Debug)] +pub(crate) enum ClientKeyExchangeParams { + Ecdh(ClientEcdhParams), + Dh(ClientDhParams), +} + +#[cfg(feature = "tls12")] +impl ClientKeyExchangeParams { + pub(crate) fn pub_key(&self) -> &[u8] { + match self { + Self::Ecdh(ecdh) => &ecdh.public.0, + Self::Dh(dh) => &dh.public.0, + } + } + + pub(crate) fn encode(&self, buf: &mut Vec<u8>) { + match self { + Self::Ecdh(ecdh) => ecdh.encode(buf), + Self::Dh(dh) => dh.encode(buf), + } + } +} + +#[cfg(feature = "tls12")] +impl KxDecode<'_> for ClientKeyExchangeParams { + fn decode(r: &mut Reader<'_>, algo: KeyExchangeAlgorithm) -> Result<Self, InvalidMessage> { + use KeyExchangeAlgorithm::*; + Ok(match algo { + ECDHE => Self::Ecdh(ClientEcdhParams::read(r)?), + DHE => Self::Dh(ClientDhParams::read(r)?), + }) + } +} + +#[cfg(feature = "tls12")] +#[derive(Debug)] +pub(crate) struct ClientEcdhParams { + /// RFC4492: `opaque point <1..2^8-1>;` + pub(crate) public: PayloadU8<NonEmpty>, +} + +#[cfg(feature = "tls12")] +impl Codec<'_> for ClientEcdhParams { + fn encode(&self, bytes: &mut Vec<u8>) { + self.public.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let pb = PayloadU8::read(r)?; + Ok(Self { public: pb }) + } +} + +#[cfg(feature = "tls12")] +#[derive(Debug)] +pub(crate) struct ClientDhParams { + /// RFC5246: `opaque dh_Yc<1..2^16-1>;` + pub(crate) public: PayloadU16<NonEmpty>, +} + +#[cfg(feature = "tls12")] +impl Codec<'_> for ClientDhParams { + fn encode(&self, bytes: &mut Vec<u8>) { + self.public.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + public: PayloadU16::read(r)?, + }) + } +} + +#[derive(Debug)] +pub(crate) struct ServerEcdhParams { + pub(crate) curve_params: EcParameters, + /// RFC4492: `opaque point <1..2^8-1>;` + pub(crate) public: PayloadU8<NonEmpty>, +} + +impl ServerEcdhParams { + #[cfg(feature = "tls12")] + pub(crate) fn new(kx: &dyn ActiveKeyExchange) -> Self { + Self { + curve_params: EcParameters { + curve_type: ECCurveType::NamedCurve, + named_group: kx.group(), + }, + public: PayloadU8::new(kx.pub_key().to_vec()), + } + } +} + +impl Codec<'_> for ServerEcdhParams { + fn encode(&self, bytes: &mut Vec<u8>) { + self.curve_params.encode(bytes); + self.public.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let cp = EcParameters::read(r)?; + let pb = PayloadU8::read(r)?; + + Ok(Self { + curve_params: cp, + public: pb, + }) + } +} + +#[derive(Debug)] +#[allow(non_snake_case)] +pub(crate) struct ServerDhParams { + /// RFC5246: `opaque dh_p<1..2^16-1>;` + pub(crate) dh_p: PayloadU16<NonEmpty>, + /// RFC5246: `opaque dh_g<1..2^16-1>;` + pub(crate) dh_g: PayloadU16<NonEmpty>, + /// RFC5246: `opaque dh_Ys<1..2^16-1>;` + pub(crate) dh_Ys: PayloadU16<NonEmpty>, +} + +impl ServerDhParams { + #[cfg(feature = "tls12")] + pub(crate) fn new(kx: &dyn ActiveKeyExchange) -> Self { + let Some(params) = kx.ffdhe_group() else { + panic!("invalid NamedGroup for DHE key exchange: {:?}", kx.group()); + }; + + Self { + dh_p: PayloadU16::new(params.p.to_vec()), + dh_g: PayloadU16::new(params.g.to_vec()), + dh_Ys: PayloadU16::new(kx.pub_key().to_vec()), + } + } + + #[cfg(feature = "tls12")] + pub(crate) fn as_ffdhe_group(&self) -> FfdheGroup<'_> { + FfdheGroup::from_params_trimming_leading_zeros(&self.dh_p.0, &self.dh_g.0) + } +} + +impl Codec<'_> for ServerDhParams { + fn encode(&self, bytes: &mut Vec<u8>) { + self.dh_p.encode(bytes); + self.dh_g.encode(bytes); + self.dh_Ys.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + dh_p: PayloadU16::read(r)?, + dh_g: PayloadU16::read(r)?, + dh_Ys: PayloadU16::read(r)?, + }) + } +} + +#[allow(dead_code)] +#[derive(Debug)] +pub(crate) enum ServerKeyExchangeParams { + Ecdh(ServerEcdhParams), + Dh(ServerDhParams), +} + +impl ServerKeyExchangeParams { + #[cfg(feature = "tls12")] + pub(crate) fn new(kx: &dyn ActiveKeyExchange) -> Self { + match kx.group().key_exchange_algorithm() { + KeyExchangeAlgorithm::DHE => Self::Dh(ServerDhParams::new(kx)), + KeyExchangeAlgorithm::ECDHE => Self::Ecdh(ServerEcdhParams::new(kx)), + } + } + + #[cfg(feature = "tls12")] + pub(crate) fn pub_key(&self) -> &[u8] { + match self { + Self::Ecdh(ecdh) => &ecdh.public.0, + Self::Dh(dh) => &dh.dh_Ys.0, + } + } + + pub(crate) fn encode(&self, buf: &mut Vec<u8>) { + match self { + Self::Ecdh(ecdh) => ecdh.encode(buf), + Self::Dh(dh) => dh.encode(buf), + } + } +} + +#[cfg(feature = "tls12")] +impl KxDecode<'_> for ServerKeyExchangeParams { + fn decode(r: &mut Reader<'_>, algo: KeyExchangeAlgorithm) -> Result<Self, InvalidMessage> { + use KeyExchangeAlgorithm::*; + Ok(match algo { + ECDHE => Self::Ecdh(ServerEcdhParams::read(r)?), + DHE => Self::Dh(ServerDhParams::read(r)?), + }) + } +} + +#[derive(Debug)] +pub(crate) struct ServerKeyExchange { + pub(crate) params: ServerKeyExchangeParams, + pub(crate) dss: DigitallySignedStruct, +} + +impl ServerKeyExchange { + pub(crate) fn encode(&self, buf: &mut Vec<u8>) { + self.params.encode(buf); + self.dss.encode(buf); + } +} + +#[derive(Debug)] +pub(crate) enum ServerKeyExchangePayload { + Known(ServerKeyExchange), + Unknown(Payload<'static>), +} + +impl From<ServerKeyExchange> for ServerKeyExchangePayload { + fn from(value: ServerKeyExchange) -> Self { + Self::Known(value) + } +} + +impl Codec<'_> for ServerKeyExchangePayload { + fn encode(&self, bytes: &mut Vec<u8>) { + match self { + Self::Known(x) => x.encode(bytes), + Self::Unknown(x) => x.encode(bytes), + } + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + // read as Unknown, fully parse when we know the + // KeyExchangeAlgorithm + Ok(Self::Unknown(Payload::read(r).into_owned())) + } +} + +impl ServerKeyExchangePayload { + #[cfg(feature = "tls12")] + pub(crate) fn unwrap_given_kxa(&self, kxa: KeyExchangeAlgorithm) -> Option<ServerKeyExchange> { + if let Self::Unknown(unk) = self { + let mut rd = Reader::init(unk.bytes()); + + let result = ServerKeyExchange { + params: ServerKeyExchangeParams::decode(&mut rd, kxa).ok()?, + dss: DigitallySignedStruct::read(&mut rd).ok()?, + }; + + if !rd.any_left() { + return Some(result); + }; + } + + None + } +} + +/// RFC5246: `ClientCertificateType certificate_types<1..2^8-1>;` +impl TlsListElement for ClientCertificateType { + const SIZE_LEN: ListLength = ListLength::NonZeroU8 { + empty_error: InvalidMessage::IllegalEmptyList("ClientCertificateTypes"), + }; +} + +wrapped_payload!( + /// A `DistinguishedName` is a `Vec<u8>` wrapped in internal types. + /// + /// It contains the DER or BER encoded [`Subject` field from RFC 5280](https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6) + /// for a single certificate. The Subject field is [encoded as an RFC 5280 `Name`](https://datatracker.ietf.org/doc/html/rfc5280#page-116). + /// It can be decoded using [x509-parser's FromDer trait](https://docs.rs/x509-parser/latest/x509_parser/prelude/trait.FromDer.html). + /// + /// ```ignore + /// for name in distinguished_names { + /// use x509_parser::prelude::FromDer; + /// println!("{}", x509_parser::x509::X509Name::from_der(&name.0)?.1); + /// } + /// ``` + /// + /// The TLS encoding is defined in RFC5246: `opaque DistinguishedName<1..2^16-1>;` + pub struct DistinguishedName, + PayloadU16<NonEmpty>, +); + +impl DistinguishedName { + /// Create a [`DistinguishedName`] after prepending its outer SEQUENCE encoding. + /// + /// This can be decoded using [x509-parser's FromDer trait](https://docs.rs/x509-parser/latest/x509_parser/prelude/trait.FromDer.html). + /// + /// ```ignore + /// use x509_parser::prelude::FromDer; + /// println!("{}", x509_parser::x509::X509Name::from_der(dn.as_ref())?.1); + /// ``` + pub fn in_sequence(bytes: &[u8]) -> Self { + Self(PayloadU16::new(wrap_in_sequence(bytes))) + } +} + +/// RFC8446: `DistinguishedName authorities<3..2^16-1>;` however, +/// RFC5246: `DistinguishedName certificate_authorities<0..2^16-1>;` +impl TlsListElement for DistinguishedName { + const SIZE_LEN: ListLength = ListLength::U16; +} + +#[derive(Debug)] +pub(crate) struct CertificateRequestPayload { + pub(crate) certtypes: Vec<ClientCertificateType>, + pub(crate) sigschemes: Vec<SignatureScheme>, + pub(crate) canames: Vec<DistinguishedName>, +} + +impl Codec<'_> for CertificateRequestPayload { + fn encode(&self, bytes: &mut Vec<u8>) { + self.certtypes.encode(bytes); + self.sigschemes.encode(bytes); + self.canames.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let certtypes = Vec::read(r)?; + let sigschemes = Vec::read(r)?; + let canames = Vec::read(r)?; + + if sigschemes.is_empty() { + warn!("meaningless CertificateRequest message"); + Err(InvalidMessage::NoSignatureSchemes) + } else { + Ok(Self { + certtypes, + sigschemes, + canames, + }) + } + } +} + +extension_struct! { + pub(crate) struct CertificateRequestExtensions { + ExtensionType::SignatureAlgorithms => + pub(crate) signature_algorithms: Option<Vec<SignatureScheme>>, + + ExtensionType::CertificateAuthorities => + pub(crate) authority_names: Option<Vec<DistinguishedName>>, + + ExtensionType::CompressCertificate => + pub(crate) certificate_compression_algorithms: Option<Vec<CertificateCompressionAlgorithm>>, + } +} + +impl Codec<'_> for CertificateRequestExtensions { + fn encode(&self, bytes: &mut Vec<u8>) { + let extensions = LengthPrefixedBuffer::new(ListLength::U16, bytes); + + for ext in Self::ALL_EXTENSIONS { + self.encode_one(*ext, extensions.buf); + } + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let mut out = Self::default(); + + let mut checker = DuplicateExtensionChecker::new(); + + let len = usize::from(u16::read(r)?); + let mut sub = r.sub(len)?; + + while sub.any_left() { + out.read_one(&mut sub, |unknown| checker.check(unknown))?; + } + + if out + .signature_algorithms + .as_ref() + .map(|algs| algs.is_empty()) + .unwrap_or_default() + { + return Err(InvalidMessage::NoSignatureSchemes); + } + + Ok(out) + } +} + +#[derive(Debug)] +pub(crate) struct CertificateRequestPayloadTls13 { + pub(crate) context: PayloadU8, + pub(crate) extensions: CertificateRequestExtensions, +} + +impl Codec<'_> for CertificateRequestPayloadTls13 { + fn encode(&self, bytes: &mut Vec<u8>) { + self.context.encode(bytes); + self.extensions.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let context = PayloadU8::read(r)?; + let extensions = CertificateRequestExtensions::read(r)?; + + Ok(Self { + context, + extensions, + }) + } +} + +// -- NewSessionTicket -- +#[derive(Debug)] +pub(crate) struct NewSessionTicketPayload { + pub(crate) lifetime_hint: u32, + // Tickets can be large (KB), so we deserialise this straight + // into an Arc, so it can be passed directly into the client's + // session object without copying. + pub(crate) ticket: Arc<PayloadU16>, +} + +impl NewSessionTicketPayload { + #[cfg(feature = "tls12")] + pub(crate) fn new(lifetime_hint: u32, ticket: Vec<u8>) -> Self { + Self { + lifetime_hint, + ticket: Arc::new(PayloadU16::new(ticket)), + } + } +} + +impl Codec<'_> for NewSessionTicketPayload { + fn encode(&self, bytes: &mut Vec<u8>) { + self.lifetime_hint.encode(bytes); + self.ticket.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let lifetime = u32::read(r)?; + let ticket = Arc::new(PayloadU16::read(r)?); + + Ok(Self { + lifetime_hint: lifetime, + ticket, + }) + } +} + +// -- NewSessionTicket electric boogaloo -- +extension_struct! { + pub(crate) struct NewSessionTicketExtensions { + ExtensionType::EarlyData => + pub(crate) max_early_data_size: Option<u32>, + } +} + +impl Codec<'_> for NewSessionTicketExtensions { + fn encode(&self, bytes: &mut Vec<u8>) { + let extensions = LengthPrefixedBuffer::new(ListLength::U16, bytes); + + for ext in Self::ALL_EXTENSIONS { + self.encode_one(*ext, extensions.buf); + } + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let mut out = Self::default(); + + let mut checker = DuplicateExtensionChecker::new(); + + let len = usize::from(u16::read(r)?); + let mut sub = r.sub(len)?; + + while sub.any_left() { + out.read_one(&mut sub, |unknown| checker.check(unknown))?; + } + + Ok(out) + } +} + +#[derive(Debug)] +pub(crate) struct NewSessionTicketPayloadTls13 { + pub(crate) lifetime: u32, + pub(crate) age_add: u32, + pub(crate) nonce: PayloadU8, + pub(crate) ticket: Arc<PayloadU16>, + pub(crate) extensions: NewSessionTicketExtensions, +} + +impl NewSessionTicketPayloadTls13 { + pub(crate) fn new(lifetime: u32, age_add: u32, nonce: Vec<u8>, ticket: Vec<u8>) -> Self { + Self { + lifetime, + age_add, + nonce: PayloadU8::new(nonce), + ticket: Arc::new(PayloadU16::new(ticket)), + extensions: NewSessionTicketExtensions::default(), + } + } +} + +impl Codec<'_> for NewSessionTicketPayloadTls13 { + fn encode(&self, bytes: &mut Vec<u8>) { + self.lifetime.encode(bytes); + self.age_add.encode(bytes); + self.nonce.encode(bytes); + self.ticket.encode(bytes); + self.extensions.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let lifetime = u32::read(r)?; + let age_add = u32::read(r)?; + let nonce = PayloadU8::read(r)?; + // nb. RFC8446: `opaque ticket<1..2^16-1>;` + let ticket = Arc::new(match PayloadU16::<NonEmpty>::read(r) { + Err(InvalidMessage::IllegalEmptyValue) => Err(InvalidMessage::EmptyTicketValue), + Err(err) => Err(err), + Ok(pl) => Ok(PayloadU16::new(pl.0)), + }?); + let extensions = NewSessionTicketExtensions::read(r)?; + + Ok(Self { + lifetime, + age_add, + nonce, + ticket, + extensions, + }) + } +} + +// -- RFC6066 certificate status types + +/// Only supports OCSP +#[derive(Clone, Debug)] +pub(crate) struct CertificateStatus<'a> { + pub(crate) ocsp_response: PayloadU24<'a>, +} + +impl<'a> Codec<'a> for CertificateStatus<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + CertificateStatusType::OCSP.encode(bytes); + self.ocsp_response.encode(bytes); + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + let typ = CertificateStatusType::read(r)?; + + match typ { + CertificateStatusType::OCSP => Ok(Self { + ocsp_response: PayloadU24::read(r)?, + }), + _ => Err(InvalidMessage::InvalidCertificateStatusType), + } + } +} + +impl<'a> CertificateStatus<'a> { + pub(crate) fn new(ocsp: &'a [u8]) -> Self { + CertificateStatus { + ocsp_response: PayloadU24(Payload::Borrowed(ocsp)), + } + } + + #[cfg(feature = "tls12")] + pub(crate) fn into_inner(self) -> Vec<u8> { + self.ocsp_response.0.into_vec() + } + + pub(crate) fn into_owned(self) -> CertificateStatus<'static> { + CertificateStatus { + ocsp_response: self.ocsp_response.into_owned(), + } + } +} + +// -- RFC8879 compressed certificates + +#[derive(Debug)] +pub(crate) struct CompressedCertificatePayload<'a> { + pub(crate) alg: CertificateCompressionAlgorithm, + pub(crate) uncompressed_len: u32, + pub(crate) compressed: PayloadU24<'a>, +} + +impl<'a> Codec<'a> for CompressedCertificatePayload<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + self.alg.encode(bytes); + codec::u24(self.uncompressed_len).encode(bytes); + self.compressed.encode(bytes); + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + Ok(Self { + alg: CertificateCompressionAlgorithm::read(r)?, + uncompressed_len: codec::u24::read(r)?.0, + compressed: PayloadU24::read(r)?, + }) + } +} + +impl CompressedCertificatePayload<'_> { + fn into_owned(self) -> CompressedCertificatePayload<'static> { + CompressedCertificatePayload { + compressed: self.compressed.into_owned(), + ..self + } + } + + pub(crate) fn as_borrowed(&self) -> CompressedCertificatePayload<'_> { + CompressedCertificatePayload { + alg: self.alg, + uncompressed_len: self.uncompressed_len, + compressed: PayloadU24(Payload::Borrowed(self.compressed.0.bytes())), + } + } +} + +#[derive(Debug)] +pub(crate) enum HandshakePayload<'a> { + HelloRequest, + ClientHello(ClientHelloPayload), + ServerHello(ServerHelloPayload), + HelloRetryRequest(HelloRetryRequest), + Certificate(CertificateChain<'a>), + CertificateTls13(CertificatePayloadTls13<'a>), + CompressedCertificate(CompressedCertificatePayload<'a>), + ServerKeyExchange(ServerKeyExchangePayload), + CertificateRequest(CertificateRequestPayload), + CertificateRequestTls13(CertificateRequestPayloadTls13), + CertificateVerify(DigitallySignedStruct), + ServerHelloDone, + EndOfEarlyData, + ClientKeyExchange(Payload<'a>), + NewSessionTicket(NewSessionTicketPayload), + NewSessionTicketTls13(NewSessionTicketPayloadTls13), + EncryptedExtensions(Box<ServerExtensions<'a>>), + KeyUpdate(KeyUpdateRequest), + Finished(Payload<'a>), + CertificateStatus(CertificateStatus<'a>), + MessageHash(Payload<'a>), + Unknown((HandshakeType, Payload<'a>)), +} + +impl HandshakePayload<'_> { + fn encode(&self, bytes: &mut Vec<u8>) { + use self::HandshakePayload::*; + match self { + HelloRequest | ServerHelloDone | EndOfEarlyData => {} + ClientHello(x) => x.encode(bytes), + ServerHello(x) => x.encode(bytes), + HelloRetryRequest(x) => x.encode(bytes), + Certificate(x) => x.encode(bytes), + CertificateTls13(x) => x.encode(bytes), + CompressedCertificate(x) => x.encode(bytes), + ServerKeyExchange(x) => x.encode(bytes), + ClientKeyExchange(x) => x.encode(bytes), + CertificateRequest(x) => x.encode(bytes), + CertificateRequestTls13(x) => x.encode(bytes), + CertificateVerify(x) => x.encode(bytes), + NewSessionTicket(x) => x.encode(bytes), + NewSessionTicketTls13(x) => x.encode(bytes), + EncryptedExtensions(x) => x.encode(bytes), + KeyUpdate(x) => x.encode(bytes), + Finished(x) => x.encode(bytes), + CertificateStatus(x) => x.encode(bytes), + MessageHash(x) => x.encode(bytes), + Unknown((_, x)) => x.encode(bytes), + } + } + + pub(crate) fn handshake_type(&self) -> HandshakeType { + use self::HandshakePayload::*; + match self { + HelloRequest => HandshakeType::HelloRequest, + ClientHello(_) => HandshakeType::ClientHello, + ServerHello(_) => HandshakeType::ServerHello, + HelloRetryRequest(_) => HandshakeType::HelloRetryRequest, + Certificate(_) | CertificateTls13(_) => HandshakeType::Certificate, + CompressedCertificate(_) => HandshakeType::CompressedCertificate, + ServerKeyExchange(_) => HandshakeType::ServerKeyExchange, + CertificateRequest(_) | CertificateRequestTls13(_) => HandshakeType::CertificateRequest, + CertificateVerify(_) => HandshakeType::CertificateVerify, + ServerHelloDone => HandshakeType::ServerHelloDone, + EndOfEarlyData => HandshakeType::EndOfEarlyData, + ClientKeyExchange(_) => HandshakeType::ClientKeyExchange, + NewSessionTicket(_) | NewSessionTicketTls13(_) => HandshakeType::NewSessionTicket, + EncryptedExtensions(_) => HandshakeType::EncryptedExtensions, + KeyUpdate(_) => HandshakeType::KeyUpdate, + Finished(_) => HandshakeType::Finished, + CertificateStatus(_) => HandshakeType::CertificateStatus, + MessageHash(_) => HandshakeType::MessageHash, + Unknown((t, _)) => *t, + } + } + + fn wire_handshake_type(&self) -> HandshakeType { + match self.handshake_type() { + // A `HelloRetryRequest` appears on the wire as a `ServerHello` with a magic `random` value. + HandshakeType::HelloRetryRequest => HandshakeType::ServerHello, + other => other, + } + } + + fn into_owned(self) -> HandshakePayload<'static> { + use HandshakePayload::*; + + match self { + HelloRequest => HelloRequest, + ClientHello(x) => ClientHello(x), + ServerHello(x) => ServerHello(x), + HelloRetryRequest(x) => HelloRetryRequest(x), + Certificate(x) => Certificate(x.into_owned()), + CertificateTls13(x) => CertificateTls13(x.into_owned()), + CompressedCertificate(x) => CompressedCertificate(x.into_owned()), + ServerKeyExchange(x) => ServerKeyExchange(x), + CertificateRequest(x) => CertificateRequest(x), + CertificateRequestTls13(x) => CertificateRequestTls13(x), + CertificateVerify(x) => CertificateVerify(x), + ServerHelloDone => ServerHelloDone, + EndOfEarlyData => EndOfEarlyData, + ClientKeyExchange(x) => ClientKeyExchange(x.into_owned()), + NewSessionTicket(x) => NewSessionTicket(x), + NewSessionTicketTls13(x) => NewSessionTicketTls13(x), + EncryptedExtensions(x) => EncryptedExtensions(Box::new(x.into_owned())), + KeyUpdate(x) => KeyUpdate(x), + Finished(x) => Finished(x.into_owned()), + CertificateStatus(x) => CertificateStatus(x.into_owned()), + MessageHash(x) => MessageHash(x.into_owned()), + Unknown((t, x)) => Unknown((t, x.into_owned())), + } + } +} + +#[derive(Debug)] +pub struct HandshakeMessagePayload<'a>(pub(crate) HandshakePayload<'a>); + +impl<'a> Codec<'a> for HandshakeMessagePayload<'a> { + fn encode(&self, bytes: &mut Vec<u8>) { + self.payload_encode(bytes, Encoding::Standard); + } + + fn read(r: &mut Reader<'a>) -> Result<Self, InvalidMessage> { + Self::read_version(r, ProtocolVersion::TLSv1_2) + } +} + +impl<'a> HandshakeMessagePayload<'a> { + pub(crate) fn read_version( + r: &mut Reader<'a>, + vers: ProtocolVersion, + ) -> Result<Self, InvalidMessage> { + let typ = HandshakeType::read(r)?; + let len = codec::u24::read(r)?.0 as usize; + let mut sub = r.sub(len)?; + + let payload = match typ { + HandshakeType::HelloRequest if sub.left() == 0 => HandshakePayload::HelloRequest, + HandshakeType::ClientHello => { + HandshakePayload::ClientHello(ClientHelloPayload::read(&mut sub)?) + } + HandshakeType::ServerHello => { + let version = ProtocolVersion::read(&mut sub)?; + let random = Random::read(&mut sub)?; + + if random == HELLO_RETRY_REQUEST_RANDOM { + let mut hrr = HelloRetryRequest::read(&mut sub)?; + hrr.legacy_version = version; + HandshakePayload::HelloRetryRequest(hrr) + } else { + let mut shp = ServerHelloPayload::read(&mut sub)?; + shp.legacy_version = version; + shp.random = random; + HandshakePayload::ServerHello(shp) + } + } + HandshakeType::Certificate if vers == ProtocolVersion::TLSv1_3 => { + let p = CertificatePayloadTls13::read(&mut sub)?; + HandshakePayload::CertificateTls13(p) + } + HandshakeType::Certificate => { + HandshakePayload::Certificate(CertificateChain::read(&mut sub)?) + } + HandshakeType::ServerKeyExchange => { + let p = ServerKeyExchangePayload::read(&mut sub)?; + HandshakePayload::ServerKeyExchange(p) + } + HandshakeType::ServerHelloDone => { + sub.expect_empty("ServerHelloDone")?; + HandshakePayload::ServerHelloDone + } + HandshakeType::ClientKeyExchange => { + HandshakePayload::ClientKeyExchange(Payload::read(&mut sub)) + } + HandshakeType::CertificateRequest if vers == ProtocolVersion::TLSv1_3 => { + let p = CertificateRequestPayloadTls13::read(&mut sub)?; + HandshakePayload::CertificateRequestTls13(p) + } + HandshakeType::CertificateRequest => { + let p = CertificateRequestPayload::read(&mut sub)?; + HandshakePayload::CertificateRequest(p) + } + HandshakeType::CompressedCertificate => HandshakePayload::CompressedCertificate( + CompressedCertificatePayload::read(&mut sub)?, + ), + HandshakeType::CertificateVerify => { + HandshakePayload::CertificateVerify(DigitallySignedStruct::read(&mut sub)?) + } + HandshakeType::NewSessionTicket if vers == ProtocolVersion::TLSv1_3 => { + let p = NewSessionTicketPayloadTls13::read(&mut sub)?; + HandshakePayload::NewSessionTicketTls13(p) + } + HandshakeType::NewSessionTicket => { + let p = NewSessionTicketPayload::read(&mut sub)?; + HandshakePayload::NewSessionTicket(p) + } + HandshakeType::EncryptedExtensions => { + HandshakePayload::EncryptedExtensions(Box::new(ServerExtensions::read(&mut sub)?)) + } + HandshakeType::KeyUpdate => { + HandshakePayload::KeyUpdate(KeyUpdateRequest::read(&mut sub)?) + } + HandshakeType::EndOfEarlyData => { + sub.expect_empty("EndOfEarlyData")?; + HandshakePayload::EndOfEarlyData + } + HandshakeType::Finished => HandshakePayload::Finished(Payload::read(&mut sub)), + HandshakeType::CertificateStatus => { + HandshakePayload::CertificateStatus(CertificateStatus::read(&mut sub)?) + } + HandshakeType::MessageHash => { + // does not appear on the wire + return Err(InvalidMessage::UnexpectedMessage("MessageHash")); + } + HandshakeType::HelloRetryRequest => { + // not legal on wire + return Err(InvalidMessage::UnexpectedMessage("HelloRetryRequest")); + } + _ => HandshakePayload::Unknown((typ, Payload::read(&mut sub))), + }; + + sub.expect_empty("HandshakeMessagePayload") + .map(|_| Self(payload)) + } + + pub(crate) fn encoding_for_binder_signing(&self) -> Vec<u8> { + let mut ret = self.get_encoding(); + let ret_len = ret.len() - self.total_binder_length(); + ret.truncate(ret_len); + ret + } + + pub(crate) fn total_binder_length(&self) -> usize { + match &self.0 { + HandshakePayload::ClientHello(ch) => match &ch.preshared_key_offer { + Some(offer) => { + let mut binders_encoding = Vec::new(); + offer + .binders + .encode(&mut binders_encoding); + binders_encoding.len() + } + _ => 0, + }, + _ => 0, + } + } + + pub(crate) fn payload_encode(&self, bytes: &mut Vec<u8>, encoding: Encoding) { + // output type, length, and encoded payload + self.0 + .wire_handshake_type() + .encode(bytes); + + let nested = LengthPrefixedBuffer::new( + ListLength::U24 { + max: usize::MAX, + error: InvalidMessage::MessageTooLarge, + }, + bytes, + ); + + match &self.0 { + // for Server Hello and HelloRetryRequest payloads we need to encode the payload + // differently based on the purpose of the encoding. + HandshakePayload::ServerHello(payload) => payload.payload_encode(nested.buf, encoding), + HandshakePayload::HelloRetryRequest(payload) => { + payload.payload_encode(nested.buf, encoding) + } + + // All other payload types are encoded the same regardless of purpose. + _ => self.0.encode(nested.buf), + } + } + + pub(crate) fn build_handshake_hash(hash: &[u8]) -> Self { + Self(HandshakePayload::MessageHash(Payload::new(hash.to_vec()))) + } + + pub(crate) fn into_owned(self) -> HandshakeMessagePayload<'static> { + HandshakeMessagePayload(self.0.into_owned()) + } +} + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct HpkeSymmetricCipherSuite { + pub kdf_id: HpkeKdf, + pub aead_id: HpkeAead, +} + +impl Codec<'_> for HpkeSymmetricCipherSuite { + fn encode(&self, bytes: &mut Vec<u8>) { + self.kdf_id.encode(bytes); + self.aead_id.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + kdf_id: HpkeKdf::read(r)?, + aead_id: HpkeAead::read(r)?, + }) + } +} + +/// draft-ietf-tls-esni-24: `HpkeSymmetricCipherSuite cipher_suites<4..2^16-4>;` +impl TlsListElement for HpkeSymmetricCipherSuite { + const SIZE_LEN: ListLength = ListLength::NonZeroU16 { + empty_error: InvalidMessage::IllegalEmptyList("HpkeSymmetricCipherSuites"), + }; +} + +#[derive(Clone, Debug, PartialEq)] +pub struct HpkeKeyConfig { + pub config_id: u8, + pub kem_id: HpkeKem, + /// draft-ietf-tls-esni-24: `opaque HpkePublicKey<1..2^16-1>;` + pub public_key: PayloadU16<NonEmpty>, + pub symmetric_cipher_suites: Vec<HpkeSymmetricCipherSuite>, +} + +impl Codec<'_> for HpkeKeyConfig { + fn encode(&self, bytes: &mut Vec<u8>) { + self.config_id.encode(bytes); + self.kem_id.encode(bytes); + self.public_key.encode(bytes); + self.symmetric_cipher_suites + .encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + config_id: u8::read(r)?, + kem_id: HpkeKem::read(r)?, + public_key: PayloadU16::read(r)?, + symmetric_cipher_suites: Vec::<HpkeSymmetricCipherSuite>::read(r)?, + }) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct EchConfigContents { + pub key_config: HpkeKeyConfig, + pub maximum_name_length: u8, + pub public_name: DnsName<'static>, + pub extensions: Vec<EchConfigExtension>, +} + +impl EchConfigContents { + /// Returns true if there is more than one extension of a given + /// type. + pub(crate) fn has_duplicate_extension(&self) -> bool { + has_duplicates::<_, _, u16>( + self.extensions + .iter() + .map(|ext| ext.ext_type()), + ) + } + + /// Returns true if there is at least one mandatory unsupported extension. + pub(crate) fn has_unknown_mandatory_extension(&self) -> bool { + self.extensions + .iter() + // An extension is considered mandatory if the high bit of its type is set. + .any(|ext| { + matches!(ext.ext_type(), ExtensionType::Unknown(_)) + && u16::from(ext.ext_type()) & 0x8000 != 0 + }) + } +} + +impl Codec<'_> for EchConfigContents { + fn encode(&self, bytes: &mut Vec<u8>) { + self.key_config.encode(bytes); + self.maximum_name_length.encode(bytes); + let dns_name = &self.public_name.borrow(); + PayloadU8::<MaybeEmpty>::encode_slice(dns_name.as_ref().as_ref(), bytes); + self.extensions.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + key_config: HpkeKeyConfig::read(r)?, + maximum_name_length: u8::read(r)?, + public_name: { + DnsName::try_from( + PayloadU8::<MaybeEmpty>::read(r)? + .0 + .as_slice(), + ) + .map_err(|_| InvalidMessage::InvalidServerName)? + .to_owned() + }, + extensions: Vec::read(r)?, + }) + } +} + +/// An encrypted client hello (ECH) config. +#[derive(Clone, Debug, PartialEq)] +pub enum EchConfigPayload { + /// A recognized V18 ECH configuration. + V18(EchConfigContents), + /// An unknown version ECH configuration. + Unknown { + version: EchVersion, + contents: PayloadU16, + }, +} + +impl TlsListElement for EchConfigPayload { + const SIZE_LEN: ListLength = ListLength::U16; +} + +impl Codec<'_> for EchConfigPayload { + fn encode(&self, bytes: &mut Vec<u8>) { + match self { + Self::V18(c) => { + // Write the version, the length, and the contents. + EchVersion::V18.encode(bytes); + let inner = LengthPrefixedBuffer::new(ListLength::U16, bytes); + c.encode(inner.buf); + } + Self::Unknown { version, contents } => { + // Unknown configuration versions are opaque. + version.encode(bytes); + contents.encode(bytes); + } + } + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let version = EchVersion::read(r)?; + let length = u16::read(r)?; + let mut contents = r.sub(length as usize)?; + + Ok(match version { + EchVersion::V18 => Self::V18(EchConfigContents::read(&mut contents)?), + _ => { + // Note: we don't PayloadU16::read() here because we've already read the length prefix. + let data = PayloadU16::new(contents.rest().into()); + Self::Unknown { + version, + contents: data, + } + } + }) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum EchConfigExtension { + Unknown(UnknownExtension), +} + +impl EchConfigExtension { + pub(crate) fn ext_type(&self) -> ExtensionType { + match self { + Self::Unknown(r) => r.typ, + } + } +} + +impl Codec<'_> for EchConfigExtension { + fn encode(&self, bytes: &mut Vec<u8>) { + self.ext_type().encode(bytes); + + let nested = LengthPrefixedBuffer::new(ListLength::U16, bytes); + match self { + Self::Unknown(r) => r.encode(nested.buf), + } + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let typ = ExtensionType::read(r)?; + let len = u16::read(r)? as usize; + let mut sub = r.sub(len)?; + + #[allow(clippy::match_single_binding)] // Future-proofing. + let ext = match typ { + _ => Self::Unknown(UnknownExtension::read(typ, &mut sub)), + }; + + sub.expect_empty("EchConfigExtension") + .map(|_| ext) + } +} + +impl TlsListElement for EchConfigExtension { + const SIZE_LEN: ListLength = ListLength::U16; +} + +/// Representation of the `ECHClientHello` client extension specified in +/// [draft-ietf-tls-esni Section 5]. +/// +/// [draft-ietf-tls-esni Section 5]: <https://www.ietf.org/archive/id/draft-ietf-tls-esni-18.html#section-5> +#[derive(Clone, Debug)] +pub(crate) enum EncryptedClientHello { + /// A `ECHClientHello` with type [EchClientHelloType::ClientHelloOuter]. + Outer(EncryptedClientHelloOuter), + /// An empty `ECHClientHello` with type [EchClientHelloType::ClientHelloInner]. + /// + /// This variant has no payload. + Inner, +} + +impl Codec<'_> for EncryptedClientHello { + fn encode(&self, bytes: &mut Vec<u8>) { + match self { + Self::Outer(payload) => { + EchClientHelloType::ClientHelloOuter.encode(bytes); + payload.encode(bytes); + } + Self::Inner => { + EchClientHelloType::ClientHelloInner.encode(bytes); + // Empty payload. + } + } + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + match EchClientHelloType::read(r)? { + EchClientHelloType::ClientHelloOuter => { + Ok(Self::Outer(EncryptedClientHelloOuter::read(r)?)) + } + EchClientHelloType::ClientHelloInner => Ok(Self::Inner), + _ => Err(InvalidMessage::InvalidContentType), + } + } +} + +/// Representation of the ECHClientHello extension with type outer specified in +/// [draft-ietf-tls-esni Section 5]. +/// +/// [draft-ietf-tls-esni Section 5]: <https://www.ietf.org/archive/id/draft-ietf-tls-esni-18.html#section-5> +#[derive(Clone, Debug)] +pub(crate) struct EncryptedClientHelloOuter { + /// The cipher suite used to encrypt ClientHelloInner. Must match a value from + /// ECHConfigContents.cipher_suites list. + pub cipher_suite: HpkeSymmetricCipherSuite, + /// The ECHConfigContents.key_config.config_id for the chosen ECHConfig. + pub config_id: u8, + /// The HPKE encapsulated key, used by servers to decrypt the corresponding payload field. + /// This field is empty in a ClientHelloOuter sent in response to a HelloRetryRequest. + pub enc: PayloadU16, + /// The serialized and encrypted ClientHelloInner structure, encrypted using HPKE. + pub payload: PayloadU16<NonEmpty>, +} + +impl Codec<'_> for EncryptedClientHelloOuter { + fn encode(&self, bytes: &mut Vec<u8>) { + self.cipher_suite.encode(bytes); + self.config_id.encode(bytes); + self.enc.encode(bytes); + self.payload.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + cipher_suite: HpkeSymmetricCipherSuite::read(r)?, + config_id: u8::read(r)?, + enc: PayloadU16::read(r)?, + payload: PayloadU16::read(r)?, + }) + } +} + +/// Representation of the ECHEncryptedExtensions extension specified in +/// [draft-ietf-tls-esni Section 5]. +/// +/// [draft-ietf-tls-esni Section 5]: <https://www.ietf.org/archive/id/draft-ietf-tls-esni-18.html#section-5> +#[derive(Clone, Debug)] +pub(crate) struct ServerEncryptedClientHello { + pub(crate) retry_configs: Vec<EchConfigPayload>, +} + +impl Codec<'_> for ServerEncryptedClientHello { + fn encode(&self, bytes: &mut Vec<u8>) { + self.retry_configs.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + Ok(Self { + retry_configs: Vec::<EchConfigPayload>::read(r)?, + }) + } +} + +/// The method of encoding to use for a handshake message. +/// +/// In some cases a handshake message may be encoded differently depending on the purpose +/// the encoded message is being used for. +pub(crate) enum Encoding { + /// Standard RFC 8446 encoding. + Standard, + /// Encoding for ECH confirmation for HRR. + EchConfirmation, + /// Encoding for ECH inner client hello. + EchInnerHello { to_compress: Vec<ExtensionType> }, +} + +fn has_duplicates<I: IntoIterator<Item = E>, E: Into<T>, T: Eq + Ord>(iter: I) -> bool { + let mut seen = BTreeSet::new(); + + for x in iter { + if !seen.insert(x.into()) { + return true; + } + } + + false +} + +struct DuplicateExtensionChecker(BTreeSet<u16>); + +impl DuplicateExtensionChecker { + fn new() -> Self { + Self(BTreeSet::new()) + } + + fn check(&mut self, typ: ExtensionType) -> Result<(), InvalidMessage> { + let u = u16::from(typ); + match self.0.insert(u) { + true => Ok(()), + false => Err(InvalidMessage::DuplicateExtension(u)), + } + } +} + +fn low_quality_integer_hash(mut x: u32) -> u32 { + x = x + .wrapping_add(0x7ed55d16) + .wrapping_add(x << 12); + x = (x ^ 0xc761c23c) ^ (x >> 19); + x = x + .wrapping_add(0x165667b1) + .wrapping_add(x << 5); + x = x.wrapping_add(0xd3a2646c) ^ (x << 9); + x = x + .wrapping_add(0xfd7046c5) + .wrapping_add(x << 3); + x = (x ^ 0xb55a4f09) ^ (x >> 16); + x +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_ech_config_dupe_exts() { + let unknown_ext = EchConfigExtension::Unknown(UnknownExtension { + typ: ExtensionType::Unknown(0x42), + payload: Payload::new(vec![0x42]), + }); + let mut config = config_template(); + config + .extensions + .push(unknown_ext.clone()); + config.extensions.push(unknown_ext); + + assert!(config.has_duplicate_extension()); + assert!(!config.has_unknown_mandatory_extension()); + } + + #[test] + fn test_ech_config_mandatory_exts() { + let mandatory_unknown_ext = EchConfigExtension::Unknown(UnknownExtension { + typ: ExtensionType::Unknown(0x42 | 0x8000), // Note: high bit set. + payload: Payload::new(vec![0x42]), + }); + let mut config = config_template(); + config + .extensions + .push(mandatory_unknown_ext); + + assert!(!config.has_duplicate_extension()); + assert!(config.has_unknown_mandatory_extension()); + } + + fn config_template() -> EchConfigContents { + EchConfigContents { + key_config: HpkeKeyConfig { + config_id: 0, + kem_id: HpkeKem::DHKEM_P256_HKDF_SHA256, + public_key: PayloadU16::new(b"xxx".into()), + symmetric_cipher_suites: vec![HpkeSymmetricCipherSuite { + kdf_id: HpkeKdf::HKDF_SHA256, + aead_id: HpkeAead::AES_128_GCM, + }], + }, + maximum_name_length: 0, + public_name: DnsName::try_from("example.com").unwrap(), + extensions: vec![], + } + } +} diff --git a/vendor/rustls/src/msgs/handshake_test.rs b/vendor/rustls/src/msgs/handshake_test.rs new file mode 100644 index 00000000..53cf88fa --- /dev/null +++ b/vendor/rustls/src/msgs/handshake_test.rs @@ -0,0 +1,1051 @@ +use std::prelude::v1::*; +use std::{format, println, vec}; + +use pki_types::{CertificateDer, DnsName}; + +use super::base::{Payload, PayloadU8, PayloadU16, PayloadU24}; +use super::codec::{Codec, Reader, put_u16}; +use super::enums::{ + ClientCertificateType, Compression, ECCurveType, ExtensionType, KeyUpdateRequest, NamedGroup, +}; +use super::handshake::{ + CertificateChain, CertificateEntry, CertificateExtensions, CertificatePayloadTls13, + CertificateRequestExtensions, CertificateRequestPayload, CertificateRequestPayloadTls13, + CertificateStatus, CertificateStatusRequest, ClientExtensions, ClientHelloPayload, + ClientSessionTicket, CompressedCertificatePayload, DistinguishedName, EcParameters, + EncryptedClientHello, HandshakeMessagePayload, HandshakePayload, HelloRetryRequest, + HelloRetryRequestExtensions, KeyShareEntry, NewSessionTicketExtensions, + NewSessionTicketPayload, NewSessionTicketPayloadTls13, PresharedKeyBinder, + PresharedKeyIdentity, PresharedKeyOffer, ProtocolName, PskKeyExchangeModes, Random, + ServerDhParams, ServerEcdhParams, ServerEncryptedClientHello, ServerExtensions, + ServerHelloPayload, ServerKeyExchange, ServerKeyExchangeParams, ServerKeyExchangePayload, + ServerNamePayload, SessionId, SingleProtocolName, SupportedEcPointFormats, + SupportedProtocolVersions, +}; +use crate::enums::{ + CertificateCompressionAlgorithm, CertificateType, CipherSuite, HandshakeType, ProtocolVersion, + SignatureScheme, +}; +use crate::error::InvalidMessage; +use crate::sync::Arc; +use crate::verify::DigitallySignedStruct; + +#[test] +fn rejects_short_random() { + let bytes = [0x01; 31]; + let mut rd = Reader::init(&bytes); + assert!(Random::read(&mut rd).is_err()); +} + +#[test] +fn reads_random() { + let bytes = [0x01; 32]; + let mut rd = Reader::init(&bytes); + let rnd = Random::read(&mut rd).unwrap(); + println!("{rnd:?}"); + + assert!(!rd.any_left()); +} + +#[test] +fn debug_random() { + assert_eq!( + "0101010101010101010101010101010101010101010101010101010101010101", + format!("{:?}", Random::from([1; 32])) + ); +} + +#[test] +fn rejects_truncated_session_id() { + let bytes = [32; 32]; + let mut rd = Reader::init(&bytes); + assert!(SessionId::read(&mut rd).is_err()); +} + +#[test] +fn rejects_session_id_with_bad_length() { + let bytes = [33; 33]; + let mut rd = Reader::init(&bytes); + assert!(SessionId::read(&mut rd).is_err()); +} + +#[test] +fn session_id_with_different_lengths_are_unequal() { + let a = SessionId::read(&mut Reader::init(&[1u8, 1])).unwrap(); + let b = SessionId::read(&mut Reader::init(&[2u8, 1, 2])).unwrap(); + assert_ne!(a, b); +} + +#[test] +fn accepts_short_session_id() { + let bytes = [1; 2]; + let mut rd = Reader::init(&bytes); + let sess = SessionId::read(&mut rd).unwrap(); + println!("{sess:?}"); + + #[cfg(feature = "tls12")] + assert!(!sess.is_empty()); + assert_ne!(sess, SessionId::empty()); + assert!(!rd.any_left()); +} + +#[test] +fn accepts_empty_session_id() { + let bytes = [0; 1]; + let mut rd = Reader::init(&bytes); + let sess = SessionId::read(&mut rd).unwrap(); + println!("{sess:?}"); + + #[cfg(feature = "tls12")] + assert!(sess.is_empty()); + assert_eq!(sess, SessionId::empty()); + assert!(!rd.any_left()); +} + +#[test] +fn debug_session_id() { + let bytes = [ + 32, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, + ]; + let mut rd = Reader::init(&bytes); + let sess = SessionId::read(&mut rd).unwrap(); + assert_eq!( + "0101010101010101010101010101010101010101010101010101010101010101", + format!("{sess:?}") + ); +} + +#[test] +fn refuses_client_exts_with_unparsed_bytes() { + let bytes = [0x00u8, 0x08, 0x00, 0x0b, 0x00, 0x04, 0x02, 0xf8, 0x01, 0x02]; + assert_eq!( + ClientExtensions::read_bytes(&bytes).unwrap_err(), + InvalidMessage::TrailingData("ClientExtensions") + ); +} + +#[test] +fn refuses_server_ext_with_unparsed_bytes() { + let bytes = [0x00u8, 0x08, 0x00, 0x0b, 0x00, 0x04, 0x02, 0xf8, 0x01, 0x02]; + assert_eq!( + ServerExtensions::read_bytes(&bytes).unwrap_err(), + InvalidMessage::TrailingData("ServerExtensions") + ); +} + +#[test] +fn refuses_certificate_ext_with_unparsed_bytes() { + let bytes = [ + 0x00u8, 0x09, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x01, + ]; + assert_eq!( + CertificateExtensions::read_bytes(&bytes).unwrap_err(), + InvalidMessage::TrailingData("CertificateExtensions") + ); +} + +#[test] +fn refuses_certificate_ext_with_unknown_type() { + let bytes = [0x00u8, 0x08, 0x00, 0x05, 0x00, 0x03, 0x99, 0x00, 0x00, 0x00]; + assert_eq!( + CertificateExtensions::read_bytes(&bytes).unwrap_err(), + InvalidMessage::InvalidCertificateStatusType + ); +} + +#[test] +fn refuses_certificate_req_ext_with_unparsed_bytes() { + let bytes = [ + 0x00u8, 0x09, 0x00, 0x0d, 0x00, 0x05, 0x00, 0x02, 0x01, 0x02, 0xff, + ]; + assert_eq!( + CertificateRequestExtensions::read_bytes(&bytes).unwrap_err(), + InvalidMessage::TrailingData("CertificateRequestExtensions") + ); +} + +#[test] +fn refuses_certificate_req_ext_with_duplicate() { + let bytes = [0x00u8, 0x08, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00]; + assert_eq!( + CertificateRequestExtensions::read_bytes(&bytes).unwrap_err(), + InvalidMessage::DuplicateExtension(0x0099) + ); +} + +#[test] +fn refuses_new_session_ticket_ext_with_unparsed_bytes() { + let bytes = [ + 0x00u8, 0x09, 0x00, 0x2a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, + ]; + assert_eq!( + NewSessionTicketExtensions::read_bytes(&bytes).unwrap_err(), + InvalidMessage::TrailingData("NewSessionTicketExtensions") + ); +} + +#[test] +fn refuses_new_session_ticket_ext_with_duplicate_extension() { + let bytes = [0x00u8, 0x08, 0x00, 0x99, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00]; + assert_eq!( + NewSessionTicketExtensions::read_bytes(&bytes).unwrap_err(), + InvalidMessage::DuplicateExtension(0x0099) + ); +} + +#[test] +fn rejects_truncated_sni() { + let bytes = [0, 1, 0]; + assert!(ServerNamePayload::read(&mut Reader::init(&bytes)).is_err()); + + let bytes = [0, 2, 0, 1]; + assert!(ServerNamePayload::read(&mut Reader::init(&bytes)).is_err()); + + let bytes = [0, 3, 0, 1, 0]; + assert!(ServerNamePayload::read(&mut Reader::init(&bytes)).is_err()); + + let bytes = [0, 4, 0, 2, 0, 0]; + assert!(ServerNamePayload::read(&mut Reader::init(&bytes)).is_err()); + + let bytes = [0, 5, 0, 3, 0, 0, 0]; + assert!(ServerNamePayload::read(&mut Reader::init(&bytes)).is_err()); + + let bytes = [0, 5, 0, 3, 0, 0, 1]; + assert!(ServerNamePayload::read(&mut Reader::init(&bytes)).is_err()); + + let bytes = [0, 6, 0, 4, 0, 0, 2, 0x68]; + assert!(ServerNamePayload::read(&mut Reader::init(&bytes)).is_err()); +} + +#[test] +fn rejects_empty_sni_extension() { + assert_eq!( + ClientExtensions::read_bytes(&[0, 6, 0, 0, 0, 2, 0, 0]).unwrap_err(), + InvalidMessage::IllegalEmptyList("ServerNames") + ); +} + +#[test] +fn rejects_duplicate_names_in_sni_extension() { + assert_eq!( + ClientExtensions::read_bytes(&[0, 14, 0, 0, 0, 10, 0, 8, 0, 0, 1, b'a', 0, 0, 1, b'b',]) + .unwrap_err(), + InvalidMessage::InvalidServerName + ); +} + +#[test] +fn can_round_trip_psk_identity() { + let bytes = [0, 1, 0x99, 0x11, 0x22, 0x33, 0x44]; + let psk_id = PresharedKeyIdentity::read(&mut Reader::init(&bytes)).unwrap(); + println!("{psk_id:?}"); + assert_eq!(psk_id.obfuscated_ticket_age, 0x11223344); + assert_eq!(psk_id.get_encoding(), bytes.to_vec()); + + let bytes = [0, 5, 0x1, 0x2, 0x3, 0x4, 0x5, 0x11, 0x22, 0x33, 0x44]; + let psk_id = PresharedKeyIdentity::read(&mut Reader::init(&bytes)).unwrap(); + println!("{psk_id:?}"); + assert_eq!(psk_id.identity.0, vec![0x1, 0x2, 0x3, 0x4, 0x5]); + assert_eq!(psk_id.obfuscated_ticket_age, 0x11223344); + assert_eq!(psk_id.get_encoding(), bytes.to_vec()); +} + +#[test] +fn can_round_trip_psk_offer() { + let bytes = [ + 0, 7, 0, 1, 0x99, 0x11, 0x22, 0x33, 0x44, 0, 4, 3, 0x01, 0x02, 0x3, + ]; + let psko = PresharedKeyOffer::read(&mut Reader::init(&bytes)).unwrap(); + println!("{psko:?}"); + + assert_eq!(psko.identities.len(), 1); + assert_eq!(psko.identities[0].identity.0, vec![0x99]); + assert_eq!(psko.identities[0].obfuscated_ticket_age, 0x11223344); + assert_eq!(psko.binders.len(), 1); + assert_eq!(psko.binders[0].as_ref(), &[1, 2, 3]); + assert_eq!(psko.get_encoding(), bytes.to_vec()); +} + +#[test] +fn can_round_trip_cert_status_req_for_ocsp() { + let ext = CertificateStatusRequest::build_ocsp(); + println!("{ext:?}"); + + let bytes = [ + 0, 11, 1, // OCSP + 0, 5, 0, 3, 0, 1, 1, 0, 1, 2, + ]; + + let csr = CertificateStatusRequest::read(&mut Reader::init(&bytes)).unwrap(); + println!("{csr:?}"); + assert_eq!(csr.get_encoding(), bytes.to_vec()); +} + +#[test] +fn can_round_trip_cert_status_req_for_other() { + let bytes = [ + 0, 5, 2, // !OCSP + 1, 2, 3, 4, + ]; + + let csr = CertificateStatusRequest::read(&mut Reader::init(&bytes)).unwrap(); + println!("{csr:?}"); + assert_eq!(csr.get_encoding(), bytes.to_vec()); +} + +#[test] +fn can_print_all_client_extensions() { + println!("client hello {:?}", sample_client_hello_payload()); +} + +#[test] +fn can_clone_all_client_extensions() { + let exts = sample_client_hello_payload().extensions; + let exts2 = exts.clone(); + println!("{exts:?}, {exts2:?}"); +} + +#[test] +fn client_extensions_basics() { + let src = ClientExtensions { + early_data_request: Some(()), + ..Default::default() + }; + let mut target = ClientExtensions::default(); + + assert_eq!(src.collect_used(), vec![ExtensionType::EarlyData]); + assert_eq!(target.collect_used(), vec![]); + + target.clone_one(&src, ExtensionType::EarlyData); + assert_eq!(target.collect_used(), vec![ExtensionType::EarlyData]); +} + +#[test] +fn client_extensions_empty() { + // both sides of empty-encoding branch + assert_eq!(ClientExtensions::default().get_encoding(), Vec::<u8>::new()); + assert_eq!( + ClientExtensions::read_bytes(&[]) + .unwrap() + .collect_used(), + vec![] + ); + + let early_data = b"\x00\x04\x00\x2a\x00\x00"; + assert_eq!( + ClientExtensions { + early_data_request: Some(()), + ..Default::default() + } + .get_encoding(), + early_data + ); + assert_eq!( + ClientExtensions::read_bytes(early_data) + .unwrap() + .collect_used(), + vec![ExtensionType::EarlyData] + ); +} + +#[test] +fn client_extensions_decode_checks_duplicates() { + // base + ClientExtensions::read_bytes(b"\x00\x04\x00\x2a\x00\x00").unwrap(); + + // duplicate known + assert_eq!( + ClientExtensions::read_bytes(b"\x00\x08\x00\x2a\x00\x00\x00\x2a\x00\x00").unwrap_err(), + InvalidMessage::DuplicateExtension(0x002a) + ); + + // duplicate unknown + assert_eq!( + ClientExtensions::read_bytes(b"\x00\x08\xff\xff\x00\x00\xff\xff\x00\x00").unwrap_err(), + InvalidMessage::DuplicateExtension(0xffff) + ); +} + +#[test] +fn client_extensions_ordering() { + // the important thing here is that PSK requests come last, + // ECH requests come second to last, and order of other extensions + // do vary. + + let psk_offer = PresharedKeyOffer { + identities: vec![], + binders: vec![], + }; + + let psk_and_ech = ClientExtensions { + early_data_request: Some(()), + extended_master_secret_request: Some(()), + preshared_key_offer: Some(psk_offer.clone()), + encrypted_client_hello: Some(EncryptedClientHello::Inner), + ..Default::default() + }; + + let psk_and_ech_with_contiguous = ClientExtensions { + contiguous_extensions: vec![ExtensionType::ExtendedMasterSecret], + ..psk_and_ech.clone() + }; + + let ech = ClientExtensions { + early_data_request: Some(()), + extended_master_secret_request: Some(()), + encrypted_client_hello: Some(EncryptedClientHello::Inner), + ..Default::default() + }; + + let psk = ClientExtensions { + early_data_request: Some(()), + extended_master_secret_request: Some(()), + preshared_key_offer: Some(psk_offer), + ..Default::default() + }; + + let neither = ClientExtensions { + early_data_request: Some(()), + extended_master_secret_request: Some(()), + ..Default::default() + }; + + fn encoding_with_order(order_seed: u16, exts: &ClientExtensions<'_>) -> Vec<u8> { + let mut e = exts.clone(); + e.order_seed = order_seed; + e.get_encoding() + } + + assert_ne!( + encoding_with_order(0, &psk_and_ech), + encoding_with_order(1, &psk_and_ech) + ); + assert_eq!( + encoding_with_order(0, &psk_and_ech_with_contiguous), + encoding_with_order(1, &psk_and_ech_with_contiguous) + ); + assert_ne!(encoding_with_order(0, &ech), encoding_with_order(1, &ech)); + assert_ne!(encoding_with_order(0, &psk), encoding_with_order(1, &psk)); + assert_ne!( + encoding_with_order(0, &neither), + encoding_with_order(1, &neither) + ); + + // check order invariants hold for all seeds + for seed in 0..=0xffff { + // must end with ECH and then PSK + assert!(encoding_with_order(seed, &psk_and_ech).ends_with( + b"\xfe\x0d\x00\x01\x01\ + \x00\x29\x00\x04\x00\x00\x00\x00" + )); + + // must end with EMS, then ECH and then PSK + assert!( + encoding_with_order(seed, &psk_and_ech_with_contiguous).ends_with( + b"\x00\x17\x00\x00\ + \xfe\x0d\x00\x01\x01\ + \x00\x29\x00\x04\x00\x00\x00\x00" + ) + ); + + // just PSK + assert!(encoding_with_order(seed, &psk).ends_with(b"\x00\x29\x00\x04\x00\x00\x00\x00")); + + // just ECH + assert!(encoding_with_order(seed, &ech).ends_with(b"\xfe\x0d\x00\x01\x01")); + } +} + +#[test] +fn test_truncated_psk_offer() { + let ext = PresharedKeyOffer { + identities: vec![PresharedKeyIdentity::new(vec![3, 4, 5], 123456)], + binders: vec![PresharedKeyBinder::from(vec![1, 2, 3])], + }; + + let mut enc = ext.get_encoding(); + println!("testing {ext:?} enc {enc:?}"); + for l in 0..enc.len() { + if l == 9 { + continue; + } + put_u16(l as u16, &mut enc); + let rc = PresharedKeyOffer::read_bytes(&enc); + assert!(rc.is_err()); + } +} + +#[test] +fn test_truncated_client_hello_is_detected() { + let ch = sample_client_hello_payload(); + let enc = ch.get_encoding(); + println!("testing {ch:?} enc {enc:?}"); + + for l in 0..enc.len() { + println!("len {:?} enc {:?}", l, &enc[..l]); + if l == 41 { + continue; // where extensions are empty + } + assert!(ClientHelloPayload::read_bytes(&enc[..l]).is_err()); + } +} + +#[test] +fn test_truncated_client_extension_is_detected() { + let chp = sample_client_hello_payload(); + + let enc = chp.extensions.get_encoding(); + println!("testing enc {enc:?}"); + + // "outer" truncation, i.e., where the extension-level length is longer than + // the input + for l in 1..enc.len() { + assert!(ClientExtensions::read_bytes(&enc[..l]).is_err()); + } +} + +#[test] +fn test_truncated_hello_retry_extension_is_detected() { + let hrr = sample_hello_retry_request(); + + let mut enc = hrr.extensions.get_encoding(); + println!("testing enc {enc:?}"); + + // "outer" truncation, i.e., where the extension-level length is longer than + // the input + for l in 0..enc.len() { + assert!(HelloRetryRequestExtensions::read_bytes(&enc[..l]).is_err()); + } + + // "inner" truncation, where the extension-level length agrees with the input + // length, but isn't long enough for the type of extension + for l in 0..(enc.len() - 4) { + put_u16(l as u16, &mut enc); + println!(" encoding {enc:?} len {l:?}"); + assert!(HelloRetryRequestExtensions::read_bytes(&enc).is_err()); + } +} + +#[test] +fn test_truncated_server_extension_is_detected() { + let shp = sample_server_hello_payload(); + + let mut enc = shp.extensions.get_encoding(); + println!("testing enc {enc:?}"); + + // "outer" truncation, i.e., where the extension-level length is longer than + // the input + for l in 0..enc.len() { + assert!(ServerExtensions::read_bytes(&enc[..l]).is_err()); + } + + // "inner" truncation, where the extension-level length agrees with the input + // length, but isn't long enough for the type of extension + for l in 0..(enc.len() - 4) { + put_u16(l as u16, &mut enc[..2]); + println!(" encoding {enc:?} len {l:?}"); + assert!(ServerExtensions::read_bytes(&enc).is_err()); + } +} + +#[test] +fn can_print_all_server_extensions() { + println!("server hello {:?}", sample_server_hello_payload()); +} + +#[test] +fn can_clone_all_server_extensions() { + let exts = sample_server_hello_payload().extensions; + let exts2 = exts.clone(); + println!("{exts:?}, {exts2:?}"); +} + +#[test] +fn can_round_trip_all_tls12_handshake_payloads() { + for hm in all_tls12_handshake_payloads().iter() { + println!("{:?}", hm.0.handshake_type()); + let bytes = hm.get_encoding(); + let mut rd = Reader::init(&bytes); + let other = HandshakeMessagePayload::read(&mut rd).unwrap(); + assert!(!rd.any_left()); + assert_eq!(hm.get_encoding(), other.get_encoding()); + + println!("{hm:?}"); + println!("{other:?}"); + } +} + +#[test] +fn can_into_owned_all_tls12_handshake_payloads() { + for hm in all_tls12_handshake_payloads().drain(..) { + let enc = hm.get_encoding(); + let debug = format!("{hm:?}"); + let other = hm.into_owned(); + assert_eq!(enc, other.get_encoding()); + assert_eq!(debug, format!("{other:?}")); + } +} + +#[test] +fn can_detect_truncation_of_all_tls12_handshake_payloads() { + for hm in all_tls12_handshake_payloads().iter() { + let mut enc = hm.get_encoding(); + println!("test {hm:?} enc {enc:?}"); + + // outer truncation + for l in 0..enc.len() { + assert!(HandshakeMessagePayload::read_bytes(&enc[..l]).is_err()) + } + + // inner truncation + for l in 0..enc.len() - 4 { + put_u24(l as u32, &mut enc[1..]); + println!(" check len {l:?} enc {enc:?}"); + + match (hm.0.handshake_type(), l) { + (HandshakeType::ClientHello, 41) + | (HandshakeType::ServerHello, 38) + | (HandshakeType::ServerKeyExchange, _) + | (HandshakeType::ClientKeyExchange, _) + | (HandshakeType::Finished, _) + | (HandshakeType::Unknown(_), _) => continue, + _ => {} + }; + + assert!( + HandshakeMessagePayload::read_version( + &mut Reader::init(&enc), + ProtocolVersion::TLSv1_2 + ) + .is_err() + ); + assert!(HandshakeMessagePayload::read_bytes(&enc).is_err()); + } + } +} + +#[test] +fn can_round_trip_all_tls13_handshake_payloads() { + for hm in all_tls13_handshake_payloads().iter() { + println!("{:?}", hm.0.handshake_type()); + let bytes = hm.get_encoding(); + let mut rd = Reader::init(&bytes); + + let other = + HandshakeMessagePayload::read_version(&mut rd, ProtocolVersion::TLSv1_3).unwrap(); + assert!(!rd.any_left()); + assert_eq!(hm.get_encoding(), other.get_encoding()); + + println!("{hm:?}"); + println!("{other:?}"); + } +} + +#[test] +fn can_into_owned_all_tls13_handshake_payloads() { + for hm in all_tls13_handshake_payloads().drain(..) { + let enc = hm.get_encoding(); + let debug = format!("{hm:?}"); + let other = hm.into_owned(); + assert_eq!(enc, other.get_encoding()); + assert_eq!(debug, format!("{other:?}")); + } +} + +#[test] +fn can_detect_truncation_of_all_tls13_handshake_payloads() { + for hm in all_tls13_handshake_payloads().iter() { + let mut enc = hm.get_encoding(); + println!("test {hm:?} enc {enc:?}"); + + // outer truncation + for l in 0..enc.len() { + assert!(HandshakeMessagePayload::read_bytes(&enc[..l]).is_err()) + } + + // inner truncation + for l in 0..enc.len() - 4 { + put_u24(l as u32, &mut enc[1..]); + println!(" check len {l:?} enc {enc:?}"); + + match (hm.0.handshake_type(), l) { + (HandshakeType::ClientHello, 41) + | (HandshakeType::ServerHello, 38) + | (HandshakeType::ServerKeyExchange, _) + | (HandshakeType::ClientKeyExchange, _) + | (HandshakeType::Finished, _) + | (HandshakeType::Unknown(_), _) => continue, + _ => {} + }; + + assert!( + HandshakeMessagePayload::read_version( + &mut Reader::init(&enc), + ProtocolVersion::TLSv1_3 + ) + .is_err() + ); + } + } +} + +fn put_u24(u: u32, b: &mut [u8]) { + b[0] = (u >> 16) as u8; + b[1] = (u >> 8) as u8; + b[2] = u as u8; +} + +#[test] +fn cannot_read_message_hash_from_network() { + let mh = HandshakeMessagePayload(HandshakePayload::MessageHash(Payload::new(vec![1, 2, 3]))); + println!("mh {mh:?}"); + let enc = mh.get_encoding(); + assert!(HandshakeMessagePayload::read_bytes(&enc).is_err()); +} + +#[test] +fn cannot_decode_huge_certificate() { + let mut buf = [0u8; 65 * 1024]; + // exactly 64KB decodes fine + buf[0] = 0x0b; + buf[1] = 0x01; + buf[2] = 0x00; + buf[3] = 0x03; + buf[4] = 0x01; + buf[5] = 0x00; + buf[6] = 0x00; + buf[7] = 0x00; + buf[8] = 0xff; + buf[9] = 0xfd; + HandshakeMessagePayload::read_bytes(&buf[..0x10000 + 7]).unwrap(); + + // however 64KB + 1 byte does not + buf[1] = 0x01; + buf[2] = 0x00; + buf[3] = 0x04; + buf[4] = 0x01; + buf[5] = 0x00; + buf[6] = 0x01; + assert_eq!( + HandshakeMessagePayload::read_bytes(&buf[..0x10001 + 7]).unwrap_err(), + InvalidMessage::CertificatePayloadTooLarge + ); +} + +#[test] +fn can_decode_server_hello_from_api_devicecheck_apple_com() { + let data = include_bytes!("../testdata/hello-api.devicecheck.apple.com.bin"); + let mut r = Reader::init(data); + let hm = HandshakeMessagePayload::read(&mut r).unwrap(); + println!("msg: {hm:?}"); +} + +#[test] +fn wrapped_dn_encoding() { + let subject = b"subject"; + let dn = DistinguishedName::in_sequence(&subject[..]); + const DER_SEQUENCE_TAG: u8 = 0x30; + let expected_prefix = vec![DER_SEQUENCE_TAG, subject.len() as u8]; + assert_eq!(dn.as_ref(), [expected_prefix, subject.to_vec()].concat()); +} + +fn sample_hello_retry_request() -> HelloRetryRequest { + HelloRetryRequest { + legacy_version: ProtocolVersion::TLSv1_2, + session_id: SessionId::empty(), + cipher_suite: CipherSuite::TLS_NULL_WITH_NULL_NULL, + extensions: HelloRetryRequestExtensions { + key_share: Some(NamedGroup::X25519), + cookie: Some(PayloadU16::new(vec![0])), + supported_versions: Some(ProtocolVersion::TLSv1_2), + encrypted_client_hello: Some(Payload::new(vec![1, 2, 3])), + order: None, + }, + } +} + +fn sample_client_hello_payload() -> ClientHelloPayload { + ClientHelloPayload { + client_version: ProtocolVersion::TLSv1_2, + random: Random::from([0; 32]), + session_id: SessionId::empty(), + cipher_suites: vec![CipherSuite::TLS_NULL_WITH_NULL_NULL], + compression_methods: vec![Compression::Null], + extensions: Box::new(ClientExtensions { + server_name: Some(ServerNamePayload::from( + &DnsName::try_from("hello").unwrap(), + )), + cookie: Some(PayloadU16::new(vec![1, 2, 3])), + signature_schemes: Some(vec![SignatureScheme::ECDSA_NISTP256_SHA256]), + session_ticket: Some(ClientSessionTicket::Request), + ec_point_formats: Some(SupportedEcPointFormats::default()), + named_groups: Some(vec![NamedGroup::X25519]), + protocols: Some(vec![ProtocolName::from(vec![0])]), + supported_versions: Some(SupportedProtocolVersions { + tls13: true, + ..Default::default() + }), + key_shares: Some(vec![KeyShareEntry::new(NamedGroup::X25519, &[1, 2, 3][..])]), + preshared_key_modes: Some(PskKeyExchangeModes { + psk_dhe: true, + psk: false, + }), + preshared_key_offer: Some(PresharedKeyOffer { + identities: vec![ + PresharedKeyIdentity::new(vec![3, 4, 5], 123456), + PresharedKeyIdentity::new(vec![6, 7, 8], 7891011), + ], + binders: vec![ + PresharedKeyBinder::from(vec![1, 2, 3]), + PresharedKeyBinder::from(vec![3, 4, 5]), + ], + }), + extended_master_secret_request: Some(()), + certificate_status_request: Some(CertificateStatusRequest::build_ocsp()), + server_certificate_types: Some(vec![CertificateType::RawPublicKey]), + client_certificate_types: Some(vec![CertificateType::RawPublicKey]), + transport_parameters: Some(Payload::new(vec![1, 2, 3])), + early_data_request: Some(()), + certificate_compression_algorithms: Some(vec![CertificateCompressionAlgorithm::Brotli]), + encrypted_client_hello: Some(EncryptedClientHello::Inner), + encrypted_client_hello_outer: Some(vec![ExtensionType::SCT]), + ..Default::default() + }), + } +} + +fn sample_server_hello_payload() -> ServerHelloPayload { + ServerHelloPayload { + legacy_version: ProtocolVersion::TLSv1_2, + random: Random::from([0; 32]), + session_id: SessionId::empty(), + cipher_suite: CipherSuite::TLS_NULL_WITH_NULL_NULL, + compression_method: Compression::Null, + extensions: Box::new(ServerExtensions { + ec_point_formats: Some(SupportedEcPointFormats::default()), + server_name_ack: Some(()), + session_ticket_ack: Some(()), + renegotiation_info: Some(PayloadU8::new(vec![0])), + selected_protocol: Some(SingleProtocolName::new(ProtocolName::from(vec![0]))), + key_share: Some(KeyShareEntry::new(NamedGroup::X25519, &[1, 2, 3][..])), + preshared_key: Some(3), + early_data_ack: Some(()), + encrypted_client_hello_ack: Some(ServerEncryptedClientHello { + retry_configs: vec![], + }), + extended_master_secret_ack: Some(()), + certificate_status_request_ack: Some(()), + selected_version: Some(ProtocolVersion::TLSv1_2), + transport_parameters: Some(Payload::new(vec![1, 2, 3])), + transport_parameters_draft: None, + client_certificate_type: Some(CertificateType::RawPublicKey), + server_certificate_type: Some(CertificateType::RawPublicKey), + unknown_extensions: Default::default(), + }), + } +} + +fn all_tls12_handshake_payloads() -> Vec<HandshakeMessagePayload<'static>> { + vec![ + HandshakeMessagePayload(HandshakePayload::HelloRequest), + HandshakeMessagePayload(HandshakePayload::ClientHello(sample_client_hello_payload())), + HandshakeMessagePayload(HandshakePayload::ServerHello(sample_server_hello_payload())), + HandshakeMessagePayload(HandshakePayload::HelloRetryRequest( + sample_hello_retry_request(), + )), + HandshakeMessagePayload(HandshakePayload::Certificate(CertificateChain(vec![ + CertificateDer::from(vec![1, 2, 3]), + ]))), + HandshakeMessagePayload(HandshakePayload::ServerKeyExchange( + sample_ecdhe_server_key_exchange_payload(), + )), + HandshakeMessagePayload(HandshakePayload::ServerKeyExchange( + sample_dhe_server_key_exchange_payload(), + )), + HandshakeMessagePayload(HandshakePayload::ServerKeyExchange( + sample_unknown_server_key_exchange_payload(), + )), + HandshakeMessagePayload(HandshakePayload::CertificateRequest( + sample_certificate_request_payload(), + )), + HandshakeMessagePayload(HandshakePayload::ServerHelloDone), + HandshakeMessagePayload(HandshakePayload::ClientKeyExchange(Payload::Borrowed(&[ + 1, 2, 3, + ]))), + HandshakeMessagePayload(HandshakePayload::NewSessionTicket( + sample_new_session_ticket_payload(), + )), + HandshakeMessagePayload(HandshakePayload::EncryptedExtensions( + sample_encrypted_extensions(), + )), + HandshakeMessagePayload(HandshakePayload::KeyUpdate( + KeyUpdateRequest::UpdateRequested, + )), + HandshakeMessagePayload(HandshakePayload::KeyUpdate( + KeyUpdateRequest::UpdateNotRequested, + )), + HandshakeMessagePayload(HandshakePayload::Finished(Payload::Borrowed(&[1, 2, 3]))), + HandshakeMessagePayload(HandshakePayload::CertificateStatus( + sample_certificate_status(), + )), + HandshakeMessagePayload(HandshakePayload::Unknown(( + HandshakeType::Unknown(99), + Payload::Borrowed(&[1, 2, 3]), + ))), + ] +} + +fn all_tls13_handshake_payloads() -> Vec<HandshakeMessagePayload<'static>> { + vec![ + HandshakeMessagePayload(HandshakePayload::HelloRequest), + HandshakeMessagePayload(HandshakePayload::ClientHello(sample_client_hello_payload())), + HandshakeMessagePayload(HandshakePayload::ServerHello(sample_server_hello_payload())), + HandshakeMessagePayload(HandshakePayload::HelloRetryRequest( + sample_hello_retry_request(), + )), + HandshakeMessagePayload(HandshakePayload::CertificateTls13( + sample_certificate_payload_tls13(), + )), + HandshakeMessagePayload(HandshakePayload::CompressedCertificate( + sample_compressed_certificate(), + )), + HandshakeMessagePayload(HandshakePayload::ServerKeyExchange( + sample_ecdhe_server_key_exchange_payload(), + )), + HandshakeMessagePayload(HandshakePayload::ServerKeyExchange( + sample_dhe_server_key_exchange_payload(), + )), + HandshakeMessagePayload(HandshakePayload::ServerKeyExchange( + sample_unknown_server_key_exchange_payload(), + )), + HandshakeMessagePayload(HandshakePayload::CertificateRequestTls13( + sample_certificate_request_payload_tls13(), + )), + HandshakeMessagePayload(HandshakePayload::CertificateVerify( + DigitallySignedStruct::new(SignatureScheme::ECDSA_NISTP256_SHA256, vec![1, 2, 3]), + )), + HandshakeMessagePayload(HandshakePayload::ServerHelloDone), + HandshakeMessagePayload(HandshakePayload::ClientKeyExchange(Payload::Borrowed(&[ + 1, 2, 3, + ]))), + HandshakeMessagePayload(HandshakePayload::NewSessionTicketTls13( + sample_new_session_ticket_payload_tls13(), + )), + HandshakeMessagePayload(HandshakePayload::EncryptedExtensions( + sample_encrypted_extensions(), + )), + HandshakeMessagePayload(HandshakePayload::KeyUpdate( + KeyUpdateRequest::UpdateRequested, + )), + HandshakeMessagePayload(HandshakePayload::KeyUpdate( + KeyUpdateRequest::UpdateNotRequested, + )), + HandshakeMessagePayload(HandshakePayload::Finished(Payload::Borrowed(&[1, 2, 3]))), + HandshakeMessagePayload(HandshakePayload::CertificateStatus( + sample_certificate_status(), + )), + HandshakeMessagePayload(HandshakePayload::Unknown(( + HandshakeType::Unknown(99), + Payload::Borrowed(&[1, 2, 3]), + ))), + ] +} + +fn sample_certificate_payload_tls13() -> CertificatePayloadTls13<'static> { + CertificatePayloadTls13 { + context: PayloadU8::new(vec![1, 2, 3]), + entries: vec![CertificateEntry { + cert: CertificateDer::from(vec![3, 4, 5]), + extensions: CertificateExtensions { + status: Some(CertificateStatus { + ocsp_response: PayloadU24(Payload::new(vec![1, 2, 3])), + }), + }, + }], + } +} + +fn sample_compressed_certificate() -> CompressedCertificatePayload<'static> { + CompressedCertificatePayload { + alg: CertificateCompressionAlgorithm::Brotli, + uncompressed_len: 123, + compressed: PayloadU24(Payload::new(vec![1, 2, 3])), + } +} + +fn sample_ecdhe_server_key_exchange_payload() -> ServerKeyExchangePayload { + ServerKeyExchangePayload::Known(ServerKeyExchange { + params: ServerKeyExchangeParams::Ecdh(ServerEcdhParams { + curve_params: EcParameters { + curve_type: ECCurveType::NamedCurve, + named_group: NamedGroup::X25519, + }, + public: PayloadU8::new(vec![1, 2, 3]), + }), + dss: DigitallySignedStruct::new(SignatureScheme::RSA_PSS_SHA256, vec![1, 2, 3]), + }) +} + +fn sample_dhe_server_key_exchange_payload() -> ServerKeyExchangePayload { + ServerKeyExchangePayload::Known(ServerKeyExchange { + params: ServerKeyExchangeParams::Dh(ServerDhParams { + dh_p: PayloadU16::new(vec![1, 2, 3]), + dh_g: PayloadU16::new(vec![2]), + dh_Ys: PayloadU16::new(vec![1, 2]), + }), + dss: DigitallySignedStruct::new(SignatureScheme::RSA_PSS_SHA256, vec![1, 2, 3]), + }) +} + +fn sample_unknown_server_key_exchange_payload() -> ServerKeyExchangePayload { + ServerKeyExchangePayload::Unknown(Payload::Borrowed(&[1, 2, 3])) +} + +fn sample_certificate_request_payload() -> CertificateRequestPayload { + CertificateRequestPayload { + certtypes: vec![ClientCertificateType::RSASign], + sigschemes: vec![SignatureScheme::ECDSA_NISTP256_SHA256], + canames: vec![DistinguishedName::from(vec![1, 2, 3])], + } +} + +fn sample_certificate_request_payload_tls13() -> CertificateRequestPayloadTls13 { + CertificateRequestPayloadTls13 { + context: PayloadU8::new(vec![1, 2, 3]), + extensions: CertificateRequestExtensions { + signature_algorithms: Some(vec![SignatureScheme::ECDSA_NISTP256_SHA256]), + authority_names: Some(vec![DistinguishedName::from(vec![1, 2, 3])]), + certificate_compression_algorithms: Some(vec![CertificateCompressionAlgorithm::Zlib]), + }, + } +} + +fn sample_new_session_ticket_payload() -> NewSessionTicketPayload { + NewSessionTicketPayload { + lifetime_hint: 1234, + ticket: Arc::new(PayloadU16::new(vec![1, 2, 3])), + } +} + +fn sample_new_session_ticket_payload_tls13() -> NewSessionTicketPayloadTls13 { + NewSessionTicketPayloadTls13 { + lifetime: 123, + age_add: 1234, + nonce: PayloadU8::new(vec![1, 2, 3]), + ticket: Arc::new(PayloadU16::new(vec![4, 5, 6])), + extensions: NewSessionTicketExtensions { + max_early_data_size: Some(1234), + }, + } +} + +fn sample_encrypted_extensions() -> Box<ServerExtensions<'static>> { + sample_server_hello_payload().extensions +} + +fn sample_certificate_status() -> CertificateStatus<'static> { + CertificateStatus { + ocsp_response: PayloadU24(Payload::new(vec![1, 2, 3])), + } +} diff --git a/vendor/rustls/src/msgs/macros.rs b/vendor/rustls/src/msgs/macros.rs new file mode 100644 index 00000000..53252a20 --- /dev/null +++ b/vendor/rustls/src/msgs/macros.rs @@ -0,0 +1,313 @@ +/// A macro which defines an enum type. +macro_rules! enum_builder { + ( + $(#[doc = $comment:literal])* + #[repr($uint:ty)] + $enum_vis:vis enum $enum_name:ident + { + $( $enum_var:ident => $enum_val:literal),* $(,)? + $( !Debug: + $( $enum_var_nd:ident => $enum_val_nd:literal),* $(,)? + )? + } + ) => { + $(#[doc = $comment])* + #[non_exhaustive] + #[derive(PartialEq, Eq, Clone, Copy)] + $enum_vis enum $enum_name { + $( $enum_var),* + $(, $($enum_var_nd),* )? + ,Unknown($uint) + } + + impl $enum_name { + // NOTE(allow) generated irrespective if there are callers + #[allow(dead_code)] + $enum_vis fn to_array(self) -> [u8; core::mem::size_of::<$uint>()] { + <$uint>::from(self).to_be_bytes() + } + + // NOTE(allow) generated irrespective if there are callers + #[allow(dead_code)] + $enum_vis fn as_str(&self) -> Option<&'static str> { + match self { + $( $enum_name::$enum_var => Some(stringify!($enum_var))),* + $(, $( $enum_name::$enum_var_nd => Some(stringify!($enum_var_nd))),* )? + ,$enum_name::Unknown(_) => None, + } + } + } + + impl Codec<'_> for $enum_name { + // NOTE(allow) fully qualified Vec is only needed in no-std mode + #[allow(unused_qualifications)] + fn encode(&self, bytes: &mut alloc::vec::Vec<u8>) { + <$uint>::from(*self).encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, crate::error::InvalidMessage> { + match <$uint>::read(r) { + Ok(x) => Ok($enum_name::from(x)), + Err(_) => Err(crate::error::InvalidMessage::MissingData(stringify!($enum_name))), + } + } + } + + impl From<$uint> for $enum_name { + fn from(x: $uint) -> Self { + match x { + $($enum_val => $enum_name::$enum_var),* + $(, $($enum_val_nd => $enum_name::$enum_var_nd),* )? + , x => $enum_name::Unknown(x), + } + } + } + + impl From<$enum_name> for $uint { + fn from(value: $enum_name) -> Self { + match value { + $( $enum_name::$enum_var => $enum_val),* + $(, $( $enum_name::$enum_var_nd => $enum_val_nd),* )? + ,$enum_name::Unknown(x) => x + } + } + } + + impl core::fmt::Debug for $enum_name { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + $( $enum_name::$enum_var => f.write_str(stringify!($enum_var)), )* + _ => write!(f, "{}(0x{:x?})", stringify!($enum_name), <$uint>::from(*self)), + } + } + } + }; +} + +/// A macro which defines a structure containing TLS extensions +/// +/// The contents are defined by two blocks, which are merged to +/// give the struct's items. The second block is optional. +/// +/// The first block defines the items read-into by decoding, +/// and used for encoding. +/// +/// The type of each item in the first block _must_ be an `Option`. +/// This records the presence of that extension. +/// +/// Each item in the first block is prefixed with a match arm, +/// which must match an `ExtensionType` variant. This maps +/// the item to its extension type. +/// +/// Items in the second block are not encoded or decoded-to. +/// They therefore must have a reasonable `Default` value. +/// +/// All items must have a `Default`, `Debug` and `Clone`. +macro_rules! extension_struct { + ( + $(#[doc = $comment:literal])* + $struct_vis:vis struct $struct_name:ident$(<$struct_lt:lifetime>)* + { + $( + $(#[$item_attr:meta])* + $item_id:path => $item_vis:vis $item_slot:ident : Option<$item_ty:ty>, + )+ + } $( + { + $( + $(#[$meta_attr:meta])* + $meta_vis:vis $meta_slot:ident : $meta_ty:ty, + )+ + })* + ) => { + $(#[doc = $comment])* + #[non_exhaustive] + #[derive(Clone, Default)] + $struct_vis struct $struct_name$(<$struct_lt>)* { + $( + $(#[$item_attr])* + $item_vis $item_slot: Option<$item_ty>, + )+ + $($( + $(#[$meta_attr])* + $meta_vis $meta_slot: $meta_ty, + )+)* + } + + impl<'a> $struct_name$(<$struct_lt>)* { + /// Reads one extension typ, length and body from `r`. + /// + /// Unhandled extensions (according to `read_extension_body()` are inserted into `unknown_extensions`) + fn read_one( + &mut self, + r: &mut Reader<'a>, + mut unknown: impl FnMut(ExtensionType) -> Result<(), InvalidMessage>, + ) -> Result<ExtensionType, InvalidMessage> { + let typ = ExtensionType::read(r)?; + let len = usize::from(u16::read(r)?); + let mut ext_body = r.sub(len)?; + match self.read_extension_body(typ, &mut ext_body)? { + true => ext_body.expect_empty(stringify!($struct_name))?, + false => unknown(typ)?, + + }; + Ok(typ) + } + + /// Reads one extension body for an extension named by `typ`. + /// + /// Returns `true` if handled, `false` otherwise. + /// + /// `r` is fully consumed if `typ` is unhandled. + fn read_extension_body( + &mut self, + typ: ExtensionType, + r: &mut Reader<'a>, + ) -> Result<bool, InvalidMessage> { + match typ { + $( + $item_id => Self::read_once(r, $item_id, &mut self.$item_slot)?, + )* + + // read and ignore unhandled extensions + _ => { + r.rest(); + return Ok(false); + } + } + + Ok(true) + } + + /// Decode `r` as `T` into `out`, only if `out` is `None`. + fn read_once<T>(r: &mut Reader<'a>, id: ExtensionType, out: &mut Option<T>) -> Result<(), InvalidMessage> + where T: Codec<'a>, + { + if let Some(_) = out { + return Err(InvalidMessage::DuplicateExtension(u16::from(id))); + } + + *out = Some(T::read(r)?); + Ok(()) + } + + /// Encode one extension body for `typ` into `output`. + /// + /// Adds nothing to `output` if `typ` is absent from this + /// struct, either because it is `None` or unhandled by + /// this struct. + fn encode_one( + &self, + typ: ExtensionType, + output: &mut Vec<u8>, + ) { + match typ { + $( + $item_id => if let Some(item) = &self.$item_slot { + typ.encode(output); + item.encode(LengthPrefixedBuffer::new(ListLength::U16, output).buf); + }, + + )* + _ => {}, + } + } + + /// Return a list of extensions whose items are `Some` + #[allow(dead_code)] + pub(crate) fn collect_used(&self) -> Vec<ExtensionType> { + let mut r = Vec::with_capacity(Self::ALL_EXTENSIONS.len()); + + $( + if let Some(_) = &self.$item_slot { + r.push($item_id); + } + )* + + r + } + + /// Clone the value of the extension identified by `typ` from `source` to `self`. + /// + /// Does nothing if `typ` is not an extension handled by this object. + #[allow(dead_code)] + pub(crate) fn clone_one( + &mut self, + source: &Self, + typ: ExtensionType, + ) { + match typ { + $( + $item_id => self.$item_slot = source.$item_slot.clone(), + )* + _ => {}, + } + } + + /// Remove the extension identified by `typ` from `self`. + #[allow(dead_code)] + pub(crate) fn clear(&mut self, typ: ExtensionType) { + match typ { + $( + $item_id => self.$item_slot = None, + )* + _ => {}, + } + } + + /// Return true if all present extensions are named in `allowed` + #[allow(dead_code)] + pub(crate) fn only_contains(&self, allowed: &[ExtensionType]) -> bool { + $( + if let Some(_) = &self.$item_slot { + if !allowed.contains(&$item_id) { + return false; + } + } + )* + + true + } + + /// Return true if any extension named in `exts` is present. + #[allow(dead_code)] + pub(crate) fn contains_any(&self, exts: &[ExtensionType]) -> bool { + for e in exts { + if self.contains(*e) { + return true; + } + } + false + } + + fn contains(&self, e: ExtensionType) -> bool { + match e { + $( + + $item_id => self.$item_slot.is_some(), + )* + _ => false, + } + } + + /// Every `ExtensionType` this structure may encode/decode. + const ALL_EXTENSIONS: &'static [ExtensionType] = &[ + $($item_id,)* + ]; + } + + impl<'a> core::fmt::Debug for $struct_name$(<$struct_lt>)* { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct(stringify!($struct_name)); + $( + if let Some(ext) = &self.$item_slot { + ds.field(stringify!($item_slot), ext); + } + )* + $($( + ds.field(stringify!($meta_slot), &self.$meta_slot); + )+)* + ds.finish_non_exhaustive() + } + } + } +} diff --git a/vendor/rustls/src/msgs/message/inbound.rs b/vendor/rustls/src/msgs/message/inbound.rs new file mode 100644 index 00000000..f8b2181c --- /dev/null +++ b/vendor/rustls/src/msgs/message/inbound.rs @@ -0,0 +1,161 @@ +use core::ops::{Deref, DerefMut, Range}; + +use crate::enums::{ContentType, ProtocolVersion}; +use crate::error::{Error, PeerMisbehaved}; +use crate::msgs::fragmenter::MAX_FRAGMENT_LEN; + +/// A TLS frame, named TLSPlaintext in the standard. +/// +/// This inbound type borrows its encrypted payload from a buffer elsewhere. +/// It is used for joining and is consumed by decryption. +pub struct InboundOpaqueMessage<'a> { + pub typ: ContentType, + pub version: ProtocolVersion, + pub payload: BorrowedPayload<'a>, +} + +impl<'a> InboundOpaqueMessage<'a> { + /// Construct a new `InboundOpaqueMessage` from constituent fields. + /// + /// `payload` is borrowed. + pub fn new(typ: ContentType, version: ProtocolVersion, payload: &'a mut [u8]) -> Self { + Self { + typ, + version, + payload: BorrowedPayload(payload), + } + } + + /// Force conversion into a plaintext message. + /// + /// This should only be used for messages that are known to be in plaintext. Otherwise, the + /// `InboundOpaqueMessage` should be decrypted into a `PlainMessage` using a `MessageDecrypter`. + pub fn into_plain_message(self) -> InboundPlainMessage<'a> { + InboundPlainMessage { + typ: self.typ, + version: self.version, + payload: self.payload.into_inner(), + } + } + + /// Force conversion into a plaintext message. + /// + /// `range` restricts the resulting message: this function panics if it is out of range for + /// the underlying message payload. + /// + /// This should only be used for messages that are known to be in plaintext. Otherwise, the + /// `InboundOpaqueMessage` should be decrypted into a `PlainMessage` using a `MessageDecrypter`. + pub fn into_plain_message_range(self, range: Range<usize>) -> InboundPlainMessage<'a> { + InboundPlainMessage { + typ: self.typ, + version: self.version, + payload: &self.payload.into_inner()[range], + } + } + + /// For TLS1.3 (only), checks the length msg.payload is valid and removes the padding. + /// + /// Returns an error if the message (pre-unpadding) is too long, or the padding is invalid, + /// or the message (post-unpadding) is too long. + pub fn into_tls13_unpadded_message(mut self) -> Result<InboundPlainMessage<'a>, Error> { + let payload = &mut self.payload; + + if payload.len() > MAX_FRAGMENT_LEN + 1 { + return Err(Error::PeerSentOversizedRecord); + } + + self.typ = unpad_tls13_payload(payload); + if self.typ == ContentType::Unknown(0) { + return Err(PeerMisbehaved::IllegalTlsInnerPlaintext.into()); + } + + if payload.len() > MAX_FRAGMENT_LEN { + return Err(Error::PeerSentOversizedRecord); + } + + self.version = ProtocolVersion::TLSv1_3; + Ok(self.into_plain_message()) + } +} + +pub struct BorrowedPayload<'a>(&'a mut [u8]); + +impl Deref for BorrowedPayload<'_> { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl DerefMut for BorrowedPayload<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +impl<'a> BorrowedPayload<'a> { + pub fn truncate(&mut self, len: usize) { + if len >= self.len() { + return; + } + + self.0 = core::mem::take(&mut self.0) + .split_at_mut(len) + .0; + } + + pub(crate) fn into_inner(self) -> &'a mut [u8] { + self.0 + } + + pub(crate) fn pop(&mut self) -> Option<u8> { + if self.is_empty() { + return None; + } + + let len = self.len(); + let last = self[len - 1]; + self.truncate(len - 1); + Some(last) + } +} + +/// A TLS frame, named `TLSPlaintext` in the standard. +/// +/// This inbound type borrows its decrypted payload from the original buffer. +/// It results from decryption. +#[derive(Debug)] +pub struct InboundPlainMessage<'a> { + pub typ: ContentType, + pub version: ProtocolVersion, + pub payload: &'a [u8], +} + +impl InboundPlainMessage<'_> { + /// Returns true if the payload is a CCS message. + /// + /// We passthrough ChangeCipherSpec messages in the deframer without decrypting them. + /// Note: this is prior to the record layer, so is unencrypted. See + /// third paragraph of section 5 in RFC8446. + pub(crate) fn is_valid_ccs(&self) -> bool { + self.typ == ContentType::ChangeCipherSpec && self.payload == [0x01] + } +} + +/// Decode a TLS1.3 `TLSInnerPlaintext` encoding. +/// +/// `p` is a message payload, immediately post-decryption. This function +/// removes zero padding bytes, until a non-zero byte is encountered which is +/// the content type, which is returned. See RFC8446 s5.2. +/// +/// ContentType(0) is returned if the message payload is empty or all zeroes. +fn unpad_tls13_payload(p: &mut BorrowedPayload<'_>) -> ContentType { + loop { + match p.pop() { + Some(0) => {} + Some(content_type) => return ContentType::from(content_type), + None => return ContentType::Unknown(0), + } + } +} diff --git a/vendor/rustls/src/msgs/message/mod.rs b/vendor/rustls/src/msgs/message/mod.rs new file mode 100644 index 00000000..d5cecc94 --- /dev/null +++ b/vendor/rustls/src/msgs/message/mod.rs @@ -0,0 +1,262 @@ +use crate::enums::{AlertDescription, ContentType, HandshakeType, ProtocolVersion}; +use crate::error::InvalidMessage; +use crate::msgs::alert::AlertMessagePayload; +use crate::msgs::base::Payload; +use crate::msgs::ccs::ChangeCipherSpecPayload; +use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::enums::{AlertLevel, KeyUpdateRequest}; +use crate::msgs::handshake::{HandshakeMessagePayload, HandshakePayload}; + +mod inbound; +pub use inbound::{BorrowedPayload, InboundOpaqueMessage, InboundPlainMessage}; + +mod outbound; +use alloc::vec::Vec; + +pub(crate) use outbound::read_opaque_message_header; +pub use outbound::{OutboundChunks, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload}; + +#[derive(Debug)] +pub enum MessagePayload<'a> { + Alert(AlertMessagePayload), + // one handshake message, parsed + Handshake { + parsed: HandshakeMessagePayload<'a>, + encoded: Payload<'a>, + }, + // (potentially) multiple handshake messages, unparsed + HandshakeFlight(Payload<'a>), + ChangeCipherSpec(ChangeCipherSpecPayload), + ApplicationData(Payload<'a>), +} + +impl<'a> MessagePayload<'a> { + pub fn encode(&self, bytes: &mut Vec<u8>) { + match self { + Self::Alert(x) => x.encode(bytes), + Self::Handshake { encoded, .. } => bytes.extend(encoded.bytes()), + Self::HandshakeFlight(x) => bytes.extend(x.bytes()), + Self::ChangeCipherSpec(x) => x.encode(bytes), + Self::ApplicationData(x) => x.encode(bytes), + } + } + + pub fn handshake(parsed: HandshakeMessagePayload<'a>) -> Self { + Self::Handshake { + encoded: Payload::new(parsed.get_encoding()), + parsed, + } + } + + pub fn new( + typ: ContentType, + vers: ProtocolVersion, + payload: &'a [u8], + ) -> Result<Self, InvalidMessage> { + let mut r = Reader::init(payload); + match typ { + ContentType::ApplicationData => Ok(Self::ApplicationData(Payload::Borrowed(payload))), + ContentType::Alert => AlertMessagePayload::read(&mut r).map(MessagePayload::Alert), + ContentType::Handshake => { + HandshakeMessagePayload::read_version(&mut r, vers).map(|parsed| Self::Handshake { + parsed, + encoded: Payload::Borrowed(payload), + }) + } + ContentType::ChangeCipherSpec => { + ChangeCipherSpecPayload::read(&mut r).map(MessagePayload::ChangeCipherSpec) + } + _ => Err(InvalidMessage::InvalidContentType), + } + } + + pub fn content_type(&self) -> ContentType { + match self { + Self::Alert(_) => ContentType::Alert, + Self::Handshake { .. } | Self::HandshakeFlight(_) => ContentType::Handshake, + Self::ChangeCipherSpec(_) => ContentType::ChangeCipherSpec, + Self::ApplicationData(_) => ContentType::ApplicationData, + } + } + + pub(crate) fn into_owned(self) -> MessagePayload<'static> { + use MessagePayload::*; + match self { + Alert(x) => Alert(x), + Handshake { parsed, encoded } => Handshake { + parsed: parsed.into_owned(), + encoded: encoded.into_owned(), + }, + HandshakeFlight(x) => HandshakeFlight(x.into_owned()), + ChangeCipherSpec(x) => ChangeCipherSpec(x), + ApplicationData(x) => ApplicationData(x.into_owned()), + } + } +} + +impl From<Message<'_>> for PlainMessage { + fn from(msg: Message<'_>) -> Self { + let typ = msg.payload.content_type(); + let payload = match msg.payload { + MessagePayload::ApplicationData(payload) => payload.into_owned(), + _ => { + let mut buf = Vec::new(); + msg.payload.encode(&mut buf); + Payload::Owned(buf) + } + }; + + Self { + typ, + version: msg.version, + payload, + } + } +} + +/// A decrypted TLS frame +/// +/// This type owns all memory for its interior parts. It can be decrypted from an OpaqueMessage +/// or encrypted into an OpaqueMessage, and it is also used for joining and fragmenting. +#[derive(Clone, Debug)] +pub struct PlainMessage { + pub typ: ContentType, + pub version: ProtocolVersion, + pub payload: Payload<'static>, +} + +impl PlainMessage { + pub fn into_unencrypted_opaque(self) -> OutboundOpaqueMessage { + OutboundOpaqueMessage { + version: self.version, + typ: self.typ, + payload: PrefixedPayload::from(self.payload.bytes()), + } + } + + pub fn borrow_inbound(&self) -> InboundPlainMessage<'_> { + InboundPlainMessage { + version: self.version, + typ: self.typ, + payload: self.payload.bytes(), + } + } + + pub fn borrow_outbound(&self) -> OutboundPlainMessage<'_> { + OutboundPlainMessage { + version: self.version, + typ: self.typ, + payload: self.payload.bytes().into(), + } + } +} + +/// A message with decoded payload +#[derive(Debug)] +pub struct Message<'a> { + pub version: ProtocolVersion, + pub payload: MessagePayload<'a>, +} + +impl Message<'_> { + pub fn is_handshake_type(&self, hstyp: HandshakeType) -> bool { + // Bit of a layering violation, but OK. + if let MessagePayload::Handshake { parsed, .. } = &self.payload { + parsed.0.handshake_type() == hstyp + } else { + false + } + } + + pub fn build_alert(level: AlertLevel, desc: AlertDescription) -> Self { + Self { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::Alert(AlertMessagePayload { + level, + description: desc, + }), + } + } + + pub fn build_key_update_notify() -> Self { + Self { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::KeyUpdate(KeyUpdateRequest::UpdateNotRequested), + )), + } + } + + pub fn build_key_update_request() -> Self { + Self { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::KeyUpdate(KeyUpdateRequest::UpdateRequested), + )), + } + } + + #[cfg(feature = "std")] + pub(crate) fn into_owned(self) -> Message<'static> { + let Self { version, payload } = self; + Message { + version, + payload: payload.into_owned(), + } + } + + #[cfg(test)] + pub(crate) fn into_wire_bytes(self) -> Vec<u8> { + PlainMessage::from(self) + .into_unencrypted_opaque() + .encode() + } +} + +impl TryFrom<PlainMessage> for Message<'static> { + type Error = InvalidMessage; + + fn try_from(plain: PlainMessage) -> Result<Self, Self::Error> { + Ok(Self { + version: plain.version, + payload: MessagePayload::new(plain.typ, plain.version, plain.payload.bytes())? + .into_owned(), + }) + } +} + +/// Parses a plaintext message into a well-typed [`Message`]. +/// +/// A [`PlainMessage`] must contain plaintext content. Encrypted content should be stored in an +/// [`InboundOpaqueMessage`] and decrypted before being stored into a [`PlainMessage`]. +impl<'a> TryFrom<InboundPlainMessage<'a>> for Message<'a> { + type Error = InvalidMessage; + + fn try_from(plain: InboundPlainMessage<'a>) -> Result<Self, Self::Error> { + Ok(Self { + version: plain.version, + payload: MessagePayload::new(plain.typ, plain.version, plain.payload)?, + }) + } +} + +#[derive(Debug)] +pub enum MessageError { + TooShortForHeader, + TooShortForLength, + InvalidEmptyPayload, + MessageTooLarge, + InvalidContentType, + UnknownProtocolVersion, +} + +/// Content type, version and size. +pub(crate) const HEADER_SIZE: usize = 1 + 2 + 2; + +/// Maximum message payload size. +/// That's 2^14 payload bytes and a 2KB allowance for ciphertext overheads. +const MAX_PAYLOAD: u16 = 16_384 + 2048; + +/// Maximum on-the-wire message size. +#[cfg(feature = "std")] +pub(crate) const MAX_WIRE_SIZE: usize = MAX_PAYLOAD as usize + HEADER_SIZE; diff --git a/vendor/rustls/src/msgs/message/outbound.rs b/vendor/rustls/src/msgs/message/outbound.rs new file mode 100644 index 00000000..810c066c --- /dev/null +++ b/vendor/rustls/src/msgs/message/outbound.rs @@ -0,0 +1,422 @@ +use alloc::vec::Vec; + +use super::{HEADER_SIZE, MAX_PAYLOAD, MessageError, PlainMessage}; +use crate::enums::{ContentType, ProtocolVersion}; +use crate::msgs::base::Payload; +use crate::msgs::codec::{Codec, Reader}; +use crate::record_layer::RecordLayer; + +/// A TLS frame, named `TLSPlaintext` in the standard. +/// +/// This outbound type borrows its "to be encrypted" payload from the "user". +/// It is used for fragmenting and is consumed by encryption. +#[derive(Debug)] +pub struct OutboundPlainMessage<'a> { + pub typ: ContentType, + pub version: ProtocolVersion, + pub payload: OutboundChunks<'a>, +} + +impl OutboundPlainMessage<'_> { + pub(crate) fn encoded_len(&self, record_layer: &RecordLayer) -> usize { + HEADER_SIZE + record_layer.encrypted_len(self.payload.len()) + } + + pub(crate) fn to_unencrypted_opaque(&self) -> OutboundOpaqueMessage { + let mut payload = PrefixedPayload::with_capacity(self.payload.len()); + payload.extend_from_chunks(&self.payload); + OutboundOpaqueMessage { + version: self.version, + typ: self.typ, + payload, + } + } +} + +/// A collection of borrowed plaintext slices. +/// +/// Warning: OutboundChunks does not guarantee that the simplest variant is used. +/// Multiple can hold non fragmented or empty payloads. +#[derive(Debug, Clone)] +pub enum OutboundChunks<'a> { + /// A single byte slice. Contrary to `Multiple`, this uses a single pointer indirection + Single(&'a [u8]), + /// A collection of chunks (byte slices) + /// and cursors to single out a fragmented range of bytes. + /// OutboundChunks assumes that start <= end + Multiple { + chunks: &'a [&'a [u8]], + start: usize, + end: usize, + }, +} + +impl<'a> OutboundChunks<'a> { + /// Create a payload from a slice of byte slices. + /// If fragmented the cursors are added by default: start = 0, end = length + pub fn new(chunks: &'a [&'a [u8]]) -> Self { + if chunks.len() == 1 { + Self::Single(chunks[0]) + } else { + Self::Multiple { + chunks, + start: 0, + end: chunks + .iter() + .map(|chunk| chunk.len()) + .sum(), + } + } + } + + /// Create a payload with a single empty slice + pub fn new_empty() -> Self { + Self::Single(&[]) + } + + /// Flatten the slice of byte slices to an owned vector of bytes + pub fn to_vec(&self) -> Vec<u8> { + let mut vec = Vec::with_capacity(self.len()); + self.copy_to_vec(&mut vec); + vec + } + + /// Append all bytes to a vector + pub fn copy_to_vec(&self, vec: &mut Vec<u8>) { + match *self { + Self::Single(chunk) => vec.extend_from_slice(chunk), + Self::Multiple { chunks, start, end } => { + let mut size = 0; + for chunk in chunks.iter() { + let psize = size; + let len = chunk.len(); + size += len; + if size <= start || psize >= end { + continue; + } + let start = start.saturating_sub(psize); + let end = if end - psize < len { end - psize } else { len }; + vec.extend_from_slice(&chunk[start..end]); + } + } + } + } + + /// Split self in two, around an index + /// Works similarly to `split_at` in the core library, except it doesn't panic if out of bound + pub fn split_at(&self, mid: usize) -> (Self, Self) { + match *self { + Self::Single(chunk) => { + let mid = Ord::min(mid, chunk.len()); + (Self::Single(&chunk[..mid]), Self::Single(&chunk[mid..])) + } + Self::Multiple { chunks, start, end } => { + let mid = Ord::min(start + mid, end); + ( + Self::Multiple { + chunks, + start, + end: mid, + }, + Self::Multiple { + chunks, + start: mid, + end, + }, + ) + } + } + } + + /// Returns true if the payload is empty + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Returns the cumulative length of all chunks + pub fn len(&self) -> usize { + match self { + Self::Single(chunk) => chunk.len(), + Self::Multiple { start, end, .. } => end - start, + } + } +} + +impl<'a> From<&'a [u8]> for OutboundChunks<'a> { + fn from(payload: &'a [u8]) -> Self { + Self::Single(payload) + } +} + +/// A TLS frame, named `TLSPlaintext` in the standard. +/// +/// This outbound type owns all memory for its interior parts. +/// It results from encryption and is used for io write. +#[derive(Clone, Debug)] +pub struct OutboundOpaqueMessage { + pub typ: ContentType, + pub version: ProtocolVersion, + pub payload: PrefixedPayload, +} + +impl OutboundOpaqueMessage { + /// Construct a new `OpaqueMessage` from constituent fields. + /// + /// `body` is moved into the `payload` field. + pub fn new(typ: ContentType, version: ProtocolVersion, payload: PrefixedPayload) -> Self { + Self { + typ, + version, + payload, + } + } + + /// Construct by decoding from a [`Reader`]. + /// + /// `MessageError` allows callers to distinguish between valid prefixes (might + /// become valid if we read more data) and invalid data. + pub fn read(r: &mut Reader<'_>) -> Result<Self, MessageError> { + let (typ, version, len) = read_opaque_message_header(r)?; + + let content = r + .take(len as usize) + .ok_or(MessageError::TooShortForLength)?; + + Ok(Self { + typ, + version, + payload: PrefixedPayload::from(content), + }) + } + + pub fn encode(self) -> Vec<u8> { + let length = self.payload.len() as u16; + let mut encoded_payload = self.payload.0; + encoded_payload[0] = self.typ.into(); + encoded_payload[1..3].copy_from_slice(&self.version.to_array()); + encoded_payload[3..5].copy_from_slice(&(length).to_be_bytes()); + encoded_payload + } + + /// Force conversion into a plaintext message. + /// + /// This should only be used for messages that are known to be in plaintext. Otherwise, the + /// `OutboundOpaqueMessage` should be decrypted into a `PlainMessage` using a `MessageDecrypter`. + pub fn into_plain_message(self) -> PlainMessage { + PlainMessage { + version: self.version, + typ: self.typ, + payload: Payload::Owned(self.payload.as_ref().to_vec()), + } + } +} + +#[derive(Clone, Debug)] +pub struct PrefixedPayload(Vec<u8>); + +impl PrefixedPayload { + pub fn with_capacity(capacity: usize) -> Self { + let mut prefixed_payload = Vec::with_capacity(HEADER_SIZE + capacity); + prefixed_payload.resize(HEADER_SIZE, 0); + Self(prefixed_payload) + } + + pub fn extend_from_slice(&mut self, slice: &[u8]) { + self.0.extend_from_slice(slice) + } + + pub fn extend_from_chunks(&mut self, chunks: &OutboundChunks<'_>) { + chunks.copy_to_vec(&mut self.0) + } + + pub fn truncate(&mut self, len: usize) { + self.0.truncate(len + HEADER_SIZE) + } + + fn len(&self) -> usize { + self.0.len() - HEADER_SIZE + } +} + +impl AsRef<[u8]> for PrefixedPayload { + fn as_ref(&self) -> &[u8] { + &self.0[HEADER_SIZE..] + } +} + +impl AsMut<[u8]> for PrefixedPayload { + fn as_mut(&mut self) -> &mut [u8] { + &mut self.0[HEADER_SIZE..] + } +} + +impl<'a> Extend<&'a u8> for PrefixedPayload { + fn extend<T: IntoIterator<Item = &'a u8>>(&mut self, iter: T) { + self.0.extend(iter) + } +} + +impl From<&[u8]> for PrefixedPayload { + fn from(content: &[u8]) -> Self { + let mut payload = Vec::with_capacity(HEADER_SIZE + content.len()); + payload.extend(&[0u8; HEADER_SIZE]); + payload.extend(content); + Self(payload) + } +} + +impl<const N: usize> From<&[u8; N]> for PrefixedPayload { + fn from(content: &[u8; N]) -> Self { + Self::from(&content[..]) + } +} + +pub(crate) fn read_opaque_message_header( + r: &mut Reader<'_>, +) -> Result<(ContentType, ProtocolVersion, u16), MessageError> { + let typ = ContentType::read(r).map_err(|_| MessageError::TooShortForHeader)?; + // Don't accept any new content-types. + if let ContentType::Unknown(_) = typ { + return Err(MessageError::InvalidContentType); + } + + let version = ProtocolVersion::read(r).map_err(|_| MessageError::TooShortForHeader)?; + // Accept only versions 0x03XX for any XX. + match &version { + ProtocolVersion::Unknown(v) if (v & 0xff00) != 0x0300 => { + return Err(MessageError::UnknownProtocolVersion); + } + _ => {} + }; + + let len = u16::read(r).map_err(|_| MessageError::TooShortForHeader)?; + + // Reject undersize messages + // implemented per section 5.1 of RFC8446 (TLSv1.3) + // per section 6.2.1 of RFC5246 (TLSv1.2) + if typ != ContentType::ApplicationData && len == 0 { + return Err(MessageError::InvalidEmptyPayload); + } + + // Reject oversize messages + if len >= MAX_PAYLOAD { + return Err(MessageError::MessageTooLarge); + } + + Ok((typ, version, len)) +} + +#[cfg(test)] +mod tests { + use std::{println, vec}; + + use super::*; + + #[test] + fn split_at_with_single_slice() { + let owner: &[u8] = &[0, 1, 2, 3, 4, 5, 6, 7]; + let borrowed_payload = OutboundChunks::Single(owner); + + let (before, after) = borrowed_payload.split_at(6); + println!("before:{before:?}\nafter:{after:?}"); + assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5]); + assert_eq!(after.to_vec(), &[6, 7]); + } + + #[test] + fn split_at_with_multiple_slices() { + let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]]; + let borrowed_payload = OutboundChunks::new(&owner); + + let (before, after) = borrowed_payload.split_at(3); + println!("before:{before:?}\nafter:{after:?}"); + assert_eq!(before.to_vec(), &[0, 1, 2]); + assert_eq!(after.to_vec(), &[3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + + let (before, after) = borrowed_payload.split_at(8); + println!("before:{before:?}\nafter:{after:?}"); + assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7]); + assert_eq!(after.to_vec(), &[8, 9, 10, 11, 12]); + + let (before, after) = borrowed_payload.split_at(11); + println!("before:{before:?}\nafter:{after:?}"); + assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!(after.to_vec(), &[11, 12]); + } + + #[test] + fn split_out_of_bounds() { + let owner: Vec<&[u8]> = vec![&[0, 1, 2, 3], &[4, 5], &[6, 7, 8], &[9, 10, 11, 12]]; + + let single_payload = OutboundChunks::Single(owner[0]); + let (before, after) = single_payload.split_at(17); + println!("before:{before:?}\nafter:{after:?}"); + assert_eq!(before.to_vec(), &[0, 1, 2, 3]); + assert!(after.is_empty()); + + let multiple_payload = OutboundChunks::new(&owner); + let (before, after) = multiple_payload.split_at(17); + println!("before:{before:?}\nafter:{after:?}"); + assert_eq!(before.to_vec(), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); + assert!(after.is_empty()); + + let empty_payload = OutboundChunks::new_empty(); + let (before, after) = empty_payload.split_at(17); + println!("before:{before:?}\nafter:{after:?}"); + assert!(before.is_empty()); + assert!(after.is_empty()); + } + + #[test] + fn empty_slices_mixed() { + let owner: Vec<&[u8]> = vec![&[], &[], &[0], &[], &[1, 2], &[], &[3], &[4], &[], &[]]; + let mut borrowed_payload = OutboundChunks::new(&owner); + let mut fragment_count = 0; + let mut fragment; + let expected_fragments: &[&[u8]] = &[&[0, 1], &[2, 3], &[4]]; + + while !borrowed_payload.is_empty() { + (fragment, borrowed_payload) = borrowed_payload.split_at(2); + println!("{fragment:?}"); + assert_eq!(&expected_fragments[fragment_count], &fragment.to_vec()); + fragment_count += 1; + } + assert_eq!(fragment_count, expected_fragments.len()); + } + + #[test] + fn exhaustive_splitting() { + let owner: Vec<u8> = (0..127).collect(); + let slices = (0..7) + .map(|i| &owner[((1 << i) - 1)..((1 << (i + 1)) - 1)]) + .collect::<Vec<_>>(); + let payload = OutboundChunks::new(&slices); + + assert_eq!(payload.to_vec(), owner); + println!("{payload:#?}"); + + for start in 0..128 { + for end in start..128 { + for mid in 0..(end - start) { + let witness = owner[start..end].split_at(mid); + let split_payload = payload + .split_at(end) + .0 + .split_at(start) + .1 + .split_at(mid); + assert_eq!( + witness.0, + split_payload.0.to_vec(), + "start: {start}, mid:{mid}, end:{end}" + ); + assert_eq!( + witness.1, + split_payload.1.to_vec(), + "start: {start}, mid:{mid}, end:{end}" + ); + } + } + } + } +} diff --git a/vendor/rustls/src/msgs/message_test.rs b/vendor/rustls/src/msgs/message_test.rs new file mode 100644 index 00000000..965a3572 --- /dev/null +++ b/vendor/rustls/src/msgs/message_test.rs @@ -0,0 +1,124 @@ +use std::io::Read; +use std::path::{Path, PathBuf}; +use std::prelude::v1::*; +use std::{format, fs, println, vec}; + +use super::base::Payload; +use super::codec::Reader; +use super::enums::AlertLevel; +use super::message::{Message, OutboundOpaqueMessage, PlainMessage}; +use crate::enums::{AlertDescription, HandshakeType}; +use crate::msgs::base::{MaybeEmpty, NonEmpty, PayloadU8, PayloadU16, PayloadU24}; + +#[test] +fn test_read_fuzz_corpus() { + fn corpus_dir() -> PathBuf { + let from_subcrate = Path::new("../fuzz/corpus/message"); + let from_root = Path::new("fuzz/corpus/message"); + + if from_root.is_dir() { + from_root.to_path_buf() + } else { + from_subcrate.to_path_buf() + } + } + + for file in fs::read_dir(corpus_dir()).unwrap() { + let mut f = fs::File::open(file.unwrap().path()).unwrap(); + let mut bytes = Vec::new(); + f.read_to_end(&mut bytes).unwrap(); + + let mut rd = Reader::init(&bytes); + let msg = OutboundOpaqueMessage::read(&mut rd) + .unwrap() + .into_plain_message(); + println!("{msg:?}"); + + let Ok(msg) = Message::try_from(msg) else { + continue; + }; + + let enc = PlainMessage::from(msg) + .into_unencrypted_opaque() + .encode(); + assert_eq!(bytes.to_vec(), enc); + assert_eq!(bytes[..rd.used()].to_vec(), enc); + } +} + +#[test] +fn can_read_safari_client_hello_with_ip_address_in_sni_extension() { + let _ = env_logger::Builder::new() + .filter(None, log::LevelFilter::Trace) + .try_init(); + + let bytes = b"\ + \x16\x03\x01\x00\xeb\x01\x00\x00\xe7\x03\x03\xb6\x1f\xe4\x3a\x55\ + \x90\x3e\xc0\x28\x9c\x12\xe0\x5c\x84\xea\x90\x1b\xfb\x11\xfc\xbd\ + \x25\x55\xda\x9f\x51\x93\x1b\x8d\x92\x66\xfd\x00\x00\x2e\xc0\x2c\ + \xc0\x2b\xc0\x24\xc0\x23\xc0\x0a\xc0\x09\xcc\xa9\xc0\x30\xc0\x2f\ + \xc0\x28\xc0\x27\xc0\x14\xc0\x13\xcc\xa8\x00\x9d\x00\x9c\x00\x3d\ + \x00\x3c\x00\x35\x00\x2f\xc0\x08\xc0\x12\x00\x0a\x01\x00\x00\x90\ + \xff\x01\x00\x01\x00\x00\x00\x00\x0e\x00\x0c\x00\x00\x09\x31\x32\ + \x37\x2e\x30\x2e\x30\x2e\x31\x00\x17\x00\x00\x00\x0d\x00\x18\x00\ + \x16\x04\x03\x08\x04\x04\x01\x05\x03\x02\x03\x08\x05\x08\x05\x05\ + \x01\x08\x06\x06\x01\x02\x01\x00\x05\x00\x05\x01\x00\x00\x00\x00\ + \x33\x74\x00\x00\x00\x12\x00\x00\x00\x10\x00\x30\x00\x2e\x02\x68\ + \x32\x05\x68\x32\x2d\x31\x36\x05\x68\x32\x2d\x31\x35\x05\x68\x32\ + \x2d\x31\x34\x08\x73\x70\x64\x79\x2f\x33\x2e\x31\x06\x73\x70\x64\ + \x79\x2f\x33\x08\x68\x74\x74\x70\x2f\x31\x2e\x31\x00\x0b\x00\x02\ + \x01\x00\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19"; + let mut rd = Reader::init(bytes); + let m = OutboundOpaqueMessage::read(&mut rd).unwrap(); + println!("m = {m:?}"); + Message::try_from(m.into_plain_message()).unwrap(); +} + +#[test] +fn alert_is_not_handshake() { + let m = Message::build_alert(AlertLevel::Fatal, AlertDescription::DecodeError); + assert!(!m.is_handshake_type(HandshakeType::ClientHello)); +} + +#[test] +fn construct_all_types() { + let samples = [ + &b"\x14\x03\x04\x00\x01\x01"[..], + &b"\x15\x03\x04\x00\x02\x01\x16"[..], + &b"\x16\x03\x04\x00\x05\x18\x00\x00\x01\x00"[..], + &b"\x17\x03\x04\x00\x04\x11\x22\x33\x44"[..], + &b"\x18\x03\x04\x00\x04\x11\x22\x33\x44"[..], + ]; + for &bytes in samples.iter() { + let m = OutboundOpaqueMessage::read(&mut Reader::init(bytes)).unwrap(); + println!("m = {m:?}"); + let m = Message::try_from(m.into_plain_message()); + println!("m' = {m:?}"); + } +} + +#[test] +fn debug_payload() { + assert_eq!("01020304", format!("{:?}", Payload::new(vec![1, 2, 3, 4]))); + assert_eq!( + "01020304", + format!("{:?}", PayloadU8::<NonEmpty>::new(vec![1, 2, 3, 4])) + ); + assert_eq!( + "01020304", + format!("{:?}", PayloadU16::<MaybeEmpty>::new(vec![1, 2, 3, 4])) + ); + assert_eq!( + "01020304", + format!("{:?}", PayloadU24(Payload::new(vec![1, 2, 3, 4]))) + ); +} + +#[test] +fn into_wire_format() { + // Message::into_wire_bytes() include both message-level and handshake-level headers + assert_eq!( + Message::build_key_update_request().into_wire_bytes(), + &[0x16, 0x3, 0x4, 0x0, 0x5, 0x18, 0x0, 0x0, 0x1, 0x1] + ); +} diff --git a/vendor/rustls/src/msgs/mod.rs b/vendor/rustls/src/msgs/mod.rs new file mode 100644 index 00000000..270c2dbc --- /dev/null +++ b/vendor/rustls/src/msgs/mod.rs @@ -0,0 +1,74 @@ +#![allow(missing_docs)] +//! <https://langsec.org> cat says: +//! +//! ```text +//! ___ _ _ _ _ ___ ___ ___ ___ ___ _ _ ___ _____ ___ ___ _ _ +//! | __| | | | | | | | _ \ __/ __/ _ \ / __| \| |_ _|_ _|_ _/ _ \| \| | +//! | _|| |_| | |__| |__ | / _| (_| (_) | (_ | .` || | | | | | (_) | .` | +//! |_| \___/|____|____| |_|_\___\___\___/ \___|_|\_|___| |_| |___\___/|_|\_| +//! +//! +//! .__....._ _.....__, +//! .": o :': ;': o :". +//! `. `-' .'. .'. `-' .' +//! `---' `---' +//! +//! _...----... ... ... ...----..._ +//! .-'__..-""'---- `. `"` .' ----'""-..__`-. +//! '.-' _.--"""' `-._.-' '"""--._ `-.` +//! ' .-"' : `"-. ` +//! ' `. _.'"'._ .' ` +//! `. ,.-'" "'-., .' +//! `. .' +//! `-._ _.-' +//! `"'--...___...--'"` +//! +//! ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ ___ _ _ ___ +//! | _ ) __| __/ _ \| _ \ __| | _ \ _ \/ _ \ / __| __/ __/ __|_ _| \| |/ __| +//! | _ \ _|| _| (_) | / _| | _/ / (_) | (__| _|\__ \__ \| || .` | (_ | +//! |___/___|_| \___/|_|_\___| |_| |_|_\\___/ \___|___|___/___/___|_|\_|\___| +//! ``` +//! +//! <https://langsec.org/ForWantOfANail-h2hc2014.pdf> + +#[macro_use] +mod macros; + +pub(crate) mod alert; +pub(crate) mod base; +pub(crate) mod ccs; +pub(crate) mod codec; +pub(crate) mod deframer; +pub(crate) mod enums; +pub(crate) mod fragmenter; +pub(crate) mod handshake; +pub(crate) mod message; +pub(crate) mod persist; + +#[cfg(test)] +mod handshake_test; + +pub mod ffdhe_groups; +#[cfg(test)] +mod message_test; + +#[cfg(test)] +mod tests { + use super::codec::Reader; + use super::message::{Message, OutboundOpaqueMessage}; + + #[test] + fn smoketest() { + let bytes = include_bytes!("../testdata/handshake-test.1.bin"); + let mut r = Reader::init(bytes); + + while r.any_left() { + let m = OutboundOpaqueMessage::read(&mut r).unwrap(); + + let out = m.clone().encode(); + assert!(!out.is_empty()); + + Message::try_from(m.into_plain_message()).unwrap(); + } + } +} diff --git a/vendor/rustls/src/msgs/persist.rs b/vendor/rustls/src/msgs/persist.rs new file mode 100644 index 00000000..8231c8ff --- /dev/null +++ b/vendor/rustls/src/msgs/persist.rs @@ -0,0 +1,492 @@ +use alloc::vec::Vec; +use core::cmp; + +use pki_types::{DnsName, UnixTime}; +use zeroize::Zeroizing; + +use crate::client::ResolvesClientCert; +use crate::enums::{CipherSuite, ProtocolVersion}; +use crate::error::InvalidMessage; +use crate::msgs::base::{MaybeEmpty, PayloadU8, PayloadU16}; +use crate::msgs::codec::{Codec, Reader}; +#[cfg(feature = "tls12")] +use crate::msgs::handshake::SessionId; +use crate::msgs::handshake::{CertificateChain, ProtocolName}; +use crate::sync::{Arc, Weak}; +#[cfg(feature = "tls12")] +use crate::tls12::Tls12CipherSuite; +use crate::tls13::Tls13CipherSuite; +use crate::verify::ServerCertVerifier; + +pub(crate) struct Retrieved<T> { + pub(crate) value: T, + retrieved_at: UnixTime, +} + +impl<T> Retrieved<T> { + pub(crate) fn new(value: T, retrieved_at: UnixTime) -> Self { + Self { + value, + retrieved_at, + } + } + + pub(crate) fn map<M>(&self, f: impl FnOnce(&T) -> Option<&M>) -> Option<Retrieved<&M>> { + Some(Retrieved { + value: f(&self.value)?, + retrieved_at: self.retrieved_at, + }) + } +} + +impl Retrieved<&Tls13ClientSessionValue> { + pub(crate) fn obfuscated_ticket_age(&self) -> u32 { + let age_secs = self + .retrieved_at + .as_secs() + .saturating_sub(self.value.common.epoch); + let age_millis = age_secs as u32 * 1000; + age_millis.wrapping_add(self.value.age_add) + } +} + +impl<T: core::ops::Deref<Target = ClientSessionCommon>> Retrieved<T> { + pub(crate) fn has_expired(&self) -> bool { + let common = &*self.value; + common.lifetime_secs != 0 + && common + .epoch + .saturating_add(u64::from(common.lifetime_secs)) + < self.retrieved_at.as_secs() + } +} + +impl<T> core::ops::Deref for Retrieved<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.value + } +} + +#[derive(Debug)] +pub struct Tls13ClientSessionValue { + suite: &'static Tls13CipherSuite, + age_add: u32, + max_early_data_size: u32, + pub(crate) common: ClientSessionCommon, + quic_params: PayloadU16, +} + +impl Tls13ClientSessionValue { + pub(crate) fn new( + suite: &'static Tls13CipherSuite, + ticket: Arc<PayloadU16>, + secret: &[u8], + server_cert_chain: CertificateChain<'static>, + server_cert_verifier: &Arc<dyn ServerCertVerifier>, + client_creds: &Arc<dyn ResolvesClientCert>, + time_now: UnixTime, + lifetime_secs: u32, + age_add: u32, + max_early_data_size: u32, + ) -> Self { + Self { + suite, + age_add, + max_early_data_size, + common: ClientSessionCommon::new( + ticket, + secret, + time_now, + lifetime_secs, + server_cert_chain, + server_cert_verifier, + client_creds, + ), + quic_params: PayloadU16::new(Vec::new()), + } + } + + pub fn max_early_data_size(&self) -> u32 { + self.max_early_data_size + } + + pub fn suite(&self) -> &'static Tls13CipherSuite { + self.suite + } + + #[doc(hidden)] + /// Test only: rewind epoch by `delta` seconds. + pub fn rewind_epoch(&mut self, delta: u32) { + self.common.epoch -= delta as u64; + } + + #[doc(hidden)] + /// Test only: replace `max_early_data_size` with `new` + pub fn _private_set_max_early_data_size(&mut self, new: u32) { + self.max_early_data_size = new; + } + + pub fn set_quic_params(&mut self, quic_params: &[u8]) { + self.quic_params = PayloadU16::new(quic_params.to_vec()); + } + + pub fn quic_params(&self) -> Vec<u8> { + self.quic_params.0.clone() + } +} + +impl core::ops::Deref for Tls13ClientSessionValue { + type Target = ClientSessionCommon; + + fn deref(&self) -> &Self::Target { + &self.common + } +} + +#[derive(Debug, Clone)] +pub struct Tls12ClientSessionValue { + #[cfg(feature = "tls12")] + suite: &'static Tls12CipherSuite, + #[cfg(feature = "tls12")] + pub(crate) session_id: SessionId, + #[cfg(feature = "tls12")] + extended_ms: bool, + #[doc(hidden)] + #[cfg(feature = "tls12")] + pub(crate) common: ClientSessionCommon, +} + +#[cfg(feature = "tls12")] +impl Tls12ClientSessionValue { + pub(crate) fn new( + suite: &'static Tls12CipherSuite, + session_id: SessionId, + ticket: Arc<PayloadU16>, + master_secret: &[u8], + server_cert_chain: CertificateChain<'static>, + server_cert_verifier: &Arc<dyn ServerCertVerifier>, + client_creds: &Arc<dyn ResolvesClientCert>, + time_now: UnixTime, + lifetime_secs: u32, + extended_ms: bool, + ) -> Self { + Self { + suite, + session_id, + extended_ms, + common: ClientSessionCommon::new( + ticket, + master_secret, + time_now, + lifetime_secs, + server_cert_chain, + server_cert_verifier, + client_creds, + ), + } + } + + pub(crate) fn ticket(&mut self) -> Arc<PayloadU16> { + self.common.ticket.clone() + } + + pub(crate) fn extended_ms(&self) -> bool { + self.extended_ms + } + + pub(crate) fn suite(&self) -> &'static Tls12CipherSuite { + self.suite + } + + #[doc(hidden)] + /// Test only: rewind epoch by `delta` seconds. + pub fn rewind_epoch(&mut self, delta: u32) { + self.common.epoch -= delta as u64; + } +} + +#[cfg(feature = "tls12")] +impl core::ops::Deref for Tls12ClientSessionValue { + type Target = ClientSessionCommon; + + fn deref(&self) -> &Self::Target { + &self.common + } +} + +#[derive(Debug, Clone)] +pub struct ClientSessionCommon { + ticket: Arc<PayloadU16>, + secret: Zeroizing<PayloadU8>, + epoch: u64, + lifetime_secs: u32, + server_cert_chain: Arc<CertificateChain<'static>>, + server_cert_verifier: Weak<dyn ServerCertVerifier>, + client_creds: Weak<dyn ResolvesClientCert>, +} + +impl ClientSessionCommon { + fn new( + ticket: Arc<PayloadU16>, + secret: &[u8], + time_now: UnixTime, + lifetime_secs: u32, + server_cert_chain: CertificateChain<'static>, + server_cert_verifier: &Arc<dyn ServerCertVerifier>, + client_creds: &Arc<dyn ResolvesClientCert>, + ) -> Self { + Self { + ticket, + secret: Zeroizing::new(PayloadU8::new(secret.to_vec())), + epoch: time_now.as_secs(), + lifetime_secs: cmp::min(lifetime_secs, MAX_TICKET_LIFETIME), + server_cert_chain: Arc::new(server_cert_chain), + server_cert_verifier: Arc::downgrade(server_cert_verifier), + client_creds: Arc::downgrade(client_creds), + } + } + + pub(crate) fn compatible_config( + &self, + server_cert_verifier: &Arc<dyn ServerCertVerifier>, + client_creds: &Arc<dyn ResolvesClientCert>, + ) -> bool { + let same_verifier = Weak::ptr_eq( + &Arc::downgrade(server_cert_verifier), + &self.server_cert_verifier, + ); + let same_creds = Weak::ptr_eq(&Arc::downgrade(client_creds), &self.client_creds); + + match (same_verifier, same_creds) { + (true, true) => true, + (false, _) => { + crate::log::trace!("resumption not allowed between different ServerCertVerifiers"); + false + } + (_, _) => { + crate::log::trace!( + "resumption not allowed between different ResolvesClientCert values" + ); + false + } + } + } + + pub(crate) fn server_cert_chain(&self) -> &CertificateChain<'static> { + &self.server_cert_chain + } + + pub(crate) fn secret(&self) -> &[u8] { + self.secret.0.as_ref() + } + + pub(crate) fn ticket(&self) -> &[u8] { + self.ticket.0.as_ref() + } +} + +static MAX_TICKET_LIFETIME: u32 = 7 * 24 * 60 * 60; + +/// This is the maximum allowed skew between server and client clocks, over +/// the maximum ticket lifetime period. This encompasses TCP retransmission +/// times in case packet loss occurs when the client sends the ClientHello +/// or receives the NewSessionTicket, _and_ actual clock skew over this period. +static MAX_FRESHNESS_SKEW_MS: u32 = 60 * 1000; + +// --- Server types --- +#[derive(Debug)] +pub struct ServerSessionValue { + pub(crate) sni: Option<DnsName<'static>>, + pub(crate) version: ProtocolVersion, + pub(crate) cipher_suite: CipherSuite, + pub(crate) master_secret: Zeroizing<PayloadU8>, + pub(crate) extended_ms: bool, + pub(crate) client_cert_chain: Option<CertificateChain<'static>>, + pub(crate) alpn: Option<PayloadU8>, + pub(crate) application_data: PayloadU16, + pub creation_time_sec: u64, + pub(crate) age_obfuscation_offset: u32, + freshness: Option<bool>, +} + +impl Codec<'_> for ServerSessionValue { + fn encode(&self, bytes: &mut Vec<u8>) { + if let Some(sni) = &self.sni { + 1u8.encode(bytes); + let sni_bytes: &str = sni.as_ref(); + PayloadU8::<MaybeEmpty>::encode_slice(sni_bytes.as_bytes(), bytes); + } else { + 0u8.encode(bytes); + } + self.version.encode(bytes); + self.cipher_suite.encode(bytes); + self.master_secret.encode(bytes); + (u8::from(self.extended_ms)).encode(bytes); + if let Some(chain) = &self.client_cert_chain { + 1u8.encode(bytes); + chain.encode(bytes); + } else { + 0u8.encode(bytes); + } + if let Some(alpn) = &self.alpn { + 1u8.encode(bytes); + alpn.encode(bytes); + } else { + 0u8.encode(bytes); + } + self.application_data.encode(bytes); + self.creation_time_sec.encode(bytes); + self.age_obfuscation_offset + .encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let has_sni = u8::read(r)?; + let sni = if has_sni == 1 { + let dns_name = PayloadU8::<MaybeEmpty>::read(r)?; + let dns_name = match DnsName::try_from(dns_name.0.as_slice()) { + Ok(dns_name) => dns_name.to_owned(), + Err(_) => return Err(InvalidMessage::InvalidServerName), + }; + + Some(dns_name) + } else { + None + }; + + let v = ProtocolVersion::read(r)?; + let cs = CipherSuite::read(r)?; + let ms = Zeroizing::new(PayloadU8::read(r)?); + let ems = u8::read(r)?; + let has_ccert = u8::read(r)? == 1; + let ccert = if has_ccert { + Some(CertificateChain::read(r)?.into_owned()) + } else { + None + }; + let has_alpn = u8::read(r)? == 1; + let alpn = if has_alpn { + Some(PayloadU8::read(r)?) + } else { + None + }; + let application_data = PayloadU16::read(r)?; + let creation_time_sec = u64::read(r)?; + let age_obfuscation_offset = u32::read(r)?; + + Ok(Self { + sni, + version: v, + cipher_suite: cs, + master_secret: ms, + extended_ms: ems == 1u8, + client_cert_chain: ccert, + alpn, + application_data, + creation_time_sec, + age_obfuscation_offset, + freshness: None, + }) + } +} + +impl ServerSessionValue { + pub(crate) fn new( + sni: Option<&DnsName<'_>>, + v: ProtocolVersion, + cs: CipherSuite, + ms: &[u8], + client_cert_chain: Option<CertificateChain<'static>>, + alpn: Option<ProtocolName>, + application_data: Vec<u8>, + creation_time: UnixTime, + age_obfuscation_offset: u32, + ) -> Self { + Self { + sni: sni.map(|dns| dns.to_owned()), + version: v, + cipher_suite: cs, + master_secret: Zeroizing::new(PayloadU8::new(ms.to_vec())), + extended_ms: false, + client_cert_chain, + alpn: alpn.map(|p| PayloadU8::new(p.as_ref().to_vec())), + application_data: PayloadU16::new(application_data), + creation_time_sec: creation_time.as_secs(), + age_obfuscation_offset, + freshness: None, + } + } + + #[cfg(feature = "tls12")] + pub(crate) fn set_extended_ms_used(&mut self) { + self.extended_ms = true; + } + + pub(crate) fn set_freshness( + mut self, + obfuscated_client_age_ms: u32, + time_now: UnixTime, + ) -> Self { + let client_age_ms = obfuscated_client_age_ms.wrapping_sub(self.age_obfuscation_offset); + let server_age_ms = (time_now + .as_secs() + .saturating_sub(self.creation_time_sec) as u32) + .saturating_mul(1000); + + let age_difference = server_age_ms.abs_diff(client_age_ms); + + self.freshness = Some(age_difference <= MAX_FRESHNESS_SKEW_MS); + self + } + + pub(crate) fn is_fresh(&self) -> bool { + self.freshness.unwrap_or_default() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(feature = "std")] // for UnixTime::now + #[test] + fn serversessionvalue_is_debug() { + use std::{println, vec}; + let ssv = ServerSessionValue::new( + None, + ProtocolVersion::TLSv1_3, + CipherSuite::TLS13_AES_128_GCM_SHA256, + &[1, 2, 3], + None, + None, + vec![4, 5, 6], + UnixTime::now(), + 0x12345678, + ); + println!("{ssv:?}"); + } + + #[test] + fn serversessionvalue_no_sni() { + let bytes = [ + 0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d, + ]; + let mut rd = Reader::init(&bytes); + let ssv = ServerSessionValue::read(&mut rd).unwrap(); + assert_eq!(ssv.get_encoding(), bytes); + } + + #[test] + fn serversessionvalue_with_cert() { + let bytes = [ + 0x00, 0x03, 0x03, 0xc0, 0x23, 0x03, 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0xfe, 0xed, 0xf0, 0x0d, + ]; + let mut rd = Reader::init(&bytes); + let ssv = ServerSessionValue::read(&mut rd).unwrap(); + assert_eq!(ssv.get_encoding(), bytes); + } +} diff --git a/vendor/rustls/src/polyfill.rs b/vendor/rustls/src/polyfill.rs new file mode 100644 index 00000000..82571a22 --- /dev/null +++ b/vendor/rustls/src/polyfill.rs @@ -0,0 +1,9 @@ +/// Non-panicking `let (nonce, ciphertext) = ciphertext.split_at(...)`. +// TODO(XXX): remove once MSRV reaches 1.80 +#[allow(dead_code)] // Complicated conditional compilation guards elided +pub(crate) fn try_split_at(slice: &[u8], mid: usize) -> Option<(&[u8], &[u8])> { + match mid > slice.len() { + true => None, + false => Some(slice.split_at(mid)), + } +} diff --git a/vendor/rustls/src/quic.rs b/vendor/rustls/src/quic.rs new file mode 100644 index 00000000..f00f3bba --- /dev/null +++ b/vendor/rustls/src/quic.rs @@ -0,0 +1,1022 @@ +use alloc::boxed::Box; +use alloc::collections::VecDeque; +use alloc::vec::Vec; +#[cfg(feature = "std")] +use core::fmt::Debug; + +/// This module contains optional APIs for implementing QUIC TLS. +use crate::common_state::Side; +use crate::crypto::cipher::{AeadKey, Iv}; +use crate::crypto::tls13::{Hkdf, HkdfExpander, OkmBlock}; +use crate::enums::AlertDescription; +use crate::error::Error; +use crate::tls13::Tls13CipherSuite; +use crate::tls13::key_schedule::{ + hkdf_expand_label, hkdf_expand_label_aead_key, hkdf_expand_label_block, +}; + +#[cfg(feature = "std")] +mod connection { + use alloc::vec::Vec; + use core::fmt::{self, Debug}; + use core::ops::{Deref, DerefMut}; + + use pki_types::ServerName; + + use super::{DirectionalKeys, KeyChange, Version}; + use crate::client::{ClientConfig, ClientConnectionData}; + use crate::common_state::{CommonState, DEFAULT_BUFFER_LIMIT, Protocol}; + use crate::conn::{ConnectionCore, SideData}; + use crate::enums::{AlertDescription, ContentType, ProtocolVersion}; + use crate::error::Error; + use crate::msgs::base::Payload; + use crate::msgs::deframer::buffers::{DeframerVecBuffer, Locator}; + use crate::msgs::handshake::{ + ClientExtensionsInput, ServerExtensionsInput, TransportParameters, + }; + use crate::msgs::message::InboundPlainMessage; + use crate::server::{ServerConfig, ServerConnectionData}; + use crate::sync::Arc; + use crate::vecbuf::ChunkVecBuffer; + + /// A QUIC client or server connection. + #[derive(Debug)] + pub enum Connection { + /// A client connection + Client(ClientConnection), + /// A server connection + Server(ServerConnection), + } + + impl Connection { + /// Return the TLS-encoded transport parameters for the session's peer. + /// + /// See [`ConnectionCommon::quic_transport_parameters()`] for more details. + pub fn quic_transport_parameters(&self) -> Option<&[u8]> { + match self { + Self::Client(conn) => conn.quic_transport_parameters(), + Self::Server(conn) => conn.quic_transport_parameters(), + } + } + + /// Compute the keys for encrypting/decrypting 0-RTT packets, if available + pub fn zero_rtt_keys(&self) -> Option<DirectionalKeys> { + match self { + Self::Client(conn) => conn.zero_rtt_keys(), + Self::Server(conn) => conn.zero_rtt_keys(), + } + } + + /// Consume unencrypted TLS handshake data. + /// + /// Handshake data obtained from separate encryption levels should be supplied in separate calls. + pub fn read_hs(&mut self, plaintext: &[u8]) -> Result<(), Error> { + match self { + Self::Client(conn) => conn.read_hs(plaintext), + Self::Server(conn) => conn.read_hs(plaintext), + } + } + + /// Emit unencrypted TLS handshake data. + /// + /// When this returns `Some(_)`, the new keys must be used for future handshake data. + pub fn write_hs(&mut self, buf: &mut Vec<u8>) -> Option<KeyChange> { + match self { + Self::Client(conn) => conn.write_hs(buf), + Self::Server(conn) => conn.write_hs(buf), + } + } + + /// Emit the TLS description code of a fatal alert, if one has arisen. + /// + /// Check after `read_hs` returns `Err(_)`. + pub fn alert(&self) -> Option<AlertDescription> { + match self { + Self::Client(conn) => conn.alert(), + Self::Server(conn) => conn.alert(), + } + } + + /// Derives key material from the agreed connection secrets. + /// + /// This function fills in `output` with `output.len()` bytes of key + /// material derived from the master session secret using `label` + /// and `context` for diversification. Ownership of the buffer is taken + /// by the function and returned via the Ok result to ensure no key + /// material leaks if the function fails. + /// + /// See RFC5705 for more details on what this does and is for. + /// + /// For TLS1.3 connections, this function does not use the + /// "early" exporter at any point. + /// + /// This function fails if called prior to the handshake completing; + /// check with [`CommonState::is_handshaking`] first. + #[inline] + pub fn export_keying_material<T: AsMut<[u8]>>( + &self, + output: T, + label: &[u8], + context: Option<&[u8]>, + ) -> Result<T, Error> { + match self { + Self::Client(conn) => conn + .core + .export_keying_material(output, label, context), + Self::Server(conn) => conn + .core + .export_keying_material(output, label, context), + } + } + } + + impl Deref for Connection { + type Target = CommonState; + + fn deref(&self) -> &Self::Target { + match self { + Self::Client(conn) => &conn.core.common_state, + Self::Server(conn) => &conn.core.common_state, + } + } + } + + impl DerefMut for Connection { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + Self::Client(conn) => &mut conn.core.common_state, + Self::Server(conn) => &mut conn.core.common_state, + } + } + } + + /// A QUIC client connection. + pub struct ClientConnection { + inner: ConnectionCommon<ClientConnectionData>, + } + + impl ClientConnection { + /// Make a new QUIC ClientConnection. + /// + /// This differs from `ClientConnection::new()` in that it takes an extra `params` argument, + /// which contains the TLS-encoded transport parameters to send. + pub fn new( + config: Arc<ClientConfig>, + quic_version: Version, + name: ServerName<'static>, + params: Vec<u8>, + ) -> Result<Self, Error> { + Self::new_with_alpn( + config.clone(), + quic_version, + name, + params, + config.alpn_protocols.clone(), + ) + } + + /// Make a new QUIC ClientConnection with custom ALPN protocols. + pub fn new_with_alpn( + config: Arc<ClientConfig>, + quic_version: Version, + name: ServerName<'static>, + params: Vec<u8>, + alpn_protocols: Vec<Vec<u8>>, + ) -> Result<Self, Error> { + if !config.supports_version(ProtocolVersion::TLSv1_3) { + return Err(Error::General( + "TLS 1.3 support is required for QUIC".into(), + )); + } + + if !config.supports_protocol(Protocol::Quic) { + return Err(Error::General( + "at least one ciphersuite must support QUIC".into(), + )); + } + + let exts = ClientExtensionsInput { + transport_parameters: Some(match quic_version { + Version::V1Draft => TransportParameters::QuicDraft(Payload::new(params)), + Version::V1 | Version::V2 => TransportParameters::Quic(Payload::new(params)), + }), + + ..ClientExtensionsInput::from_alpn(alpn_protocols) + }; + + let mut inner = ConnectionCore::for_client(config, name, exts, Protocol::Quic)?; + inner.common_state.quic.version = quic_version; + Ok(Self { + inner: inner.into(), + }) + } + + /// Returns True if the server signalled it will process early data. + /// + /// If you sent early data and this returns false at the end of the + /// handshake then the server will not process the data. This + /// is not an error, but you may wish to resend the data. + pub fn is_early_data_accepted(&self) -> bool { + self.inner.core.is_early_data_accepted() + } + + /// Returns the number of TLS1.3 tickets that have been received. + pub fn tls13_tickets_received(&self) -> u32 { + self.inner.tls13_tickets_received + } + } + + impl Deref for ClientConnection { + type Target = ConnectionCommon<ClientConnectionData>; + + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl DerefMut for ClientConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } + } + + impl Debug for ClientConnection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("quic::ClientConnection") + .finish() + } + } + + impl From<ClientConnection> for Connection { + fn from(c: ClientConnection) -> Self { + Self::Client(c) + } + } + + /// A QUIC server connection. + pub struct ServerConnection { + inner: ConnectionCommon<ServerConnectionData>, + } + + impl ServerConnection { + /// Make a new QUIC ServerConnection. + /// + /// This differs from `ServerConnection::new()` in that it takes an extra `params` argument, + /// which contains the TLS-encoded transport parameters to send. + pub fn new( + config: Arc<ServerConfig>, + quic_version: Version, + params: Vec<u8>, + ) -> Result<Self, Error> { + if !config.supports_version(ProtocolVersion::TLSv1_3) { + return Err(Error::General( + "TLS 1.3 support is required for QUIC".into(), + )); + } + + if !config.supports_protocol(Protocol::Quic) { + return Err(Error::General( + "at least one ciphersuite must support QUIC".into(), + )); + } + + if config.max_early_data_size != 0 && config.max_early_data_size != 0xffff_ffff { + return Err(Error::General( + "QUIC sessions must set a max early data of 0 or 2^32-1".into(), + )); + } + + let exts = ServerExtensionsInput { + transport_parameters: Some(match quic_version { + Version::V1Draft => TransportParameters::QuicDraft(Payload::new(params)), + Version::V1 | Version::V2 => TransportParameters::Quic(Payload::new(params)), + }), + }; + + let mut core = ConnectionCore::for_server(config, exts)?; + core.common_state.protocol = Protocol::Quic; + core.common_state.quic.version = quic_version; + Ok(Self { inner: core.into() }) + } + + /// Explicitly discard early data, notifying the client + /// + /// Useful if invariants encoded in `received_resumption_data()` cannot be respected. + /// + /// Must be called while `is_handshaking` is true. + pub fn reject_early_data(&mut self) { + self.inner.core.reject_early_data() + } + + /// Retrieves the server name, if any, used to select the certificate and + /// private key. + /// + /// This returns `None` until some time after the client's server name indication + /// (SNI) extension value is processed during the handshake. It will never be + /// `None` when the connection is ready to send or process application data, + /// unless the client does not support SNI. + /// + /// This is useful for application protocols that need to enforce that the + /// server name matches an application layer protocol hostname. For + /// example, HTTP/1.1 servers commonly expect the `Host:` header field of + /// every request on a connection to match the hostname in the SNI extension + /// when the client provides the SNI extension. + /// + /// The server name is also used to match sessions during session resumption. + pub fn server_name(&self) -> Option<&str> { + self.inner.core.get_sni_str() + } + } + + impl Deref for ServerConnection { + type Target = ConnectionCommon<ServerConnectionData>; + + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl DerefMut for ServerConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } + } + + impl Debug for ServerConnection { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("quic::ServerConnection") + .finish() + } + } + + impl From<ServerConnection> for Connection { + fn from(c: ServerConnection) -> Self { + Self::Server(c) + } + } + + /// A shared interface for QUIC connections. + pub struct ConnectionCommon<Data> { + core: ConnectionCore<Data>, + deframer_buffer: DeframerVecBuffer, + sendable_plaintext: ChunkVecBuffer, + } + + impl<Data: SideData> ConnectionCommon<Data> { + /// Return the TLS-encoded transport parameters for the session's peer. + /// + /// While the transport parameters are technically available prior to the + /// completion of the handshake, they cannot be fully trusted until the + /// handshake completes, and reliance on them should be minimized. + /// However, any tampering with the parameters will cause the handshake + /// to fail. + pub fn quic_transport_parameters(&self) -> Option<&[u8]> { + self.core + .common_state + .quic + .params + .as_ref() + .map(|v| v.as_ref()) + } + + /// Compute the keys for encrypting/decrypting 0-RTT packets, if available + pub fn zero_rtt_keys(&self) -> Option<DirectionalKeys> { + let suite = self + .core + .common_state + .suite + .and_then(|suite| suite.tls13())?; + Some(DirectionalKeys::new( + suite, + suite.quic?, + self.core + .common_state + .quic + .early_secret + .as_ref()?, + self.core.common_state.quic.version, + )) + } + + /// Consume unencrypted TLS handshake data. + /// + /// Handshake data obtained from separate encryption levels should be supplied in separate calls. + pub fn read_hs(&mut self, plaintext: &[u8]) -> Result<(), Error> { + let range = self.deframer_buffer.extend(plaintext); + + self.core.hs_deframer.input_message( + InboundPlainMessage { + typ: ContentType::Handshake, + version: ProtocolVersion::TLSv1_3, + payload: &self.deframer_buffer.filled()[range.clone()], + }, + &Locator::new(self.deframer_buffer.filled()), + range.end, + ); + + self.core + .hs_deframer + .coalesce(self.deframer_buffer.filled_mut())?; + + self.core + .process_new_packets(&mut self.deframer_buffer, &mut self.sendable_plaintext)?; + + Ok(()) + } + + /// Emit unencrypted TLS handshake data. + /// + /// When this returns `Some(_)`, the new keys must be used for future handshake data. + pub fn write_hs(&mut self, buf: &mut Vec<u8>) -> Option<KeyChange> { + self.core + .common_state + .quic + .write_hs(buf) + } + + /// Emit the TLS description code of a fatal alert, if one has arisen. + /// + /// Check after `read_hs` returns `Err(_)`. + pub fn alert(&self) -> Option<AlertDescription> { + self.core.common_state.quic.alert + } + } + + impl<Data> Deref for ConnectionCommon<Data> { + type Target = CommonState; + + fn deref(&self) -> &Self::Target { + &self.core.common_state + } + } + + impl<Data> DerefMut for ConnectionCommon<Data> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.core.common_state + } + } + + impl<Data> From<ConnectionCore<Data>> for ConnectionCommon<Data> { + fn from(core: ConnectionCore<Data>) -> Self { + Self { + core, + deframer_buffer: DeframerVecBuffer::default(), + sendable_plaintext: ChunkVecBuffer::new(Some(DEFAULT_BUFFER_LIMIT)), + } + } + } +} + +#[cfg(feature = "std")] +pub use connection::{ClientConnection, Connection, ConnectionCommon, ServerConnection}; + +#[derive(Default)] +pub(crate) struct Quic { + /// QUIC transport parameters received from the peer during the handshake + pub(crate) params: Option<Vec<u8>>, + pub(crate) alert: Option<AlertDescription>, + pub(crate) hs_queue: VecDeque<(bool, Vec<u8>)>, + pub(crate) early_secret: Option<OkmBlock>, + pub(crate) hs_secrets: Option<Secrets>, + pub(crate) traffic_secrets: Option<Secrets>, + /// Whether keys derived from traffic_secrets have been passed to the QUIC implementation + #[cfg(feature = "std")] + pub(crate) returned_traffic_keys: bool, + pub(crate) version: Version, +} + +#[cfg(feature = "std")] +impl Quic { + pub(crate) fn write_hs(&mut self, buf: &mut Vec<u8>) -> Option<KeyChange> { + while let Some((_, msg)) = self.hs_queue.pop_front() { + buf.extend_from_slice(&msg); + if let Some(&(true, _)) = self.hs_queue.front() { + if self.hs_secrets.is_some() { + // Allow the caller to switch keys before proceeding. + break; + } + } + } + + if let Some(secrets) = self.hs_secrets.take() { + return Some(KeyChange::Handshake { + keys: Keys::new(&secrets), + }); + } + + if let Some(mut secrets) = self.traffic_secrets.take() { + if !self.returned_traffic_keys { + self.returned_traffic_keys = true; + let keys = Keys::new(&secrets); + secrets.update(); + return Some(KeyChange::OneRtt { + keys, + next: secrets, + }); + } + } + + None + } +} + +/// Secrets used to encrypt/decrypt traffic +#[derive(Clone)] +pub struct Secrets { + /// Secret used to encrypt packets transmitted by the client + pub(crate) client: OkmBlock, + /// Secret used to encrypt packets transmitted by the server + pub(crate) server: OkmBlock, + /// Cipher suite used with these secrets + suite: &'static Tls13CipherSuite, + quic: &'static dyn Algorithm, + side: Side, + version: Version, +} + +impl Secrets { + pub(crate) fn new( + client: OkmBlock, + server: OkmBlock, + suite: &'static Tls13CipherSuite, + quic: &'static dyn Algorithm, + side: Side, + version: Version, + ) -> Self { + Self { + client, + server, + suite, + quic, + side, + version, + } + } + + /// Derive the next set of packet keys + pub fn next_packet_keys(&mut self) -> PacketKeySet { + let keys = PacketKeySet::new(self); + self.update(); + keys + } + + pub(crate) fn update(&mut self) { + self.client = hkdf_expand_label_block( + self.suite + .hkdf_provider + .expander_for_okm(&self.client) + .as_ref(), + self.version.key_update_label(), + &[], + ); + self.server = hkdf_expand_label_block( + self.suite + .hkdf_provider + .expander_for_okm(&self.server) + .as_ref(), + self.version.key_update_label(), + &[], + ); + } + + fn local_remote(&self) -> (&OkmBlock, &OkmBlock) { + match self.side { + Side::Client => (&self.client, &self.server), + Side::Server => (&self.server, &self.client), + } + } +} + +/// Keys used to communicate in a single direction +pub struct DirectionalKeys { + /// Encrypts or decrypts a packet's headers + pub header: Box<dyn HeaderProtectionKey>, + /// Encrypts or decrypts the payload of a packet + pub packet: Box<dyn PacketKey>, +} + +impl DirectionalKeys { + pub(crate) fn new( + suite: &'static Tls13CipherSuite, + quic: &'static dyn Algorithm, + secret: &OkmBlock, + version: Version, + ) -> Self { + let builder = KeyBuilder::new(secret, version, quic, suite.hkdf_provider); + Self { + header: builder.header_protection_key(), + packet: builder.packet_key(), + } + } +} + +/// All AEADs we support have 16-byte tags. +const TAG_LEN: usize = 16; + +/// Authentication tag from an AEAD seal operation. +pub struct Tag([u8; TAG_LEN]); + +impl From<&[u8]> for Tag { + fn from(value: &[u8]) -> Self { + let mut array = [0u8; TAG_LEN]; + array.copy_from_slice(value); + Self(array) + } +} + +impl AsRef<[u8]> for Tag { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +/// How a `Tls13CipherSuite` generates `PacketKey`s and `HeaderProtectionKey`s. +pub trait Algorithm: Send + Sync { + /// Produce a `PacketKey` encrypter/decrypter for this suite. + /// + /// `suite` is the entire suite this `Algorithm` appeared in. + /// `key` and `iv` is the key material to use. + fn packet_key(&self, key: AeadKey, iv: Iv) -> Box<dyn PacketKey>; + + /// Produce a `HeaderProtectionKey` encrypter/decrypter for this suite. + /// + /// `key` is the key material, which is `aead_key_len()` bytes in length. + fn header_protection_key(&self, key: AeadKey) -> Box<dyn HeaderProtectionKey>; + + /// The length in bytes of keys for this Algorithm. + /// + /// This controls the size of `AeadKey`s presented to `packet_key()` and `header_protection_key()`. + fn aead_key_len(&self) -> usize; + + /// Whether this algorithm is FIPS-approved. + fn fips(&self) -> bool { + false + } +} + +/// A QUIC header protection key +pub trait HeaderProtectionKey: Send + Sync { + /// Adds QUIC Header Protection. + /// + /// `sample` must contain the sample of encrypted payload; see + /// [Header Protection Sample]. + /// + /// `first` must reference the first byte of the header, referred to as + /// `packet[0]` in [Header Protection Application]. + /// + /// `packet_number` must reference the Packet Number field; this is + /// `packet[pn_offset:pn_offset+pn_length]` in [Header Protection Application]. + /// + /// Returns an error without modifying anything if `sample` is not + /// the correct length (see [Header Protection Sample] and [`Self::sample_len()`]), + /// or `packet_number` is longer than allowed (see [Packet Number Encoding and Decoding]). + /// + /// Otherwise, `first` and `packet_number` will have the header protection added. + /// + /// [Header Protection Application]: https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.1 + /// [Header Protection Sample]: https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.2 + /// [Packet Number Encoding and Decoding]: https://datatracker.ietf.org/doc/html/rfc9000#section-17.1 + fn encrypt_in_place( + &self, + sample: &[u8], + first: &mut u8, + packet_number: &mut [u8], + ) -> Result<(), Error>; + + /// Removes QUIC Header Protection. + /// + /// `sample` must contain the sample of encrypted payload; see + /// [Header Protection Sample]. + /// + /// `first` must reference the first byte of the header, referred to as + /// `packet[0]` in [Header Protection Application]. + /// + /// `packet_number` must reference the Packet Number field; this is + /// `packet[pn_offset:pn_offset+pn_length]` in [Header Protection Application]. + /// + /// Returns an error without modifying anything if `sample` is not + /// the correct length (see [Header Protection Sample] and [`Self::sample_len()`]), + /// or `packet_number` is longer than allowed (see + /// [Packet Number Encoding and Decoding]). + /// + /// Otherwise, `first` and `packet_number` will have the header protection removed. + /// + /// [Header Protection Application]: https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.1 + /// [Header Protection Sample]: https://datatracker.ietf.org/doc/html/rfc9001#section-5.4.2 + /// [Packet Number Encoding and Decoding]: https://datatracker.ietf.org/doc/html/rfc9000#section-17.1 + fn decrypt_in_place( + &self, + sample: &[u8], + first: &mut u8, + packet_number: &mut [u8], + ) -> Result<(), Error>; + + /// Expected sample length for the key's algorithm + fn sample_len(&self) -> usize; +} + +/// Keys to encrypt or decrypt the payload of a packet +pub trait PacketKey: Send + Sync { + /// Encrypt a QUIC packet + /// + /// Takes a `packet_number`, used to derive the nonce; the packet `header`, which is used as + /// the additional authenticated data; and the `payload`. The authentication tag is returned if + /// encryption succeeds. + /// + /// Fails if and only if the payload is longer than allowed by the cipher suite's AEAD algorithm. + fn encrypt_in_place( + &self, + packet_number: u64, + header: &[u8], + payload: &mut [u8], + ) -> Result<Tag, Error>; + + /// Decrypt a QUIC packet + /// + /// Takes the packet `header`, which is used as the additional authenticated data, and the + /// `payload`, which includes the authentication tag. + /// + /// If the return value is `Ok`, the decrypted payload can be found in `payload`, up to the + /// length found in the return value. + fn decrypt_in_place<'a>( + &self, + packet_number: u64, + header: &[u8], + payload: &'a mut [u8], + ) -> Result<&'a [u8], Error>; + + /// Tag length for the underlying AEAD algorithm + fn tag_len(&self) -> usize; + + /// Number of QUIC messages that can be safely encrypted with a single key of this type. + /// + /// Once a `MessageEncrypter` produced for this suite has encrypted more than + /// `confidentiality_limit` messages, an attacker gains an advantage in distinguishing it + /// from an ideal pseudorandom permutation (PRP). + /// + /// This is to be set on the assumption that messages are maximally sized -- + /// 2 ** 16. For non-QUIC TCP connections see [`CipherSuiteCommon::confidentiality_limit`][csc-limit]. + /// + /// [csc-limit]: crate::crypto::CipherSuiteCommon::confidentiality_limit + fn confidentiality_limit(&self) -> u64; + + /// Number of QUIC messages that can be safely decrypted with a single key of this type + /// + /// Once a `MessageDecrypter` produced for this suite has failed to decrypt `integrity_limit` + /// messages, an attacker gains an advantage in forging messages. + /// + /// This is not relevant for TLS over TCP (which is also implemented in this crate) + /// because a single failed decryption is fatal to the connection. + /// However, this quantity is used by QUIC. + fn integrity_limit(&self) -> u64; +} + +/// Packet protection keys for bidirectional 1-RTT communication +pub struct PacketKeySet { + /// Encrypts outgoing packets + pub local: Box<dyn PacketKey>, + /// Decrypts incoming packets + pub remote: Box<dyn PacketKey>, +} + +impl PacketKeySet { + fn new(secrets: &Secrets) -> Self { + let (local, remote) = secrets.local_remote(); + let (version, alg, hkdf) = (secrets.version, secrets.quic, secrets.suite.hkdf_provider); + Self { + local: KeyBuilder::new(local, version, alg, hkdf).packet_key(), + remote: KeyBuilder::new(remote, version, alg, hkdf).packet_key(), + } + } +} + +pub(crate) struct KeyBuilder<'a> { + expander: Box<dyn HkdfExpander>, + version: Version, + alg: &'a dyn Algorithm, +} + +impl<'a> KeyBuilder<'a> { + pub(crate) fn new( + secret: &OkmBlock, + version: Version, + alg: &'a dyn Algorithm, + hkdf: &'a dyn Hkdf, + ) -> Self { + Self { + expander: hkdf.expander_for_okm(secret), + version, + alg, + } + } + + /// Derive packet keys + pub(crate) fn packet_key(&self) -> Box<dyn PacketKey> { + let aead_key_len = self.alg.aead_key_len(); + let packet_key = hkdf_expand_label_aead_key( + self.expander.as_ref(), + aead_key_len, + self.version.packet_key_label(), + &[], + ); + + let packet_iv = + hkdf_expand_label(self.expander.as_ref(), self.version.packet_iv_label(), &[]); + self.alg + .packet_key(packet_key, packet_iv) + } + + /// Derive header protection keys + pub(crate) fn header_protection_key(&self) -> Box<dyn HeaderProtectionKey> { + let header_key = hkdf_expand_label_aead_key( + self.expander.as_ref(), + self.alg.aead_key_len(), + self.version.header_key_label(), + &[], + ); + self.alg + .header_protection_key(header_key) + } +} + +/// Produces QUIC initial keys from a TLS 1.3 ciphersuite and a QUIC key generation algorithm. +#[derive(Clone, Copy)] +pub struct Suite { + /// The TLS 1.3 ciphersuite used to derive keys. + pub suite: &'static Tls13CipherSuite, + /// The QUIC key generation algorithm used to derive keys. + pub quic: &'static dyn Algorithm, +} + +impl Suite { + /// Produce a set of initial keys given the connection ID, side and version + pub fn keys(&self, client_dst_connection_id: &[u8], side: Side, version: Version) -> Keys { + Keys::initial( + version, + self.suite, + self.quic, + client_dst_connection_id, + side, + ) + } +} + +/// Complete set of keys used to communicate with the peer +pub struct Keys { + /// Encrypts outgoing packets + pub local: DirectionalKeys, + /// Decrypts incoming packets + pub remote: DirectionalKeys, +} + +impl Keys { + /// Construct keys for use with initial packets + pub fn initial( + version: Version, + suite: &'static Tls13CipherSuite, + quic: &'static dyn Algorithm, + client_dst_connection_id: &[u8], + side: Side, + ) -> Self { + const CLIENT_LABEL: &[u8] = b"client in"; + const SERVER_LABEL: &[u8] = b"server in"; + let salt = version.initial_salt(); + let hs_secret = suite + .hkdf_provider + .extract_from_secret(Some(salt), client_dst_connection_id); + + let secrets = Secrets { + version, + client: hkdf_expand_label_block(hs_secret.as_ref(), CLIENT_LABEL, &[]), + server: hkdf_expand_label_block(hs_secret.as_ref(), SERVER_LABEL, &[]), + suite, + quic, + side, + }; + Self::new(&secrets) + } + + fn new(secrets: &Secrets) -> Self { + let (local, remote) = secrets.local_remote(); + Self { + local: DirectionalKeys::new(secrets.suite, secrets.quic, local, secrets.version), + remote: DirectionalKeys::new(secrets.suite, secrets.quic, remote, secrets.version), + } + } +} + +/// Key material for use in QUIC packet spaces +/// +/// QUIC uses 4 different sets of keys (and progressive key updates for long-running connections): +/// +/// * Initial: these can be created from [`Keys::initial()`] +/// * 0-RTT keys: can be retrieved from [`ConnectionCommon::zero_rtt_keys()`] +/// * Handshake: these are returned from [`ConnectionCommon::write_hs()`] after `ClientHello` and +/// `ServerHello` messages have been exchanged +/// * 1-RTT keys: these are returned from [`ConnectionCommon::write_hs()`] after the handshake is done +/// +/// Once the 1-RTT keys have been exchanged, either side may initiate a key update. Progressive +/// update keys can be obtained from the [`Secrets`] returned in [`KeyChange::OneRtt`]. Note that +/// only packet keys are updated by key updates; header protection keys remain the same. +pub enum KeyChange { + /// Keys for the handshake space + Handshake { + /// Header and packet keys for the handshake space + keys: Keys, + }, + /// Keys for 1-RTT data + OneRtt { + /// Header and packet keys for 1-RTT data + keys: Keys, + /// Secrets to derive updated keys from + next: Secrets, + }, +} + +/// QUIC protocol version +/// +/// Governs version-specific behavior in the TLS layer +#[non_exhaustive] +#[derive(Clone, Copy, Debug)] +pub enum Version { + /// Draft versions 29, 30, 31 and 32 + V1Draft, + /// First stable RFC + V1, + /// Anti-ossification variant of V1 + V2, +} + +impl Version { + fn initial_salt(self) -> &'static [u8; 20] { + match self { + Self::V1Draft => &[ + // https://datatracker.ietf.org/doc/html/draft-ietf-quic-tls-32#section-5.2 + 0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, + 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99, + ], + Self::V1 => &[ + // https://www.rfc-editor.org/rfc/rfc9001.html#name-initial-secrets + 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, + 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a, + ], + Self::V2 => &[ + // https://www.ietf.org/archive/id/draft-ietf-quic-v2-10.html#name-initial-salt-2 + 0x0d, 0xed, 0xe3, 0xde, 0xf7, 0x00, 0xa6, 0xdb, 0x81, 0x93, 0x81, 0xbe, 0x6e, 0x26, + 0x9d, 0xcb, 0xf9, 0xbd, 0x2e, 0xd9, + ], + } + } + + /// Key derivation label for packet keys. + pub(crate) fn packet_key_label(&self) -> &'static [u8] { + match self { + Self::V1Draft | Self::V1 => b"quic key", + Self::V2 => b"quicv2 key", + } + } + + /// Key derivation label for packet "IV"s. + pub(crate) fn packet_iv_label(&self) -> &'static [u8] { + match self { + Self::V1Draft | Self::V1 => b"quic iv", + Self::V2 => b"quicv2 iv", + } + } + + /// Key derivation for header keys. + pub(crate) fn header_key_label(&self) -> &'static [u8] { + match self { + Self::V1Draft | Self::V1 => b"quic hp", + Self::V2 => b"quicv2 hp", + } + } + + fn key_update_label(&self) -> &'static [u8] { + match self { + Self::V1Draft | Self::V1 => b"quic ku", + Self::V2 => b"quicv2 ku", + } + } +} + +impl Default for Version { + fn default() -> Self { + Self::V1 + } +} + +#[cfg(test)] +mod tests { + use std::prelude::v1::*; + + use super::PacketKey; + use crate::quic::HeaderProtectionKey; + + #[test] + fn auto_traits() { + fn assert_auto<T: Send + Sync>() {} + assert_auto::<Box<dyn PacketKey>>(); + assert_auto::<Box<dyn HeaderProtectionKey>>(); + } +} diff --git a/vendor/rustls/src/rand.rs b/vendor/rustls/src/rand.rs new file mode 100644 index 00000000..23593863 --- /dev/null +++ b/vendor/rustls/src/rand.rs @@ -0,0 +1,34 @@ +//! The single place where we generate random material for our own use. + +use alloc::vec; +use alloc::vec::Vec; + +use crate::crypto::SecureRandom; + +/// Make a [`Vec<u8>`] of the given size containing random material. +pub(crate) fn random_vec( + secure_random: &dyn SecureRandom, + len: usize, +) -> Result<Vec<u8>, GetRandomFailed> { + let mut v = vec![0; len]; + secure_random.fill(&mut v)?; + Ok(v) +} + +/// Return a uniformly random [`u32`]. +pub(crate) fn random_u32(secure_random: &dyn SecureRandom) -> Result<u32, GetRandomFailed> { + let mut buf = [0u8; 4]; + secure_random.fill(&mut buf)?; + Ok(u32::from_be_bytes(buf)) +} + +/// Return a uniformly random [`u16`]. +pub(crate) fn random_u16(secure_random: &dyn SecureRandom) -> Result<u16, GetRandomFailed> { + let mut buf = [0u8; 2]; + secure_random.fill(&mut buf)?; + Ok(u16::from_be_bytes(buf)) +} + +/// Random material generation failed. +#[derive(Debug)] +pub struct GetRandomFailed; diff --git a/vendor/rustls/src/record_layer.rs b/vendor/rustls/src/record_layer.rs new file mode 100644 index 00000000..8c2ea6b9 --- /dev/null +++ b/vendor/rustls/src/record_layer.rs @@ -0,0 +1,336 @@ +use alloc::boxed::Box; +use core::cmp::min; + +use crate::crypto::cipher::{InboundOpaqueMessage, MessageDecrypter, MessageEncrypter}; +use crate::error::Error; +use crate::log::trace; +use crate::msgs::message::{InboundPlainMessage, OutboundOpaqueMessage, OutboundPlainMessage}; + +#[derive(PartialEq)] +enum DirectionState { + /// No keying material. + Invalid, + + /// Keying material present, but not yet in use. + Prepared, + + /// Keying material in use. + Active, +} + +/// Record layer that tracks decryption and encryption keys. +pub(crate) struct RecordLayer { + message_encrypter: Box<dyn MessageEncrypter>, + message_decrypter: Box<dyn MessageDecrypter>, + write_seq_max: u64, + write_seq: u64, + read_seq: u64, + has_decrypted: bool, + encrypt_state: DirectionState, + decrypt_state: DirectionState, + + // Message encrypted with other keys may be encountered, so failures + // should be swallowed by the caller. This struct tracks the amount + // of message size this is allowed for. + trial_decryption_len: Option<usize>, +} + +impl RecordLayer { + /// Create new record layer with no keys. + pub(crate) fn new() -> Self { + Self { + message_encrypter: <dyn MessageEncrypter>::invalid(), + message_decrypter: <dyn MessageDecrypter>::invalid(), + write_seq_max: 0, + write_seq: 0, + read_seq: 0, + has_decrypted: false, + encrypt_state: DirectionState::Invalid, + decrypt_state: DirectionState::Invalid, + trial_decryption_len: None, + } + } + + /// Decrypt a TLS message. + /// + /// `encr` is a decoded message allegedly received from the peer. + /// If it can be decrypted, its decryption is returned. Otherwise, + /// an error is returned. + pub(crate) fn decrypt_incoming<'a>( + &mut self, + encr: InboundOpaqueMessage<'a>, + ) -> Result<Option<Decrypted<'a>>, Error> { + if self.decrypt_state != DirectionState::Active { + return Ok(Some(Decrypted { + want_close_before_decrypt: false, + plaintext: encr.into_plain_message(), + })); + } + + // Set to `true` if the peer appears to getting close to encrypting + // too many messages with this key. + // + // Perhaps if we send an alert well before their counter wraps, a + // buggy peer won't make a terrible mistake here? + // + // Note that there's no reason to refuse to decrypt: the security + // failure has already happened. + let want_close_before_decrypt = self.read_seq == SEQ_SOFT_LIMIT; + + let encrypted_len = encr.payload.len(); + match self + .message_decrypter + .decrypt(encr, self.read_seq) + { + Ok(plaintext) => { + self.read_seq += 1; + if !self.has_decrypted { + self.has_decrypted = true; + } + Ok(Some(Decrypted { + want_close_before_decrypt, + plaintext, + })) + } + Err(Error::DecryptError) if self.doing_trial_decryption(encrypted_len) => { + trace!("Dropping undecryptable message after aborted early_data"); + Ok(None) + } + Err(err) => Err(err), + } + } + + /// Encrypt a TLS message. + /// + /// `plain` is a TLS message we'd like to send. This function + /// panics if the requisite keying material hasn't been established yet. + pub(crate) fn encrypt_outgoing( + &mut self, + plain: OutboundPlainMessage<'_>, + ) -> OutboundOpaqueMessage { + debug_assert!(self.encrypt_state == DirectionState::Active); + assert!(self.next_pre_encrypt_action() != PreEncryptAction::Refuse); + let seq = self.write_seq; + self.write_seq += 1; + self.message_encrypter + .encrypt(plain, seq) + .unwrap() + } + + /// Prepare to use the given `MessageEncrypter` for future message encryption. + /// It is not used until you call `start_encrypting`. + pub(crate) fn prepare_message_encrypter( + &mut self, + cipher: Box<dyn MessageEncrypter>, + max_messages: u64, + ) { + self.message_encrypter = cipher; + self.write_seq = 0; + self.write_seq_max = min(SEQ_SOFT_LIMIT, max_messages); + self.encrypt_state = DirectionState::Prepared; + } + + /// Prepare to use the given `MessageDecrypter` for future message decryption. + /// It is not used until you call `start_decrypting`. + pub(crate) fn prepare_message_decrypter(&mut self, cipher: Box<dyn MessageDecrypter>) { + self.message_decrypter = cipher; + self.read_seq = 0; + self.decrypt_state = DirectionState::Prepared; + } + + /// Start using the `MessageEncrypter` previously provided to the previous + /// call to `prepare_message_encrypter`. + pub(crate) fn start_encrypting(&mut self) { + debug_assert!(self.encrypt_state == DirectionState::Prepared); + self.encrypt_state = DirectionState::Active; + } + + /// Start using the `MessageDecrypter` previously provided to the previous + /// call to `prepare_message_decrypter`. + pub(crate) fn start_decrypting(&mut self) { + debug_assert!(self.decrypt_state == DirectionState::Prepared); + self.decrypt_state = DirectionState::Active; + } + + /// Set and start using the given `MessageEncrypter` for future outgoing + /// message encryption. + pub(crate) fn set_message_encrypter( + &mut self, + cipher: Box<dyn MessageEncrypter>, + max_messages: u64, + ) { + self.prepare_message_encrypter(cipher, max_messages); + self.start_encrypting(); + } + + /// Set and start using the given `MessageDecrypter` for future incoming + /// message decryption. + pub(crate) fn set_message_decrypter(&mut self, cipher: Box<dyn MessageDecrypter>) { + self.prepare_message_decrypter(cipher); + self.start_decrypting(); + self.trial_decryption_len = None; + } + + /// Set and start using the given `MessageDecrypter` for future incoming + /// message decryption, and enable "trial decryption" mode for when TLS1.3 + /// 0-RTT is attempted but rejected by the server. + pub(crate) fn set_message_decrypter_with_trial_decryption( + &mut self, + cipher: Box<dyn MessageDecrypter>, + max_length: usize, + ) { + self.prepare_message_decrypter(cipher); + self.start_decrypting(); + self.trial_decryption_len = Some(max_length); + } + + pub(crate) fn finish_trial_decryption(&mut self) { + self.trial_decryption_len = None; + } + + pub(crate) fn next_pre_encrypt_action(&self) -> PreEncryptAction { + self.pre_encrypt_action(0) + } + + /// Return a remedial action when we are near to encrypting too many messages. + /// + /// `add` is added to the current sequence number. `add` as `0` means + /// "the next message processed by `encrypt_outgoing`" + pub(crate) fn pre_encrypt_action(&self, add: u64) -> PreEncryptAction { + match self.write_seq.saturating_add(add) { + v if v == self.write_seq_max => PreEncryptAction::RefreshOrClose, + SEQ_HARD_LIMIT.. => PreEncryptAction::Refuse, + _ => PreEncryptAction::Nothing, + } + } + + pub(crate) fn is_encrypting(&self) -> bool { + self.encrypt_state == DirectionState::Active + } + + /// Return true if we have ever decrypted a message. This is used in place + /// of checking the read_seq since that will be reset on key updates. + pub(crate) fn has_decrypted(&self) -> bool { + self.has_decrypted + } + + pub(crate) fn write_seq(&self) -> u64 { + self.write_seq + } + + pub(crate) fn read_seq(&self) -> u64 { + self.read_seq + } + + pub(crate) fn encrypted_len(&self, payload_len: usize) -> usize { + self.message_encrypter + .encrypted_payload_len(payload_len) + } + + fn doing_trial_decryption(&mut self, requested: usize) -> bool { + match self + .trial_decryption_len + .and_then(|value| value.checked_sub(requested)) + { + Some(remaining) => { + self.trial_decryption_len = Some(remaining); + true + } + _ => false, + } + } +} + +/// Result of decryption. +#[derive(Debug)] +pub(crate) struct Decrypted<'a> { + /// Whether the peer appears to be getting close to encrypting too many messages with this key. + pub(crate) want_close_before_decrypt: bool, + /// The decrypted message. + pub(crate) plaintext: InboundPlainMessage<'a>, +} + +#[derive(Debug, Eq, PartialEq)] +pub(crate) enum PreEncryptAction { + /// No action is needed before calling `encrypt_outgoing` + Nothing, + + /// A `key_update` request should be sent ASAP. + /// + /// If that is not possible (for example, the connection is TLS1.2), a `close_notify` + /// alert should be sent instead. + RefreshOrClose, + + /// Do not call `encrypt_outgoing` further, it will panic rather than + /// over-use the key. + Refuse, +} + +const SEQ_SOFT_LIMIT: u64 = 0xffff_ffff_ffff_0000u64; +const SEQ_HARD_LIMIT: u64 = 0xffff_ffff_ffff_fffeu64; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_has_decrypted() { + use crate::{ContentType, ProtocolVersion}; + + struct PassThroughDecrypter; + impl MessageDecrypter for PassThroughDecrypter { + fn decrypt<'a>( + &mut self, + m: InboundOpaqueMessage<'a>, + _: u64, + ) -> Result<InboundPlainMessage<'a>, Error> { + Ok(m.into_plain_message()) + } + } + + // A record layer starts out invalid, having never decrypted. + let mut record_layer = RecordLayer::new(); + assert!(matches!( + record_layer.decrypt_state, + DirectionState::Invalid + )); + assert_eq!(record_layer.read_seq, 0); + assert!(!record_layer.has_decrypted()); + + // Preparing the record layer should update the decrypt state, but shouldn't affect whether it + // has decrypted. + record_layer.prepare_message_decrypter(Box::new(PassThroughDecrypter)); + assert!(matches!( + record_layer.decrypt_state, + DirectionState::Prepared + )); + assert_eq!(record_layer.read_seq, 0); + assert!(!record_layer.has_decrypted()); + + // Starting decryption should update the decrypt state, but not affect whether it has decrypted. + record_layer.start_decrypting(); + assert!(matches!(record_layer.decrypt_state, DirectionState::Active)); + assert_eq!(record_layer.read_seq, 0); + assert!(!record_layer.has_decrypted()); + + // Decrypting a message should update the read_seq and track that we have now performed + // a decryption. + record_layer + .decrypt_incoming(InboundOpaqueMessage::new( + ContentType::Handshake, + ProtocolVersion::TLSv1_2, + &mut [0xC0, 0xFF, 0xEE], + )) + .unwrap(); + assert!(matches!(record_layer.decrypt_state, DirectionState::Active)); + assert_eq!(record_layer.read_seq, 1); + assert!(record_layer.has_decrypted()); + + // Resetting the record layer message decrypter (as if a key update occurred) should reset + // the read_seq number, but not our knowledge of whether we have decrypted previously. + record_layer.set_message_decrypter(Box::new(PassThroughDecrypter)); + assert!(matches!(record_layer.decrypt_state, DirectionState::Active)); + assert_eq!(record_layer.read_seq, 0); + assert!(record_layer.has_decrypted()); + } +} diff --git a/vendor/rustls/src/server/builder.rs b/vendor/rustls/src/server/builder.rs new file mode 100644 index 00000000..d1b7b24a --- /dev/null +++ b/vendor/rustls/src/server/builder.rs @@ -0,0 +1,127 @@ +use alloc::vec::Vec; +use core::marker::PhantomData; + +use pki_types::{CertificateDer, PrivateKeyDer}; + +use super::{ResolvesServerCert, ServerConfig, handy}; +use crate::builder::{ConfigBuilder, WantsVerifier}; +use crate::error::Error; +use crate::sign::{CertifiedKey, SingleCertAndKey}; +use crate::sync::Arc; +use crate::verify::{ClientCertVerifier, NoClientAuth}; +use crate::{NoKeyLog, compress, versions}; + +impl ConfigBuilder<ServerConfig, WantsVerifier> { + /// Choose how to verify client certificates. + pub fn with_client_cert_verifier( + self, + client_cert_verifier: Arc<dyn ClientCertVerifier>, + ) -> ConfigBuilder<ServerConfig, WantsServerCert> { + ConfigBuilder { + state: WantsServerCert { + versions: self.state.versions, + verifier: client_cert_verifier, + }, + provider: self.provider, + time_provider: self.time_provider, + side: PhantomData, + } + } + + /// Disable client authentication. + pub fn with_no_client_auth(self) -> ConfigBuilder<ServerConfig, WantsServerCert> { + self.with_client_cert_verifier(Arc::new(NoClientAuth)) + } +} + +/// A config builder state where the caller must supply how to provide a server certificate to +/// the connecting peer. +/// +/// For more information, see the [`ConfigBuilder`] documentation. +#[derive(Clone, Debug)] +pub struct WantsServerCert { + versions: versions::EnabledVersions, + verifier: Arc<dyn ClientCertVerifier>, +} + +impl ConfigBuilder<ServerConfig, WantsServerCert> { + /// Sets a single certificate chain and matching private key. This + /// certificate and key is used for all subsequent connections, + /// irrespective of things like SNI hostname. + /// + /// Note that the end-entity certificate must have the + /// [Subject Alternative Name](https://tools.ietf.org/html/rfc6125#section-4.1) + /// extension to describe, e.g., the valid DNS name. The `commonName` field is + /// disregarded. + /// + /// `cert_chain` is a vector of DER-encoded certificates. + /// `key_der` is a DER-encoded private key as PKCS#1, PKCS#8, or SEC1. The + /// `aws-lc-rs` and `ring` [`CryptoProvider`][crate::CryptoProvider]s support + /// all three encodings, but other `CryptoProviders` may not. + /// + /// This function fails if `key_der` is invalid, or if the + /// `SubjectPublicKeyInfo` from the private key does not match the public + /// key for the end-entity certificate from the `cert_chain`. + pub fn with_single_cert( + self, + cert_chain: Vec<CertificateDer<'static>>, + key_der: PrivateKeyDer<'static>, + ) -> Result<ServerConfig, Error> { + let certified_key = CertifiedKey::from_der(cert_chain, key_der, self.crypto_provider())?; + Ok(self.with_cert_resolver(Arc::new(SingleCertAndKey::from(certified_key)))) + } + + /// Sets a single certificate chain, matching private key and optional OCSP + /// response. This certificate and key is used for all + /// subsequent connections, irrespective of things like SNI hostname. + /// + /// `cert_chain` is a vector of DER-encoded certificates. + /// `key_der` is a DER-encoded private key as PKCS#1, PKCS#8, or SEC1. The + /// `aws-lc-rs` and `ring` [`CryptoProvider`][crate::CryptoProvider]s support + /// all three encodings, but other `CryptoProviders` may not. + /// `ocsp` is a DER-encoded OCSP response. Ignored if zero length. + /// + /// This function fails if `key_der` is invalid, or if the + /// `SubjectPublicKeyInfo` from the private key does not match the public + /// key for the end-entity certificate from the `cert_chain`. + pub fn with_single_cert_with_ocsp( + self, + cert_chain: Vec<CertificateDer<'static>>, + key_der: PrivateKeyDer<'static>, + ocsp: Vec<u8>, + ) -> Result<ServerConfig, Error> { + let mut certified_key = + CertifiedKey::from_der(cert_chain, key_der, self.crypto_provider())?; + certified_key.ocsp = Some(ocsp); + Ok(self.with_cert_resolver(Arc::new(SingleCertAndKey::from(certified_key)))) + } + + /// Sets a custom [`ResolvesServerCert`]. + pub fn with_cert_resolver(self, cert_resolver: Arc<dyn ResolvesServerCert>) -> ServerConfig { + ServerConfig { + provider: self.provider, + verifier: self.state.verifier, + cert_resolver, + ignore_client_order: false, + max_fragment_size: None, + #[cfg(feature = "std")] + session_storage: handy::ServerSessionMemoryCache::new(256), + #[cfg(not(feature = "std"))] + session_storage: Arc::new(handy::NoServerSessionStorage {}), + ticketer: Arc::new(handy::NeverProducesTickets {}), + alpn_protocols: Vec::new(), + versions: self.state.versions, + key_log: Arc::new(NoKeyLog {}), + enable_secret_extraction: false, + max_early_data_size: 0, + send_half_rtt_data: false, + send_tls13_tickets: 2, + #[cfg(feature = "tls12")] + require_ems: cfg!(feature = "fips"), + time_provider: self.time_provider, + cert_compressors: compress::default_cert_compressors().to_vec(), + cert_compression_cache: Arc::new(compress::CompressionCache::default()), + cert_decompressors: compress::default_cert_decompressors().to_vec(), + } + } +} diff --git a/vendor/rustls/src/server/common.rs b/vendor/rustls/src/server/common.rs new file mode 100644 index 00000000..8310998a --- /dev/null +++ b/vendor/rustls/src/server/common.rs @@ -0,0 +1,35 @@ +use pki_types::CertificateDer; + +use crate::sign; + +/// ActiveCertifiedKey wraps [`sign::CertifiedKey`] and tracks OSCP state in a single handshake. +pub(super) struct ActiveCertifiedKey<'a> { + key: &'a sign::CertifiedKey, + ocsp: Option<&'a [u8]>, +} + +impl ActiveCertifiedKey<'_> { + pub(super) fn from_certified_key(key: &sign::CertifiedKey) -> ActiveCertifiedKey<'_> { + ActiveCertifiedKey { + key, + ocsp: key.ocsp.as_deref(), + } + } + + /// Get the certificate chain + #[inline] + pub(super) fn get_cert(&self) -> &[CertificateDer<'static>] { + &self.key.cert + } + + /// Get the signing key + #[inline] + pub(super) fn get_key(&self) -> &dyn sign::SigningKey { + &*self.key.key + } + + #[inline] + pub(super) fn get_ocsp(&self) -> Option<&[u8]> { + self.ocsp + } +} diff --git a/vendor/rustls/src/server/handy.rs b/vendor/rustls/src/server/handy.rs new file mode 100644 index 00000000..ea3ec5d9 --- /dev/null +++ b/vendor/rustls/src/server/handy.rs @@ -0,0 +1,356 @@ +use alloc::vec::Vec; +use core::fmt::Debug; + +use crate::server::ClientHello; +use crate::sync::Arc; +use crate::{server, sign}; + +/// Something which never stores sessions. +#[derive(Debug)] +pub struct NoServerSessionStorage {} + +impl server::StoresServerSessions for NoServerSessionStorage { + fn put(&self, _id: Vec<u8>, _sec: Vec<u8>) -> bool { + false + } + fn get(&self, _id: &[u8]) -> Option<Vec<u8>> { + None + } + fn take(&self, _id: &[u8]) -> Option<Vec<u8>> { + None + } + fn can_cache(&self) -> bool { + false + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +mod cache { + use alloc::vec::Vec; + use core::fmt::{Debug, Formatter}; + + use crate::lock::Mutex; + use crate::sync::Arc; + use crate::{limited_cache, server}; + + /// An implementer of `StoresServerSessions` that stores everything + /// in memory. If enforces a limit on the number of stored sessions + /// to bound memory usage. + pub struct ServerSessionMemoryCache { + cache: Mutex<limited_cache::LimitedCache<Vec<u8>, Vec<u8>>>, + } + + impl ServerSessionMemoryCache { + /// Make a new ServerSessionMemoryCache. `size` is the maximum + /// number of stored sessions, and may be rounded-up for + /// efficiency. + #[cfg(feature = "std")] + pub fn new(size: usize) -> Arc<Self> { + Arc::new(Self { + cache: Mutex::new(limited_cache::LimitedCache::new(size)), + }) + } + + /// Make a new ServerSessionMemoryCache. `size` is the maximum + /// number of stored sessions, and may be rounded-up for + /// efficiency. + #[cfg(not(feature = "std"))] + pub fn new<M: crate::lock::MakeMutex>(size: usize) -> Arc<Self> { + Arc::new(Self { + cache: Mutex::new::<M>(limited_cache::LimitedCache::new(size)), + }) + } + } + + impl server::StoresServerSessions for ServerSessionMemoryCache { + fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool { + self.cache + .lock() + .unwrap() + .insert(key, value); + true + } + + fn get(&self, key: &[u8]) -> Option<Vec<u8>> { + self.cache + .lock() + .unwrap() + .get(key) + .cloned() + } + + fn take(&self, key: &[u8]) -> Option<Vec<u8>> { + self.cache.lock().unwrap().remove(key) + } + + fn can_cache(&self) -> bool { + true + } + } + + impl Debug for ServerSessionMemoryCache { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.debug_struct("ServerSessionMemoryCache") + .finish() + } + } + + #[cfg(test)] + mod tests { + use std::vec; + + use super::*; + use crate::server::StoresServerSessions; + + #[test] + fn test_serversessionmemorycache_accepts_put() { + let c = ServerSessionMemoryCache::new(4); + assert!(c.put(vec![0x01], vec![0x02])); + } + + #[test] + fn test_serversessionmemorycache_persists_put() { + let c = ServerSessionMemoryCache::new(4); + assert!(c.put(vec![0x01], vec![0x02])); + assert_eq!(c.get(&[0x01]), Some(vec![0x02])); + assert_eq!(c.get(&[0x01]), Some(vec![0x02])); + } + + #[test] + fn test_serversessionmemorycache_overwrites_put() { + let c = ServerSessionMemoryCache::new(4); + assert!(c.put(vec![0x01], vec![0x02])); + assert!(c.put(vec![0x01], vec![0x04])); + assert_eq!(c.get(&[0x01]), Some(vec![0x04])); + } + + #[test] + fn test_serversessionmemorycache_drops_to_maintain_size_invariant() { + let c = ServerSessionMemoryCache::new(2); + assert!(c.put(vec![0x01], vec![0x02])); + assert!(c.put(vec![0x03], vec![0x04])); + assert!(c.put(vec![0x05], vec![0x06])); + assert!(c.put(vec![0x07], vec![0x08])); + assert!(c.put(vec![0x09], vec![0x0a])); + + let count = c.get(&[0x01]).iter().count() + + c.get(&[0x03]).iter().count() + + c.get(&[0x05]).iter().count() + + c.get(&[0x07]).iter().count() + + c.get(&[0x09]).iter().count(); + + assert!(count < 5); + } + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +pub use cache::ServerSessionMemoryCache; + +/// Something which never produces tickets. +#[derive(Debug)] +pub(super) struct NeverProducesTickets {} + +impl server::ProducesTickets for NeverProducesTickets { + fn enabled(&self) -> bool { + false + } + fn lifetime(&self) -> u32 { + 0 + } + fn encrypt(&self, _bytes: &[u8]) -> Option<Vec<u8>> { + None + } + fn decrypt(&self, _bytes: &[u8]) -> Option<Vec<u8>> { + None + } +} + +/// An exemplar `ResolvesServerCert` implementation that always resolves to a single +/// [RFC 7250] raw public key. +/// +/// [RFC 7250]: https://tools.ietf.org/html/rfc7250 +#[derive(Clone, Debug)] +pub struct AlwaysResolvesServerRawPublicKeys(Arc<sign::CertifiedKey>); + +impl AlwaysResolvesServerRawPublicKeys { + /// Create a new `AlwaysResolvesServerRawPublicKeys` instance. + pub fn new(certified_key: Arc<sign::CertifiedKey>) -> Self { + Self(certified_key) + } +} + +impl server::ResolvesServerCert for AlwaysResolvesServerRawPublicKeys { + fn resolve(&self, _client_hello: ClientHello<'_>) -> Option<Arc<sign::CertifiedKey>> { + Some(self.0.clone()) + } + + fn only_raw_public_keys(&self) -> bool { + true + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +mod sni_resolver { + use alloc::string::{String, ToString}; + use core::fmt::Debug; + + use pki_types::{DnsName, ServerName}; + + use crate::error::Error; + use crate::hash_map::HashMap; + use crate::server::ClientHello; + use crate::sync::Arc; + use crate::webpki::{ParsedCertificate, verify_server_name}; + use crate::{server, sign}; + + /// Something that resolves do different cert chains/keys based + /// on client-supplied server name (via SNI). + #[derive(Debug)] + pub struct ResolvesServerCertUsingSni { + by_name: HashMap<String, Arc<sign::CertifiedKey>>, + } + + impl ResolvesServerCertUsingSni { + /// Create a new and empty (i.e., knows no certificates) resolver. + pub fn new() -> Self { + Self { + by_name: HashMap::new(), + } + } + + /// Add a new `sign::CertifiedKey` to be used for the given SNI `name`. + /// + /// This function fails if `name` is not a valid DNS name, or if + /// it's not valid for the supplied certificate, or if the certificate + /// chain is syntactically faulty. + pub fn add(&mut self, name: &str, ck: sign::CertifiedKey) -> Result<(), Error> { + let server_name = { + let checked_name = DnsName::try_from(name) + .map_err(|_| Error::General("Bad DNS name".into())) + .map(|name| name.to_lowercase_owned())?; + ServerName::DnsName(checked_name) + }; + + // Check the certificate chain for validity: + // - it should be non-empty list + // - the first certificate should be parsable as a x509v3, + // - the first certificate should quote the given server name + // (if provided) + // + // These checks are not security-sensitive. They are the + // *server* attempting to detect accidental misconfiguration. + + ck.end_entity_cert() + .and_then(ParsedCertificate::try_from) + .and_then(|cert| verify_server_name(&cert, &server_name))?; + + if let ServerName::DnsName(name) = server_name { + self.by_name + .insert(name.as_ref().to_string(), Arc::new(ck)); + } + Ok(()) + } + } + + impl server::ResolvesServerCert for ResolvesServerCertUsingSni { + fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<sign::CertifiedKey>> { + if let Some(name) = client_hello.server_name() { + self.by_name.get(name).cloned() + } else { + // This kind of resolver requires SNI + None + } + } + } + + #[cfg(test)] + mod tests { + use super::*; + use crate::server::ResolvesServerCert; + + #[test] + fn test_resolvesservercertusingsni_requires_sni() { + let rscsni = ResolvesServerCertUsingSni::new(); + assert!( + rscsni + .resolve(ClientHello { + server_name: &None, + signature_schemes: &[], + alpn: None, + server_cert_types: None, + client_cert_types: None, + cipher_suites: &[], + certificate_authorities: None, + named_groups: None, + }) + .is_none() + ); + } + + #[test] + fn test_resolvesservercertusingsni_handles_unknown_name() { + let rscsni = ResolvesServerCertUsingSni::new(); + let name = DnsName::try_from("hello.com") + .unwrap() + .to_owned(); + assert!( + rscsni + .resolve(ClientHello { + server_name: &Some(name), + signature_schemes: &[], + alpn: None, + server_cert_types: None, + client_cert_types: None, + cipher_suites: &[], + certificate_authorities: None, + named_groups: None, + }) + .is_none() + ); + } + } +} + +#[cfg(any(feature = "std", feature = "hashbrown"))] +pub use sni_resolver::ResolvesServerCertUsingSni; + +#[cfg(test)] +mod tests { + use std::vec; + + use super::*; + use crate::server::{ProducesTickets, StoresServerSessions}; + + #[test] + fn test_noserversessionstorage_drops_put() { + let c = NoServerSessionStorage {}; + assert!(!c.put(vec![0x01], vec![0x02])); + } + + #[test] + fn test_noserversessionstorage_denies_gets() { + let c = NoServerSessionStorage {}; + c.put(vec![0x01], vec![0x02]); + assert_eq!(c.get(&[]), None); + assert_eq!(c.get(&[0x01]), None); + assert_eq!(c.get(&[0x02]), None); + } + + #[test] + fn test_noserversessionstorage_denies_takes() { + let c = NoServerSessionStorage {}; + assert_eq!(c.take(&[]), None); + assert_eq!(c.take(&[0x01]), None); + assert_eq!(c.take(&[0x02]), None); + } + + #[test] + fn test_neverproducestickets_does_nothing() { + let npt = NeverProducesTickets {}; + assert!(!npt.enabled()); + assert_eq!(0, npt.lifetime()); + assert_eq!(None, npt.encrypt(&[])); + assert_eq!(None, npt.decrypt(&[])); + } +} diff --git a/vendor/rustls/src/server/hs.rs b/vendor/rustls/src/server/hs.rs new file mode 100644 index 00000000..d98336e4 --- /dev/null +++ b/vendor/rustls/src/server/hs.rs @@ -0,0 +1,763 @@ +use alloc::borrow::ToOwned; +use alloc::boxed::Box; +use alloc::vec::Vec; + +use pki_types::DnsName; + +use super::server_conn::ServerConnectionData; +#[cfg(feature = "tls12")] +use super::tls12; +use crate::common_state::{KxState, Protocol, State}; +use crate::conn::ConnectionRandoms; +use crate::crypto::SupportedKxGroup; +use crate::enums::{ + AlertDescription, CertificateType, CipherSuite, HandshakeType, ProtocolVersion, + SignatureAlgorithm, SignatureScheme, +}; +use crate::error::{Error, PeerIncompatible, PeerMisbehaved}; +use crate::hash_hs::{HandshakeHash, HandshakeHashBuffer}; +use crate::log::{debug, trace}; +use crate::msgs::enums::{Compression, ExtensionType, NamedGroup}; +#[cfg(feature = "tls12")] +use crate::msgs::handshake::SessionId; +use crate::msgs::handshake::{ + ClientHelloPayload, HandshakePayload, KeyExchangeAlgorithm, ProtocolName, Random, + ServerExtensions, ServerExtensionsInput, ServerNamePayload, SingleProtocolName, + TransportParameters, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist; +use crate::server::common::ActiveCertifiedKey; +use crate::server::{ClientHello, ServerConfig, tls13}; +use crate::sync::Arc; +use crate::{SupportedCipherSuite, suites}; + +pub(super) type NextState<'a> = Box<dyn State<ServerConnectionData> + 'a>; +pub(super) type NextStateOrError<'a> = Result<NextState<'a>, Error>; +pub(super) type ServerContext<'a> = crate::common_state::Context<'a, ServerConnectionData>; + +pub(super) fn can_resume( + suite: SupportedCipherSuite, + sni: &Option<DnsName<'_>>, + using_ems: bool, + resumedata: &persist::ServerSessionValue, +) -> bool { + // The RFCs underspecify what happens if we try to resume to + // an unoffered/varying suite. We merely don't resume in weird cases. + // + // RFC 6066 says "A server that implements this extension MUST NOT accept + // the request to resume the session if the server_name extension contains + // a different name. Instead, it proceeds with a full handshake to + // establish a new session." + // + // RFC 8446: "The server MUST ensure that it selects + // a compatible PSK (if any) and cipher suite." + resumedata.cipher_suite == suite.suite() + && (resumedata.extended_ms == using_ems || (resumedata.extended_ms && !using_ems)) + && &resumedata.sni == sni +} + +#[derive(Default)] +pub(super) struct ExtensionProcessing { + // extensions to reply with + pub(super) extensions: Box<ServerExtensions<'static>>, + #[cfg(feature = "tls12")] + pub(super) send_ticket: bool, +} + +impl ExtensionProcessing { + pub(super) fn new(extra_exts: ServerExtensionsInput<'static>) -> Self { + let ServerExtensionsInput { + transport_parameters, + } = extra_exts; + + let mut extensions = Box::new(ServerExtensions::default()); + match transport_parameters { + Some(TransportParameters::Quic(v)) => extensions.transport_parameters = Some(v), + Some(TransportParameters::QuicDraft(v)) => { + extensions.transport_parameters_draft = Some(v) + } + None => {} + } + + Self { + extensions, + #[cfg(feature = "tls12")] + send_ticket: false, + } + } + + pub(super) fn process_common( + &mut self, + config: &ServerConfig, + cx: &mut ServerContext<'_>, + ocsp_response: &mut Option<&[u8]>, + hello: &ClientHelloPayload, + resumedata: Option<&persist::ServerSessionValue>, + ) -> Result<(), Error> { + // ALPN + let our_protocols = &config.alpn_protocols; + if let Some(their_protocols) = &hello.protocols { + cx.common.alpn_protocol = our_protocols + .iter() + .find(|ours| { + their_protocols + .iter() + .any(|theirs| theirs.as_ref() == ours.as_slice()) + }) + .map(|bytes| ProtocolName::from(bytes.clone())); + if let Some(selected_protocol) = &cx.common.alpn_protocol { + debug!("Chosen ALPN protocol {selected_protocol:?}"); + + self.extensions.selected_protocol = + Some(SingleProtocolName::new(selected_protocol.clone())); + } else if !our_protocols.is_empty() { + return Err(cx.common.send_fatal_alert( + AlertDescription::NoApplicationProtocol, + Error::NoApplicationProtocol, + )); + } + } + + if cx.common.is_quic() { + // QUIC has strict ALPN, unlike TLS's more backwards-compatible behavior. RFC 9001 + // says: "The server MUST treat the inability to select a compatible application + // protocol as a connection error of type 0x0178". We judge that ALPN was desired + // (rather than some out-of-band protocol negotiation mechanism) if and only if any ALPN + // protocols were configured locally or offered by the client. This helps prevent + // successful establishment of connections between peers that can't understand + // each other. + if cx.common.alpn_protocol.is_none() + && (!our_protocols.is_empty() || hello.protocols.is_some()) + { + return Err(cx.common.send_fatal_alert( + AlertDescription::NoApplicationProtocol, + Error::NoApplicationProtocol, + )); + } + + let transport_params = hello + .transport_parameters + .as_ref() + .or(hello + .transport_parameters_draft + .as_ref()); + match transport_params { + Some(params) => cx.common.quic.params = Some(params.to_owned().into_vec()), + None => { + return Err(cx + .common + .missing_extension(PeerMisbehaved::MissingQuicTransportParameters)); + } + } + } + + let for_resume = resumedata.is_some(); + // SNI + if let (false, Some(ServerNamePayload::SingleDnsName(_))) = (for_resume, &hello.server_name) + { + self.extensions.server_name_ack = Some(()); + } + + // Send status_request response if we have one. This is not allowed + // if we're resuming, and is only triggered if we have an OCSP response + // to send. + if !for_resume + && hello + .certificate_status_request + .is_some() + { + if ocsp_response.is_some() && !cx.common.is_tls13() { + // Only TLS1.2 sends confirmation in ServerHello + self.extensions + .certificate_status_request_ack = Some(()); + } + } else { + // Throw away any OCSP response so we don't try to send it later. + ocsp_response.take(); + } + + self.validate_server_cert_type_extension(hello, config, cx)?; + self.validate_client_cert_type_extension(hello, config, cx)?; + + Ok(()) + } + + #[cfg(feature = "tls12")] + pub(super) fn process_tls12( + &mut self, + config: &ServerConfig, + hello: &ClientHelloPayload, + using_ems: bool, + ) { + // Renegotiation. + // (We don't do reneg at all, but would support the secure version if we did.) + + use crate::msgs::base::PayloadU8; + let secure_reneg_offered = hello.renegotiation_info.is_some() + || hello + .cipher_suites + .contains(&CipherSuite::TLS_EMPTY_RENEGOTIATION_INFO_SCSV); + + if secure_reneg_offered { + self.extensions.renegotiation_info = Some(PayloadU8::new(Vec::new())); + } + + // Tickets: + // If we get any SessionTicket extension and have tickets enabled, + // we send an ack. + if hello.session_ticket.is_some() && config.ticketer.enabled() { + self.send_ticket = true; + self.extensions.session_ticket_ack = Some(()); + } + + // Confirm use of EMS if offered. + if using_ems { + self.extensions + .extended_master_secret_ack = Some(()); + } + } + + fn validate_server_cert_type_extension( + &mut self, + hello: &ClientHelloPayload, + config: &ServerConfig, + cx: &mut ServerContext<'_>, + ) -> Result<(), Error> { + let client_supports = hello + .server_certificate_types + .as_deref() + .unwrap_or_default(); + + self.process_cert_type_extension( + client_supports, + config + .cert_resolver + .only_raw_public_keys(), + ExtensionType::ServerCertificateType, + cx, + ) + } + + fn validate_client_cert_type_extension( + &mut self, + hello: &ClientHelloPayload, + config: &ServerConfig, + cx: &mut ServerContext<'_>, + ) -> Result<(), Error> { + let client_supports = hello + .client_certificate_types + .as_deref() + .unwrap_or_default(); + + self.process_cert_type_extension( + client_supports, + config + .verifier + .requires_raw_public_keys(), + ExtensionType::ClientCertificateType, + cx, + ) + } + + fn process_cert_type_extension( + &mut self, + client_supports: &[CertificateType], + requires_raw_keys: bool, + extension_type: ExtensionType, + cx: &mut ServerContext<'_>, + ) -> Result<(), Error> { + debug_assert!( + extension_type == ExtensionType::ClientCertificateType + || extension_type == ExtensionType::ServerCertificateType + ); + let raw_key_negotation_result = match ( + requires_raw_keys, + client_supports.contains(&CertificateType::RawPublicKey), + client_supports.contains(&CertificateType::X509), + ) { + (true, true, _) => Ok((extension_type, CertificateType::RawPublicKey)), + (false, _, true) => Ok((extension_type, CertificateType::X509)), + (false, true, false) => Err(Error::PeerIncompatible( + PeerIncompatible::IncorrectCertificateTypeExtension, + )), + (true, false, _) => Err(Error::PeerIncompatible( + PeerIncompatible::IncorrectCertificateTypeExtension, + )), + (false, false, false) => return Ok(()), + }; + + match raw_key_negotation_result { + Ok((ExtensionType::ClientCertificateType, cert_type)) => { + self.extensions.client_certificate_type = Some(cert_type); + } + Ok((ExtensionType::ServerCertificateType, cert_type)) => { + self.extensions.server_certificate_type = Some(cert_type); + } + Err(err) => { + return Err(cx + .common + .send_fatal_alert(AlertDescription::HandshakeFailure, err)); + } + Ok((_, _)) => unreachable!(), + } + Ok(()) + } +} + +pub(super) struct ExpectClientHello { + pub(super) config: Arc<ServerConfig>, + pub(super) extra_exts: ServerExtensionsInput<'static>, + pub(super) transcript: HandshakeHashOrBuffer, + #[cfg(feature = "tls12")] + pub(super) session_id: SessionId, + #[cfg(feature = "tls12")] + pub(super) using_ems: bool, + pub(super) done_retry: bool, + pub(super) send_tickets: usize, +} + +impl ExpectClientHello { + pub(super) fn new( + config: Arc<ServerConfig>, + extra_exts: ServerExtensionsInput<'static>, + ) -> Self { + let mut transcript_buffer = HandshakeHashBuffer::new(); + + if config.verifier.offer_client_auth() { + transcript_buffer.set_client_auth_enabled(); + } + + Self { + config, + extra_exts, + transcript: HandshakeHashOrBuffer::Buffer(transcript_buffer), + #[cfg(feature = "tls12")] + session_id: SessionId::empty(), + #[cfg(feature = "tls12")] + using_ems: false, + done_retry: false, + send_tickets: 0, + } + } + + /// Continues handling of a `ClientHello` message once config and certificate are available. + pub(super) fn with_certified_key( + self, + mut sig_schemes: Vec<SignatureScheme>, + client_hello: &ClientHelloPayload, + m: &Message<'_>, + cx: &mut ServerContext<'_>, + ) -> NextStateOrError<'static> { + let tls13_enabled = self + .config + .supports_version(ProtocolVersion::TLSv1_3); + let tls12_enabled = self + .config + .supports_version(ProtocolVersion::TLSv1_2); + + // Are we doing TLS1.3? + let version = if let Some(versions) = &client_hello.supported_versions { + if versions.tls13 && tls13_enabled { + ProtocolVersion::TLSv1_3 + } else if !versions.tls12 || !tls12_enabled { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::Tls12NotOfferedOrEnabled, + )); + } else if cx.common.is_quic() { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::Tls13RequiredForQuic, + )); + } else { + ProtocolVersion::TLSv1_2 + } + } else if u16::from(client_hello.client_version) < u16::from(ProtocolVersion::TLSv1_2) { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::Tls12NotOffered, + )); + } else if !tls12_enabled && tls13_enabled { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::SupportedVersionsExtensionRequired, + )); + } else if cx.common.is_quic() { + return Err(cx.common.send_fatal_alert( + AlertDescription::ProtocolVersion, + PeerIncompatible::Tls13RequiredForQuic, + )); + } else { + ProtocolVersion::TLSv1_2 + }; + + cx.common.negotiated_version = Some(version); + + // We communicate to the upper layer what kind of key they should choose + // via the sigschemes value. Clients tend to treat this extension + // orthogonally to offered ciphersuites (even though, in TLS1.2 it is not). + // So: reduce the offered sigschemes to those compatible with the + // intersection of ciphersuites. + let client_suites = self + .config + .provider + .cipher_suites + .iter() + .copied() + .filter(|scs| { + client_hello + .cipher_suites + .contains(&scs.suite()) + }) + .collect::<Vec<_>>(); + + sig_schemes + .retain(|scheme| suites::compatible_sigscheme_for_suites(*scheme, &client_suites)); + + // We adhere to the TLS 1.2 RFC by not exposing this to the cert resolver if TLS version is 1.2 + let certificate_authorities = match version { + ProtocolVersion::TLSv1_2 => None, + _ => client_hello + .certificate_authority_names + .as_deref(), + }; + // Choose a certificate. + let certkey = { + let client_hello = ClientHello { + server_name: &cx.data.sni, + signature_schemes: &sig_schemes, + alpn: client_hello.protocols.as_ref(), + client_cert_types: client_hello + .client_certificate_types + .as_deref(), + server_cert_types: client_hello + .server_certificate_types + .as_deref(), + cipher_suites: &client_hello.cipher_suites, + certificate_authorities, + named_groups: client_hello.named_groups.as_deref(), + }; + trace!("Resolving server certificate: {client_hello:#?}"); + + let certkey = self + .config + .cert_resolver + .resolve(client_hello); + + certkey.ok_or_else(|| { + cx.common.send_fatal_alert( + AlertDescription::AccessDenied, + Error::General("no server certificate chain resolved".to_owned()), + ) + })? + }; + let certkey = ActiveCertifiedKey::from_certified_key(&certkey); + + let (suite, skxg) = self + .choose_suite_and_kx_group( + version, + certkey.get_key().algorithm(), + cx.common.protocol, + client_hello + .named_groups + .as_deref() + .unwrap_or_default(), + &client_hello.cipher_suites, + ) + .map_err(|incompat| { + cx.common + .send_fatal_alert(AlertDescription::HandshakeFailure, incompat) + })?; + + debug!("decided upon suite {suite:?}"); + cx.common.suite = Some(suite); + cx.common.kx_state = KxState::Start(skxg); + + // Start handshake hash. + let starting_hash = suite.hash_provider(); + let transcript = match self.transcript { + HandshakeHashOrBuffer::Buffer(inner) => inner.start_hash(starting_hash), + HandshakeHashOrBuffer::Hash(inner) + if inner.algorithm() == starting_hash.algorithm() => + { + inner + } + _ => { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::HandshakeHashVariedAfterRetry, + )); + } + }; + + // Save their Random. + let randoms = ConnectionRandoms::new( + client_hello.random, + Random::new(self.config.provider.secure_random)?, + ); + match suite { + SupportedCipherSuite::Tls13(suite) => tls13::CompleteClientHelloHandling { + config: self.config, + transcript, + suite, + randoms, + done_retry: self.done_retry, + send_tickets: self.send_tickets, + extra_exts: self.extra_exts, + } + .handle_client_hello(cx, certkey, m, client_hello, skxg, sig_schemes), + #[cfg(feature = "tls12")] + SupportedCipherSuite::Tls12(suite) => tls12::CompleteClientHelloHandling { + config: self.config, + transcript, + session_id: self.session_id, + suite, + using_ems: self.using_ems, + randoms, + send_ticket: self.send_tickets > 0, + extra_exts: self.extra_exts, + } + .handle_client_hello( + cx, + certkey, + m, + client_hello, + skxg, + sig_schemes, + tls13_enabled, + ), + } + } + + fn choose_suite_and_kx_group( + &self, + selected_version: ProtocolVersion, + sig_key_algorithm: SignatureAlgorithm, + protocol: Protocol, + client_groups: &[NamedGroup], + client_suites: &[CipherSuite], + ) -> Result<(SupportedCipherSuite, &'static dyn SupportedKxGroup), PeerIncompatible> { + // Determine which `KeyExchangeAlgorithm`s are theoretically possible, based + // on the offered and supported groups. + let mut ecdhe_possible = false; + let mut ffdhe_possible = false; + let mut ffdhe_offered = false; + let mut supported_groups = Vec::with_capacity(client_groups.len()); + + for offered_group in client_groups { + let supported = self + .config + .provider + .kx_groups + .iter() + .find(|skxg| { + skxg.usable_for_version(selected_version) && skxg.name() == *offered_group + }); + + match offered_group.key_exchange_algorithm() { + KeyExchangeAlgorithm::DHE => { + ffdhe_possible |= supported.is_some(); + ffdhe_offered = true; + } + KeyExchangeAlgorithm::ECDHE => { + ecdhe_possible |= supported.is_some(); + } + } + + supported_groups.push(supported); + } + + let first_supported_dhe_kxg = if selected_version == ProtocolVersion::TLSv1_2 { + // https://datatracker.ietf.org/doc/html/rfc7919#section-4 (paragraph 2) + let first_supported_dhe_kxg = self + .config + .provider + .kx_groups + .iter() + .find(|skxg| skxg.name().key_exchange_algorithm() == KeyExchangeAlgorithm::DHE); + ffdhe_possible |= !ffdhe_offered && first_supported_dhe_kxg.is_some(); + first_supported_dhe_kxg + } else { + // In TLS1.3, the server may only directly negotiate a group. + None + }; + + if !ecdhe_possible && !ffdhe_possible { + return Err(PeerIncompatible::NoKxGroupsInCommon); + } + + let mut suitable_suites_iter = self + .config + .provider + .cipher_suites + .iter() + .filter(|suite| { + // Reduce our supported ciphersuites by the certified key's algorithm. + suite.usable_for_signature_algorithm(sig_key_algorithm) + // And version + && suite.version().version == selected_version + // And protocol + && suite.usable_for_protocol(protocol) + // And support one of key exchange groups + && (ecdhe_possible && suite.usable_for_kx_algorithm(KeyExchangeAlgorithm::ECDHE) + || ffdhe_possible && suite.usable_for_kx_algorithm(KeyExchangeAlgorithm::DHE)) + }); + + // RFC 7919 (https://datatracker.ietf.org/doc/html/rfc7919#section-4) requires us to send + // the InsufficientSecurity alert in case we don't recognize client's FFDHE groups (i.e., + // `suitable_suites` becomes empty). But that does not make a lot of sense (e.g., client + // proposes FFDHE4096 and we only support FFDHE2048), so we ignore that requirement here, + // and continue to send HandshakeFailure. + + let suite = if self.config.ignore_client_order { + suitable_suites_iter.find(|suite| client_suites.contains(&suite.suite())) + } else { + let suitable_suites = suitable_suites_iter.collect::<Vec<_>>(); + client_suites + .iter() + .find_map(|client_suite| { + suitable_suites + .iter() + .find(|x| *client_suite == x.suite()) + }) + .copied() + } + .ok_or(PeerIncompatible::NoCipherSuitesInCommon)?; + + // Finally, choose a key exchange group that is compatible with the selected cipher + // suite. + let maybe_skxg = supported_groups + .iter() + .find_map(|maybe_skxg| match maybe_skxg { + Some(skxg) => suite + .usable_for_kx_algorithm(skxg.name().key_exchange_algorithm()) + .then_some(*skxg), + None => None, + }); + + if selected_version == ProtocolVersion::TLSv1_3 { + // This unwrap is structurally guaranteed by the early return for `!ffdhe_possible && !ecdhe_possible` + return Ok((*suite, *maybe_skxg.unwrap())); + } + + // For TLS1.2, the server can unilaterally choose a DHE group if it has one and + // there was no better option. + match maybe_skxg { + Some(skxg) => Ok((*suite, *skxg)), + None if suite.usable_for_kx_algorithm(KeyExchangeAlgorithm::DHE) => { + // If kx for the selected cipher suite is DHE and no DHE groups are specified in the extension, + // the server is free to choose DHE params, we choose the first DHE kx group of the provider. + if let Some(server_selected_ffdhe_skxg) = first_supported_dhe_kxg { + Ok((*suite, *server_selected_ffdhe_skxg)) + } else { + Err(PeerIncompatible::NoKxGroupsInCommon) + } + } + None => Err(PeerIncompatible::NoKxGroupsInCommon), + } + } +} + +impl State<ServerConnectionData> for ExpectClientHello { + fn handle<'m>( + self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> NextStateOrError<'m> + where + Self: 'm, + { + let (client_hello, sig_schemes) = process_client_hello(&m, self.done_retry, cx)?; + self.with_certified_key(sig_schemes, client_hello, &m, cx) + } + + fn into_owned(self: Box<Self>) -> NextState<'static> { + self + } +} + +/// Configuration-independent validation of a `ClientHello` message. +/// +/// This represents the first part of the `ClientHello` handling, where we do all validation that +/// doesn't depend on a `ServerConfig` being available and extract everything needed to build a +/// [`ClientHello`] value for a [`ResolvesServerCert`]. +/// +/// Note that this will modify `data.sni` even if config or certificate resolution fail. +/// +/// [`ResolvesServerCert`]: crate::server::ResolvesServerCert +pub(super) fn process_client_hello<'m>( + m: &'m Message<'m>, + done_retry: bool, + cx: &mut ServerContext<'_>, +) -> Result<(&'m ClientHelloPayload, Vec<SignatureScheme>), Error> { + let client_hello = + require_handshake_msg!(m, HandshakeType::ClientHello, HandshakePayload::ClientHello)?; + trace!("we got a clienthello {client_hello:?}"); + + if !client_hello + .compression_methods + .contains(&Compression::Null) + { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerIncompatible::NullCompressionRequired, + )); + } + + // No handshake messages should follow this one in this flight. + cx.common.check_aligned_handshake()?; + + // Extract and validate the SNI DNS name, if any, before giving it to + // the cert resolver. In particular, if it is invalid then we should + // send an Illegal Parameter alert instead of the Internal Error alert + // (or whatever) that we'd send if this were checked later or in a + // different way. + // + // [RFC6066][] specifies that literal IP addresses are illegal in + // `ServerName`s with a `name_type` of `host_name`. + // + // Some clients incorrectly send such extensions: we choose to + // successfully parse these (into `ServerNamePayload::IpAddress`) + // but then act like the client sent no `server_name` extension. + // + // [RFC6066]: https://datatracker.ietf.org/doc/html/rfc6066#section-3 + let sni = match &client_hello.server_name { + Some(ServerNamePayload::SingleDnsName(dns_name)) => Some(dns_name.to_lowercase_owned()), + Some(ServerNamePayload::IpAddress) => None, + Some(ServerNamePayload::Invalid) => { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::ServerNameMustContainOneHostName, + )); + } + None => None, + }; + + // save only the first SNI + if let (Some(sni), false) = (&sni, done_retry) { + // Save the SNI into the session. + // The SNI hostname is immutable once set. + assert!(cx.data.sni.is_none()); + cx.data.sni = Some(sni.clone()); + } else if cx.data.sni != sni { + return Err(PeerMisbehaved::ServerNameDifferedOnRetry.into()); + } + + let sig_schemes = client_hello + .signature_schemes + .as_ref() + .ok_or_else(|| { + cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::SignatureAlgorithmsExtensionRequired, + ) + })?; + + Ok((client_hello, sig_schemes.to_owned())) +} + +pub(crate) enum HandshakeHashOrBuffer { + Buffer(HandshakeHashBuffer), + Hash(HandshakeHash), +} diff --git a/vendor/rustls/src/server/server_conn.rs b/vendor/rustls/src/server/server_conn.rs new file mode 100644 index 00000000..d1024b14 --- /dev/null +++ b/vendor/rustls/src/server/server_conn.rs @@ -0,0 +1,1288 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::fmt; +use core::fmt::{Debug, Formatter}; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +#[cfg(feature = "std")] +use std::io; + +use pki_types::{DnsName, UnixTime}; + +use super::hs; +#[cfg(feature = "std")] +use crate::WantsVerifier; +use crate::builder::ConfigBuilder; +use crate::common_state::{CommonState, Side}; +#[cfg(feature = "std")] +use crate::common_state::{Protocol, State}; +use crate::conn::{ConnectionCommon, ConnectionCore, UnbufferedConnectionCommon}; +#[cfg(doc)] +use crate::crypto; +use crate::crypto::CryptoProvider; +use crate::enums::{CertificateType, CipherSuite, ProtocolVersion, SignatureScheme}; +use crate::error::Error; +use crate::kernel::KernelConnection; +use crate::log::trace; +use crate::msgs::base::Payload; +use crate::msgs::handshake::{ClientHelloPayload, ProtocolName, ServerExtensionsInput}; +use crate::msgs::message::Message; +use crate::suites::ExtractedSecrets; +use crate::sync::Arc; +#[cfg(feature = "std")] +use crate::time_provider::DefaultTimeProvider; +use crate::time_provider::TimeProvider; +use crate::vecbuf::ChunkVecBuffer; +use crate::{ + DistinguishedName, KeyLog, NamedGroup, WantsVersions, compress, sign, verify, versions, +}; + +/// A trait for the ability to store server session data. +/// +/// The keys and values are opaque. +/// +/// Inserted keys are randomly chosen by the library and have +/// no internal structure (in other words, you may rely on all +/// bits being uniformly random). Queried keys are untrusted data. +/// +/// Both the keys and values should be treated as +/// **highly sensitive data**, containing enough key material +/// to break all security of the corresponding sessions. +/// +/// Implementations can be lossy (in other words, forgetting +/// key/value pairs) without any negative security consequences. +/// +/// However, note that `take` **must** reliably delete a returned +/// value. If it does not, there may be security consequences. +/// +/// `put` and `take` are mutating operations; this isn't expressed +/// in the type system to allow implementations freedom in +/// how to achieve interior mutability. `Mutex` is a common +/// choice. +pub trait StoresServerSessions: Debug + Send + Sync { + /// Store session secrets encoded in `value` against `key`, + /// overwrites any existing value against `key`. Returns `true` + /// if the value was stored. + fn put(&self, key: Vec<u8>, value: Vec<u8>) -> bool; + + /// Find a value with the given `key`. Return it, or None + /// if it doesn't exist. + fn get(&self, key: &[u8]) -> Option<Vec<u8>>; + + /// Find a value with the given `key`. Return it and delete it; + /// or None if it doesn't exist. + fn take(&self, key: &[u8]) -> Option<Vec<u8>>; + + /// Whether the store can cache another session. This is used to indicate to clients + /// whether their session can be resumed; the implementation is not required to remember + /// a session even if it returns `true` here. + fn can_cache(&self) -> bool; +} + +/// A trait for the ability to encrypt and decrypt tickets. +pub trait ProducesTickets: Debug + Send + Sync { + /// Returns true if this implementation will encrypt/decrypt + /// tickets. Should return false if this is a dummy + /// implementation: the server will not send the SessionTicket + /// extension and will not call the other functions. + fn enabled(&self) -> bool; + + /// Returns the lifetime in seconds of tickets produced now. + /// The lifetime is provided as a hint to clients that the + /// ticket will not be useful after the given time. + /// + /// This lifetime must be implemented by key rolling and + /// erasure, *not* by storing a lifetime in the ticket. + /// + /// The objective is to limit damage to forward secrecy caused + /// by tickets, not just limiting their lifetime. + fn lifetime(&self) -> u32; + + /// Encrypt and authenticate `plain`, returning the resulting + /// ticket. Return None if `plain` cannot be encrypted for + /// some reason: an empty ticket will be sent and the connection + /// will continue. + fn encrypt(&self, plain: &[u8]) -> Option<Vec<u8>>; + + /// Decrypt `cipher`, validating its authenticity protection + /// and recovering the plaintext. `cipher` is fully attacker + /// controlled, so this decryption must be side-channel free, + /// panic-proof, and otherwise bullet-proof. If the decryption + /// fails, return None. + fn decrypt(&self, cipher: &[u8]) -> Option<Vec<u8>>; +} + +/// How to choose a certificate chain and signing key for use +/// in server authentication. +/// +/// This is suitable when selecting a certificate does not require +/// I/O or when the application is using blocking I/O anyhow. +/// +/// For applications that use async I/O and need to do I/O to choose +/// a certificate (for instance, fetching a certificate from a data store), +/// the [`Acceptor`] interface is more suitable. +pub trait ResolvesServerCert: Debug + Send + Sync { + /// Choose a certificate chain and matching key given simplified + /// ClientHello information. + /// + /// Return `None` to abort the handshake. + fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<sign::CertifiedKey>>; + + /// Return true when the server only supports raw public keys. + fn only_raw_public_keys(&self) -> bool { + false + } +} + +/// A struct representing the received Client Hello +#[derive(Debug)] +pub struct ClientHello<'a> { + pub(super) server_name: &'a Option<DnsName<'a>>, + pub(super) signature_schemes: &'a [SignatureScheme], + pub(super) alpn: Option<&'a Vec<ProtocolName>>, + pub(super) server_cert_types: Option<&'a [CertificateType]>, + pub(super) client_cert_types: Option<&'a [CertificateType]>, + pub(super) cipher_suites: &'a [CipherSuite], + /// The [certificate_authorities] extension, if it was sent by the client. + /// + /// [certificate_authorities]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.4 + pub(super) certificate_authorities: Option<&'a [DistinguishedName]>, + pub(super) named_groups: Option<&'a [NamedGroup]>, +} + +impl<'a> ClientHello<'a> { + /// Get the server name indicator. + /// + /// Returns `None` if the client did not supply a SNI. + pub fn server_name(&self) -> Option<&str> { + self.server_name + .as_ref() + .map(<DnsName<'_> as AsRef<str>>::as_ref) + } + + /// Get the compatible signature schemes. + /// + /// Returns standard-specified default if the client omitted this extension. + pub fn signature_schemes(&self) -> &[SignatureScheme] { + self.signature_schemes + } + + /// Get the ALPN protocol identifiers submitted by the client. + /// + /// Returns `None` if the client did not include an ALPN extension. + /// + /// Application Layer Protocol Negotiation (ALPN) is a TLS extension that lets a client + /// submit a set of identifiers that each a represent an application-layer protocol. + /// The server will then pick its preferred protocol from the set submitted by the client. + /// Each identifier is represented as a byte array, although common values are often ASCII-encoded. + /// See the official RFC-7301 specifications at <https://datatracker.ietf.org/doc/html/rfc7301> + /// for more information on ALPN. + /// + /// For example, a HTTP client might specify "http/1.1" and/or "h2". Other well-known values + /// are listed in the at IANA registry at + /// <https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids>. + /// + /// The server can specify supported ALPN protocols by setting [`ServerConfig::alpn_protocols`]. + /// During the handshake, the server will select the first protocol configured that the client supports. + pub fn alpn(&self) -> Option<impl Iterator<Item = &'a [u8]>> { + self.alpn.map(|protocols| { + protocols + .iter() + .map(|proto| proto.as_ref()) + }) + } + + /// Get cipher suites. + pub fn cipher_suites(&self) -> &[CipherSuite] { + self.cipher_suites + } + + /// Get the server certificate types offered in the ClientHello. + /// + /// Returns `None` if the client did not include a certificate type extension. + pub fn server_cert_types(&self) -> Option<&'a [CertificateType]> { + self.server_cert_types + } + + /// Get the client certificate types offered in the ClientHello. + /// + /// Returns `None` if the client did not include a certificate type extension. + pub fn client_cert_types(&self) -> Option<&'a [CertificateType]> { + self.client_cert_types + } + + /// Get the [certificate_authorities] extension sent by the client. + /// + /// Returns `None` if the client did not send this extension. + /// + /// [certificate_authorities]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.4 + pub fn certificate_authorities(&self) -> Option<&'a [DistinguishedName]> { + self.certificate_authorities + } + + /// Get the [`named_groups`] extension sent by the client. + /// + /// This means different things in different versions of TLS: + /// + /// Originally it was introduced as the "[`elliptic_curves`]" extension for TLS1.2. + /// It described the elliptic curves supported by a client for all purposes: key + /// exchange, signature verification (for server authentication), and signing (for + /// client auth). Later [RFC7919] extended this to include FFDHE "named groups", + /// but FFDHE groups in this context only relate to key exchange. + /// + /// In TLS1.3 it was renamed to "[`named_groups`]" and now describes all types + /// of key exchange mechanisms, and does not relate at all to elliptic curves + /// used for signatures. + /// + /// [`elliptic_curves`]: https://datatracker.ietf.org/doc/html/rfc4492#section-5.1.1 + /// [RFC7919]: https://datatracker.ietf.org/doc/html/rfc7919#section-2 + /// [`named_groups`]:https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.7 + pub fn named_groups(&self) -> Option<&'a [NamedGroup]> { + self.named_groups + } +} + +/// Common configuration for a set of server sessions. +/// +/// Making one of these is cheap, though one of the inputs may be expensive: gathering trust roots +/// from the operating system to add to the [`RootCertStore`] passed to a `ClientCertVerifier` +/// builder may take on the order of a few hundred milliseconds. +/// +/// These must be created via the [`ServerConfig::builder()`] or [`ServerConfig::builder_with_provider()`] +/// function. +/// +/// # Defaults +/// +/// * [`ServerConfig::max_fragment_size`]: the default is `None` (meaning 16kB). +/// * [`ServerConfig::session_storage`]: if the `std` feature is enabled, the default stores 256 +/// sessions in memory. If the `std` feature is not enabled, the default is to not store any +/// sessions. In a no-std context, by enabling the `hashbrown` feature you may provide your +/// own `session_storage` using [`ServerSessionMemoryCache`] and a `crate::lock::MakeMutex` +/// implementation. +/// * [`ServerConfig::alpn_protocols`]: the default is empty -- no ALPN protocol is negotiated. +/// * [`ServerConfig::key_log`]: key material is not logged. +/// * [`ServerConfig::send_tls13_tickets`]: 2 tickets are sent. +/// * [`ServerConfig::cert_compressors`]: depends on the crate features, see [`compress::default_cert_compressors()`]. +/// * [`ServerConfig::cert_compression_cache`]: caches the most recently used 4 compressions +/// * [`ServerConfig::cert_decompressors`]: depends on the crate features, see [`compress::default_cert_decompressors()`]. +/// +/// # Sharing resumption storage between `ServerConfig`s +/// +/// In a program using many `ServerConfig`s it may improve resumption rates +/// (which has a significant impact on connection performance) if those +/// configs share [`ServerConfig::session_storage`] or [`ServerConfig::ticketer`]. +/// +/// However, caution is needed: other fields influence the security of a session +/// and resumption between them can be surprising. If sharing +/// [`ServerConfig::session_storage`] or [`ServerConfig::ticketer`] between two +/// `ServerConfig`s, you should also evaluate the following fields and ensure +/// they are equivalent: +/// +/// * `ServerConfig::verifier` -- client authentication requirements, +/// * [`ServerConfig::cert_resolver`] -- server identities. +/// +/// To illustrate, imagine two `ServerConfig`s `A` and `B`. `A` requires +/// client authentication, `B` does not. If `A` and `B` shared a resumption store, +/// it would be possible for a session originated by `B` (that is, an unauthenticated client) +/// to be inserted into the store, and then resumed by `A`. This would give a false +/// impression to the user of `A` that the client was authenticated. This is possible +/// whether the resumption is performed statefully (via [`ServerConfig::session_storage`]) +/// or statelessly (via [`ServerConfig::ticketer`]). +/// +/// _Unlike_ `ClientConfig`, rustls does not enforce any policy here. +/// +/// [`RootCertStore`]: crate::RootCertStore +/// [`ServerSessionMemoryCache`]: crate::server::handy::ServerSessionMemoryCache +#[derive(Clone, Debug)] +pub struct ServerConfig { + /// Source of randomness and other crypto. + pub(super) provider: Arc<CryptoProvider>, + + /// Ignore the client's ciphersuite order. Instead, + /// choose the top ciphersuite in the server list + /// which is supported by the client. + pub ignore_client_order: bool, + + /// The maximum size of plaintext input to be emitted in a single TLS record. + /// A value of None is equivalent to the [TLS maximum] of 16 kB. + /// + /// rustls enforces an arbitrary minimum of 32 bytes for this field. + /// Out of range values are reported as errors from [ServerConnection::new]. + /// + /// Setting this value to a little less than the TCP MSS may improve latency + /// for stream-y workloads. + /// + /// [TLS maximum]: https://datatracker.ietf.org/doc/html/rfc8446#section-5.1 + /// [ServerConnection::new]: crate::server::ServerConnection::new + pub max_fragment_size: Option<usize>, + + /// How to store client sessions. + /// + /// See [ServerConfig#sharing-resumption-storage-between-serverconfigs] + /// for a warning related to this field. + pub session_storage: Arc<dyn StoresServerSessions>, + + /// How to produce tickets. + /// + /// See [ServerConfig#sharing-resumption-storage-between-serverconfigs] + /// for a warning related to this field. + pub ticketer: Arc<dyn ProducesTickets>, + + /// How to choose a server cert and key. This is usually set by + /// [ConfigBuilder::with_single_cert] or [ConfigBuilder::with_cert_resolver]. + /// For async applications, see also [Acceptor]. + pub cert_resolver: Arc<dyn ResolvesServerCert>, + + /// Protocol names we support, most preferred first. + /// If empty we don't do ALPN at all. + pub alpn_protocols: Vec<Vec<u8>>, + + /// Supported protocol versions, in no particular order. + /// The default is all supported versions. + pub(super) versions: versions::EnabledVersions, + + /// How to verify client certificates. + pub(super) verifier: Arc<dyn verify::ClientCertVerifier>, + + /// How to output key material for debugging. The default + /// does nothing. + pub key_log: Arc<dyn KeyLog>, + + /// Allows traffic secrets to be extracted after the handshake, + /// e.g. for kTLS setup. + pub enable_secret_extraction: bool, + + /// Amount of early data to accept for sessions created by + /// this config. Specify 0 to disable early data. The + /// default is 0. + /// + /// Read the early data via [`ServerConnection::early_data`]. + /// + /// The units for this are _both_ plaintext bytes, _and_ ciphertext + /// bytes, depending on whether the server accepts a client's early_data + /// or not. It is therefore recommended to include some slop in + /// this value to account for the unknown amount of ciphertext + /// expansion in the latter case. + pub max_early_data_size: u32, + + /// Whether the server should send "0.5RTT" data. This means the server + /// sends data after its first flight of handshake messages, without + /// waiting for the client to complete the handshake. + /// + /// This can improve TTFB latency for either server-speaks-first protocols, + /// or client-speaks-first protocols when paired with "0RTT" data. This + /// comes at the cost of a subtle weakening of the normal handshake + /// integrity guarantees that TLS provides. Note that the initial + /// `ClientHello` is indirectly authenticated because it is included + /// in the transcript used to derive the keys used to encrypt the data. + /// + /// This only applies to TLS1.3 connections. TLS1.2 connections cannot + /// do this optimisation and this setting is ignored for them. It is + /// also ignored for TLS1.3 connections that even attempt client + /// authentication. + /// + /// This defaults to false. This means the first application data + /// sent by the server comes after receiving and validating the client's + /// handshake up to the `Finished` message. This is the safest option. + pub send_half_rtt_data: bool, + + /// How many TLS1.3 tickets to send immediately after a successful + /// handshake. + /// + /// Because TLS1.3 tickets are single-use, this allows + /// a client to perform multiple resumptions. + /// + /// The default is 2. + /// + /// If this is 0, no tickets are sent and clients will not be able to + /// do any resumption. + pub send_tls13_tickets: usize, + + /// If set to `true`, requires the client to support the extended + /// master secret extraction method defined in [RFC 7627]. + /// + /// The default is `true` if the "fips" crate feature is enabled, + /// `false` otherwise. + /// + /// It must be set to `true` to meet FIPS requirement mentioned in section + /// **D.Q Transition of the TLS 1.2 KDF to Support the Extended Master + /// Secret** from [FIPS 140-3 IG.pdf]. + /// + /// [RFC 7627]: https://datatracker.ietf.org/doc/html/rfc7627 + /// [FIPS 140-3 IG.pdf]: https://csrc.nist.gov/csrc/media/Projects/cryptographic-module-validation-program/documents/fips%20140-3/FIPS%20140-3%20IG.pdf + #[cfg(feature = "tls12")] + pub require_ems: bool, + + /// Provides the current system time + pub time_provider: Arc<dyn TimeProvider>, + + /// How to compress the server's certificate chain. + /// + /// If a client supports this extension, and advertises support + /// for one of the compression algorithms included here, the + /// server certificate will be compressed according to [RFC8779]. + /// + /// This only applies to TLS1.3 connections. It is ignored for + /// TLS1.2 connections. + /// + /// [RFC8779]: https://datatracker.ietf.org/doc/rfc8879/ + pub cert_compressors: Vec<&'static dyn compress::CertCompressor>, + + /// Caching for compressed certificates. + /// + /// This is optional: [`compress::CompressionCache::Disabled`] gives + /// a cache that does no caching. + pub cert_compression_cache: Arc<compress::CompressionCache>, + + /// How to decompress the clients's certificate chain. + /// + /// If this is non-empty, the [RFC8779] certificate compression + /// extension is offered when requesting client authentication, + /// and any compressed certificates are transparently decompressed + /// during the handshake. + /// + /// This only applies to TLS1.3 connections. It is ignored for + /// TLS1.2 connections. + /// + /// [RFC8779]: https://datatracker.ietf.org/doc/rfc8879/ + pub cert_decompressors: Vec<&'static dyn compress::CertDecompressor>, +} + +impl ServerConfig { + /// Create a builder for a server configuration with + /// [the process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider] + /// and safe protocol version defaults. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + #[cfg(feature = "std")] + pub fn builder() -> ConfigBuilder<Self, WantsVerifier> { + Self::builder_with_protocol_versions(versions::DEFAULT_VERSIONS) + } + + /// Create a builder for a server configuration with + /// [the process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider] + /// and the provided protocol versions. + /// + /// Panics if + /// - the supported versions are not compatible with the provider (eg. + /// the combination of ciphersuites supported by the provider and supported + /// versions lead to zero cipher suites being usable), + /// - if a `CryptoProvider` cannot be resolved using a combination of + /// the crate features and process default. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + #[cfg(feature = "std")] + pub fn builder_with_protocol_versions( + versions: &[&'static versions::SupportedProtocolVersion], + ) -> ConfigBuilder<Self, WantsVerifier> { + // Safety assumptions: + // 1. that the provider has been installed (explicitly or implicitly) + // 2. that the process-level default provider is usable with the supplied protocol versions. + Self::builder_with_provider( + CryptoProvider::get_default_or_install_from_crate_features().clone(), + ) + .with_protocol_versions(versions) + .unwrap() + } + + /// Create a builder for a server configuration with a specific [`CryptoProvider`]. + /// + /// This will use the provider's configured ciphersuites. You must additionally choose + /// which protocol versions to enable, using `with_protocol_versions` or + /// `with_safe_default_protocol_versions` and handling the `Result` in case a protocol + /// version is not supported by the provider's ciphersuites. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + #[cfg(feature = "std")] + pub fn builder_with_provider( + provider: Arc<CryptoProvider>, + ) -> ConfigBuilder<Self, WantsVersions> { + ConfigBuilder { + state: WantsVersions {}, + provider, + time_provider: Arc::new(DefaultTimeProvider), + side: PhantomData, + } + } + + /// Create a builder for a server configuration with no default implementation details. + /// + /// This API must be used by `no_std` users. + /// + /// You must provide a specific [`TimeProvider`]. + /// + /// You must provide a specific [`CryptoProvider`]. + /// + /// This will use the provider's configured ciphersuites. You must additionally choose + /// which protocol versions to enable, using `with_protocol_versions` or + /// `with_safe_default_protocol_versions` and handling the `Result` in case a protocol + /// version is not supported by the provider's ciphersuites. + /// + /// For more information, see the [`ConfigBuilder`] documentation. + pub fn builder_with_details( + provider: Arc<CryptoProvider>, + time_provider: Arc<dyn TimeProvider>, + ) -> ConfigBuilder<Self, WantsVersions> { + ConfigBuilder { + state: WantsVersions {}, + provider, + time_provider, + side: PhantomData, + } + } + + /// Return `true` if connections made with this `ServerConfig` will + /// operate in FIPS mode. + /// + /// This is different from [`CryptoProvider::fips()`]: [`CryptoProvider::fips()`] + /// is concerned only with cryptography, whereas this _also_ covers TLS-level + /// configuration that NIST recommends. + pub fn fips(&self) -> bool { + #[cfg(feature = "tls12")] + { + self.provider.fips() && self.require_ems + } + + #[cfg(not(feature = "tls12"))] + { + self.provider.fips() + } + } + + /// Return the crypto provider used to construct this client configuration. + pub fn crypto_provider(&self) -> &Arc<CryptoProvider> { + &self.provider + } + + /// We support a given TLS version if it's quoted in the configured + /// versions *and* at least one ciphersuite for this version is + /// also configured. + pub(crate) fn supports_version(&self, v: ProtocolVersion) -> bool { + self.versions.contains(v) + && self + .provider + .cipher_suites + .iter() + .any(|cs| cs.version().version == v) + } + + #[cfg(feature = "std")] + pub(crate) fn supports_protocol(&self, proto: Protocol) -> bool { + self.provider + .cipher_suites + .iter() + .any(|cs| cs.usable_for_protocol(proto)) + } + + pub(super) fn current_time(&self) -> Result<UnixTime, Error> { + self.time_provider + .current_time() + .ok_or(Error::FailedToGetCurrentTime) + } +} + +#[cfg(feature = "std")] +mod connection { + use alloc::boxed::Box; + use core::fmt; + use core::fmt::{Debug, Formatter}; + use core::ops::{Deref, DerefMut}; + use std::io; + + use super::{ + Accepted, Accepting, EarlyDataState, ServerConfig, ServerConnectionData, + ServerExtensionsInput, + }; + use crate::common_state::{CommonState, Context, Side}; + use crate::conn::{ConnectionCommon, ConnectionCore}; + use crate::error::Error; + use crate::server::hs; + use crate::suites::ExtractedSecrets; + use crate::sync::Arc; + use crate::vecbuf::ChunkVecBuffer; + + /// Allows reading of early data in resumed TLS1.3 connections. + /// + /// "Early data" is also known as "0-RTT data". + /// + /// This structure implements [`std::io::Read`]. + pub struct ReadEarlyData<'a> { + early_data: &'a mut EarlyDataState, + } + + impl<'a> ReadEarlyData<'a> { + fn new(early_data: &'a mut EarlyDataState) -> Self { + ReadEarlyData { early_data } + } + } + + impl io::Read for ReadEarlyData<'_> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + self.early_data.read(buf) + } + + #[cfg(read_buf)] + fn read_buf(&mut self, cursor: core::io::BorrowedCursor<'_>) -> io::Result<()> { + self.early_data.read_buf(cursor) + } + } + + /// This represents a single TLS server connection. + /// + /// Send TLS-protected data to the peer using the `io::Write` trait implementation. + /// Read data from the peer using the `io::Read` trait implementation. + pub struct ServerConnection { + pub(super) inner: ConnectionCommon<ServerConnectionData>, + } + + impl ServerConnection { + /// Make a new ServerConnection. `config` controls how + /// we behave in the TLS protocol. + pub fn new(config: Arc<ServerConfig>) -> Result<Self, Error> { + Ok(Self { + inner: ConnectionCommon::from(ConnectionCore::for_server( + config, + ServerExtensionsInput::default(), + )?), + }) + } + + /// Retrieves the server name, if any, used to select the certificate and + /// private key. + /// + /// This returns `None` until some time after the client's server name indication + /// (SNI) extension value is processed during the handshake. It will never be + /// `None` when the connection is ready to send or process application data, + /// unless the client does not support SNI. + /// + /// This is useful for application protocols that need to enforce that the + /// server name matches an application layer protocol hostname. For + /// example, HTTP/1.1 servers commonly expect the `Host:` header field of + /// every request on a connection to match the hostname in the SNI extension + /// when the client provides the SNI extension. + /// + /// The server name is also used to match sessions during session resumption. + pub fn server_name(&self) -> Option<&str> { + self.inner.core.get_sni_str() + } + + /// Application-controlled portion of the resumption ticket supplied by the client, if any. + /// + /// Recovered from the prior session's `set_resumption_data`. Integrity is guaranteed by rustls. + /// + /// Returns `Some` if and only if a valid resumption ticket has been received from the client. + pub fn received_resumption_data(&self) -> Option<&[u8]> { + self.inner + .core + .data + .received_resumption_data + .as_ref() + .map(|x| &x[..]) + } + + /// Set the resumption data to embed in future resumption tickets supplied to the client. + /// + /// Defaults to the empty byte string. Must be less than 2^15 bytes to allow room for other + /// data. Should be called while `is_handshaking` returns true to ensure all transmitted + /// resumption tickets are affected. + /// + /// Integrity will be assured by rustls, but the data will be visible to the client. If secrecy + /// from the client is desired, encrypt the data separately. + pub fn set_resumption_data(&mut self, data: &[u8]) { + assert!(data.len() < 2usize.pow(15)); + self.inner.core.data.resumption_data = data.into(); + } + + /// Explicitly discard early data, notifying the client + /// + /// Useful if invariants encoded in `received_resumption_data()` cannot be respected. + /// + /// Must be called while `is_handshaking` is true. + pub fn reject_early_data(&mut self) { + self.inner.core.reject_early_data() + } + + /// Returns an `io::Read` implementer you can read bytes from that are + /// received from a client as TLS1.3 0RTT/"early" data, during the handshake. + /// + /// This returns `None` in many circumstances, such as : + /// + /// - Early data is disabled if [`ServerConfig::max_early_data_size`] is zero (the default). + /// - The session negotiated with the client is not TLS1.3. + /// - The client just doesn't support early data. + /// - The connection doesn't resume an existing session. + /// - The client hasn't sent a full ClientHello yet. + pub fn early_data(&mut self) -> Option<ReadEarlyData<'_>> { + let data = &mut self.inner.core.data; + if data.early_data.was_accepted() { + Some(ReadEarlyData::new(&mut data.early_data)) + } else { + None + } + } + + /// Return true if the connection was made with a `ServerConfig` that is FIPS compatible. + /// + /// This is different from [`crate::crypto::CryptoProvider::fips()`]: + /// it is concerned only with cryptography, whereas this _also_ covers TLS-level + /// configuration that NIST recommends, as well as ECH HPKE suites if applicable. + pub fn fips(&self) -> bool { + self.inner.core.common_state.fips + } + + /// Extract secrets, so they can be used when configuring kTLS, for example. + /// Should be used with care as it exposes secret key material. + pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + self.inner.dangerous_extract_secrets() + } + } + + impl Debug for ServerConnection { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ServerConnection") + .finish() + } + } + + impl Deref for ServerConnection { + type Target = ConnectionCommon<ServerConnectionData>; + + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl DerefMut for ServerConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } + } + + impl From<ServerConnection> for crate::Connection { + fn from(conn: ServerConnection) -> Self { + Self::Server(conn) + } + } + + /// Handle a server-side connection before configuration is available. + /// + /// `Acceptor` allows the caller to choose a [`ServerConfig`] after reading + /// the [`super::ClientHello`] of an incoming connection. This is useful for servers + /// that choose different certificates or cipher suites based on the + /// characteristics of the `ClientHello`. In particular it is useful for + /// servers that need to do some I/O to load a certificate and its private key + /// and don't want to use the blocking interface provided by + /// [`super::ResolvesServerCert`]. + /// + /// Create an Acceptor with [`Acceptor::default()`]. + /// + /// # Example + /// + /// ```no_run + /// # #[cfg(feature = "aws_lc_rs")] { + /// # fn choose_server_config( + /// # _: rustls::server::ClientHello, + /// # ) -> std::sync::Arc<rustls::ServerConfig> { + /// # unimplemented!(); + /// # } + /// # #[allow(unused_variables)] + /// # fn main() { + /// use rustls::server::{Acceptor, ServerConfig}; + /// let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap(); + /// for stream in listener.incoming() { + /// let mut stream = stream.unwrap(); + /// let mut acceptor = Acceptor::default(); + /// let accepted = loop { + /// acceptor.read_tls(&mut stream).unwrap(); + /// if let Some(accepted) = acceptor.accept().unwrap() { + /// break accepted; + /// } + /// }; + /// + /// // For some user-defined choose_server_config: + /// let config = choose_server_config(accepted.client_hello()); + /// let conn = accepted + /// .into_connection(config) + /// .unwrap(); + /// + /// // Proceed with handling the ServerConnection. + /// } + /// # } + /// # } + /// ``` + pub struct Acceptor { + inner: Option<ConnectionCommon<ServerConnectionData>>, + } + + impl Default for Acceptor { + /// Return an empty Acceptor, ready to receive bytes from a new client connection. + fn default() -> Self { + Self { + inner: Some( + ConnectionCore::new( + Box::new(Accepting), + ServerConnectionData::default(), + CommonState::new(Side::Server), + ) + .into(), + ), + } + } + } + + impl Acceptor { + /// Read TLS content from `rd`. + /// + /// Returns an error if this `Acceptor` has already yielded an [`Accepted`]. For more details, + /// refer to [`Connection::read_tls()`]. + /// + /// [`Connection::read_tls()`]: crate::Connection::read_tls + pub fn read_tls(&mut self, rd: &mut dyn io::Read) -> Result<usize, io::Error> { + match &mut self.inner { + Some(conn) => conn.read_tls(rd), + None => Err(io::Error::new( + io::ErrorKind::Other, + "acceptor cannot read after successful acceptance", + )), + } + } + + /// Check if a `ClientHello` message has been received. + /// + /// Returns `Ok(None)` if the complete `ClientHello` has not yet been received. + /// Do more I/O and then call this function again. + /// + /// Returns `Ok(Some(accepted))` if the connection has been accepted. Call + /// `accepted.into_connection()` to continue. Do not call this function again. + /// + /// Returns `Err((err, alert))` if an error occurred. If an alert is returned, the + /// application should call `alert.write()` to send the alert to the client. It should + /// not call `accept()` again. + pub fn accept(&mut self) -> Result<Option<Accepted>, (Error, AcceptedAlert)> { + let Some(mut connection) = self.inner.take() else { + return Err(( + Error::General("Acceptor polled after completion".into()), + AcceptedAlert::empty(), + )); + }; + + let message = match connection.first_handshake_message() { + Ok(Some(msg)) => msg, + Ok(None) => { + self.inner = Some(connection); + return Ok(None); + } + Err(err) => return Err((err, AcceptedAlert::from(connection))), + }; + + let mut cx = Context::from(&mut connection); + let sig_schemes = match hs::process_client_hello(&message, false, &mut cx) { + Ok((_, sig_schemes)) => sig_schemes, + Err(err) => { + return Err((err, AcceptedAlert::from(connection))); + } + }; + + Ok(Some(Accepted { + connection, + message, + sig_schemes, + })) + } + } + + /// Represents a TLS alert resulting from handling the client's `ClientHello` message. + /// + /// When [`Acceptor::accept()`] returns an error, it yields an `AcceptedAlert` such that the + /// application can communicate failure to the client via [`AcceptedAlert::write()`]. + pub struct AcceptedAlert(ChunkVecBuffer); + + impl AcceptedAlert { + pub(super) fn empty() -> Self { + Self(ChunkVecBuffer::new(None)) + } + + /// Send the alert to the client. + /// + /// To account for short writes this function should be called repeatedly until it + /// returns `Ok(0)` or an error. + pub fn write(&mut self, wr: &mut dyn io::Write) -> Result<usize, io::Error> { + self.0.write_to(wr) + } + + /// Send the alert to the client. + /// + /// This function will invoke the writer until the buffer is empty. + pub fn write_all(&mut self, wr: &mut dyn io::Write) -> Result<(), io::Error> { + while self.write(wr)? != 0 {} + Ok(()) + } + } + + impl From<ConnectionCommon<ServerConnectionData>> for AcceptedAlert { + fn from(conn: ConnectionCommon<ServerConnectionData>) -> Self { + Self(conn.core.common_state.sendable_tls) + } + } + + impl Debug for AcceptedAlert { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("AcceptedAlert").finish() + } + } +} + +#[cfg(feature = "std")] +pub use connection::{AcceptedAlert, Acceptor, ReadEarlyData, ServerConnection}; + +/// Unbuffered version of `ServerConnection` +/// +/// See the [`crate::unbuffered`] module docs for more details +pub struct UnbufferedServerConnection { + inner: UnbufferedConnectionCommon<ServerConnectionData>, +} + +impl UnbufferedServerConnection { + /// Make a new ServerConnection. `config` controls how we behave in the TLS protocol. + pub fn new(config: Arc<ServerConfig>) -> Result<Self, Error> { + Ok(Self { + inner: UnbufferedConnectionCommon::from(ConnectionCore::for_server( + config, + ServerExtensionsInput::default(), + )?), + }) + } + + /// Extract secrets, so they can be used when configuring kTLS, for example. + /// Should be used with care as it exposes secret key material. + #[deprecated = "dangerous_extract_secrets() does not support session tickets or \ + key updates, use dangerous_into_kernel_connection() instead"] + pub fn dangerous_extract_secrets(self) -> Result<ExtractedSecrets, Error> { + self.inner.dangerous_extract_secrets() + } + + /// Extract secrets and an [`KernelConnection`] object. + /// + /// This allows you use rustls to manage keys and then manage encryption and + /// decryption yourself (e.g. for kTLS). + /// + /// Should be used with care as it exposes secret key material. + /// + /// See the [`crate::kernel`] documentations for details on prerequisites + /// for calling this method. + pub fn dangerous_into_kernel_connection( + self, + ) -> Result<(ExtractedSecrets, KernelConnection<ServerConnectionData>), Error> { + self.inner + .core + .dangerous_into_kernel_connection() + } +} + +impl Deref for UnbufferedServerConnection { + type Target = UnbufferedConnectionCommon<ServerConnectionData>; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for UnbufferedServerConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl UnbufferedConnectionCommon<ServerConnectionData> { + pub(crate) fn pop_early_data(&mut self) -> Option<Vec<u8>> { + self.core.data.early_data.pop() + } + + pub(crate) fn peek_early_data(&self) -> Option<&[u8]> { + self.core.data.early_data.peek() + } +} + +/// Represents a `ClientHello` message received through the [`Acceptor`]. +/// +/// Contains the state required to resume the connection through [`Accepted::into_connection()`]. +pub struct Accepted { + connection: ConnectionCommon<ServerConnectionData>, + message: Message<'static>, + sig_schemes: Vec<SignatureScheme>, +} + +impl Accepted { + /// Get the [`ClientHello`] for this connection. + pub fn client_hello(&self) -> ClientHello<'_> { + let payload = Self::client_hello_payload(&self.message); + let ch = ClientHello { + server_name: &self.connection.core.data.sni, + signature_schemes: &self.sig_schemes, + alpn: payload.protocols.as_ref(), + server_cert_types: payload + .server_certificate_types + .as_deref(), + client_cert_types: payload + .client_certificate_types + .as_deref(), + cipher_suites: &payload.cipher_suites, + certificate_authorities: payload + .certificate_authority_names + .as_deref(), + named_groups: payload.named_groups.as_deref(), + }; + + trace!("Accepted::client_hello(): {ch:#?}"); + ch + } + + /// Convert the [`Accepted`] into a [`ServerConnection`]. + /// + /// Takes the state returned from [`Acceptor::accept()`] as well as the [`ServerConfig`] and + /// [`sign::CertifiedKey`] that should be used for the session. Returns an error if + /// configuration-dependent validation of the received `ClientHello` message fails. + #[cfg(feature = "std")] + pub fn into_connection( + mut self, + config: Arc<ServerConfig>, + ) -> Result<ServerConnection, (Error, AcceptedAlert)> { + if let Err(err) = self + .connection + .set_max_fragment_size(config.max_fragment_size) + { + // We have a connection here, but it won't contain an alert since the error + // is with the fragment size configured in the `ServerConfig`. + return Err((err, AcceptedAlert::empty())); + } + + self.connection.enable_secret_extraction = config.enable_secret_extraction; + + let state = hs::ExpectClientHello::new(config, ServerExtensionsInput::default()); + let mut cx = hs::ServerContext::from(&mut self.connection); + + let ch = Self::client_hello_payload(&self.message); + let new = match state.with_certified_key(self.sig_schemes, ch, &self.message, &mut cx) { + Ok(new) => new, + Err(err) => return Err((err, AcceptedAlert::from(self.connection))), + }; + + self.connection.replace_state(new); + Ok(ServerConnection { + inner: self.connection, + }) + } + + fn client_hello_payload<'a>(message: &'a Message<'_>) -> &'a ClientHelloPayload { + match &message.payload { + crate::msgs::message::MessagePayload::Handshake { parsed, .. } => match &parsed.0 { + crate::msgs::handshake::HandshakePayload::ClientHello(ch) => ch, + _ => unreachable!(), + }, + _ => unreachable!(), + } + } +} + +impl Debug for Accepted { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("Accepted").finish() + } +} + +#[cfg(feature = "std")] +struct Accepting; + +#[cfg(feature = "std")] +impl State<ServerConnectionData> for Accepting { + fn handle<'m>( + self: Box<Self>, + _cx: &mut hs::ServerContext<'_>, + _m: Message<'m>, + ) -> Result<Box<dyn State<ServerConnectionData> + 'm>, Error> + where + Self: 'm, + { + Err(Error::General("unreachable state".into())) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +pub(super) enum EarlyDataState { + New, + Accepted { + received: ChunkVecBuffer, + left: usize, + }, + Rejected, +} + +impl Default for EarlyDataState { + fn default() -> Self { + Self::New + } +} + +impl EarlyDataState { + pub(super) fn reject(&mut self) { + *self = Self::Rejected; + } + + pub(super) fn accept(&mut self, max_size: usize) { + *self = Self::Accepted { + received: ChunkVecBuffer::new(Some(max_size)), + left: max_size, + }; + } + + #[cfg(feature = "std")] + fn was_accepted(&self) -> bool { + matches!(self, Self::Accepted { .. }) + } + + pub(super) fn was_rejected(&self) -> bool { + matches!(self, Self::Rejected) + } + + fn peek(&self) -> Option<&[u8]> { + match self { + Self::Accepted { received, .. } => received.peek(), + _ => None, + } + } + + fn pop(&mut self) -> Option<Vec<u8>> { + match self { + Self::Accepted { received, .. } => received.pop(), + _ => None, + } + } + + #[cfg(feature = "std")] + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + match self { + Self::Accepted { received, .. } => received.read(buf), + _ => Err(io::Error::from(io::ErrorKind::BrokenPipe)), + } + } + + #[cfg(read_buf)] + fn read_buf(&mut self, cursor: core::io::BorrowedCursor<'_>) -> io::Result<()> { + match self { + Self::Accepted { received, .. } => received.read_buf(cursor), + _ => Err(io::Error::from(io::ErrorKind::BrokenPipe)), + } + } + + pub(super) fn take_received_plaintext(&mut self, bytes: Payload<'_>) -> bool { + let available = bytes.bytes().len(); + let Self::Accepted { received, left } = self else { + return false; + }; + + if received.apply_limit(available) != available || available > *left { + return false; + } + + received.append(bytes.into_vec()); + *left -= available; + true + } +} + +impl Debug for EarlyDataState { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + Self::New => write!(f, "EarlyDataState::New"), + Self::Accepted { received, left } => write!( + f, + "EarlyDataState::Accepted {{ received: {}, left: {} }}", + received.len(), + left + ), + Self::Rejected => write!(f, "EarlyDataState::Rejected"), + } + } +} + +impl ConnectionCore<ServerConnectionData> { + pub(crate) fn for_server( + config: Arc<ServerConfig>, + extra_exts: ServerExtensionsInput<'static>, + ) -> Result<Self, Error> { + let mut common = CommonState::new(Side::Server); + common.set_max_fragment_size(config.max_fragment_size)?; + common.enable_secret_extraction = config.enable_secret_extraction; + common.fips = config.fips(); + Ok(Self::new( + Box::new(hs::ExpectClientHello::new(config, extra_exts)), + ServerConnectionData::default(), + common, + )) + } + + #[cfg(feature = "std")] + pub(crate) fn reject_early_data(&mut self) { + assert!( + self.common_state.is_handshaking(), + "cannot retroactively reject early data" + ); + self.data.early_data.reject(); + } + + #[cfg(feature = "std")] + pub(crate) fn get_sni_str(&self) -> Option<&str> { + self.data.get_sni_str() + } +} + +/// State associated with a server connection. +#[derive(Default, Debug)] +pub struct ServerConnectionData { + pub(super) sni: Option<DnsName<'static>>, + pub(super) received_resumption_data: Option<Vec<u8>>, + pub(super) resumption_data: Vec<u8>, + pub(super) early_data: EarlyDataState, +} + +impl ServerConnectionData { + #[cfg(feature = "std")] + pub(super) fn get_sni_str(&self) -> Option<&str> { + self.sni.as_ref().map(AsRef::as_ref) + } +} + +impl crate::conn::SideData for ServerConnectionData {} + +#[cfg(feature = "std")] +#[cfg(test)] +mod tests { + use std::format; + + use super::*; + + // these branches not reachable externally, unless something else goes wrong. + #[test] + fn test_read_in_new_state() { + assert_eq!( + format!("{:?}", EarlyDataState::default().read(&mut [0u8; 5])), + "Err(Kind(BrokenPipe))" + ); + } + + #[cfg(read_buf)] + #[test] + fn test_read_buf_in_new_state() { + use core::io::BorrowedBuf; + + let mut buf = [0u8; 5]; + let mut buf: BorrowedBuf<'_> = buf.as_mut_slice().into(); + assert_eq!( + format!("{:?}", EarlyDataState::default().read_buf(buf.unfilled())), + "Err(Kind(BrokenPipe))" + ); + } +} diff --git a/vendor/rustls/src/server/test.rs b/vendor/rustls/src/server/test.rs new file mode 100644 index 00000000..9254dbc6 --- /dev/null +++ b/vendor/rustls/src/server/test.rs @@ -0,0 +1,369 @@ +use std::prelude::v1::*; +use std::vec; + +use super::ServerConnectionData; +use crate::common_state::Context; +use crate::enums::{CipherSuite, SignatureScheme}; +use crate::msgs::base::PayloadU16; +use crate::msgs::enums::{Compression, NamedGroup}; +use crate::msgs::handshake::{ + ClientExtensions, ClientHelloPayload, HandshakeMessagePayload, HandshakePayload, KeyShareEntry, + Random, ServerNamePayload, SessionId, SupportedProtocolVersions, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::{CommonState, Error, PeerIncompatible, PeerMisbehaved, ProtocolVersion, Side}; + +#[test] +fn null_compression_required() { + assert_eq!( + test_process_client_hello(ClientHelloPayload { + compression_methods: vec![], + ..minimal_client_hello() + }), + Err(PeerIncompatible::NullCompressionRequired.into()), + ); +} + +#[test] +fn server_ignores_sni_with_ip_address() { + let mut ch = minimal_client_hello(); + ch.extensions.server_name = Some(ServerNamePayload::IpAddress); + std::println!("{:?}", ch.extensions); + assert_eq!(test_process_client_hello(ch), Ok(())); +} + +#[test] +fn server_rejects_sni_with_illegal_dns_name() { + let mut ch = minimal_client_hello(); + ch.extensions.server_name = Some(ServerNamePayload::Invalid); + std::println!("{:?}", ch.extensions); + assert_eq!( + test_process_client_hello(ch), + Err(PeerMisbehaved::ServerNameMustContainOneHostName.into()) + ); +} + +fn test_process_client_hello(hello: ClientHelloPayload) -> Result<(), Error> { + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload(HandshakePayload::ClientHello( + hello, + ))), + }; + super::hs::process_client_hello( + &m, + false, + &mut Context { + common: &mut CommonState::new(Side::Server), + data: &mut ServerConnectionData::default(), + sendable_plaintext: None, + }, + ) + .map(|_| ()) +} + +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use super::super::*; + use crate::common_state::KxState; + use crate::crypto::{ + ActiveKeyExchange, CryptoProvider, KeyExchangeAlgorithm, SupportedKxGroup, + }; + use crate::enums::CertificateType; + use crate::pki_types::pem::PemObject; + use crate::pki_types::{CertificateDer, PrivateKeyDer}; + use crate::server::{AlwaysResolvesServerRawPublicKeys, ServerConfig, ServerConnection}; + use crate::sign::CertifiedKey; + use crate::sync::Arc; + use crate::{CipherSuiteCommon, SupportedCipherSuite, Tls12CipherSuite, version}; + + #[cfg(feature = "tls12")] + #[test] + fn test_server_rejects_no_extended_master_secret_extension_when_require_ems_or_fips() { + let provider = super::provider::default_provider(); + let mut config = ServerConfig::builder_with_provider(provider.into()) + .with_protocol_versions(&[&version::TLS12]) + .unwrap() + .with_no_client_auth() + .with_single_cert(server_cert(), server_key()) + .unwrap(); + + if config.provider.fips() { + assert!(config.require_ems); + } else { + config.require_ems = true; + } + let mut conn = ServerConnection::new(config.into()).unwrap(); + + let mut ch = minimal_client_hello(); + ch.extensions + .extended_master_secret_request + .take(); + let ch = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientHello(ch), + )), + }; + conn.read_tls(&mut ch.into_wire_bytes().as_slice()) + .unwrap(); + + assert_eq!( + conn.process_new_packets(), + Err(Error::PeerIncompatible( + PeerIncompatible::ExtendedMasterSecretExtensionRequired + )) + ); + } + + #[cfg(feature = "tls12")] + #[test] + fn server_picks_ffdhe_group_when_clienthello_has_no_ffdhe_group_in_groups_ext() { + let config = ServerConfig::builder_with_provider(ffdhe_provider().into()) + .with_protocol_versions(&[&version::TLS12]) + .unwrap() + .with_no_client_auth() + .with_single_cert(server_cert(), server_key()) + .unwrap(); + + let mut ch = minimal_client_hello(); + ch.cipher_suites + .push(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256.suite()); + + server_chooses_ffdhe_group_for_client_hello( + ServerConnection::new(config.into()).unwrap(), + ch, + ); + } + + #[cfg(feature = "tls12")] + #[test] + fn server_picks_ffdhe_group_when_clienthello_has_no_groups_ext() { + let config = ServerConfig::builder_with_provider(ffdhe_provider().into()) + .with_protocol_versions(&[&version::TLS12]) + .unwrap() + .with_no_client_auth() + .with_single_cert(server_cert(), server_key()) + .unwrap(); + + let mut ch = minimal_client_hello(); + ch.cipher_suites + .push(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256.suite()); + ch.extensions.named_groups.take(); + + server_chooses_ffdhe_group_for_client_hello( + ServerConnection::new(config.into()).unwrap(), + ch, + ); + } + + #[cfg(feature = "tls12")] + #[test] + fn server_accepts_client_with_no_ecpoints_extension_and_only_ffdhe_cipher_suites() { + let config = ServerConfig::builder_with_provider(ffdhe_provider().into()) + .with_protocol_versions(&[&version::TLS12]) + .unwrap() + .with_no_client_auth() + .with_single_cert(server_cert(), server_key()) + .unwrap(); + + let mut ch = minimal_client_hello(); + ch.cipher_suites + .push(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256.suite()); + ch.extensions.ec_point_formats.take(); + + server_chooses_ffdhe_group_for_client_hello( + ServerConnection::new(config.into()).unwrap(), + ch, + ); + } + + fn server_chooses_ffdhe_group_for_client_hello( + mut conn: ServerConnection, + client_hello: ClientHelloPayload, + ) { + let ch = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientHello(client_hello), + )), + }; + conn.read_tls(&mut ch.into_wire_bytes().as_slice()) + .unwrap(); + conn.process_new_packets().unwrap(); + + let KxState::Start(skxg) = &conn.kx_state else { + panic!("unexpected kx_state"); + }; + assert_eq!(skxg.name(), FAKE_FFDHE_GROUP.name()); + } + + #[test] + fn test_server_requiring_rpk_client_rejects_x509_client() { + let mut ch = minimal_client_hello(); + ch.extensions.client_certificate_types = Some(vec![CertificateType::X509]); + let ch = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientHello(ch), + )), + }; + + let mut conn = ServerConnection::new(server_config_for_rpk().into()).unwrap(); + conn.read_tls(&mut ch.into_wire_bytes().as_slice()) + .unwrap(); + assert_eq!( + conn.process_new_packets().unwrap_err(), + PeerIncompatible::IncorrectCertificateTypeExtension.into(), + ); + } + + #[test] + fn test_rpk_only_server_rejects_x509_only_client() { + let mut ch = minimal_client_hello(); + ch.extensions.server_certificate_types = Some(vec![CertificateType::X509]); + let ch = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ClientHello(ch), + )), + }; + + let mut conn = ServerConnection::new(server_config_for_rpk().into()).unwrap(); + conn.read_tls(&mut ch.into_wire_bytes().as_slice()) + .unwrap(); + assert_eq!( + conn.process_new_packets().unwrap_err(), + PeerIncompatible::IncorrectCertificateTypeExtension.into(), + ); + } + + fn server_config_for_rpk() -> ServerConfig { + let x25519_provider = CryptoProvider { + kx_groups: vec![super::provider::kx_group::X25519], + ..super::provider::default_provider() + }; + ServerConfig::builder_with_provider(x25519_provider.into()) + .with_protocol_versions(&[&version::TLS13]) + .unwrap() + .with_no_client_auth() + .with_cert_resolver(Arc::new(AlwaysResolvesServerRawPublicKeys::new(Arc::new( + server_certified_key(), + )))) + } + + fn server_certified_key() -> CertifiedKey { + let key = super::provider::default_provider() + .key_provider + .load_private_key(server_key()) + .unwrap(); + let public_key_as_cert = vec![CertificateDer::from( + key.public_key() + .unwrap() + .as_ref() + .to_vec(), + )]; + CertifiedKey::new(public_key_as_cert, key) + } + + fn server_key() -> PrivateKeyDer<'static> { + PrivateKeyDer::from_pem_reader( + &mut include_bytes!("../../../test-ca/rsa-2048/end.key").as_slice(), + ) + .unwrap() + } + + fn server_cert() -> Vec<CertificateDer<'static>> { + vec![ + CertificateDer::from(&include_bytes!("../../../test-ca/rsa-2048/end.der")[..]), + CertificateDer::from(&include_bytes!("../../../test-ca/rsa-2048/inter.der")[..]), + ] + } + + fn ffdhe_provider() -> CryptoProvider { + CryptoProvider { + kx_groups: vec![FAKE_FFDHE_GROUP], + cipher_suites: vec![TLS_DHE_RSA_WITH_AES_128_GCM_SHA256], + ..super::provider::default_provider() + } + } + + static FAKE_FFDHE_GROUP: &'static dyn SupportedKxGroup = &FakeFfdheGroup; + + #[derive(Debug)] + struct FakeFfdheGroup; + + impl SupportedKxGroup for FakeFfdheGroup { + fn name(&self) -> NamedGroup { + NamedGroup::FFDHE2048 + } + + fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> { + Ok(Box::new(ActiveFakeFfdhe)) + } + } + + #[derive(Debug)] + struct ActiveFakeFfdhe; + + impl ActiveKeyExchange for ActiveFakeFfdhe { + #[cfg_attr(coverage_nightly, coverage(off))] + fn complete( + self: Box<Self>, + _peer_pub_key: &[u8], + ) -> Result<crate::crypto::SharedSecret, Error> { + todo!() + } + + fn pub_key(&self) -> &[u8] { + b"ActiveFakeFfdhe pub key" + } + + fn group(&self) -> NamedGroup { + NamedGroup::FFDHE2048 + } + } + + static TLS_DHE_RSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls12(&TLS12_DHE_RSA_WITH_AES_128_GCM_SHA256); + + static TLS12_DHE_RSA_WITH_AES_128_GCM_SHA256: Tls12CipherSuite = + match &super::provider::cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 { + SupportedCipherSuite::Tls12(provider) => Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + ..provider.common + }, + kx: KeyExchangeAlgorithm::DHE, + ..**provider + }, + _ => unreachable!(), + }; +} + +fn minimal_client_hello() -> ClientHelloPayload { + ClientHelloPayload { + client_version: ProtocolVersion::TLSv1_3, + random: Random::from([0u8; 32]), + session_id: SessionId::empty(), + cipher_suites: vec![ + CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + CipherSuite::TLS13_AES_128_GCM_SHA256, + ], + compression_methods: vec![Compression::Null], + extensions: Box::new(ClientExtensions { + signature_schemes: Some(vec![SignatureScheme::RSA_PSS_SHA256]), + named_groups: Some(vec![NamedGroup::X25519, NamedGroup::secp256r1]), + supported_versions: Some(SupportedProtocolVersions { + tls12: true, + tls13: true, + }), + key_shares: Some(vec![KeyShareEntry { + group: NamedGroup::X25519, + payload: PayloadU16::new(vec![0xab; 32]), + }]), + extended_master_secret_request: Some(()), + ..ClientExtensions::default() + }), + } +} diff --git a/vendor/rustls/src/server/tls12.rs b/vendor/rustls/src/server/tls12.rs new file mode 100644 index 00000000..d3dfa5c8 --- /dev/null +++ b/vendor/rustls/src/server/tls12.rs @@ -0,0 +1,1003 @@ +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; + +pub(super) use client_hello::CompleteClientHelloHandling; +use pki_types::UnixTime; +use subtle::ConstantTimeEq; + +use super::common::ActiveCertifiedKey; +use super::hs::{self, ServerContext}; +use super::server_conn::{ProducesTickets, ServerConfig, ServerConnectionData}; +use crate::check::inappropriate_message; +use crate::common_state::{CommonState, HandshakeFlightTls12, HandshakeKind, Side, State}; +use crate::conn::ConnectionRandoms; +use crate::conn::kernel::{Direction, KernelContext, KernelState}; +use crate::crypto::ActiveKeyExchange; +use crate::enums::{AlertDescription, ContentType, HandshakeType, ProtocolVersion}; +use crate::error::{Error, PeerIncompatible, PeerMisbehaved}; +use crate::hash_hs::HandshakeHash; +use crate::log::{debug, trace}; +use crate::msgs::base::Payload; +use crate::msgs::ccs::ChangeCipherSpecPayload; +use crate::msgs::codec::Codec; +use crate::msgs::handshake::{ + CertificateChain, ClientKeyExchangeParams, HandshakeMessagePayload, HandshakePayload, + NewSessionTicketPayload, NewSessionTicketPayloadTls13, SessionId, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist; +use crate::suites::PartiallyExtractedSecrets; +use crate::sync::Arc; +use crate::tls12::{self, ConnectionSecrets, Tls12CipherSuite}; +use crate::{ConnectionTrafficSecrets, verify}; + +mod client_hello { + use pki_types::CertificateDer; + + use super::*; + use crate::common_state::KxState; + use crate::crypto::SupportedKxGroup; + use crate::enums::SignatureScheme; + use crate::msgs::enums::{ClientCertificateType, Compression}; + use crate::msgs::handshake::{ + CertificateRequestPayload, CertificateStatus, ClientHelloPayload, ClientSessionTicket, + Random, ServerExtensionsInput, ServerHelloPayload, ServerKeyExchange, + ServerKeyExchangeParams, ServerKeyExchangePayload, + }; + use crate::sign; + use crate::verify::DigitallySignedStruct; + + pub(in crate::server) struct CompleteClientHelloHandling { + pub(in crate::server) config: Arc<ServerConfig>, + pub(in crate::server) transcript: HandshakeHash, + pub(in crate::server) session_id: SessionId, + pub(in crate::server) suite: &'static Tls12CipherSuite, + pub(in crate::server) using_ems: bool, + pub(in crate::server) randoms: ConnectionRandoms, + pub(in crate::server) send_ticket: bool, + pub(in crate::server) extra_exts: ServerExtensionsInput<'static>, + } + + impl CompleteClientHelloHandling { + pub(in crate::server) fn handle_client_hello( + mut self, + cx: &mut ServerContext<'_>, + server_key: ActiveCertifiedKey<'_>, + chm: &Message<'_>, + client_hello: &ClientHelloPayload, + selected_kxg: &'static dyn SupportedKxGroup, + sigschemes_ext: Vec<SignatureScheme>, + tls13_enabled: bool, + ) -> hs::NextStateOrError<'static> { + // -- TLS1.2 only from hereon in -- + self.transcript.add_message(chm); + + if client_hello + .extended_master_secret_request + .is_some() + { + self.using_ems = true; + } else if self.config.require_ems { + return Err(cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::ExtendedMasterSecretExtensionRequired, + )); + } + + // "RFC 4492 specified that if this extension is missing, + // it means that only the uncompressed point format is + // supported" + // - <https://datatracker.ietf.org/doc/html/rfc8422#section-5.1.2> + let supported_ec_point_formats = client_hello + .ec_point_formats + .unwrap_or_default(); + + trace!("ecpoints {supported_ec_point_formats:?}"); + + if !supported_ec_point_formats.uncompressed { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerIncompatible::UncompressedEcPointsRequired, + )); + } + + // -- If TLS1.3 is enabled, signal the downgrade in the server random + if tls13_enabled { + self.randoms.server[24..].copy_from_slice(&tls12::DOWNGRADE_SENTINEL); + } + + // -- Check for resumption -- + // We can do this either by (in order of preference): + // 1. receiving a ticket that decrypts + // 2. receiving a sessionid that is in our cache + // + // If we receive a ticket, the sessionid won't be in our + // cache, so don't check. + // + // If either works, we end up with a ServerConnectionValue + // which is passed to start_resumption and concludes + // our handling of the ClientHello. + // + let mut ticket_received = false; + let resume_data = client_hello + .session_ticket + .as_ref() + .and_then(|ticket_ext| match ticket_ext { + ClientSessionTicket::Offer(ticket) => Some(ticket), + _ => None, + }) + .and_then(|ticket| { + ticket_received = true; + debug!("Ticket received"); + let data = self + .config + .ticketer + .decrypt(ticket.bytes()); + if data.is_none() { + debug!("Ticket didn't decrypt"); + } + data + }) + .or_else(|| { + // Perhaps resume? If we received a ticket, the sessionid + // does not correspond to a real session. + if client_hello.session_id.is_empty() || ticket_received { + return None; + } + + self.config + .session_storage + .get(client_hello.session_id.as_ref()) + }) + .and_then(|x| persist::ServerSessionValue::read_bytes(&x).ok()) + .filter(|resumedata| { + hs::can_resume(self.suite.into(), &cx.data.sni, self.using_ems, resumedata) + }); + + if let Some(data) = resume_data { + return self.start_resumption(cx, client_hello, &client_hello.session_id, data); + } + + // Now we have chosen a ciphersuite, we can make kx decisions. + let sigschemes = self + .suite + .resolve_sig_schemes(&sigschemes_ext); + + if sigschemes.is_empty() { + return Err(cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::NoSignatureSchemesInCommon, + )); + } + + let mut ocsp_response = server_key.get_ocsp(); + + // If we're not offered a ticket or a potential session ID, allocate a session ID. + if !self.config.session_storage.can_cache() { + self.session_id = SessionId::empty(); + } else if self.session_id.is_empty() && !ticket_received { + self.session_id = SessionId::random(self.config.provider.secure_random)?; + } + + cx.common.kx_state = KxState::Start(selected_kxg); + cx.common.handshake_kind = Some(HandshakeKind::Full); + + let mut flight = HandshakeFlightTls12::new(&mut self.transcript); + + self.send_ticket = emit_server_hello( + &mut flight, + &self.config, + cx, + self.session_id, + self.suite, + self.using_ems, + &mut ocsp_response, + client_hello, + None, + &self.randoms, + self.extra_exts, + )?; + emit_certificate(&mut flight, server_key.get_cert()); + if let Some(ocsp_response) = ocsp_response { + emit_cert_status(&mut flight, ocsp_response); + } + let server_kx = emit_server_kx( + &mut flight, + sigschemes, + selected_kxg, + server_key.get_key(), + &self.randoms, + )?; + let doing_client_auth = emit_certificate_req(&mut flight, &self.config)?; + emit_server_hello_done(&mut flight); + + flight.finish(cx.common); + + if doing_client_auth { + Ok(Box::new(ExpectCertificate { + config: self.config, + transcript: self.transcript, + randoms: self.randoms, + session_id: self.session_id, + suite: self.suite, + using_ems: self.using_ems, + server_kx, + send_ticket: self.send_ticket, + })) + } else { + Ok(Box::new(ExpectClientKx { + config: self.config, + transcript: self.transcript, + randoms: self.randoms, + session_id: self.session_id, + suite: self.suite, + using_ems: self.using_ems, + server_kx, + client_cert: None, + send_ticket: self.send_ticket, + })) + } + } + + fn start_resumption( + mut self, + cx: &mut ServerContext<'_>, + client_hello: &ClientHelloPayload, + id: &SessionId, + resumedata: persist::ServerSessionValue, + ) -> hs::NextStateOrError<'static> { + debug!("Resuming connection"); + + if resumedata.extended_ms && !self.using_ems { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::ResumptionAttemptedWithVariedEms, + )); + } + + self.session_id = *id; + let mut flight = HandshakeFlightTls12::new(&mut self.transcript); + self.send_ticket = emit_server_hello( + &mut flight, + &self.config, + cx, + self.session_id, + self.suite, + self.using_ems, + &mut None, + client_hello, + Some(&resumedata), + &self.randoms, + self.extra_exts, + )?; + flight.finish(cx.common); + + let secrets = ConnectionSecrets::new_resume( + self.randoms, + self.suite, + &resumedata.master_secret.0, + ); + self.config.key_log.log( + "CLIENT_RANDOM", + &secrets.randoms.client, + &secrets.master_secret, + ); + cx.common + .start_encryption_tls12(&secrets, Side::Server); + cx.common.peer_certificates = resumedata.client_cert_chain; + cx.common.handshake_kind = Some(HandshakeKind::Resumed); + + if self.send_ticket { + let now = self.config.current_time()?; + + emit_ticket( + &secrets, + &mut self.transcript, + self.using_ems, + cx, + &*self.config.ticketer, + now, + )?; + } + emit_ccs(cx.common); + cx.common + .record_layer + .start_encrypting(); + emit_finished(&secrets, &mut self.transcript, cx.common); + + Ok(Box::new(ExpectCcs { + config: self.config, + secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + resuming: true, + send_ticket: self.send_ticket, + })) + } + } + + fn emit_server_hello( + flight: &mut HandshakeFlightTls12<'_>, + config: &ServerConfig, + cx: &mut ServerContext<'_>, + session_id: SessionId, + suite: &'static Tls12CipherSuite, + using_ems: bool, + ocsp_response: &mut Option<&[u8]>, + hello: &ClientHelloPayload, + resumedata: Option<&persist::ServerSessionValue>, + randoms: &ConnectionRandoms, + extra_exts: ServerExtensionsInput<'static>, + ) -> Result<bool, Error> { + let mut ep = hs::ExtensionProcessing::new(extra_exts); + ep.process_common(config, cx, ocsp_response, hello, resumedata)?; + ep.process_tls12(config, hello, using_ems); + + let sh = HandshakeMessagePayload(HandshakePayload::ServerHello(ServerHelloPayload { + legacy_version: ProtocolVersion::TLSv1_2, + random: Random::from(randoms.server), + session_id, + cipher_suite: suite.common.suite, + compression_method: Compression::Null, + extensions: ep.extensions, + })); + trace!("sending server hello {sh:?}"); + flight.add(sh); + + Ok(ep.send_ticket) + } + + fn emit_certificate( + flight: &mut HandshakeFlightTls12<'_>, + cert_chain: &[CertificateDer<'static>], + ) { + flight.add(HandshakeMessagePayload(HandshakePayload::Certificate( + CertificateChain(cert_chain.to_vec()), + ))); + } + + fn emit_cert_status(flight: &mut HandshakeFlightTls12<'_>, ocsp: &[u8]) { + flight.add(HandshakeMessagePayload( + HandshakePayload::CertificateStatus(CertificateStatus::new(ocsp)), + )); + } + + fn emit_server_kx( + flight: &mut HandshakeFlightTls12<'_>, + sigschemes: Vec<SignatureScheme>, + selected_group: &'static dyn SupportedKxGroup, + signing_key: &dyn sign::SigningKey, + randoms: &ConnectionRandoms, + ) -> Result<Box<dyn ActiveKeyExchange>, Error> { + let kx = selected_group.start()?; + let kx_params = ServerKeyExchangeParams::new(&*kx); + + let mut msg = Vec::new(); + msg.extend(randoms.client); + msg.extend(randoms.server); + kx_params.encode(&mut msg); + + let signer = signing_key + .choose_scheme(&sigschemes) + .ok_or_else(|| Error::General("incompatible signing key".to_string()))?; + let sigscheme = signer.scheme(); + let sig = signer.sign(&msg)?; + + let skx = ServerKeyExchangePayload::from(ServerKeyExchange { + params: kx_params, + dss: DigitallySignedStruct::new(sigscheme, sig), + }); + + flight.add(HandshakeMessagePayload( + HandshakePayload::ServerKeyExchange(skx), + )); + Ok(kx) + } + + fn emit_certificate_req( + flight: &mut HandshakeFlightTls12<'_>, + config: &ServerConfig, + ) -> Result<bool, Error> { + let client_auth = &config.verifier; + + if !client_auth.offer_client_auth() { + return Ok(false); + } + + let verify_schemes = client_auth.supported_verify_schemes(); + + let names = config + .verifier + .root_hint_subjects() + .to_vec(); + + let cr = CertificateRequestPayload { + certtypes: vec![ + ClientCertificateType::RSASign, + ClientCertificateType::ECDSASign, + ], + sigschemes: verify_schemes, + canames: names, + }; + + let creq = HandshakeMessagePayload(HandshakePayload::CertificateRequest(cr)); + + trace!("Sending CertificateRequest {creq:?}"); + flight.add(creq); + Ok(true) + } + + fn emit_server_hello_done(flight: &mut HandshakeFlightTls12<'_>) { + flight.add(HandshakeMessagePayload(HandshakePayload::ServerHelloDone)); + } +} + +// --- Process client's Certificate for client auth --- +struct ExpectCertificate { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + randoms: ConnectionRandoms, + session_id: SessionId, + suite: &'static Tls12CipherSuite, + using_ems: bool, + server_kx: Box<dyn ActiveKeyExchange>, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectCertificate { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + self.transcript.add_message(&m); + let cert_chain = require_handshake_msg_move!( + m, + HandshakeType::Certificate, + HandshakePayload::Certificate + )?; + + // If we can't determine if the auth is mandatory, abort + let mandatory = self + .config + .verifier + .client_auth_mandatory(); + + trace!("certs {cert_chain:?}"); + + let client_cert = match cert_chain.split_first() { + None if mandatory => { + return Err(cx.common.send_fatal_alert( + AlertDescription::CertificateRequired, + Error::NoCertificatesPresented, + )); + } + None => { + debug!("client auth requested but no certificate supplied"); + self.transcript.abandon_client_auth(); + None + } + Some((end_entity, intermediates)) => { + let now = self.config.current_time()?; + + self.config + .verifier + .verify_client_cert(end_entity, intermediates, now) + .map_err(|err| { + cx.common + .send_cert_verify_error_alert(err) + })?; + + Some(cert_chain) + } + }; + + Ok(Box::new(ExpectClientKx { + config: self.config, + transcript: self.transcript, + randoms: self.randoms, + session_id: self.session_id, + suite: self.suite, + using_ems: self.using_ems, + server_kx: self.server_kx, + client_cert, + send_ticket: self.send_ticket, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process client's KeyExchange --- +struct ExpectClientKx<'a> { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + randoms: ConnectionRandoms, + session_id: SessionId, + suite: &'static Tls12CipherSuite, + using_ems: bool, + server_kx: Box<dyn ActiveKeyExchange>, + client_cert: Option<CertificateChain<'a>>, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectClientKx<'_> { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let client_kx = require_handshake_msg!( + m, + HandshakeType::ClientKeyExchange, + HandshakePayload::ClientKeyExchange + )?; + self.transcript.add_message(&m); + let ems_seed = self + .using_ems + .then(|| self.transcript.current_hash()); + + // Complete key agreement, and set up encryption with the + // resulting premaster secret. + let peer_kx_params = tls12::decode_kx_params::<ClientKeyExchangeParams>( + self.suite.kx, + cx.common, + client_kx.bytes(), + )?; + let secrets = ConnectionSecrets::from_key_exchange( + self.server_kx, + peer_kx_params.pub_key(), + ems_seed, + self.randoms, + self.suite, + ) + .map_err(|err| { + cx.common + .send_fatal_alert(AlertDescription::IllegalParameter, err) + })?; + cx.common.kx_state.complete(); + + self.config.key_log.log( + "CLIENT_RANDOM", + &secrets.randoms.client, + &secrets.master_secret, + ); + cx.common + .start_encryption_tls12(&secrets, Side::Server); + + match self.client_cert { + Some(client_cert) => Ok(Box::new(ExpectCertificateVerify { + config: self.config, + secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + client_cert, + send_ticket: self.send_ticket, + })), + _ => Ok(Box::new(ExpectCcs { + config: self.config, + secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + resuming: false, + send_ticket: self.send_ticket, + })), + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectClientKx { + config: self.config, + transcript: self.transcript, + randoms: self.randoms, + session_id: self.session_id, + suite: self.suite, + using_ems: self.using_ems, + server_kx: self.server_kx, + client_cert: self + .client_cert + .map(|cert| cert.into_owned()), + send_ticket: self.send_ticket, + }) + } +} + +// --- Process client's certificate proof --- +struct ExpectCertificateVerify<'a> { + config: Arc<ServerConfig>, + secrets: ConnectionSecrets, + transcript: HandshakeHash, + session_id: SessionId, + using_ems: bool, + client_cert: CertificateChain<'a>, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectCertificateVerify<'_> { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let rc = { + let sig = require_handshake_msg!( + m, + HandshakeType::CertificateVerify, + HandshakePayload::CertificateVerify + )?; + + match self.transcript.take_handshake_buf() { + Some(msgs) => { + let certs = &self.client_cert; + self.config + .verifier + .verify_tls12_signature(&msgs, &certs[0], sig) + } + None => { + // This should be unreachable; the handshake buffer was initialized with + // client authentication if the verifier wants to offer it. + // `transcript.abandon_client_auth()` can extract it, but its only caller in + // this flow will also set `ExpectClientKx::client_cert` to `None`, making it + // impossible to reach this state. + return Err(cx.common.send_fatal_alert( + AlertDescription::AccessDenied, + Error::General("client authentication not set up".into()), + )); + } + } + }; + + if let Err(e) = rc { + return Err(cx + .common + .send_cert_verify_error_alert(e)); + } + + trace!("client CertificateVerify OK"); + cx.common.peer_certificates = Some(self.client_cert.into_owned()); + + self.transcript.add_message(&m); + Ok(Box::new(ExpectCcs { + config: self.config, + secrets: self.secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + resuming: false, + send_ticket: self.send_ticket, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + Box::new(ExpectCertificateVerify { + config: self.config, + secrets: self.secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + client_cert: self.client_cert.into_owned(), + send_ticket: self.send_ticket, + }) + } +} + +// --- Process client's ChangeCipherSpec --- +struct ExpectCcs { + config: Arc<ServerConfig>, + secrets: ConnectionSecrets, + transcript: HandshakeHash, + session_id: SessionId, + using_ems: bool, + resuming: bool, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectCcs { + fn handle<'m>( + self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ChangeCipherSpec(..) => {} + payload => { + return Err(inappropriate_message( + &payload, + &[ContentType::ChangeCipherSpec], + )); + } + } + + // CCS should not be received interleaved with fragmented handshake-level + // message. + cx.common.check_aligned_handshake()?; + + cx.common + .record_layer + .start_decrypting(); + Ok(Box::new(ExpectFinished { + config: self.config, + secrets: self.secrets, + transcript: self.transcript, + session_id: self.session_id, + using_ems: self.using_ems, + resuming: self.resuming, + send_ticket: self.send_ticket, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process client's Finished --- +fn get_server_connection_value_tls12( + secrets: &ConnectionSecrets, + using_ems: bool, + cx: &ServerContext<'_>, + time_now: UnixTime, +) -> persist::ServerSessionValue { + let version = ProtocolVersion::TLSv1_2; + + let mut v = persist::ServerSessionValue::new( + cx.data.sni.as_ref(), + version, + secrets.suite().common.suite, + secrets.master_secret(), + cx.common.peer_certificates.clone(), + cx.common.alpn_protocol.clone(), + cx.data.resumption_data.clone(), + time_now, + 0, + ); + + if using_ems { + v.set_extended_ms_used(); + } + + v +} + +fn emit_ticket( + secrets: &ConnectionSecrets, + transcript: &mut HandshakeHash, + using_ems: bool, + cx: &mut ServerContext<'_>, + ticketer: &dyn ProducesTickets, + now: UnixTime, +) -> Result<(), Error> { + let plain = get_server_connection_value_tls12(secrets, using_ems, cx, now).get_encoding(); + + // If we can't produce a ticket for some reason, we can't + // report an error. Send an empty one. + let ticket = ticketer + .encrypt(&plain) + .unwrap_or_default(); + let ticket_lifetime = ticketer.lifetime(); + + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::NewSessionTicket(NewSessionTicketPayload::new( + ticket_lifetime, + ticket, + )), + )), + }; + + transcript.add_message(&m); + cx.common.send_msg(m, false); + Ok(()) +} + +fn emit_ccs(common: &mut CommonState) { + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}), + }; + + common.send_msg(m, false); +} + +fn emit_finished( + secrets: &ConnectionSecrets, + transcript: &mut HandshakeHash, + common: &mut CommonState, +) { + let vh = transcript.current_hash(); + let verify_data = secrets.server_verify_data(&vh); + let verify_data_payload = Payload::new(verify_data); + + let f = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload(HandshakePayload::Finished( + verify_data_payload, + ))), + }; + + transcript.add_message(&f); + common.send_msg(f, true); +} + +struct ExpectFinished { + config: Arc<ServerConfig>, + secrets: ConnectionSecrets, + transcript: HandshakeHash, + session_id: SessionId, + using_ems: bool, + resuming: bool, + send_ticket: bool, +} + +impl State<ServerConnectionData> for ExpectFinished { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let finished = + require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?; + + cx.common.check_aligned_handshake()?; + + let vh = self.transcript.current_hash(); + let expect_verify_data = self.secrets.client_verify_data(&vh); + + let _fin_verified = + match ConstantTimeEq::ct_eq(&expect_verify_data[..], finished.bytes()).into() { + true => verify::FinishedMessageVerified::assertion(), + false => { + return Err(cx + .common + .send_fatal_alert(AlertDescription::DecryptError, Error::DecryptError)); + } + }; + + // Save connection, perhaps + if !self.resuming && !self.session_id.is_empty() { + let now = self.config.current_time()?; + + let value = get_server_connection_value_tls12(&self.secrets, self.using_ems, cx, now); + + let worked = self + .config + .session_storage + .put(self.session_id.as_ref().to_vec(), value.get_encoding()); + #[cfg_attr(not(feature = "logging"), allow(clippy::if_same_then_else))] + if worked { + debug!("Session saved"); + } else { + debug!("Session not saved"); + } + } + + // Send our CCS and Finished. + self.transcript.add_message(&m); + if !self.resuming { + if self.send_ticket { + let now = self.config.current_time()?; + emit_ticket( + &self.secrets, + &mut self.transcript, + self.using_ems, + cx, + &*self.config.ticketer, + now, + )?; + } + emit_ccs(cx.common); + cx.common + .record_layer + .start_encrypting(); + emit_finished(&self.secrets, &mut self.transcript, cx.common); + } + + cx.common + .start_traffic(&mut cx.sendable_plaintext); + Ok(Box::new(ExpectTraffic { + secrets: self.secrets, + _fin_verified, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process traffic --- +struct ExpectTraffic { + secrets: ConnectionSecrets, + _fin_verified: verify::FinishedMessageVerified, +} + +impl ExpectTraffic {} + +impl State<ServerConnectionData> for ExpectTraffic { + fn handle<'m>( + self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ApplicationData(payload) => cx + .common + .take_received_plaintext(payload), + payload => { + return Err(inappropriate_message( + &payload, + &[ContentType::ApplicationData], + )); + } + } + Ok(self) + } + + fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) -> Result<(), Error> { + self.secrets + .export_keying_material(output, label, context); + Ok(()) + } + + fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> { + self.secrets + .extract_secrets(Side::Server) + } + + fn into_external_state(self: Box<Self>) -> Result<Box<dyn KernelState + 'static>, Error> { + Ok(self) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +impl KernelState for ExpectTraffic { + fn update_secrets(&mut self, _: Direction) -> Result<ConnectionTrafficSecrets, Error> { + Err(Error::General( + "TLS 1.2 connections do not support traffic secret updates".into(), + )) + } + + fn handle_new_session_ticket( + &mut self, + _cx: &mut KernelContext<'_>, + _message: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + unreachable!( + "server connections should never have handle_new_session_ticket called on them" + ) + } +} diff --git a/vendor/rustls/src/server/tls13.rs b/vendor/rustls/src/server/tls13.rs new file mode 100644 index 00000000..b8b70e72 --- /dev/null +++ b/vendor/rustls/src/server/tls13.rs @@ -0,0 +1,1535 @@ +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; + +pub(super) use client_hello::CompleteClientHelloHandling; +use pki_types::{CertificateDer, UnixTime}; +use subtle::ConstantTimeEq; + +use super::hs::{self, HandshakeHashOrBuffer, ServerContext}; +use super::server_conn::ServerConnectionData; +use crate::check::{inappropriate_handshake_message, inappropriate_message}; +use crate::common_state::{ + CommonState, HandshakeFlightTls13, HandshakeKind, Protocol, Side, State, +}; +use crate::conn::ConnectionRandoms; +use crate::conn::kernel::{Direction, KernelContext, KernelState}; +use crate::enums::{AlertDescription, ContentType, HandshakeType, ProtocolVersion}; +use crate::error::{Error, InvalidMessage, PeerIncompatible, PeerMisbehaved}; +use crate::hash_hs::HandshakeHash; +use crate::log::{debug, trace, warn}; +use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::enums::KeyUpdateRequest; +use crate::msgs::handshake::{ + CERTIFICATE_MAX_SIZE_LIMIT, CertificateChain, CertificatePayloadTls13, HandshakeMessagePayload, + HandshakePayload, NewSessionTicketPayloadTls13, +}; +use crate::msgs::message::{Message, MessagePayload}; +use crate::msgs::persist; +use crate::server::ServerConfig; +use crate::suites::PartiallyExtractedSecrets; +use crate::sync::Arc; +use crate::tls13::key_schedule::{ + KeyScheduleResumption, KeyScheduleTraffic, KeyScheduleTrafficWithClientFinishedPending, +}; +use crate::tls13::{ + Tls13CipherSuite, construct_client_verify_message, construct_server_verify_message, +}; +use crate::{ConnectionTrafficSecrets, compress, rand, verify}; + +mod client_hello { + use super::*; + use crate::compress::CertCompressor; + use crate::crypto::SupportedKxGroup; + use crate::enums::SignatureScheme; + use crate::msgs::base::{Payload, PayloadU8}; + use crate::msgs::ccs::ChangeCipherSpecPayload; + use crate::msgs::enums::{Compression, NamedGroup}; + use crate::msgs::handshake::{ + CertificatePayloadTls13, CertificateRequestExtensions, CertificateRequestPayloadTls13, + ClientHelloPayload, HelloRetryRequest, HelloRetryRequestExtensions, KeyShareEntry, Random, + ServerExtensions, ServerExtensionsInput, ServerHelloPayload, SessionId, + }; + use crate::server::common::ActiveCertifiedKey; + use crate::sign; + use crate::tls13::key_schedule::{ + KeyScheduleEarly, KeyScheduleHandshake, KeySchedulePreHandshake, + }; + use crate::verify::DigitallySignedStruct; + + #[derive(PartialEq)] + pub(super) enum EarlyDataDecision { + Disabled, + RequestedButRejected, + Accepted, + } + + pub(in crate::server) struct CompleteClientHelloHandling { + pub(in crate::server) config: Arc<ServerConfig>, + pub(in crate::server) transcript: HandshakeHash, + pub(in crate::server) suite: &'static Tls13CipherSuite, + pub(in crate::server) randoms: ConnectionRandoms, + pub(in crate::server) done_retry: bool, + pub(in crate::server) send_tickets: usize, + pub(in crate::server) extra_exts: ServerExtensionsInput<'static>, + } + + fn max_early_data_size(configured: u32) -> usize { + if configured != 0 { + configured as usize + } else { + // The relevant max_early_data_size may in fact be unknowable: if + // we (the server) have turned off early_data but the client has + // a stale ticket from when we allowed early_data: we'll naturally + // reject early_data but need an upper bound on the amount of data + // to drop. + // + // Use a single maximum-sized message. + 16384 + } + } + + impl CompleteClientHelloHandling { + fn check_binder( + &self, + suite: &'static Tls13CipherSuite, + client_hello: &Message<'_>, + psk: &[u8], + binder: &[u8], + ) -> bool { + let binder_plaintext = match &client_hello.payload { + MessagePayload::Handshake { parsed, encoded } => { + &encoded.bytes()[..encoded.bytes().len() - parsed.total_binder_length()] + } + _ => unreachable!(), + }; + + let handshake_hash = self + .transcript + .hash_given(binder_plaintext); + + let key_schedule = KeyScheduleEarly::new(suite, psk); + let real_binder = + key_schedule.resumption_psk_binder_key_and_sign_verify_data(&handshake_hash); + + ConstantTimeEq::ct_eq(real_binder.as_ref(), binder).into() + } + + fn attempt_tls13_ticket_decryption( + &mut self, + ticket: &[u8], + ) -> Option<persist::ServerSessionValue> { + if self.config.ticketer.enabled() { + self.config + .ticketer + .decrypt(ticket) + .and_then(|plain| persist::ServerSessionValue::read_bytes(&plain).ok()) + } else { + self.config + .session_storage + .take(ticket) + .and_then(|plain| persist::ServerSessionValue::read_bytes(&plain).ok()) + } + } + + pub(in crate::server) fn handle_client_hello( + mut self, + cx: &mut ServerContext<'_>, + server_key: ActiveCertifiedKey<'_>, + chm: &Message<'_>, + client_hello: &ClientHelloPayload, + selected_kxg: &'static dyn SupportedKxGroup, + mut sigschemes_ext: Vec<SignatureScheme>, + ) -> hs::NextStateOrError<'static> { + if client_hello.compression_methods.len() != 1 { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::OfferedIncorrectCompressions, + )); + } + + sigschemes_ext.retain(SignatureScheme::supported_in_tls13); + + let shares_ext = client_hello + .key_shares + .as_ref() + .ok_or_else(|| { + cx.common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::KeyShareExtensionRequired, + ) + })?; + + if client_hello.has_keyshare_extension_with_duplicates() { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::OfferedDuplicateKeyShares, + )); + } + + if client_hello.has_certificate_compression_extension_with_duplicates() { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::OfferedDuplicateCertificateCompressions, + )); + } + + let cert_compressor = client_hello + .certificate_compression_algorithms + .as_ref() + .and_then(|offered| + // prefer server order when choosing a compression: the client's + // extension here does not denote any preference. + self.config + .cert_compressors + .iter() + .find(|compressor| offered.contains(&compressor.algorithm())) + .cloned()); + + let early_data_requested = client_hello + .early_data_request + .is_some(); + + // EarlyData extension is illegal in second ClientHello + if self.done_retry && early_data_requested { + return Err({ + cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::EarlyDataAttemptedInSecondClientHello, + ) + }); + } + + // See if there is a KeyShare for the selected kx group. + let chosen_share_and_kxg = shares_ext.iter().find_map(|share| { + (share.group == selected_kxg.name()).then_some((share, selected_kxg)) + }); + + let Some(chosen_share_and_kxg) = chosen_share_and_kxg else { + // We don't have a suitable key share. Send a HelloRetryRequest + // for the mutually_preferred_group. + self.transcript.add_message(chm); + + if self.done_retry { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::RefusedToFollowHelloRetryRequest, + )); + } + + emit_hello_retry_request( + &mut self.transcript, + self.suite, + client_hello.session_id, + cx.common, + selected_kxg.name(), + ); + emit_fake_ccs(cx.common); + + let skip_early_data = max_early_data_size(self.config.max_early_data_size); + + let next = Box::new(hs::ExpectClientHello { + config: self.config, + transcript: HandshakeHashOrBuffer::Hash(self.transcript), + #[cfg(feature = "tls12")] + session_id: SessionId::empty(), + #[cfg(feature = "tls12")] + using_ems: false, + done_retry: true, + send_tickets: self.send_tickets, + extra_exts: self.extra_exts, + }); + + return if early_data_requested { + Ok(Box::new(ExpectAndSkipRejectedEarlyData { + skip_data_left: skip_early_data, + next, + })) + } else { + Ok(next) + }; + }; + + let mut chosen_psk_index = None; + let mut resumedata = None; + + if let Some(psk_offer) = &client_hello.preshared_key_offer { + // "A client MUST provide a "psk_key_exchange_modes" extension if it + // offers a "pre_shared_key" extension. If clients offer + // "pre_shared_key" without a "psk_key_exchange_modes" extension, + // servers MUST abort the handshake." - RFC8446 4.2.9 + if client_hello + .preshared_key_modes + .is_none() + { + return Err(cx.common.send_fatal_alert( + AlertDescription::MissingExtension, + PeerMisbehaved::MissingPskModesExtension, + )); + } + + if psk_offer.binders.is_empty() { + return Err(cx.common.send_fatal_alert( + AlertDescription::DecodeError, + PeerMisbehaved::MissingBinderInPskExtension, + )); + } + + if psk_offer.binders.len() != psk_offer.identities.len() { + return Err(cx.common.send_fatal_alert( + AlertDescription::IllegalParameter, + PeerMisbehaved::PskExtensionWithMismatchedIdsAndBinders, + )); + } + + let now = self.config.current_time()?; + + for (i, psk_id) in psk_offer.identities.iter().enumerate() { + let maybe_resume_data = self + .attempt_tls13_ticket_decryption(&psk_id.identity.0) + .map(|resumedata| { + resumedata.set_freshness(psk_id.obfuscated_ticket_age, now) + }) + .filter(|resumedata| { + hs::can_resume(self.suite.into(), &cx.data.sni, false, resumedata) + }); + + let Some(resume) = maybe_resume_data else { + continue; + }; + + if !self.check_binder( + self.suite, + chm, + &resume.master_secret.0, + psk_offer.binders[i].as_ref(), + ) { + return Err(cx.common.send_fatal_alert( + AlertDescription::DecryptError, + PeerMisbehaved::IncorrectBinder, + )); + } + + chosen_psk_index = Some(i); + resumedata = Some(resume); + break; + } + } + + if !client_hello + .preshared_key_modes + .as_ref() + .map(|offer| offer.psk_dhe) + .unwrap_or_default() + { + debug!("Client unwilling to resume, PSK_DHE_KE not offered"); + self.send_tickets = 0; + chosen_psk_index = None; + resumedata = None; + } else { + self.send_tickets = self.config.send_tls13_tickets; + } + + if let Some(resume) = &resumedata { + cx.data.received_resumption_data = Some(resume.application_data.0.clone()); + cx.common + .peer_certificates + .clone_from(&resume.client_cert_chain); + } + + let full_handshake = resumedata.is_none(); + self.transcript.add_message(chm); + let key_schedule = emit_server_hello( + &mut self.transcript, + &self.randoms, + self.suite, + cx, + &client_hello.session_id, + chosen_share_and_kxg, + chosen_psk_index, + resumedata + .as_ref() + .map(|x| &x.master_secret.0[..]), + &self.config, + )?; + if !self.done_retry { + emit_fake_ccs(cx.common); + } + + if full_handshake { + cx.common + .handshake_kind + .get_or_insert(HandshakeKind::Full); + } else { + cx.common.handshake_kind = Some(HandshakeKind::Resumed); + } + + let mut ocsp_response = server_key.get_ocsp(); + let mut flight = HandshakeFlightTls13::new(&mut self.transcript); + let doing_early_data = emit_encrypted_extensions( + &mut flight, + self.suite, + cx, + &mut ocsp_response, + client_hello, + resumedata.as_ref(), + self.extra_exts, + &self.config, + )?; + + let doing_client_auth = if full_handshake { + let client_auth = emit_certificate_req_tls13(&mut flight, &self.config)?; + + if let Some(compressor) = cert_compressor { + emit_compressed_certificate_tls13( + &mut flight, + &self.config, + server_key.get_cert(), + ocsp_response, + compressor, + ); + } else { + emit_certificate_tls13(&mut flight, server_key.get_cert(), ocsp_response); + } + emit_certificate_verify_tls13( + &mut flight, + cx.common, + server_key.get_key(), + &sigschemes_ext, + )?; + client_auth + } else { + false + }; + + // If we're not doing early data, then the next messages we receive + // are encrypted with the handshake keys. + match doing_early_data { + EarlyDataDecision::Disabled => { + key_schedule.set_handshake_decrypter(None, cx.common); + cx.data.early_data.reject(); + } + EarlyDataDecision::RequestedButRejected => { + debug!( + "Client requested early_data, but not accepted: switching to handshake keys with trial decryption" + ); + key_schedule.set_handshake_decrypter( + Some(max_early_data_size(self.config.max_early_data_size)), + cx.common, + ); + cx.data.early_data.reject(); + } + EarlyDataDecision::Accepted => { + cx.data + .early_data + .accept(self.config.max_early_data_size as usize); + } + } + + cx.common.check_aligned_handshake()?; + let key_schedule_traffic = + emit_finished_tls13(flight, &self.randoms, cx, key_schedule, &self.config); + + if !doing_client_auth && self.config.send_half_rtt_data { + // Application data can be sent immediately after Finished, in one + // flight. However, if client auth is enabled, we don't want to send + // application data to an unauthenticated peer. + cx.common + .start_outgoing_traffic(&mut cx.sendable_plaintext); + } + + if doing_client_auth { + if self + .config + .cert_decompressors + .is_empty() + { + Ok(Box::new(ExpectCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: key_schedule_traffic, + send_tickets: self.send_tickets, + message_already_in_transcript: false, + })) + } else { + Ok(Box::new(ExpectCertificateOrCompressedCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: key_schedule_traffic, + send_tickets: self.send_tickets, + })) + } + } else if doing_early_data == EarlyDataDecision::Accepted && !cx.common.is_quic() { + // Not used for QUIC: RFC 9001 §8.3: Clients MUST NOT send the EndOfEarlyData + // message. A server MUST treat receipt of a CRYPTO frame in a 0-RTT packet as a + // connection error of type PROTOCOL_VIOLATION. + Ok(Box::new(ExpectEarlyData { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: key_schedule_traffic, + send_tickets: self.send_tickets, + })) + } else { + Ok(Box::new(ExpectFinished { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: key_schedule_traffic, + send_tickets: self.send_tickets, + })) + } + } + } + + fn emit_server_hello( + transcript: &mut HandshakeHash, + randoms: &ConnectionRandoms, + suite: &'static Tls13CipherSuite, + cx: &mut ServerContext<'_>, + session_id: &SessionId, + share_and_kxgroup: (&KeyShareEntry, &'static dyn SupportedKxGroup), + chosen_psk_idx: Option<usize>, + resuming_psk: Option<&[u8]>, + config: &ServerConfig, + ) -> Result<KeyScheduleHandshake, Error> { + // Prepare key exchange; the caller already found the matching SupportedKxGroup + let (share, kxgroup) = share_and_kxgroup; + debug_assert_eq!(kxgroup.name(), share.group); + let ckx = kxgroup + .start_and_complete(&share.payload.0) + .map_err(|err| { + cx.common + .send_fatal_alert(AlertDescription::IllegalParameter, err) + })?; + cx.common.kx_state.complete(); + + let extensions = Box::new(ServerExtensions { + key_share: Some(KeyShareEntry::new(ckx.group, ckx.pub_key)), + selected_version: Some(ProtocolVersion::TLSv1_3), + preshared_key: chosen_psk_idx.map(|idx| idx as u16), + ..Default::default() + }); + + let sh = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::ServerHello(ServerHelloPayload { + legacy_version: ProtocolVersion::TLSv1_2, + random: Random::from(randoms.server), + session_id: *session_id, + cipher_suite: suite.common.suite, + compression_method: Compression::Null, + extensions, + }), + )), + }; + + cx.common.check_aligned_handshake()?; + + let client_hello_hash = transcript.hash_given(&[]); + + trace!("sending server hello {sh:?}"); + transcript.add_message(&sh); + cx.common.send_msg(sh, false); + + // Start key schedule + let key_schedule_pre_handshake = if let Some(psk) = resuming_psk { + let early_key_schedule = KeyScheduleEarly::new(suite, psk); + early_key_schedule.client_early_traffic_secret( + &client_hello_hash, + &*config.key_log, + &randoms.client, + cx.common, + ); + + KeySchedulePreHandshake::from(early_key_schedule) + } else { + KeySchedulePreHandshake::new(suite) + }; + + // Do key exchange + let key_schedule = key_schedule_pre_handshake.into_handshake(ckx.secret); + + let handshake_hash = transcript.current_hash(); + let key_schedule = key_schedule.derive_server_handshake_secrets( + handshake_hash, + &*config.key_log, + &randoms.client, + cx.common, + ); + + Ok(key_schedule) + } + + fn emit_fake_ccs(common: &mut CommonState) { + if common.is_quic() { + return; + } + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::ChangeCipherSpec(ChangeCipherSpecPayload {}), + }; + common.send_msg(m, false); + } + + fn emit_hello_retry_request( + transcript: &mut HandshakeHash, + suite: &'static Tls13CipherSuite, + session_id: SessionId, + common: &mut CommonState, + group: NamedGroup, + ) { + let req = HelloRetryRequest { + legacy_version: ProtocolVersion::TLSv1_2, + session_id, + cipher_suite: suite.common.suite, + extensions: HelloRetryRequestExtensions { + key_share: Some(group), + supported_versions: Some(ProtocolVersion::TLSv1_3), + ..Default::default() + }, + }; + + let m = Message { + version: ProtocolVersion::TLSv1_2, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::HelloRetryRequest(req), + )), + }; + + trace!("Requesting retry {m:?}"); + transcript.rollup_for_hrr(); + transcript.add_message(&m); + common.send_msg(m, false); + common.handshake_kind = Some(HandshakeKind::FullWithHelloRetryRequest); + } + + fn decide_if_early_data_allowed( + cx: &mut ServerContext<'_>, + client_hello: &ClientHelloPayload, + resumedata: Option<&persist::ServerSessionValue>, + suite: &'static Tls13CipherSuite, + config: &ServerConfig, + ) -> EarlyDataDecision { + let early_data_requested = client_hello + .early_data_request + .is_some(); + let rejected_or_disabled = match early_data_requested { + true => EarlyDataDecision::RequestedButRejected, + false => EarlyDataDecision::Disabled, + }; + + let Some(resume) = resumedata else { + // never any early data if not resuming. + return rejected_or_disabled; + }; + + /* Non-zero max_early_data_size controls whether early_data is allowed at all. + * We also require stateful resumption. */ + let early_data_configured = config.max_early_data_size > 0 && !config.ticketer.enabled(); + + /* "For PSKs provisioned via NewSessionTicket, a server MUST validate + * that the ticket age for the selected PSK identity (computed by + * subtracting ticket_age_add from PskIdentity.obfuscated_ticket_age + * modulo 2^32) is within a small tolerance of the time since the ticket + * was issued (see Section 8)." -- this is implemented in ServerSessionValue::set_freshness() + * and related. + * + * "In order to accept early data, the server [...] MUST verify that the + * following values are the same as those associated with the + * selected PSK: + * + * - The TLS version number + * - The selected cipher suite + * - The selected ALPN [RFC7301] protocol, if any" + * + * (RFC8446, 4.2.10) */ + let early_data_possible = early_data_requested + && resume.is_fresh() + && Some(resume.version) == cx.common.negotiated_version + && resume.cipher_suite == suite.common.suite + && resume.alpn.as_ref().map(|p| &p.0[..]) == cx.common.alpn_protocol.as_deref(); + + if early_data_configured && early_data_possible && !cx.data.early_data.was_rejected() { + EarlyDataDecision::Accepted + } else { + if cx.common.is_quic() { + // Clobber value set in tls13::emit_server_hello + cx.common.quic.early_secret = None; + } + + rejected_or_disabled + } + } + + fn emit_encrypted_extensions( + flight: &mut HandshakeFlightTls13<'_>, + suite: &'static Tls13CipherSuite, + cx: &mut ServerContext<'_>, + ocsp_response: &mut Option<&[u8]>, + hello: &ClientHelloPayload, + resumedata: Option<&persist::ServerSessionValue>, + extra_exts: ServerExtensionsInput<'static>, + config: &ServerConfig, + ) -> Result<EarlyDataDecision, Error> { + let mut ep = hs::ExtensionProcessing::new(extra_exts); + ep.process_common(config, cx, ocsp_response, hello, resumedata)?; + + let early_data = decide_if_early_data_allowed(cx, hello, resumedata, suite, config); + if early_data == EarlyDataDecision::Accepted { + ep.extensions.early_data_ack = Some(()); + } + + let ee = HandshakeMessagePayload(HandshakePayload::EncryptedExtensions(ep.extensions)); + + trace!("sending encrypted extensions {ee:?}"); + flight.add(ee); + Ok(early_data) + } + + fn emit_certificate_req_tls13( + flight: &mut HandshakeFlightTls13<'_>, + config: &ServerConfig, + ) -> Result<bool, Error> { + if !config.verifier.offer_client_auth() { + return Ok(false); + } + + let cr = CertificateRequestPayloadTls13 { + context: PayloadU8::empty(), + extensions: CertificateRequestExtensions { + signature_algorithms: Some( + config + .verifier + .supported_verify_schemes(), + ), + certificate_compression_algorithms: match config.cert_decompressors.as_slice() { + &[] => None, + decomps => Some( + decomps + .iter() + .map(|decomp| decomp.algorithm()) + .collect(), + ), + }, + authority_names: match config.verifier.root_hint_subjects() { + &[] => None, + authorities => Some(authorities.to_vec()), + }, + }, + }; + + let creq = HandshakeMessagePayload(HandshakePayload::CertificateRequestTls13(cr)); + + trace!("Sending CertificateRequest {creq:?}"); + flight.add(creq); + Ok(true) + } + + fn emit_certificate_tls13( + flight: &mut HandshakeFlightTls13<'_>, + cert_chain: &[CertificateDer<'static>], + ocsp_response: Option<&[u8]>, + ) { + let cert = HandshakeMessagePayload(HandshakePayload::CertificateTls13( + CertificatePayloadTls13::new(cert_chain.iter(), ocsp_response), + )); + + trace!("sending certificate {cert:?}"); + flight.add(cert); + } + + fn emit_compressed_certificate_tls13( + flight: &mut HandshakeFlightTls13<'_>, + config: &ServerConfig, + cert_chain: &[CertificateDer<'static>], + ocsp_response: Option<&[u8]>, + cert_compressor: &'static dyn CertCompressor, + ) { + let payload = CertificatePayloadTls13::new(cert_chain.iter(), ocsp_response); + + let Ok(entry) = config + .cert_compression_cache + .compression_for(cert_compressor, &payload) + else { + return emit_certificate_tls13(flight, cert_chain, ocsp_response); + }; + + let c = HandshakeMessagePayload(HandshakePayload::CompressedCertificate( + entry.compressed_cert_payload(), + )); + + trace!("sending compressed certificate {c:?}"); + flight.add(c); + } + + fn emit_certificate_verify_tls13( + flight: &mut HandshakeFlightTls13<'_>, + common: &mut CommonState, + signing_key: &dyn sign::SigningKey, + schemes: &[SignatureScheme], + ) -> Result<(), Error> { + let message = construct_server_verify_message(&flight.transcript.current_hash()); + + let signer = signing_key + .choose_scheme(schemes) + .ok_or_else(|| { + common.send_fatal_alert( + AlertDescription::HandshakeFailure, + PeerIncompatible::NoSignatureSchemesInCommon, + ) + })?; + + let scheme = signer.scheme(); + let sig = signer.sign(message.as_ref())?; + + let cv = DigitallySignedStruct::new(scheme, sig); + + let cv = HandshakeMessagePayload(HandshakePayload::CertificateVerify(cv)); + + trace!("sending certificate-verify {cv:?}"); + flight.add(cv); + Ok(()) + } + + fn emit_finished_tls13( + mut flight: HandshakeFlightTls13<'_>, + randoms: &ConnectionRandoms, + cx: &mut ServerContext<'_>, + key_schedule: KeyScheduleHandshake, + config: &ServerConfig, + ) -> KeyScheduleTrafficWithClientFinishedPending { + let handshake_hash = flight.transcript.current_hash(); + let verify_data = key_schedule.sign_server_finish(&handshake_hash); + let verify_data_payload = Payload::new(verify_data.as_ref()); + + let fin = HandshakeMessagePayload(HandshakePayload::Finished(verify_data_payload)); + + trace!("sending finished {fin:?}"); + flight.add(fin); + let hash_at_server_fin = flight.transcript.current_hash(); + flight.finish(cx.common); + + // Now move to application data keys. Read key change is deferred until + // the Finish message is received & validated. + key_schedule.into_traffic_with_client_finished_pending( + hash_at_server_fin, + &*config.key_log, + &randoms.client, + cx.common, + ) + } +} + +struct ExpectAndSkipRejectedEarlyData { + skip_data_left: usize, + next: Box<hs::ExpectClientHello>, +} + +impl State<ServerConnectionData> for ExpectAndSkipRejectedEarlyData { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + /* "The server then ignores early data by skipping all records with an external + * content type of "application_data" (indicating that they are encrypted), + * up to the configured max_early_data_size." + * (RFC8446, 14.2.10) */ + if let MessagePayload::ApplicationData(skip_data) = &m.payload { + if skip_data.bytes().len() <= self.skip_data_left { + self.skip_data_left -= skip_data.bytes().len(); + return Ok(self); + } + } + + self.next.handle(cx, m) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificateOrCompressedCertificate { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, +} + +impl State<ServerConnectionData> for ExpectCertificateOrCompressedCertificate { + fn handle<'m>( + self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CertificateTls13(..)), + .. + } => Box::new(ExpectCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: self.key_schedule, + send_tickets: self.send_tickets, + message_already_in_transcript: false, + }) + .handle(cx, m), + + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::CompressedCertificate(..)), + .. + } => Box::new(ExpectCompressedCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: self.key_schedule, + send_tickets: self.send_tickets, + }) + .handle(cx, m), + + payload => Err(inappropriate_handshake_message( + &payload, + &[ContentType::Handshake], + &[ + HandshakeType::Certificate, + HandshakeType::CompressedCertificate, + ], + )), + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCompressedCertificate { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, +} + +impl State<ServerConnectionData> for ExpectCompressedCertificate { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + self.transcript.add_message(&m); + let compressed_cert = require_handshake_msg_move!( + m, + HandshakeType::CompressedCertificate, + HandshakePayload::CompressedCertificate + )?; + + let selected_decompressor = self + .config + .cert_decompressors + .iter() + .find(|item| item.algorithm() == compressed_cert.alg); + + let Some(decompressor) = selected_decompressor else { + return Err(cx.common.send_fatal_alert( + AlertDescription::BadCertificate, + PeerMisbehaved::SelectedUnofferedCertCompression, + )); + }; + + if compressed_cert.uncompressed_len as usize > CERTIFICATE_MAX_SIZE_LIMIT { + return Err(cx.common.send_fatal_alert( + AlertDescription::BadCertificate, + InvalidMessage::MessageTooLarge, + )); + } + + let mut decompress_buffer = vec![0u8; compressed_cert.uncompressed_len as usize]; + if let Err(compress::DecompressionFailed) = + decompressor.decompress(compressed_cert.compressed.0.bytes(), &mut decompress_buffer) + { + return Err(cx.common.send_fatal_alert( + AlertDescription::BadCertificate, + PeerMisbehaved::InvalidCertCompression, + )); + } + + let cert_payload = + match CertificatePayloadTls13::read(&mut Reader::init(&decompress_buffer)) { + Ok(cm) => cm, + Err(err) => { + return Err(cx + .common + .send_fatal_alert(AlertDescription::BadCertificate, err)); + } + }; + trace!( + "Client certificate decompressed using {:?} ({} bytes -> {})", + compressed_cert.alg, + compressed_cert + .compressed + .0 + .bytes() + .len(), + compressed_cert.uncompressed_len, + ); + + let m = Message { + version: ProtocolVersion::TLSv1_3, + payload: MessagePayload::handshake(HandshakeMessagePayload( + HandshakePayload::CertificateTls13(cert_payload.into_owned()), + )), + }; + + Box::new(ExpectCertificate { + config: self.config, + transcript: self.transcript, + suite: self.suite, + key_schedule: self.key_schedule, + send_tickets: self.send_tickets, + message_already_in_transcript: true, + }) + .handle(cx, m) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificate { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, + message_already_in_transcript: bool, +} + +impl State<ServerConnectionData> for ExpectCertificate { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + if !self.message_already_in_transcript { + self.transcript.add_message(&m); + } + let certp = require_handshake_msg_move!( + m, + HandshakeType::Certificate, + HandshakePayload::CertificateTls13 + )?; + + // We don't send any CertificateRequest extensions, so any extensions + // here are illegal. + if certp + .entries + .iter() + .any(|e| !e.extensions.only_contains(&[])) + { + return Err(PeerMisbehaved::UnsolicitedCertExtension.into()); + } + + let client_cert = certp.into_certificate_chain(); + + let mandatory = self + .config + .verifier + .client_auth_mandatory(); + + let Some((end_entity, intermediates)) = client_cert.split_first() else { + if !mandatory { + debug!("client auth requested but no certificate supplied"); + self.transcript.abandon_client_auth(); + return Ok(Box::new(ExpectFinished { + config: self.config, + suite: self.suite, + key_schedule: self.key_schedule, + transcript: self.transcript, + send_tickets: self.send_tickets, + })); + } + + return Err(cx.common.send_fatal_alert( + AlertDescription::CertificateRequired, + Error::NoCertificatesPresented, + )); + }; + + let now = self.config.current_time()?; + + self.config + .verifier + .verify_client_cert(end_entity, intermediates, now) + .map_err(|err| { + cx.common + .send_cert_verify_error_alert(err) + })?; + + Ok(Box::new(ExpectCertificateVerify { + config: self.config, + suite: self.suite, + transcript: self.transcript, + key_schedule: self.key_schedule, + client_cert: client_cert.into_owned(), + send_tickets: self.send_tickets, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +struct ExpectCertificateVerify { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + client_cert: CertificateChain<'static>, + send_tickets: usize, +} + +impl State<ServerConnectionData> for ExpectCertificateVerify { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let rc = { + let sig = require_handshake_msg!( + m, + HandshakeType::CertificateVerify, + HandshakePayload::CertificateVerify + )?; + let handshake_hash = self.transcript.current_hash(); + self.transcript.abandon_client_auth(); + let certs = &self.client_cert; + let msg = construct_client_verify_message(&handshake_hash); + + self.config + .verifier + .verify_tls13_signature(msg.as_ref(), &certs[0], sig) + }; + + if let Err(e) = rc { + return Err(cx + .common + .send_cert_verify_error_alert(e)); + } + + trace!("client CertificateVerify OK"); + cx.common.peer_certificates = Some(self.client_cert); + + self.transcript.add_message(&m); + Ok(Box::new(ExpectFinished { + config: self.config, + suite: self.suite, + key_schedule: self.key_schedule, + transcript: self.transcript, + send_tickets: self.send_tickets, + })) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process (any number of) early ApplicationData messages, +// followed by a terminating handshake EndOfEarlyData message --- + +struct ExpectEarlyData { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, +} + +impl State<ServerConnectionData> for ExpectEarlyData { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ApplicationData(payload) => { + match cx + .data + .early_data + .take_received_plaintext(payload) + { + true => Ok(self), + false => Err(cx.common.send_fatal_alert( + AlertDescription::UnexpectedMessage, + PeerMisbehaved::TooMuchEarlyDataReceived, + )), + } + } + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::EndOfEarlyData), + .. + } => { + self.key_schedule + .update_decrypter(cx.common); + self.transcript.add_message(&m); + Ok(Box::new(ExpectFinished { + config: self.config, + suite: self.suite, + key_schedule: self.key_schedule, + transcript: self.transcript, + send_tickets: self.send_tickets, + })) + } + payload => Err(inappropriate_handshake_message( + &payload, + &[ContentType::ApplicationData, ContentType::Handshake], + &[HandshakeType::EndOfEarlyData], + )), + } + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process client's Finished --- +fn get_server_session_value( + suite: &'static Tls13CipherSuite, + resumption: &KeyScheduleResumption, + cx: &ServerContext<'_>, + nonce: &[u8], + time_now: UnixTime, + age_obfuscation_offset: u32, +) -> persist::ServerSessionValue { + let version = ProtocolVersion::TLSv1_3; + + let secret = resumption.derive_ticket_psk(nonce); + + persist::ServerSessionValue::new( + cx.data.sni.as_ref(), + version, + suite.common.suite, + secret.as_ref(), + cx.common.peer_certificates.clone(), + cx.common.alpn_protocol.clone(), + cx.data.resumption_data.clone(), + time_now, + age_obfuscation_offset, + ) +} + +struct ExpectFinished { + config: Arc<ServerConfig>, + transcript: HandshakeHash, + suite: &'static Tls13CipherSuite, + key_schedule: KeyScheduleTrafficWithClientFinishedPending, + send_tickets: usize, +} + +impl ExpectFinished { + fn emit_ticket( + flight: &mut HandshakeFlightTls13<'_>, + suite: &'static Tls13CipherSuite, + cx: &ServerContext<'_>, + resumption: &KeyScheduleResumption, + config: &ServerConfig, + ) -> Result<(), Error> { + let secure_random = config.provider.secure_random; + let nonce = rand::random_vec(secure_random, 32)?; + let age_add = rand::random_u32(secure_random)?; + + let now = config.current_time()?; + + let plain = + get_server_session_value(suite, resumption, cx, &nonce, now, age_add).get_encoding(); + + let stateless = config.ticketer.enabled(); + let (ticket, lifetime) = if stateless { + let Some(ticket) = config.ticketer.encrypt(&plain) else { + return Ok(()); + }; + (ticket, config.ticketer.lifetime()) + } else { + let id = rand::random_vec(secure_random, 32)?; + let stored = config + .session_storage + .put(id.clone(), plain); + if !stored { + trace!("resumption not available; not issuing ticket"); + return Ok(()); + } + let stateful_lifetime = 24 * 60 * 60; // this is a bit of a punt + (id, stateful_lifetime) + }; + + let mut payload = NewSessionTicketPayloadTls13::new(lifetime, age_add, nonce, ticket); + + if config.max_early_data_size > 0 { + if !stateless { + payload.extensions.max_early_data_size = Some(config.max_early_data_size); + } else { + // We implement RFC8446 section 8.1: by enforcing that 0-RTT is + // only possible if using stateful resumption + warn!("early_data with stateless resumption is not allowed"); + } + } + + let t = HandshakeMessagePayload(HandshakePayload::NewSessionTicketTls13(payload)); + trace!("sending new ticket {t:?} (stateless: {stateless})"); + flight.add(t); + + Ok(()) + } +} + +impl State<ServerConnectionData> for ExpectFinished { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + let finished = + require_handshake_msg!(m, HandshakeType::Finished, HandshakePayload::Finished)?; + + let handshake_hash = self.transcript.current_hash(); + let (key_schedule_before_finished, expect_verify_data) = self + .key_schedule + .sign_client_finish(&handshake_hash, cx.common); + + let fin = match ConstantTimeEq::ct_eq(expect_verify_data.as_ref(), finished.bytes()).into() + { + true => verify::FinishedMessageVerified::assertion(), + false => { + return Err(cx + .common + .send_fatal_alert(AlertDescription::DecryptError, Error::DecryptError)); + } + }; + + // Note: future derivations include Client Finished, but not the + // main application data keying. + self.transcript.add_message(&m); + + cx.common.check_aligned_handshake()?; + + let (key_schedule_traffic, resumption) = + key_schedule_before_finished.into_traffic(self.transcript.current_hash()); + + let mut flight = HandshakeFlightTls13::new(&mut self.transcript); + for _ in 0..self.send_tickets { + Self::emit_ticket(&mut flight, self.suite, cx, &resumption, &self.config)?; + } + flight.finish(cx.common); + + // Application data may now flow, even if we have client auth enabled. + cx.common + .start_traffic(&mut cx.sendable_plaintext); + + Ok(match cx.common.is_quic() { + true => Box::new(ExpectQuicTraffic { + key_schedule: key_schedule_traffic, + _fin_verified: fin, + }), + false => Box::new(ExpectTraffic { + key_schedule: key_schedule_traffic, + _fin_verified: fin, + }), + }) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +// --- Process traffic --- +struct ExpectTraffic { + key_schedule: KeyScheduleTraffic, + _fin_verified: verify::FinishedMessageVerified, +} + +impl ExpectTraffic { + fn handle_key_update( + &mut self, + common: &mut CommonState, + key_update_request: &KeyUpdateRequest, + ) -> Result<(), Error> { + if let Protocol::Quic = common.protocol { + return Err(common.send_fatal_alert( + AlertDescription::UnexpectedMessage, + PeerMisbehaved::KeyUpdateReceivedInQuicConnection, + )); + } + + common.check_aligned_handshake()?; + + if common.should_update_key(key_update_request)? { + self.key_schedule + .update_encrypter_and_notify(common); + } + + // Update our read-side keys. + self.key_schedule + .update_decrypter(common); + Ok(()) + } +} + +impl State<ServerConnectionData> for ExpectTraffic { + fn handle<'m>( + mut self: Box<Self>, + cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + match m.payload { + MessagePayload::ApplicationData(payload) => cx + .common + .take_received_plaintext(payload), + MessagePayload::Handshake { + parsed: HandshakeMessagePayload(HandshakePayload::KeyUpdate(key_update)), + .. + } => self.handle_key_update(cx.common, &key_update)?, + payload => { + return Err(inappropriate_handshake_message( + &payload, + &[ContentType::ApplicationData, ContentType::Handshake], + &[HandshakeType::KeyUpdate], + )); + } + } + + Ok(self) + } + + fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) -> Result<(), Error> { + self.key_schedule + .export_keying_material(output, label, context) + } + + fn extract_secrets(&self) -> Result<PartiallyExtractedSecrets, Error> { + self.key_schedule + .extract_secrets(Side::Server) + } + + fn send_key_update_request(&mut self, common: &mut CommonState) -> Result<(), Error> { + self.key_schedule + .request_key_update_and_update_encrypter(common) + } + + fn into_external_state(self: Box<Self>) -> Result<Box<dyn KernelState + 'static>, Error> { + Ok(self) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +impl KernelState for ExpectTraffic { + fn update_secrets(&mut self, dir: Direction) -> Result<ConnectionTrafficSecrets, Error> { + self.key_schedule + .refresh_traffic_secret(match dir { + Direction::Transmit => Side::Server, + Direction::Receive => Side::Client, + }) + } + + fn handle_new_session_ticket( + &mut self, + _cx: &mut KernelContext<'_>, + _message: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + unreachable!( + "server connections should never have handle_new_session_ticket called on them" + ) + } +} + +struct ExpectQuicTraffic { + key_schedule: KeyScheduleTraffic, + _fin_verified: verify::FinishedMessageVerified, +} + +impl State<ServerConnectionData> for ExpectQuicTraffic { + fn handle<'m>( + self: Box<Self>, + _cx: &mut ServerContext<'_>, + m: Message<'m>, + ) -> hs::NextStateOrError<'m> + where + Self: 'm, + { + // reject all messages + Err(inappropriate_message(&m.payload, &[])) + } + + fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) -> Result<(), Error> { + self.key_schedule + .export_keying_material(output, label, context) + } + + fn into_owned(self: Box<Self>) -> hs::NextState<'static> { + self + } +} + +impl KernelState for ExpectQuicTraffic { + fn update_secrets(&mut self, _: Direction) -> Result<ConnectionTrafficSecrets, Error> { + Err(Error::General( + "QUIC connections do not support key updates".into(), + )) + } + + fn handle_new_session_ticket( + &mut self, + _cx: &mut KernelContext<'_>, + _message: &NewSessionTicketPayloadTls13, + ) -> Result<(), Error> { + unreachable!("handle_new_session_ticket should not be called for server-side connections") + } +} diff --git a/vendor/rustls/src/stream.rs b/vendor/rustls/src/stream.rs new file mode 100644 index 00000000..d92d05e3 --- /dev/null +++ b/vendor/rustls/src/stream.rs @@ -0,0 +1,282 @@ +use core::ops::{Deref, DerefMut}; +use std::io::{BufRead, IoSlice, Read, Result, Write}; + +use crate::conn::{ConnectionCommon, SideData}; + +/// This type implements `io::Read` and `io::Write`, encapsulating +/// a Connection `C` and an underlying transport `T`, such as a socket. +/// +/// This allows you to use a rustls Connection like a normal stream. +#[derive(Debug)] +pub struct Stream<'a, C: 'a + ?Sized, T: 'a + Read + Write + ?Sized> { + /// Our TLS connection + pub conn: &'a mut C, + + /// The underlying transport, like a socket + pub sock: &'a mut T, +} + +impl<'a, C, T, S> Stream<'a, C, T> +where + C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>, + T: 'a + Read + Write, + S: SideData, +{ + /// Make a new Stream using the Connection `conn` and socket-like object + /// `sock`. This does not fail and does no IO. + pub fn new(conn: &'a mut C, sock: &'a mut T) -> Self { + Self { conn, sock } + } + + /// If we're handshaking, complete all the IO for that. + /// If we have data to write, write it all. + fn complete_prior_io(&mut self) -> Result<()> { + if self.conn.is_handshaking() { + self.conn.complete_io(self.sock)?; + } + + if self.conn.wants_write() { + self.conn.complete_io(self.sock)?; + } + + Ok(()) + } + + fn prepare_read(&mut self) -> Result<()> { + self.complete_prior_io()?; + + // We call complete_io() in a loop since a single call may read only + // a partial packet from the underlying transport. A full packet is + // needed to get more plaintext, which we must do if EOF has not been + // hit. + while self.conn.wants_read() { + if self.conn.complete_io(self.sock)?.0 == 0 { + break; + } + } + + Ok(()) + } + + // Implements `BufRead::fill_buf` but with more flexible lifetimes, so StreamOwned can reuse it + fn fill_buf(mut self) -> Result<&'a [u8]> + where + S: 'a, + { + self.prepare_read()?; + self.conn.reader().into_first_chunk() + } +} + +impl<'a, C, T, S> Read for Stream<'a, C, T> +where + C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>, + T: 'a + Read + Write, + S: SideData, +{ + fn read(&mut self, buf: &mut [u8]) -> Result<usize> { + self.prepare_read()?; + self.conn.reader().read(buf) + } + + #[cfg(read_buf)] + fn read_buf(&mut self, cursor: core::io::BorrowedCursor<'_>) -> Result<()> { + self.prepare_read()?; + self.conn.reader().read_buf(cursor) + } +} + +impl<'a, C, T, S> BufRead for Stream<'a, C, T> +where + C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>, + T: 'a + Read + Write, + S: 'a + SideData, +{ + fn fill_buf(&mut self) -> Result<&[u8]> { + // reborrow to get an owned `Stream` + Stream { + conn: self.conn, + sock: self.sock, + } + .fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.conn.reader().consume(amt) + } +} + +impl<'a, C, T, S> Write for Stream<'a, C, T> +where + C: 'a + DerefMut + Deref<Target = ConnectionCommon<S>>, + T: 'a + Read + Write, + S: SideData, +{ + fn write(&mut self, buf: &[u8]) -> Result<usize> { + self.complete_prior_io()?; + + let len = self.conn.writer().write(buf)?; + + // Try to write the underlying transport here, but don't let + // any errors mask the fact we've consumed `len` bytes. + // Callers will learn of permanent errors on the next call. + let _ = self.conn.complete_io(self.sock); + + Ok(len) + } + + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> { + self.complete_prior_io()?; + + let len = self + .conn + .writer() + .write_vectored(bufs)?; + + // Try to write the underlying transport here, but don't let + // any errors mask the fact we've consumed `len` bytes. + // Callers will learn of permanent errors on the next call. + let _ = self.conn.complete_io(self.sock); + + Ok(len) + } + + fn flush(&mut self) -> Result<()> { + self.complete_prior_io()?; + + self.conn.writer().flush()?; + if self.conn.wants_write() { + self.conn.complete_io(self.sock)?; + } + Ok(()) + } +} + +/// This type implements `io::Read` and `io::Write`, encapsulating +/// and owning a Connection `C` and an underlying blocking transport +/// `T`, such as a socket. +/// +/// This allows you to use a rustls Connection like a normal stream. +#[derive(Debug)] +pub struct StreamOwned<C: Sized, T: Read + Write + Sized> { + /// Our connection + pub conn: C, + + /// The underlying transport, like a socket + pub sock: T, +} + +impl<C, T, S> StreamOwned<C, T> +where + C: DerefMut + Deref<Target = ConnectionCommon<S>>, + T: Read + Write, + S: SideData, +{ + /// Make a new StreamOwned taking the Connection `conn` and socket-like + /// object `sock`. This does not fail and does no IO. + /// + /// This is the same as `Stream::new` except `conn` and `sock` are + /// moved into the StreamOwned. + pub fn new(conn: C, sock: T) -> Self { + Self { conn, sock } + } + + /// Get a reference to the underlying socket + pub fn get_ref(&self) -> &T { + &self.sock + } + + /// Get a mutable reference to the underlying socket + pub fn get_mut(&mut self) -> &mut T { + &mut self.sock + } + + /// Extract the `conn` and `sock` parts from the `StreamOwned` + pub fn into_parts(self) -> (C, T) { + (self.conn, self.sock) + } +} + +impl<'a, C, T, S> StreamOwned<C, T> +where + C: DerefMut + Deref<Target = ConnectionCommon<S>>, + T: Read + Write, + S: SideData, +{ + fn as_stream(&'a mut self) -> Stream<'a, C, T> { + Stream { + conn: &mut self.conn, + sock: &mut self.sock, + } + } +} + +impl<C, T, S> Read for StreamOwned<C, T> +where + C: DerefMut + Deref<Target = ConnectionCommon<S>>, + T: Read + Write, + S: SideData, +{ + fn read(&mut self, buf: &mut [u8]) -> Result<usize> { + self.as_stream().read(buf) + } + + #[cfg(read_buf)] + fn read_buf(&mut self, cursor: core::io::BorrowedCursor<'_>) -> Result<()> { + self.as_stream().read_buf(cursor) + } +} + +impl<C, T, S> BufRead for StreamOwned<C, T> +where + C: DerefMut + Deref<Target = ConnectionCommon<S>>, + T: Read + Write, + S: 'static + SideData, +{ + fn fill_buf(&mut self) -> Result<&[u8]> { + self.as_stream().fill_buf() + } + + fn consume(&mut self, amt: usize) { + self.as_stream().consume(amt) + } +} + +impl<C, T, S> Write for StreamOwned<C, T> +where + C: DerefMut + Deref<Target = ConnectionCommon<S>>, + T: Read + Write, + S: SideData, +{ + fn write(&mut self, buf: &[u8]) -> Result<usize> { + self.as_stream().write(buf) + } + + fn flush(&mut self) -> Result<()> { + self.as_stream().flush() + } +} + +#[cfg(test)] +mod tests { + use std::net::TcpStream; + + use super::{Stream, StreamOwned}; + use crate::client::ClientConnection; + use crate::server::ServerConnection; + + #[test] + fn stream_can_be_created_for_connection_and_tcpstream() { + type _Test<'a> = Stream<'a, ClientConnection, TcpStream>; + } + + #[test] + fn streamowned_can_be_created_for_client_and_tcpstream() { + type _Test = StreamOwned<ClientConnection, TcpStream>; + } + + #[test] + fn streamowned_can_be_created_for_server_and_tcpstream() { + type _Test = StreamOwned<ServerConnection, TcpStream>; + } +} diff --git a/vendor/rustls/src/suites.rs b/vendor/rustls/src/suites.rs new file mode 100644 index 00000000..c40b88df --- /dev/null +++ b/vendor/rustls/src/suites.rs @@ -0,0 +1,272 @@ +use core::fmt; + +use crate::common_state::Protocol; +use crate::crypto::cipher::{AeadKey, Iv}; +use crate::crypto::{self, KeyExchangeAlgorithm}; +use crate::enums::{CipherSuite, SignatureAlgorithm, SignatureScheme}; +use crate::msgs::handshake::ALL_KEY_EXCHANGE_ALGORITHMS; +#[cfg(feature = "tls12")] +use crate::tls12::Tls12CipherSuite; +use crate::tls13::Tls13CipherSuite; +#[cfg(feature = "tls12")] +use crate::versions::TLS12; +use crate::versions::{SupportedProtocolVersion, TLS13}; + +/// Common state for cipher suites (both for TLS 1.2 and TLS 1.3) +pub struct CipherSuiteCommon { + /// The TLS enumeration naming this cipher suite. + pub suite: CipherSuite, + + /// Which hash function the suite uses. + pub hash_provider: &'static dyn crypto::hash::Hash, + + /// Number of TCP-TLS messages that can be safely encrypted with a single key of this type + /// + /// Once a `MessageEncrypter` produced for this suite has encrypted more than + /// `confidentiality_limit` messages, an attacker gains an advantage in distinguishing it + /// from an ideal pseudorandom permutation (PRP). + /// + /// This is to be set on the assumption that messages are maximally sized -- + /// each is 2<sup>14</sup> bytes. It **does not** consider confidentiality limits for + /// QUIC connections - see the [`quic::KeyBuilder.confidentiality_limit`] field for + /// this context. + /// + /// For AES-GCM implementations, this should be set to 2<sup>24</sup> to limit attack + /// probability to one in 2<sup>60</sup>. See [AEBounds] (Table 1) and [draft-irtf-aead-limits-08]: + /// + /// ```python + /// >>> p = 2 ** -60 + /// >>> L = (2 ** 14 // 16) + 1 + /// >>> qlim = (math.sqrt(p) * (2 ** (129 // 2)) - 1) / (L + 1) + /// >>> print(int(qlim).bit_length()) + /// 24 + /// ``` + /// [AEBounds]: https://eprint.iacr.org/2024/051.pdf + /// [draft-irtf-aead-limits-08]: https://www.ietf.org/archive/id/draft-irtf-cfrg-aead-limits-08.html#section-5.1.1 + /// + /// For chacha20-poly1305 implementations, this should be set to `u64::MAX`: + /// see <https://www.ietf.org/archive/id/draft-irtf-cfrg-aead-limits-08.html#section-5.2.1> + pub confidentiality_limit: u64, +} + +impl CipherSuiteCommon { + /// Return `true` if this is backed by a FIPS-approved implementation. + /// + /// This means all the constituent parts that do cryptography return `true` for `fips()`. + pub fn fips(&self) -> bool { + self.hash_provider.fips() + } +} + +/// A cipher suite supported by rustls. +/// +/// This type carries both configuration and implementation. Compare with +/// [`CipherSuite`], which carries solely a cipher suite identifier. +#[derive(Clone, Copy, PartialEq)] +pub enum SupportedCipherSuite { + /// A TLS 1.2 cipher suite + #[cfg(feature = "tls12")] + Tls12(&'static Tls12CipherSuite), + /// A TLS 1.3 cipher suite + Tls13(&'static Tls13CipherSuite), +} + +impl SupportedCipherSuite { + /// The cipher suite's identifier + pub fn suite(&self) -> CipherSuite { + self.common().suite + } + + /// The hash function the ciphersuite uses. + pub(crate) fn hash_provider(&self) -> &'static dyn crypto::hash::Hash { + self.common().hash_provider + } + + pub(crate) fn common(&self) -> &CipherSuiteCommon { + match self { + #[cfg(feature = "tls12")] + Self::Tls12(inner) => &inner.common, + Self::Tls13(inner) => &inner.common, + } + } + + /// Return the inner `Tls13CipherSuite` for this suite, if it is a TLS1.3 suite. + pub fn tls13(&self) -> Option<&'static Tls13CipherSuite> { + match self { + #[cfg(feature = "tls12")] + Self::Tls12(_) => None, + Self::Tls13(inner) => Some(inner), + } + } + + /// Return supported protocol version for the cipher suite. + pub fn version(&self) -> &'static SupportedProtocolVersion { + match self { + #[cfg(feature = "tls12")] + Self::Tls12(_) => &TLS12, + Self::Tls13(_) => &TLS13, + } + } + + /// Return true if this suite is usable for a key only offering `sig_alg` + /// signatures. This resolves to true for all TLS1.3 suites. + pub fn usable_for_signature_algorithm(&self, _sig_alg: SignatureAlgorithm) -> bool { + match self { + Self::Tls13(_) => true, // no constraint expressed by ciphersuite (e.g., TLS1.3) + #[cfg(feature = "tls12")] + Self::Tls12(inner) => inner + .sign + .iter() + .any(|scheme| scheme.algorithm() == _sig_alg), + } + } + + /// Return true if this suite is usable for the given [`Protocol`]. + /// + /// All cipher suites are usable for TCP-TLS. Only TLS1.3 suites + /// with `Tls13CipherSuite::quic` provided are usable for QUIC. + pub(crate) fn usable_for_protocol(&self, proto: Protocol) -> bool { + match proto { + Protocol::Tcp => true, + Protocol::Quic => self + .tls13() + .and_then(|cs| cs.quic) + .is_some(), + } + } + + /// Return `true` if this is backed by a FIPS-approved implementation. + pub fn fips(&self) -> bool { + match self { + #[cfg(feature = "tls12")] + Self::Tls12(cs) => cs.fips(), + Self::Tls13(cs) => cs.fips(), + } + } + + /// Return the list of `KeyExchangeAlgorithm`s supported by this cipher suite. + /// + /// TLS 1.3 cipher suites support both ECDHE and DHE key exchange, but TLS 1.2 suites + /// support one or the other. + pub(crate) fn key_exchange_algorithms(&self) -> &[KeyExchangeAlgorithm] { + match self { + #[cfg(feature = "tls12")] + Self::Tls12(tls12) => core::slice::from_ref(&tls12.kx), + Self::Tls13(_) => ALL_KEY_EXCHANGE_ALGORITHMS, + } + } + + /// Say if the given `KeyExchangeAlgorithm` is supported by this cipher suite. + /// + /// TLS 1.3 cipher suites support all key exchange types, but TLS 1.2 suites + /// support only one. + pub(crate) fn usable_for_kx_algorithm(&self, _kxa: KeyExchangeAlgorithm) -> bool { + match self { + #[cfg(feature = "tls12")] + Self::Tls12(tls12) => tls12.kx == _kxa, + Self::Tls13(_) => true, + } + } +} + +impl fmt::Debug for SupportedCipherSuite { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.suite().fmt(f) + } +} + +/// Return true if `sigscheme` is usable by any of the given suites. +pub(crate) fn compatible_sigscheme_for_suites( + sigscheme: SignatureScheme, + common_suites: &[SupportedCipherSuite], +) -> bool { + let sigalg = sigscheme.algorithm(); + common_suites + .iter() + .any(|&suite| suite.usable_for_signature_algorithm(sigalg)) +} + +/// Secrets for transmitting/receiving data over a TLS session. +/// +/// After performing a handshake with rustls, these secrets can be extracted +/// to configure kTLS for a socket, and have the kernel take over encryption +/// and/or decryption. +pub struct ExtractedSecrets { + /// sequence number and secrets for the "tx" (transmit) direction + pub tx: (u64, ConnectionTrafficSecrets), + + /// sequence number and secrets for the "rx" (receive) direction + pub rx: (u64, ConnectionTrafficSecrets), +} + +/// [ExtractedSecrets] minus the sequence numbers +pub(crate) struct PartiallyExtractedSecrets { + /// secrets for the "tx" (transmit) direction + pub(crate) tx: ConnectionTrafficSecrets, + + /// secrets for the "rx" (receive) direction + pub(crate) rx: ConnectionTrafficSecrets, +} + +/// Secrets used to encrypt/decrypt data in a TLS session. +/// +/// These can be used to configure kTLS for a socket in one direction. +/// The only other piece of information needed is the sequence number, +/// which is in [ExtractedSecrets]. +#[non_exhaustive] +pub enum ConnectionTrafficSecrets { + /// Secrets for the AES_128_GCM AEAD algorithm + Aes128Gcm { + /// AEAD Key + key: AeadKey, + /// Initialization vector + iv: Iv, + }, + + /// Secrets for the AES_256_GCM AEAD algorithm + Aes256Gcm { + /// AEAD Key + key: AeadKey, + /// Initialization vector + iv: Iv, + }, + + /// Secrets for the CHACHA20_POLY1305 AEAD algorithm + Chacha20Poly1305 { + /// AEAD Key + key: AeadKey, + /// Initialization vector + iv: Iv, + }, +} + +#[cfg(test)] +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use std::println; + + use super::provider::tls13::*; + + #[test] + fn test_scs_is_debug() { + println!("{:?}", super::provider::ALL_CIPHER_SUITES); + } + + #[test] + fn test_can_resume_to() { + assert!( + TLS13_AES_128_GCM_SHA256 + .tls13() + .unwrap() + .can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL) + .is_some() + ); + assert!( + TLS13_AES_256_GCM_SHA384 + .tls13() + .unwrap() + .can_resume_from(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL) + .is_none() + ); + } +} diff --git a/vendor/rustls/src/test_macros.rs b/vendor/rustls/src/test_macros.rs new file mode 100644 index 00000000..237578e0 --- /dev/null +++ b/vendor/rustls/src/test_macros.rs @@ -0,0 +1,71 @@ +//! Macros used for unit testing. + +/// Instantiate the given test functions once for each built-in provider. +/// +/// The selected provider module is bound as `provider`; you can rely on this +/// having the union of the items common to the `crypto::ring` and +/// `crypto::aws_lc_rs` modules. +#[cfg(test)] +macro_rules! test_for_each_provider { + ($($tt:tt)+) => { + #[cfg(feature = "ring")] + mod test_with_ring { + use crate::crypto::ring as provider; + #[allow(unused_imports)] + use super::*; + $($tt)+ + } + + #[cfg(feature = "aws_lc_rs")] + mod test_with_aws_lc_rs { + use crate::crypto::aws_lc_rs as provider; + #[allow(unused_imports)] + use super::*; + $($tt)+ + } + }; +} + +/// Instantiate the given benchmark functions once for each built-in provider. +/// +/// The selected provider module is bound as `provider`; you can rely on this +/// having the union of the items common to the `crypto::ring` and +/// `crypto::aws_lc_rs` modules. +#[cfg(bench)] +macro_rules! bench_for_each_provider { + ($($tt:tt)+) => { + #[cfg(feature = "ring")] + mod bench_with_ring { + use crate::crypto::ring as provider; + #[allow(unused_imports)] + use super::*; + $($tt)+ + } + + #[cfg(feature = "aws_lc_rs")] + mod bench_with_aws_lc_rs { + use crate::crypto::aws_lc_rs as provider; + #[allow(unused_imports)] + use super::*; + $($tt)+ + } + }; +} + +#[cfg(test)] +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + #[test] + fn test_each_provider() { + std::println!("provider is {:?}", super::provider::default_provider()); + } +} + +#[cfg(all(test, bench))] +#[macro_rules_attribute::apply(bench_for_each_provider)] +mod benchmarks { + #[bench] + fn bench_each_provider(b: &mut test::Bencher) { + b.iter(|| super::provider::default_provider()); + } +} diff --git a/vendor/rustls/src/ticketer.rs b/vendor/rustls/src/ticketer.rs new file mode 100644 index 00000000..d1b5e143 --- /dev/null +++ b/vendor/rustls/src/ticketer.rs @@ -0,0 +1,365 @@ +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::mem; +#[cfg(feature = "std")] +use std::sync::{RwLock, RwLockReadGuard}; + +use pki_types::UnixTime; + +use crate::lock::{Mutex, MutexGuard}; +use crate::server::ProducesTickets; +#[cfg(not(feature = "std"))] +use crate::time_provider::TimeProvider; +use crate::{Error, rand}; + +#[derive(Debug)] +pub(crate) struct TicketSwitcherState { + next: Option<Box<dyn ProducesTickets>>, + current: Box<dyn ProducesTickets>, + previous: Option<Box<dyn ProducesTickets>>, + next_switch_time: u64, +} + +/// A ticketer that has a 'current' sub-ticketer and a single +/// 'previous' ticketer. It creates a new ticketer every so +/// often, demoting the current ticketer. +#[cfg_attr(feature = "std", derive(Debug))] +pub struct TicketSwitcher { + pub(crate) generator: fn() -> Result<Box<dyn ProducesTickets>, rand::GetRandomFailed>, + lifetime: u32, + state: Mutex<TicketSwitcherState>, + #[cfg(not(feature = "std"))] + time_provider: &'static dyn TimeProvider, +} + +impl TicketSwitcher { + /// Creates a new `TicketSwitcher`, which rotates through sub-ticketers + /// based on the passage of time. + /// + /// `lifetime` is in seconds, and is how long the current ticketer + /// is used to generate new tickets. Tickets are accepted for no + /// longer than twice this duration. `generator` produces a new + /// `ProducesTickets` implementation. + #[cfg(feature = "std")] + #[deprecated(note = "use TicketRotator instead")] + pub fn new( + lifetime: u32, + generator: fn() -> Result<Box<dyn ProducesTickets>, rand::GetRandomFailed>, + ) -> Result<Self, Error> { + Ok(Self { + generator, + lifetime, + state: Mutex::new(TicketSwitcherState { + next: Some(generator()?), + current: generator()?, + previous: None, + next_switch_time: UnixTime::now() + .as_secs() + .saturating_add(u64::from(lifetime)), + }), + }) + } + + /// Creates a new `TicketSwitcher`, which rotates through sub-ticketers + /// based on the passage of time. + /// + /// `lifetime` is in seconds, and is how long the current ticketer + /// is used to generate new tickets. Tickets are accepted for no + /// longer than twice this duration. `generator` produces a new + /// `ProducesTickets` implementation. + #[cfg(not(feature = "std"))] + pub fn new<M: crate::lock::MakeMutex>( + lifetime: u32, + generator: fn() -> Result<Box<dyn ProducesTickets>, rand::GetRandomFailed>, + time_provider: &'static dyn TimeProvider, + ) -> Result<Self, Error> { + Ok(Self { + generator, + lifetime, + state: Mutex::new::<M>(TicketSwitcherState { + next: Some(generator()?), + current: generator()?, + previous: None, + next_switch_time: time_provider + .current_time() + .unwrap() + .as_secs() + .saturating_add(u64::from(lifetime)), + }), + time_provider, + }) + } + + /// If it's time, demote the `current` ticketer to `previous` (so it + /// does no new encryptions but can do decryption) and use next for a + /// new `current` ticketer. + /// + /// Calling this regularly will ensure timely key erasure. Otherwise, + /// key erasure will be delayed until the next encrypt/decrypt call. + /// + /// For efficiency, this is also responsible for locking the state mutex + /// and returning the mutexguard. + pub(crate) fn maybe_roll(&self, now: UnixTime) -> Option<MutexGuard<'_, TicketSwitcherState>> { + // The code below aims to make switching as efficient as possible + // in the common case that the generator never fails. To achieve this + // we run the following steps: + // 1. If no switch is necessary, just return the mutexguard + // 2. Shift over all of the ticketers (so current becomes previous, + // and next becomes current). After this, other threads can + // start using the new current ticketer. + // 3. unlock mutex and generate new ticketer. + // 4. Place new ticketer in next and return current + // + // There are a few things to note here. First, we don't check whether + // a new switch might be needed in step 4, even though, due to locking + // and entropy collection, significant amounts of time may have passed. + // This is to guarantee that the thread doing the switch will eventually + // make progress. + // + // Second, because next may be None, step 2 can fail. In that case + // we enter a recovery mode where we generate 2 new ticketers, one for + // next and one for the current ticketer. We then take the mutex a + // second time and redo the time check to see if a switch is still + // necessary. + // + // This somewhat convoluted approach ensures good availability of the + // mutex, by ensuring that the state is usable and the mutex not held + // during generation. It also ensures that, so long as the inner + // ticketer never generates panics during encryption/decryption, + // we are guaranteed to never panic when holding the mutex. + + let now = now.as_secs(); + let mut are_recovering = false; // Are we recovering from previous failure? + { + // Scope the mutex so we only take it for as long as needed + let mut state = self.state.lock()?; + + // Fast path in case we do not need to switch to the next ticketer yet + if now <= state.next_switch_time { + return Some(state); + } + + // Make the switch, or mark for recovery if not possible + match state.next.take() { + Some(next) => { + state.previous = Some(mem::replace(&mut state.current, next)); + state.next_switch_time = now.saturating_add(u64::from(self.lifetime)); + } + _ => are_recovering = true, + } + } + + // We always need a next, so generate it now + let next = (self.generator)().ok()?; + if !are_recovering { + // Normal path, generate new next and place it in the state + let mut state = self.state.lock()?; + state.next = Some(next); + Some(state) + } else { + // Recovering, generate also a new current ticketer, and modify state + // as needed. (we need to redo the time check, otherwise this might + // result in very rapid switching of ticketers) + let new_current = (self.generator)().ok()?; + let mut state = self.state.lock()?; + state.next = Some(next); + if now > state.next_switch_time { + state.previous = Some(mem::replace(&mut state.current, new_current)); + state.next_switch_time = now.saturating_add(u64::from(self.lifetime)); + } + Some(state) + } + } +} + +impl ProducesTickets for TicketSwitcher { + fn lifetime(&self) -> u32 { + self.lifetime * 2 + } + + fn enabled(&self) -> bool { + true + } + + fn encrypt(&self, message: &[u8]) -> Option<Vec<u8>> { + #[cfg(feature = "std")] + let now = UnixTime::now(); + #[cfg(not(feature = "std"))] + let now = self + .time_provider + .current_time() + .unwrap(); + + self.maybe_roll(now)? + .current + .encrypt(message) + } + + fn decrypt(&self, ciphertext: &[u8]) -> Option<Vec<u8>> { + #[cfg(feature = "std")] + let now = UnixTime::now(); + #[cfg(not(feature = "std"))] + let now = self + .time_provider + .current_time() + .unwrap(); + + let state = self.maybe_roll(now)?; + + // Decrypt with the current key; if that fails, try with the previous. + state + .current + .decrypt(ciphertext) + .or_else(|| { + state + .previous + .as_ref() + .and_then(|previous| previous.decrypt(ciphertext)) + }) + } +} + +#[cfg(not(feature = "std"))] +impl core::fmt::Debug for TicketSwitcher { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("TicketSwitcher") + .field("generator", &self.generator) + .field("lifetime", &self.lifetime) + .field("state", &**self.state.lock().unwrap()) + .finish() + } +} + +#[cfg(feature = "std")] +#[derive(Debug)] +pub(crate) struct TicketRotatorState { + current: Box<dyn ProducesTickets>, + previous: Option<Box<dyn ProducesTickets>>, + next_switch_time: u64, +} + +/// A ticketer that has a 'current' sub-ticketer and a single +/// 'previous' ticketer. It creates a new ticketer every so +/// often, demoting the current ticketer. +#[cfg(feature = "std")] +pub struct TicketRotator { + pub(crate) generator: fn() -> Result<Box<dyn ProducesTickets>, rand::GetRandomFailed>, + lifetime: u32, + state: RwLock<TicketRotatorState>, +} + +#[cfg(feature = "std")] +impl TicketRotator { + /// Creates a new `TicketRotator`, which rotates through sub-ticketers + /// based on the passage of time. + /// + /// `lifetime` is in seconds, and is how long the current ticketer + /// is used to generate new tickets. Tickets are accepted for no + /// longer than twice this duration. `generator` produces a new + /// `ProducesTickets` implementation. + pub fn new( + lifetime: u32, + generator: fn() -> Result<Box<dyn ProducesTickets>, rand::GetRandomFailed>, + ) -> Result<Self, Error> { + Ok(Self { + generator, + lifetime, + state: RwLock::new(TicketRotatorState { + current: generator()?, + previous: None, + next_switch_time: UnixTime::now() + .as_secs() + .saturating_add(u64::from(lifetime)), + }), + }) + } + + /// If it's time, demote the `current` ticketer to `previous` (so it + /// does no new encryptions but can do decryption) and replace it + /// with a new one. + /// + /// Calling this regularly will ensure timely key erasure. Otherwise, + /// key erasure will be delayed until the next encrypt/decrypt call. + /// + /// For efficiency, this is also responsible for locking the state rwlock + /// and returning it for read. + pub(crate) fn maybe_roll( + &self, + now: UnixTime, + ) -> Option<RwLockReadGuard<'_, TicketRotatorState>> { + let now = now.as_secs(); + + // Fast, common, & read-only path in case we do not need to switch + // to the next ticketer yet + { + let read = self.state.read().ok()?; + + if now <= read.next_switch_time { + return Some(read); + } + } + + // We need to switch ticketers, and make a new one. + // Generate a potential "next" ticketer outside the lock. + let next = (self.generator)().ok()?; + + let mut write = self.state.write().ok()?; + + if now <= write.next_switch_time { + // Another thread beat us to it. Nothing to do. + drop(write); + + return self.state.read().ok(); + } + + // Now we have: + // - confirmed we need rotation + // - confirmed we are the thread that will do it + // - successfully made the replacement ticketer + write.previous = Some(mem::replace(&mut write.current, next)); + write.next_switch_time = now.saturating_add(u64::from(self.lifetime)); + drop(write); + + self.state.read().ok() + } +} + +#[cfg(feature = "std")] +impl ProducesTickets for TicketRotator { + fn lifetime(&self) -> u32 { + self.lifetime * 2 + } + + fn enabled(&self) -> bool { + true + } + + fn encrypt(&self, message: &[u8]) -> Option<Vec<u8>> { + self.maybe_roll(UnixTime::now())? + .current + .encrypt(message) + } + + fn decrypt(&self, ciphertext: &[u8]) -> Option<Vec<u8>> { + let state = self.maybe_roll(UnixTime::now())?; + + // Decrypt with the current key; if that fails, try with the previous. + state + .current + .decrypt(ciphertext) + .or_else(|| { + state + .previous + .as_ref() + .and_then(|previous| previous.decrypt(ciphertext)) + }) + } +} + +#[cfg(feature = "std")] +impl core::fmt::Debug for TicketRotator { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + f.debug_struct("TicketRotator") + .finish_non_exhaustive() + } +} diff --git a/vendor/rustls/src/time_provider.rs b/vendor/rustls/src/time_provider.rs new file mode 100644 index 00000000..e43f4d6d --- /dev/null +++ b/vendor/rustls/src/time_provider.rs @@ -0,0 +1,30 @@ +//! The library's source of time. + +use core::fmt::Debug; + +use pki_types::UnixTime; + +/// An object that provides the current time. +/// +/// This is used to, for example, check if a certificate has expired during +/// certificate validation, or to check the age of a ticket. +pub trait TimeProvider: Debug + Send + Sync { + /// Returns the current wall time. + /// + /// This is not required to be monotonic. + /// + /// Return `None` if unable to retrieve the time. + fn current_time(&self) -> Option<UnixTime>; +} + +#[derive(Debug)] +#[cfg(feature = "std")] +/// Default `TimeProvider` implementation that uses `std` +pub struct DefaultTimeProvider; + +#[cfg(feature = "std")] +impl TimeProvider for DefaultTimeProvider { + fn current_time(&self) -> Option<UnixTime> { + Some(UnixTime::now()) + } +} diff --git a/vendor/rustls/src/tls12/mod.rs b/vendor/rustls/src/tls12/mod.rs new file mode 100644 index 00000000..c9271850 --- /dev/null +++ b/vendor/rustls/src/tls12/mod.rs @@ -0,0 +1,380 @@ +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; +use core::fmt; + +use zeroize::Zeroize; + +use crate::common_state::{CommonState, Side}; +use crate::conn::ConnectionRandoms; +use crate::crypto; +use crate::crypto::cipher::{AeadKey, MessageDecrypter, MessageEncrypter, Tls12AeadAlgorithm}; +use crate::crypto::hash; +use crate::enums::{AlertDescription, SignatureScheme}; +use crate::error::{Error, InvalidMessage}; +use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::handshake::{KeyExchangeAlgorithm, KxDecode}; +use crate::suites::{CipherSuiteCommon, PartiallyExtractedSecrets, SupportedCipherSuite}; + +/// A TLS 1.2 cipher suite supported by rustls. +pub struct Tls12CipherSuite { + /// Common cipher suite fields. + pub common: CipherSuiteCommon, + + /// How to compute the TLS1.2 PRF for the suite's hash function. + /// + /// If you have a TLS1.2 PRF implementation, you should directly implement the [`crypto::tls12::Prf`] trait. + /// + /// If not, you can implement the [`crypto::hmac::Hmac`] trait (and associated), and then use + /// [`crypto::tls12::PrfUsingHmac`]. + pub prf_provider: &'static dyn crypto::tls12::Prf, + + /// How to exchange/agree keys. + /// + /// In TLS1.2, the key exchange method (eg, Elliptic Curve Diffie-Hellman with Ephemeral keys -- ECDHE) + /// is baked into the cipher suite, but the details to achieve it are negotiated separately. + /// + /// This controls how protocol messages (like the `ClientKeyExchange` message) are interpreted + /// once this cipher suite has been negotiated. + pub kx: KeyExchangeAlgorithm, + + /// How to sign messages for authentication. + /// + /// This is a set of [`SignatureScheme`]s that are usable once this cipher suite has been + /// negotiated. + /// + /// The precise scheme used is then chosen from this set by the selected authentication key. + pub sign: &'static [SignatureScheme], + + /// How to produce a [`MessageDecrypter`] or [`MessageEncrypter`] + /// from raw key material. + pub aead_alg: &'static dyn Tls12AeadAlgorithm, +} + +impl Tls12CipherSuite { + /// Resolve the set of supported [`SignatureScheme`]s from the + /// offered signature schemes. If we return an empty + /// set, the handshake terminates. + pub fn resolve_sig_schemes(&self, offered: &[SignatureScheme]) -> Vec<SignatureScheme> { + self.sign + .iter() + .filter(|pref| offered.contains(pref)) + .cloned() + .collect() + } + + /// Return `true` if this is backed by a FIPS-approved implementation. + /// + /// This means all the constituent parts that do cryptography return `true` for `fips()`. + pub fn fips(&self) -> bool { + self.common.fips() && self.prf_provider.fips() && self.aead_alg.fips() + } +} + +impl From<&'static Tls12CipherSuite> for SupportedCipherSuite { + fn from(s: &'static Tls12CipherSuite) -> Self { + Self::Tls12(s) + } +} + +impl PartialEq for Tls12CipherSuite { + fn eq(&self, other: &Self) -> bool { + self.common.suite == other.common.suite + } +} + +impl fmt::Debug for Tls12CipherSuite { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Tls12CipherSuite") + .field("suite", &self.common.suite) + .finish() + } +} + +/// TLS1.2 per-connection keying material +pub(crate) struct ConnectionSecrets { + pub(crate) randoms: ConnectionRandoms, + suite: &'static Tls12CipherSuite, + pub(crate) master_secret: [u8; 48], +} + +impl ConnectionSecrets { + pub(crate) fn from_key_exchange( + kx: Box<dyn crypto::ActiveKeyExchange>, + peer_pub_key: &[u8], + ems_seed: Option<hash::Output>, + randoms: ConnectionRandoms, + suite: &'static Tls12CipherSuite, + ) -> Result<Self, Error> { + let mut ret = Self { + randoms, + suite, + master_secret: [0u8; 48], + }; + + let (label, seed) = match ems_seed { + Some(seed) => ("extended master secret", Seed::Ems(seed)), + None => ( + "master secret", + Seed::Randoms(join_randoms(&ret.randoms.client, &ret.randoms.server)), + ), + }; + + // The API contract for for_key_exchange is that the caller guarantees `label` and `seed` + // slice parameters are non-empty. + // `label` is guaranteed non-empty because it's assigned from a `&str` above. + // `seed.as_ref()` is guaranteed non-empty by documentation on the AsRef impl. + ret.suite + .prf_provider + .for_key_exchange( + &mut ret.master_secret, + kx, + peer_pub_key, + label.as_bytes(), + seed.as_ref(), + )?; + + Ok(ret) + } + + pub(crate) fn new_resume( + randoms: ConnectionRandoms, + suite: &'static Tls12CipherSuite, + master_secret: &[u8], + ) -> Self { + let mut ret = Self { + randoms, + suite, + master_secret: [0u8; 48], + }; + ret.master_secret + .copy_from_slice(master_secret); + ret + } + + /// Make a `MessageCipherPair` based on the given supported ciphersuite `self.suite`, + /// and the session's `secrets`. + pub(crate) fn make_cipher_pair(&self, side: Side) -> MessageCipherPair { + // Make a key block, and chop it up. + // Note: we don't implement any ciphersuites with nonzero mac_key_len. + let key_block = self.make_key_block(); + let shape = self.suite.aead_alg.key_block_shape(); + + let (client_write_key, key_block) = key_block.split_at(shape.enc_key_len); + let (server_write_key, key_block) = key_block.split_at(shape.enc_key_len); + let (client_write_iv, key_block) = key_block.split_at(shape.fixed_iv_len); + let (server_write_iv, extra) = key_block.split_at(shape.fixed_iv_len); + + let (write_key, write_iv, read_key, read_iv) = match side { + Side::Client => ( + client_write_key, + client_write_iv, + server_write_key, + server_write_iv, + ), + Side::Server => ( + server_write_key, + server_write_iv, + client_write_key, + client_write_iv, + ), + }; + + ( + self.suite + .aead_alg + .decrypter(AeadKey::new(read_key), read_iv), + self.suite + .aead_alg + .encrypter(AeadKey::new(write_key), write_iv, extra), + ) + } + + fn make_key_block(&self) -> Vec<u8> { + let shape = self.suite.aead_alg.key_block_shape(); + + let len = (shape.enc_key_len + shape.fixed_iv_len) * 2 + shape.explicit_nonce_len; + + let mut out = vec![0u8; len]; + + // NOTE: opposite order to above for no good reason. + // Don't design security protocols on drugs, kids. + let randoms = join_randoms(&self.randoms.server, &self.randoms.client); + self.suite.prf_provider.for_secret( + &mut out, + &self.master_secret, + b"key expansion", + &randoms, + ); + + out + } + + pub(crate) fn suite(&self) -> &'static Tls12CipherSuite { + self.suite + } + + pub(crate) fn master_secret(&self) -> &[u8] { + &self.master_secret[..] + } + + fn make_verify_data(&self, handshake_hash: &hash::Output, label: &[u8]) -> Vec<u8> { + let mut out = vec![0u8; 12]; + + self.suite.prf_provider.for_secret( + &mut out, + &self.master_secret, + label, + handshake_hash.as_ref(), + ); + + out + } + + pub(crate) fn client_verify_data(&self, handshake_hash: &hash::Output) -> Vec<u8> { + self.make_verify_data(handshake_hash, b"client finished") + } + + pub(crate) fn server_verify_data(&self, handshake_hash: &hash::Output) -> Vec<u8> { + self.make_verify_data(handshake_hash, b"server finished") + } + + pub(crate) fn export_keying_material( + &self, + output: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) { + let mut randoms = Vec::new(); + randoms.extend_from_slice(&self.randoms.client); + randoms.extend_from_slice(&self.randoms.server); + if let Some(context) = context { + assert!(context.len() <= 0xffff); + (context.len() as u16).encode(&mut randoms); + randoms.extend_from_slice(context); + } + + self.suite + .prf_provider + .for_secret(output, &self.master_secret, label, &randoms); + } + + pub(crate) fn extract_secrets(&self, side: Side) -> Result<PartiallyExtractedSecrets, Error> { + // Make a key block, and chop it up + let key_block = self.make_key_block(); + let shape = self.suite.aead_alg.key_block_shape(); + + let (client_key, key_block) = key_block.split_at(shape.enc_key_len); + let (server_key, key_block) = key_block.split_at(shape.enc_key_len); + let (client_iv, key_block) = key_block.split_at(shape.fixed_iv_len); + let (server_iv, explicit_nonce) = key_block.split_at(shape.fixed_iv_len); + + let client_secrets = self.suite.aead_alg.extract_keys( + AeadKey::new(client_key), + client_iv, + explicit_nonce, + )?; + let server_secrets = self.suite.aead_alg.extract_keys( + AeadKey::new(server_key), + server_iv, + explicit_nonce, + )?; + + let (tx, rx) = match side { + Side::Client => (client_secrets, server_secrets), + Side::Server => (server_secrets, client_secrets), + }; + Ok(PartiallyExtractedSecrets { tx, rx }) + } +} + +impl Drop for ConnectionSecrets { + fn drop(&mut self) { + self.master_secret.zeroize(); + } +} + +enum Seed { + Ems(hash::Output), + Randoms([u8; 64]), +} + +impl AsRef<[u8]> for Seed { + /// This is guaranteed to return a non-empty slice. + fn as_ref(&self) -> &[u8] { + match self { + // seed is a hash::Output, which is a fixed, non-zero length array. + Self::Ems(seed) => seed.as_ref(), + // randoms is a fixed, non-zero length array. + Self::Randoms(randoms) => randoms.as_ref(), + } + } +} + +fn join_randoms(first: &[u8; 32], second: &[u8; 32]) -> [u8; 64] { + let mut randoms = [0u8; 64]; + randoms[..32].copy_from_slice(first); + randoms[32..].copy_from_slice(second); + randoms +} + +type MessageCipherPair = (Box<dyn MessageDecrypter>, Box<dyn MessageEncrypter>); + +pub(crate) fn decode_kx_params<'a, T: KxDecode<'a>>( + kx_algorithm: KeyExchangeAlgorithm, + common: &mut CommonState, + kx_params: &'a [u8], +) -> Result<T, Error> { + let mut rd = Reader::init(kx_params); + let kx_params = T::decode(&mut rd, kx_algorithm)?; + match rd.any_left() { + false => Ok(kx_params), + true => Err(common.send_fatal_alert( + AlertDescription::DecodeError, + InvalidMessage::InvalidDhParams, + )), + } +} + +pub(crate) const DOWNGRADE_SENTINEL: [u8; 8] = [0x44, 0x4f, 0x57, 0x4e, 0x47, 0x52, 0x44, 0x01]; + +#[cfg(test)] +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use super::provider::kx_group::X25519; + use super::*; + use crate::common_state::{CommonState, Side}; + use crate::msgs::handshake::{ServerEcdhParams, ServerKeyExchangeParams}; + + #[test] + fn server_ecdhe_remaining_bytes() { + let key = X25519.start().unwrap(); + let server_params = ServerEcdhParams::new(&*key); + let mut server_buf = Vec::new(); + server_params.encode(&mut server_buf); + server_buf.push(34); + + let mut common = CommonState::new(Side::Client); + assert!( + decode_kx_params::<ServerKeyExchangeParams>( + KeyExchangeAlgorithm::ECDHE, + &mut common, + &server_buf + ) + .is_err() + ); + } + + #[test] + fn client_ecdhe_invalid() { + let mut common = CommonState::new(Side::Server); + assert!( + decode_kx_params::<ServerKeyExchangeParams>( + KeyExchangeAlgorithm::ECDHE, + &mut common, + &[34], + ) + .is_err() + ); + } +} diff --git a/vendor/rustls/src/tls13/key_schedule.rs b/vendor/rustls/src/tls13/key_schedule.rs new file mode 100644 index 00000000..4bf04214 --- /dev/null +++ b/vendor/rustls/src/tls13/key_schedule.rs @@ -0,0 +1,1308 @@ +//! Key schedule maintenance for TLS1.3 + +use alloc::boxed::Box; +use alloc::string::ToString; +use core::ops::Deref; + +use crate::common_state::{CommonState, Side}; +use crate::crypto::cipher::{AeadKey, Iv, MessageDecrypter, Tls13AeadAlgorithm}; +use crate::crypto::tls13::{Hkdf, HkdfExpander, OkmBlock, OutputLengthError, expand}; +use crate::crypto::{SharedSecret, hash, hmac}; +use crate::error::Error; +use crate::msgs::message::Message; +use crate::suites::PartiallyExtractedSecrets; +use crate::{ConnectionTrafficSecrets, KeyLog, Tls13CipherSuite, quic}; + +// We express the state of a contained KeySchedule using these +// typestates. This means we can write code that cannot accidentally +// (e.g.) encrypt application data using a KeySchedule solely constructed +// with an empty or trivial secret, or extract the wrong kind of secrets +// at a given point. + +/// The "early secret" stage of the key schedule WITH a PSK. +/// +/// This is only useful when you need to use one of the binder +/// keys, the "client_early_traffic_secret", or +/// "early_exporter_master_secret". +/// +/// See [`KeySchedulePreHandshake`] for more information. +pub(crate) struct KeyScheduleEarly { + ks: KeySchedule, +} + +impl KeyScheduleEarly { + pub(crate) fn new(suite: &'static Tls13CipherSuite, secret: &[u8]) -> Self { + Self { + ks: KeySchedule::new(suite, secret), + } + } + + /// Computes the `client_early_traffic_secret` and writes it + /// to `common`. + /// + /// `hs_hash` is `Transcript-Hash(ClientHello)`. + /// + /// ```text + /// Derive-Secret(., "c e traffic", ClientHello) + /// = client_early_traffic_secret + /// ``` + pub(crate) fn client_early_traffic_secret( + &self, + hs_hash: &hash::Output, + key_log: &dyn KeyLog, + client_random: &[u8; 32], + common: &mut CommonState, + ) { + let client_early_traffic_secret = self.ks.derive_logged_secret( + SecretKind::ClientEarlyTrafficSecret, + hs_hash.as_ref(), + key_log, + client_random, + ); + + match common.side { + Side::Client => self + .ks + .set_encrypter(&client_early_traffic_secret, common), + Side::Server => self + .ks + .set_decrypter(&client_early_traffic_secret, common), + } + + if common.is_quic() { + // If 0-RTT should be rejected, this will be clobbered by ExtensionProcessing + // before the application can see. + common.quic.early_secret = Some(client_early_traffic_secret); + } + } + + pub(crate) fn resumption_psk_binder_key_and_sign_verify_data( + &self, + hs_hash: &hash::Output, + ) -> hmac::Tag { + let resumption_psk_binder_key = self + .ks + .derive_for_empty_hash(SecretKind::ResumptionPskBinderKey); + self.ks + .sign_verify_data(&resumption_psk_binder_key, hs_hash) + } +} + +/// The "early secret" stage of the key schedule. +/// +/// Call [`KeySchedulePreHandshake::new`] to create it without +/// a PSK, or use [`From<KeyScheduleEarly>`] to create it with +/// a PSK. +/// +/// ```text +/// 0 +/// | +/// v +/// PSK -> HKDF-Extract = Early Secret +/// | +/// +-----> Derive-Secret(., "ext binder" | "res binder", "") +/// | = binder_key +/// | +/// +-----> Derive-Secret(., "c e traffic", ClientHello) +/// | = client_early_traffic_secret +/// | +/// +-----> Derive-Secret(., "e exp master", ClientHello) +/// | = early_exporter_master_secret +/// v +/// Derive-Secret(., "derived", "") +/// ``` +pub(crate) struct KeySchedulePreHandshake { + ks: KeySchedule, +} + +impl KeySchedulePreHandshake { + /// Creates a key schedule without a PSK. + pub(crate) fn new(suite: &'static Tls13CipherSuite) -> Self { + Self { + ks: KeySchedule::new_with_empty_secret(suite), + } + } + + /// `shared_secret` is the "(EC)DHE" secret input to + /// "HKDF-Extract": + /// + /// ```text + /// (EC)DHE -> HKDF-Extract = Handshake Secret + /// ``` + pub(crate) fn into_handshake( + mut self, + shared_secret: SharedSecret, + ) -> KeyScheduleHandshakeStart { + self.ks + .input_secret(shared_secret.secret_bytes()); + KeyScheduleHandshakeStart { ks: self.ks } + } +} + +/// Creates a key schedule with a PSK. +impl From<KeyScheduleEarly> for KeySchedulePreHandshake { + fn from(KeyScheduleEarly { ks }: KeyScheduleEarly) -> Self { + Self { ks } + } +} + +/// KeySchedule during handshake. +/// +/// Created by [`KeySchedulePreHandshake`]. +pub(crate) struct KeyScheduleHandshakeStart { + ks: KeySchedule, +} + +impl KeyScheduleHandshakeStart { + pub(crate) fn derive_client_handshake_secrets( + mut self, + early_data_enabled: bool, + hs_hash: hash::Output, + suite: &'static Tls13CipherSuite, + key_log: &dyn KeyLog, + client_random: &[u8; 32], + common: &mut CommonState, + ) -> KeyScheduleHandshake { + debug_assert_eq!(common.side, Side::Client); + // Suite might have changed due to resumption + self.ks.inner = suite.into(); + let new = self.into_handshake(hs_hash, key_log, client_random, common); + + // Decrypt with the peer's key, encrypt with our own key + new.ks + .set_decrypter(&new.server_handshake_traffic_secret, common); + + if !early_data_enabled { + // Set the client encryption key for handshakes if early data is not used + new.ks + .set_encrypter(&new.client_handshake_traffic_secret, common); + } + + new + } + + pub(crate) fn derive_server_handshake_secrets( + self, + hs_hash: hash::Output, + key_log: &dyn KeyLog, + client_random: &[u8; 32], + common: &mut CommonState, + ) -> KeyScheduleHandshake { + debug_assert_eq!(common.side, Side::Server); + let new = self.into_handshake(hs_hash, key_log, client_random, common); + + // Set up to encrypt with handshake secrets, but decrypt with early_data keys. + // If not doing early_data after all, this is corrected later to the handshake + // keys (now stored in key_schedule). + new.ks + .set_encrypter(&new.server_handshake_traffic_secret, common); + new + } + + pub(crate) fn server_ech_confirmation_secret( + &mut self, + client_hello_inner_random: &[u8], + hs_hash: hash::Output, + ) -> [u8; 8] { + /* + Per ietf-tls-esni-17 section 7.2: + <https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-17#section-7.2> + accept_confirmation = HKDF-Expand-Label( + HKDF-Extract(0, ClientHelloInner.random), + "ech accept confirmation", + transcript_ech_conf,8) + */ + hkdf_expand_label( + self.ks + .suite + .hkdf_provider + .extract_from_secret(None, client_hello_inner_random) + .as_ref(), + SecretKind::ServerEchConfirmationSecret.to_bytes(), + hs_hash.as_ref(), + ) + } + + fn into_handshake( + self, + hs_hash: hash::Output, + key_log: &dyn KeyLog, + client_random: &[u8; 32], + common: &mut CommonState, + ) -> KeyScheduleHandshake { + // Use an empty handshake hash for the initial handshake. + let client_secret = self.ks.derive_logged_secret( + SecretKind::ClientHandshakeTrafficSecret, + hs_hash.as_ref(), + key_log, + client_random, + ); + + let server_secret = self.ks.derive_logged_secret( + SecretKind::ServerHandshakeTrafficSecret, + hs_hash.as_ref(), + key_log, + client_random, + ); + + if common.is_quic() { + common.quic.hs_secrets = Some(quic::Secrets::new( + client_secret.clone(), + server_secret.clone(), + self.ks.suite, + self.ks.suite.quic.unwrap(), + common.side, + common.quic.version, + )); + } + + KeyScheduleHandshake { + ks: self.ks, + client_handshake_traffic_secret: client_secret, + server_handshake_traffic_secret: server_secret, + } + } +} + +pub(crate) struct KeyScheduleHandshake { + ks: KeySchedule, + client_handshake_traffic_secret: OkmBlock, + server_handshake_traffic_secret: OkmBlock, +} + +impl KeyScheduleHandshake { + pub(crate) fn sign_server_finish(&self, hs_hash: &hash::Output) -> hmac::Tag { + self.ks + .sign_finish(&self.server_handshake_traffic_secret, hs_hash) + } + + pub(crate) fn set_handshake_encrypter(&self, common: &mut CommonState) { + debug_assert_eq!(common.side, Side::Client); + self.ks + .set_encrypter(&self.client_handshake_traffic_secret, common); + } + + pub(crate) fn set_handshake_decrypter( + &self, + skip_requested: Option<usize>, + common: &mut CommonState, + ) { + debug_assert_eq!(common.side, Side::Server); + let secret = &self.client_handshake_traffic_secret; + match skip_requested { + None => self.ks.set_decrypter(secret, common), + Some(max_early_data_size) => common + .record_layer + .set_message_decrypter_with_trial_decryption( + self.ks + .derive_decrypter(&self.client_handshake_traffic_secret), + max_early_data_size, + ), + } + } + + pub(crate) fn into_traffic_with_client_finished_pending( + self, + hs_hash: hash::Output, + key_log: &dyn KeyLog, + client_random: &[u8; 32], + common: &mut CommonState, + ) -> KeyScheduleTrafficWithClientFinishedPending { + debug_assert_eq!(common.side, Side::Server); + + let before_finished = + KeyScheduleBeforeFinished::new(self.ks, hs_hash, key_log, client_random); + let (_client_secret, server_secret) = ( + &before_finished.current_client_traffic_secret, + &before_finished.current_server_traffic_secret, + ); + + before_finished + .ks + .set_encrypter(server_secret, common); + + if common.is_quic() { + common.quic.traffic_secrets = Some(quic::Secrets::new( + _client_secret.clone(), + server_secret.clone(), + before_finished.ks.suite, + before_finished.ks.suite.quic.unwrap(), + common.side, + common.quic.version, + )); + } + + KeyScheduleTrafficWithClientFinishedPending { + handshake_client_traffic_secret: self.client_handshake_traffic_secret, + before_finished, + } + } + + pub(crate) fn into_pre_finished_client_traffic( + self, + pre_finished_hash: hash::Output, + handshake_hash: hash::Output, + key_log: &dyn KeyLog, + client_random: &[u8; 32], + ) -> (KeyScheduleClientBeforeFinished, hmac::Tag) { + let before_finished = + KeyScheduleBeforeFinished::new(self.ks, pre_finished_hash, key_log, client_random); + let tag = before_finished + .ks + .sign_finish(&self.client_handshake_traffic_secret, &handshake_hash); + (KeyScheduleClientBeforeFinished(before_finished), tag) + } +} + +/// Keys derived (but not installed) before client's Finished message. +pub(crate) struct KeyScheduleBeforeFinished { + ks: KeySchedule, + current_client_traffic_secret: OkmBlock, + current_server_traffic_secret: OkmBlock, + current_exporter_secret: OkmBlock, +} + +impl KeyScheduleBeforeFinished { + fn new( + mut ks: KeySchedule, + hs_hash: hash::Output, + key_log: &dyn KeyLog, + client_random: &[u8; 32], + ) -> Self { + ks.input_empty(); + + let current_client_traffic_secret = ks.derive_logged_secret( + SecretKind::ClientApplicationTrafficSecret, + hs_hash.as_ref(), + key_log, + client_random, + ); + + let current_server_traffic_secret = ks.derive_logged_secret( + SecretKind::ServerApplicationTrafficSecret, + hs_hash.as_ref(), + key_log, + client_random, + ); + + let current_exporter_secret = ks.derive_logged_secret( + SecretKind::ExporterMasterSecret, + hs_hash.as_ref(), + key_log, + client_random, + ); + + Self { + ks, + current_client_traffic_secret, + current_server_traffic_secret, + current_exporter_secret, + } + } + + pub(crate) fn into_traffic( + self, + hs_hash: hash::Output, + ) -> (KeyScheduleTraffic, KeyScheduleResumption) { + let Self { + ks, + current_client_traffic_secret, + current_server_traffic_secret, + current_exporter_secret, + } = self; + + let resumption_master_secret = + ks.derive(SecretKind::ResumptionMasterSecret, hs_hash.as_ref()); + + ( + KeyScheduleTraffic { + ks: ks.inner, + current_client_traffic_secret, + current_server_traffic_secret, + current_exporter_secret, + }, + KeyScheduleResumption { + ks: ks.inner, + resumption_master_secret, + }, + ) + } +} + +/// Client-side key schedule before the finished message is sent. +/// +/// This differs from `KeyScheduleTrafficWithClientFinishedPending` because +/// none of the final traffic secrets are installed yet. After the finished +/// message is sent, `into_traffic()` does that. +pub(crate) struct KeyScheduleClientBeforeFinished(KeyScheduleBeforeFinished); + +impl KeyScheduleClientBeforeFinished { + pub(crate) fn into_traffic( + self, + common: &mut CommonState, + hs_hash: hash::Output, + ) -> (KeyScheduleTraffic, KeyScheduleResumption) { + let next = self.0; + + debug_assert_eq!(common.side, Side::Client); + let (client_secret, server_secret) = ( + &next.current_client_traffic_secret, + &next.current_server_traffic_secret, + ); + + next.ks + .set_decrypter(server_secret, common); + next.ks + .set_encrypter(client_secret, common); + + if common.is_quic() { + common.quic.traffic_secrets = Some(quic::Secrets::new( + client_secret.clone(), + server_secret.clone(), + next.ks.suite, + next.ks.suite.quic.unwrap(), + common.side, + common.quic.version, + )); + } + + next.into_traffic(hs_hash) + } +} + +/// KeySchedule during traffic stage, retaining the ability to calculate the client's +/// finished verify_data. The traffic stage key schedule can be extracted from it +/// through signing the client finished hash. +pub(crate) struct KeyScheduleTrafficWithClientFinishedPending { + handshake_client_traffic_secret: OkmBlock, + before_finished: KeyScheduleBeforeFinished, +} + +impl KeyScheduleTrafficWithClientFinishedPending { + pub(crate) fn update_decrypter(&self, common: &mut CommonState) { + debug_assert_eq!(common.side, Side::Server); + self.before_finished + .ks + .set_decrypter(&self.handshake_client_traffic_secret, common); + } + + pub(crate) fn sign_client_finish( + self, + hs_hash: &hash::Output, + common: &mut CommonState, + ) -> (KeyScheduleBeforeFinished, hmac::Tag) { + debug_assert_eq!(common.side, Side::Server); + let tag = self + .before_finished + .ks + .sign_finish(&self.handshake_client_traffic_secret, hs_hash); + + // Install keying to read future messages. + self.before_finished.ks.set_decrypter( + &self + .before_finished + .current_client_traffic_secret, + common, + ); + + (self.before_finished, tag) + } +} + +/// KeySchedule during traffic stage. All traffic & exporter keys are guaranteed +/// to be available. +pub(crate) struct KeyScheduleTraffic { + ks: KeyScheduleSuite, + current_client_traffic_secret: OkmBlock, + current_server_traffic_secret: OkmBlock, + current_exporter_secret: OkmBlock, +} + +impl KeyScheduleTraffic { + pub(crate) fn update_encrypter_and_notify(&mut self, common: &mut CommonState) { + let secret = self.next_application_traffic_secret(common.side); + common.enqueue_key_update_notification(); + self.ks.set_encrypter(&secret, common); + } + + pub(crate) fn request_key_update_and_update_encrypter( + &mut self, + common: &mut CommonState, + ) -> Result<(), Error> { + common.check_aligned_handshake()?; + common.send_msg_encrypt(Message::build_key_update_request().into()); + let secret = self.next_application_traffic_secret(common.side); + self.ks.set_encrypter(&secret, common); + Ok(()) + } + + pub(crate) fn update_decrypter(&mut self, common: &mut CommonState) { + let secret = self.next_application_traffic_secret(common.side.peer()); + self.ks.set_decrypter(&secret, common); + } + + pub(crate) fn next_application_traffic_secret(&mut self, side: Side) -> OkmBlock { + let current = match side { + Side::Client => &mut self.current_client_traffic_secret, + Side::Server => &mut self.current_server_traffic_secret, + }; + + let secret = self.ks.derive_next(current); + *current = secret.clone(); + secret + } + + pub(crate) fn export_keying_material( + &self, + out: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) -> Result<(), Error> { + self.ks + .export_keying_material(&self.current_exporter_secret, out, label, context) + } + + pub(crate) fn refresh_traffic_secret( + &mut self, + side: Side, + ) -> Result<ConnectionTrafficSecrets, Error> { + let secret = self.next_application_traffic_secret(side); + let (key, iv) = expand_secret( + &secret, + self.ks.suite.hkdf_provider, + self.ks.suite.aead_alg.key_len(), + ); + Ok(self + .ks + .suite + .aead_alg + .extract_keys(key, iv)?) + } + + pub(crate) fn extract_secrets(&self, side: Side) -> Result<PartiallyExtractedSecrets, Error> { + let (client_key, client_iv) = expand_secret( + &self.current_client_traffic_secret, + self.ks.suite.hkdf_provider, + self.ks.suite.aead_alg.key_len(), + ); + let (server_key, server_iv) = expand_secret( + &self.current_server_traffic_secret, + self.ks.suite.hkdf_provider, + self.ks.suite.aead_alg.key_len(), + ); + let client_secrets = self + .ks + .suite + .aead_alg + .extract_keys(client_key, client_iv)?; + let server_secrets = self + .ks + .suite + .aead_alg + .extract_keys(server_key, server_iv)?; + + let (tx, rx) = match side { + Side::Client => (client_secrets, server_secrets), + Side::Server => (server_secrets, client_secrets), + }; + Ok(PartiallyExtractedSecrets { tx, rx }) + } +} + +pub(crate) struct KeyScheduleResumption { + ks: KeyScheduleSuite, + resumption_master_secret: OkmBlock, +} + +impl KeyScheduleResumption { + pub(crate) fn derive_ticket_psk(&self, nonce: &[u8]) -> OkmBlock { + self.ks + .derive_ticket_psk(&self.resumption_master_secret, nonce) + } +} + +fn expand_secret(secret: &OkmBlock, hkdf: &'static dyn Hkdf, aead_key_len: usize) -> (AeadKey, Iv) { + let expander = hkdf.expander_for_okm(secret); + + ( + hkdf_expand_label_aead_key(expander.as_ref(), aead_key_len, b"key", &[]), + hkdf_expand_label(expander.as_ref(), b"iv", &[]), + ) +} + +/// This is the TLS1.3 key schedule. It stores the current secret and +/// the type of hash. This isn't used directly; but only through the +/// typestates. +struct KeySchedule { + current: Box<dyn HkdfExpander>, + inner: KeyScheduleSuite, +} + +impl KeySchedule { + fn new(suite: &'static Tls13CipherSuite, secret: &[u8]) -> Self { + Self { + current: suite + .hkdf_provider + .extract_from_secret(None, secret), + inner: suite.into(), + } + } + + /// Creates a key schedule without a PSK. + fn new_with_empty_secret(suite: &'static Tls13CipherSuite) -> Self { + Self { + current: suite + .hkdf_provider + .extract_from_zero_ikm(None), + inner: suite.into(), + } + } + + /// Input the empty secret. + /// + /// RFC 8446: "If a given secret is not available, then the + /// 0-value consisting of a string of Hash.length bytes set + /// to zeros is used." + fn input_empty(&mut self) { + let salt = self.derive_for_empty_hash(SecretKind::DerivedSecret); + self.current = self + .suite + .hkdf_provider + .extract_from_zero_ikm(Some(salt.as_ref())); + } + + /// Input the given secret. + fn input_secret(&mut self, secret: &[u8]) { + let salt = self.derive_for_empty_hash(SecretKind::DerivedSecret); + self.current = self + .suite + .hkdf_provider + .extract_from_secret(Some(salt.as_ref()), secret); + } + + /// Derive a secret of given `kind`, using current handshake hash `hs_hash`. + /// + /// More specifically + /// ```text + /// Derive-Secret(., "derived", Messages) + /// ``` + /// where `hs_hash` is `Messages`. + fn derive(&self, kind: SecretKind, hs_hash: &[u8]) -> OkmBlock { + hkdf_expand_label_block(self.current.as_ref(), kind.to_bytes(), hs_hash) + } + + fn derive_logged_secret( + &self, + kind: SecretKind, + hs_hash: &[u8], + key_log: &dyn KeyLog, + client_random: &[u8; 32], + ) -> OkmBlock { + let output = self.derive(kind, hs_hash); + + let log_label = kind + .log_label() + .expect("not a loggable secret"); + if key_log.will_log(log_label) { + key_log.log(log_label, client_random, output.as_ref()); + } + output + } + + /// Derive a secret of given `kind` using the hash of the empty string + /// for the handshake hash. + /// + /// More specifically: + /// ```text + /// Derive-Secret(., Label, "") + /// ``` + /// where `kind` is `Label`. + /// + /// Useful only for the following `SecretKind`s: + /// - `SecretKind::ExternalPskBinderKey` + /// - `SecretKind::ResumptionPSKBinderKey` + /// - `SecretKind::DerivedSecret` + fn derive_for_empty_hash(&self, kind: SecretKind) -> OkmBlock { + let hp = self.suite.common.hash_provider; + let empty_hash = hp + .algorithm() + .hash_for_empty_input() + .unwrap_or_else(|| hp.start().finish()); + self.derive(kind, empty_hash.as_ref()) + } +} + +impl Deref for KeySchedule { + type Target = KeyScheduleSuite; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +/// This is a component part of `KeySchedule`, and groups operations +/// that do not depend on the root key schedule secret. +#[derive(Clone, Copy)] +struct KeyScheduleSuite { + suite: &'static Tls13CipherSuite, +} + +impl KeyScheduleSuite { + fn set_encrypter(&self, secret: &OkmBlock, common: &mut CommonState) { + let expander = self + .suite + .hkdf_provider + .expander_for_okm(secret); + let key = derive_traffic_key(expander.as_ref(), self.suite.aead_alg); + let iv = derive_traffic_iv(expander.as_ref()); + + common + .record_layer + .set_message_encrypter( + self.suite.aead_alg.encrypter(key, iv), + self.suite.common.confidentiality_limit, + ); + } + + fn set_decrypter(&self, secret: &OkmBlock, common: &mut CommonState) { + common + .record_layer + .set_message_decrypter(self.derive_decrypter(secret)); + } + + fn derive_decrypter(&self, secret: &OkmBlock) -> Box<dyn MessageDecrypter> { + let expander = self + .suite + .hkdf_provider + .expander_for_okm(secret); + let key = derive_traffic_key(expander.as_ref(), self.suite.aead_alg); + let iv = derive_traffic_iv(expander.as_ref()); + self.suite.aead_alg.decrypter(key, iv) + } + + /// Sign the finished message consisting of `hs_hash` using a current + /// traffic secret. + /// + /// See RFC 8446 section 4.4.4. + fn sign_finish(&self, base_key: &OkmBlock, hs_hash: &hash::Output) -> hmac::Tag { + self.sign_verify_data(base_key, hs_hash) + } + + /// Sign the finished message consisting of `hs_hash` using the key material + /// `base_key`. + /// + /// See RFC 8446 section 4.4.4. + fn sign_verify_data(&self, base_key: &OkmBlock, hs_hash: &hash::Output) -> hmac::Tag { + let expander = self + .suite + .hkdf_provider + .expander_for_okm(base_key); + let hmac_key = hkdf_expand_label_block(expander.as_ref(), b"finished", &[]); + + self.suite + .hkdf_provider + .hmac_sign(&hmac_key, hs_hash.as_ref()) + } + + /// Derive the next application traffic secret, returning it. + fn derive_next(&self, base_key: &OkmBlock) -> OkmBlock { + let expander = self + .suite + .hkdf_provider + .expander_for_okm(base_key); + hkdf_expand_label_block(expander.as_ref(), b"traffic upd", &[]) + } + + /// Derive the PSK to use given a resumption_master_secret and + /// ticket_nonce. + fn derive_ticket_psk(&self, rms: &OkmBlock, nonce: &[u8]) -> OkmBlock { + let expander = self + .suite + .hkdf_provider + .expander_for_okm(rms); + hkdf_expand_label_block(expander.as_ref(), b"resumption", nonce) + } + + fn export_keying_material( + &self, + current_exporter_secret: &OkmBlock, + out: &mut [u8], + label: &[u8], + context: Option<&[u8]>, + ) -> Result<(), Error> { + let secret = { + let h_empty = self + .suite + .common + .hash_provider + .hash(&[]); + + let expander = self + .suite + .hkdf_provider + .expander_for_okm(current_exporter_secret); + hkdf_expand_label_block(expander.as_ref(), label, h_empty.as_ref()) + }; + + let h_context = self + .suite + .common + .hash_provider + .hash(context.unwrap_or(&[])); + + let expander = self + .suite + .hkdf_provider + .expander_for_okm(&secret); + hkdf_expand_label_slice(expander.as_ref(), b"exporter", h_context.as_ref(), out) + .map_err(|_| Error::General("exporting too much".to_string())) + } +} + +impl From<&'static Tls13CipherSuite> for KeyScheduleSuite { + fn from(suite: &'static Tls13CipherSuite) -> Self { + Self { suite } + } +} + +/// [HKDF-Expand-Label] where the output is an AEAD key. +/// +/// [HKDF-Expand-Label]: <https://www.rfc-editor.org/rfc/rfc8446#section-7.1> +pub fn derive_traffic_key( + expander: &dyn HkdfExpander, + aead_alg: &dyn Tls13AeadAlgorithm, +) -> AeadKey { + hkdf_expand_label_aead_key(expander, aead_alg.key_len(), b"key", &[]) +} + +/// [HKDF-Expand-Label] where the output is an IV. +/// +/// [HKDF-Expand-Label]: <https://www.rfc-editor.org/rfc/rfc8446#section-7.1> +pub fn derive_traffic_iv(expander: &dyn HkdfExpander) -> Iv { + hkdf_expand_label(expander, b"iv", &[]) +} + +/// [HKDF-Expand-Label] where the output length is a compile-time constant, and therefore +/// it is infallible. +/// +/// [HKDF-Expand-Label]: <https://www.rfc-editor.org/rfc/rfc8446#section-7.1> +pub(crate) fn hkdf_expand_label<T: From<[u8; N]>, const N: usize>( + expander: &dyn HkdfExpander, + label: &[u8], + context: &[u8], +) -> T { + hkdf_expand_label_inner(expander, label, context, N, |e, info| expand(e, info)) +} + +/// [HKDF-Expand-Label] where the output is one block in size. +pub(crate) fn hkdf_expand_label_block( + expander: &dyn HkdfExpander, + label: &[u8], + context: &[u8], +) -> OkmBlock { + hkdf_expand_label_inner(expander, label, context, expander.hash_len(), |e, info| { + e.expand_block(info) + }) +} + +/// [HKDF-Expand-Label] where the output is an AEAD key. +pub(crate) fn hkdf_expand_label_aead_key( + expander: &dyn HkdfExpander, + key_len: usize, + label: &[u8], + context: &[u8], +) -> AeadKey { + hkdf_expand_label_inner(expander, label, context, key_len, |e, info| { + let key: AeadKey = expand(e, info); + key.with_length(key_len) + }) +} + +/// [HKDF-Expand-Label] where the output is a slice. +/// +/// This can fail because HKDF-Expand is limited in its maximum output length. +fn hkdf_expand_label_slice( + expander: &dyn HkdfExpander, + label: &[u8], + context: &[u8], + output: &mut [u8], +) -> Result<(), OutputLengthError> { + hkdf_expand_label_inner(expander, label, context, output.len(), |e, info| { + e.expand_slice(info, output) + }) +} + +pub(crate) fn server_ech_hrr_confirmation_secret( + hkdf_provider: &'static dyn Hkdf, + client_hello_inner_random: &[u8], + hs_hash: hash::Output, +) -> [u8; 8] { + /* + Per ietf-tls-esni-17 section 7.2.1: + <https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-17#section-7.2.1> + hrr_accept_confirmation = HKDF-Expand-Label( + HKDF-Extract(0, ClientHelloInner1.random), + "hrr ech accept confirmation", + transcript_hrr_ech_conf, + 8) + */ + hkdf_expand_label( + hkdf_provider + .extract_from_secret(None, client_hello_inner_random) + .as_ref(), + SecretKind::ServerEchHrrConfirmationSecret.to_bytes(), + hs_hash.as_ref(), + ) +} + +fn hkdf_expand_label_inner<F, T>( + expander: &dyn HkdfExpander, + label: &[u8], + context: &[u8], + n: usize, + f: F, +) -> T +where + F: FnOnce(&dyn HkdfExpander, &[&[u8]]) -> T, +{ + const LABEL_PREFIX: &[u8] = b"tls13 "; + + let output_len = u16::to_be_bytes(n as u16); + let label_len = u8::to_be_bytes((LABEL_PREFIX.len() + label.len()) as u8); + let context_len = u8::to_be_bytes(context.len() as u8); + + let info = &[ + &output_len[..], + &label_len[..], + LABEL_PREFIX, + label, + &context_len[..], + context, + ]; + + f(expander, info) +} + +/// The kinds of secret we can extract from `KeySchedule`. +#[derive(Debug, Clone, Copy, PartialEq)] +enum SecretKind { + ResumptionPskBinderKey, + ClientEarlyTrafficSecret, + ClientHandshakeTrafficSecret, + ServerHandshakeTrafficSecret, + ClientApplicationTrafficSecret, + ServerApplicationTrafficSecret, + ExporterMasterSecret, + ResumptionMasterSecret, + DerivedSecret, + ServerEchConfirmationSecret, + ServerEchHrrConfirmationSecret, +} + +impl SecretKind { + fn to_bytes(self) -> &'static [u8] { + use self::SecretKind::*; + match self { + ResumptionPskBinderKey => b"res binder", + ClientEarlyTrafficSecret => b"c e traffic", + ClientHandshakeTrafficSecret => b"c hs traffic", + ServerHandshakeTrafficSecret => b"s hs traffic", + ClientApplicationTrafficSecret => b"c ap traffic", + ServerApplicationTrafficSecret => b"s ap traffic", + ExporterMasterSecret => b"exp master", + ResumptionMasterSecret => b"res master", + DerivedSecret => b"derived", + // https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#section-7.2 + ServerEchConfirmationSecret => b"ech accept confirmation", + // https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-18#section-7.2.1 + ServerEchHrrConfirmationSecret => b"hrr ech accept confirmation", + } + } + + fn log_label(self) -> Option<&'static str> { + use self::SecretKind::*; + Some(match self { + ClientEarlyTrafficSecret => "CLIENT_EARLY_TRAFFIC_SECRET", + ClientHandshakeTrafficSecret => "CLIENT_HANDSHAKE_TRAFFIC_SECRET", + ServerHandshakeTrafficSecret => "SERVER_HANDSHAKE_TRAFFIC_SECRET", + ClientApplicationTrafficSecret => "CLIENT_TRAFFIC_SECRET_0", + ServerApplicationTrafficSecret => "SERVER_TRAFFIC_SECRET_0", + ExporterMasterSecret => "EXPORTER_SECRET", + _ => { + return None; + } + }) + } +} + +#[cfg(test)] +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use core::fmt::Debug; + use std::prelude::v1::*; + use std::vec; + + use super::provider::ring_like::aead; + use super::provider::tls13::{ + TLS13_AES_128_GCM_SHA256_INTERNAL, TLS13_CHACHA20_POLY1305_SHA256_INTERNAL, + }; + use super::{KeySchedule, SecretKind, derive_traffic_iv, derive_traffic_key}; + use crate::KeyLog; + use crate::msgs::enums::HashAlgorithm; + + #[test] + fn empty_hash() { + let sha256 = super::provider::tls13::TLS13_AES_128_GCM_SHA256 + .tls13() + .unwrap() + .common + .hash_provider; + let sha384 = super::provider::tls13::TLS13_AES_256_GCM_SHA384 + .tls13() + .unwrap() + .common + .hash_provider; + + assert!( + sha256.start().finish().as_ref() + == HashAlgorithm::SHA256 + .hash_for_empty_input() + .unwrap() + .as_ref() + ); + assert!( + sha384.start().finish().as_ref() + == HashAlgorithm::SHA384 + .hash_for_empty_input() + .unwrap() + .as_ref() + ); + + // a theoretical example of unsupported hash + assert!( + HashAlgorithm::SHA1 + .hash_for_empty_input() + .is_none() + ); + } + + #[test] + fn test_vectors() { + /* These test vectors generated with OpenSSL. */ + let hs_start_hash = [ + 0xec, 0x14, 0x7a, 0x06, 0xde, 0xa3, 0xc8, 0x84, 0x6c, 0x02, 0xb2, 0x23, 0x8e, 0x41, + 0xbd, 0xdc, 0x9d, 0x89, 0xf9, 0xae, 0xa1, 0x7b, 0x5e, 0xfd, 0x4d, 0x74, 0x82, 0xaf, + 0x75, 0x88, 0x1c, 0x0a, + ]; + + let hs_full_hash = [ + 0x75, 0x1a, 0x3d, 0x4a, 0x14, 0xdf, 0xab, 0xeb, 0x68, 0xe9, 0x2c, 0xa5, 0x91, 0x8e, + 0x24, 0x08, 0xb9, 0xbc, 0xb0, 0x74, 0x89, 0x82, 0xec, 0x9c, 0x32, 0x30, 0xac, 0x30, + 0xbb, 0xeb, 0x23, 0xe2, + ]; + + let ecdhe_secret = [ + 0xe7, 0xb8, 0xfe, 0xf8, 0x90, 0x3b, 0x52, 0x0c, 0xb9, 0xa1, 0x89, 0x71, 0xb6, 0x9d, + 0xd4, 0x5d, 0xca, 0x53, 0xce, 0x2f, 0x12, 0xbf, 0x3b, 0xef, 0x93, 0x15, 0xe3, 0x12, + 0x71, 0xdf, 0x4b, 0x40, + ]; + + let client_hts = [ + 0x61, 0x7b, 0x35, 0x07, 0x6b, 0x9d, 0x0e, 0x08, 0xcf, 0x73, 0x1d, 0x94, 0xa8, 0x66, + 0x14, 0x78, 0x41, 0x09, 0xef, 0x25, 0x55, 0x51, 0x92, 0x1d, 0xd4, 0x6e, 0x04, 0x01, + 0x35, 0xcf, 0x46, 0xab, + ]; + + let client_hts_key = [ + 0x62, 0xd0, 0xdd, 0x00, 0xf6, 0x96, 0x19, 0xd3, 0xb8, 0x19, 0x3a, 0xb4, 0xa0, 0x95, + 0x85, 0xa7, + ]; + + let client_hts_iv = [ + 0xff, 0xf7, 0x5d, 0xf5, 0xad, 0x35, 0xd5, 0xcb, 0x3c, 0x53, 0xf3, 0xa9, + ]; + + let server_hts = [ + 0xfc, 0xf7, 0xdf, 0xe6, 0x4f, 0xa2, 0xc0, 0x4f, 0x62, 0x35, 0x38, 0x7f, 0x43, 0x4e, + 0x01, 0x42, 0x23, 0x36, 0xd9, 0xc0, 0x39, 0xde, 0x68, 0x47, 0xa0, 0xb9, 0xdd, 0xcf, + 0x29, 0xa8, 0x87, 0x59, + ]; + + let server_hts_key = [ + 0x04, 0x67, 0xf3, 0x16, 0xa8, 0x05, 0xb8, 0xc4, 0x97, 0xee, 0x67, 0x04, 0x7b, 0xbc, + 0xbc, 0x54, + ]; + + let server_hts_iv = [ + 0xde, 0x83, 0xa7, 0x3e, 0x9d, 0x81, 0x4b, 0x04, 0xc4, 0x8b, 0x78, 0x09, + ]; + + let client_ats = [ + 0xc1, 0x4a, 0x6d, 0x79, 0x76, 0xd8, 0x10, 0x2b, 0x5a, 0x0c, 0x99, 0x51, 0x49, 0x3f, + 0xee, 0x87, 0xdc, 0xaf, 0xf8, 0x2c, 0x24, 0xca, 0xb2, 0x14, 0xe8, 0xbe, 0x71, 0xa8, + 0x20, 0x6d, 0xbd, 0xa5, + ]; + + let client_ats_key = [ + 0xcc, 0x9f, 0x5f, 0x98, 0x0b, 0x5f, 0x10, 0x30, 0x6c, 0xba, 0xd7, 0xbe, 0x98, 0xd7, + 0x57, 0x2e, + ]; + + let client_ats_iv = [ + 0xb8, 0x09, 0x29, 0xe8, 0xd0, 0x2c, 0x70, 0xf6, 0x11, 0x62, 0xed, 0x6b, + ]; + + let server_ats = [ + 0x2c, 0x90, 0x77, 0x38, 0xd3, 0xf8, 0x37, 0x02, 0xd1, 0xe4, 0x59, 0x8f, 0x48, 0x48, + 0x53, 0x1d, 0x9f, 0x93, 0x65, 0x49, 0x1b, 0x9f, 0x7f, 0x52, 0xc8, 0x22, 0x29, 0x0d, + 0x4c, 0x23, 0x21, 0x92, + ]; + + let server_ats_key = [ + 0x0c, 0xb2, 0x95, 0x62, 0xd8, 0xd8, 0x8f, 0x48, 0xb0, 0x2c, 0xbf, 0xbe, 0xd7, 0xe6, + 0x2b, 0xb3, + ]; + + let server_ats_iv = [ + 0x0d, 0xb2, 0x8f, 0x98, 0x85, 0x86, 0xa1, 0xb7, 0xe4, 0xd5, 0xc6, 0x9c, + ]; + + let mut ks = KeySchedule::new_with_empty_secret(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL); + ks.input_secret(&ecdhe_secret); + + assert_traffic_secret( + &ks, + SecretKind::ClientHandshakeTrafficSecret, + &hs_start_hash, + &client_hts, + &client_hts_key, + &client_hts_iv, + ); + + assert_traffic_secret( + &ks, + SecretKind::ServerHandshakeTrafficSecret, + &hs_start_hash, + &server_hts, + &server_hts_key, + &server_hts_iv, + ); + + ks.input_empty(); + + assert_traffic_secret( + &ks, + SecretKind::ClientApplicationTrafficSecret, + &hs_full_hash, + &client_ats, + &client_ats_key, + &client_ats_iv, + ); + + assert_traffic_secret( + &ks, + SecretKind::ServerApplicationTrafficSecret, + &hs_full_hash, + &server_ats, + &server_ats_key, + &server_ats_iv, + ); + } + + fn assert_traffic_secret( + ks: &KeySchedule, + kind: SecretKind, + hash: &[u8], + expected_traffic_secret: &[u8], + expected_key: &[u8], + expected_iv: &[u8], + ) { + #[derive(Debug)] + struct Log<'a>(&'a [u8]); + impl KeyLog for Log<'_> { + fn log(&self, _label: &str, _client_random: &[u8], secret: &[u8]) { + assert_eq!(self.0, secret); + } + } + let log = Log(expected_traffic_secret); + let traffic_secret = ks.derive_logged_secret(kind, hash, &log, &[0; 32]); + + // Since we can't test key equality, we test the output of sealing with the key instead. + let aead_alg = &aead::AES_128_GCM; + let expander = TLS13_AES_128_GCM_SHA256_INTERNAL + .hkdf_provider + .expander_for_okm(&traffic_secret); + let key = derive_traffic_key( + expander.as_ref(), + TLS13_AES_128_GCM_SHA256_INTERNAL.aead_alg, + ); + let key = aead::UnboundKey::new(aead_alg, key.as_ref()).unwrap(); + let seal_output = seal_zeroes(key); + let expected_key = aead::UnboundKey::new(aead_alg, expected_key).unwrap(); + let expected_seal_output = seal_zeroes(expected_key); + assert_eq!(seal_output, expected_seal_output); + assert!(seal_output.len() >= 48); // Sanity check. + + let iv = derive_traffic_iv(expander.as_ref()); + assert_eq!(iv.as_ref(), expected_iv); + } + + fn seal_zeroes(key: aead::UnboundKey) -> Vec<u8> { + let key = aead::LessSafeKey::new(key); + let mut seal_output = vec![0; 32]; + key.seal_in_place_append_tag( + aead::Nonce::assume_unique_for_key([0; aead::NONCE_LEN]), + aead::Aad::empty(), + &mut seal_output, + ) + .unwrap(); + seal_output + } +} + +#[cfg(all(test, bench))] +#[macro_rules_attribute::apply(bench_for_each_provider)] +mod benchmarks { + #[bench] + fn bench_sha256(b: &mut test::Bencher) { + use core::fmt::Debug; + + use super::provider::tls13::TLS13_CHACHA20_POLY1305_SHA256_INTERNAL; + use super::{KeySchedule, SecretKind, derive_traffic_iv, derive_traffic_key}; + use crate::KeyLog; + + fn extract_traffic_secret(ks: &KeySchedule, kind: SecretKind) { + #[derive(Debug)] + struct Log; + + impl KeyLog for Log { + fn log(&self, _label: &str, _client_random: &[u8], _secret: &[u8]) {} + } + + let hash = [0u8; 32]; + let traffic_secret = ks.derive_logged_secret(kind, &hash, &Log, &[0u8; 32]); + let traffic_secret_expander = TLS13_CHACHA20_POLY1305_SHA256_INTERNAL + .hkdf_provider + .expander_for_okm(&traffic_secret); + test::black_box(derive_traffic_key( + traffic_secret_expander.as_ref(), + TLS13_CHACHA20_POLY1305_SHA256_INTERNAL.aead_alg, + )); + test::black_box(derive_traffic_iv(traffic_secret_expander.as_ref())); + } + + b.iter(|| { + let mut ks = + KeySchedule::new_with_empty_secret(TLS13_CHACHA20_POLY1305_SHA256_INTERNAL); + ks.input_secret(&[0u8; 32]); + + extract_traffic_secret(&ks, SecretKind::ClientHandshakeTrafficSecret); + extract_traffic_secret(&ks, SecretKind::ServerHandshakeTrafficSecret); + + ks.input_empty(); + + extract_traffic_secret(&ks, SecretKind::ClientApplicationTrafficSecret); + extract_traffic_secret(&ks, SecretKind::ServerApplicationTrafficSecret); + }); + } +} diff --git a/vendor/rustls/src/tls13/mod.rs b/vendor/rustls/src/tls13/mod.rs new file mode 100644 index 00000000..ab55c889 --- /dev/null +++ b/vendor/rustls/src/tls13/mod.rs @@ -0,0 +1,125 @@ +use core::fmt; + +use crate::crypto; +use crate::crypto::hash; +use crate::suites::{CipherSuiteCommon, SupportedCipherSuite}; + +pub(crate) mod key_schedule; + +/// A TLS 1.3 cipher suite supported by rustls. +pub struct Tls13CipherSuite { + /// Common cipher suite fields. + pub common: CipherSuiteCommon, + + /// How to complete HKDF with the suite's hash function. + /// + /// If you have a HKDF implementation, you should directly implement the `crypto::tls13::Hkdf` + /// trait (and associated). + /// + /// If not, you can implement the [`crypto::hmac::Hmac`] trait (and associated), and then use + /// [`crypto::tls13::HkdfUsingHmac`]. + pub hkdf_provider: &'static dyn crypto::tls13::Hkdf, + + /// How to produce a [MessageDecrypter] or [MessageEncrypter] + /// from raw key material. + /// + /// [MessageDecrypter]: crate::crypto::cipher::MessageDecrypter + /// [MessageEncrypter]: crate::crypto::cipher::MessageEncrypter + pub aead_alg: &'static dyn crypto::cipher::Tls13AeadAlgorithm, + + /// How to create QUIC header and record protection algorithms + /// for this suite. + /// + /// Provide `None` to opt out of QUIC support for this suite. It will + /// not be offered in QUIC handshakes. + pub quic: Option<&'static dyn crate::quic::Algorithm>, +} + +impl Tls13CipherSuite { + /// Can a session using suite self resume from suite prev? + pub fn can_resume_from(&self, prev: &'static Self) -> Option<&'static Self> { + (prev.common.hash_provider.algorithm() == self.common.hash_provider.algorithm()) + .then_some(prev) + } + + /// Return `true` if this is backed by a FIPS-approved implementation. + /// + /// This means all the constituent parts that do cryptography return `true` for `fips()`. + pub fn fips(&self) -> bool { + let Self { + common, + hkdf_provider, + aead_alg, + quic, + } = self; + common.fips() + && hkdf_provider.fips() + && aead_alg.fips() + && quic.map(|q| q.fips()).unwrap_or(true) + } + + /// Returns a `quic::Suite` for the ciphersuite, if supported. + pub fn quic_suite(&'static self) -> Option<crate::quic::Suite> { + self.quic + .map(|quic| crate::quic::Suite { quic, suite: self }) + } +} + +impl From<&'static Tls13CipherSuite> for SupportedCipherSuite { + fn from(s: &'static Tls13CipherSuite) -> Self { + Self::Tls13(s) + } +} + +impl PartialEq for Tls13CipherSuite { + fn eq(&self, other: &Self) -> bool { + self.common.suite == other.common.suite + } +} + +impl fmt::Debug for Tls13CipherSuite { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Tls13CipherSuite") + .field("suite", &self.common.suite) + .finish() + } +} + +/// Constructs the signature message specified in section 4.4.3 of RFC8446. +pub(crate) fn construct_client_verify_message(handshake_hash: &hash::Output) -> VerifyMessage { + VerifyMessage::new(handshake_hash, CLIENT_CONSTANT) +} + +/// Constructs the signature message specified in section 4.4.3 of RFC8446. +pub(crate) fn construct_server_verify_message(handshake_hash: &hash::Output) -> VerifyMessage { + VerifyMessage::new(handshake_hash, SERVER_CONSTANT) +} + +pub(crate) struct VerifyMessage { + buf: [u8; MAX_VERIFY_MSG], + used: usize, +} + +impl VerifyMessage { + fn new(handshake_hash: &hash::Output, context_string_with_0: &[u8; 34]) -> Self { + let used = 64 + context_string_with_0.len() + handshake_hash.as_ref().len(); + let mut buf = [0x20u8; MAX_VERIFY_MSG]; + + let (_spaces, context) = buf.split_at_mut(64); + let (context, hash) = context.split_at_mut(34); + context.copy_from_slice(context_string_with_0); + hash[..handshake_hash.as_ref().len()].copy_from_slice(handshake_hash.as_ref()); + + Self { buf, used } + } +} + +impl AsRef<[u8]> for VerifyMessage { + fn as_ref(&self) -> &[u8] { + &self.buf[..self.used] + } +} + +const SERVER_CONSTANT: &[u8; 34] = b"TLS 1.3, server CertificateVerify\x00"; +const CLIENT_CONSTANT: &[u8; 34] = b"TLS 1.3, client CertificateVerify\x00"; +const MAX_VERIFY_MSG: usize = 64 + CLIENT_CONSTANT.len() + hash::Output::MAX_LEN; diff --git a/vendor/rustls/src/vecbuf.rs b/vendor/rustls/src/vecbuf.rs new file mode 100644 index 00000000..8a4a1f3e --- /dev/null +++ b/vendor/rustls/src/vecbuf.rs @@ -0,0 +1,374 @@ +use alloc::collections::VecDeque; +use alloc::vec::Vec; +use core::{cmp, mem}; +#[cfg(feature = "std")] +use std::io; +#[cfg(feature = "std")] +use std::io::Read; + +#[cfg(feature = "std")] +use crate::msgs::message::OutboundChunks; + +/// This is a byte buffer that is built from a deque of byte vectors. +/// +/// This avoids extra copies when appending a new byte vector, +/// at the expense of more complexity when reading out. +pub(crate) struct ChunkVecBuffer { + /// How many bytes have been consumed in the first chunk. + /// + /// Invariant: zero if `chunks.is_empty()` + /// Invariant: 0 <= `prefix_used` < `chunks[0].len()` + prefix_used: usize, + + chunks: VecDeque<Vec<u8>>, + + /// The total upper limit (in bytes) of this object. + limit: Option<usize>, +} + +impl ChunkVecBuffer { + pub(crate) fn new(limit: Option<usize>) -> Self { + Self { + prefix_used: 0, + chunks: VecDeque::new(), + limit, + } + } + + /// Sets the upper limit on how many bytes this + /// object can store. + /// + /// Setting a lower limit than the currently stored + /// data is not an error. + /// + /// A [`None`] limit is interpreted as no limit. + pub(crate) fn set_limit(&mut self, new_limit: Option<usize>) { + self.limit = new_limit; + } + + /// If we're empty + pub(crate) fn is_empty(&self) -> bool { + self.chunks.is_empty() + } + + /// How many bytes we're storing + pub(crate) fn len(&self) -> usize { + self.chunks + .iter() + .fold(0usize, |acc, chunk| acc + chunk.len()) + - self.prefix_used + } + + /// For a proposed append of `len` bytes, how many + /// bytes should we actually append to adhere to the + /// currently set `limit`? + pub(crate) fn apply_limit(&self, len: usize) -> usize { + if let Some(limit) = self.limit { + let space = limit.saturating_sub(self.len()); + cmp::min(len, space) + } else { + len + } + } + + /// Take and append the given `bytes`. + pub(crate) fn append(&mut self, bytes: Vec<u8>) -> usize { + let len = bytes.len(); + + if !bytes.is_empty() { + if self.chunks.is_empty() { + debug_assert_eq!(self.prefix_used, 0); + } + + self.chunks.push_back(bytes); + } + + len + } + + /// Take one of the chunks from this object. + /// + /// This function returns `None` if the object `is_empty`. + pub(crate) fn pop(&mut self) -> Option<Vec<u8>> { + let mut first = self.chunks.pop_front(); + + if let Some(first) = &mut first { + // slice off `prefix_used` if needed (uncommon) + let prefix = mem::take(&mut self.prefix_used); + first.drain(0..prefix); + } + + first + } + + #[cfg(read_buf)] + /// Read data out of this object, writing it into `cursor`. + pub(crate) fn read_buf(&mut self, mut cursor: core::io::BorrowedCursor<'_>) -> io::Result<()> { + while !self.is_empty() && cursor.capacity() > 0 { + let chunk = &self.chunks[0][self.prefix_used..]; + let used = cmp::min(chunk.len(), cursor.capacity()); + cursor.append(&chunk[..used]); + self.consume(used); + } + + Ok(()) + } + + /// Inspect the first chunk from this object. + pub(crate) fn peek(&self) -> Option<&[u8]> { + self.chunks + .front() + .map(|ch| ch.as_slice()) + } +} + +#[cfg(feature = "std")] +impl ChunkVecBuffer { + pub(crate) fn is_full(&self) -> bool { + self.limit + .map(|limit| self.len() > limit) + .unwrap_or_default() + } + + /// Append a copy of `bytes`, perhaps a prefix if + /// we're near the limit. + pub(crate) fn append_limited_copy(&mut self, payload: OutboundChunks<'_>) -> usize { + let take = self.apply_limit(payload.len()); + self.append(payload.split_at(take).0.to_vec()); + take + } + + /// Read data out of this object, writing it into `buf` + /// and returning how many bytes were written there. + pub(crate) fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + let mut offs = 0; + + while offs < buf.len() && !self.is_empty() { + let used = (&self.chunks[0][self.prefix_used..]).read(&mut buf[offs..])?; + + self.consume(used); + offs += used; + } + + Ok(offs) + } + + pub(crate) fn consume_first_chunk(&mut self, used: usize) { + // this backs (infallible) `BufRead::consume`, where `used` is + // user-supplied. + assert!( + used <= self + .chunk() + .map(|ch| ch.len()) + .unwrap_or_default(), + "illegal `BufRead::consume` usage", + ); + self.consume(used); + } + + fn consume(&mut self, used: usize) { + // first, mark the rightmost extent of the used buffer + self.prefix_used += used; + + // then reduce `prefix_used` by discarding wholly-covered + // buffers + while let Some(buf) = self.chunks.front() { + if self.prefix_used < buf.len() { + return; + } else { + self.prefix_used -= buf.len(); + self.chunks.pop_front(); + } + } + + debug_assert_eq!( + self.prefix_used, 0, + "attempted to `ChunkVecBuffer::consume` more than available" + ); + } + + /// Read data out of this object, passing it `wr` + pub(crate) fn write_to(&mut self, wr: &mut dyn io::Write) -> io::Result<usize> { + if self.is_empty() { + return Ok(0); + } + + let mut prefix = self.prefix_used; + let mut bufs = [io::IoSlice::new(&[]); 64]; + for (iov, chunk) in bufs.iter_mut().zip(self.chunks.iter()) { + *iov = io::IoSlice::new(&chunk[prefix..]); + prefix = 0; + } + let len = cmp::min(bufs.len(), self.chunks.len()); + let bufs = &bufs[..len]; + let used = wr.write_vectored(bufs)?; + let available_bytes = bufs.iter().map(|ch| ch.len()).sum(); + + if used > available_bytes { + // This is really unrecoverable, since the amount of data written + // is now unknown. Consume all the potentially-written data in + // case the caller ignores the error. + // See <https://github.com/rustls/rustls/issues/2316> for background. + self.consume(available_bytes); + return Err(io::Error::new( + io::ErrorKind::Other, + std::format!("illegal write_vectored return value ({used} > {available_bytes})"), + )); + } + self.consume(used); + Ok(used) + } + + /// Returns the first contiguous chunk of data, or None if empty. + pub(crate) fn chunk(&self) -> Option<&[u8]> { + self.chunks + .front() + .map(|chunk| &chunk[self.prefix_used..]) + } +} + +#[cfg(all(test, feature = "std"))] +mod tests { + use alloc::vec; + use alloc::vec::Vec; + + use super::ChunkVecBuffer; + + #[test] + fn short_append_copy_with_limit() { + let mut cvb = ChunkVecBuffer::new(Some(12)); + assert_eq!(cvb.append_limited_copy(b"hello"[..].into()), 5); + assert_eq!(cvb.append_limited_copy(b"world"[..].into()), 5); + assert_eq!(cvb.append_limited_copy(b"hello"[..].into()), 2); + assert_eq!(cvb.append_limited_copy(b"world"[..].into()), 0); + + let mut buf = [0u8; 12]; + assert_eq!(cvb.read(&mut buf).unwrap(), 12); + assert_eq!(buf.to_vec(), b"helloworldhe".to_vec()); + } + + #[test] + fn read_byte_by_byte() { + let mut cvb = ChunkVecBuffer::new(None); + cvb.append(b"test fixture data".to_vec()); + assert!(!cvb.is_empty()); + for expect in b"test fixture data" { + let mut byte = [0]; + assert_eq!(cvb.read(&mut byte).unwrap(), 1); + assert_eq!(byte[0], *expect); + } + + assert_eq!(cvb.read(&mut [0]).unwrap(), 0); + } + + #[test] + fn every_possible_chunk_interleaving() { + let input = (0..=0xffu8) + .cycle() + .take(4096) + .collect::<Vec<u8>>(); + + for input_chunk_len in 1..64usize { + for output_chunk_len in 1..65usize { + std::println!("check input={input_chunk_len} output={output_chunk_len}"); + let mut cvb = ChunkVecBuffer::new(None); + for chunk in input.chunks(input_chunk_len) { + cvb.append(chunk.to_vec()); + } + + assert_eq!(cvb.len(), input.len()); + let mut buf = vec![0u8; output_chunk_len]; + + for expect in input.chunks(output_chunk_len) { + assert_eq!(expect.len(), cvb.read(&mut buf).unwrap()); + assert_eq!(expect, &buf[..expect.len()]); + } + + assert_eq!(cvb.read(&mut [0]).unwrap(), 0); + } + } + } + + #[cfg(read_buf)] + #[test] + fn read_buf() { + use core::io::BorrowedBuf; + use core::mem::MaybeUninit; + + { + let mut cvb = ChunkVecBuffer::new(None); + cvb.append(b"test ".to_vec()); + cvb.append(b"fixture ".to_vec()); + cvb.append(b"data".to_vec()); + + let mut buf = [MaybeUninit::<u8>::uninit(); 8]; + let mut buf: BorrowedBuf<'_> = buf.as_mut_slice().into(); + cvb.read_buf(buf.unfilled()).unwrap(); + assert_eq!(buf.filled(), b"test fix"); + buf.clear(); + cvb.read_buf(buf.unfilled()).unwrap(); + assert_eq!(buf.filled(), b"ture dat"); + buf.clear(); + cvb.read_buf(buf.unfilled()).unwrap(); + assert_eq!(buf.filled(), b"a"); + } + + { + let mut cvb = ChunkVecBuffer::new(None); + cvb.append(b"short message".to_vec()); + + let mut buf = [MaybeUninit::<u8>::uninit(); 1024]; + let mut buf: BorrowedBuf<'_> = buf.as_mut_slice().into(); + cvb.read_buf(buf.unfilled()).unwrap(); + assert_eq!(buf.filled(), b"short message"); + } + } +} + +#[cfg(bench)] +mod benchmarks { + use alloc::vec; + + use super::ChunkVecBuffer; + + #[bench] + fn read_one_byte_from_large_message(b: &mut test::Bencher) { + b.iter(|| { + let mut cvb = ChunkVecBuffer::new(None); + cvb.append(vec![0u8; 16_384]); + assert_eq!(1, cvb.read(&mut [0u8]).unwrap()); + }); + } + + #[bench] + fn read_all_individual_from_large_message(b: &mut test::Bencher) { + b.iter(|| { + let mut cvb = ChunkVecBuffer::new(None); + cvb.append(vec![0u8; 16_384]); + loop { + if let Ok(0) = cvb.read(&mut [0u8]) { + break; + } + } + }); + } + + #[bench] + fn read_half_bytes_from_large_message(b: &mut test::Bencher) { + b.iter(|| { + let mut cvb = ChunkVecBuffer::new(None); + cvb.append(vec![0u8; 16_384]); + assert_eq!(8192, cvb.read(&mut [0u8; 8192]).unwrap()); + assert_eq!(8192, cvb.read(&mut [0u8; 8192]).unwrap()); + }); + } + + #[bench] + fn read_entire_large_message(b: &mut test::Bencher) { + b.iter(|| { + let mut cvb = ChunkVecBuffer::new(None); + cvb.append(vec![0u8; 16_384]); + assert_eq!(16_384, cvb.read(&mut [0u8; 16_384]).unwrap()); + }); + } +} diff --git a/vendor/rustls/src/verify.rs b/vendor/rustls/src/verify.rs new file mode 100644 index 00000000..ce07e3a0 --- /dev/null +++ b/vendor/rustls/src/verify.rs @@ -0,0 +1,382 @@ +use alloc::vec::Vec; +use core::fmt::Debug; + +use pki_types::{CertificateDer, ServerName, UnixTime}; + +use crate::enums::SignatureScheme; +use crate::error::{Error, InvalidMessage}; +use crate::msgs::base::PayloadU16; +use crate::msgs::codec::{Codec, Reader}; +use crate::msgs::handshake::DistinguishedName; + +// Marker types. These are used to bind the fact some verification +// (certificate chain or handshake signature) has taken place into +// protocol states. We use this to have the compiler check that there +// are no 'goto fail'-style elisions of important checks before we +// reach the traffic stage. +// +// These types are public, but cannot be directly constructed. This +// means their origins can be precisely determined by looking +// for their `assertion` constructors. + +/// Zero-sized marker type representing verification of a signature. +#[derive(Debug)] +pub struct HandshakeSignatureValid(()); + +impl HandshakeSignatureValid { + /// Make a `HandshakeSignatureValid` + pub fn assertion() -> Self { + Self(()) + } +} + +#[derive(Debug)] +pub(crate) struct FinishedMessageVerified(()); + +impl FinishedMessageVerified { + pub(crate) fn assertion() -> Self { + Self(()) + } +} + +/// Zero-sized marker type representing verification of a server cert chain. +#[allow(unreachable_pub)] +#[derive(Debug)] +pub struct ServerCertVerified(()); + +#[allow(unreachable_pub)] +impl ServerCertVerified { + /// Make a `ServerCertVerified` + pub fn assertion() -> Self { + Self(()) + } +} + +/// Zero-sized marker type representing verification of a client cert chain. +#[derive(Debug)] +pub struct ClientCertVerified(()); + +impl ClientCertVerified { + /// Make a `ClientCertVerified` + pub fn assertion() -> Self { + Self(()) + } +} + +/// Something that can verify a server certificate chain, and verify +/// signatures made by certificates. +#[allow(unreachable_pub)] +pub trait ServerCertVerifier: Debug + Send + Sync { + /// Verify the end-entity certificate `end_entity` is valid for the + /// hostname `dns_name` and chains to at least one trust anchor. + /// + /// `intermediates` contains all certificates other than `end_entity` that + /// were sent as part of the server's [Certificate] message. It is in the + /// same order that the server sent them and may be empty. + /// + /// Note that none of the certificates have been parsed yet, so it is the responsibility of + /// the implementer to handle invalid data. It is recommended that the implementer returns + /// [`Error::InvalidCertificate(CertificateError::BadEncoding)`] when these cases are encountered. + /// + /// [Certificate]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.4.2 + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result<ServerCertVerified, Error>; + + /// Verify a signature allegedly by the given server certificate. + /// + /// `message` is not hashed, and needs hashing during the verification. + /// The signature and algorithm are within `dss`. `cert` contains the + /// public key to use. + /// + /// `cert` has already been validated by [`ServerCertVerifier::verify_server_cert`]. + /// + /// If and only if the signature is valid, return `Ok(HandshakeSignatureValid)`. + /// Otherwise, return an error -- rustls will send an alert and abort the + /// connection. + /// + /// This method is only called for TLS1.2 handshakes. Note that, in TLS1.2, + /// SignatureSchemes such as `SignatureScheme::ECDSA_NISTP256_SHA256` are not + /// in fact bound to the specific curve implied in their name. + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error>; + + /// Verify a signature allegedly by the given server certificate. + /// + /// This method is only called for TLS1.3 handshakes. + /// + /// This method is very similar to `verify_tls12_signature`: but note the + /// tighter ECDSA SignatureScheme semantics -- e.g. `SignatureScheme::ECDSA_NISTP256_SHA256` + /// must only validate signatures using public keys on the right curve -- + /// rustls does not enforce this requirement for you. + /// + /// `cert` has already been validated by [`ServerCertVerifier::verify_server_cert`]. + /// + /// If and only if the signature is valid, return `Ok(HandshakeSignatureValid)`. + /// Otherwise, return an error -- rustls will send an alert and abort the + /// connection. + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error>; + + /// Return the list of SignatureSchemes that this verifier will handle, + /// in `verify_tls12_signature` and `verify_tls13_signature` calls. + /// + /// This should be in priority order, with the most preferred first. + fn supported_verify_schemes(&self) -> Vec<SignatureScheme>; + + /// Returns whether this verifier requires raw public keys as defined + /// in [RFC 7250](https://tools.ietf.org/html/rfc7250). + fn requires_raw_public_keys(&self) -> bool { + false + } + + /// Return the [`DistinguishedName`]s of certificate authorities that this verifier trusts. + /// + /// If specified, will be sent as the [`certificate_authorities`] extension in ClientHello. + /// Note that this is only applicable to TLS 1.3. + /// + /// [`certificate_authorities`]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.4 + fn root_hint_subjects(&self) -> Option<&[DistinguishedName]> { + None + } +} + +/// Something that can verify a client certificate chain +#[allow(unreachable_pub)] +pub trait ClientCertVerifier: Debug + Send + Sync { + /// Returns `true` to enable the server to request a client certificate and + /// `false` to skip requesting a client certificate. Defaults to `true`. + fn offer_client_auth(&self) -> bool { + true + } + + /// Return `true` to require a client certificate and `false` to make + /// client authentication optional. + /// Defaults to `self.offer_client_auth()`. + fn client_auth_mandatory(&self) -> bool { + self.offer_client_auth() + } + + /// Returns the [`DistinguishedName`] [subjects] that the server will hint to clients to + /// identify acceptable authentication trust anchors. + /// + /// These hint values help the client pick a client certificate it believes the server will + /// accept. The hints must be DER-encoded X.500 distinguished names, per [RFC 5280 A.1]. They + /// are sent in the [`certificate_authorities`] extension of a [`CertificateRequest`] message + /// when [ClientCertVerifier::offer_client_auth] is true. When an empty list is sent the client + /// should always provide a client certificate if it has one. + /// + /// Generally this list should contain the [`DistinguishedName`] of each root trust + /// anchor in the root cert store that the server is configured to use for authenticating + /// presented client certificates. + /// + /// In some circumstances this list may be customized to include [`DistinguishedName`] entries + /// that do not correspond to a trust anchor in the server's root cert store. For example, + /// the server may be configured to trust a root CA that cross-signed an issuer certificate + /// that the client considers a trust anchor. From the server's perspective the cross-signed + /// certificate is an intermediate, and not present in the server's root cert store. The client + /// may have the cross-signed certificate configured as a trust anchor, and be unaware of the + /// root CA that cross-signed it. If the server's hints list only contained the subjects of the + /// server's root store the client would consider a client certificate issued by the cross-signed + /// issuer unacceptable, since its subject was not hinted. To avoid this circumstance the server + /// should customize the hints list to include the subject of the cross-signed issuer in addition + /// to the subjects from the root cert store. + /// + /// [subjects]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.6 + /// [RFC 5280 A.1]: https://www.rfc-editor.org/rfc/rfc5280#appendix-A.1 + /// [`CertificateRequest`]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.3.2 + /// [`certificate_authorities`]: https://datatracker.ietf.org/doc/html/rfc8446#section-4.2.4 + fn root_hint_subjects(&self) -> &[DistinguishedName]; + + /// Verify the end-entity certificate `end_entity` is valid, acceptable, + /// and chains to at least one of the trust anchors trusted by + /// this verifier. + /// + /// `intermediates` contains the intermediate certificates the + /// client sent along with the end-entity certificate; it is in the same + /// order that the peer sent them and may be empty. + /// + /// Note that none of the certificates have been parsed yet, so it is the responsibility of + /// the implementer to handle invalid data. It is recommended that the implementer returns + /// an [InvalidCertificate] error with the [BadEncoding] variant when these cases are encountered. + /// + /// [InvalidCertificate]: Error#variant.InvalidCertificate + /// [BadEncoding]: crate::CertificateError#variant.BadEncoding + fn verify_client_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + now: UnixTime, + ) -> Result<ClientCertVerified, Error>; + + /// Verify a signature allegedly by the given client certificate. + /// + /// `message` is not hashed, and needs hashing during the verification. + /// The signature and algorithm are within `dss`. `cert` contains the + /// public key to use. + /// + /// `cert` has already been validated by [`ClientCertVerifier::verify_client_cert`]. + /// + /// If and only if the signature is valid, return `Ok(HandshakeSignatureValid)`. + /// Otherwise, return an error -- rustls will send an alert and abort the + /// connection. + /// + /// This method is only called for TLS1.2 handshakes. Note that, in TLS1.2, + /// SignatureSchemes such as `SignatureScheme::ECDSA_NISTP256_SHA256` are not + /// in fact bound to the specific curve implied in their name. + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error>; + + /// Verify a signature allegedly by the given client certificate. + /// + /// This method is only called for TLS1.3 handshakes. + /// + /// This method is very similar to `verify_tls12_signature`, but note the + /// tighter ECDSA SignatureScheme semantics in TLS 1.3. For example, + /// `SignatureScheme::ECDSA_NISTP256_SHA256` + /// must only validate signatures using public keys on the right curve -- + /// rustls does not enforce this requirement for you. + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error>; + + /// Return the list of SignatureSchemes that this verifier will handle, + /// in `verify_tls12_signature` and `verify_tls13_signature` calls. + /// + /// This should be in priority order, with the most preferred first. + fn supported_verify_schemes(&self) -> Vec<SignatureScheme>; + + /// Returns whether this verifier requires raw public keys as defined + /// in [RFC 7250](https://tools.ietf.org/html/rfc7250). + fn requires_raw_public_keys(&self) -> bool { + false + } +} + +/// Turns off client authentication. +/// +/// In contrast to using +/// `WebPkiClientVerifier::builder(roots).allow_unauthenticated().build()`, the `NoClientAuth` +/// `ClientCertVerifier` will not offer client authentication at all, vs offering but not +/// requiring it. +#[derive(Debug)] +pub struct NoClientAuth; + +impl ClientCertVerifier for NoClientAuth { + fn offer_client_auth(&self) -> bool { + false + } + + fn root_hint_subjects(&self) -> &[DistinguishedName] { + unimplemented!(); + } + + fn verify_client_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _now: UnixTime, + ) -> Result<ClientCertVerified, Error> { + unimplemented!(); + } + + fn verify_tls12_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + unimplemented!(); + } + + fn verify_tls13_signature( + &self, + _message: &[u8], + _cert: &CertificateDer<'_>, + _dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + unimplemented!(); + } + + fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { + unimplemented!(); + } +} + +/// This type combines a [`SignatureScheme`] and a signature payload produced with that scheme. +#[derive(Debug, Clone)] +pub struct DigitallySignedStruct { + /// The [`SignatureScheme`] used to produce the signature. + pub scheme: SignatureScheme, + sig: PayloadU16, +} + +impl DigitallySignedStruct { + pub(crate) fn new(scheme: SignatureScheme, sig: Vec<u8>) -> Self { + Self { + scheme, + sig: PayloadU16::new(sig), + } + } + + /// Get the signature. + pub fn signature(&self) -> &[u8] { + &self.sig.0 + } +} + +impl Codec<'_> for DigitallySignedStruct { + fn encode(&self, bytes: &mut Vec<u8>) { + self.scheme.encode(bytes); + self.sig.encode(bytes); + } + + fn read(r: &mut Reader<'_>) -> Result<Self, InvalidMessage> { + let scheme = SignatureScheme::read(r)?; + let sig = PayloadU16::read(r)?; + + Ok(Self { scheme, sig }) + } +} + +#[test] +fn assertions_are_debug() { + use std::format; + + assert_eq!( + format!("{:?}", ClientCertVerified::assertion()), + "ClientCertVerified(())" + ); + assert_eq!( + format!("{:?}", HandshakeSignatureValid::assertion()), + "HandshakeSignatureValid(())" + ); + assert_eq!( + format!("{:?}", FinishedMessageVerified::assertion()), + "FinishedMessageVerified(())" + ); + assert_eq!( + format!("{:?}", ServerCertVerified::assertion()), + "ServerCertVerified(())" + ); +} diff --git a/vendor/rustls/src/verifybench.rs b/vendor/rustls/src/verifybench.rs new file mode 100644 index 00000000..06213e7b --- /dev/null +++ b/vendor/rustls/src/verifybench.rs @@ -0,0 +1,244 @@ +//! This program does benchmarking of the functions in verify.rs, +//! that do certificate chain validation and signature verification. +//! +//! This uses captured certificate chains for a selection of websites, +//! saved in `testdata/cert-{SITE}.{I}.der`. +//! +//! To update that data: +//! +//! - delete all `testdata/cert-*.der`. +//! - run the `admin/capture-certdata` script. +//! - update the verification timestamp near the bottom of this file +//! to the current time. +//! - where a website's chain length changed, reflect that in the list +//! of certificate files below. +//! +//! This does not need to be done regularly; because the verification +//! time is fixed, it only needs doing if a root certificate is +//! distrusted. + +#![cfg(bench)] + +use core::time::Duration; +use std::prelude::v1::*; + +use pki_types::{CertificateDer, ServerName, UnixTime}; +use webpki_roots; + +use crate::crypto::CryptoProvider; +use crate::verify::ServerCertVerifier; +use crate::webpki::{RootCertStore, WebPkiServerVerifier}; + +#[macro_rules_attribute::apply(bench_for_each_provider)] +mod benchmarks { + use super::{Context, provider}; + + #[bench] + fn reddit_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "reddit.com", + &[ + include_bytes!("testdata/cert-reddit.0.der"), + include_bytes!("testdata/cert-reddit.1.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn github_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "github.com", + &[ + include_bytes!("testdata/cert-github.0.der"), + include_bytes!("testdata/cert-github.1.der"), + include_bytes!("testdata/cert-github.2.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn arstechnica_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "arstechnica.com", + &[ + include_bytes!("testdata/cert-arstechnica.0.der"), + include_bytes!("testdata/cert-arstechnica.1.der"), + include_bytes!("testdata/cert-arstechnica.2.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn servo_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "servo.org", + &[ + include_bytes!("testdata/cert-servo.0.der"), + include_bytes!("testdata/cert-servo.1.der"), + include_bytes!("testdata/cert-servo.2.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn twitter_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "twitter.com", + &[ + include_bytes!("testdata/cert-twitter.0.der"), + include_bytes!("testdata/cert-twitter.1.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn wikipedia_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "wikipedia.org", + &[ + include_bytes!("testdata/cert-wikipedia.0.der"), + include_bytes!("testdata/cert-wikipedia.1.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn google_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "www.google.com", + &[ + include_bytes!("testdata/cert-google.0.der"), + include_bytes!("testdata/cert-google.1.der"), + include_bytes!("testdata/cert-google.2.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn hn_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "news.ycombinator.com", + &[ + include_bytes!("testdata/cert-hn.0.der"), + include_bytes!("testdata/cert-hn.1.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn stackoverflow_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "stackoverflow.com", + &[ + include_bytes!("testdata/cert-stackoverflow.0.der"), + include_bytes!("testdata/cert-stackoverflow.1.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn duckduckgo_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "duckduckgo.com", + &[ + include_bytes!("testdata/cert-duckduckgo.0.der"), + include_bytes!("testdata/cert-duckduckgo.1.der"), + include_bytes!("testdata/cert-duckduckgo.2.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn rustlang_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "www.rust-lang.org", + &[ + include_bytes!("testdata/cert-rustlang.0.der"), + include_bytes!("testdata/cert-rustlang.1.der"), + include_bytes!("testdata/cert-rustlang.2.der"), + include_bytes!("testdata/cert-rustlang.3.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } + + #[bench] + fn wapo_cert(b: &mut test::Bencher) { + let ctx = Context::new( + provider::default_provider(), + "www.washingtonpost.com", + &[ + include_bytes!("testdata/cert-wapo.0.der"), + include_bytes!("testdata/cert-wapo.1.der"), + ], + ); + b.iter(|| ctx.verify_once()); + } +} + +struct Context { + server_name: ServerName<'static>, + chain: Vec<CertificateDer<'static>>, + now: UnixTime, + verifier: WebPkiServerVerifier, +} + +impl Context { + fn new(provider: CryptoProvider, domain: &'static str, certs: &[&'static [u8]]) -> Self { + let mut roots = RootCertStore::empty(); + roots.extend( + webpki_roots::TLS_SERVER_ROOTS + .iter() + .cloned(), + ); + Self { + server_name: domain.try_into().unwrap(), + chain: certs + .iter() + .copied() + .map(|bytes| CertificateDer::from(bytes.to_vec())) + .collect(), + now: UnixTime::since_unix_epoch(Duration::from_secs(1_746_605_469)), + verifier: WebPkiServerVerifier::new_without_revocation( + roots, + provider.signature_verification_algorithms, + ), + } + } + + fn verify_once(&self) { + const OCSP_RESPONSE: &[u8] = &[]; + + let (end_entity, intermediates) = self.chain.split_first().unwrap(); + self.verifier + .verify_server_cert( + end_entity, + intermediates, + &self.server_name, + OCSP_RESPONSE, + self.now, + ) + .unwrap(); + } +} diff --git a/vendor/rustls/src/versions.rs b/vendor/rustls/src/versions.rs new file mode 100644 index 00000000..4acdf99a --- /dev/null +++ b/vendor/rustls/src/versions.rs @@ -0,0 +1,97 @@ +use core::fmt; + +use crate::enums::ProtocolVersion; + +/// A TLS protocol version supported by rustls. +/// +/// All possible instances of this class are provided by the library in +/// the [`ALL_VERSIONS`] array, as well as individually as [`TLS12`] +/// and [`TLS13`]. +#[non_exhaustive] +#[derive(Eq, PartialEq)] +pub struct SupportedProtocolVersion { + /// The TLS enumeration naming this version. + pub version: ProtocolVersion, +} + +impl fmt::Debug for SupportedProtocolVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.version.fmt(f) + } +} + +/// TLS1.2 +#[cfg(feature = "tls12")] +pub static TLS12: SupportedProtocolVersion = SupportedProtocolVersion { + version: ProtocolVersion::TLSv1_2, +}; + +/// TLS1.3 +pub static TLS13: SupportedProtocolVersion = SupportedProtocolVersion { + version: ProtocolVersion::TLSv1_3, +}; + +/// A list of all the protocol versions supported by rustls. +pub static ALL_VERSIONS: &[&SupportedProtocolVersion] = &[ + &TLS13, + #[cfg(feature = "tls12")] + &TLS12, +]; + +/// The version configuration that an application should use by default. +/// +/// This will be [`ALL_VERSIONS`] for now, but gives space in the future +/// to remove a version from here and require users to opt-in to older +/// versions. +pub static DEFAULT_VERSIONS: &[&SupportedProtocolVersion] = ALL_VERSIONS; + +#[derive(Clone, Copy)] +pub(crate) struct EnabledVersions { + #[cfg(feature = "tls12")] + tls12: Option<&'static SupportedProtocolVersion>, + tls13: Option<&'static SupportedProtocolVersion>, +} + +impl fmt::Debug for EnabledVersions { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut list = &mut f.debug_list(); + #[cfg(feature = "tls12")] + if let Some(v) = self.tls12 { + list = list.entry(v); + } + if let Some(v) = self.tls13 { + list = list.entry(v); + } + list.finish() + } +} + +impl EnabledVersions { + pub(crate) fn new(versions: &[&'static SupportedProtocolVersion]) -> Self { + let mut ev = Self { + #[cfg(feature = "tls12")] + tls12: None, + tls13: None, + }; + + for v in versions { + match v.version { + #[cfg(feature = "tls12")] + ProtocolVersion::TLSv1_2 => ev.tls12 = Some(v), + ProtocolVersion::TLSv1_3 => ev.tls13 = Some(v), + _ => {} + } + } + + ev + } + + pub(crate) fn contains(&self, version: ProtocolVersion) -> bool { + match version { + #[cfg(feature = "tls12")] + ProtocolVersion::TLSv1_2 => self.tls12.is_some(), + ProtocolVersion::TLSv1_3 => self.tls13.is_some(), + _ => false, + } + } +} diff --git a/vendor/rustls/src/webpki/anchors.rs b/vendor/rustls/src/webpki/anchors.rs new file mode 100644 index 00000000..b526ed25 --- /dev/null +++ b/vendor/rustls/src/webpki/anchors.rs @@ -0,0 +1,143 @@ +use alloc::vec::Vec; +use alloc::{fmt, format}; + +use pki_types::{CertificateDer, TrustAnchor}; +use webpki::anchor_from_trusted_cert; + +use super::pki_error; +use crate::log::{debug, trace}; +use crate::{DistinguishedName, Error}; + +/// A container for root certificates able to provide a root-of-trust +/// for connection authentication. +#[derive(Clone)] +pub struct RootCertStore { + /// The list of roots. + pub roots: Vec<TrustAnchor<'static>>, +} + +impl RootCertStore { + /// Make a new, empty `RootCertStore`. + pub fn empty() -> Self { + Self { roots: Vec::new() } + } + + /// Parse the given DER-encoded certificates and add all that can be parsed + /// in a best-effort fashion. + /// + /// This is because large collections of root certificates often + /// include ancient or syntactically invalid certificates. + /// + /// Returns the number of certificates added, and the number that were ignored. + pub fn add_parsable_certificates<'a>( + &mut self, + der_certs: impl IntoIterator<Item = CertificateDer<'a>>, + ) -> (usize, usize) { + let mut valid_count = 0; + let mut invalid_count = 0; + + for der_cert in der_certs { + #[cfg_attr(not(feature = "logging"), allow(unused_variables))] + match anchor_from_trusted_cert(&der_cert) { + Ok(anchor) => { + self.roots.push(anchor.to_owned()); + valid_count += 1; + } + Err(err) => { + trace!("invalid cert der {:?}", der_cert.as_ref()); + debug!("certificate parsing failed: {err:?}"); + invalid_count += 1; + } + }; + } + + debug!( + "add_parsable_certificates processed {valid_count} valid and {invalid_count} invalid certs" + ); + + (valid_count, invalid_count) + } + + /// Add a single DER-encoded certificate to the store. + /// + /// This is suitable for a small set of root certificates that are expected to parse + /// successfully. For large collections of roots (for example from a system store) it + /// is expected that some of them might not be valid according to the rules rustls + /// implements. As long as a relatively limited number of certificates are affected, + /// this should not be a cause for concern. Use [`RootCertStore::add_parsable_certificates`] + /// in order to add as many valid roots as possible and to understand how many certificates + /// have been diagnosed as malformed. + pub fn add(&mut self, der: CertificateDer<'_>) -> Result<(), Error> { + self.roots.push( + anchor_from_trusted_cert(&der) + .map_err(pki_error)? + .to_owned(), + ); + Ok(()) + } + + /// Return the DER encoded [`DistinguishedName`] of each trust anchor subject in the root + /// cert store. + /// + /// Each [`DistinguishedName`] will be a DER-encoded X.500 distinguished name, per + /// [RFC 5280 A.1], including the outer `SEQUENCE`. + /// + /// [RFC 5280 A.1]: https://www.rfc-editor.org/rfc/rfc5280#appendix-A.1 + pub fn subjects(&self) -> Vec<DistinguishedName> { + self.roots + .iter() + .map(|ta| DistinguishedName::in_sequence(ta.subject.as_ref())) + .collect() + } + + /// Return true if there are no certificates. + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Say how many certificates are in the container. + pub fn len(&self) -> usize { + self.roots.len() + } +} + +impl FromIterator<TrustAnchor<'static>> for RootCertStore { + fn from_iter<T: IntoIterator<Item = TrustAnchor<'static>>>(iter: T) -> Self { + Self { + roots: iter.into_iter().collect(), + } + } +} + +impl Extend<TrustAnchor<'static>> for RootCertStore { + fn extend<T: IntoIterator<Item = TrustAnchor<'static>>>(&mut self, iter: T) { + self.roots.extend(iter); + } +} + +impl fmt::Debug for RootCertStore { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("RootCertStore") + .field("roots", &format!("({} roots)", &self.roots.len())) + .finish() + } +} + +#[test] +fn root_cert_store_debug() { + use core::iter; + + use pki_types::Der; + + let ta = TrustAnchor { + subject: Der::from_slice(&[]), + subject_public_key_info: Der::from_slice(&[]), + name_constraints: None, + }; + let store = RootCertStore::from_iter(iter::repeat(ta).take(138)); + + assert_eq!( + format!("{store:?}"), + "RootCertStore { roots: \"(138 roots)\" }" + ); +} diff --git a/vendor/rustls/src/webpki/client_verifier.rs b/vendor/rustls/src/webpki/client_verifier.rs new file mode 100644 index 00000000..060f0f77 --- /dev/null +++ b/vendor/rustls/src/webpki/client_verifier.rs @@ -0,0 +1,665 @@ +use alloc::vec::Vec; + +use pki_types::{CertificateDer, CertificateRevocationListDer, UnixTime}; +use webpki::{CertRevocationList, ExpirationPolicy, RevocationCheckDepth, UnknownStatusPolicy}; + +use super::{VerifierBuilderError, pki_error}; +#[cfg(doc)] +use crate::ConfigBuilder; +#[cfg(doc)] +use crate::crypto; +use crate::crypto::{CryptoProvider, WebPkiSupportedAlgorithms}; +#[cfg(doc)] +use crate::server::ServerConfig; +use crate::sync::Arc; +use crate::verify::{ + ClientCertVerified, ClientCertVerifier, DigitallySignedStruct, HandshakeSignatureValid, + NoClientAuth, +}; +use crate::webpki::parse_crls; +use crate::webpki::verify::{ParsedCertificate, verify_tls12_signature, verify_tls13_signature}; +use crate::{DistinguishedName, Error, RootCertStore, SignatureScheme}; + +/// A builder for configuring a `webpki` client certificate verifier. +/// +/// For more information, see the [`WebPkiClientVerifier`] documentation. +#[derive(Debug, Clone)] +pub struct ClientCertVerifierBuilder { + roots: Arc<RootCertStore>, + root_hint_subjects: Vec<DistinguishedName>, + crls: Vec<CertificateRevocationListDer<'static>>, + revocation_check_depth: RevocationCheckDepth, + unknown_revocation_policy: UnknownStatusPolicy, + revocation_expiration_policy: ExpirationPolicy, + anon_policy: AnonymousClientPolicy, + supported_algs: WebPkiSupportedAlgorithms, +} + +impl ClientCertVerifierBuilder { + pub(crate) fn new( + roots: Arc<RootCertStore>, + supported_algs: WebPkiSupportedAlgorithms, + ) -> Self { + Self { + root_hint_subjects: roots.subjects(), + roots, + crls: Vec::new(), + anon_policy: AnonymousClientPolicy::Deny, + revocation_check_depth: RevocationCheckDepth::Chain, + unknown_revocation_policy: UnknownStatusPolicy::Deny, + revocation_expiration_policy: ExpirationPolicy::Ignore, + supported_algs, + } + } + + /// Clear the list of trust anchor hint subjects. + /// + /// By default, the client cert verifier will use the subjects provided by the root cert + /// store configured for client authentication. Calling this function will remove these + /// hint subjects, indicating the client should make a free choice of which certificate + /// to send. + /// + /// See [`ClientCertVerifier::root_hint_subjects`] for more information on + /// circumstances where you may want to clear the default hint subjects. + pub fn clear_root_hint_subjects(mut self) -> Self { + self.root_hint_subjects = Vec::default(); + self + } + + /// Add additional [`DistinguishedName`]s to the list of trust anchor hint subjects. + /// + /// By default, the client cert verifier will use the subjects provided by the root cert + /// store configured for client authentication. Calling this function will add to these + /// existing hint subjects. Calling this function with empty `subjects` will have no + /// effect. + /// + /// See [`ClientCertVerifier::root_hint_subjects`] for more information on + /// circumstances where you may want to override the default hint subjects. + pub fn add_root_hint_subjects( + mut self, + subjects: impl IntoIterator<Item = DistinguishedName>, + ) -> Self { + self.root_hint_subjects.extend(subjects); + self + } + + /// Verify the revocation state of presented client certificates against the provided + /// certificate revocation lists (CRLs). Calling `with_crls` multiple times appends the + /// given CRLs to the existing collection. + /// + /// By default all certificates in the verified chain built from the presented client + /// certificate to a trust anchor will have their revocation status checked. Calling + /// [`only_check_end_entity_revocation`][Self::only_check_end_entity_revocation] will + /// change this behavior to only check the end entity client certificate. + /// + /// By default if a certificate's revocation status can not be determined using the + /// configured CRLs, it will be treated as an error. Calling + /// [`allow_unknown_revocation_status`][Self::allow_unknown_revocation_status] will change + /// this behavior to allow unknown revocation status. + pub fn with_crls( + mut self, + crls: impl IntoIterator<Item = CertificateRevocationListDer<'static>>, + ) -> Self { + self.crls.extend(crls); + self + } + + /// Only check the end entity certificate revocation status when using CRLs. + /// + /// If CRLs are provided using [`with_crls`][Self::with_crls] only check the end entity + /// certificate's revocation status. Overrides the default behavior of checking revocation + /// status for each certificate in the verified chain built to a trust anchor + /// (excluding the trust anchor itself). + /// + /// If no CRLs are provided then this setting has no effect. Neither the end entity certificate + /// or any intermediates will have revocation status checked. + pub fn only_check_end_entity_revocation(mut self) -> Self { + self.revocation_check_depth = RevocationCheckDepth::EndEntity; + self + } + + /// Allow unauthenticated clients to connect. + /// + /// Clients that offer a client certificate issued by a trusted root, and clients that offer no + /// client certificate will be allowed to connect. + pub fn allow_unauthenticated(mut self) -> Self { + self.anon_policy = AnonymousClientPolicy::Allow; + self + } + + /// Allow unknown certificate revocation status when using CRLs. + /// + /// If CRLs are provided with [`with_crls`][Self::with_crls] and it isn't possible to + /// determine the revocation status of a certificate, do not treat it as an error condition. + /// Overrides the default behavior where unknown revocation status is considered an error. + /// + /// If no CRLs are provided then this setting has no effect as revocation status checks + /// are not performed. + pub fn allow_unknown_revocation_status(mut self) -> Self { + self.unknown_revocation_policy = UnknownStatusPolicy::Allow; + self + } + + /// Enforce the CRL nextUpdate field (i.e. expiration) + /// + /// If CRLs are provided with [`with_crls`][Self::with_crls] and the verification time is + /// beyond the time in the CRL nextUpdate field, it is expired and treated as an error condition. + /// Overrides the default behavior where expired CRLs are not treated as an error condition. + /// + /// If no CRLs are provided then this setting has no effect as revocation status checks + /// are not performed. + pub fn enforce_revocation_expiration(mut self) -> Self { + self.revocation_expiration_policy = ExpirationPolicy::Enforce; + self + } + + /// Build a client certificate verifier. The built verifier will be used for the server to offer + /// client certificate authentication, to control how offered client certificates are validated, + /// and to determine what to do with anonymous clients that do not respond to the client + /// certificate authentication offer with a client certificate. + /// + /// If `with_signature_verification_algorithms` was not called on the builder, a default set of + /// signature verification algorithms is used, controlled by the selected [`CryptoProvider`]. + /// + /// Once built, the provided `Arc<dyn ClientCertVerifier>` can be used with a Rustls + /// [`ServerConfig`] to configure client certificate validation using + /// [`with_client_cert_verifier`][ConfigBuilder<ClientConfig, WantsVerifier>::with_client_cert_verifier]. + /// + /// # Errors + /// This function will return a [`VerifierBuilderError`] if: + /// 1. No trust anchors have been provided. + /// 2. DER encoded CRLs have been provided that can not be parsed successfully. + pub fn build(self) -> Result<Arc<dyn ClientCertVerifier>, VerifierBuilderError> { + if self.roots.is_empty() { + return Err(VerifierBuilderError::NoRootAnchors); + } + + Ok(Arc::new(WebPkiClientVerifier::new( + self.roots, + self.root_hint_subjects, + parse_crls(self.crls)?, + self.revocation_check_depth, + self.unknown_revocation_policy, + self.revocation_expiration_policy, + self.anon_policy, + self.supported_algs, + ))) + } +} + +/// A client certificate verifier that uses the `webpki` crate[^1] to perform client certificate +/// validation. +/// +/// It must be created via the [`WebPkiClientVerifier::builder()`] or +/// [`WebPkiClientVerifier::builder_with_provider()`] functions. +/// +/// Once built, the provided `Arc<dyn ClientCertVerifier>` can be used with a Rustls [`ServerConfig`] +/// to configure client certificate validation using [`with_client_cert_verifier`][ConfigBuilder<ClientConfig, WantsVerifier>::with_client_cert_verifier]. +/// +/// Example: +/// +/// To require all clients present a client certificate issued by a trusted CA: +/// ```no_run +/// # #[cfg(any(feature = "ring", feature = "aws_lc_rs"))] { +/// # use rustls::RootCertStore; +/// # use rustls::server::WebPkiClientVerifier; +/// # let roots = RootCertStore::empty(); +/// let client_verifier = WebPkiClientVerifier::builder(roots.into()) +/// .build() +/// .unwrap(); +/// # } +/// ``` +/// +/// Or, to allow clients presenting a client certificate authenticated by a trusted CA, or +/// anonymous clients that present no client certificate: +/// ```no_run +/// # #[cfg(any(feature = "ring", feature = "aws_lc_rs"))] { +/// # use rustls::RootCertStore; +/// # use rustls::server::WebPkiClientVerifier; +/// # let roots = RootCertStore::empty(); +/// let client_verifier = WebPkiClientVerifier::builder(roots.into()) +/// .allow_unauthenticated() +/// .build() +/// .unwrap(); +/// # } +/// ``` +/// +/// If you wish to disable advertising client authentication: +/// ```no_run +/// # use rustls::RootCertStore; +/// # use rustls::server::WebPkiClientVerifier; +/// # let roots = RootCertStore::empty(); +/// let client_verifier = WebPkiClientVerifier::no_client_auth(); +/// ``` +/// +/// You can also configure the client verifier to check for certificate revocation with +/// client certificate revocation lists (CRLs): +/// ```no_run +/// # #[cfg(any(feature = "ring", feature = "aws_lc_rs"))] { +/// # use rustls::RootCertStore; +/// # use rustls::server::{WebPkiClientVerifier}; +/// # let roots = RootCertStore::empty(); +/// # let crls = Vec::new(); +/// let client_verifier = WebPkiClientVerifier::builder(roots.into()) +/// .with_crls(crls) +/// .build() +/// .unwrap(); +/// # } +/// ``` +/// +/// [^1]: <https://github.com/rustls/webpki> +#[derive(Debug)] +pub struct WebPkiClientVerifier { + roots: Arc<RootCertStore>, + root_hint_subjects: Vec<DistinguishedName>, + crls: Vec<CertRevocationList<'static>>, + revocation_check_depth: RevocationCheckDepth, + unknown_revocation_policy: UnknownStatusPolicy, + revocation_expiration_policy: ExpirationPolicy, + anonymous_policy: AnonymousClientPolicy, + supported_algs: WebPkiSupportedAlgorithms, +} + +impl WebPkiClientVerifier { + /// Create a builder for the `webpki` client certificate verifier configuration using + /// the [process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider]. + /// + /// Client certificate authentication will be offered by the server, and client certificates + /// will be verified using the trust anchors found in the provided `roots`. If you + /// wish to disable client authentication use [`WebPkiClientVerifier::no_client_auth()`] instead. + /// + /// Use [`Self::builder_with_provider`] if you wish to specify an explicit provider. + /// + /// For more information, see the [`ClientCertVerifierBuilder`] documentation. + pub fn builder(roots: Arc<RootCertStore>) -> ClientCertVerifierBuilder { + Self::builder_with_provider( + roots, + CryptoProvider::get_default_or_install_from_crate_features().clone(), + ) + } + + /// Create a builder for the `webpki` client certificate verifier configuration using + /// a specified [`CryptoProvider`]. + /// + /// Client certificate authentication will be offered by the server, and client certificates + /// will be verified using the trust anchors found in the provided `roots`. If you + /// wish to disable client authentication use [WebPkiClientVerifier::no_client_auth()] instead. + /// + /// The cryptography used comes from the specified [`CryptoProvider`]. + /// + /// For more information, see the [`ClientCertVerifierBuilder`] documentation. + pub fn builder_with_provider( + roots: Arc<RootCertStore>, + provider: Arc<CryptoProvider>, + ) -> ClientCertVerifierBuilder { + ClientCertVerifierBuilder::new(roots, provider.signature_verification_algorithms) + } + + /// Create a new `WebPkiClientVerifier` that disables client authentication. The server will + /// not offer client authentication and anonymous clients will be accepted. + /// + /// This is in contrast to using `WebPkiClientVerifier::builder().allow_unauthenticated().build()`, + /// which will produce a verifier that will offer client authentication, but not require it. + pub fn no_client_auth() -> Arc<dyn ClientCertVerifier> { + Arc::new(NoClientAuth {}) + } + + /// Construct a new `WebpkiClientVerifier`. + /// + /// * `roots` is a list of trust anchors to use for certificate validation. + /// * `root_hint_subjects` is a list of distinguished names to use for hinting acceptable + /// certificate authority subjects to a client. + /// * `crls` is a `Vec` of owned certificate revocation lists (CRLs) to use for + /// client certificate validation. + /// * `revocation_check_depth` controls which certificates have their revocation status checked + /// when `crls` are provided. + /// * `unknown_revocation_policy` controls how certificates with an unknown revocation status + /// are handled when `crls` are provided. + /// * `anonymous_policy` controls whether client authentication is required, or if anonymous + /// clients can connect. + /// * `supported_algs` specifies which signature verification algorithms should be used. + pub(crate) fn new( + roots: Arc<RootCertStore>, + root_hint_subjects: Vec<DistinguishedName>, + crls: Vec<CertRevocationList<'static>>, + revocation_check_depth: RevocationCheckDepth, + unknown_revocation_policy: UnknownStatusPolicy, + revocation_expiration_policy: ExpirationPolicy, + anonymous_policy: AnonymousClientPolicy, + supported_algs: WebPkiSupportedAlgorithms, + ) -> Self { + Self { + roots, + root_hint_subjects, + crls, + revocation_check_depth, + unknown_revocation_policy, + revocation_expiration_policy, + anonymous_policy, + supported_algs, + } + } +} + +impl ClientCertVerifier for WebPkiClientVerifier { + fn offer_client_auth(&self) -> bool { + true + } + + fn client_auth_mandatory(&self) -> bool { + match self.anonymous_policy { + AnonymousClientPolicy::Allow => false, + AnonymousClientPolicy::Deny => true, + } + } + + fn root_hint_subjects(&self) -> &[DistinguishedName] { + &self.root_hint_subjects + } + + fn verify_client_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + now: UnixTime, + ) -> Result<ClientCertVerified, Error> { + let cert = ParsedCertificate::try_from(end_entity)?; + + let crl_refs = self.crls.iter().collect::<Vec<_>>(); + + let revocation = if self.crls.is_empty() { + None + } else { + Some( + webpki::RevocationOptionsBuilder::new(&crl_refs) + // Note: safe to unwrap here - new is only fallible if no CRLs are provided + // and we verify this above. + .unwrap() + .with_depth(self.revocation_check_depth) + .with_status_policy(self.unknown_revocation_policy) + .with_expiration_policy(self.revocation_expiration_policy) + .build(), + ) + }; + + cert.0 + .verify_for_usage( + self.supported_algs.all, + &self.roots.roots, + intermediates, + now, + webpki::KeyUsage::client_auth(), + revocation, + None, + ) + .map_err(pki_error) + .map(|_| ClientCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + verify_tls12_signature(message, cert, dss, &self.supported_algs) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + verify_tls13_signature(message, cert, dss, &self.supported_algs) + } + + fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { + self.supported_algs.supported_schemes() + } +} + +/// Controls how the [WebPkiClientVerifier] handles anonymous clients. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(crate) enum AnonymousClientPolicy { + /// Clients that do not present a client certificate are allowed. + Allow, + /// Clients that do not present a client certificate are denied. + Deny, +} + +#[cfg(test)] +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use std::prelude::v1::*; + use std::{format, println, vec}; + + use pki_types::pem::PemObject; + use pki_types::{CertificateDer, CertificateRevocationListDer}; + + use super::{WebPkiClientVerifier, provider}; + use crate::RootCertStore; + use crate::server::VerifierBuilderError; + use crate::sync::Arc; + + fn load_crls(crls_der: &[&[u8]]) -> Vec<CertificateRevocationListDer<'static>> { + crls_der + .iter() + .map(|pem_bytes| CertificateRevocationListDer::from_pem_slice(pem_bytes).unwrap()) + .collect() + } + + fn test_crls() -> Vec<CertificateRevocationListDer<'static>> { + load_crls(&[ + include_bytes!("../../../test-ca/ecdsa-p256/client.revoked.crl.pem").as_slice(), + include_bytes!("../../../test-ca/rsa-2048/client.revoked.crl.pem").as_slice(), + ]) + } + + fn load_roots(roots_der: &[&[u8]]) -> Arc<RootCertStore> { + let mut roots = RootCertStore::empty(); + roots_der.iter().for_each(|der| { + roots + .add(CertificateDer::from(der.to_vec())) + .unwrap() + }); + roots.into() + } + + fn test_roots() -> Arc<RootCertStore> { + load_roots(&[ + include_bytes!("../../../test-ca/ecdsa-p256/ca.der").as_slice(), + include_bytes!("../../../test-ca/rsa-2048/ca.der").as_slice(), + ]) + } + + #[test] + fn test_client_verifier_no_auth() { + // We should be able to build a verifier that turns off client authentication. + WebPkiClientVerifier::no_client_auth(); + } + + #[test] + fn test_client_verifier_required_auth() { + // We should be able to build a verifier that requires client authentication, and does + // no revocation checking. + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_client_verifier_optional_auth() { + // We should be able to build a verifier that allows client authentication, and anonymous + // access, and does no revocation checking. + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .allow_unauthenticated(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_client_verifier_without_crls_required_auth() { + // We should be able to build a verifier that requires client authentication, and does + // no revocation checking, that hasn't been configured to determine how to handle + // unauthenticated clients yet. + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_client_verifier_without_crls_opptional_auth() { + // We should be able to build a verifier that allows client authentication, + // and anonymous access, that does no revocation checking. + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .allow_unauthenticated(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_with_invalid_crls() { + // Trying to build a client verifier with invalid CRLs should error at build time. + let result = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .with_crls(vec![CertificateRevocationListDer::from(vec![0xFF])]) + .build(); + assert!(matches!(result, Err(VerifierBuilderError::InvalidCrl(_)))); + } + + #[test] + fn test_with_crls_multiple_calls() { + // We should be able to call `with_crls` on a client verifier multiple times. + let initial_crls = test_crls(); + let extra_crls = + load_crls(&[ + include_bytes!("../../../test-ca/eddsa/client.revoked.crl.pem").as_slice(), + ]); + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .with_crls(initial_crls.clone()) + .with_crls(extra_crls.clone()); + + // There should be the expected number of crls. + assert_eq!(builder.crls.len(), initial_crls.len() + extra_crls.len()); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_client_verifier_with_crls_required_auth_implicit() { + // We should be able to build a verifier that requires client authentication, and that does + // revocation checking with CRLs, and that does not allow any anonymous access. + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .with_crls(test_crls()); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_client_verifier_with_crls_optional_auth() { + // We should be able to build a verifier that supports client authentication, that does + // revocation checking with CRLs, and that allows anonymous access. + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .with_crls(test_crls()) + .allow_unauthenticated(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_client_verifier_ee_only() { + // We should be able to build a client verifier that only checks EE revocation status. + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .with_crls(test_crls()) + .only_check_end_entity_revocation(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_client_verifier_allow_unknown() { + // We should be able to build a client verifier that allows unknown revocation status + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .with_crls(test_crls()) + .allow_unknown_revocation_status(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_client_verifier_enforce_expiration() { + // We should be able to build a client verifier that allows unknown revocation status + let builder = WebPkiClientVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .with_crls(test_crls()) + .enforce_revocation_expiration(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_builder_no_roots() { + // Trying to create a client verifier builder with no trust anchors should fail at build time + let result = WebPkiClientVerifier::builder_with_provider( + RootCertStore::empty().into(), + provider::default_provider().into(), + ) + .build(); + assert!(matches!(result, Err(VerifierBuilderError::NoRootAnchors))); + } + + #[test] + fn smoke() { + let all = vec![ + VerifierBuilderError::NoRootAnchors, + VerifierBuilderError::InvalidCrl(crate::CertRevocationListError::ParseError), + ]; + + for err in all { + let _ = format!("{err:?}"); + let _ = format!("{err}"); + } + } +} diff --git a/vendor/rustls/src/webpki/mod.rs b/vendor/rustls/src/webpki/mod.rs new file mode 100644 index 00000000..24960a1e --- /dev/null +++ b/vendor/rustls/src/webpki/mod.rs @@ -0,0 +1,313 @@ +use alloc::vec::Vec; +use core::fmt; + +use pki_types::CertificateRevocationListDer; +use webpki::{CertRevocationList, InvalidNameContext, OwnedCertRevocationList}; + +use crate::error::{ + CertRevocationListError, CertificateError, Error, ExtendedKeyPurpose, OtherError, +}; +#[cfg(feature = "std")] +use crate::sync::Arc; + +mod anchors; +mod client_verifier; +mod server_verifier; +mod verify; + +pub use anchors::RootCertStore; +pub use client_verifier::{ClientCertVerifierBuilder, WebPkiClientVerifier}; +pub use server_verifier::{ServerCertVerifierBuilder, WebPkiServerVerifier}; +// Conditionally exported from crate. +#[allow(unreachable_pub)] +pub use verify::{ + ParsedCertificate, verify_server_cert_signed_by_trust_anchor, verify_server_name, +}; +pub use verify::{ + WebPkiSupportedAlgorithms, verify_tls12_signature, verify_tls13_signature, + verify_tls13_signature_with_raw_key, +}; + +/// An error that can occur when building a certificate verifier. +#[derive(Debug, Clone)] +#[non_exhaustive] +pub enum VerifierBuilderError { + /// No root trust anchors were provided. + NoRootAnchors, + /// A provided CRL could not be parsed. + InvalidCrl(CertRevocationListError), +} + +impl From<CertRevocationListError> for VerifierBuilderError { + fn from(value: CertRevocationListError) -> Self { + Self::InvalidCrl(value) + } +} + +impl fmt::Display for VerifierBuilderError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::NoRootAnchors => write!(f, "no root trust anchors were provided"), + Self::InvalidCrl(e) => write!(f, "provided CRL could not be parsed: {e:?}"), + } + } +} + +#[cfg(feature = "std")] +impl std::error::Error for VerifierBuilderError {} + +fn pki_error(error: webpki::Error) -> Error { + use webpki::Error::*; + match error { + BadDer | BadDerTime | TrailingData(_) => CertificateError::BadEncoding.into(), + CertNotValidYet { time, not_before } => { + CertificateError::NotValidYetContext { time, not_before }.into() + } + CertExpired { time, not_after } => { + CertificateError::ExpiredContext { time, not_after }.into() + } + InvalidCertValidity => CertificateError::Expired.into(), + UnknownIssuer => CertificateError::UnknownIssuer.into(), + CertNotValidForName(InvalidNameContext { + expected, + presented, + }) => CertificateError::NotValidForNameContext { + expected, + presented, + } + .into(), + CertRevoked => CertificateError::Revoked.into(), + UnknownRevocationStatus => CertificateError::UnknownRevocationStatus.into(), + CrlExpired { time, next_update } => { + CertificateError::ExpiredRevocationListContext { time, next_update }.into() + } + IssuerNotCrlSigner => CertRevocationListError::IssuerInvalidForCrl.into(), + + InvalidSignatureForPublicKey => CertificateError::BadSignature.into(), + #[allow(deprecated)] + UnsupportedSignatureAlgorithm | UnsupportedSignatureAlgorithmForPublicKey => { + CertificateError::UnsupportedSignatureAlgorithm.into() + } + UnsupportedSignatureAlgorithmContext(cx) => { + CertificateError::UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: cx.signature_algorithm_id, + supported_algorithms: cx.supported_algorithms, + } + .into() + } + UnsupportedSignatureAlgorithmForPublicKeyContext(cx) => { + CertificateError::UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: cx.signature_algorithm_id, + public_key_algorithm_id: cx.public_key_algorithm_id, + } + .into() + } + + InvalidCrlSignatureForPublicKey => CertRevocationListError::BadSignature.into(), + #[allow(deprecated)] + UnsupportedCrlSignatureAlgorithm | UnsupportedCrlSignatureAlgorithmForPublicKey => { + CertRevocationListError::UnsupportedSignatureAlgorithm.into() + } + UnsupportedCrlSignatureAlgorithmContext(cx) => { + CertRevocationListError::UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: cx.signature_algorithm_id, + supported_algorithms: cx.supported_algorithms, + } + .into() + } + UnsupportedCrlSignatureAlgorithmForPublicKeyContext(cx) => { + CertRevocationListError::UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: cx.signature_algorithm_id, + public_key_algorithm_id: cx.public_key_algorithm_id, + } + .into() + } + + #[allow(deprecated)] + RequiredEkuNotFound => CertificateError::InvalidPurpose.into(), + RequiredEkuNotFoundContext(webpki::RequiredEkuNotFoundContext { required, present }) => { + CertificateError::InvalidPurposeContext { + required: ExtendedKeyPurpose::for_values(required.oid_values()), + presented: present + .into_iter() + .map(|eku| ExtendedKeyPurpose::for_values(eku.into_iter())) + .collect(), + } + .into() + } + + _ => CertificateError::Other(OtherError( + #[cfg(feature = "std")] + Arc::new(error), + )) + .into(), + } +} + +fn crl_error(e: webpki::Error) -> CertRevocationListError { + use webpki::Error::*; + match e { + InvalidCrlSignatureForPublicKey => CertRevocationListError::BadSignature, + #[allow(deprecated)] + UnsupportedCrlSignatureAlgorithm | UnsupportedCrlSignatureAlgorithmForPublicKey => { + CertRevocationListError::UnsupportedSignatureAlgorithm + } + UnsupportedCrlSignatureAlgorithmContext(cx) => { + CertRevocationListError::UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: cx.signature_algorithm_id, + supported_algorithms: cx.supported_algorithms, + } + } + UnsupportedSignatureAlgorithmForPublicKeyContext(cx) => { + CertRevocationListError::UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: cx.signature_algorithm_id, + public_key_algorithm_id: cx.public_key_algorithm_id, + } + } + InvalidCrlNumber => CertRevocationListError::InvalidCrlNumber, + InvalidSerialNumber => CertRevocationListError::InvalidRevokedCertSerialNumber, + IssuerNotCrlSigner => CertRevocationListError::IssuerInvalidForCrl, + MalformedExtensions | BadDer | BadDerTime => CertRevocationListError::ParseError, + UnsupportedCriticalExtension => CertRevocationListError::UnsupportedCriticalExtension, + UnsupportedCrlVersion => CertRevocationListError::UnsupportedCrlVersion, + UnsupportedDeltaCrl => CertRevocationListError::UnsupportedDeltaCrl, + UnsupportedIndirectCrl => CertRevocationListError::UnsupportedIndirectCrl, + UnsupportedRevocationReason => CertRevocationListError::UnsupportedRevocationReason, + + _ => CertRevocationListError::Other(OtherError( + #[cfg(feature = "std")] + Arc::new(e), + )), + } +} + +fn parse_crls( + crls: Vec<CertificateRevocationListDer<'_>>, +) -> Result<Vec<CertRevocationList<'_>>, CertRevocationListError> { + crls.iter() + .map(|der| OwnedCertRevocationList::from_der(der.as_ref()).map(Into::into)) + .collect::<Result<Vec<_>, _>>() + .map_err(crl_error) +} + +#[cfg(test)] +mod tests { + use super::*; + use alloc::vec; + + #[test] + fn pki_crl_errors() { + // CRL signature errors should be turned into BadSignature. + assert_eq!( + pki_error(webpki::Error::InvalidCrlSignatureForPublicKey), + Error::InvalidCertRevocationList(CertRevocationListError::BadSignature), + ); + + #[allow(deprecated)] + { + assert_eq!( + pki_error(webpki::Error::UnsupportedCrlSignatureAlgorithm), + Error::InvalidCertRevocationList( + CertRevocationListError::UnsupportedSignatureAlgorithm + ), + ); + assert_eq!( + pki_error(webpki::Error::UnsupportedCrlSignatureAlgorithmForPublicKey), + Error::InvalidCertRevocationList( + CertRevocationListError::UnsupportedSignatureAlgorithm + ), + ); + } + + assert_eq!( + pki_error(webpki::Error::UnsupportedCrlSignatureAlgorithmContext( + webpki::UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: vec![], + supported_algorithms: vec![], + } + )), + Error::InvalidCertRevocationList( + CertRevocationListError::UnsupportedSignatureAlgorithmContext { + signature_algorithm_id: vec![], + supported_algorithms: vec![], + } + ) + ); + assert_eq!( + pki_error( + webpki::Error::UnsupportedCrlSignatureAlgorithmForPublicKeyContext( + webpki::UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: vec![], + public_key_algorithm_id: vec![], + } + ) + ), + Error::InvalidCertRevocationList( + CertRevocationListError::UnsupportedSignatureAlgorithmForPublicKeyContext { + signature_algorithm_id: vec![], + public_key_algorithm_id: vec![], + } + ) + ); + + // Revoked cert errors should be turned into Revoked. + assert_eq!( + pki_error(webpki::Error::CertRevoked), + Error::InvalidCertificate(CertificateError::Revoked), + ); + + // Issuer not CRL signer errors should be turned into IssuerInvalidForCrl + assert_eq!( + pki_error(webpki::Error::IssuerNotCrlSigner), + Error::InvalidCertRevocationList(CertRevocationListError::IssuerInvalidForCrl) + ); + } + + #[test] + fn crl_error_from_webpki() { + use CertRevocationListError::*; + #[allow(deprecated)] + let testcases = &[ + (webpki::Error::InvalidCrlSignatureForPublicKey, BadSignature), + ( + webpki::Error::UnsupportedCrlSignatureAlgorithm, + UnsupportedSignatureAlgorithm, + ), + ( + webpki::Error::UnsupportedCrlSignatureAlgorithmForPublicKey, + UnsupportedSignatureAlgorithm, + ), + (webpki::Error::InvalidCrlNumber, InvalidCrlNumber), + ( + webpki::Error::InvalidSerialNumber, + InvalidRevokedCertSerialNumber, + ), + (webpki::Error::IssuerNotCrlSigner, IssuerInvalidForCrl), + (webpki::Error::MalformedExtensions, ParseError), + (webpki::Error::BadDer, ParseError), + (webpki::Error::BadDerTime, ParseError), + ( + webpki::Error::UnsupportedCriticalExtension, + UnsupportedCriticalExtension, + ), + (webpki::Error::UnsupportedCrlVersion, UnsupportedCrlVersion), + (webpki::Error::UnsupportedDeltaCrl, UnsupportedDeltaCrl), + ( + webpki::Error::UnsupportedIndirectCrl, + UnsupportedIndirectCrl, + ), + ( + webpki::Error::UnsupportedRevocationReason, + UnsupportedRevocationReason, + ), + ]; + for t in testcases { + assert_eq!(crl_error(t.0.clone()), t.1); + } + + assert!(matches!( + crl_error(webpki::Error::NameConstraintViolation), + Other(..) + )); + } +} diff --git a/vendor/rustls/src/webpki/server_verifier.rs b/vendor/rustls/src/webpki/server_verifier.rs new file mode 100644 index 00000000..5aa92350 --- /dev/null +++ b/vendor/rustls/src/webpki/server_verifier.rs @@ -0,0 +1,448 @@ +use alloc::vec::Vec; + +use pki_types::{CertificateDer, CertificateRevocationListDer, ServerName, UnixTime}; +use webpki::{CertRevocationList, ExpirationPolicy, RevocationCheckDepth, UnknownStatusPolicy}; + +use crate::crypto::{CryptoProvider, WebPkiSupportedAlgorithms}; +use crate::log::trace; +use crate::sync::Arc; +use crate::verify::{ + DigitallySignedStruct, HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, +}; +use crate::webpki::verify::{ + ParsedCertificate, verify_server_cert_signed_by_trust_anchor_impl, verify_tls12_signature, + verify_tls13_signature, +}; +use crate::webpki::{VerifierBuilderError, parse_crls, verify_server_name}; +#[cfg(doc)] +use crate::{ConfigBuilder, ServerConfig, crypto}; +use crate::{Error, RootCertStore, SignatureScheme}; + +/// A builder for configuring a `webpki` server certificate verifier. +/// +/// For more information, see the [`WebPkiServerVerifier`] documentation. +#[derive(Debug, Clone)] +pub struct ServerCertVerifierBuilder { + roots: Arc<RootCertStore>, + crls: Vec<CertificateRevocationListDer<'static>>, + revocation_check_depth: RevocationCheckDepth, + unknown_revocation_policy: UnknownStatusPolicy, + revocation_expiration_policy: ExpirationPolicy, + supported_algs: WebPkiSupportedAlgorithms, +} + +impl ServerCertVerifierBuilder { + pub(crate) fn new( + roots: Arc<RootCertStore>, + supported_algs: WebPkiSupportedAlgorithms, + ) -> Self { + Self { + roots, + crls: Vec::new(), + revocation_check_depth: RevocationCheckDepth::Chain, + unknown_revocation_policy: UnknownStatusPolicy::Deny, + revocation_expiration_policy: ExpirationPolicy::Ignore, + supported_algs, + } + } + + /// Verify the revocation state of presented client certificates against the provided + /// certificate revocation lists (CRLs). Calling `with_crls` multiple times appends the + /// given CRLs to the existing collection. + pub fn with_crls( + mut self, + crls: impl IntoIterator<Item = CertificateRevocationListDer<'static>>, + ) -> Self { + self.crls.extend(crls); + self + } + + /// Only check the end entity certificate revocation status when using CRLs. + /// + /// If CRLs are provided using [`with_crls`][Self::with_crls] only check the end entity + /// certificate's revocation status. Overrides the default behavior of checking revocation + /// status for each certificate in the verified chain built to a trust anchor + /// (excluding the trust anchor itself). + /// + /// If no CRLs are provided then this setting has no effect. Neither the end entity certificate + /// or any intermediates will have revocation status checked. + pub fn only_check_end_entity_revocation(mut self) -> Self { + self.revocation_check_depth = RevocationCheckDepth::EndEntity; + self + } + + /// Allow unknown certificate revocation status when using CRLs. + /// + /// If CRLs are provided with [`with_crls`][Self::with_crls] and it isn't possible to + /// determine the revocation status of a certificate, do not treat it as an error condition. + /// Overrides the default behavior where unknown revocation status is considered an error. + /// + /// If no CRLs are provided then this setting has no effect as revocation status checks + /// are not performed. + pub fn allow_unknown_revocation_status(mut self) -> Self { + self.unknown_revocation_policy = UnknownStatusPolicy::Allow; + self + } + + /// Enforce the CRL nextUpdate field (i.e. expiration) + /// + /// If CRLs are provided with [`with_crls`][Self::with_crls] and the verification time is + /// beyond the time in the CRL nextUpdate field, it is expired and treated as an error condition. + /// Overrides the default behavior where expired CRLs are not treated as an error condition. + /// + /// If no CRLs are provided then this setting has no effect as revocation status checks + /// are not performed. + pub fn enforce_revocation_expiration(mut self) -> Self { + self.revocation_expiration_policy = ExpirationPolicy::Enforce; + self + } + + /// Build a server certificate verifier, allowing control over the root certificates to use as + /// trust anchors, and to control how server certificate revocation checking is performed. + /// + /// If `with_signature_verification_algorithms` was not called on the builder, a default set of + /// signature verification algorithms is used, controlled by the selected [`crypto::CryptoProvider`]. + /// + /// Once built, the provided `Arc<dyn ServerCertVerifier>` can be used with a Rustls + /// [`ServerConfig`] to configure client certificate validation using + /// [`with_client_cert_verifier`][ConfigBuilder<ClientConfig, WantsVerifier>::with_client_cert_verifier]. + /// + /// # Errors + /// This function will return a [`VerifierBuilderError`] if: + /// 1. No trust anchors have been provided. + /// 2. DER encoded CRLs have been provided that can not be parsed successfully. + pub fn build(self) -> Result<Arc<WebPkiServerVerifier>, VerifierBuilderError> { + if self.roots.is_empty() { + return Err(VerifierBuilderError::NoRootAnchors); + } + + Ok(WebPkiServerVerifier::new( + self.roots, + parse_crls(self.crls)?, + self.revocation_check_depth, + self.unknown_revocation_policy, + self.revocation_expiration_policy, + self.supported_algs, + ) + .into()) + } +} + +/// Default `ServerCertVerifier`, see the trait impl for more information. +#[allow(unreachable_pub)] +#[derive(Debug)] +pub struct WebPkiServerVerifier { + roots: Arc<RootCertStore>, + crls: Vec<CertRevocationList<'static>>, + revocation_check_depth: RevocationCheckDepth, + unknown_revocation_policy: UnknownStatusPolicy, + revocation_expiration_policy: ExpirationPolicy, + supported: WebPkiSupportedAlgorithms, +} + +#[allow(unreachable_pub)] +impl WebPkiServerVerifier { + /// Create a builder for the `webpki` server certificate verifier configuration using + /// the [process-default `CryptoProvider`][CryptoProvider#using-the-per-process-default-cryptoprovider]. + /// + /// Server certificates will be verified using the trust anchors found in the provided `roots`. + /// + /// Use [`Self::builder_with_provider`] if you wish to specify an explicit provider. + /// + /// For more information, see the [`ServerCertVerifierBuilder`] documentation. + pub fn builder(roots: Arc<RootCertStore>) -> ServerCertVerifierBuilder { + Self::builder_with_provider( + roots, + CryptoProvider::get_default_or_install_from_crate_features().clone(), + ) + } + + /// Create a builder for the `webpki` server certificate verifier configuration using + /// a specified [`CryptoProvider`]. + /// + /// Server certificates will be verified using the trust anchors found in the provided `roots`. + /// + /// The cryptography used comes from the specified [`CryptoProvider`]. + /// + /// For more information, see the [`ServerCertVerifierBuilder`] documentation. + pub fn builder_with_provider( + roots: Arc<RootCertStore>, + provider: Arc<CryptoProvider>, + ) -> ServerCertVerifierBuilder { + ServerCertVerifierBuilder::new(roots, provider.signature_verification_algorithms) + } + + /// Short-cut for creating a `WebPkiServerVerifier` that does not perform certificate revocation + /// checking, avoiding the need to use a builder. + pub(crate) fn new_without_revocation( + roots: impl Into<Arc<RootCertStore>>, + supported_algs: WebPkiSupportedAlgorithms, + ) -> Self { + Self::new( + roots, + Vec::default(), + RevocationCheckDepth::Chain, + UnknownStatusPolicy::Allow, + ExpirationPolicy::Ignore, + supported_algs, + ) + } + + /// Constructs a new `WebPkiServerVerifier`. + /// + /// * `roots` is the set of trust anchors to trust for issuing server certs. + /// * `crls` are a vec of owned certificate revocation lists (CRLs) to use for + /// client certificate validation. + /// * `revocation_check_depth` controls which certificates have their revocation status checked + /// when `crls` are provided. + /// * `unknown_revocation_policy` controls how certificates with an unknown revocation status + /// are handled when `crls` are provided. + /// * `supported` is the set of supported algorithms that will be used for + /// certificate verification and TLS handshake signature verification. + pub(crate) fn new( + roots: impl Into<Arc<RootCertStore>>, + crls: Vec<CertRevocationList<'static>>, + revocation_check_depth: RevocationCheckDepth, + unknown_revocation_policy: UnknownStatusPolicy, + revocation_expiration_policy: ExpirationPolicy, + supported: WebPkiSupportedAlgorithms, + ) -> Self { + Self { + roots: roots.into(), + crls, + revocation_check_depth, + unknown_revocation_policy, + revocation_expiration_policy, + supported, + } + } +} + +impl ServerCertVerifier for WebPkiServerVerifier { + /// Will verify the certificate is valid in the following ways: + /// - Signed by a trusted `RootCertStore` CA + /// - Not Expired + /// - Valid for DNS entry + /// - Valid revocation status (if applicable). + /// + /// Depending on the verifier's configuration revocation status checking may be performed for + /// each certificate in the chain to a root CA (excluding the root itself), or only the + /// end entity certificate. Similarly, unknown revocation status may be treated as an error + /// or allowed based on configuration. + fn verify_server_cert( + &self, + end_entity: &CertificateDer<'_>, + intermediates: &[CertificateDer<'_>], + server_name: &ServerName<'_>, + ocsp_response: &[u8], + now: UnixTime, + ) -> Result<ServerCertVerified, Error> { + let cert = ParsedCertificate::try_from(end_entity)?; + + let crl_refs = self.crls.iter().collect::<Vec<_>>(); + + let revocation = if self.crls.is_empty() { + None + } else { + // Note: unwrap here is safe because RevocationOptionsBuilder only errors when given + // empty CRLs. + Some( + webpki::RevocationOptionsBuilder::new(crl_refs.as_slice()) + // Note: safe to unwrap here - new is only fallible if no CRLs are provided + // and we verify this above. + .unwrap() + .with_depth(self.revocation_check_depth) + .with_status_policy(self.unknown_revocation_policy) + .with_expiration_policy(self.revocation_expiration_policy) + .build(), + ) + }; + + // Note: we use the crate-internal `_impl` fn here in order to provide revocation + // checking information, if applicable. + verify_server_cert_signed_by_trust_anchor_impl( + &cert, + &self.roots, + intermediates, + revocation, + now, + self.supported.all, + )?; + + if !ocsp_response.is_empty() { + trace!("Unvalidated OCSP response: {:?}", ocsp_response.to_vec()); + } + + verify_server_name(&cert, server_name)?; + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + verify_tls12_signature(message, cert, dss, &self.supported) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, Error> { + verify_tls13_signature(message, cert, dss, &self.supported) + } + + fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { + self.supported.supported_schemes() + } +} + +#[cfg(test)] +#[macro_rules_attribute::apply(test_for_each_provider)] +mod tests { + use std::prelude::v1::*; + use std::{println, vec}; + + use pki_types::pem::PemObject; + use pki_types::{CertificateDer, CertificateRevocationListDer}; + + use super::{VerifierBuilderError, WebPkiServerVerifier, provider}; + use crate::RootCertStore; + use crate::sync::Arc; + + fn load_crls(crls_der: &[&[u8]]) -> Vec<CertificateRevocationListDer<'static>> { + crls_der + .iter() + .map(|pem_bytes| CertificateRevocationListDer::from_pem_slice(pem_bytes).unwrap()) + .collect() + } + + fn test_crls() -> Vec<CertificateRevocationListDer<'static>> { + load_crls(&[ + include_bytes!("../../../test-ca/ecdsa-p256/client.revoked.crl.pem").as_slice(), + include_bytes!("../../../test-ca/rsa-2048/client.revoked.crl.pem").as_slice(), + ]) + } + + fn load_roots(roots_der: &[&[u8]]) -> Arc<RootCertStore> { + let mut roots = RootCertStore::empty(); + roots_der.iter().for_each(|der| { + roots + .add(CertificateDer::from(der.to_vec())) + .unwrap() + }); + roots.into() + } + + fn test_roots() -> Arc<RootCertStore> { + load_roots(&[ + include_bytes!("../../../test-ca/ecdsa-p256/ca.der").as_slice(), + include_bytes!("../../../test-ca/rsa-2048/ca.der").as_slice(), + ]) + } + + #[test] + fn test_with_invalid_crls() { + // Trying to build a server verifier with invalid CRLs should error at build time. + let result = WebPkiServerVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .with_crls(vec![CertificateRevocationListDer::from(vec![0xFF])]) + .build(); + assert!(matches!(result, Err(VerifierBuilderError::InvalidCrl(_)))); + } + + #[test] + fn test_with_crls_multiple_calls() { + // We should be able to call `with_crls` on a server verifier multiple times. + let initial_crls = test_crls(); + let extra_crls = + load_crls(&[ + include_bytes!("../../../test-ca/eddsa/client.revoked.crl.pem").as_slice(), + ]); + + let builder = WebPkiServerVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .with_crls(initial_crls.clone()) + .with_crls(extra_crls.clone()); + + // There should be the expected number of crls. + assert_eq!(builder.crls.len(), initial_crls.len() + extra_crls.len()); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_builder_no_roots() { + // Trying to create a server verifier builder with no trust anchors should fail at build time + let result = WebPkiServerVerifier::builder_with_provider( + RootCertStore::empty().into(), + provider::default_provider().into(), + ) + .build(); + assert!(matches!(result, Err(VerifierBuilderError::NoRootAnchors))); + } + + #[test] + fn test_server_verifier_ee_only() { + // We should be able to build a server cert. verifier that only checks the EE cert. + let builder = WebPkiServerVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .only_check_end_entity_revocation(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_server_verifier_allow_unknown() { + // We should be able to build a server cert. verifier that allows unknown revocation + // status. + let builder = WebPkiServerVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .allow_unknown_revocation_status(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_server_verifier_allow_unknown_ee_only() { + // We should be able to build a server cert. verifier that allows unknown revocation + // status and only checks the EE cert. + let builder = WebPkiServerVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .allow_unknown_revocation_status() + .only_check_end_entity_revocation(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } + + #[test] + fn test_server_verifier_enforce_expiration() { + // We should be able to build a server cert. verifier that allows unknown revocation + // status. + let builder = WebPkiServerVerifier::builder_with_provider( + test_roots(), + provider::default_provider().into(), + ) + .enforce_revocation_expiration(); + // The builder should be Debug. + println!("{builder:?}"); + builder.build().unwrap(); + } +} diff --git a/vendor/rustls/src/webpki/verify.rs b/vendor/rustls/src/webpki/verify.rs new file mode 100644 index 00000000..5c3e79be --- /dev/null +++ b/vendor/rustls/src/webpki/verify.rs @@ -0,0 +1,289 @@ +use alloc::vec::Vec; +use core::fmt; + +use pki_types::{ + CertificateDer, ServerName, SignatureVerificationAlgorithm, SubjectPublicKeyInfoDer, UnixTime, +}; + +use super::anchors::RootCertStore; +use super::pki_error; +use crate::enums::SignatureScheme; +use crate::error::{Error, PeerMisbehaved}; +use crate::verify::{DigitallySignedStruct, HandshakeSignatureValid}; + +/// Verify that the end-entity certificate `end_entity` is a valid server cert +/// and chains to at least one of the trust anchors in the `roots` [RootCertStore]. +/// +/// This function is primarily useful when building a custom certificate verifier. It +/// performs **no revocation checking**. Implementers must handle this themselves, +/// along with checking that the server certificate is valid for the subject name +/// being used (see [`verify_server_name`]). +/// +/// `intermediates` contains all certificates other than `end_entity` that +/// were sent as part of the server's `Certificate` message. It is in the +/// same order that the server sent them and may be empty. +#[allow(dead_code)] +pub fn verify_server_cert_signed_by_trust_anchor( + cert: &ParsedCertificate<'_>, + roots: &RootCertStore, + intermediates: &[CertificateDer<'_>], + now: UnixTime, + supported_algs: &[&dyn SignatureVerificationAlgorithm], +) -> Result<(), Error> { + verify_server_cert_signed_by_trust_anchor_impl( + cert, + roots, + intermediates, + None, // No revocation checking supported with this API. + now, + supported_algs, + ) +} + +/// Verify that the `end_entity` has an alternative name matching the `server_name`. +/// +/// Note: this only verifies the name and should be used in conjunction with more verification +/// like [verify_server_cert_signed_by_trust_anchor] +pub fn verify_server_name( + cert: &ParsedCertificate<'_>, + server_name: &ServerName<'_>, +) -> Result<(), Error> { + cert.0 + .verify_is_valid_for_subject_name(server_name) + .map_err(pki_error) +} + +/// Describes which `webpki` signature verification algorithms are supported and +/// how they map to TLS [`SignatureScheme`]s. +#[derive(Clone, Copy)] +#[allow(unreachable_pub)] +pub struct WebPkiSupportedAlgorithms { + /// A list of all supported signature verification algorithms. + /// + /// Used for verifying certificate chains. + /// + /// The order of this list is not significant. + pub all: &'static [&'static dyn SignatureVerificationAlgorithm], + + /// A mapping from TLS `SignatureScheme`s to matching webpki signature verification algorithms. + /// + /// This is one (`SignatureScheme`) to many ([`SignatureVerificationAlgorithm`]) because + /// (depending on the protocol version) there is not necessary a 1-to-1 mapping. + /// + /// For TLS1.2, all `SignatureVerificationAlgorithm`s are tried in sequence. + /// + /// For TLS1.3, only the first is tried. + /// + /// The supported schemes in this mapping is communicated to the peer and the order is significant. + /// The first mapping is our highest preference. + pub mapping: &'static [( + SignatureScheme, + &'static [&'static dyn SignatureVerificationAlgorithm], + )], +} + +impl WebPkiSupportedAlgorithms { + /// Return all the `scheme` items in `mapping`, maintaining order. + pub fn supported_schemes(&self) -> Vec<SignatureScheme> { + self.mapping + .iter() + .map(|item| item.0) + .collect() + } + + /// Return the first item in `mapping` that matches `scheme`. + fn convert_scheme( + &self, + scheme: SignatureScheme, + ) -> Result<&[&'static dyn SignatureVerificationAlgorithm], Error> { + self.mapping + .iter() + .filter_map(|item| if item.0 == scheme { Some(item.1) } else { None }) + .next() + .ok_or_else(|| PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()) + } + + /// Return `true` if all cryptography is FIPS-approved. + pub fn fips(&self) -> bool { + self.all.iter().all(|alg| alg.fips()) + && self + .mapping + .iter() + .all(|item| item.1.iter().all(|alg| alg.fips())) + } +} + +impl fmt::Debug for WebPkiSupportedAlgorithms { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "WebPkiSupportedAlgorithms {{ all: [ .. ], mapping: ")?; + f.debug_list() + .entries(self.mapping.iter().map(|item| item.0)) + .finish()?; + write!(f, " }}") + } +} + +/// Wrapper around internal representation of a parsed certificate. +/// +/// This is used in order to avoid parsing twice when specifying custom verification +pub struct ParsedCertificate<'a>(pub(crate) webpki::EndEntityCert<'a>); + +impl ParsedCertificate<'_> { + /// Get the parsed certificate's SubjectPublicKeyInfo (SPKI) + pub fn subject_public_key_info(&self) -> SubjectPublicKeyInfoDer<'static> { + self.0.subject_public_key_info() + } +} + +impl<'a> TryFrom<&'a CertificateDer<'a>> for ParsedCertificate<'a> { + type Error = Error; + fn try_from(value: &'a CertificateDer<'a>) -> Result<Self, Self::Error> { + webpki::EndEntityCert::try_from(value) + .map_err(pki_error) + .map(ParsedCertificate) + } +} + +/// Verify a message signature using the `cert` public key and any supported scheme. +/// +/// This function verifies the `dss` signature over `message` using the subject public key from +/// `cert`. Since TLS 1.2 doesn't provide enough information to map the `dss.scheme` into a single +/// [`SignatureVerificationAlgorithm`], this function will map to several candidates and try each in +/// succession until one succeeds or we exhaust all candidates. +/// +/// See [WebPkiSupportedAlgorithms::mapping] for more information. +pub fn verify_tls12_signature( + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + supported_schemes: &WebPkiSupportedAlgorithms, +) -> Result<HandshakeSignatureValid, Error> { + let possible_algs = supported_schemes.convert_scheme(dss.scheme)?; + let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?; + + let mut error = None; + for alg in possible_algs { + match cert.verify_signature(*alg, message, dss.signature()) { + Err(err @ webpki::Error::UnsupportedSignatureAlgorithmForPublicKeyContext(_)) => { + error = Some(err); + continue; + } + Err(e) => return Err(pki_error(e)), + Ok(()) => return Ok(HandshakeSignatureValid::assertion()), + } + } + + #[allow(deprecated)] // The `unwrap_or()` should be statically unreachable + Err(pki_error(error.unwrap_or( + webpki::Error::UnsupportedSignatureAlgorithmForPublicKey, + ))) +} + +/// Verify a message signature using the `cert` public key and the first TLS 1.3 compatible +/// supported scheme. +/// +/// This function verifies the `dss` signature over `message` using the subject public key from +/// `cert`. Unlike [verify_tls12_signature], this function only tries the first matching scheme. See +/// [WebPkiSupportedAlgorithms::mapping] for more information. +pub fn verify_tls13_signature( + msg: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + supported_schemes: &WebPkiSupportedAlgorithms, +) -> Result<HandshakeSignatureValid, Error> { + if !dss.scheme.supported_in_tls13() { + return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()); + } + + let alg = supported_schemes.convert_scheme(dss.scheme)?[0]; + + let cert = webpki::EndEntityCert::try_from(cert).map_err(pki_error)?; + + cert.verify_signature(alg, msg, dss.signature()) + .map_err(pki_error) + .map(|_| HandshakeSignatureValid::assertion()) +} + +/// Verify a message signature using a raw public key and the first TLS 1.3 compatible +/// supported scheme. +pub fn verify_tls13_signature_with_raw_key( + msg: &[u8], + spki: &SubjectPublicKeyInfoDer<'_>, + dss: &DigitallySignedStruct, + supported_schemes: &WebPkiSupportedAlgorithms, +) -> Result<HandshakeSignatureValid, Error> { + if !dss.scheme.supported_in_tls13() { + return Err(PeerMisbehaved::SignedHandshakeWithUnadvertisedSigScheme.into()); + } + + let raw_key = webpki::RawPublicKeyEntity::try_from(spki).map_err(pki_error)?; + let alg = supported_schemes.convert_scheme(dss.scheme)?[0]; + + raw_key + .verify_signature(alg, msg, dss.signature()) + .map_err(pki_error) + .map(|_| HandshakeSignatureValid::assertion()) +} + +/// Verify that the end-entity certificate `end_entity` is a valid server cert +/// and chains to at least one of the trust anchors in the `roots` [RootCertStore]. +/// +/// `intermediates` contains all certificates other than `end_entity` that +/// were sent as part of the server's `Certificate` message. It is in the +/// same order that the server sent them and may be empty. +/// +/// `revocation` controls how revocation checking is performed, if at all. +/// +/// This function exists to be used by [`verify_server_cert_signed_by_trust_anchor`], +/// and differs only in providing a `Option<webpki::RevocationOptions>` argument. We +/// can't include this argument in `verify_server_cert_signed_by_trust_anchor` because +/// it will leak the webpki types into Rustls' public API. +pub(crate) fn verify_server_cert_signed_by_trust_anchor_impl( + cert: &ParsedCertificate<'_>, + roots: &RootCertStore, + intermediates: &[CertificateDer<'_>], + revocation: Option<webpki::RevocationOptions<'_>>, + now: UnixTime, + supported_algs: &[&dyn SignatureVerificationAlgorithm], +) -> Result<(), Error> { + let result = cert.0.verify_for_usage( + supported_algs, + &roots.roots, + intermediates, + now, + webpki::KeyUsage::server_auth(), + revocation, + None, + ); + match result { + Ok(_) => Ok(()), + Err(e) => Err(pki_error(e)), + } +} + +#[cfg(test)] +mod tests { + use std::format; + + use super::*; + + #[test] + fn certificate_debug() { + assert_eq!( + "CertificateDer(0x6162)", + format!("{:?}", CertificateDer::from(b"ab".to_vec())) + ); + } + + #[cfg(feature = "ring")] + #[test] + fn webpki_supported_algorithms_is_debug() { + assert_eq!( + "WebPkiSupportedAlgorithms { all: [ .. ], mapping: [ECDSA_NISTP384_SHA384, ECDSA_NISTP256_SHA256, ED25519, RSA_PSS_SHA512, RSA_PSS_SHA384, RSA_PSS_SHA256, RSA_PKCS1_SHA512, RSA_PKCS1_SHA384, RSA_PKCS1_SHA256] }", + format!( + "{:?}", + crate::crypto::ring::default_provider().signature_verification_algorithms + ) + ); + } +} diff --git a/vendor/rustls/src/x509.rs b/vendor/rustls/src/x509.rs new file mode 100644 index 00000000..2620fe8a --- /dev/null +++ b/vendor/rustls/src/x509.rs @@ -0,0 +1,147 @@ +// Additional x509/asn1 functions to those provided in webpki/ring. + +use alloc::vec::Vec; + +/// Prepend stuff to `bytes` to put it in a DER SEQUENCE. +pub(crate) fn wrap_in_sequence(bytes: &[u8]) -> Vec<u8> { + asn1_wrap(DER_SEQUENCE_TAG, bytes, &[]) +} + +/// Prepend stuff to `bytes_a` + `bytes_b` to put it in a DER SEQUENCE. +#[cfg_attr(not(feature = "ring"), allow(dead_code))] +pub(crate) fn wrap_concat_in_sequence(bytes_a: &[u8], bytes_b: &[u8]) -> Vec<u8> { + asn1_wrap(DER_SEQUENCE_TAG, bytes_a, bytes_b) +} + +/// Prepend stuff to `bytes` to put it in a DER BIT STRING. +pub(crate) fn wrap_in_bit_string(bytes: &[u8]) -> Vec<u8> { + asn1_wrap(DER_BIT_STRING_TAG, &[0u8], bytes) +} + +/// Prepend stuff to `bytes` to put it in a DER OCTET STRING. +#[cfg_attr(not(feature = "ring"), allow(dead_code))] +pub(crate) fn wrap_in_octet_string(bytes: &[u8]) -> Vec<u8> { + asn1_wrap(DER_OCTET_STRING_TAG, bytes, &[]) +} + +fn asn1_wrap(tag: u8, bytes_a: &[u8], bytes_b: &[u8]) -> Vec<u8> { + let len = bytes_a.len() + bytes_b.len(); + + if len <= 0x7f { + // Short form + let mut ret = Vec::with_capacity(2 + len); + ret.push(tag); + ret.push(len as u8); + ret.extend_from_slice(bytes_a); + ret.extend_from_slice(bytes_b); + ret + } else { + // Long form + let size = len.to_be_bytes(); + let leading_zero_bytes = size + .iter() + .position(|&x| x != 0) + .unwrap_or(size.len()); + assert!(leading_zero_bytes < size.len()); + let encoded_bytes = size.len() - leading_zero_bytes; + + let mut ret = Vec::with_capacity(2 + encoded_bytes + len); + ret.push(tag); + + ret.push(0x80 + encoded_bytes as u8); + ret.extend_from_slice(&size[leading_zero_bytes..]); + + ret.extend_from_slice(bytes_a); + ret.extend_from_slice(bytes_b); + ret + } +} + +const DER_SEQUENCE_TAG: u8 = 0x30; +const DER_BIT_STRING_TAG: u8 = 0x03; +const DER_OCTET_STRING_TAG: u8 = 0x04; + +#[cfg(test)] +mod tests { + use std::vec; + + use super::*; + + #[test] + fn test_empty() { + assert_eq!(vec![0x30, 0x00], wrap_in_sequence(&[])); + } + + #[test] + fn test_small() { + assert_eq!( + vec![0x30, 0x04, 0x00, 0x11, 0x22, 0x33], + wrap_in_sequence(&[0x00, 0x11, 0x22, 0x33]) + ); + } + + #[test] + fn test_medium() { + let mut val = Vec::new(); + val.resize(255, 0x12); + assert_eq!( + vec![0x30, 0x81, 0xff, 0x12, 0x12, 0x12], + wrap_in_sequence(&val)[..6] + ); + } + + #[test] + fn test_large() { + let mut val = Vec::new(); + val.resize(4660, 0x12); + wrap_in_sequence(&val); + assert_eq!( + vec![0x30, 0x82, 0x12, 0x34, 0x12, 0x12], + wrap_in_sequence(&val)[..6] + ); + } + + #[test] + fn test_huge() { + let mut val = Vec::new(); + val.resize(0xffff, 0x12); + let result = wrap_in_sequence(&val); + assert_eq!(vec![0x30, 0x82, 0xff, 0xff, 0x12, 0x12], result[..6]); + assert_eq!(result.len(), 0xffff + 4); + } + + #[test] + fn test_gigantic() { + let mut val = Vec::new(); + val.resize(0x100000, 0x12); + let result = wrap_in_sequence(&val); + assert_eq!(vec![0x30, 0x83, 0x10, 0x00, 0x00, 0x12, 0x12], result[..7]); + assert_eq!(result.len(), 0x100000 + 5); + } + + #[test] + fn test_ludicrous() { + let mut val = Vec::new(); + val.resize(0x1000000, 0x12); + let result = wrap_in_sequence(&val); + assert_eq!( + vec![0x30, 0x84, 0x01, 0x00, 0x00, 0x00, 0x12, 0x12], + result[..8] + ); + assert_eq!(result.len(), 0x1000000 + 6); + } + + #[test] + fn test_wrap_in_bit_string() { + // The BIT STRING encoding starts with a single octet on + // the front saying how many bits to disregard from the + // last octet. So this zero means "no bits" unused, which + // is correct because our input is an string of octets. + // + // So if we encode &[0x55u8] with this function, we should get: + // + // 0x03 0x02 0x00 0x55 + // ^ tag ^ len ^ no unused bits ^ value + assert_eq!(wrap_in_bit_string(&[0x55u8]), vec![0x03, 0x02, 0x00, 0x55]); + } +} |
