summaryrefslogtreecommitdiff
path: root/vendor/rustls
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-10 13:11:11 -0600
committermo khan <mo@mokhan.ca>2025-07-10 13:11:11 -0600
commit01959b16a21b22b5df5f16569c2a8e8f92beecef (patch)
tree32afa5d747c5466345c59ec52161a7cba3d6d755 /vendor/rustls
parentff30574117a996df332e23d1fb6f65259b316b5b (diff)
chore: vendor dependencies
Diffstat (limited to 'vendor/rustls')
-rw-r--r--vendor/rustls/.cargo-checksum.json1
-rw-r--r--vendor/rustls/Cargo.lock1131
-rw-r--r--vendor/rustls/Cargo.toml233
-rw-r--r--vendor/rustls/LICENSE-APACHE201
-rw-r--r--vendor/rustls/LICENSE-ISC15
-rw-r--r--vendor/rustls/LICENSE-MIT25
-rw-r--r--vendor/rustls/README.md222
-rw-r--r--vendor/rustls/benches/benchmarks.rs20
-rw-r--r--vendor/rustls/build.rs13
-rw-r--r--vendor/rustls/examples/internal/test_ca.rs352
-rw-r--r--vendor/rustls/src/bs_debug.rs80
-rw-r--r--vendor/rustls/src/builder.rs290
-rw-r--r--vendor/rustls/src/check.rs70
-rw-r--r--vendor/rustls/src/client/builder.rs186
-rw-r--r--vendor/rustls/src/client/client_conn.rs1062
-rw-r--r--vendor/rustls/src/client/common.rs119
-rw-r--r--vendor/rustls/src/client/ech.rs899
-rw-r--r--vendor/rustls/src/client/handy.rs390
-rw-r--r--vendor/rustls/src/client/hs.rs1178
-rw-r--r--vendor/rustls/src/client/test.rs712
-rw-r--r--vendor/rustls/src/client/tls12.rs1372
-rw-r--r--vendor/rustls/src/client/tls13.rs1700
-rw-r--r--vendor/rustls/src/common_state.rs1049
-rw-r--r--vendor/rustls/src/compress.rs677
-rw-r--r--vendor/rustls/src/conn.rs1280
-rw-r--r--vendor/rustls/src/conn/kernel.rs268
-rw-r--r--vendor/rustls/src/conn/unbuffered.rs613
-rw-r--r--vendor/rustls/src/crypto/aws_lc_rs/hpke.rs1191
-rw-r--r--vendor/rustls/src/crypto/aws_lc_rs/mod.rs318
-rw-r--r--vendor/rustls/src/crypto/aws_lc_rs/pq/hybrid.rs205
-rw-r--r--vendor/rustls/src/crypto/aws_lc_rs/pq/mlkem.rs103
-rw-r--r--vendor/rustls/src/crypto/aws_lc_rs/pq/mod.rs48
-rw-r--r--vendor/rustls/src/crypto/aws_lc_rs/sign.rs876
-rw-r--r--vendor/rustls/src/crypto/aws_lc_rs/ticketer.rs397
-rw-r--r--vendor/rustls/src/crypto/aws_lc_rs/tls12.rs465
-rw-r--r--vendor/rustls/src/crypto/aws_lc_rs/tls13.rs425
-rw-r--r--vendor/rustls/src/crypto/cipher.rs350
-rw-r--r--vendor/rustls/src/crypto/hash.rs81
-rw-r--r--vendor/rustls/src/crypto/hmac.rs73
-rw-r--r--vendor/rustls/src/crypto/hpke.rs145
-rw-r--r--vendor/rustls/src/crypto/mod.rs756
-rw-r--r--vendor/rustls/src/crypto/ring/hash.rs60
-rw-r--r--vendor/rustls/src/crypto/ring/hmac.rs48
-rw-r--r--vendor/rustls/src/crypto/ring/kx.rs188
-rw-r--r--vendor/rustls/src/crypto/ring/mod.rs203
-rw-r--r--vendor/rustls/src/crypto/ring/quic.rs416
-rw-r--r--vendor/rustls/src/crypto/ring/sign.rs822
-rw-r--r--vendor/rustls/src/crypto/ring/ticketer.rs366
-rw-r--r--vendor/rustls/src/crypto/ring/tls12.rs406
-rw-r--r--vendor/rustls/src/crypto/ring/tls13.rs336
-rw-r--r--vendor/rustls/src/crypto/signer.rs233
-rw-r--r--vendor/rustls/src/crypto/tls12.rs169
-rw-r--r--vendor/rustls/src/crypto/tls13.rs406
-rw-r--r--vendor/rustls/src/enums.rs691
-rw-r--r--vendor/rustls/src/error.rs1387
-rw-r--r--vendor/rustls/src/hash_hs.rs347
-rw-r--r--vendor/rustls/src/key_log.rs61
-rw-r--r--vendor/rustls/src/key_log_file.rs162
-rw-r--r--vendor/rustls/src/lib.rs715
-rw-r--r--vendor/rustls/src/limited_cache.rs250
-rw-r--r--vendor/rustls/src/lock.rs89
-rw-r--r--vendor/rustls/src/manual/defaults.rs71
-rw-r--r--vendor/rustls/src/manual/features.rs100
-rw-r--r--vendor/rustls/src/manual/fips.rs62
-rw-r--r--vendor/rustls/src/manual/howto.rs124
-rw-r--r--vendor/rustls/src/manual/implvulns.rs104
-rw-r--r--vendor/rustls/src/manual/mod.rs34
-rw-r--r--vendor/rustls/src/manual/tlsvulns.rs175
-rw-r--r--vendor/rustls/src/msgs/alert.rs26
-rw-r--r--vendor/rustls/src/msgs/base.rs238
-rw-r--r--vendor/rustls/src/msgs/ccs.rs23
-rw-r--r--vendor/rustls/src/msgs/codec.rs404
-rw-r--r--vendor/rustls/src/msgs/deframer/buffers.rs296
-rw-r--r--vendor/rustls/src/msgs/deframer/handshake.rs525
-rw-r--r--vendor/rustls/src/msgs/deframer/mod.rs238
-rw-r--r--vendor/rustls/src/msgs/enums.rs510
-rw-r--r--vendor/rustls/src/msgs/ffdhe_groups.rs323
-rw-r--r--vendor/rustls/src/msgs/fragmenter.rs232
-rw-r--r--vendor/rustls/src/msgs/handshake.rs3210
-rw-r--r--vendor/rustls/src/msgs/handshake_test.rs1051
-rw-r--r--vendor/rustls/src/msgs/macros.rs313
-rw-r--r--vendor/rustls/src/msgs/message/inbound.rs161
-rw-r--r--vendor/rustls/src/msgs/message/mod.rs262
-rw-r--r--vendor/rustls/src/msgs/message/outbound.rs422
-rw-r--r--vendor/rustls/src/msgs/message_test.rs124
-rw-r--r--vendor/rustls/src/msgs/mod.rs74
-rw-r--r--vendor/rustls/src/msgs/persist.rs492
-rw-r--r--vendor/rustls/src/polyfill.rs9
-rw-r--r--vendor/rustls/src/quic.rs1022
-rw-r--r--vendor/rustls/src/rand.rs34
-rw-r--r--vendor/rustls/src/record_layer.rs336
-rw-r--r--vendor/rustls/src/server/builder.rs127
-rw-r--r--vendor/rustls/src/server/common.rs35
-rw-r--r--vendor/rustls/src/server/handy.rs356
-rw-r--r--vendor/rustls/src/server/hs.rs763
-rw-r--r--vendor/rustls/src/server/server_conn.rs1288
-rw-r--r--vendor/rustls/src/server/test.rs369
-rw-r--r--vendor/rustls/src/server/tls12.rs1003
-rw-r--r--vendor/rustls/src/server/tls13.rs1535
-rw-r--r--vendor/rustls/src/stream.rs282
-rw-r--r--vendor/rustls/src/suites.rs272
-rw-r--r--vendor/rustls/src/test_macros.rs71
-rw-r--r--vendor/rustls/src/ticketer.rs365
-rw-r--r--vendor/rustls/src/time_provider.rs30
-rw-r--r--vendor/rustls/src/tls12/mod.rs380
-rw-r--r--vendor/rustls/src/tls13/key_schedule.rs1308
-rw-r--r--vendor/rustls/src/tls13/mod.rs125
-rw-r--r--vendor/rustls/src/vecbuf.rs374
-rw-r--r--vendor/rustls/src/verify.rs382
-rw-r--r--vendor/rustls/src/verifybench.rs244
-rw-r--r--vendor/rustls/src/versions.rs97
-rw-r--r--vendor/rustls/src/webpki/anchors.rs143
-rw-r--r--vendor/rustls/src/webpki/client_verifier.rs665
-rw-r--r--vendor/rustls/src/webpki/mod.rs313
-rw-r--r--vendor/rustls/src/webpki/server_verifier.rs448
-rw-r--r--vendor/rustls/src/webpki/verify.rs289
-rw-r--r--vendor/rustls/src/x509.rs147
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).
+
+[![Build Status](https://github.com/rustls/rustls/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/rustls/rustls/actions/workflows/build.yml?query=branch%3Amain)
+[![Coverage Status (codecov.io)](https://codecov.io/gh/rustls/rustls/branch/main/graph/badge.svg)](https://codecov.io/gh/rustls/rustls/)
+[![Documentation](https://docs.rs/rustls/badge.svg)](https://docs.rs/rustls/)
+[![Chat](https://img.shields.io/discord/976380008299917365?logo=discord)](https://discord.gg/MCSB76RU96)
+[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9034/badge)](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]);
+ }
+}