diff --git a/.github/workflows/rustls-rustcrypto.yml b/.github/workflows/rustls-rustcrypto.yml index 37e374a..f0056aa 100644 --- a/.github/workflows/rustls-rustcrypto.yml +++ b/.github/workflows/rustls-rustcrypto.yml @@ -63,7 +63,7 @@ jobs: with: toolchain: ${{ matrix.rust }} targets: ${{ matrix.target }} - - run: cargo build --no-default-features --features tls12,alloc --release --target ${{ matrix.target }} + - run: cargo build --no-default-features --features tls12,full,alloc --release --target ${{ matrix.target }} test: strategy: @@ -80,7 +80,7 @@ jobs: toolchain: ${{ matrix.toolchain }} - run: cargo test --features tls12 - name: Test no_std with alloc - run: cargo test --no-default-features --features tls12,alloc + run: cargo test --no-default-features --features tls12,full,alloc cross: strategy: diff --git a/Cargo.lock b/Cargo.lock index 2643e4a..e21957b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -21,6 +21,7 @@ dependencies = [ "cfg-if", "cipher", "cpufeatures", + "zeroize", ] [[package]] @@ -30,18 +31,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", - "aes", "cipher", "ctr", "ghash", "subtle", + "zeroize", ] [[package]] name = "autocfg" -version = "1.0.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base16ct" @@ -64,17 +65,44 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byteorder" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + [[package]] name = "cc" -version = "1.0.90" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" +checksum = "27f657647bcff5394bf56c7317665bbf790a137a50eaaa5c6bfbb9e27a518f2d" +dependencies = [ + "shlex", +] + +[[package]] +name = "ccm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae3c82e4355234767756212c570e29833699ab63e6ffd161887314cc5b43847" +dependencies = [ + "aead", + "cipher", + "ctr", + "subtle", +] [[package]] name = "cfg-if" @@ -125,13 +153,23 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" dependencies = [ "libc", ] +[[package]] +name = "crrl" +version = "0.9.0" +source = "git+https://github.com/stevefan1999-personal/crrl#7e502920ebedccce4aaa930d4c9a26787f157101" +dependencies = [ + "rand_core", + "sha2", + "sha3", +] + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -165,16 +203,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest", "fiat-crypto", - "platforms", "rustc_version", "subtle", "zeroize", @@ -198,10 +235,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" dependencies = [ "const-oid", + "der_derive", + "flagset", "pem-rfc7468", "zeroize", ] +[[package]] +name = "der_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8034092389675178f570469e6c3b0465d3d30b4505c294a6550db47f3c17ad18" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -246,12 +296,17 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", - "serde", "sha2", "subtle", "zeroize", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "elliptic-curve" version = "0.13.8" @@ -285,9 +340,15 @@ dependencies = [ [[package]] name = "fiat-crypto" -version = "0.2.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1676f435fc1dadde4d03e43f5d62b259e1ce5f40bd4ffb21db2b42ebe59c1382" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "flagset" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" [[package]] name = "generic-array" @@ -309,6 +370,7 @@ dependencies = [ "cfg-if", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -359,32 +421,50 @@ dependencies = [ "generic-array", ] +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" dependencies = [ - "spin 0.5.2", + "spin", ] [[package]] name = "libc" -version = "0.2.154" +version = "0.2.168" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "5aaeb2981e0606ca11d79718f8bb01164f1d6ed75080182d3abf017e6d244b6d" [[package]] name = "libm" -version = "0.2.8" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "num-bigint-dig" @@ -414,9 +494,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -425,9 +505,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", "libm", @@ -435,9 +515,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "opaque-debug" @@ -469,6 +549,19 @@ dependencies = [ "sha2", ] +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "paste" version = "1.0.15" @@ -495,16 +588,6 @@ dependencies = [ "spki", ] -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "der", - "spki", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -512,16 +595,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" dependencies = [ "der", - "pkcs5", "spki", + "subtle", ] -[[package]] -name = "platforms" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" - [[package]] name = "poly1305" version = "0.8.0" @@ -547,9 +624,12 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] [[package]] name = "primeorder" @@ -562,18 +642,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.79" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -627,16 +707,16 @@ dependencies = [ "cfg-if", "getrandom", "libc", - "spin 0.9.8", + "spin", "untrusted", "windows-sys", ] [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest", @@ -655,18 +735,18 @@ dependencies = [ [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustls" -version = "0.23.12" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c58f8c84392efc0a126acce10fa59ff7b3d2ac06ab451a33f2741989b806b044" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "log", "once_cell", @@ -678,27 +758,35 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.7.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-rustcrypto" version = "0.0.2-alpha" dependencies = [ "aead", + "aes", "aes-gcm", + "bytes", + "ccm", "chacha20poly1305", + "crrl", "crypto-common", "der", "digest", "ecdsa", "ed25519-dalek", + "elliptic-curve", "getrandom", "hmac", + "itertools", "p256", "p384", + "p521", "paste", + "pkcs1", "pkcs8", "rand_core", "rsa", @@ -708,14 +796,17 @@ dependencies = [ "sec1", "sha2", "signature", + "spki", + "typenum", "x25519-dalek", + "x509-cert", ] [[package]] name = "rustls-webpki" -version = "0.102.6" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6b52d4fda176fd835fdc55a835d4a89b8499cad995885a21149d5ad62f852e" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -738,41 +829,48 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] -name = "serde" -version = "1.0.197" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "serde_derive", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "serde_derive" -version = "1.0.197" +name = "sha2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "proc-macro2", - "quote", - "syn", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "sha2" +name = "sha3" version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" dependencies = [ - "cfg-if", - "cpufeatures", "digest", + "keccak", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signature" version = "2.2.0" @@ -785,15 +883,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "spin" @@ -813,21 +905,42 @@ dependencies = [ [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.52" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "tls_codec" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "typenum" version = "1.17.0" @@ -836,9 +949,9 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" [[package]] name = "universal-hash" @@ -858,9 +971,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" @@ -868,6 +981,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + [[package]] name = "windows-sys" version = "0.52.0" @@ -879,13 +1046,14 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" +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", @@ -894,45 +1062,51 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.52.4" +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 = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.52.4" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "x25519-dalek" @@ -945,11 +1119,46 @@ dependencies = [ "zeroize", ] +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid", + "der", + "sha1", + "signature", + "spki", + "tls_codec", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] diff --git a/Cargo.toml b/Cargo.toml index 01ab6b0..aa86c8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,47 +13,216 @@ categories = ["cryptography", "no-std"] keywords = ["rustls", "tls"] edition = "2021" rust-version = "1.75" -resolver = "1" # Hack to enable the `custom` feature of `getrandom` +resolver = "2" # Ensure all dependencies + feats are mapped to crate features for correct usage # default features often have std breaking no_std and potentially other unwanted [dependencies] -aead = { version = "0.5.2", default-features = false } -aes-gcm = { version = "0.10.3", default-features = false, features = ["aes", "alloc"] } -chacha20poly1305 = { version = "0.10.1", default-features = false } +# Cryptographic dependencies +aead = { version = "0.5.2", default-features = false, optional = true } +aes = { version = "0.8.4", default-features = false, optional = true } +aes-gcm = { version = "0.10.3", default-features = false, optional = true } +ccm = { version = "0.5.0", default-features = false, optional = true } +chacha20poly1305 = { version = "0.10.1", default-features = false, optional = true } +crrl = { git = "https://github.com/stevefan1999-personal/crrl", version = "0.9.0", default-features = false, optional = true } crypto-common = { version = "0.1.6", default-features = false } -der = { version = "0.7.9", default-features = false } +der = { version = "0.7.9", default-features = false, optional = true } digest = { version = "0.10.7", default-features = false } -ecdsa = { version = "0.16.8", default-features = false, features = ["alloc"] } -ed25519-dalek = { version = "2", default-features = false, features = ["pkcs8"] } +ecdsa = { version = "0.16.9", default-features = false, optional = true } +ed25519-dalek = { version = "2", default-features = false, optional = true } +elliptic-curve = { version = "0.13.8", default-features = false, optional = true } hmac = { version = "0.12.1", default-features = false } -p256 = { version = "0.13.2", default-features = false, features = ["pem", "ecdsa", "ecdh"] } -p384 = { version = "0.13.0", default-features = false, features = ["pem", "ecdsa", "ecdh"] } +p256 = { version = "0.13.2", default-features = false, optional = true } +p384 = { version = "0.13.0", default-features = false, optional = true } +p521 = { version = "0.13.3", default-features = false, optional = true } +pkcs1 = { version = "0.7.5", default-features = false, optional = true } +pkcs8 = { version = "0.10.2", default-features = false, optional = true } +rsa = { version = "0.9.7", default-features = false, optional = true } +sec1 = { version = "0.7.3", default-features = false, optional = true } +sha2 = { version = "0.10.8", default-features = false } +signature = { version = "2.2.0", default-features = false, optional = true } +typenum = { version = "1.17.0", features = ["no_std", "const-generics"] } +x25519-dalek = { version = "2", default-features = false, optional = true } + +# External groups +getrandom = { version = "0.2", default-features = false, features = ["custom"] } paste = { version = "1.0.15", default-features = false } -pkcs8 = { version = "0.10.2", default-features = false, features = ["pem", "pkcs5"] } -pki-types = { package = "rustls-pki-types", version = "1.0.1", default-features = false } -rand_core = { version = "0.6.4", default-features = false, features = ["getrandom"] } -rsa = { version = "0.9.2", default-features = false, features = ["sha2"] } -rustls = { version = "0.23.12", default-features = false } -sec1 = { version = "0.7.3", default-features = false, features = ["pkcs8", "pem"] } -sha2 = { version = "0.10.7", default-features = false } -signature = { version = "2.1.0", default-features = false } -webpki = { package = "rustls-webpki", version = "0.102.0", default-features = false } -x25519-dalek = { version = "2", default-features = false } +pki-types = { package = "rustls-pki-types", version = "1.10.0", default-features = false } +rand_core = { version = "0.6.4", default-features = false, features = [ + "getrandom", +], optional = true } +rustls = { version = "0.23.19", default-features = false } +webpki = { package = "rustls-webpki", version = "0.102.8", default-features = false, optional = true } + +[target.'cfg(target_arch = "wasm32")'.dependencies] +getrandom = { version = "0.2", features = ["wasm-bindgen"] } [dev-dependencies] -getrandom = { version = "0.2", features = ["custom"] } # workaround to build on no_std targets +bytes = { version = "1.9.0", default-features = false } +itertools = { version = "0.13.0", default-features = false } +rsa = { version = "0.9.7", default-features = false, features = ["sha2"] } +rustls = { version = "0.23.19", default-features = false, features = ["std"] } +sha2 = { version = "0.10.8", default-features = false } +spki = { version = "0.7.3", default-features = false, features = ["alloc"] } +x509-cert = { version = "0.2.5", default-features = false, features = [ + "builder", +] } [features] -default = ["std", "tls12", "zeroize"] +default = ["std", "tls12", "zeroize", "full", "fast"] +full = [ + "aead-full", + "sign-full", + "verify-full", + "kx-full", + "hash-full", + "format", +] +format = ["pem", "pkcs1", "pkcs8", "sec1"] logging = ["rustls/logging"] tls12 = ["rustls/tls12"] -# Only enable feature in upstream if there is an overall effect e.g. aead/alloc in-place -# zeroize is another typical that can be turned off +# RustCrypto is preparing to migrate to core::error::Error +# and in before most of the use case for std is just std::error::Error +std = ["alloc", "rustls/std"] +alloc = [ + "ecdsa?/alloc", + "elliptic-curve?/alloc", + "pkcs8?/alloc", + "sec1?/alloc", + "signature?/alloc", +] +zeroize = [ + "aes-gcm?/zeroize", + "aes?/zeroize", + "der?/zeroize", + "ed25519-dalek?/zeroize", + "pkcs1?/zeroize", + "sec1?/zeroize", + "x25519-dalek?/zeroize", +] +subtle = ["digest/subtle", "pkcs8?/subtle", "sec1?/subtle"] +fast = [ + "ed25519-dalek?/fast", + "rsa?/u64_digit", + "x25519-dalek?/precomputed-tables", +] + +nist = [] +p256 = ["dep:p256", "nist"] +p384 = ["dep:p384", "nist"] +p521 = ["dep:p521", "nist"] +ed25519 = ["dep:ed25519-dalek"] + +ecdsa = ["dep:ecdsa", "verify", "signature", "rand", "der", "elliptic-curve"] +ecdsa-p256 = ["ecdsa", "p256", "p256/ecdsa"] +ecdsa-p384 = ["ecdsa", "p384", "p384/ecdsa"] +ecdsa-p521 = ["ecdsa", "p521", "p521/ecdsa"] +ecdsa-full = ["ecdsa-p256", "ecdsa-p384", "ecdsa-p521"] + +eddsa = ["verify", "signature", "elliptic-curve"] +eddsa-ed25519 = ["eddsa", "ed25519"] +eddsa-full = ["eddsa-ed25519"] + +kx = ["rand", "elliptic-curve"] +kx-x448 = ["kx", "x448"] +kx-x25519 = ["kx", "dep:x25519-dalek"] +kx-nist = ["sec1"] +kx-p256 = ["kx", "p256", "kx-nist", "p256/ecdh"] +kx-p384 = ["kx", "p384", "kx-nist", "p384/ecdh"] +kx-p521 = ["kx", "p521", "kx-nist", "p521/ecdh"] +kx-full = ["kx-x448", "kx-x25519", "kx-p256", "kx-p384", "kx-p521"] + +rsa = ["dep:rsa", "rsa/sha2", "pkcs1"] +rsa-pkcs1 = ["rsa", "pkcs1"] +rsa-pss = ["rsa"] + +aead = ["dep:aead"] +aead-aes-gcm = ["aead", "aes-gcm"] +aead-aes-ccm = ["aead", "aes-ccm"] +aead-chacha20poly1305 = ["aead", "chacha20poly1305"] +aead-full = ["aead-aes-gcm", "aead-aes-ccm", "aead-chacha20poly1305"] + +sign = ["signature", "der"] +sign-ecdsa-nist = ["sign"] +sign-ecdsa-p256 = ["sign-ecdsa-nist", "ecdsa-p256"] +sign-ecdsa-p384 = ["sign-ecdsa-nist", "ecdsa-p384"] +sign-ecdsa-p521 = ["sign-ecdsa-nist", "ecdsa-p521"] +sign-eddsa = ["sign"] +sign-eddsa-ed25519 = ["sign-eddsa", "eddsa-ed25519"] +sign-rsa = ["sign", "rsa"] +sign-rsa-pkcs1 = ["sign-rsa", "rsa-pkcs1"] +sign-rsa-pss = ["sign-rsa", "rsa-pss"] +sign-full = [ + "sign-ecdsa-p256", + "sign-ecdsa-p384", + "sign-ecdsa-p521", + "sign-eddsa-ed25519", + "sign-rsa-pkcs1", + "sign-rsa-pss", +] + +verify = ["dep:webpki"] +verify-ecdsa-nist = ["verify"] +verify-ecdsa-p256 = ["verify-ecdsa-nist", "ecdsa-p256"] +verify-ecdsa-p256-sha256 = ["verify-ecdsa-p256", "hash-sha256"] +verify-ecdsa-p256-sha384 = ["verify-ecdsa-p256", "hash-sha384"] +verify-ecdsa-p384 = ["verify-ecdsa-nist", "ecdsa-p384"] +verify-ecdsa-p384-sha256 = ["verify-ecdsa-p384", "hash-sha256"] +verify-ecdsa-p384-sha384 = ["verify-ecdsa-p384", "hash-sha384"] +verify-eddsa-ed25519 = ["verify", "eddsa-ed25519"] +verify-rsa-pkcs1 = ["verify", "rsa-pkcs1"] +verify-rsa-pkcs1-sha256 = ["verify-rsa-pkcs1", "hash-sha256"] +verify-rsa-pkcs1-sha384 = ["verify-rsa-pkcs1", "hash-sha384"] +verify-rsa-pkcs1-sha512 = ["verify-rsa-pkcs1", "hash-sha512"] +verify-rsa-pss = ["verify", "rsa-pss"] +verify-rsa-pss-sha256 = ["verify-rsa-pss", "hash-sha256"] +verify-rsa-pss-sha384 = ["verify-rsa-pss", "hash-sha384"] +verify-rsa-pss-sha512 = ["verify-rsa-pss", "hash-sha512"] +verify-full = [ + "verify-ecdsa-p256-sha256", + "verify-ecdsa-p256-sha384", + "verify-ecdsa-p384-sha256", + "verify-ecdsa-p384-sha384", + "verify-eddsa-ed25519", + "verify-rsa-pkcs1-sha256", + "verify-rsa-pkcs1-sha384", + "verify-rsa-pkcs1-sha512", + "verify-rsa-pss-sha256", + "verify-rsa-pss-sha384", + "verify-rsa-pss-sha512", +] + +hash = [] +hash-sha224 = ["hash"] +hash-sha256 = ["hash"] +hash-sha384 = ["hash"] +hash-sha512 = ["hash"] +hash-full = ["hash-sha224", "hash-sha256", "hash-sha384", "hash-sha512"] + +# Formats +der = ["dep:der"] +sec1 = ["dep:sec1", "elliptic-curve?/sec1", "sec1/pkcs8"] +pem = ["elliptic-curve?/pem", "ecdsa?/pem"] +pkcs1 = ["dep:pkcs1"] +pkcs8 = [ + "dep:pkcs8", + "ecdsa?/pkcs8", + "ed25519-dalek?/pkcs8", + "elliptic-curve?/pkcs8", + "p256?/pkcs8", + "p384?/pkcs8", + "p521?/pkcs8", + "sec1?/pkcs8", +] -# TODO: go through all of these that what gets exposed re: std error type -std = ["alloc", "webpki/std", "pki-types/std", "rustls/std", "ed25519-dalek/std"] -# TODO: go through all of these to ensure to_vec etc. impls are exposed -alloc = ["webpki/alloc", "pki-types/alloc", "aead/alloc", "ed25519-dalek/alloc"] -zeroize = ["ed25519-dalek/zeroize", "x25519-dalek/zeroize"] +aes = ["dep:aes"] +aes-ccm = ["aes", "ccm"] +aes-gcm = ["dep:aes-gcm", "aes", "gcm"] +ccm = ["dep:ccm"] +chacha20poly1305 = ["dep:chacha20poly1305"] +elliptic-curve = ["dep:elliptic-curve"] +gcm = [] +rand = ["dep:rand_core", "signature?/rand_core"] +signature = ["dep:signature"] +x448 = ["dep:crrl", "crrl/x448"] \ No newline at end of file diff --git a/README.md b/README.md index 39739a7..9f631e4 100644 --- a/README.md +++ b/README.md @@ -9,23 +9,60 @@ [RustCrypto]-based provider implementation for version 0.23 of [rustls], maintained by the RustCrypto organization. -## ⚠️USE THIS AT YOUR OWN RISK! DO NOT USE THIS IN PRODUCTION⚠️ +## ⚠️CAUTION⚠️ -Not only that this is incomplete that only few selected TLS suites implemented (it should be well enough to cover 70% of the usage), but the elephant in the room is that neither did rustls nor RustCrypto packages were formally verified and certified with FIPS compliance. +While a basic test suite that tests all available cipher suites and key exchange combinations passes, and it is cross-validated with OpenSSL, and is also running in ESP32 for experimental (but near-production) secure communication, please be reminded that (as of 2024) not all RustCrypto packages are formally verified and optimized for specific architecture, and none of the packages in RustCrypto are certified with FIPS compliance. Note that RustCrypto performance is generally inferior than ring, but in exchange you got a pure Rust implementation that theoretically compiles everywhere Rust was ported to. In our case, we need to have `std` but foundational support for future `no_std` expansion is already here. ## Supported Cipher Suites -- 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 -- TLS13_AES_128_GCM_SHA256 -- TLS13_AES_256_GCM_SHA384 -- TLS13_CHACHA20_POLY1305_SHA256 +Only the recommended ([TLS1.2](https://ciphersuite.info/cs/?security=recommended&singlepage=true&tls=tls12), [TLS1.3](https://ciphersuite.info/cs/?security=recommended&singlepage=true&tls=tls13)) and secure ([TLS1.2](https://ciphersuite.info/cs/?security=secure&singlepage=true&tls=tls12), [TLS1.3](https://ciphersuite.info/cs/?security=secure&singlepage=true&tls=tls13)) suites will be chosen + +For TLS 1.3: + +- [x] (Recommended) TLS_AES_128_GCM_SHA256 +- [x] (Recommended) TLS_AES_256_GCM_SHA384 +- [x] (Recommended) TLS_CHACHA20_POLY1305_SHA256 +- [x] (Secure) TLS_AES_128_CCM_8_SHA256 +- [x] (Secure) TLS_AES_128_CCM_SHA256 + +For TLS 1.2: + +- [ ] (Recommended) TLS_ECCPWD_WITH_AES_128_GCM_SHA256 +- [ ] (Recommended) TLS_ECCPWD_WITH_AES_256_GCM_SHA384 +- [x] (Recommended) TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 +- [x] (Recommended) TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 +- [ ] (Recommended) TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 +- [ ] (Recommended) TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 +- [ ] (Recommended) TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 +- [ ] (Recommended) TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 +- [x] (Recommended) TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 +- [ ] (Recommended) TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 +- [ ] (Recommended) TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384 +- [ ] (Recommended) TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 +- [ ] (Secure) TLS_ECCPWD_WITH_AES_128_CCM_SHA256 +- [ ] (Secure) TLS_ECCPWD_WITH_AES_256_CCM_SHA384 +- [x] (Secure) TLS_ECDHE_ECDSA_WITH_AES_128_CCM +- [x] (Secure) TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 +- [x] (Secure) TLS_ECDHE_ECDSA_WITH_AES_256_CCM +- [x] (Secure) TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 +- [ ] (Secure) TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256 +- [ ] (Secure) TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256 +- [x] (Secure) TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 +- [x] (Secure) TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +- [ ] (Secure) TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 +- [ ] (Secure) TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 +- [ ] (Secure) TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 +- [ ] (Secure) TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 +- [x] (Secure) TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + +\* PSK support is currently blocked due to [it not currently being supported in Rustls as of 2024](https://github.com/rustls/rustls/issues/174). +If you want this feature, consider financially supporting the Rustls project by being a funder in [Prossimo](https://www.memorysafety.org/initiative/rustls/) + +\* While both [CAMELLIA](https://github.com/RustCrypto/block-ciphers/tree/master/camellia) and [ARIA](https://github.com/RustCrypto/block-ciphers/tree/master/aria) block cipher are in RustCrypto, they are still in 0.1.0 and not currently viable for production use + +\* As RustCrypto do not have a [Dragonfly](https://www.ietf.org/proceedings/83/slides/slides-83-cfrg-0.pdf) implementation, nor it is planned yet, [RFC8492](https://datatracker.ietf.org/doc/html/rfc8492) and thus ECCPWD family of cipher suites would be hard to implement for the known future ## License @@ -44,8 +81,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. -[//]: # (badges) - +[//]: # "badges" [crate-image]: https://img.shields.io/crates/v/rustls-rustcrypto [crate-link]: https://crates.io/crates/rustls-rustcrypto [docs-image]: https://docs.rs/rustls-rustcrypto/badge.svg @@ -56,8 +92,6 @@ dual licensed as above, without any additional terms or conditions. [rustc-image]: https://img.shields.io/badge/rustc-1.75+-blue.svg [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/434751-TLS - -[//]: # (links) - +[//]: # "links" [RustCrypto]: https://github.com/RustCrypto/ [rustls]: https://github.com/rustls/rustls/ diff --git a/examples-external/client.rs b/examples-external/client.rs deleted file mode 100644 index 2493c61..0000000 --- a/examples-external/client.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::str::FromStr; - -use anyhow::anyhow; -use http_body_util::{BodyExt, Empty}; -use hyper::{body::Bytes, Uri}; -use hyper_util::{client::legacy::Client, rt::TokioExecutor}; -use rustls_rustcrypto::provider; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - env_logger::init(); - - // Prepare the HTTPS connector - let https = hyper_rustls::HttpsConnectorBuilder::new() - .with_provider_and_webpki_roots(provider())? - .https_or_http() - .enable_all_versions() - .build(); - - // Build the hyper client from the HTTPS connector. - let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(https); - - // Prepare a chain of futures which sends a GET request, inspects - // the returned headers, collects the whole body and prints it to - // stdout. - let fut = async move { - let res = client - .get(Uri::from_str("https://ecc256.badssl.com/")?) - .await - .map_err(|e| anyhow!("Could not get: {:?}", e))?; - println!("Status:\n{}", res.status()); - println!("Headers:\n{:#?}", res.headers()); - - let body = res - .into_body() - .collect() - .await - .map_err(|e| anyhow!("Could not get body: {:?}", e))? - .to_bytes(); - - println!("Body:\n{}", String::from_utf8_lossy(&body)); - - Ok(()) - }; - - fut.await -} diff --git a/examples-external/server.rs b/examples-external/server.rs deleted file mode 100644 index d31745b..0000000 --- a/examples-external/server.rs +++ /dev/null @@ -1,126 +0,0 @@ -use std::{net::ToSocketAddrs, sync::Arc}; - -use futures_util::StreamExt; -use http_body_util::{BodyExt, Full}; -use hyper::{ - body::{Bytes, Incoming}, - service::service_fn, - Method, Request, Response, StatusCode, -}; -use hyper_util::{ - rt::{TokioExecutor, TokioIo}, - server::conn::auto::Builder, -}; -use pki_types::PrivateKeyDer; -use rustls::ServerConfig; -use rustls_rustcrypto::provider; -use tls_listener::{SpawningHandshakes, TlsListener}; -use tokio::{net::TcpListener, signal::ctrl_c}; -use tokio_rustls::TlsAcceptor; -struct TestPki { - server_cert_der: Vec, - server_key_der: Vec, -} - -impl TestPki { - fn new() -> Self { - let alg = &rcgen::PKCS_ECDSA_P384_SHA384; - let mut ca_params = rcgen::CertificateParams::new(Vec::new()); - ca_params - .distinguished_name - .push(rcgen::DnType::OrganizationName, "Rustls Server Acceptor"); - ca_params - .distinguished_name - .push(rcgen::DnType::CommonName, "Example CA"); - ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained); - ca_params.key_usages = vec![ - rcgen::KeyUsagePurpose::KeyCertSign, - rcgen::KeyUsagePurpose::DigitalSignature, - rcgen::KeyUsagePurpose::CrlSign, - ]; - ca_params.alg = alg; - let ca_cert = rcgen::Certificate::from_params(ca_params).unwrap(); - - // Create a server end entity cert issued by the CA. - let mut server_ee_params = rcgen::CertificateParams::new(vec!["localhost".to_string()]); - server_ee_params.is_ca = rcgen::IsCa::NoCa; - server_ee_params.extended_key_usages = vec![rcgen::ExtendedKeyUsagePurpose::ServerAuth]; - server_ee_params.alg = alg; - let server_cert = rcgen::Certificate::from_params(server_ee_params).unwrap(); - let server_cert_der = server_cert.serialize_der_with_signer(&ca_cert).unwrap(); - let server_key_der = server_cert.serialize_private_key_der(); - Self { - server_cert_der, - server_key_der, - } - } -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - env_logger::init(); - - let pki = TestPki::new(); - let addr = "0.0.0.0:4443" - .to_socket_addrs()? - .next() - .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::AddrNotAvailable))?; - - let incoming = TcpListener::bind(&addr).await?; - - let mut server_config = ServerConfig::builder_with_provider(Arc::new(provider())) - .with_safe_default_protocol_versions()? - .with_no_client_auth() - .with_single_cert( - vec![pki.server_cert_der.clone().into()], - PrivateKeyDer::Pkcs8(pki.server_key_der.clone().into()), - )?; - server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()]; - let tls_acceptor = TlsAcceptor::from(Arc::new(server_config)); - let service = service_fn(echo); - - println!("Starting to serve on https://{}.", addr); - - TlsListener::new(SpawningHandshakes(tls_acceptor), incoming) - .take_until(ctrl_c()) - .for_each_concurrent(None, |s| async { - match s { - Ok((stream, remote_addr)) => { - println!("accepted client from {}", remote_addr); - if let Err(err) = Builder::new(TokioExecutor::new()) - .serve_connection(TokioIo::new(stream), service) - .await - { - eprintln!("failed to serve connection: {err:#}"); - } - } - Err(e) => { - eprintln!("failed to perform tls handshake: {:?}", e); - } - } - }) - .await; - - Ok(()) -} - -// Custom echo service, handling two different routes and a -// catch-all 404 responder. -async fn echo(req: Request) -> Result>, hyper::Error> { - let mut response = Response::new(Full::default()); - match (req.method(), req.uri().path()) { - // Help route. - (&Method::GET, "/") => { - *response.body_mut() = Full::from("Try POST /echo\n"); - } - // Echo service route. - (&Method::POST, "/echo") => { - *response.body_mut() = Full::from(req.into_body().collect().await?.to_bytes()); - } - // Catch-all 404. - _ => { - *response.status_mut() = StatusCode::NOT_FOUND; - } - }; - Ok(response) -} diff --git a/examples-xsmall/.cargo/config.toml b/examples-xsmall/.cargo/config.toml deleted file mode 100644 index 9203efd..0000000 --- a/examples-xsmall/.cargo/config.toml +++ /dev/null @@ -1,3 +0,0 @@ -[unstable] -build-std = ["core", "alloc", "std", "compiler_builtins"] -build-std-features = ["compiler-builtins-mem"] diff --git a/examples-xsmall/Cargo.lock b/examples-xsmall/Cargo.lock deleted file mode 100644 index 331859c..0000000 --- a/examples-xsmall/Cargo.lock +++ /dev/null @@ -1,1597 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "backtrace" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base16ct" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" - -[[package]] -name = "base64" -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" - -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - -[[package]] -name = "ccm" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae3c82e4355234767756212c570e29833699ab63e6ffd161887314cc5b43847" -dependencies = [ - "aead", - "cipher", - "ctr", - "subtle", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chacha20" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" -dependencies = [ - "cfg-if", - "cipher", - "cpufeatures", -] - -[[package]] -name = "chacha20poly1305" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" -dependencies = [ - "aead", - "chacha20", - "cipher", - "poly1305", - "zeroize", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", - "zeroize", -] - -[[package]] -name = "const-oid" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" - -[[package]] -name = "constcat" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f272d0c4cf831b4fa80ee529c7707f76585986e910e1fbce1d7921970bc1a241" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "cpufeatures" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" -dependencies = [ - "libc", -] - -[[package]] -name = "crypto-bigint" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740fe28e594155f10cfc383984cbefd529d7396050557148f79cb0f621204124" -dependencies = [ - "generic-array", - "rand_core", - "subtle", - "zeroize", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "rand_core", - "typenum", -] - -[[package]] -name = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "curve25519-dalek" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622178105f911d937a42cdb140730ba4a3ed2becd8ae6ce39c7d28b5d75d4588" -dependencies = [ - "cfg-if", - "cpufeatures", - "curve25519-dalek-derive", - "digest", - "fiat-crypto", - "platforms", - "rustc_version", - "subtle", - "zeroize", -] - -[[package]] -name = "curve25519-dalek-derive" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "der" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" -dependencies = [ - "const-oid", - "pem-rfc7468", - "zeroize", -] - -[[package]] -name = "deranged" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "const-oid", - "crypto-common", - "subtle", -] - -[[package]] -name = "ecdsa" -version = "0.16.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b1e0c257a9e9f25f90ff76d7a68360ed497ee519c8e428d1825ef0000799d4" -dependencies = [ - "der", - "digest", - "elliptic-curve", - "rfc6979", - "signature", - "spki", -] - -[[package]] -name = "ed25519" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60f6d271ca33075c88028be6f04d502853d63a5ece419d269c15315d4fc1cf1d" -dependencies = [ - "pkcs8", - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" -dependencies = [ - "curve25519-dalek", - "ed25519", - "serde", - "sha2", - "zeroize", -] - -[[package]] -name = "elliptic-curve" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" -dependencies = [ - "base16ct", - "crypto-bigint", - "digest", - "ff", - "generic-array", - "group", - "hkdf", - "pem-rfc7468", - "pkcs8", - "rand_core", - "sec1", - "subtle", - "zeroize", -] - -[[package]] -name = "examples-xsmall" -version = "0.1.0" -dependencies = [ - "anyhow", - "hyper", - "hyper-rustls", - "rcgen", - "rustls", - "rustls-pki-types", - "rustls-provider-rustcrypto", - "tokio", - "vc-ltl", -] - -[[package]] -name = "ff" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" -dependencies = [ - "rand_core", - "subtle", -] - -[[package]] -name = "fiat-crypto" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0870c84016d4b481be5c9f323c24f65e31e901ae618f0e80f4308fb00de1d2d" - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "pin-utils", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", - "zeroize", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" - -[[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" -dependencies = [ - "ff", - "rand_core", - "subtle", -] - -[[package]] -name = "h2" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - -[[package]] -name = "hyper" -version = "0.14.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.9", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.25.0-alpha.1" -source = "git+https://github.com/stevefan1999-personal/hyper-rustls#f0bb1651fc010d9e993687006d92b10dcdd99c2d" -dependencies = [ - "futures-util", - "http", - "hyper", - "log", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "webpki-roots", -] - -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -dependencies = [ - "spin 0.5.2", -] - -[[package]] -name = "libc" -version = "0.2.148" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" - -[[package]] -name = "libm" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "memchr" -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "wasi", - "windows-sys", -] - -[[package]] -name = "num-bigint-dig" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" -dependencies = [ - "byteorder", - "lazy_static", - "libm", - "num-integer", - "num-iter", - "num-traits", - "rand", - "smallvec", - "zeroize", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "object" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "p256" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p384" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" -dependencies = [ - "ecdsa", - "elliptic-curve", - "primeorder", - "sha2", -] - -[[package]] -name = "p521" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2808cdb09072e3b966f5da8280cd29b252988afb898f688074d8dd1d26c0db" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "paste" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" - -[[package]] -name = "pem" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3163d2912b7c3b52d651a055f2c7eec9ba5cd22d26ef75b8dd3a59980b185923" -dependencies = [ - "base64", - "serde", -] - -[[package]] -name = "pem-rfc7468" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" -dependencies = [ - "base64ct", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkcs1" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" -dependencies = [ - "der", - "pkcs8", - "spki", -] - -[[package]] -name = "pkcs5" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e847e2c91a18bfa887dd028ec33f2fe6f25db77db3619024764914affe8b69a6" -dependencies = [ - "der", - "spki", -] - -[[package]] -name = "pkcs8" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" -dependencies = [ - "der", - "pkcs5", - "spki", -] - -[[package]] -name = "platforms" -version = "3.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4503fa043bf02cee09a9582e9554b4c6403b2ef55e4612e96561d294419429f8" - -[[package]] -name = "poly1305" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" -dependencies = [ - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "polyval" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb" -dependencies = [ - "cfg-if", - "cpufeatures", - "opaque-debug", - "universal-hash", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "primeorder" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3" -dependencies = [ - "elliptic-curve", -] - -[[package]] -name = "proc-macro2" -version = "1.0.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rcgen" -version = "0.11.3" -source = "git+https://github.com/rustls/rcgen#0318d2f0a4bb9e65c84d284acff8587bd418d837" -dependencies = [ - "pem", - "ring", - "time", - "yasna", -] - -[[package]] -name = "rfc6979" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" -dependencies = [ - "hmac", - "subtle", -] - -[[package]] -name = "ring" -version = "0.17.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb0205304757e5d899b9c2e448b867ffd03ae7f988002e47cd24954391394d0b" -dependencies = [ - "cc", - "getrandom", - "libc", - "spin 0.9.8", - "untrusted", - "windows-sys", -] - -[[package]] -name = "rsa" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" -dependencies = [ - "byteorder", - "const-oid", - "digest", - "num-bigint-dig", - "num-integer", - "num-iter", - "num-traits", - "pkcs1", - "pkcs8", - "rand_core", - "sha2", - "signature", - "spki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustls" -version = "0.22.0-alpha.4" -source = "git+https://github.com/rustls/rustls#b7a6091ab458467a554d62a57c4ff9cf48a552af" -dependencies = [ - "log", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.7.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f84fefc2b087ffd1677ed5910ec4b2650b1eb45ed083d38630304f48496b69" -dependencies = [ - "openssl-probe", - "rustls-pemfile", - "rustls-pki-types", - "schannel", - "security-framework", -] - -[[package]] -name = "rustls-pemfile" -version = "2.0.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4aaa4fe93b39faddb6a8f99568c3e5880680156da0d46818e884a071381f67fe" -dependencies = [ - "base64", - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47003264dea418db67060fa420ad16d0d2f8f0a0360d825c00e177ac52cb5d8" - -[[package]] -name = "rustls-provider-rustcrypto" -version = "0.0.2" -dependencies = [ - "aead", - "aes-gcm", - "block-buffer", - "ccm", - "chacha20poly1305", - "constcat", - "crypto-common", - "der", - "digest", - "ecdsa", - "ed25519-dalek", - "elliptic-curve", - "generic-array", - "hmac", - "p256", - "p384", - "p521", - "paste", - "pkcs8", - "rand_core", - "rsa", - "rustls", - "rustls-pki-types", - "rustls-webpki", - "sec1", - "sha2", - "signature", - "subtle", - "typenum", - "x25519-dalek", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.0-alpha.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34d9ed3a8267782ba32d257ff5b197b63eef19a467dbd1be011caaae35ee416e" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - -[[package]] -name = "schannel" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "sec1" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" -dependencies = [ - "base16ct", - "der", - "generic-array", - "pkcs8", - "subtle", - "zeroize", -] - -[[package]] -name = "security-framework" -version = "2.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" - -[[package]] -name = "serde" -version = "1.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sha2" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "signature" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500" -dependencies = [ - "digest", - "rand_core", -] - -[[package]] -name = "slab" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "socket2" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" -dependencies = [ - "libc", - "windows-sys", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - -[[package]] -name = "spki" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" -dependencies = [ - "base64ct", - "der", -] - -[[package]] -name = "subtle" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" - -[[package]] -name = "syn" -version = "2.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "time" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6bb557fd245c28e6411aa56b6403c689ad95061f50e4be16c274e70a17e48" -dependencies = [ - "deranged", - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" - -[[package]] -name = "tokio" -version = "1.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" -dependencies = [ - "backtrace", - "bytes", - "libc", - "mio", - "num_cpus", - "pin-project-lite", - "socket2 0.5.4", - "tokio-macros", - "windows-sys", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-rustls" -version = "0.25.0-alpha.2" -source = "git+https://github.com/rustls/tokio-rustls#82d32c410f2364332c5df656717193f278769e1c" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "universal-hash" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] - -[[package]] -name = "untrusted" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" - -[[package]] -name = "vc-ltl" -version = "5.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e98ae3481f41a314867586b0bfbd20eabf5b196a420b1750a69d1a37b80d25" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "webpki-roots" -version = "0.26.0-alpha.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42157929d7ca9c353222a4d1763c52ef86d25d0fd2eca66076df5975fd4e25ed" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "x25519-dalek" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" -dependencies = [ - "curve25519-dalek", - "rand_core", - "serde", - "zeroize", -] - -[[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.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] diff --git a/examples-xsmall/Cargo.toml b/examples-xsmall/Cargo.toml deleted file mode 100644 index eaaa4f1..0000000 --- a/examples-xsmall/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "examples-xsmall" -version = "0.1.0" -edition = "2021" - -[dev-dependencies] -anyhow = "1.0.75" -hyper = "0.14.27" -hyper-rustls = { version = "=0.25.0-alpha.1", features = ["http2", "webpki-roots"] } -pki-types = { package = "rustls-pki-types", version = "0.2.1", default-features = false } -rcgen = "0.11.1" -rustls = { version = "=0.22.0-alpha.4", default-features = false } -rustls-provider-rustcrypto = { path = "../" } -tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "io-std", "io-util", "net"] } -vc-ltl = "5.0.8" - -[features] -tls12 = ["rustls-provider-rustcrypto/tls12"] - -[profile.release] -strip = true -opt-level = "s" -lto = true -codegen-units = 1 - -[patch.crates-io] -rustls = { git = 'https://github.com/rustls/rustls' } -tokio-rustls = { git = 'https://github.com/rustls/tokio-rustls' } -hyper-rustls = { git = "https://github.com/stevefan1999-personal/hyper-rustls" } -rcgen = { git = "https://github.com/rustls/rcgen" } \ No newline at end of file diff --git a/examples-xsmall/README.md b/examples-xsmall/README.md deleted file mode 100644 index 8704344..0000000 --- a/examples-xsmall/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# examples-xsmall - -This is identical to the examples one level above, except it is using some Rust techniques known to man to generate small binaries as a mean to test the compactness of RustTLS \ No newline at end of file diff --git a/examples-xsmall/examples/client.rs b/examples-xsmall/examples/client.rs deleted file mode 100644 index d396acd..0000000 --- a/examples-xsmall/examples/client.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::str::FromStr; - -use anyhow::anyhow; -use hyper::{body::to_bytes, client, Body, Uri}; -use pki_types::CertificateDer; -use rustls::{ - client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, - DigitallySignedStruct, ServerName, SignatureScheme, -}; -use rustls_provider_rustcrypto::Provider; - -#[derive(Debug)] -struct NoopServerVerifier; - -impl ServerCertVerifier for NoopServerVerifier { - fn verify_server_cert( - &self, - _end_entity: &CertificateDer<'_>, - _intermediates: &[CertificateDer<'_>], - _server_name: &ServerName, - _ocsp_response: &[u8], - _now: pki_types::UnixTime, - ) -> Result { - Ok(ServerCertVerified::assertion()) - } - - fn verify_tls12_signature( - &self, - _message: &[u8], - _cert: &CertificateDer<'_>, - _dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - - fn verify_tls13_signature( - &self, - _message: &[u8], - _cert: &CertificateDer<'_>, - _dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - - fn supported_verify_schemes(&self) -> Vec { - vec![ - SignatureScheme::RSA_PKCS1_SHA1, - SignatureScheme::ECDSA_SHA1_Legacy, - SignatureScheme::RSA_PKCS1_SHA256, - SignatureScheme::ECDSA_NISTP256_SHA256, - SignatureScheme::RSA_PKCS1_SHA384, - SignatureScheme::ECDSA_NISTP384_SHA384, - SignatureScheme::RSA_PKCS1_SHA512, - SignatureScheme::ECDSA_NISTP521_SHA512, - SignatureScheme::RSA_PSS_SHA256, - SignatureScheme::RSA_PSS_SHA384, - SignatureScheme::RSA_PSS_SHA512, - SignatureScheme::ED25519, - SignatureScheme::ED448, - ] - } -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - // Prepare the HTTPS connector - let https = hyper_rustls::HttpsConnectorBuilder::new() - .with_provider_and_webpki_roots(&Provider) - .https_or_http() - .enable_all_versions() - .build(); - - // Build the hyper client from the HTTPS connector. - let client: client::Client<_, hyper::Body> = client::Client::builder().build(https); - - // Prepare a chain of futures which sends a GET request, inspects - // the returned headers, collects the whole body and prints it to - // stdout. - let fut = async move { - let res = client - .get(Uri::from_str("https://ecc256.badssl.com/")?) - .await - .map_err(|e| anyhow!("Could not get: {:?}", e))?; - println!("Status:\n{}", res.status()); - println!("Headers:\n{:#?}", res.headers()); - - let body: Body = res.into_body(); - let body = to_bytes(body) - .await - .map_err(|e| anyhow!("Could not get body: {:?}", e))?; - println!("Body:\n{}", String::from_utf8_lossy(&body)); - - Ok(()) - }; - - fut.await -} diff --git a/examples-xsmall/examples/server.rs b/examples-xsmall/examples/server.rs deleted file mode 100644 index 93c504f..0000000 --- a/examples-xsmall/examples/server.rs +++ /dev/null @@ -1,97 +0,0 @@ -use std::{ - io::{self}, - net::ToSocketAddrs, -}; - -use hyper::{ - server::conn::AddrIncoming, - service::{make_service_fn, service_fn}, - Body, Method, Request, Response, Server, StatusCode, -}; -use hyper_rustls::TlsAcceptor; -use pki_types::PrivateKeyDer; -use rustls_provider_rustcrypto::Provider; -struct TestPki { - server_cert_der: Vec, - server_key_der: Vec, -} - -impl TestPki { - fn new() -> Self { - let alg = &rcgen::PKCS_ECDSA_P256_SHA256; - let mut ca_params = rcgen::CertificateParams::new(Vec::new()); - ca_params - .distinguished_name - .push(rcgen::DnType::OrganizationName, "Rustls Server Acceptor"); - ca_params - .distinguished_name - .push(rcgen::DnType::CommonName, "Example CA"); - ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained); - ca_params.key_usages = vec![ - rcgen::KeyUsagePurpose::KeyCertSign, - rcgen::KeyUsagePurpose::DigitalSignature, - rcgen::KeyUsagePurpose::CrlSign, - ]; - ca_params.alg = alg; - let ca_cert = rcgen::Certificate::from_params(ca_params).unwrap(); - - // Create a server end entity cert issued by the CA. - let mut server_ee_params = rcgen::CertificateParams::new(vec!["localhost".to_string()]); - server_ee_params.is_ca = rcgen::IsCa::NoCa; - server_ee_params.extended_key_usages = vec![rcgen::ExtendedKeyUsagePurpose::ServerAuth]; - server_ee_params.alg = alg; - let server_cert = rcgen::Certificate::from_params(server_ee_params).unwrap(); - let server_cert_der = server_cert.serialize_der_with_signer(&ca_cert).unwrap(); - let server_key_der = server_cert.serialize_private_key_der(); - Self { - server_cert_der, - server_key_der, - } - } -} - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let pki = TestPki::new(); - let addr = "0.0.0.0:4443" - .to_socket_addrs()? - .next() - .ok_or_else(|| std::io::Error::from(std::io::ErrorKind::AddrNotAvailable))?; - let incoming = AddrIncoming::bind(&addr)?; - let acceptor = TlsAcceptor::builder() - .with_provider_and_single_cert( - &Provider, - vec![pki.server_cert_der.clone().into()], - PrivateKeyDer::Pkcs8(pki.server_key_der.clone().into()), - )? - .with_all_versions_alpn() - .with_incoming(incoming); - let service = make_service_fn(|_| async { Ok::<_, io::Error>(service_fn(echo)) }); - let server = Server::builder(acceptor).serve(service); - - // Run the future, keep going until an error occurs. - println!("Starting to serve on https://{}.", addr); - server.await?; - Ok(()) -} - -// Custom echo service, handling two different routes and a -// catch-all 404 responder. -async fn echo(req: Request) -> Result, hyper::Error> { - let mut response = Response::new(Body::empty()); - match (req.method(), req.uri().path()) { - // Help route. - (&Method::GET, "/") => { - *response.body_mut() = Body::from("Try POST /echo\n"); - } - // Echo service route. - (&Method::POST, "/echo") => { - *response.body_mut() = req.into_body(); - } - // Catch-all 404. - _ => { - *response.status_mut() = StatusCode::NOT_FOUND; - } - }; - Ok(response) -} diff --git a/src/aead.rs b/src/aead.rs index 6ff57b7..a5ad58f 100644 --- a/src/aead.rs +++ b/src/aead.rs @@ -1,10 +1,19 @@ use aead::Buffer; use rustls::crypto::cipher::{BorrowedPayload, PrefixedPayload}; -pub mod chacha20; +#[cfg(feature = "chacha20poly1305")] +pub const CHACHAPOLY1305_OVERHEAD: usize = 16; + +#[cfg(feature = "chacha20poly1305")] +pub struct ChaCha20Poly1305; + +#[cfg(feature = "gcm")] pub mod gcm; -pub(crate) struct EncryptBufferAdapter<'a>(&'a mut PrefixedPayload); +#[cfg(feature = "ccm")] +pub mod ccm; + +pub(crate) struct EncryptBufferAdapter<'a>(pub(crate) &'a mut PrefixedPayload); impl AsRef<[u8]> for EncryptBufferAdapter<'_> { fn as_ref(&self) -> &[u8] { @@ -29,7 +38,7 @@ impl Buffer for EncryptBufferAdapter<'_> { } } -pub(crate) struct DecryptBufferAdapter<'a, 'p>(&'a mut BorrowedPayload<'p>); +pub(crate) struct DecryptBufferAdapter<'a, 'p>(pub(crate) &'a mut BorrowedPayload<'p>); impl AsRef<[u8]> for DecryptBufferAdapter<'_, '_> { fn as_ref(&self) -> &[u8] { @@ -52,3 +61,6 @@ impl Buffer for DecryptBufferAdapter<'_, '_> { self.0.truncate(len) } } + +#[cfg(feature = "aes")] +pub mod aes; diff --git a/src/aead/aes.rs b/src/aead/aes.rs new file mode 100644 index 0000000..099544d --- /dev/null +++ b/src/aead/aes.rs @@ -0,0 +1,42 @@ +use aes::{Aes128, Aes256}; + +#[cfg(feature = "gcm")] +use aes_gcm::AesGcm; + +#[cfg(feature = "ccm")] +use { + ccm::Ccm, + typenum::{U16, U8}, +}; + +#[cfg(any(feature = "gcm", feature = "ccm"))] +use typenum::U12; + +// The AEAD_AES_128_CCM authenticated encryption algorithm works as +// specified in [CCM], using AES-128 as the block cipher, by providing +// the key, nonce, associated data, and plaintext to that mode of +// operation. The formatting and counter generation function are as +// specified in Appendix A of that reference, and the values of the +// parameters identified in that appendix are as follows: +// the nonce length n is 12, +// the tag length t is 16, and +// the value of q is 3. +#[cfg(feature = "ccm")] +pub type Aes128Ccm = Ccm; +#[cfg(feature = "ccm")] +pub type Aes256Ccm = Ccm; + +// The AEAD_AES_128_CCM_8 authenticated encryption algorithm is +// identical to the AEAD_AES_128_CCM algorithm (see Section 5.3 of +// [RFC5116]), except that it uses 8 octets for authentication, instead +// of the full 16 octets used by AEAD_AES_128_CCM. +#[cfg(feature = "ccm")] +pub type Aes128Ccm8 = Ccm; +#[cfg(feature = "ccm")] +pub type Aes256Ccm8 = Ccm; + +#[cfg(feature = "gcm")] +pub type Aes128Gcm = AesGcm; + +#[cfg(feature = "gcm")] +pub type Aes256Gcm = AesGcm; diff --git a/src/aead/ccm.rs b/src/aead/ccm.rs new file mode 100644 index 0000000..caa9fe2 --- /dev/null +++ b/src/aead/ccm.rs @@ -0,0 +1,11 @@ +#[cfg(feature = "aes-ccm")] +pub struct Aes128Ccm; + +#[cfg(feature = "aes-ccm")] +pub struct Aes256Ccm; + +#[cfg(feature = "aes-ccm")] +pub struct Aes128Ccm8; + +#[cfg(feature = "aes-ccm")] +pub struct Aes256Ccm8; diff --git a/src/aead/chacha20.rs b/src/aead/chacha20.rs deleted file mode 100644 index 995b79e..0000000 --- a/src/aead/chacha20.rs +++ /dev/null @@ -1,197 +0,0 @@ -#[cfg(feature = "alloc")] -use alloc::boxed::Box; - -use super::{DecryptBufferAdapter, EncryptBufferAdapter}; - -use chacha20poly1305::{AeadInPlace, KeyInit, KeySizeUser}; -use rustls::crypto::cipher::{ - self, AeadKey, InboundOpaqueMessage, InboundPlainMessage, Iv, MessageDecrypter, - MessageEncrypter, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, - Tls13AeadAlgorithm, UnsupportedOperationError, -}; -use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion}; - -#[cfg(feature = "tls12")] -use rustls::crypto::cipher::{KeyBlockShape, Tls12AeadAlgorithm, NONCE_LEN}; - -pub struct Chacha20Poly1305; - -impl Tls13AeadAlgorithm for Chacha20Poly1305 { - fn encrypter(&self, key: AeadKey, iv: Iv) -> Box { - Box::new(Tls13Cipher( - chacha20poly1305::ChaCha20Poly1305::new_from_slice(key.as_ref()) - .expect("key should be valid"), - iv, - )) - } - - fn decrypter(&self, key: AeadKey, iv: Iv) -> Box { - Box::new(Tls13Cipher( - chacha20poly1305::ChaCha20Poly1305::new_from_slice(key.as_ref()) - .expect("key should be valid"), - iv, - )) - } - - fn key_len(&self) -> usize { - chacha20poly1305::ChaCha20Poly1305::key_size() - } - - fn extract_keys( - &self, - key: AeadKey, - iv: Iv, - ) -> Result { - Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv }) - } -} - -#[cfg(feature = "tls12")] -impl Tls12AeadAlgorithm for Chacha20Poly1305 { - fn encrypter(&self, key: AeadKey, iv: &[u8], _: &[u8]) -> Box { - Box::new(Tls12Cipher( - chacha20poly1305::ChaCha20Poly1305::new_from_slice(key.as_ref()) - .expect("key should be valid"), - Iv::copy(iv), - )) - } - - fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box { - Box::new(Tls12Cipher( - chacha20poly1305::ChaCha20Poly1305::new_from_slice(key.as_ref()) - .expect("key should be valid"), - Iv::copy(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 { - // This should always be true because KeyBlockShape and the Iv nonce len are in - // agreement. - debug_assert_eq!(NONCE_LEN, iv.len()); - Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { - key, - iv: Iv::new(iv[..].try_into().expect("conversion should succeed")), - }) - } -} - -struct Tls13Cipher(chacha20poly1305::ChaCha20Poly1305, Iv); - -impl MessageEncrypter for Tls13Cipher { - fn encrypt( - &mut self, - m: OutboundPlainMessage<'_>, - seq: u64, - ) -> Result { - let total_len = self.encrypted_payload_len(m.payload.len()); - let mut payload = PrefixedPayload::with_capacity(total_len); - - payload.extend_from_chunks(&m.payload); - payload.extend_from_slice(&m.typ.to_array()); - - let nonce: chacha20poly1305::Nonce = cipher::Nonce::new(&self.1, seq).0.into(); - let aad = cipher::make_tls13_aad(total_len); - - self.0 - .encrypt_in_place(&nonce, &aad, &mut EncryptBufferAdapter(&mut payload)) - .map_err(|_| rustls::Error::EncryptError) - .map(|()| { - OutboundOpaqueMessage::new( - ContentType::ApplicationData, - ProtocolVersion::TLSv1_2, - payload, - ) - }) - } - - fn encrypted_payload_len(&self, payload_len: usize) -> usize { - payload_len + 1 + CHACHAPOLY1305_OVERHEAD - } -} - -impl MessageDecrypter for Tls13Cipher { - fn decrypt<'a>( - &mut self, - mut m: InboundOpaqueMessage<'a>, - seq: u64, - ) -> Result, rustls::Error> { - let payload = &mut m.payload; - let nonce: chacha20poly1305::Nonce = cipher::Nonce::new(&self.1, seq).0.into(); - let aad = cipher::make_tls13_aad(payload.len()); - - self.0 - .decrypt_in_place(&nonce, &aad, &mut DecryptBufferAdapter(payload)) - .map_err(|_| rustls::Error::DecryptError)?; - - m.into_tls13_unpadded_message() - } -} - -#[cfg(feature = "tls12")] -struct Tls12Cipher(chacha20poly1305::ChaCha20Poly1305, Iv); - -#[cfg(feature = "tls12")] -impl MessageEncrypter for Tls12Cipher { - fn encrypt( - &mut self, - m: OutboundPlainMessage<'_>, - seq: u64, - ) -> Result { - let total_len = self.encrypted_payload_len(m.payload.len()); - let mut payload = PrefixedPayload::with_capacity(total_len); - - payload.extend_from_chunks(&m.payload); - - let nonce: chacha20poly1305::Nonce = cipher::Nonce::new(&self.1, seq).0.into(); - let aad = cipher::make_tls12_aad(seq, m.typ, m.version, m.payload.len()); - - self.0 - .encrypt_in_place(&nonce, &aad, &mut EncryptBufferAdapter(&mut payload)) - .map_err(|_| rustls::Error::EncryptError) - .map(|_| OutboundOpaqueMessage::new(m.typ, m.version, payload)) - } - - fn encrypted_payload_len(&self, payload_len: usize) -> usize { - payload_len + CHACHAPOLY1305_OVERHEAD - } -} - -#[cfg(feature = "tls12")] -impl MessageDecrypter for Tls12Cipher { - fn decrypt<'a>( - &mut self, - mut m: InboundOpaqueMessage<'a>, - seq: u64, - ) -> Result, rustls::Error> { - let payload = &m.payload; - let nonce: chacha20poly1305::Nonce = cipher::Nonce::new(&self.1, seq).0.into(); - let aad = cipher::make_tls12_aad( - seq, - m.typ, - m.version, - payload.len() - CHACHAPOLY1305_OVERHEAD, - ); - - let payload = &mut m.payload; - self.0 - .decrypt_in_place(&nonce, &aad, &mut DecryptBufferAdapter(payload)) - .map_err(|_| rustls::Error::DecryptError)?; - - Ok(m.into_plain_message()) - } -} - -const CHACHAPOLY1305_OVERHEAD: usize = 16; diff --git a/src/aead/gcm.rs b/src/aead/gcm.rs index 14be62b..5dbdfbf 100644 --- a/src/aead/gcm.rs +++ b/src/aead/gcm.rs @@ -1,235 +1,5 @@ -#[cfg(feature = "alloc")] -use alloc::boxed::Box; +#[cfg(feature = "aes-gcm")] +pub struct Aes128Gcm; -use super::{DecryptBufferAdapter, EncryptBufferAdapter}; - -use aead::AeadInPlace; -use crypto_common::{KeyInit, KeySizeUser}; -use paste::paste; -use rustls::crypto::cipher::{ - self, AeadKey, InboundOpaqueMessage, InboundPlainMessage, MessageDecrypter, MessageEncrypter, - OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, Tls13AeadAlgorithm, -}; -use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion}; - -#[cfg(feature = "tls12")] -use { - aead::AeadCore, - crypto_common::typenum::Unsigned, - rustls::crypto::cipher::{Iv, KeyBlockShape, Tls12AeadAlgorithm}, -}; - -#[cfg(feature = "tls12")] -const TLS12_GCM_EXPLICIT_NONCE_LEN: usize = 8; - -#[cfg(feature = "tls12")] -const TLS12_GCM_OVERHEAD: usize = TLS12_GCM_EXPLICIT_NONCE_LEN + 16; - -macro_rules! impl_gcm_tls13 { - ($name: ident, $aead: ty, $overhead: expr) => { - paste! { - pub struct []; - - impl Tls13AeadAlgorithm for [] { - fn encrypter(&self, key: AeadKey, iv: cipher::Iv) -> Box { - Box::new([]( - $aead::new_from_slice(key.as_ref()).unwrap(), - iv, - )) - } - - fn decrypter(&self, key: AeadKey, iv: cipher::Iv) -> Box { - Box::new([]( - $aead::new_from_slice(key.as_ref()).unwrap(), - iv, - )) - } - - fn key_len(&self) -> usize { - $aead::key_size() - } - fn extract_keys( - &self, - key: AeadKey, - iv: cipher::Iv, - ) -> Result { - Ok(ConnectionTrafficSecrets::Aes256Gcm { key, iv }) - } - } - - struct []($aead, cipher::Iv); - - impl MessageEncrypter for [] { - fn encrypt(&mut self, m: OutboundPlainMessage<'_>, seq: u64) -> Result { - let total_len = self.encrypted_payload_len(m.payload.len()); - let mut payload = PrefixedPayload::with_capacity(total_len); - - let nonce = cipher::Nonce::new(&self.1, seq).0; - let aad = cipher::make_tls13_aad(total_len); - payload.extend_from_chunks(&m.payload); - payload.extend_from_slice(&m.typ.to_array()); - - self.0 - .encrypt_in_place(&nonce.into(), &aad, &mut EncryptBufferAdapter(&mut payload)) - .map_err(|_| rustls::Error::EncryptError) - .map(|_| OutboundOpaqueMessage::new( - ContentType::ApplicationData, - ProtocolVersion::TLSv1_2, - payload, - )) - } - - fn encrypted_payload_len(&self, payload_len: usize) -> usize { - payload_len + 1 + $overhead - } - } - - impl MessageDecrypter for [] { - fn decrypt<'a>(&mut self, mut m: InboundOpaqueMessage<'a>, seq: u64) -> Result, rustls::Error> { - let payload = &mut m.payload; - let nonce = cipher::Nonce::new(&self.1, seq).0; - let aad = cipher::make_tls13_aad(payload.len()); - - self.0 - .decrypt_in_place(&nonce.into(), &aad, &mut DecryptBufferAdapter(payload)) - .map_err(|_| rustls::Error::DecryptError)?; - - m.into_tls13_unpadded_message() - } - } - - } - }; -} - -#[cfg(feature = "tls12")] -macro_rules! impl_gcm_tls12 { - ($name: ident, $aead: ty, $nonce: expr, $overhead: expr) => { - paste! { - #[cfg(feature = "tls12")] - pub struct []; - - #[cfg(feature = "tls12")] - impl Tls12AeadAlgorithm for [] { - fn encrypter(&self, key: AeadKey, write_iv: &[u8], explicit: &[u8]) -> Box { - Box::new([]( - $aead::new_from_slice(key.as_ref()).unwrap(), - { - let mut iv: [u8; 12] = [0; 12]; - iv[..4].copy_from_slice(write_iv); - iv[4..].copy_from_slice(explicit); - iv - }, - )) - } - - fn decrypter(&self, dec_key: AeadKey, dec_iv: &[u8]) -> Box { - Box::new([]( - $aead::new_from_slice(dec_key.as_ref()).unwrap(), - dec_iv.try_into().unwrap(), - )) - } - - fn key_block_shape(&self) -> KeyBlockShape { - KeyBlockShape { - enc_key_len: $aead::key_size(), - fixed_iv_len: 4, - explicit_nonce_len: 8, - } - } - - fn extract_keys( - &self, - key: AeadKey, - iv: &[u8], - _explicit: &[u8], - ) -> Result { - Ok(ConnectionTrafficSecrets::Aes128Gcm { - key, - iv: Iv::new(iv[..].try_into().unwrap()), - }) - } - } - - #[cfg(feature = "tls12")] - struct []($aead, [u8; 12]); - - #[cfg(feature = "tls12")] - impl MessageEncrypter for [] { - fn encrypt(&mut self, m: OutboundPlainMessage<'_>, seq: u64) -> Result { - let total_len = self.encrypted_payload_len(m.payload.len()); - let mut payload = PrefixedPayload::with_capacity(total_len); - - let nonce = cipher::Nonce::new(&self.1.into(), seq).0; - let aad = cipher::make_tls12_aad(seq, m.typ, m.version, m.payload.len()); - payload.extend_from_slice(&nonce.as_ref()[4..]); // explicit - payload.extend_from_chunks(&m.payload); - - self.0 - .encrypt_in_place_detached(&nonce.into(), &aad, &mut payload.as_mut()[$nonce..]) - .map(|tag| payload.extend(tag.as_ref() as &[u8])) - .map_err(|_| rustls::Error::EncryptError) - .map(|_| OutboundOpaqueMessage::new(m.typ, m.version, payload)) - } - fn encrypted_payload_len(&self, payload_len: usize) -> usize { - payload_len + $nonce + <$aead as AeadCore>::TagSize::USIZE - } - } - - #[cfg(feature = "tls12")] - struct []($aead, [u8; 4]); - - #[cfg(feature = "tls12")] - impl MessageDecrypter for [] { - fn decrypt<'a>(&mut self, mut m: InboundOpaqueMessage<'a>, seq: u64) -> Result, rustls::Error> { - type TagSize = <$aead as AeadCore>::TagSize; - - let payload = &m.payload; - - if payload.len() < $overhead { - return Err(rustls::Error::DecryptError); - } - - let nonce: aead::Nonce<$aead> = { - let mut nonce = [0u8; 12]; - nonce[..4].copy_from_slice(&self.1); // dec_iv - nonce[4..].copy_from_slice(&payload[..$nonce]); - nonce.into() - }; - - let aad = cipher::make_tls12_aad(seq, m.typ, m.version, payload.len() - $overhead); - - let payload = &mut m.payload; - let tag_pos = { - let payload = &mut payload[$nonce..]; - let tag_pos = payload.len() - TagSize::to_usize(); - let (msg, tag) = payload.split_at_mut(tag_pos); - - let tag = aes_gcm::Tag::::from_slice(tag); - self.0 - .decrypt_in_place_detached(&nonce, &aad, msg, tag) - .map_err(|_| rustls::Error::DecryptError)?; - tag_pos - }; - - // We defer the truncation to here, because we may inadvertently shifted the - // original data if the decryption failed. Another way to avoid this is - // to clone the payload slice starting after the explicit nonce, - // but this will cause an additional cloning and copying - payload.rotate_left($nonce); - payload.truncate(tag_pos); - Ok(m.into_plain_message()) - } - } - } - }; -} - -impl_gcm_tls13! {Aes128Gcm, aes_gcm::Aes128Gcm, 16} -impl_gcm_tls13! {Aes256Gcm, aes_gcm::Aes256Gcm, 16} - -#[cfg(feature = "tls12")] -impl_gcm_tls12! {Aes128Gcm, aes_gcm::Aes128Gcm, TLS12_GCM_EXPLICIT_NONCE_LEN, TLS12_GCM_OVERHEAD} - -#[cfg(feature = "tls12")] -impl_gcm_tls12! {Aes256Gcm, aes_gcm::Aes256Gcm, TLS12_GCM_EXPLICIT_NONCE_LEN, TLS12_GCM_OVERHEAD} +#[cfg(feature = "aes-gcm")] +pub struct Aes256Gcm; diff --git a/src/hash.rs b/src/hash.rs index dfa58e1..7221fb8 100644 --- a/src/hash.rs +++ b/src/hash.rs @@ -4,17 +4,16 @@ use alloc::boxed::Box; use digest::{Digest, OutputSizeUser}; use paste::paste; use rustls::crypto::{self, hash}; -use sha2::{Sha256, Sha384}; macro_rules! impl_hash { ($name:ident, $ty:ty, $algo:ty) => { paste! { #[allow(non_camel_case_types)] - struct []; + pub struct []; - impl hash::Hash for [] { + impl hash::Hash for [] { fn start(&self) -> Box { - Box::new([]($ty::new())) + Box::new([]($ty::new())) } fn hash(&self, data: &[u8]) -> hash::Output { @@ -31,15 +30,15 @@ macro_rules! impl_hash { } #[allow(non_camel_case_types)] - struct []($ty); + pub struct []($ty); - impl hash::Context for [] { + impl hash::Context for [] { fn fork_finish(&self) -> hash::Output { hash::Output::new(&self.0.clone().finalize()[..]) } fn fork(&self) -> Box { - Box::new([](self.0.clone())) + Box::new([](self.0.clone())) } fn finish(self: Box) -> hash::Output { @@ -51,12 +50,16 @@ macro_rules! impl_hash { } } - pub const $name: &dyn crypto::hash::Hash = &[]; + pub const $name: &dyn crypto::hash::Hash = &[]; } }; } -// impl_hash! {SHA224, Sha224, hash::HashAlgorithm::SHA224} -impl_hash! {SHA256, Sha256, hash::HashAlgorithm::SHA256} -impl_hash! {SHA384, Sha384, hash::HashAlgorithm::SHA384} -// impl_hash! {SHA512, Sha512, hash::HashAlgorithm::SHA512} +#[cfg(feature = "hash-sha224")] +impl_hash! {SHA224, ::sha2::Sha224, hash::HashAlgorithm::SHA224} +#[cfg(feature = "hash-sha256")] +impl_hash! {SHA256, ::sha2::Sha256, hash::HashAlgorithm::SHA256} +#[cfg(feature = "hash-sha384")] +impl_hash! {SHA384, ::sha2::Sha384, hash::HashAlgorithm::SHA384} +#[cfg(feature = "hash-sha512")] +impl_hash! {SHA512, ::sha2::Sha512, hash::HashAlgorithm::SHA512} diff --git a/src/hmac.rs b/src/hmac.rs index 8388c27..5e235cc 100644 --- a/src/hmac.rs +++ b/src/hmac.rs @@ -2,10 +2,8 @@ use alloc::boxed::Box; use crypto_common::OutputSizeUser; -use hmac::Mac; use paste::paste; -use rustls::crypto; -use sha2::{Sha256, Sha384}; +use rustls::crypto::hmac::{Hmac, Key, Tag}; macro_rules! impl_hmac { ( @@ -14,12 +12,13 @@ macro_rules! impl_hmac { ) => { paste! { #[allow(non_camel_case_types)] - struct []; + pub struct []; - impl crypto::hmac::Hmac for [] { - fn with_key(&self, key: &[u8]) -> Box { - Box::new([]( - hmac::Hmac::<$ty>::new_from_slice(key).unwrap(), + impl Hmac for [] { + fn with_key(&self, key: &[u8]) -> Box { + use ::hmac::Mac; + Box::new([]( + ::hmac::Hmac::<$ty>::new_from_slice(key).unwrap(), )) } @@ -29,28 +28,32 @@ macro_rules! impl_hmac { } #[allow(non_camel_case_types)] - struct [](hmac::Hmac<$ty>); + pub struct [](::hmac::Hmac<$ty>); - impl crypto::hmac::Key for [] { - fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> crypto::hmac::Tag { + impl Key for [] { + fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> Tag { + use ::hmac::Mac; let mut ctx = self.0.clone(); ctx.update(first); for m in middle { ctx.update(m); } ctx.update(last); - crypto::hmac::Tag::new(&ctx.finalize().into_bytes()[..]) + Tag::new(&ctx.finalize().into_bytes()[..]) } fn tag_len(&self) -> usize { $ty::output_size() } } - pub const $name: &dyn crypto::hmac::Hmac = &[]; + pub const $name: &dyn Hmac = &[]; } }; } -impl_hmac! {SHA256, Sha256} -impl_hmac! {SHA384, Sha384} -// impl_hmac! {SHA512, Sha512} +#[cfg(feature = "hash-sha256")] +impl_hmac! {SHA256, ::sha2::Sha256} +#[cfg(feature = "hash-sha384")] +impl_hmac! {SHA384, ::sha2::Sha384} +#[cfg(feature = "hash-sha512")] +impl_hmac! {SHA512, ::sha2::Sha512} diff --git a/src/kx.rs b/src/kx.rs index 66341f2..faead0b 100644 --- a/src/kx.rs +++ b/src/kx.rs @@ -1,108 +1,23 @@ -#[cfg(feature = "alloc")] -use alloc::boxed::Box; - -use crypto::{SharedSecret, SupportedKxGroup}; -use paste::paste; -use rustls::crypto; - -#[derive(Debug)] -pub struct X25519; - -impl crypto::SupportedKxGroup for X25519 { - fn name(&self) -> rustls::NamedGroup { - rustls::NamedGroup::X25519 - } - - fn start(&self) -> Result, rustls::Error> { - let priv_key = x25519_dalek::EphemeralSecret::random_from_rng(rand_core::OsRng); - let pub_key = (&priv_key).into(); - Ok(Box::new(X25519KeyExchange { priv_key, pub_key })) - } -} - -pub struct X25519KeyExchange { - priv_key: x25519_dalek::EphemeralSecret, - pub_key: x25519_dalek::PublicKey, -} - -impl crypto::ActiveKeyExchange for X25519KeyExchange { - fn complete(self: Box, peer: &[u8]) -> Result { - let peer_array: [u8; 32] = peer - .try_into() - .map_err(|_| rustls::Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?; - Ok(self - .priv_key - .diffie_hellman(&peer_array.into()) - .as_ref() - .into()) - } - - fn pub_key(&self) -> &[u8] { - self.pub_key.as_bytes() - } - - fn group(&self) -> rustls::NamedGroup { - X25519.name() - } -} - -macro_rules! impl_kx { - ($name:ident, $kx_name:ty, $secret:ty, $public_key:ty) => { - paste! { - - #[derive(Debug)] - #[allow(non_camel_case_types)] - pub struct $name; - - impl crypto::SupportedKxGroup for $name { - fn name(&self) -> rustls::NamedGroup { - $kx_name - } - - fn start(&self) -> Result, rustls::Error> { - let priv_key = $secret::random(&mut rand_core::OsRng); - let pub_key: $public_key = (&priv_key).into(); - Ok(Box::new([<$name KeyExchange>] { - priv_key, - pub_key: pub_key.to_sec1_bytes(), - })) - } - } - - #[allow(non_camel_case_types)] - pub struct [<$name KeyExchange>] { - priv_key: $secret, - pub_key: Box<[u8]>, - } - - impl crypto::ActiveKeyExchange for [<$name KeyExchange>] { - fn complete( - self: Box<[<$name KeyExchange>]>, - peer: &[u8], - ) -> Result { - let their_pub = $public_key::from_sec1_bytes(peer) - .map_err(|_| rustls::Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?; - Ok(self - .priv_key - .diffie_hellman(&their_pub) - .raw_secret_bytes() - .as_slice() - .into()) - } - - fn pub_key(&self) -> &[u8] { - &self.pub_key - } - - fn group(&self) -> rustls::NamedGroup { - $name.name() - } - } - } - }; -} - -impl_kx! {SecP256R1, rustls::NamedGroup::secp256r1, p256::ecdh::EphemeralSecret, p256::PublicKey} -impl_kx! {SecP384R1, rustls::NamedGroup::secp384r1, p384::ecdh::EphemeralSecret, p384::PublicKey} - -pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[&X25519, &SecP256R1, &SecP384R1]; +use rustls::crypto::SupportedKxGroup; + +pub const ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[ + #[cfg(feature = "kx-x448")] + &x448::X448, + #[cfg(feature = "kx-x25519")] + &x25519::X25519, + #[cfg(feature = "kx-p256")] + &nist::SecP256R1, + #[cfg(feature = "kx-p384")] + &nist::SecP384R1, + #[cfg(feature = "kx-p521")] + &nist::SecP521R1, +]; + +#[cfg(feature = "kx-nist")] +pub mod nist; + +#[cfg(feature = "kx-x25519")] +pub mod x25519; + +#[cfg(feature = "kx-x448")] +pub mod x448; diff --git a/src/kx/nist.rs b/src/kx/nist.rs new file mode 100644 index 0000000..645b2a4 --- /dev/null +++ b/src/kx/nist.rs @@ -0,0 +1,76 @@ +#[cfg(all(feature = "alloc", feature = "kx-nist"))] +use alloc::boxed::Box; + +#[cfg(feature = "kx-nist")] +use crypto::{SharedSecret, SupportedKxGroup}; + +#[cfg(feature = "kx-nist")] +use paste::paste; + +#[cfg(feature = "kx-nist")] +use rustls::crypto; + +#[cfg(feature = "kx-nist")] +macro_rules! impl_kx { + ($name:ident, $kx_name:ty, $secret:ty, $public_key:ty) => { + paste! { + #[derive(Debug)] + #[allow(non_camel_case_types)] + pub struct $name; + + impl crypto::SupportedKxGroup for $name { + fn name(&self) -> rustls::NamedGroup { + $kx_name + } + + fn start(&self) -> Result, rustls::Error> { + let priv_key = $secret::random(&mut rand_core::OsRng); + let pub_key: $public_key = (&priv_key).into(); + Ok(Box::new([<$name KeyExchange>] { + priv_key, + pub_key: pub_key.to_sec1_bytes(), + })) + } + } + + #[allow(non_camel_case_types)] + pub struct [<$name KeyExchange>] { + priv_key: $secret, + pub_key: Box<[u8]>, + } + + impl crypto::ActiveKeyExchange for [<$name KeyExchange>] { + fn complete( + self: Box<[<$name KeyExchange>]>, + peer: &[u8], + ) -> Result { + let their_pub = $public_key::from_sec1_bytes(peer) + .map_err(|_| rustls::Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?; + Ok(self + .priv_key + .diffie_hellman(&their_pub) + .raw_secret_bytes() + .as_slice() + .into()) + } + + fn pub_key(&self) -> &[u8] { + &self.pub_key + } + + fn group(&self) -> rustls::NamedGroup { + $name.name() + } + } + } + }; +} + +#[cfg(feature = "kx-p256")] +impl_kx! {SecP256R1, rustls::NamedGroup::secp256r1, ::p256::ecdh::EphemeralSecret, ::p256::PublicKey} + +#[cfg(feature = "kx-p384")] +impl_kx! {SecP384R1, rustls::NamedGroup::secp384r1, ::p384::ecdh::EphemeralSecret, ::p384::PublicKey} + +#[cfg(feature = "kx-p521")] +impl_kx! {SecP521R1, rustls::NamedGroup::secp521r1, ::p521::ecdh::EphemeralSecret, ::p521::PublicKey} diff --git a/src/kx/x25519.rs b/src/kx/x25519.rs new file mode 100644 index 0000000..0711678 --- /dev/null +++ b/src/kx/x25519.rs @@ -0,0 +1,47 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use crypto::{SharedSecret, SupportedKxGroup}; +use rustls::crypto::{self, ActiveKeyExchange}; +use x25519_dalek::{EphemeralSecret, PublicKey}; + +#[derive(Debug)] +pub struct X25519; + +impl crypto::SupportedKxGroup for X25519 { + fn name(&self) -> rustls::NamedGroup { + rustls::NamedGroup::X25519 + } + + fn start(&self) -> Result, rustls::Error> { + let priv_key = EphemeralSecret::random_from_rng(rand_core::OsRng); + let pub_key = PublicKey::from(&priv_key); + Ok(Box::new(X25519KeyExchange { priv_key, pub_key })) + } +} + +pub struct X25519KeyExchange { + priv_key: EphemeralSecret, + pub_key: PublicKey, +} + +impl ActiveKeyExchange for X25519KeyExchange { + fn complete(self: Box, peer: &[u8]) -> Result { + let peer_array: [u8; 32] = peer + .try_into() + .map_err(|_| rustls::Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?; + Ok(self + .priv_key + .diffie_hellman(&peer_array.into()) + .as_ref() + .into()) + } + + fn pub_key(&self) -> &[u8] { + self.pub_key.as_bytes() + } + + fn group(&self) -> rustls::NamedGroup { + X25519.name() + } +} diff --git a/src/kx/x448.rs b/src/kx/x448.rs new file mode 100644 index 0000000..b1dc882 --- /dev/null +++ b/src/kx/x448.rs @@ -0,0 +1,48 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use crrl::x448::{x448, x448_base}; +use crypto::{SharedSecret, SupportedKxGroup}; +use rand_core::RngCore; +use rustls::crypto::{self, ActiveKeyExchange}; + +#[derive(Debug)] +pub struct X448; + +impl crypto::SupportedKxGroup for X448 { + fn name(&self) -> rustls::NamedGroup { + rustls::NamedGroup::X448 + } + + fn start(&self) -> Result, rustls::Error> { + let priv_key = { + let mut bytes = [0u8; 56]; + rand_core::OsRng.fill_bytes(&mut bytes); + bytes + }; + let pub_key = x448_base(&priv_key); + Ok(Box::new(X448KeyExchange { priv_key, pub_key })) + } +} + +pub struct X448KeyExchange { + priv_key: [u8; 56], + pub_key: [u8; 56], +} + +impl ActiveKeyExchange for X448KeyExchange { + fn complete(self: Box, peer: &[u8]) -> Result { + let peer_public: [u8; 56] = peer + .try_into() + .map_err(|_| rustls::Error::from(rustls::PeerMisbehaved::InvalidKeyShare))?; + Ok(x448(&peer_public, &self.priv_key).as_ref().into()) + } + + fn pub_key(&self) -> &[u8] { + self.pub_key.as_slice() + } + + fn group(&self) -> rustls::NamedGroup { + X448.name() + } +} diff --git a/src/lib.rs b/src/lib.rs index 1b108b8..df901fc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,11 +27,6 @@ unused_lifetimes )] -//! # Usage -//! -//! See [`examples-xsmall`](https://github.com/RustCrypto/rustls-rustcrypto/tree/master/examples-xsmall) -//! for a usage example. - #[cfg(not(feature = "alloc"))] compile_error!("Rustls currently does not support alloc-less environments"); @@ -41,13 +36,10 @@ extern crate alloc; #[cfg(feature = "alloc")] use alloc::sync::Arc; -use rustls::crypto::{ - CipherSuiteCommon, CryptoProvider, GetRandomFailed, KeyProvider, SecureRandom, -}; -use rustls::{CipherSuite, SupportedCipherSuite, Tls13CipherSuite}; - -#[cfg(feature = "tls12")] -use rustls::SignatureScheme; +use pki_types::PrivateKeyDer; +use rustls::crypto::{CryptoProvider, GetRandomFailed, KeyProvider, SecureRandom}; +use rustls::sign::SigningKey; +use rustls::SupportedCipherSuite; #[derive(Debug)] pub struct Provider; @@ -63,209 +55,77 @@ pub fn provider() -> CryptoProvider { } impl SecureRandom for Provider { - fn fill(&self, bytes: &mut [u8]) -> Result<(), GetRandomFailed> { - use rand_core::RngCore; - rand_core::OsRng - .try_fill_bytes(bytes) - .map_err(|_| GetRandomFailed) + fn fill(&self, #[allow(unused_variables)] bytes: &mut [u8]) -> Result<(), GetRandomFailed> { + #[cfg(feature = "rand")] + { + use rand_core::RngCore; + rand_core::OsRng + .try_fill_bytes(bytes) + .map_err(|_| GetRandomFailed) + } + + #[cfg(not(feature = "rand"))] + { + Err(GetRandomFailed) + } } } impl KeyProvider for Provider { fn load_private_key( &self, - key_der: pki_types::PrivateKeyDer<'static>, - ) -> Result, rustls::Error> { - sign::any_supported_type(&key_der) + #[allow(unused_variables)] key_der: PrivateKeyDer<'static>, + ) -> Result, rustls::Error> { + #[cfg(feature = "sign")] + { + sign::any_supported_type(&key_der) + } + #[cfg(not(feature = "sign"))] + { + Err(rustls::Error::General("not key providers supported".into())) + } } } -#[cfg(feature = "tls12")] -const TLS12_ECDSA_SCHEMES: [SignatureScheme; 4] = [ - SignatureScheme::ECDSA_NISTP256_SHA256, - SignatureScheme::ECDSA_NISTP384_SHA384, - SignatureScheme::ECDSA_NISTP521_SHA512, - SignatureScheme::ED25519, -]; - -#[cfg(feature = "tls12")] -const TLS12_RSA_SCHEMES: [SignatureScheme; 6] = [ - SignatureScheme::RSA_PKCS1_SHA256, - SignatureScheme::RSA_PKCS1_SHA384, - SignatureScheme::RSA_PKCS1_SHA512, - SignatureScheme::RSA_PSS_SHA256, - SignatureScheme::RSA_PSS_SHA384, - SignatureScheme::RSA_PSS_SHA512, -]; - -#[cfg(feature = "tls12")] -pub const TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = - SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { - common: CipherSuiteCommon { - suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - hash_provider: hash::SHA256, - confidentiality_limit: u64::MAX, - }, - kx: rustls::crypto::KeyExchangeAlgorithm::ECDHE, - sign: &TLS12_ECDSA_SCHEMES, - aead_alg: &aead::gcm::Tls12Aes128Gcm, - prf_provider: &rustls::crypto::tls12::PrfUsingHmac(hmac::SHA256), - }); - -#[cfg(feature = "tls12")] -pub const TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = - SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { - common: CipherSuiteCommon { - suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - hash_provider: hash::SHA384, - confidentiality_limit: u64::MAX, - }, - kx: rustls::crypto::KeyExchangeAlgorithm::ECDHE, - sign: &TLS12_ECDSA_SCHEMES, - prf_provider: &rustls::crypto::tls12::PrfUsingHmac(hmac::SHA384), - aead_alg: &aead::gcm::Tls12Aes256Gcm, - }); - -#[cfg(feature = "tls12")] -pub const TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = - SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { - common: CipherSuiteCommon { - suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, - hash_provider: hash::SHA256, - confidentiality_limit: u64::MAX, - }, - prf_provider: &rustls::crypto::tls12::PrfUsingHmac(hmac::SHA256), - kx: rustls::crypto::KeyExchangeAlgorithm::ECDHE, - sign: &TLS12_ECDSA_SCHEMES, - aead_alg: &aead::chacha20::Chacha20Poly1305, - }); - -#[cfg(feature = "tls12")] -const TLS_ECDHE_ECDSA_SUITES: &[SupportedCipherSuite] = &[ - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, -]; - -#[cfg(feature = "tls12")] -pub const TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: SupportedCipherSuite = - SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { - common: CipherSuiteCommon { - suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - hash_provider: hash::SHA256, - confidentiality_limit: u64::MAX, - }, - kx: rustls::crypto::KeyExchangeAlgorithm::ECDHE, - sign: &TLS12_RSA_SCHEMES, - aead_alg: &aead::gcm::Tls12Aes128Gcm, - prf_provider: &rustls::crypto::tls12::PrfUsingHmac(hmac::SHA256), - }); - -#[cfg(feature = "tls12")] -pub const TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: SupportedCipherSuite = - SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { - common: CipherSuiteCommon { - suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - hash_provider: hash::SHA384, - confidentiality_limit: u64::MAX, - }, - kx: rustls::crypto::KeyExchangeAlgorithm::ECDHE, - sign: &TLS12_RSA_SCHEMES, - prf_provider: &rustls::crypto::tls12::PrfUsingHmac(hmac::SHA384), - aead_alg: &aead::gcm::Tls12Aes256Gcm, - }); - -#[cfg(feature = "tls12")] -pub const TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = - SupportedCipherSuite::Tls12(&rustls::Tls12CipherSuite { - common: CipherSuiteCommon { - suite: CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, - hash_provider: hash::SHA256, - confidentiality_limit: u64::MAX, - }, - kx: rustls::crypto::KeyExchangeAlgorithm::ECDHE, - sign: &TLS12_RSA_SCHEMES, - prf_provider: &rustls::crypto::tls12::PrfUsingHmac(hmac::SHA256), - aead_alg: &aead::chacha20::Chacha20Poly1305, - }); - -#[cfg(feature = "tls12")] -const TLS_ECDHE_RSA_SUITES: &[SupportedCipherSuite] = &[ - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, -]; - -#[cfg(feature = "tls12")] -const TLS12_SUITES: &[SupportedCipherSuite] = misc::const_concat_slices!( +pub const ALL_CIPHER_SUITES: &[SupportedCipherSuite] = misc::const_concat_slices!( SupportedCipherSuite, - TLS_ECDHE_ECDSA_SUITES, - TLS_ECDHE_RSA_SUITES + { + #[cfg(feature = "tls12")] + { + tls12::suites::TLS12_SUITES + } + + #[cfg(not(feature = "tls12"))] + { + &[] + } + }, + tls13::suites::TLS13_SUITES ); -#[cfg(not(feature = "tls12"))] -const TLS12_SUITES: &[SupportedCipherSuite] = &[]; - -pub const TLS13_AES_128_GCM_SHA256: SupportedCipherSuite = - SupportedCipherSuite::Tls13(&Tls13CipherSuite { - common: CipherSuiteCommon { - suite: CipherSuite::TLS13_AES_128_GCM_SHA256, - hash_provider: hash::SHA256, - confidentiality_limit: u64::MAX, - }, - hkdf_provider: &rustls::crypto::tls13::HkdfUsingHmac(hmac::SHA256), - aead_alg: &aead::gcm::Tls13Aes128Gcm, - quic: None, - }); - -pub const TLS13_AES_256_GCM_SHA384: SupportedCipherSuite = - SupportedCipherSuite::Tls13(&Tls13CipherSuite { - common: CipherSuiteCommon { - suite: CipherSuite::TLS13_AES_256_GCM_SHA384, - hash_provider: hash::SHA384, - confidentiality_limit: u64::MAX, - }, - hkdf_provider: &rustls::crypto::tls13::HkdfUsingHmac(hmac::SHA384), - aead_alg: &aead::gcm::Tls13Aes256Gcm, - quic: None, - }); - -const TLS13_AES_SUITES: &[SupportedCipherSuite] = - &[TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384]; - -pub const TLS13_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = - SupportedCipherSuite::Tls13(&Tls13CipherSuite { - common: CipherSuiteCommon { - suite: CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, - hash_provider: hash::SHA256, - confidentiality_limit: u64::MAX, - }, - hkdf_provider: &rustls::crypto::tls13::HkdfUsingHmac(hmac::SHA256), - aead_alg: &aead::chacha20::Chacha20Poly1305, - quic: None, - }); - -const TLS13_SUITES: &[SupportedCipherSuite] = misc::const_concat_slices!( - SupportedCipherSuite, - TLS13_AES_SUITES, - &[TLS13_CHACHA20_POLY1305_SHA256] +#[cfg(feature = "aead")] +pub mod aead; +#[cfg(feature = "hash")] +pub mod hash; +#[cfg(feature = "hash")] +pub mod hmac; +#[cfg(feature = "kx")] +pub mod kx; +pub mod misc; +#[cfg(feature = "sign")] +pub mod sign; +#[cfg(feature = "tls12")] +pub mod tls12; +pub mod tls13; +#[cfg(feature = "verify")] +pub mod verify; + +const _: () = assert!( + !ALL_CIPHER_SUITES.is_empty(), + "At least one cipher suite should be enabled" ); -static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = misc::const_concat_slices!( - SupportedCipherSuite, - if cfg!(feature = "tls12") { - TLS12_SUITES - } else { - &[] - }, - TLS13_SUITES, +const _: () = assert!( + !kx::ALL_KX_GROUPS.is_empty(), + "At least one key exchange algorithm should be enabled" ); - -mod aead; -mod hash; -mod hmac; -mod kx; -mod misc; -pub mod quic; -pub mod sign; -mod verify; diff --git a/src/quic.rs b/src/quic.rs deleted file mode 100644 index f849835..0000000 --- a/src/quic.rs +++ /dev/null @@ -1,142 +0,0 @@ -#![allow(clippy::duplicate_mod)] - -#[cfg(feature = "alloc")] -use alloc::boxed::Box; - -use aead::AeadCore; -use chacha20poly1305::{AeadInPlace, KeyInit, KeySizeUser}; -use crypto_common::typenum::Unsigned; -use rustls::crypto::cipher::{self, AeadKey, Iv}; -use rustls::{quic, Error, Tls13CipherSuite}; - -#[allow(dead_code)] // TODO -pub struct HeaderProtectionKey(AeadKey); - -impl HeaderProtectionKey { - pub fn new(key: AeadKey) -> Self { - Self(key) - } -} - -impl quic::HeaderProtectionKey for HeaderProtectionKey { - fn encrypt_in_place( - &self, - _sample: &[u8], - _first: &mut u8, - _packet_number: &mut [u8], - ) -> Result<(), Error> { - todo!() - } - - fn decrypt_in_place( - &self, - _sample: &[u8], - _first: &mut u8, - _packet_number: &mut [u8], - ) -> Result<(), Error> { - todo!() - } - - #[inline] - fn sample_len(&self) -> usize { - todo!() - } -} - -pub struct PacketKey { - /// Computes unique nonces for each packet - iv: Iv, - - /// The cipher suite used for this packet key - #[allow(dead_code)] - suite: &'static Tls13CipherSuite, - - crypto: chacha20poly1305::ChaCha20Poly1305, -} - -impl PacketKey { - pub fn new(suite: &'static Tls13CipherSuite, key: AeadKey, iv: Iv) -> Self { - Self { - iv, - suite, - crypto: chacha20poly1305::ChaCha20Poly1305::new_from_slice(key.as_ref()) - .expect("key should be valid"), - } - } -} - -impl quic::PacketKey for PacketKey { - fn encrypt_in_place( - &self, - packet_number: u64, - aad: &[u8], - payload: &mut [u8], - ) -> Result { - let nonce = cipher::Nonce::new(&self.iv, packet_number).0; - - let tag = self - .crypto - .encrypt_in_place_detached(&nonce.into(), aad, payload) - .map_err(|_| rustls::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, - aad: &[u8], - payload: &'a mut [u8], - ) -> Result<&'a [u8], Error> { - let mut payload_ = payload.to_vec(); - let payload_len = payload_.len(); - let nonce = chacha20poly1305::Nonce::from(cipher::Nonce::new(&self.iv, packet_number).0); - - self.crypto - .decrypt_in_place(&nonce, aad, &mut payload_) - .map_err(|_| rustls::Error::DecryptError)?; - - // Unfortunately the lifetime bound on decrypt_in_place sucks - payload.copy_from_slice(&payload_); - - let plain_len = payload_len - self.tag_len(); - Ok(&payload[..plain_len]) - } - - /// Tag length for the underlying AEAD algorithm - #[inline] - fn tag_len(&self) -> usize { - ::TagSize::to_usize() - } - - fn integrity_limit(&self) -> u64 { - 1 << 36 - } - - fn confidentiality_limit(&self) -> u64 { - u64::MAX - } -} - -#[allow(dead_code)] // TODO -pub struct KeyBuilder(AeadKey); - -impl rustls::quic::Algorithm for KeyBuilder { - fn packet_key(&self, _key: AeadKey, _iv: Iv) -> Box { - todo!() - } - - fn header_protection_key(&self, key: AeadKey) -> Box { - Box::new(HeaderProtectionKey::new(key)) - } - - fn aead_key_len(&self) -> usize { - chacha20poly1305::ChaCha20Poly1305::key_size() - } -} diff --git a/src/sign.rs b/src/sign.rs index e6109f5..5cc4baa 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -2,42 +2,10 @@ use alloc::{sync::Arc, vec::Vec}; use core::marker::PhantomData; -use self::ecdsa::{EcdsaSigningKeyP256, EcdsaSigningKeyP384}; -use self::eddsa::Ed25519SigningKey; -use self::rsa::RsaSigningKey; - use pki_types::PrivateKeyDer; use rustls::sign::{Signer, SigningKey}; use rustls::{Error, SignatureScheme}; -use signature::{RandomizedSigner, SignatureEncoding}; - -#[derive(Debug)] -pub struct GenericRandomizedSigner -where - S: SignatureEncoding, - T: RandomizedSigner, -{ - _marker: PhantomData, - key: Arc, - scheme: SignatureScheme, -} - -impl Signer for GenericRandomizedSigner -where - S: SignatureEncoding + Send + Sync + core::fmt::Debug, - T: RandomizedSigner + Send + Sync + core::fmt::Debug, -{ - fn sign(&self, message: &[u8]) -> Result, Error> { - self.key - .try_sign_with_rng(&mut rand_core::OsRng, message) - .map_err(|_| rustls::Error::General("signing failed".into())) - .map(|sig: S| sig.to_vec()) - } - - fn scheme(&self) -> SignatureScheme { - self.scheme - } -} +use signature::SignatureEncoding; #[derive(Debug)] pub struct GenericSigner @@ -72,11 +40,24 @@ where /// # Errors /// /// Returns an error if the key couldn't be decoded. +#[allow(unused_variables)] pub fn any_supported_type(der: &PrivateKeyDer<'_>) -> Result, rustls::Error> { - RsaSigningKey::try_from(der) - .map(|x| Arc::new(x) as _) - .or_else(|_| any_ecdsa_type(der)) - .or_else(|_| any_eddsa_type(der)) + #[cfg(feature = "sign-rsa")] + if let Ok(key) = rsa::RsaSigningKey::try_from(der) { + return Ok(Arc::new(key) as _); + } + + #[cfg(feature = "sign-ecdsa-nist")] + if let Ok(key) = any_ecdsa_type(der) { + return Ok(key); + } + + #[cfg(feature = "sign-eddsa")] + if let Ok(key) = any_eddsa_type(der) { + return Ok(key); + } + + Err(rustls::Error::General("not supported".into())) } /// Extract any supported ECDSA key from the given DER input. @@ -84,10 +65,19 @@ pub fn any_supported_type(der: &PrivateKeyDer<'_>) -> Result /// # Errors /// /// Returns an error if the key couldn't be decoded. +#[allow(unused_variables)] +#[cfg(feature = "sign-ecdsa-nist")] pub fn any_ecdsa_type(der: &PrivateKeyDer<'_>) -> Result, rustls::Error> { - let p256 = |_| EcdsaSigningKeyP256::try_from(der).map(|x| Arc::new(x) as _); - let p384 = |_| EcdsaSigningKeyP384::try_from(der).map(|x| Arc::new(x) as _); - p256(()).or_else(p384) + #[cfg(all(feature = "der", feature = "ecdsa-p256"))] + if let Ok(key) = ecdsa::nist::EcdsaSigningKeyP256::try_from(der) { + return Ok(Arc::new(key) as _); + } + #[cfg(all(feature = "der", feature = "ecdsa-p384"))] + if let Ok(key) = ecdsa::nist::EcdsaSigningKeyP384::try_from(der) { + return Ok(Arc::new(key) as _); + } + + Err(rustls::Error::General("not supported".into())) } /// Extract any supported EDDSA key from the given DER input. @@ -95,11 +85,24 @@ pub fn any_ecdsa_type(der: &PrivateKeyDer<'_>) -> Result, ru /// # Errors /// /// Returns an error if the key couldn't be decoded. +#[allow(unused_variables)] +#[cfg(feature = "sign-eddsa")] pub fn any_eddsa_type(der: &PrivateKeyDer<'_>) -> Result, rustls::Error> { // TODO: Add support for Ed448 - Ed25519SigningKey::try_from(der).map(|x| Arc::new(x) as _) + #[cfg(all(feature = "der", feature = "eddsa-ed25519"))] + if let Ok(key) = eddsa::ed25519::Ed25519SigningKey::try_from(der) { + return Ok(Arc::new(key) as _); + } + + Err(rustls::Error::General("not supported".into())) } +#[cfg(feature = "ecdsa")] pub mod ecdsa; +#[cfg(feature = "eddsa")] pub mod eddsa; +#[cfg(feature = "rsa")] pub mod rsa; + +#[cfg(feature = "rand")] +pub mod rand; diff --git a/src/sign/ecdsa.rs b/src/sign/ecdsa.rs index 3f4a2bc..1931d6e 100644 --- a/src/sign/ecdsa.rs +++ b/src/sign/ecdsa.rs @@ -1,66 +1,2 @@ -#[cfg(feature = "alloc")] -use alloc::{boxed::Box, format, sync::Arc}; -use core::marker::PhantomData; - -use paste::paste; -use pkcs8::DecodePrivateKey; -use pki_types::PrivateKeyDer; -use rustls::sign::SigningKey; -use rustls::{SignatureAlgorithm, SignatureScheme}; -use sec1::DecodeEcPrivateKey; - -macro_rules! impl_ecdsa { - ($name: ident, $scheme: expr, $signing_key: ty, $signature: ty) => { - paste! { - #[derive(Debug)] - pub struct [] { - key: Arc<$signing_key>, - scheme: SignatureScheme, - } - - impl TryFrom<&PrivateKeyDer<'_>> for [] { - type Error = rustls::Error; - - fn try_from(value: &PrivateKeyDer<'_>) -> Result { - let pkey = match value { - PrivateKeyDer::Pkcs8(der) => { - $signing_key::from_pkcs8_der(der.secret_pkcs8_der()).map_err(|e| format!("failed to decrypt private key: {e}")) - }, - PrivateKeyDer::Sec1(sec1) => { - $signing_key::from_sec1_der(sec1.secret_sec1_der()).map_err(|e| format!("failed to decrypt private key: {e}")) - }, - PrivateKeyDer::Pkcs1(_) => Err(format!("ECDSA does not support PKCS#1 key")), - _ => Err("not supported".into()), - }; - pkey.map(|kp| { - Self { - key: Arc::new(kp), - scheme: $scheme, - } - }).map_err(rustls::Error::General) - } - } - - impl SigningKey for [] { - fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { - if offered.contains(&self.scheme) { - Some(Box::new(super::GenericRandomizedSigner::<$signature, _> { - _marker: PhantomData, - key: self.key.clone(), - scheme: self.scheme, - })) - } else { - None - } - } - - fn algorithm(&self) -> SignatureAlgorithm { - SignatureAlgorithm::ECDSA - } - } - } - }; -} - -impl_ecdsa! {P256, SignatureScheme::ECDSA_NISTP256_SHA256, p256::ecdsa::SigningKey, p256::ecdsa::DerSignature} -impl_ecdsa! {P384, SignatureScheme::ECDSA_NISTP384_SHA384, p384::ecdsa::SigningKey, p384::ecdsa::DerSignature} +#[cfg(feature = "sign-ecdsa-nist")] +pub mod nist; diff --git a/src/sign/ecdsa/nist.rs b/src/sign/ecdsa/nist.rs new file mode 100644 index 0000000..8b95d53 --- /dev/null +++ b/src/sign/ecdsa/nist.rs @@ -0,0 +1,78 @@ +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, format, sync::Arc}; + +use crate::sign::rand::GenericRandomizedSigner; +use core::marker::PhantomData; +use paste::paste; +use rustls::sign::SigningKey; +use rustls::{SignatureAlgorithm, SignatureScheme}; + +#[cfg(feature = "der")] +use pki_types::PrivateKeyDer; + +macro_rules! impl_ecdsa { +($name: ident, $scheme: expr, $signing_key: ty, $signature: ty) => { + paste! { + #[derive(Debug)] + pub struct [] { + key: Arc<$signing_key>, + scheme: SignatureScheme, + } + + #[cfg(feature = "der")] + impl TryFrom<&PrivateKeyDer<'_>> for [] { + type Error = rustls::Error; + + fn try_from(value: &PrivateKeyDer<'_>) -> Result { + let pkey = match value { + #[cfg(feature = "pkcs8")] + PrivateKeyDer::Pkcs8(der) => { + use pkcs8::DecodePrivateKey; + $signing_key::from_pkcs8_der(der.secret_pkcs8_der()).map_err(|e| format!("failed to decrypt private key: {e}")) + }, + #[cfg(feature = "sec1")] + PrivateKeyDer::Sec1(sec1) => { + use sec1::DecodeEcPrivateKey; + $signing_key::from_sec1_der(sec1.secret_sec1_der()).map_err(|e| format!("failed to decrypt private key: {e}")) + }, + PrivateKeyDer::Pkcs1(_) => Err(format!("ECDSA does not support PKCS#1 key")), + _ => Err("not supported".into()), + }; + pkey.map(|kp| { + Self { + key: Arc::new(kp), + scheme: $scheme, + } + }).map_err(rustls::Error::General) + } + } + + impl SigningKey for [] { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { + if offered.contains(&self.scheme) { + Some(Box::new(GenericRandomizedSigner::<$signature, _> { + _marker: PhantomData, + key: self.key.clone(), + scheme: self.scheme, + })) + } else { + None + } + } + + fn algorithm(&self) -> SignatureAlgorithm { + SignatureAlgorithm::ECDSA + } + } + } +}; +} + +#[cfg(all(feature = "ecdsa-p256", feature = "hash-sha256"))] +impl_ecdsa! {P256, SignatureScheme::ECDSA_NISTP256_SHA256, ::p256::ecdsa::SigningKey, ::p256::ecdsa::DerSignature} + +#[cfg(all(feature = "ecdsa-p384", feature = "hash-sha384"))] +impl_ecdsa! {P384, SignatureScheme::ECDSA_NISTP384_SHA384, ::p384::ecdsa::SigningKey, ::p384::ecdsa::DerSignature} + +// #[cfg(all(feature = "ecdsa-p521", feature = "hash-sha512"))] +// impl_ecdsa! {P521, SignatureScheme::ECDSA_NISTP521_SHA512, ::p521::ecdsa::SigningKey, ::p521::ecdsa::DerSignature} diff --git a/src/sign/eddsa.rs b/src/sign/eddsa.rs index e6f0d1a..5141d74 100644 --- a/src/sign/eddsa.rs +++ b/src/sign/eddsa.rs @@ -1,57 +1,2 @@ -#[cfg(feature = "alloc")] -use alloc::{boxed::Box, format, string::ToString, sync::Arc}; -use core::marker::PhantomData; - -use pkcs8::DecodePrivateKey; -use pki_types::PrivateKeyDer; -use rustls::sign::{Signer, SigningKey}; -use rustls::{SignatureAlgorithm, SignatureScheme}; -use sec1::DecodeEcPrivateKey; - -#[derive(Debug)] -pub struct Ed25519SigningKey { - key: Arc, - scheme: SignatureScheme, -} - -impl TryFrom<&PrivateKeyDer<'_>> for Ed25519SigningKey { - type Error = rustls::Error; - - fn try_from(value: &PrivateKeyDer<'_>) -> Result { - let pkey = match value { - PrivateKeyDer::Pkcs8(der) => { - ed25519_dalek::SigningKey::from_pkcs8_der(der.secret_pkcs8_der()) - .map_err(|e| format!("failed to decrypt private key: {e}")) - } - PrivateKeyDer::Sec1(sec1) => { - ed25519_dalek::SigningKey::from_sec1_der(sec1.secret_sec1_der()) - .map_err(|e| format!("failed to decrypt private key: {e}")) - } - PrivateKeyDer::Pkcs1(_) => Err("ED25519 does not support PKCS#1 key".to_string()), - _ => Err("not supported".into()), - }; - pkey.map(|kp| Self { - key: Arc::new(kp), - scheme: SignatureScheme::ED25519, - }) - .map_err(rustls::Error::General) - } -} - -impl SigningKey for Ed25519SigningKey { - fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { - if offered.contains(&self.scheme) { - Some(Box::new(super::GenericSigner { - _marker: PhantomData, - key: self.key.clone(), - scheme: self.scheme, - })) - } else { - None - } - } - - fn algorithm(&self) -> SignatureAlgorithm { - SignatureAlgorithm::ED25519 - } -} +#[cfg(feature = "ed25519")] +pub mod ed25519; diff --git a/src/sign/eddsa/ed25519.rs b/src/sign/eddsa/ed25519.rs new file mode 100644 index 0000000..c090888 --- /dev/null +++ b/src/sign/eddsa/ed25519.rs @@ -0,0 +1,60 @@ +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, sync::Arc}; +#[cfg(all(feature = "alloc", feature = "der"))] +use alloc::{format, string::ToString}; + +use crate::sign::GenericSigner; +use core::marker::PhantomData; +use ed25519_dalek::SigningKey; +use rustls::{sign::Signer, SignatureAlgorithm, SignatureScheme}; + +#[cfg(feature = "der")] +use pki_types::PrivateKeyDer; + +#[derive(Debug)] +pub struct Ed25519SigningKey(Arc); + +#[cfg(feature = "der")] +impl TryFrom<&PrivateKeyDer<'_>> for Ed25519SigningKey { + type Error = rustls::Error; + + fn try_from(value: &PrivateKeyDer<'_>) -> Result { + let pkey = match value { + #[cfg(feature = "pkcs8")] + PrivateKeyDer::Pkcs8(der) => { + use pkcs8::DecodePrivateKey; + SigningKey::from_pkcs8_der(der.secret_pkcs8_der()) + .map_err(|e| format!("failed to decrypt private key: {e}")) + } + #[cfg(feature = "sec1")] + PrivateKeyDer::Sec1(sec1) => { + use sec1::DecodeEcPrivateKey; + SigningKey::from_sec1_der(sec1.secret_sec1_der()) + .map_err(|e| format!("failed to decrypt private key: {e}")) + } + PrivateKeyDer::Pkcs1(_) => Err("ED25519 does not support PKCS#1 key".to_string()), + _ => Err("not supported".into()), + }; + pkey.map(|kp| Self(Arc::new(kp))) + .map_err(rustls::Error::General) + } +} + +impl rustls::sign::SigningKey for Ed25519SigningKey { + fn choose_scheme(&self, offered: &[SignatureScheme]) -> Option> { + const SCHEME: SignatureScheme = SignatureScheme::ED25519; + if offered.contains(&SCHEME) { + Some(Box::new(GenericSigner { + _marker: PhantomData, + key: self.0.clone(), + scheme: SCHEME, + })) + } else { + None + } + } + + fn algorithm(&self) -> SignatureAlgorithm { + SignatureAlgorithm::ED25519 + } +} diff --git a/src/sign/rand.rs b/src/sign/rand.rs new file mode 100644 index 0000000..39ab04e --- /dev/null +++ b/src/sign/rand.rs @@ -0,0 +1,35 @@ +#[cfg(feature = "alloc")] +use alloc::{sync::Arc, vec::Vec}; +use core::marker::PhantomData; + +use rustls::sign::Signer; +use rustls::{Error, SignatureScheme}; +use signature::{RandomizedSigner, SignatureEncoding}; + +#[derive(Debug)] +pub struct GenericRandomizedSigner +where + S: SignatureEncoding, + T: RandomizedSigner, +{ + pub(crate) _marker: PhantomData, + pub(crate) key: Arc, + pub(crate) scheme: SignatureScheme, +} + +impl Signer for GenericRandomizedSigner +where + S: SignatureEncoding + Send + Sync + core::fmt::Debug, + T: RandomizedSigner + Send + Sync + core::fmt::Debug, +{ + fn sign(&self, message: &[u8]) -> Result, Error> { + self.key + .try_sign_with_rng(&mut rand_core::OsRng, message) + .map_err(|_| rustls::Error::General("signing failed".into())) + .map(|sig: S| sig.to_vec()) + } + + fn scheme(&self) -> SignatureScheme { + self.scheme + } +} diff --git a/src/sign/rsa.rs b/src/sign/rsa.rs index ab27d36..1822089 100644 --- a/src/sign/rsa.rs +++ b/src/sign/rsa.rs @@ -1,35 +1,58 @@ #[cfg(feature = "alloc")] -use alloc::{boxed::Box, format, string::ToString, sync::Arc}; +use alloc::{boxed::Box, sync::Arc}; +#[cfg(all(feature = "alloc", feature = "der"))] +use alloc::{format, string::ToString}; -use pkcs8::DecodePrivateKey; -use pki_types::PrivateKeyDer; -use rsa::pkcs1::DecodeRsaPrivateKey; use rsa::RsaPrivateKey; use rustls::sign::{Signer, SigningKey}; use rustls::{SignatureAlgorithm, SignatureScheme}; -use sha2::{Sha256, Sha384, Sha512}; + +#[cfg(feature = "hash-sha256")] +use sha2::Sha256; +#[cfg(feature = "hash-sha384")] +use sha2::Sha384; +#[cfg(feature = "hash-sha512")] +use sha2::Sha512; + +#[cfg(feature = "der")] +use pki_types::PrivateKeyDer; const ALL_RSA_SCHEMES: &[SignatureScheme] = &[ + #[cfg(all(feature = "rsa-pss", feature = "hash-sha512"))] SignatureScheme::RSA_PSS_SHA512, + #[cfg(all(feature = "rsa-pss", feature = "hash-sha384"))] SignatureScheme::RSA_PSS_SHA384, + #[cfg(all(feature = "rsa-pss", feature = "hash-sha256"))] SignatureScheme::RSA_PSS_SHA256, + #[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha512"))] SignatureScheme::RSA_PKCS1_SHA512, + #[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha384"))] SignatureScheme::RSA_PKCS1_SHA384, + #[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha256"))] SignatureScheme::RSA_PKCS1_SHA256, ]; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct RsaSigningKey(RsaPrivateKey); +#[cfg(feature = "der")] impl TryFrom<&PrivateKeyDer<'_>> for RsaSigningKey { type Error = rustls::Error; fn try_from(value: &PrivateKeyDer<'_>) -> Result { let pkey = match value { - PrivateKeyDer::Pkcs8(der) => RsaPrivateKey::from_pkcs8_der(der.secret_pkcs8_der()) - .map_err(|e| format!("failed to decrypt private key: {e}")), - PrivateKeyDer::Pkcs1(der) => RsaPrivateKey::from_pkcs1_der(der.secret_pkcs1_der()) - .map_err(|e| format!("failed to decrypt private key: {e}")), + #[cfg(feature = "pkcs8")] + PrivateKeyDer::Pkcs8(der) => { + use pkcs8::DecodePrivateKey; + RsaPrivateKey::from_pkcs8_der(der.secret_pkcs8_der()) + .map_err(|e| format!("failed to decrypt private key: {e}")) + } + #[cfg(all(feature = "pkcs8", feature = "pkcs1"))] + PrivateKeyDer::Pkcs1(der) => { + use pkcs1::DecodeRsaPrivateKey; + RsaPrivateKey::from_pkcs1_der(der.secret_pkcs1_der()) + .map_err(|e| format!("failed to decrypt private key: {e}")) + } PrivateKeyDer::Sec1(_) => Err("RSA does not support SEC-1 key".to_string()), _ => Err("not supported".into()), }; @@ -46,7 +69,7 @@ impl SigningKey for RsaSigningKey { .and_then(|&scheme| { macro_rules! signer { ($key:ty) => {{ - Some(Box::new(super::GenericRandomizedSigner { + Some(Box::new(super::rand::GenericRandomizedSigner { _marker: Default::default(), key: Arc::new(<$key>::new(self.0.clone())), scheme, @@ -55,17 +78,23 @@ impl SigningKey for RsaSigningKey { } match scheme { - SignatureScheme::RSA_PSS_SHA512 => signer! {rsa::pss::SigningKey::}, - SignatureScheme::RSA_PSS_SHA384 => signer! {rsa::pss::SigningKey::}, - SignatureScheme::RSA_PSS_SHA256 => signer! {rsa::pss::SigningKey::}, + #[cfg(all(feature = "rsa-pss", feature = "hash-sha512"))] + SignatureScheme::RSA_PSS_SHA512 => signer! {::rsa::pss::SigningKey::}, + #[cfg(all(feature = "rsa-pss", feature = "hash-sha384"))] + SignatureScheme::RSA_PSS_SHA384 => signer! {::rsa::pss::SigningKey::}, + #[cfg(all(feature = "rsa-pss", feature = "hash-sha256"))] + SignatureScheme::RSA_PSS_SHA256 => signer! {::rsa::pss::SigningKey::}, + #[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha512"))] SignatureScheme::RSA_PKCS1_SHA512 => { - signer! {rsa::pkcs1v15::SigningKey::} + signer! {::rsa::pkcs1v15::SigningKey::} } + #[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha384"))] SignatureScheme::RSA_PKCS1_SHA384 => { - signer! {rsa::pkcs1v15::SigningKey::} + signer! {::rsa::pkcs1v15::SigningKey::} } + #[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha256"))] SignatureScheme::RSA_PKCS1_SHA256 => { - signer! {rsa::pkcs1v15::SigningKey::} + signer! {::rsa::pkcs1v15::SigningKey::} } _ => None, } diff --git a/src/tls12.rs b/src/tls12.rs new file mode 100644 index 0000000..3864c43 --- /dev/null +++ b/src/tls12.rs @@ -0,0 +1,3 @@ +#[cfg(feature = "aead")] +pub mod aead; +pub mod suites; diff --git a/src/tls12/aead.rs b/src/tls12/aead.rs new file mode 100644 index 0000000..f34c173 --- /dev/null +++ b/src/tls12/aead.rs @@ -0,0 +1,8 @@ +#[cfg(feature = "chacha20poly1305")] +pub mod chacha20; + +#[cfg(feature = "gcm")] +pub mod gcm; + +#[cfg(feature = "ccm")] +pub mod ccm; diff --git a/src/tls12/aead/ccm.rs b/src/tls12/aead/ccm.rs new file mode 100644 index 0000000..23f9c8b --- /dev/null +++ b/src/tls12/aead/ccm.rs @@ -0,0 +1,132 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use crate::aead::ccm::{Aes128Ccm, Aes128Ccm8, Aes256Ccm, Aes256Ccm8}; +use ::aead::{AeadCore, AeadInPlace}; +use crypto_common::{KeyInit, KeySizeUser}; +use paste::paste; +use rustls::crypto::cipher::{ + self, make_tls12_aad, AeadKey, InboundOpaqueMessage, InboundPlainMessage, KeyBlockShape, + MessageDecrypter, MessageEncrypter, OutboundOpaqueMessage, OutboundPlainMessage, + PrefixedPayload, Tls12AeadAlgorithm, +}; +use rustls::ConnectionTrafficSecrets; +use typenum::Unsigned; + +const EXPLICIT_NONCE_LEN: usize = 8; + +macro_rules! impl_ccm { +($name: ident, $aead: ty, $nonce_pos: expr, $overhead: expr) => { + paste! { + impl Tls12AeadAlgorithm for $name { + fn encrypter(&self, key: AeadKey, write_iv: &[u8], explicit: &[u8]) -> Box { + Box::new([]( + $aead::new_from_slice(key.as_ref()).unwrap(), + { + let mut iv: [u8; 12] = [0; 12]; + iv[..4].copy_from_slice(write_iv); + iv[4..].copy_from_slice(explicit); + iv + }, + )) + } + + fn decrypter(&self, dec_key: AeadKey, dec_iv: &[u8]) -> Box { + Box::new([]( + $aead::new_from_slice(dec_key.as_ref()).unwrap(), + dec_iv.try_into().unwrap(), + )) + } + + fn key_block_shape(&self) -> KeyBlockShape { + KeyBlockShape { + enc_key_len: $aead::key_size(), + fixed_iv_len: 4, + explicit_nonce_len: EXPLICIT_NONCE_LEN, + } + } + + fn extract_keys( + &self, + _: AeadKey, + _: &[u8], + _explicit: &[u8], + ) -> Result { + Err(cipher::UnsupportedOperationError) + } + } + + pub struct []($aead, [u8; 12]); + + impl MessageEncrypter for [] { + fn encrypt(&mut self, m: OutboundPlainMessage<'_>, seq: u64) -> Result { + let total_len = self.encrypted_payload_len(m.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = cipher::Nonce::new(&self.1.into(), seq).0; + let aad = make_tls12_aad(seq, m.typ, m.version, m.payload.len()); + payload.extend_from_slice(&nonce.as_ref()[4..]); // explicit + payload.extend_from_chunks(&m.payload); + + self.0 + .encrypt_in_place_detached(&nonce.into(), &aad, &mut payload.as_mut()[$nonce_pos..]) + .map(|tag| payload.extend(tag.as_ref() as &[u8])) + .map_err(|_| rustls::Error::EncryptError) + .map(|_| OutboundOpaqueMessage::new(m.typ, m.version, payload)) + } + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + $nonce_pos + <$aead as AeadCore>::TagSize::USIZE + } + } + + pub struct []($aead, [u8; 4]); + + impl MessageDecrypter for [] { + fn decrypt<'a>(&mut self, mut m: InboundOpaqueMessage<'a>, seq: u64) -> Result, rustls::Error> { + type TagSize = <$aead as AeadCore>::TagSize; + + let payload = &m.payload; + + if payload.len() < $overhead { + return Err(rustls::Error::DecryptError); + } + + let nonce: aead::Nonce<$aead> = { + let mut nonce = [0u8; 12]; + nonce[..4].copy_from_slice(&self.1); // dec_iv + nonce[4..].copy_from_slice(&payload[..$nonce_pos]); + nonce.into() + }; + + let aad = make_tls12_aad(seq, m.typ, m.version, payload.len() - $overhead); + + let payload = &mut m.payload; + let tag_pos = { + let payload = &mut payload[$nonce_pos..]; + let tag_pos = payload.len() - TagSize::to_usize(); + let (msg, tag) = payload.split_at_mut(tag_pos); + + let tag = ccm::Tag::::from_slice(tag); + self.0 + .decrypt_in_place_detached(&nonce, &aad, msg, tag) + .map_err(|_| rustls::Error::DecryptError)?; + tag_pos + }; + + // We defer the truncation to here, because we may inadvertently shifted the + // original data if the decryption failed. Another way to avoid this is + // to clone the payload slice starting after the explicit nonce, + // but this will cause an additional cloning and copying + payload.rotate_left($nonce_pos); + payload.truncate(tag_pos); + Ok(m.into_plain_message()) + } + } + } +}; +} + +impl_ccm! {Aes128Ccm, crate::aead::aes::Aes128Ccm, EXPLICIT_NONCE_LEN, EXPLICIT_NONCE_LEN + 16} +impl_ccm! {Aes256Ccm, crate::aead::aes::Aes256Ccm, EXPLICIT_NONCE_LEN, EXPLICIT_NONCE_LEN + 16} +impl_ccm! {Aes128Ccm8, crate::aead::aes::Aes128Ccm8, EXPLICIT_NONCE_LEN, EXPLICIT_NONCE_LEN + 8} +impl_ccm! {Aes256Ccm8, crate::aead::aes::Aes256Ccm8, EXPLICIT_NONCE_LEN, EXPLICIT_NONCE_LEN + 8} diff --git a/src/tls12/aead/chacha20.rs b/src/tls12/aead/chacha20.rs new file mode 100644 index 0000000..be74677 --- /dev/null +++ b/src/tls12/aead/chacha20.rs @@ -0,0 +1,106 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use crate::aead::{ + ChaCha20Poly1305, DecryptBufferAdapter, EncryptBufferAdapter, CHACHAPOLY1305_OVERHEAD, +}; +use chacha20poly1305::{AeadInPlace, KeyInit}; +use rustls::crypto::cipher::{ + self, make_tls12_aad, AeadKey, InboundOpaqueMessage, InboundPlainMessage, Iv, MessageDecrypter, + MessageEncrypter, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, + UnsupportedOperationError, +}; +use rustls::crypto::cipher::{KeyBlockShape, Tls12AeadAlgorithm, NONCE_LEN}; +use rustls::ConnectionTrafficSecrets; + +pub struct CipherAdapter(chacha20poly1305::ChaCha20Poly1305, Iv); + +impl Tls12AeadAlgorithm for ChaCha20Poly1305 { + fn encrypter(&self, key: AeadKey, iv: &[u8], _: &[u8]) -> Box { + Box::new(CipherAdapter( + chacha20poly1305::ChaCha20Poly1305::new_from_slice(key.as_ref()) + .expect("key should be valid"), + Iv::copy(iv), + )) + } + + fn decrypter(&self, key: AeadKey, iv: &[u8]) -> Box { + Box::new(CipherAdapter( + chacha20poly1305::ChaCha20Poly1305::new_from_slice(key.as_ref()) + .expect("key should be valid"), + Iv::copy(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 { + // This should always be true because KeyBlockShape and the Iv nonce len are in + // agreement. + debug_assert_eq!(NONCE_LEN, iv.len()); + Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { + key, + iv: Iv::new(iv[..].try_into().expect("conversion should succeed")), + }) + } +} + +impl MessageEncrypter for CipherAdapter { + fn encrypt( + &mut self, + m: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result { + let total_len = self.encrypted_payload_len(m.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + payload.extend_from_chunks(&m.payload); + + let nonce: chacha20poly1305::Nonce = cipher::Nonce::new(&self.1, seq).0.into(); + let aad = make_tls12_aad(seq, m.typ, m.version, m.payload.len()); + + self.0 + .encrypt_in_place(&nonce, &aad, &mut EncryptBufferAdapter(&mut payload)) + .map_err(|_| rustls::Error::EncryptError) + .map(|_| OutboundOpaqueMessage::new(m.typ, m.version, payload)) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + CHACHAPOLY1305_OVERHEAD + } +} + +impl MessageDecrypter for CipherAdapter { + fn decrypt<'a>( + &mut self, + mut m: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result, rustls::Error> { + let payload = &m.payload; + let nonce: chacha20poly1305::Nonce = cipher::Nonce::new(&self.1, seq).0.into(); + let aad = make_tls12_aad( + seq, + m.typ, + m.version, + payload.len() - CHACHAPOLY1305_OVERHEAD, + ); + + let payload = &mut m.payload; + self.0 + .decrypt_in_place(&nonce, &aad, &mut DecryptBufferAdapter(payload)) + .map_err(|_| rustls::Error::DecryptError)?; + + Ok(m.into_plain_message()) + } +} diff --git a/src/tls12/aead/gcm.rs b/src/tls12/aead/gcm.rs new file mode 100644 index 0000000..fa718d4 --- /dev/null +++ b/src/tls12/aead/gcm.rs @@ -0,0 +1,134 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use crate::aead::gcm::{Aes128Gcm, Aes256Gcm}; +use ::aead::{AeadCore, AeadInPlace}; +use crypto_common::{KeyInit, KeySizeUser}; +use paste::paste; +use rustls::crypto::cipher::{ + self, make_tls12_aad, AeadKey, InboundOpaqueMessage, InboundPlainMessage, Iv, KeyBlockShape, + MessageDecrypter, MessageEncrypter, OutboundOpaqueMessage, OutboundPlainMessage, + PrefixedPayload, Tls12AeadAlgorithm, +}; +use rustls::ConnectionTrafficSecrets; +use typenum::Unsigned; + +const EXPLICIT_NONCE_LEN: usize = 8; +const OVERHEAD: usize = EXPLICIT_NONCE_LEN + 16; + +macro_rules! impl_gcm { +($name: ident, $aead: ty, $nonce_pos: expr, $overhead: expr) => { + paste! { + impl Tls12AeadAlgorithm for $name { + fn encrypter(&self, key: AeadKey, write_iv: &[u8], explicit: &[u8]) -> Box { + Box::new([]( + $aead::new_from_slice(key.as_ref()).unwrap(), + { + let mut iv: [u8; 12] = [0; 12]; + iv[..4].copy_from_slice(write_iv); + iv[4..].copy_from_slice(explicit); + iv + }, + )) + } + + fn decrypter(&self, dec_key: AeadKey, dec_iv: &[u8]) -> Box { + Box::new([]( + $aead::new_from_slice(dec_key.as_ref()).unwrap(), + dec_iv.try_into().unwrap(), + )) + } + + fn key_block_shape(&self) -> KeyBlockShape { + KeyBlockShape { + enc_key_len: $aead::key_size(), + fixed_iv_len: 4, + explicit_nonce_len: EXPLICIT_NONCE_LEN, + } + } + + fn extract_keys( + &self, + key: AeadKey, + iv: &[u8], + _explicit: &[u8], + ) -> Result { + Ok(ConnectionTrafficSecrets::$name { + key, + iv: Iv::new(iv[..].try_into().unwrap()), + }) + } + } + + pub struct []($aead, [u8; 12]); + + impl MessageEncrypter for [] { + fn encrypt(&mut self, m: OutboundPlainMessage<'_>, seq: u64) -> Result { + let total_len = self.encrypted_payload_len(m.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = cipher::Nonce::new(&self.1.into(), seq).0; + let aad = make_tls12_aad(seq, m.typ, m.version, m.payload.len()); + payload.extend_from_slice(&nonce.as_ref()[4..]); // explicit + payload.extend_from_chunks(&m.payload); + + self.0 + .encrypt_in_place_detached(&nonce.into(), &aad, &mut payload.as_mut()[$nonce_pos..]) + .map(|tag| payload.extend(tag.as_ref() as &[u8])) + .map_err(|_| rustls::Error::EncryptError) + .map(|_| OutboundOpaqueMessage::new(m.typ, m.version, payload)) + } + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + $nonce_pos + <$aead as AeadCore>::TagSize::USIZE + } + } + + pub struct []($aead, [u8; 4]); + + impl MessageDecrypter for [] { + fn decrypt<'a>(&mut self, mut m: InboundOpaqueMessage<'a>, seq: u64) -> Result, rustls::Error> { + type TagSize = <$aead as AeadCore>::TagSize; + + let payload = &m.payload; + + if payload.len() < $overhead { + return Err(rustls::Error::DecryptError); + } + + let nonce: aead::Nonce<$aead> = { + let mut nonce = [0u8; 12]; + nonce[..4].copy_from_slice(&self.1); // dec_iv + nonce[4..].copy_from_slice(&payload[..$nonce_pos]); + nonce.into() + }; + + let aad = make_tls12_aad(seq, m.typ, m.version, payload.len() - $overhead); + + let payload = &mut m.payload; + let tag_pos = { + let payload = &mut payload[$nonce_pos..]; + let tag_pos = payload.len() - TagSize::to_usize(); + let (msg, tag) = payload.split_at_mut(tag_pos); + + let tag = aes_gcm::Tag::::from_slice(tag); + self.0 + .decrypt_in_place_detached(&nonce, &aad, msg, tag) + .map_err(|_| rustls::Error::DecryptError)?; + tag_pos + }; + + // We defer the truncation to here, because we may inadvertently shifted the + // original data if the decryption failed. Another way to avoid this is + // to clone the payload slice starting after the explicit nonce, + // but this will cause an additional cloning and copying + payload.rotate_left($nonce_pos); + payload.truncate(tag_pos); + Ok(m.into_plain_message()) + } + } + } +}; +} + +impl_gcm! {Aes128Gcm, crate::aead::aes::Aes128Gcm, EXPLICIT_NONCE_LEN, OVERHEAD} +impl_gcm! {Aes256Gcm, crate::aead::aes::Aes256Gcm, EXPLICIT_NONCE_LEN, OVERHEAD} diff --git a/src/tls12/suites.rs b/src/tls12/suites.rs new file mode 100644 index 0000000..06eca35 --- /dev/null +++ b/src/tls12/suites.rs @@ -0,0 +1,34 @@ +use crate::misc::const_concat_slices; +use rustls::SupportedCipherSuite; + +pub const TLS12_SUITES: &[SupportedCipherSuite] = const_concat_slices!( + SupportedCipherSuite, + { + #[cfg(feature = "ecdsa")] + { + ecdsa::TLS_ECDHE_ECDSA_SUITES + } + + #[cfg(not(feature = "ecdsa"))] + { + &[] + } + }, + { + #[cfg(any(feature = "rsa-pss", feature = "rsa-pkcs1"))] + { + rsa::TLS_ECDHE_RSA_SUITES + } + + #[cfg(not(any(feature = "rsa-pss", feature = "rsa-pkcs1")))] + { + &[] + } + } +); + +#[cfg(feature = "ecdsa")] +pub mod ecdsa; +#[cfg(any(feature = "rsa-pss", feature = "rsa-pkcs1"))] +pub mod rsa; +pub mod schemes; diff --git a/src/tls12/suites/ecdsa.rs b/src/tls12/suites/ecdsa.rs new file mode 100644 index 0000000..fa8d5cb --- /dev/null +++ b/src/tls12/suites/ecdsa.rs @@ -0,0 +1,134 @@ +use rustls::SupportedCipherSuite; + +#[cfg(feature = "aead")] +use crate::tls12::suites::schemes::TLS12_ECDSA_SCHEMES; +#[cfg(feature = "aead")] +use crate::{hash, hmac}; +#[cfg(feature = "aead")] +use rustls::crypto::{tls12::PrfUsingHmac, CipherSuiteCommon, KeyExchangeAlgorithm}; +#[cfg(feature = "aead")] +use rustls::{CipherSuite, Tls12CipherSuite}; + +#[cfg(all(feature = "aes-gcm", feature = "hash-sha256"))] +use crate::aead::gcm::Aes128Gcm; + +#[cfg(all(feature = "aes-gcm", feature = "hash-sha384"))] +use crate::aead::gcm::Aes256Gcm; + +#[cfg(all(feature = "aes-ccm", feature = "hash-sha256"))] +use crate::aead::ccm::{Aes128Ccm, Aes128Ccm8, Aes256Ccm, Aes256Ccm8}; + +#[cfg(all(feature = "chacha20poly1305", feature = "hash-sha256"))] +use crate::aead::ChaCha20Poly1305; + +#[cfg(all(feature = "aes-gcm", feature = "hash-sha256"))] +pub const TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &Aes128Gcm, + prf_provider: &PrfUsingHmac(hmac::SHA256), +}; + +#[cfg(all(feature = "aes-gcm", feature = "hash-sha384"))] +pub const TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + hash_provider: hash::SHA384, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + prf_provider: &PrfUsingHmac(hmac::SHA384), + aead_alg: &Aes256Gcm, +}; + +// https://ciphersuite.info/cs/TLS_ECDHE_ECDSA_WITH_AES_128_CCM/ +#[cfg(all(feature = "aes-ccm", feature = "hash-sha256"))] +pub const TLS_ECDHE_ECDSA_WITH_AES_128_CCM: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &Aes128Ccm, + prf_provider: &PrfUsingHmac(hmac::SHA256), +}; + +// https://ciphersuite.info/cs/TLS_ECDHE_ECDSA_WITH_AES_256_CCM/ +#[cfg(all(feature = "aes-ccm", feature = "hash-sha256"))] +pub const TLS_ECDHE_ECDSA_WITH_AES_256_CCM: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &Aes256Ccm, + prf_provider: &PrfUsingHmac(hmac::SHA256), +}; + +// https://ciphersuite.info/cs/TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8/ +#[cfg(all(feature = "aes-ccm", feature = "hash-sha256"))] +pub const TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &Aes128Ccm8, + prf_provider: &PrfUsingHmac(hmac::SHA256), +}; + +// https://ciphersuite.info/cs/TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8/ +#[cfg(all(feature = "aes-ccm", feature = "hash-sha256"))] +pub const TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &Aes256Ccm8, + prf_provider: &PrfUsingHmac(hmac::SHA256), +}; + +#[cfg(all(feature = "chacha20poly1305", feature = "hash-sha256"))] +pub const TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + prf_provider: &PrfUsingHmac(hmac::SHA256), + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_ECDSA_SCHEMES, + aead_alg: &ChaCha20Poly1305, +}; + +pub const TLS_ECDHE_ECDSA_SUITES: &[SupportedCipherSuite] = &[ + #[cfg(all(feature = "aes-gcm", feature = "hash-sha256"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256), + #[cfg(all(feature = "aes-gcm", feature = "hash-sha384"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384), + #[cfg(all(feature = "aes-ccm", feature = "hash-sha256"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_ECDSA_WITH_AES_128_CCM), + #[cfg(all(feature = "aes-ccm", feature = "hash-sha256"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_ECDSA_WITH_AES_256_CCM), + #[cfg(all(feature = "aes-ccm", feature = "hash-sha256"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8), + #[cfg(all(feature = "aes-ccm", feature = "hash-sha256"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8), + #[cfg(all(feature = "chacha20poly1305", feature = "hash-sha256"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256), +]; diff --git a/src/tls12/suites/rsa.rs b/src/tls12/suites/rsa.rs new file mode 100644 index 0000000..d084532 --- /dev/null +++ b/src/tls12/suites/rsa.rs @@ -0,0 +1,67 @@ +use rustls::SupportedCipherSuite; + +#[cfg(feature = "aead")] +use crate::tls12::suites::schemes::TLS12_RSA_SCHEMES; +#[cfg(feature = "aead")] +use crate::{hash, hmac}; +#[cfg(feature = "aead")] +use rustls::crypto::{tls12::PrfUsingHmac, CipherSuiteCommon, KeyExchangeAlgorithm}; +#[cfg(feature = "aead")] +use rustls::{CipherSuite, Tls12CipherSuite}; + +#[cfg(all(feature = "gcm", feature = "hash-sha256"))] +use crate::aead::gcm::Aes128Gcm; + +#[cfg(all(feature = "gcm", feature = "hash-sha384"))] +use crate::aead::gcm::Aes256Gcm; + +#[cfg(feature = "chacha20poly1305")] +use crate::aead::ChaCha20Poly1305; + +#[cfg(all(feature = "aes-gcm", feature = "hash-sha256"))] +pub const TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_RSA_SCHEMES, + aead_alg: &Aes128Gcm, + prf_provider: &PrfUsingHmac(hmac::SHA256), +}; + +#[cfg(all(feature = "aes-gcm", feature = "hash-sha384"))] +pub const TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + hash_provider: hash::SHA384, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_RSA_SCHEMES, + aead_alg: &Aes256Gcm, + prf_provider: &PrfUsingHmac(hmac::SHA384), +}; + +#[cfg(all(feature = "chacha20poly1305", feature = "hash-sha256"))] +pub const TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256: Tls12CipherSuite = Tls12CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + kx: KeyExchangeAlgorithm::ECDHE, + sign: TLS12_RSA_SCHEMES, + prf_provider: &PrfUsingHmac(hmac::SHA256), + aead_alg: &ChaCha20Poly1305, +}; + +pub const TLS_ECDHE_RSA_SUITES: &[SupportedCipherSuite] = &[ + #[cfg(all(feature = "aes-gcm", feature = "hash-sha256"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256), + #[cfg(all(feature = "aes-gcm", feature = "hash-sha384"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384), + #[cfg(all(feature = "chacha20poly1305", feature = "hash-sha256"))] + SupportedCipherSuite::Tls12(&TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256), +]; diff --git a/src/tls12/suites/schemes.rs b/src/tls12/suites/schemes.rs new file mode 100644 index 0000000..873dcf6 --- /dev/null +++ b/src/tls12/suites/schemes.rs @@ -0,0 +1,30 @@ +#[cfg(any(feature = "ecdsa", feature = "rsa"))] +use rustls::SignatureScheme; + +#[cfg(feature = "ecdsa")] +pub const TLS12_ECDSA_SCHEMES: &[SignatureScheme] = &[ + #[cfg(all(feature = "ecdsa-p256", feature = "hash-sha256"))] + SignatureScheme::ECDSA_NISTP256_SHA256, + #[cfg(all(feature = "ecdsa-p384", feature = "hash-sha384"))] + SignatureScheme::ECDSA_NISTP384_SHA384, + #[cfg(all(feature = "ecdsa-p521", feature = "hash-sha512"))] + SignatureScheme::ECDSA_NISTP521_SHA512, + #[cfg(feature = "eddsa-ed25519")] + SignatureScheme::ED25519, +]; + +#[cfg(feature = "rsa")] +pub const TLS12_RSA_SCHEMES: &[SignatureScheme] = &[ + #[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha256"))] + SignatureScheme::RSA_PKCS1_SHA256, + #[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha384"))] + SignatureScheme::RSA_PKCS1_SHA384, + #[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha512"))] + SignatureScheme::RSA_PKCS1_SHA512, + #[cfg(all(feature = "rsa-pss", feature = "hash-sha256"))] + SignatureScheme::RSA_PSS_SHA256, + #[cfg(all(feature = "rsa-pss", feature = "hash-sha384"))] + SignatureScheme::RSA_PSS_SHA384, + #[cfg(all(feature = "rsa-pss", feature = "hash-sha512"))] + SignatureScheme::RSA_PSS_SHA512, +]; diff --git a/src/tls13.rs b/src/tls13.rs new file mode 100644 index 0000000..3864c43 --- /dev/null +++ b/src/tls13.rs @@ -0,0 +1,3 @@ +#[cfg(feature = "aead")] +pub mod aead; +pub mod suites; diff --git a/src/tls13/aead.rs b/src/tls13/aead.rs new file mode 100644 index 0000000..f34c173 --- /dev/null +++ b/src/tls13/aead.rs @@ -0,0 +1,8 @@ +#[cfg(feature = "chacha20poly1305")] +pub mod chacha20; + +#[cfg(feature = "gcm")] +pub mod gcm; + +#[cfg(feature = "ccm")] +pub mod ccm; diff --git a/src/tls13/aead/ccm.rs b/src/tls13/aead/ccm.rs new file mode 100644 index 0000000..0ca62dd --- /dev/null +++ b/src/tls13/aead/ccm.rs @@ -0,0 +1,92 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use crate::aead::ccm::{Aes128Ccm, Aes128Ccm8}; +use crate::aead::{DecryptBufferAdapter, EncryptBufferAdapter}; +use aead::AeadInPlace; +use crypto_common::{KeyInit, KeySizeUser}; +use paste::paste; +use rustls::crypto::cipher::{ + self, make_tls13_aad, AeadKey, InboundOpaqueMessage, InboundPlainMessage, MessageDecrypter, + MessageEncrypter, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, + Tls13AeadAlgorithm, UnsupportedOperationError, +}; +use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion}; + +macro_rules! impl_ccm { +($name: ident, $aead: ty, $overhead: expr) => { + paste! { + impl Tls13AeadAlgorithm for $name { + fn encrypter(&self, key: AeadKey, iv: cipher::Iv) -> Box { + Box::new([]( + $aead::new_from_slice(key.as_ref()).unwrap(), + iv, + )) + } + + fn decrypter(&self, key: AeadKey, iv: cipher::Iv) -> Box { + Box::new([]( + $aead::new_from_slice(key.as_ref()).unwrap(), + iv, + )) + } + + fn key_len(&self) -> usize { + $aead::key_size() + } + fn extract_keys( + &self, + _: AeadKey, + _: cipher::Iv, + ) -> Result { + Err(UnsupportedOperationError) + } + } + + pub struct []($aead, cipher::Iv); + + impl MessageEncrypter for [] { + fn encrypt(&mut self, m: OutboundPlainMessage<'_>, seq: u64) -> Result { + let total_len = self.encrypted_payload_len(m.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = cipher::Nonce::new(&self.1, seq).0; + let aad = make_tls13_aad(total_len); + payload.extend_from_chunks(&m.payload); + payload.extend_from_slice(&m.typ.to_array()); + + self.0 + .encrypt_in_place(&nonce.into(), &aad, &mut EncryptBufferAdapter(&mut payload)) + .map_err(|_| rustls::Error::EncryptError) + .map(|_| OutboundOpaqueMessage::new( + ContentType::ApplicationData, + ProtocolVersion::TLSv1_2, + payload, + )) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + 1 + $overhead + } + } + + impl MessageDecrypter for [] { + fn decrypt<'a>(&mut self, mut m: InboundOpaqueMessage<'a>, seq: u64) -> Result, rustls::Error> { + let payload = &mut m.payload; + let nonce = cipher::Nonce::new(&self.1, seq).0; + let aad = make_tls13_aad(payload.len()); + + self.0 + .decrypt_in_place(&nonce.into(), &aad, &mut DecryptBufferAdapter(payload)) + .map_err(|_| rustls::Error::DecryptError)?; + + m.into_tls13_unpadded_message() + } + } + + } +}; +} + +impl_ccm! {Aes128Ccm, crate::aead::aes::Aes128Ccm, 16} +impl_ccm! {Aes128Ccm8, crate::aead::aes::Aes128Ccm8, 8} diff --git a/src/tls13/aead/chacha20.rs b/src/tls13/aead/chacha20.rs new file mode 100644 index 0000000..058482c --- /dev/null +++ b/src/tls13/aead/chacha20.rs @@ -0,0 +1,95 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use crate::aead::{ + ChaCha20Poly1305, DecryptBufferAdapter, EncryptBufferAdapter, CHACHAPOLY1305_OVERHEAD, +}; +use chacha20poly1305::{AeadInPlace, KeyInit, KeySizeUser}; +use rustls::crypto::cipher::{ + self, make_tls13_aad, AeadKey, InboundOpaqueMessage, InboundPlainMessage, Iv, MessageDecrypter, + MessageEncrypter, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, + Tls13AeadAlgorithm, UnsupportedOperationError, +}; +use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion}; + +impl Tls13AeadAlgorithm for ChaCha20Poly1305 { + fn encrypter(&self, key: AeadKey, iv: Iv) -> Box { + Box::new(CipherAdapter( + chacha20poly1305::ChaCha20Poly1305::new_from_slice(key.as_ref()) + .expect("key should be valid"), + iv, + )) + } + + fn decrypter(&self, key: AeadKey, iv: Iv) -> Box { + Box::new(CipherAdapter( + chacha20poly1305::ChaCha20Poly1305::new_from_slice(key.as_ref()) + .expect("key should be valid"), + iv, + )) + } + + fn key_len(&self) -> usize { + chacha20poly1305::ChaCha20Poly1305::key_size() + } + + fn extract_keys( + &self, + key: AeadKey, + iv: Iv, + ) -> Result { + Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv }) + } +} + +pub struct CipherAdapter(chacha20poly1305::ChaCha20Poly1305, Iv); + +impl MessageEncrypter for CipherAdapter { + fn encrypt( + &mut self, + m: OutboundPlainMessage<'_>, + seq: u64, + ) -> Result { + let total_len = self.encrypted_payload_len(m.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + payload.extend_from_chunks(&m.payload); + payload.extend_from_slice(&m.typ.to_array()); + + let nonce: chacha20poly1305::Nonce = cipher::Nonce::new(&self.1, seq).0.into(); + let aad = make_tls13_aad(total_len); + + self.0 + .encrypt_in_place(&nonce, &aad, &mut EncryptBufferAdapter(&mut payload)) + .map_err(|_| rustls::Error::EncryptError) + .map(|()| { + OutboundOpaqueMessage::new( + ContentType::ApplicationData, + ProtocolVersion::TLSv1_2, + payload, + ) + }) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + 1 + CHACHAPOLY1305_OVERHEAD + } +} + +impl MessageDecrypter for CipherAdapter { + fn decrypt<'a>( + &mut self, + mut m: InboundOpaqueMessage<'a>, + seq: u64, + ) -> Result, rustls::Error> { + let payload = &mut m.payload; + let nonce: chacha20poly1305::Nonce = cipher::Nonce::new(&self.1, seq).0.into(); + let aad = make_tls13_aad(payload.len()); + + self.0 + .decrypt_in_place(&nonce, &aad, &mut DecryptBufferAdapter(payload)) + .map_err(|_| rustls::Error::DecryptError)?; + + m.into_tls13_unpadded_message() + } +} diff --git a/src/tls13/aead/gcm.rs b/src/tls13/aead/gcm.rs new file mode 100644 index 0000000..ccc7a48 --- /dev/null +++ b/src/tls13/aead/gcm.rs @@ -0,0 +1,92 @@ +#[cfg(feature = "alloc")] +use alloc::boxed::Box; + +use crate::aead::gcm::{Aes128Gcm, Aes256Gcm}; +use crate::aead::{DecryptBufferAdapter, EncryptBufferAdapter}; +use aead::AeadInPlace; +use crypto_common::{KeyInit, KeySizeUser}; +use paste::paste; +use rustls::crypto::cipher::{ + self, make_tls13_aad, AeadKey, InboundOpaqueMessage, InboundPlainMessage, MessageDecrypter, + MessageEncrypter, OutboundOpaqueMessage, OutboundPlainMessage, PrefixedPayload, + Tls13AeadAlgorithm, +}; +use rustls::{ConnectionTrafficSecrets, ContentType, ProtocolVersion}; + +macro_rules! impl_gcm { +($name: ident, $aead: ty, $overhead: expr) => { + paste! { + impl Tls13AeadAlgorithm for $name { + fn encrypter(&self, key: AeadKey, iv: cipher::Iv) -> Box { + Box::new([]( + $aead::new_from_slice(key.as_ref()).unwrap(), + iv, + )) + } + + fn decrypter(&self, key: AeadKey, iv: cipher::Iv) -> Box { + Box::new([]( + $aead::new_from_slice(key.as_ref()).unwrap(), + iv, + )) + } + + fn key_len(&self) -> usize { + $aead::key_size() + } + fn extract_keys( + &self, + key: AeadKey, + iv: cipher::Iv, + ) -> Result { + Ok(ConnectionTrafficSecrets::$name { key, iv }) + } + } + + pub struct []($aead, cipher::Iv); + + impl MessageEncrypter for [] { + fn encrypt(&mut self, m: OutboundPlainMessage<'_>, seq: u64) -> Result { + let total_len = self.encrypted_payload_len(m.payload.len()); + let mut payload = PrefixedPayload::with_capacity(total_len); + + let nonce = cipher::Nonce::new(&self.1, seq).0; + let aad = make_tls13_aad(total_len); + payload.extend_from_chunks(&m.payload); + payload.extend_from_slice(&m.typ.to_array()); + + self.0 + .encrypt_in_place(&nonce.into(), &aad, &mut EncryptBufferAdapter(&mut payload)) + .map_err(|_| rustls::Error::EncryptError) + .map(|_| OutboundOpaqueMessage::new( + ContentType::ApplicationData, + ProtocolVersion::TLSv1_2, + payload, + )) + } + + fn encrypted_payload_len(&self, payload_len: usize) -> usize { + payload_len + 1 + $overhead + } + } + + impl MessageDecrypter for [] { + fn decrypt<'a>(&mut self, mut m: InboundOpaqueMessage<'a>, seq: u64) -> Result, rustls::Error> { + let payload = &mut m.payload; + let nonce = cipher::Nonce::new(&self.1, seq).0; + let aad = make_tls13_aad(payload.len()); + + self.0 + .decrypt_in_place(&nonce.into(), &aad, &mut DecryptBufferAdapter(payload)) + .map_err(|_| rustls::Error::DecryptError)?; + + m.into_tls13_unpadded_message() + } + } + + } +}; +} + +impl_gcm! {Aes128Gcm, crate::aead::aes::Aes128Gcm, 16} +impl_gcm! {Aes256Gcm, crate::aead::aes::Aes256Gcm, 16} diff --git a/src/tls13/suites.rs b/src/tls13/suites.rs new file mode 100644 index 0000000..c849e48 --- /dev/null +++ b/src/tls13/suites.rs @@ -0,0 +1,38 @@ +use crate::const_concat_slices; +use rustls::SupportedCipherSuite; + +pub const TLS13_SUITES: &[SupportedCipherSuite] = const_concat_slices!( + SupportedCipherSuite, + { + #[cfg(feature = "aes")] + { + aes::TLS13_AES_SUITES + } + + #[cfg(not(feature = "aes"))] + { + &[] + } + }, + { + #[cfg(feature = "chacha20poly1305")] + { + &[ + #[cfg(feature = "hash-sha256")] + chacha20::TLS13_CHACHA20_POLY1305_SHA256, + ] + } + + #[cfg(not(feature = "chacha20poly1305"))] + { + &[] + } + }, + &[] +); + +#[cfg(feature = "aes")] +pub mod aes; + +#[cfg(feature = "chacha20poly1305")] +pub mod chacha20; diff --git a/src/tls13/suites/aes.rs b/src/tls13/suites/aes.rs new file mode 100644 index 0000000..02dfd7e --- /dev/null +++ b/src/tls13/suites/aes.rs @@ -0,0 +1,99 @@ +#[cfg(all(feature = "ccm", feature = "hash-sha256"))] +use crate::aead::ccm::{Aes128Ccm, Aes128Ccm8}; +#[cfg(all(feature = "gcm", feature = "hash-sha256"))] +use crate::aead::gcm::Aes128Gcm; +#[cfg(all(feature = "gcm", feature = "hash-sha384"))] +use crate::aead::gcm::Aes256Gcm; +use crate::const_concat_slices; +use crate::{hash, hmac}; +use rustls::crypto::{tls13::HkdfUsingHmac, CipherSuiteCommon}; +use rustls::{CipherSuite, SupportedCipherSuite, Tls13CipherSuite}; + +#[cfg(all(feature = "gcm", feature = "hash-sha256"))] +pub const TLS13_AES_128_GCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls13(&Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_AES_128_GCM_SHA256, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + hkdf_provider: &HkdfUsingHmac(hmac::SHA256), + aead_alg: &Aes128Gcm, + quic: None, + }); + +#[cfg(all(feature = "gcm", feature = "hash-sha384"))] +pub const TLS13_AES_256_GCM_SHA384: SupportedCipherSuite = + SupportedCipherSuite::Tls13(&Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_AES_256_GCM_SHA384, + hash_provider: hash::SHA384, + confidentiality_limit: u64::MAX, + }, + hkdf_provider: &HkdfUsingHmac(hmac::SHA384), + aead_alg: &Aes256Gcm, + quic: None, + }); + +#[cfg(all(feature = "ccm", feature = "hash-sha256"))] +pub const TLS13_AES_128_CCM_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls13(&Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_AES_128_CCM_SHA256, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + hkdf_provider: &HkdfUsingHmac(hmac::SHA256), + aead_alg: &Aes128Ccm, + quic: None, + }); + +#[cfg(all(feature = "ccm", feature = "hash-sha256"))] +pub const TLS13_AES_128_CCM_8_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls13(&Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_AES_128_CCM_8_SHA256, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + hkdf_provider: &HkdfUsingHmac(hmac::SHA256), + aead_alg: &Aes128Ccm8, + quic: None, + }); + +pub const TLS13_AES_SUITES: &[SupportedCipherSuite] = const_concat_slices!( + SupportedCipherSuite, + { + #[cfg(feature = "gcm")] + { + &[ + #[cfg(feature = "hash-sha256")] + TLS13_AES_128_GCM_SHA256, + #[cfg(feature = "hash-sha384")] + TLS13_AES_256_GCM_SHA384, + ] + } + + #[cfg(not(feature = "gcm"))] + { + &[] + } + }, + { + #[cfg(feature = "ccm")] + { + &[ + #[cfg(feature = "hash-sha256")] + TLS13_AES_128_CCM_SHA256, + #[cfg(feature = "hash-sha256")] + TLS13_AES_128_CCM_8_SHA256, + ] + } + + #[cfg(not(feature = "ccm"))] + { + &[] + } + }, + &[] +); diff --git a/src/tls13/suites/chacha20.rs b/src/tls13/suites/chacha20.rs new file mode 100644 index 0000000..6d87d07 --- /dev/null +++ b/src/tls13/suites/chacha20.rs @@ -0,0 +1,17 @@ +use crate::aead::ChaCha20Poly1305; +use crate::{hash, hmac}; +use rustls::crypto::{tls13::HkdfUsingHmac, CipherSuiteCommon}; +use rustls::{CipherSuite, SupportedCipherSuite, Tls13CipherSuite}; + +#[cfg(feature = "hash-sha256")] +pub const TLS13_CHACHA20_POLY1305_SHA256: SupportedCipherSuite = + SupportedCipherSuite::Tls13(&Tls13CipherSuite { + common: CipherSuiteCommon { + suite: CipherSuite::TLS13_CHACHA20_POLY1305_SHA256, + hash_provider: hash::SHA256, + confidentiality_limit: u64::MAX, + }, + hkdf_provider: &HkdfUsingHmac(hmac::SHA256), + aead_alg: &ChaCha20Poly1305, + quic: None, + }); diff --git a/src/verify.rs b/src/verify.rs index 2043b64..7e0ec87 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -1,46 +1,227 @@ +use core::array::TryFromSliceError; + +use crate::const_concat_slices; + +use pki_types::SignatureVerificationAlgorithm; use rustls::crypto::WebPkiSupportedAlgorithms; use rustls::SignatureScheme; -use self::ecdsa::{ECDSA_P256_SHA256, ECDSA_P256_SHA384, ECDSA_P384_SHA256, ECDSA_P384_SHA384}; -use self::eddsa::ED25519; -use self::rsa::{ - RSA_PKCS1_SHA256, RSA_PKCS1_SHA384, RSA_PKCS1_SHA512, RSA_PSS_SHA256, RSA_PSS_SHA384, - RSA_PSS_SHA512, -}; +pub(crate) enum Error { + #[cfg(feature = "signature")] + Signature, + TryFromSlice, + #[cfg(feature = "der")] + Der, + #[cfg(feature = "pkcs1")] + Pkcs1, +} -pub static ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { - all: &[ - ECDSA_P256_SHA256, - ECDSA_P256_SHA384, - ECDSA_P384_SHA256, - ECDSA_P384_SHA384, - ED25519, - RSA_PKCS1_SHA256, - RSA_PKCS1_SHA384, - RSA_PKCS1_SHA512, - RSA_PSS_SHA256, - RSA_PSS_SHA384, - RSA_PSS_SHA512, - ], - mapping: &[ - ( - SignatureScheme::ECDSA_NISTP384_SHA384, - &[ECDSA_P384_SHA384, ECDSA_P256_SHA384], - ), +#[cfg(feature = "signature")] +impl From for Error { + fn from(_: signature::Error) -> Self { + Self::Signature + } +} + +#[cfg(feature = "der")] +impl From for Error { + fn from(_: der::Error) -> Self { + Self::Der + } +} + +#[cfg(feature = "pkcs1")] +impl From for Error { + fn from(_: pkcs1::Error) -> Self { + Self::Pkcs1 + } +} + +impl From for Error { + fn from(_: TryFromSliceError) -> Self { + Self::TryFromSlice + } +} + +pub const ALL: &[&'static dyn SignatureVerificationAlgorithm] = const_concat_slices!( + &'static dyn SignatureVerificationAlgorithm, + { + #[cfg(feature = "ecdsa")] + { + &[ + #[cfg(all(feature = "ecdsa-p256", feature = "hash-sha256"))] + ecdsa::nist::ECDSA_P256_SHA256, + #[cfg(all(feature = "ecdsa-p256", feature = "hash-sha384"))] + ecdsa::nist::ECDSA_P256_SHA384, + #[cfg(all(feature = "ecdsa-p384", feature = "hash-sha256"))] + ecdsa::nist::ECDSA_P384_SHA256, + #[cfg(all(feature = "ecdsa-p384", feature = "hash-sha384"))] + ecdsa::nist::ECDSA_P384_SHA384, + ] + } + + #[cfg(not(feature = "ecdsa"))] + { + &[] + } + }, + { + #[cfg(feature = "rsa-pkcs1")] + { + &[ + #[cfg(feature = "hash-sha256")] + rsa::RSA_PKCS1_SHA256, + #[cfg(feature = "hash-sha384")] + rsa::RSA_PKCS1_SHA384, + #[cfg(feature = "hash-sha512")] + rsa::RSA_PKCS1_SHA512, + ] + } + + #[cfg(not(feature = "rsa-pkcs1"))] + { + &[] + } + }, + { + #[cfg(feature = "rsa-pss")] + { + &[ + #[cfg(feature = "hash-sha256")] + rsa::RSA_PSS_SHA256, + #[cfg(feature = "hash-sha384")] + rsa::RSA_PSS_SHA384, + #[cfg(feature = "hash-sha512")] + rsa::RSA_PSS_SHA512, + ] + } + + #[cfg(not(feature = "rsa-pss"))] + { + &[] + } + }, +); + +pub const MAPPING: &[( + SignatureScheme, + &[&'static dyn SignatureVerificationAlgorithm], +)] = const_concat_slices!( + ( + SignatureScheme, + &'static [&'static dyn SignatureVerificationAlgorithm], + ), + { + #[cfg(feature = "ecdsa")] + { + &[ + #[cfg(all(feature = "ecdsa-p384", feature = "hash-sha384"))] + ( + SignatureScheme::ECDSA_NISTP384_SHA384, + &[ + ecdsa::nist::ECDSA_P384_SHA384, + #[cfg(feature = "p256")] + ecdsa::nist::ECDSA_P256_SHA384, + ], + ), + #[cfg(all(feature = "ecdsa-p256", feature = "hash-sha256"))] + ( + SignatureScheme::ECDSA_NISTP256_SHA256, + &[ + ecdsa::nist::ECDSA_P256_SHA256, + #[cfg(feature = "ecdsa-p384")] + ecdsa::nist::ECDSA_P384_SHA256, + ], + ), + ] + } + + #[cfg(not(feature = "ecdsa"))] + { + &[] + } + }, + { + #[cfg(feature = "rsa-pkcs1")] + { + &[ + #[cfg(feature = "hash-sha256")] + (SignatureScheme::RSA_PKCS1_SHA256, &[rsa::RSA_PKCS1_SHA256]), + #[cfg(feature = "hash-sha384")] + (SignatureScheme::RSA_PKCS1_SHA384, &[rsa::RSA_PKCS1_SHA384]), + #[cfg(feature = "hash-sha512")] + (SignatureScheme::RSA_PKCS1_SHA512, &[rsa::RSA_PKCS1_SHA512]), + ] + } + + #[cfg(not(feature = "rsa-pkcs1"))] + { + &[] + } + }, + { + #[cfg(feature = "rsa-pss")] + { + &[ + #[cfg(feature = "hash-sha256")] + (SignatureScheme::RSA_PSS_SHA256, &[rsa::RSA_PSS_SHA256]), + #[cfg(feature = "hash-sha384")] + (SignatureScheme::RSA_PSS_SHA384, &[rsa::RSA_PSS_SHA384]), + #[cfg(feature = "hash-sha512")] + (SignatureScheme::RSA_PSS_SHA512, &[rsa::RSA_PSS_SHA512]), + ] + } + + #[cfg(not(feature = "rsa-pss"))] + { + &[] + } + }, +); + +pub const ALGORITHMS: WebPkiSupportedAlgorithms = WebPkiSupportedAlgorithms { + all: const_concat_slices!(&'static dyn SignatureVerificationAlgorithm, ALL, { + #[cfg(feature = "eddsa")] + { + &[ + #[cfg(feature = "eddsa-ed25519")] + eddsa::ed25519::ED25519, + ] + } + + #[cfg(not(feature = "eddsa"))] + { + &[] + } + }), + mapping: const_concat_slices!( ( - SignatureScheme::ECDSA_NISTP256_SHA256, - &[ECDSA_P256_SHA256, ECDSA_P384_SHA256], + SignatureScheme, + &'static [&'static dyn SignatureVerificationAlgorithm], ), - (SignatureScheme::ED25519, &[ED25519]), - (SignatureScheme::RSA_PKCS1_SHA256, &[RSA_PKCS1_SHA256]), - (SignatureScheme::RSA_PKCS1_SHA384, &[RSA_PKCS1_SHA384]), - (SignatureScheme::RSA_PKCS1_SHA512, &[RSA_PKCS1_SHA512]), - (SignatureScheme::RSA_PSS_SHA256, &[RSA_PSS_SHA256]), - (SignatureScheme::RSA_PSS_SHA384, &[RSA_PSS_SHA384]), - (SignatureScheme::RSA_PSS_SHA512, &[RSA_PSS_SHA512]), - ], + MAPPING, + { + #[cfg(feature = "eddsa")] + { + &[ + #[cfg(feature = "eddsa-ed25519")] + (SignatureScheme::ED25519, &[eddsa::ed25519::ED25519]), + ] + } + + #[cfg(not(feature = "eddsa"))] + { + &[] + } + }, + ), }; +#[cfg(feature = "ecdsa")] pub mod ecdsa; + +#[cfg(feature = "eddsa")] pub mod eddsa; + +#[cfg(feature = "rsa")] pub mod rsa; diff --git a/src/verify/ecdsa.rs b/src/verify/ecdsa.rs index 669f03d..2d5c7c7 100644 --- a/src/verify/ecdsa.rs +++ b/src/verify/ecdsa.rs @@ -1,54 +1,2 @@ -use der::Decode; -use digest::Digest; -use paste::paste; -use pki_types::{AlgorithmIdentifier, InvalidSignature, SignatureVerificationAlgorithm}; -use signature::hazmat::PrehashVerifier; -use webpki::alg_id; - -macro_rules! impl_generic_ecdsa_verifer { - ( - $name:ident, - $public_key_algo:expr, - $signature_alg_id:expr, - $verifying_key:ty, - $signature:ty, - $hash:ty - ) => { - paste! { - #[allow(non_camel_case_types)] - #[derive(Debug)] - struct []; - - impl SignatureVerificationAlgorithm for [] { - fn public_key_alg_id(&self) -> AlgorithmIdentifier { - $public_key_algo - } - - fn signature_alg_id(&self) -> AlgorithmIdentifier { - $signature_alg_id - } - - fn verify_signature( - &self, - public_key: &[u8], - message: &[u8], - signature: &[u8], - ) -> Result<(), InvalidSignature> { - let signature = <$signature>::from_der(signature).map_err(|_| InvalidSignature)?; - let verifying_key = <$verifying_key>::from_sec1_bytes(public_key).map_err(|_| InvalidSignature)?; - let digest = &<$hash>::digest(&message); - verifying_key - .verify_prehash(digest, &signature) - .map_err(|_| InvalidSignature) - } - } - - pub const $name: &dyn SignatureVerificationAlgorithm = &[]; - } - }; -} - -impl_generic_ecdsa_verifer! {ECDSA_P256_SHA256, alg_id::ECDSA_P256, alg_id::ECDSA_SHA256, p256::ecdsa::VerifyingKey, p256::ecdsa::DerSignature, sha2::Sha256} -impl_generic_ecdsa_verifer! {ECDSA_P256_SHA384, alg_id::ECDSA_P256, alg_id::ECDSA_SHA384, p256::ecdsa::VerifyingKey, p256::ecdsa::DerSignature, sha2::Sha384} -impl_generic_ecdsa_verifer! {ECDSA_P384_SHA256, alg_id::ECDSA_P384, alg_id::ECDSA_SHA256, p384::ecdsa::VerifyingKey, p384::ecdsa::DerSignature, sha2::Sha256} -impl_generic_ecdsa_verifer! {ECDSA_P384_SHA384, alg_id::ECDSA_P384, alg_id::ECDSA_SHA384, p384::ecdsa::VerifyingKey, p384::ecdsa::DerSignature, sha2::Sha384} +#[cfg(feature = "verify-ecdsa-nist")] +pub mod nist; diff --git a/src/verify/ecdsa/nist.rs b/src/verify/ecdsa/nist.rs new file mode 100644 index 0000000..581d92b --- /dev/null +++ b/src/verify/ecdsa/nist.rs @@ -0,0 +1,64 @@ +use digest::Digest; +use paste::paste; +use pki_types::{AlgorithmIdentifier, InvalidSignature, SignatureVerificationAlgorithm}; +use signature::hazmat::PrehashVerifier; +use webpki::alg_id; + +macro_rules! impl_generic_ecdsa_verifer { +( + $name:ident, + $public_key_algo:expr, + $signature_alg_id:expr, + $verifying_key:ty, + $signature:ty, + $hash:ty +) => { + paste! { + #[allow(non_camel_case_types)] + #[derive(Debug)] + pub struct []; + + impl [] { + fn verify_inner(public_key: &[u8], message: &[u8], signature: &[u8]) -> Result<(), crate::verify::Error> { + use der::Decode; + + let signature = <$signature>::from_der(signature)?; + let verifying_key = <$verifying_key>::from_sec1_bytes(public_key)?; + let digest = &<$hash>::digest(&message); + verifying_key.verify_prehash(digest, &signature)?; + Ok(()) + } + } + + impl SignatureVerificationAlgorithm for [] { + fn public_key_alg_id(&self) -> AlgorithmIdentifier { + $public_key_algo + } + + fn signature_alg_id(&self) -> AlgorithmIdentifier { + $signature_alg_id + } + + fn verify_signature( + &self, + public_key: &[u8], + message: &[u8], + signature: &[u8], + ) -> Result<(), InvalidSignature> { + Self::verify_inner(public_key, message, signature).map_err(|_| InvalidSignature) + } + } + + pub const $name: &dyn SignatureVerificationAlgorithm = &[]; + } +}; +} + +#[cfg(all(feature = "ecdsa-p256", feature = "hash-sha256"))] +impl_generic_ecdsa_verifer! {ECDSA_P256_SHA256, alg_id::ECDSA_P256, alg_id::ECDSA_SHA256, ::p256::ecdsa::VerifyingKey, ::p256::ecdsa::DerSignature, ::sha2::Sha256} +#[cfg(all(feature = "ecdsa-p256", feature = "hash-sha384"))] +impl_generic_ecdsa_verifer! {ECDSA_P256_SHA384, alg_id::ECDSA_P256, alg_id::ECDSA_SHA384, ::p256::ecdsa::VerifyingKey, ::p256::ecdsa::DerSignature, ::sha2::Sha384} +#[cfg(all(feature = "ecdsa-p384", feature = "hash-sha256"))] +impl_generic_ecdsa_verifer! {ECDSA_P384_SHA256, alg_id::ECDSA_P384, alg_id::ECDSA_SHA256, ::p384::ecdsa::VerifyingKey, ::p384::ecdsa::DerSignature, ::sha2::Sha256} +#[cfg(all(feature = "ecdsa-p384", feature = "hash-sha384"))] +impl_generic_ecdsa_verifer! {ECDSA_P384_SHA384, alg_id::ECDSA_P384, alg_id::ECDSA_SHA384, ::p384::ecdsa::VerifyingKey, ::p384::ecdsa::DerSignature, ::sha2::Sha384} diff --git a/src/verify/eddsa.rs b/src/verify/eddsa.rs index 334321c..5141d74 100644 --- a/src/verify/eddsa.rs +++ b/src/verify/eddsa.rs @@ -1,33 +1,2 @@ -use pki_types::{AlgorithmIdentifier, InvalidSignature, SignatureVerificationAlgorithm}; -use signature::Verifier; -use webpki::alg_id; - -#[derive(Debug)] -struct Ed25519Verify; - -impl SignatureVerificationAlgorithm for Ed25519Verify { - fn public_key_alg_id(&self) -> AlgorithmIdentifier { - alg_id::ED25519 - } - - fn signature_alg_id(&self) -> AlgorithmIdentifier { - alg_id::ED25519 - } - - fn verify_signature( - &self, - public_key: &[u8], - message: &[u8], - signature: &[u8], - ) -> Result<(), InvalidSignature> { - let public_key = public_key.try_into().map_err(|_| InvalidSignature)?; - let signature = - ed25519_dalek::Signature::from_slice(signature).map_err(|_| InvalidSignature)?; - ed25519_dalek::VerifyingKey::from_bytes(public_key) - .map_err(|_| InvalidSignature)? - .verify(message, &signature) - .map_err(|_| InvalidSignature) - } -} - -pub const ED25519: &dyn SignatureVerificationAlgorithm = &Ed25519Verify; +#[cfg(feature = "ed25519")] +pub mod ed25519; diff --git a/src/verify/eddsa/ed25519.rs b/src/verify/eddsa/ed25519.rs new file mode 100644 index 0000000..c57dbfe --- /dev/null +++ b/src/verify/eddsa/ed25519.rs @@ -0,0 +1,42 @@ +use ed25519_dalek::{Signature, VerifyingKey}; +use pki_types::{AlgorithmIdentifier, InvalidSignature, SignatureVerificationAlgorithm}; +use signature::Verifier; +use webpki::alg_id; + +#[derive(Debug)] +pub struct Ed25519Verify; + +impl Ed25519Verify { + fn verify_inner( + public_key: &[u8], + message: &[u8], + signature: &[u8], + ) -> Result<(), crate::verify::Error> { + let public_key = public_key.try_into()?; + let signature = Signature::from_slice(signature)?; + let verifying_key = VerifyingKey::from_bytes(public_key)?; + verifying_key.verify(message, &signature)?; + Ok(()) + } +} + +impl SignatureVerificationAlgorithm for Ed25519Verify { + fn public_key_alg_id(&self) -> AlgorithmIdentifier { + alg_id::ED25519 + } + + fn signature_alg_id(&self) -> AlgorithmIdentifier { + alg_id::ED25519 + } + + fn verify_signature( + &self, + public_key: &[u8], + message: &[u8], + signature: &[u8], + ) -> Result<(), InvalidSignature> { + Self::verify_inner(public_key, message, signature).map_err(|_| InvalidSignature) + } +} + +pub const ED25519: &dyn SignatureVerificationAlgorithm = &Ed25519Verify; diff --git a/src/verify/rsa.rs b/src/verify/rsa.rs index f7a1889..c273176 100644 --- a/src/verify/rsa.rs +++ b/src/verify/rsa.rs @@ -1,11 +1,15 @@ use paste::paste; use pki_types::{AlgorithmIdentifier, InvalidSignature, SignatureVerificationAlgorithm}; -use rsa::pkcs1::DecodeRsaPublicKey; -use rsa::{pkcs1v15, pss, RsaPublicKey}; -use sha2::{Sha256, Sha384, Sha512}; use signature::Verifier; use webpki::alg_id; +#[cfg(feature = "hash-sha256")] +use sha2::Sha256; +#[cfg(feature = "hash-sha384")] +use sha2::Sha384; +#[cfg(feature = "hash-sha512")] +use sha2::Sha512; + macro_rules! impl_generic_rsa_verifer { ( $name:ident, @@ -17,7 +21,23 @@ macro_rules! impl_generic_rsa_verifer { paste! { #[allow(non_camel_case_types)] #[derive(Debug)] - struct []; + pub struct []; + + impl [] { + fn verify_inner( + public_key: &[u8], + message: &[u8], + signature: &[u8], + ) -> Result<(), crate::verify::Error> { + use rsa::RsaPublicKey; + use pkcs1::DecodeRsaPublicKey; + + let public_key = RsaPublicKey::from_pkcs1_der(public_key)?; + let signature = <$signature>::try_from(signature)?; + <$verifying_key>::new(public_key).verify(message, &signature)?; + Ok(()) + } + } impl SignatureVerificationAlgorithm for [] { fn public_key_alg_id(&self) -> AlgorithmIdentifier { @@ -34,11 +54,7 @@ macro_rules! impl_generic_rsa_verifer { message: &[u8], signature: &[u8], ) -> Result<(), InvalidSignature> { - let public_key = RsaPublicKey::from_pkcs1_der(public_key).map_err(|_| InvalidSignature)?; - let signature = <$signature>::try_from(signature).map_err(|_| InvalidSignature)?; - <$verifying_key>::new(public_key) - .verify(message, &signature) - .map_err(|_| InvalidSignature) + Self::verify_inner(public_key, message, signature).map_err(|_| InvalidSignature) } } @@ -47,46 +63,54 @@ macro_rules! impl_generic_rsa_verifer { }; } +#[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha256"))] impl_generic_rsa_verifer!( RSA_PKCS1_SHA256, alg_id::RSA_ENCRYPTION, alg_id::RSA_PKCS1_SHA256, - pkcs1v15::VerifyingKey, - pkcs1v15::Signature + ::rsa::pkcs1v15::VerifyingKey, + ::rsa::pkcs1v15::Signature ); + +#[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha384"))] impl_generic_rsa_verifer!( RSA_PKCS1_SHA384, alg_id::RSA_ENCRYPTION, alg_id::RSA_PKCS1_SHA384, - pkcs1v15::VerifyingKey, - pkcs1v15::Signature + ::rsa::pkcs1v15::VerifyingKey, + ::rsa::pkcs1v15::Signature ); + +#[cfg(all(feature = "rsa-pkcs1", feature = "hash-sha512"))] impl_generic_rsa_verifer!( RSA_PKCS1_SHA512, alg_id::RSA_ENCRYPTION, alg_id::RSA_PKCS1_SHA512, - pkcs1v15::VerifyingKey, - pkcs1v15::Signature + ::rsa::pkcs1v15::VerifyingKey, + ::rsa::pkcs1v15::Signature ); +#[cfg(all(feature = "rsa-pss", feature = "hash-sha256"))] impl_generic_rsa_verifer!( RSA_PSS_SHA256, alg_id::RSA_ENCRYPTION, alg_id::RSA_PSS_SHA256, - pss::VerifyingKey, - pss::Signature + ::rsa::pss::VerifyingKey, + ::rsa::pss::Signature ); +#[cfg(all(feature = "rsa-pss", feature = "hash-sha384"))] impl_generic_rsa_verifer!( RSA_PSS_SHA384, alg_id::RSA_ENCRYPTION, alg_id::RSA_PSS_SHA384, - pss::VerifyingKey, - pss::Signature + ::rsa::pss::VerifyingKey, + ::rsa::pss::Signature ); +#[cfg(all(feature = "rsa-pss", feature = "hash-sha512"))] impl_generic_rsa_verifer!( RSA_PSS_SHA512, alg_id::RSA_ENCRYPTION, alg_id::RSA_PSS_SHA512, - pss::VerifyingKey, - pss::Signature + ::rsa::pss::VerifyingKey, + ::rsa::pss::Signature ); diff --git a/tests-external/badssl.rs b/tests-external/badssl.rs deleted file mode 100644 index 7da3a4d..0000000 --- a/tests-external/badssl.rs +++ /dev/null @@ -1,53 +0,0 @@ -use claim::{assert_err, assert_ok}; -use test_case::test_case; - -// For the available tests check out here: https://badssl.com/dashboard/ - -#[cfg(feature = "tls12")] -#[test_case("https://ecc256.badssl.com/", Ok(()); "test ECC256 verification")] -#[test_case("https://ecc384.badssl.com/", Ok(()); "test ECC384 verification")] -#[test_case("https://rsa2048.badssl.com/", Ok(()); "test RSA-2048 verification")] -#[test_case("https://rsa4096.badssl.com/", Ok(()); "test RSA-4096 verification")] -#[cfg_attr(TODO, test_case("https://rsa8192.badssl.com/", Err(()); "test RSA-8192 verification"))] -#[test_case("https://sha256.badssl.com/", Ok(()); "test SHA-256 hash")] -#[test_case("https://sha384.badssl.com/", Err(()); "test SHA-384 hash (but expired)")] -#[test_case("https://sha512.badssl.com/", Err(()); "test SHA-512 hash (but expired)")] -#[test_case("https://tls-v1-2.badssl.com/", Ok(()); "test general TLS1.2 verification")] -#[test_case("https://mozilla-intermediate.badssl.com/", Ok(()); "test Mozilla intermediate compatibility (TLS 1.2 only)")] -#[test_case("https://long-extended-subdomain-name-containing-many-letters-and-dashes.badssl.com/", Ok(()); "test long name with dashes")] -#[test_case("https://longextendedsubdomainnamewithoutdashesinordertotestwordwrapping.badssl.com/", Ok(()); "test long name")] -#[tokio::test] -async fn test_badssl_tls12(uri: &str, expected: Result<(), ()>) { - if expected.is_ok() { - assert_ok!(client::run_request(uri).await); - } else { - assert_err!(client::run_request(uri).await); - } -} - -#[test_case("https://hsts.badssl.com/", Ok(()); "test HSTS (TODO)")] -#[test_case("https://mozilla-intermediate.badssl.com/", Ok(()); "test Mozilla intermediate compatibility (TLS 1.3 preferred)")] -#[test_case("https://mozilla-modern.badssl.com/", Ok(()); "test Mozilla modern compatibility (TLS 1.3 required)")] -#[test_case("https://upgrade.badssl.com/", Ok(()); "test upgrade-insecure-requests")] -#[test_case("https://1000-sans.badssl.com/", Err(()); "test 1000-sans")] -#[test_case("https://10000-sans.badssl.com/", Err(()); "test 10000-sans")] -#[test_case("https://expired.badssl.com/", Err(()); "test expired")] -#[test_case("https://incomplete-chain.badssl.com/", Err(()); "test incomplete chain")] -#[test_case("https://no-common-name.badssl.com/", Err(()); "test no common name")] -#[test_case("https://no-subject.badssl.com/", Err(()); "test no subject")] -#[test_case("https://revoked.badssl.com/", Err(()); "test revoked")] -#[test_case("https://self-signed.badssl.com/", Err(()); "test self signed")] -#[test_case("https://untrusted-root.badssl.com/", Err(()); "test untrusted root")] -#[test_case("https://wrong.host.badssl.com/", Err(()); "test wrong host")] -#[test_case("https://no-sct.badssl.com/", Err(()); "test Signed Certificate Timestamp")] // NET::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED -#[cfg_attr(TODO, test_case("https://pinning-test.badssl.com/", Err(()); "test pinning test"))] // NET::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN -#[tokio::test] -async fn test_badssl(uri: &str, expected: Result<(), ()>) { - if expected.is_ok() { - assert_ok!(client::run_request(uri).await); - } else { - assert_err!(client::run_request(uri).await); - } -} - -mod client; diff --git a/tests-external/client.rs b/tests-external/client.rs deleted file mode 100644 index addae02..0000000 --- a/tests-external/client.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::str::FromStr; - -use http_body_util::{BodyExt, Empty}; -use hyper::{body::Bytes, Uri}; -use hyper_rustls::HttpsConnector; -use hyper_util::{ - client::legacy::{connect::HttpConnector, Client}, - rt::TokioExecutor, -}; -use rustls_rustcrypto::provider; - -pub fn build_hyper_client() -> anyhow::Result, Empty>> { - let https = hyper_rustls::HttpsConnectorBuilder::new() - .with_provider_and_webpki_roots(provider())? - .https_or_http() - .enable_all_versions() - .build(); - let client: Client<_, Empty> = Client::builder(TokioExecutor::new()).build(https); - - Ok(client) -} - -// I'm not sure how to exactly extract the hyper TLS error result to pinpoint -// what error it should match For now treating it as a grand result is alright -pub async fn run_request(uri: &str) -> anyhow::Result<()> { - let client = build_hyper_client()?; - let uri = Uri::from_str(uri)?; - let res = client.get(uri).await?; - - // We could definite check whether this is a HTML, but for now we don't really - // care about the body content - let bytes = res.into_body().collect().await?.to_bytes(); - - println!("{:?}", bytes); - - Ok(()) -} diff --git a/tests-external/generic.rs b/tests-external/generic.rs deleted file mode 100644 index 28f23da..0000000 --- a/tests-external/generic.rs +++ /dev/null @@ -1,30 +0,0 @@ -use claim::{assert_err, assert_ok}; -use test_case::test_case; - -// For the available tests check out here: https://badssl.com/dashboard/ - -#[test_case("https://codeforces.com/", Ok(()))] -#[test_case("https://crates.io/", Ok(()))] -#[test_case("https://doc.rust-lang.org/", Ok(()))] -#[test_case("https://github.com/", Ok(()))] -#[test_case("https://twitter.com/", Ok(()))] -#[test_case("https://wikipedia.org/", Ok(()))] -#[test_case("https://www.facebook.com/", Ok(()))] -#[test_case("https://www.google.com/", Ok(()))] -#[test_case("https://www.hackerrank.com/", Ok(()))] -#[test_case("https://www.instagram.com/", Ok(()))] -#[test_case("https://www.reddit.com/", Ok(()))] -#[test_case("https://stackoverflow.com/", Ok(()))] -#[test_case("https://www.youtube.com/", Ok(()))] -#[test_case("https://leetcode.com/", Ok(()))] -#[cfg_attr(feature = "tls12", test_case("https://www.topcoder.com/", Ok(())))] -#[tokio::test] -async fn test_generic_sites(uri: &str, expected: Result<(), ()>) { - if expected.is_ok() { - assert_ok!(client::run_request(uri).await); - } else { - assert_err!(client::run_request(uri).await); - } -} - -mod client; diff --git a/tests/builder.rs b/tests/builder.rs index bab5d85..ce0fdab 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -1,83 +1,163 @@ -use std::sync::Arc; +use std::io::{Read, Write}; +use std::sync::{Arc, OnceLock}; -use rustls::ClientConfig as RusTlsClientConfig; -use rustls::ServerConfig as RusTlsServerConfig; - -use rustls_rustcrypto::provider as rustcrypto_provider; - -mod fake_time; +use fake_cert_server_resolver::FakeServerCertResolver; use fake_time::FakeTime; - -mod fake_cert_server_verifier; -use fake_cert_server_verifier::FakeServerCertVerifier; - -mod fake_cert_client_verifier; -use fake_cert_client_verifier::FakeClientCertVerifier; +use itertools::iproduct; +use mem_socket::MemorySocket; +use rand_core::{OsRng, RngCore}; +use rustls::crypto::CryptoProvider; +use rustls::{ + ClientConfig as RusTlsClientConfig, RootCertStore, ServerConfig as RusTlsServerConfig, +}; +use rustls_rustcrypto::{provider as rustcrypto_provider, verify, Provider}; mod fake_cert_server_resolver; -use fake_cert_server_resolver::FakeServerCertResolver; +mod fake_time; -// Test integration between rustls and rustls in Client builder context -#[test] -fn integrate_client_builder_with_details_fake() { - let provider = rustcrypto_provider(); - let time_provider = FakeTime {}; +static SERVER_RESOLVER: OnceLock> = OnceLock::new(); - let fake_server_cert_verifier = FakeServerCertVerifier {}; +fn make_client_config(provider: CryptoProvider) -> RusTlsClientConfig { + let resolver = SERVER_RESOLVER.get_or_init(|| Arc::new(FakeServerCertResolver::new())); + let mut store = RootCertStore::empty(); - let builder_init = - RusTlsClientConfig::builder_with_details(Arc::new(provider), Arc::new(time_provider)); + store.add(resolver.rsa_root_cert()).unwrap(); + store.add(resolver.ecdsa_root_cert()).unwrap(); - let builder_default_versions = builder_init + RusTlsClientConfig::builder_with_details(Arc::new(provider), Arc::new(FakeTime {})) .with_safe_default_protocol_versions() - .expect("Default protocol versions error?"); + .expect("Default protocol versions error?") + .with_root_certificates(store) + // .dangerous() + // .with_custom_certificate_verifier(Arc::new(FakeServerCertVerifier {})) + .with_no_client_auth() +} - let dangerous_verifier = builder_default_versions - .dangerous() - .with_custom_certificate_verifier(Arc::new(fake_server_cert_verifier)); +fn make_server_config(provider: CryptoProvider) -> RusTlsServerConfig { + let resolver = SERVER_RESOLVER + .get_or_init(|| Arc::new(FakeServerCertResolver::new())) + .clone(); + RusTlsServerConfig::builder_with_details(Arc::new(provider), Arc::new(FakeTime {})) + .with_safe_default_protocol_versions() + .expect("Default protocol versions error?") + .with_no_client_auth() + .with_cert_resolver(resolver) +} +// Test integration between rustls and rustls in Client builder context +#[test] +fn integrate_client_builder_with_details_fake() { // Out of scope - let rustls_client_config = dangerous_verifier.with_no_client_auth(); + let rustls_client_config = make_client_config(rustcrypto_provider()); // RustCrypto is not fips assert!(!rustls_client_config.fips()); } -use rustls::DistinguishedName; - // Test integration between rustls and rustls in Server builder context #[test] fn integrate_server_builder_with_details_fake() { - let provider = rustcrypto_provider(); - let time_provider = FakeTime {}; - - let builder_init = - RusTlsServerConfig::builder_with_details(Arc::new(provider), Arc::new(time_provider)); - - let builder_default_versions = builder_init - .with_safe_default_protocol_versions() - .expect("Default protocol versions error?"); - - // A DistinguishedName is a Vec wrapped in internal types. - // DER or BER encoded Subject field from RFC 5280 for a single certificate. - // The Subject field is encoded as an RFC 5280 Name - //let b_wrap_in: &[u8] = b""; // TODO: should have constant somewhere - - let dummy_entry: &[u8] = b""; - - let client_dn = [DistinguishedName::in_sequence(dummy_entry)]; - - let client_cert_verifier = FakeClientCertVerifier { dn: client_dn }; + let rustls_server_config = make_server_config(rustcrypto_provider()); - let dangerous_verifier = - builder_default_versions.with_client_cert_verifier(Arc::new(client_cert_verifier)); + // RustCrypto is not fips + assert!(!rustls_server_config.fips()); +} - let server_cert_resolver = FakeServerCertResolver {}; +const CLIENT_MAGIC: &[u8; 18] = b"Hello from Client!"; +const SERVER_MAGIC: &[u8; 18] = b"Hello from Server!"; - // Out of scope - let rustls_client_config = - dangerous_verifier.with_cert_resolver(Arc::new(server_cert_resolver)); +// Test integration +#[test] +fn test_basic_round_trip() { + std::thread::scope(move |s| { + for provider in generate_providers() { + let base_name = format!( + "{:?}-{:?}", + provider.cipher_suites[0], provider.kx_groups[0] + ); + // Creates a pair of sockets that interconnect from client to server, and server to client + let (socket_c2s, socket_s2c) = MemorySocket::new_pair(); + + let mut random_data: [u8; 8192] = [0; 8192]; + OsRng.fill_bytes(&mut random_data); + + std::thread::Builder::new() + .name(format!("{base_name}-server")) + .spawn_scoped(s, { + let provider: CryptoProvider = provider.clone(); + move || { + let config = Arc::new(make_server_config(provider)); + let mut stream = socket_s2c; + let mut conn = rustls::ServerConnection::new(config.clone()) + .expect("failed to create server config"); + + let mut tls = rustls::Stream::new(&mut conn, &mut stream); + + { + let mut buf = [0; CLIENT_MAGIC.len()]; + tls.read_exact(&mut buf).unwrap(); + assert_eq!(&buf, CLIENT_MAGIC); + } + + tls.write_all(SERVER_MAGIC) + .expect("failed to write to client"); + tls.write_all(&random_data) + .expect("failed to write random data to client"); + tls.conn.send_close_notify(); + tls.flush().expect("failed to flush connection"); + } + }) + .unwrap(); + + std::thread::Builder::new() + .name(format!("{base_name}-client")) + .spawn_scoped(s, move || { + let mut sock = socket_c2s; + let server_name = "acme.com".try_into().expect("failed to get server name"); + let mut conn = rustls::ClientConnection::new( + Arc::new(make_client_config(provider)), + server_name, + ) + .expect("failed to create client config"); + let mut tls = rustls::Stream::new(&mut conn, &mut sock); + tls.write_all(CLIENT_MAGIC) + .expect("failed to write to server"); + + { + let mut buf = [0; SERVER_MAGIC.len()]; + tls.read_exact(&mut buf) + .expect("failed to read from server"); + assert_eq!(&buf, SERVER_MAGIC); + } + + { + let mut plaintext = Vec::new(); + tls.write_all(&random_data) + .expect("failed to write random data to server"); + tls.read_to_end(&mut plaintext) + .expect("failed to read from server"); + assert_eq!(plaintext, random_data); + } + }) + .unwrap(); + } + }); +} - // RustCrypto is not fips - assert!(!rustls_client_config.fips()); +fn generate_providers() -> impl Iterator { + let CryptoProvider { + cipher_suites, + kx_groups, + .. + } = rustcrypto_provider(); + + iproduct!(cipher_suites, kx_groups).map(|(cipher_suite, kx_group)| CryptoProvider { + cipher_suites: vec![cipher_suite], + kx_groups: vec![kx_group], + signature_verification_algorithms: verify::ALGORITHMS, + secure_random: &Provider, + key_provider: &Provider, + }) } + +mod mem_socket; diff --git a/tests/fake_cert_client_verifier.rs b/tests/fake_cert_client_verifier.rs deleted file mode 100644 index a8dc9cc..0000000 --- a/tests/fake_cert_client_verifier.rs +++ /dev/null @@ -1,71 +0,0 @@ -use rustls::DistinguishedName; -use rustls::Error; - -use rustls::SignatureScheme; - -use rustls::pki_types::CertificateDer; -use rustls::pki_types::UnixTime; -use rustls::DigitallySignedStruct; - -use rustls::client::danger::HandshakeSignatureValid; -use rustls::server::danger::ClientCertVerified; -use rustls::server::danger::ClientCertVerifier; - -#[derive(Debug)] -pub struct FakeClientCertVerifier { - pub dn: [DistinguishedName; 1], -} - -impl ClientCertVerifier for FakeClientCertVerifier { - fn root_hint_subjects(&self) -> &[DistinguishedName] { - &self.dn - } - fn verify_client_cert( - &self, - _end_entity: &CertificateDer<'_>, - _intermediates: &[CertificateDer<'_>], - _now: UnixTime, - ) -> Result { - Ok(ClientCertVerified::assertion()) - } - fn verify_tls12_signature( - &self, - _message: &[u8], - _cert: &CertificateDer<'_>, - _dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - fn verify_tls13_signature( - &self, - _message: &[u8], - _cert: &CertificateDer<'_>, - _dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - fn supported_verify_schemes(&self) -> Vec { - vec![ - SignatureScheme::RSA_PKCS1_SHA1, - SignatureScheme::ECDSA_SHA1_Legacy, - SignatureScheme::RSA_PKCS1_SHA256, - SignatureScheme::ECDSA_NISTP256_SHA256, - SignatureScheme::RSA_PKCS1_SHA384, - SignatureScheme::ECDSA_NISTP384_SHA384, - SignatureScheme::RSA_PKCS1_SHA512, - SignatureScheme::ECDSA_NISTP521_SHA512, - SignatureScheme::RSA_PSS_SHA256, - SignatureScheme::RSA_PSS_SHA384, - SignatureScheme::RSA_PSS_SHA512, - SignatureScheme::ED25519, - SignatureScheme::ED448, - //SignatureScheme::Unknown(u16), - ] - } - fn offer_client_auth(&self) -> bool { - true - } - fn client_auth_mandatory(&self) -> bool { - false - } -} diff --git a/tests/fake_cert_server_resolver.rs b/tests/fake_cert_server_resolver.rs index 7028c8b..55ce20b 100644 --- a/tests/fake_cert_server_resolver.rs +++ b/tests/fake_cert_server_resolver.rs @@ -1,15 +1,192 @@ +use core::time::Duration; +use std::str::FromStr; use std::sync::Arc; -use rustls::server::ClientHello; - -use rustls::server::ResolvesServerCert; +use der::asn1::{GeneralizedTime, Ia5String}; +use der::Encode; +use itertools::iproduct; +use pkcs8::{EncodePrivateKey, EncodePublicKey}; +use pki_types::{CertificateDer, PrivateKeyDer}; +use rand_core::{OsRng, RngCore}; +use rustls::server::{ClientHello, ResolvesServerCert}; use rustls::sign::CertifiedKey; +use rustls::CipherSuite::{ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, +}; +use rustls_rustcrypto::sign::any_supported_type; +use sha2::Sha256; +use signature::{Keypair, Signer}; +use spki::{SignatureAlgorithmIdentifier, SignatureBitStringEncoding, SubjectPublicKeyInfoOwned}; +use x509_cert::builder::{Builder, CertificateBuilder, Profile, RequestBuilder}; +use x509_cert::ext::pkix::name::GeneralName; +use x509_cert::ext::pkix::SubjectAltName; +use x509_cert::name::Name; +use x509_cert::serial_number::SerialNumber; +use x509_cert::time::{Time, Validity}; #[derive(Debug)] -pub struct FakeServerCertResolver; +pub struct FakeServerCertResolver { + rsa_cert_key: Arc, + ecdsa_cert_key: Arc, + rsa_root_cert: CertificateDer<'static>, + ecdsa_root_cert: CertificateDer<'static>, +} + +impl FakeServerCertResolver { + pub fn new() -> Self { + let (rsa_root_cert, rsa_root_key) = Self::generate_root_cert(|| { + // by running a binary search between 1024 bit and 2048 bit, 1034 bit is the first possible bit size after 1024 bit + rsa::pkcs1v15::SigningKey::::random(&mut OsRng, 1034).unwrap() + }); + let (ecdsa_root_cert, ecdsa_root_key) = + Self::generate_root_cert::<_, p256::ecdsa::DerSignature>(|| { + p256::ecdsa::SigningKey::random(&mut OsRng) + }); + + let (rsa_cert, rsa_key) = Self::generate_cert( + || { + // by running a binary search between 1024 bit and 2048 bit, 1034 bit is the first possible bit size after 1024 bit + rsa::pkcs1v15::SigningKey::::random(&mut OsRng, 1034).unwrap() + }, + rsa_root_key, + ); + let (ecdsa_cert, ecdsa_key) = Self::generate_cert::<_, _, p256::ecdsa::DerSignature>( + || p256::ecdsa::SigningKey::random(&mut OsRng), + ecdsa_root_key, + ); + + Self { + rsa_root_cert: rsa_root_cert.clone(), + ecdsa_root_cert: ecdsa_root_cert.clone(), + rsa_cert_key: Arc::new(CertifiedKey::new( + vec![rsa_cert], + any_supported_type(&rsa_key).unwrap(), + )), + ecdsa_cert_key: Arc::new(CertifiedKey::new( + vec![ecdsa_cert], + any_supported_type(&ecdsa_key).unwrap(), + )), + } + } + + pub fn rsa_root_cert(&self) -> CertificateDer<'static> { + self.rsa_root_cert.clone() + } + + pub fn ecdsa_root_cert(&self) -> CertificateDer<'static> { + self.ecdsa_root_cert.clone() + } + + fn generate_root_cert( + key_fn: impl Fn() -> Key, + ) -> (CertificateDer<'static>, Key) + where + Key: Signer + Keypair + SignatureAlgorithmIdentifier + EncodePrivateKey, + Signature: SignatureBitStringEncoding, + ::VerifyingKey: EncodePublicKey, + { + let signing_key = key_fn(); + ( + CertificateBuilder::new( + Profile::Root, + SerialNumber::from(OsRng.next_u64()), + Validity { + not_before: Time::GeneralTime( + GeneralizedTime::from_unix_duration(Duration::ZERO).unwrap(), + ), + not_after: Time::INFINITY, + }, + Name::from_str("CN=ACME Corporation CA,O=ACME Corporation,C=US").unwrap(), + SubjectPublicKeyInfoOwned::from_key(signing_key.verifying_key()).unwrap(), + &signing_key, + ) + .unwrap() + .build::() + .unwrap() + .to_der() + .unwrap() + .into(), + signing_key, + ) + } + fn generate_cert( + key_fn: impl Fn() -> Key, + ca_key: CaKey, + ) -> (CertificateDer<'static>, PrivateKeyDer<'static>) + where + Key: Signer + Keypair + SignatureAlgorithmIdentifier + EncodePrivateKey, + CaKey: Signer + Keypair + SignatureAlgorithmIdentifier + EncodePrivateKey, + Signature: SignatureBitStringEncoding, + ::VerifyingKey: EncodePublicKey, + ::VerifyingKey: EncodePublicKey, + { + let signing_key = key_fn(); + + let request = RequestBuilder::new(Name::from_str("CN=acme.com").unwrap(), &signing_key) + .unwrap() + .build() + .unwrap(); + + let mut builder = CertificateBuilder::new( + Profile::Leaf { + issuer: Name::from_str("CN=ACME Corporation CA,O=ACME Corporation,C=US").unwrap(), + enable_key_agreement: true, + enable_key_encipherment: true, + }, + SerialNumber::from(OsRng.next_u64()), + Validity { + not_before: Time::GeneralTime( + GeneralizedTime::from_unix_duration(Duration::ZERO).unwrap(), + ), + not_after: Time::INFINITY, + }, + request.info.subject, + request.info.public_key, + &ca_key, + ) + .unwrap(); + builder + .add_extension(&SubjectAltName(vec![GeneralName::DnsName( + Ia5String::new(b"acme.com").unwrap(), + )])) + .unwrap(); + ( + builder + .build::() + .unwrap() + .to_der() + .unwrap() + .into(), + PrivateKeyDer::Pkcs8( + signing_key + .to_pkcs8_der() + .unwrap() + .as_bytes() + .to_vec() + .into(), + ), + ) + } +} impl ResolvesServerCert for FakeServerCertResolver { - fn resolve(&self, _client_hello: ClientHello<'_>) -> Option> { - None + fn resolve(&self, hello: ClientHello<'_>) -> Option> { + Some( + if iproduct!( + [ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + ], + hello.cipher_suites() + ) + .any(|(a, &b)| a == b) + { + self.rsa_cert_key.clone() + } else { + self.ecdsa_cert_key.clone() + }, + ) } } diff --git a/tests/fake_cert_server_verifier.rs b/tests/fake_cert_server_verifier.rs deleted file mode 100644 index 2786058..0000000 --- a/tests/fake_cert_server_verifier.rs +++ /dev/null @@ -1,59 +0,0 @@ -use rustls::client::danger::HandshakeSignatureValid; -use rustls::client::danger::ServerCertVerified; -use rustls::client::danger::ServerCertVerifier; -use rustls::pki_types::CertificateDer; -use rustls::pki_types::ServerName; -use rustls::pki_types::UnixTime; -use rustls::DigitallySignedStruct; -use rustls::Error; -use rustls::SignatureScheme; - -#[derive(Debug)] -pub struct FakeServerCertVerifier; - -impl ServerCertVerifier for FakeServerCertVerifier { - fn verify_server_cert( - &self, - _end_entity: &CertificateDer<'_>, - _intermediates: &[CertificateDer<'_>], - _server_name: &ServerName<'_>, - _ocsp_response: &[u8], - _now: UnixTime, - ) -> Result { - Ok(ServerCertVerified::assertion()) - } - fn verify_tls12_signature( - &self, - _message: &[u8], - _cert: &CertificateDer<'_>, - _dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - fn verify_tls13_signature( - &self, - _message: &[u8], - _cert: &CertificateDer<'_>, - _dss: &DigitallySignedStruct, - ) -> Result { - Ok(HandshakeSignatureValid::assertion()) - } - fn supported_verify_schemes(&self) -> Vec { - vec![ - SignatureScheme::RSA_PKCS1_SHA1, - SignatureScheme::ECDSA_SHA1_Legacy, - SignatureScheme::RSA_PKCS1_SHA256, - SignatureScheme::ECDSA_NISTP256_SHA256, - SignatureScheme::RSA_PKCS1_SHA384, - SignatureScheme::ECDSA_NISTP384_SHA384, - SignatureScheme::RSA_PKCS1_SHA512, - SignatureScheme::ECDSA_NISTP521_SHA512, - SignatureScheme::RSA_PSS_SHA256, - SignatureScheme::RSA_PSS_SHA384, - SignatureScheme::RSA_PSS_SHA512, - SignatureScheme::ED25519, - SignatureScheme::ED448, - //SignatureScheme::Unknown(u16), - ] - } -} diff --git a/tests/fake_time.rs b/tests/fake_time.rs index 83dc3fa..12d82e9 100644 --- a/tests/fake_time.rs +++ b/tests/fake_time.rs @@ -1,3 +1,5 @@ +use core::time::Duration; + use rustls::pki_types::UnixTime; use rustls::time_provider::TimeProvider; @@ -6,6 +8,6 @@ pub struct FakeTime; impl TimeProvider for FakeTime { fn current_time(&self) -> Option { - None + Some(UnixTime::since_unix_epoch(Duration::ZERO)) } } diff --git a/tests/mem_socket.rs b/tests/mem_socket.rs new file mode 100644 index 0000000..a44182f --- /dev/null +++ b/tests/mem_socket.rs @@ -0,0 +1,103 @@ +use std::{ + io::{self, ErrorKind, Read, Write}, + sync::mpsc::{channel, Receiver, Sender}, +}; + +use bytes::{Buf, Bytes, BytesMut}; + +// The code is derived from: https://github.com/bmwill/memory-socket/blob/74110b18318c261e86d08aa53a7abe3e2a881538/src/lib.rs#L271-L405 +pub struct MemorySocket { + incoming: Receiver, + outgoing: Sender, + write_buffer: BytesMut, + current_buffer: Option, + seen_eof: bool, +} + +impl MemorySocket { + fn new(incoming: Receiver, outgoing: Sender) -> Self { + Self { + incoming, + outgoing, + write_buffer: BytesMut::new(), + current_buffer: None, + seen_eof: false, + } + } + + pub fn new_pair() -> (Self, Self) { + let (a_tx, a_rx) = channel(); + let (b_tx, b_rx) = channel(); + let a = Self::new(a_rx, b_tx); + let b = Self::new(b_rx, a_tx); + + (a, b) + } +} + +impl Read for MemorySocket { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let mut bytes_read = 0; + + loop { + // If we've already filled up the buffer then we can return + if bytes_read == buf.len() { + return Ok(bytes_read); + } + + match self.current_buffer { + // We still have data to copy to `buf` + Some(ref mut current_buffer) if current_buffer.has_remaining() => { + let bytes_to_read = + core::cmp::min(buf.len() - bytes_read, current_buffer.remaining()); + debug_assert!(bytes_to_read > 0); + + current_buffer + .take(bytes_to_read) + .copy_to_slice(&mut buf[bytes_read..(bytes_read + bytes_to_read)]); + bytes_read += bytes_to_read; + } + + // Either we've exhausted our current buffer or we don't have one + _ => { + // If we've read anything up to this point return the bytes read + if bytes_read > 0 { + return Ok(bytes_read); + } + + self.current_buffer = match self.incoming.recv() { + Ok(buf) => Some(buf), + + // The remote side hung up, if this is the first time we've seen EOF then + // we should return `Ok(0)` otherwise an UnexpectedEof Error + Err(_) => { + if self.seen_eof { + return Err(ErrorKind::UnexpectedEof.into()); + } else { + self.seen_eof = true; + return Ok(0); + } + } + } + } + } + } + } +} + +impl Write for MemorySocket { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.write_buffer.extend_from_slice(buf); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + if !self.write_buffer.is_empty() { + self.outgoing + .send(self.write_buffer.split().freeze()) + .map_err(|_| ErrorKind::BrokenPipe.into()) + } else { + Ok(()) + } + } +} diff --git a/validation/local_ping_pong_openssl/Cargo.lock b/validation/local_ping_pong_openssl/Cargo.lock index d6eebf0..bed8abb 100644 --- a/validation/local_ping_pong_openssl/Cargo.lock +++ b/validation/local_ping_pong_openssl/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "aead" @@ -70,6 +70,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "byteorder" version = "1.5.0" @@ -82,6 +88,18 @@ version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +[[package]] +name = "ccm" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae3c82e4355234767756212c570e29833699ab63e6ffd161887314cc5b43847" +dependencies = [ + "aead", + "cipher", + "ctr", + "subtle", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -328,8 +346,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -380,6 +400,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "js-sys" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -411,6 +441,12 @@ dependencies = [ "rustls-rustcrypto", ] +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -532,11 +568,24 @@ dependencies = [ "sha2", ] +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pem-rfc7468" @@ -703,9 +752,9 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +checksum = "47c75d7c5c6b673e58bf54d8544a9f432e3a925b0e80f7cd3602ab5c50c55519" dependencies = [ "const-oid", "digest", @@ -733,9 +782,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.5" +version = "0.23.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afabcee0551bd1aa3e18e5adbf2c0544722014b899adb31bd186ec638d3da97e" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" dependencies = [ "once_cell", "rustls-pki-types", @@ -746,26 +795,31 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.5.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" [[package]] name = "rustls-rustcrypto" -version = "0.1.0" +version = "0.0.2-alpha" dependencies = [ "aead", + "aes", "aes-gcm", + "ccm", "chacha20poly1305", "crypto-common", "der", "digest", "ecdsa", "ed25519-dalek", + "getrandom", "hmac", "p256", "p384", + "p521", "paste", + "pkcs1", "pkcs8", "rand_core", "rsa", @@ -780,9 +834,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.102.3" +version = "0.102.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" dependencies = [ "ring", "rustls-pki-types", @@ -941,6 +995,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/validation/local_ping_pong_openssl/src/lib.rs b/validation/local_ping_pong_openssl/src/lib.rs index e4b659d..a76579f 100644 --- a/validation/local_ping_pong_openssl/src/lib.rs +++ b/validation/local_ping_pong_openssl/src/lib.rs @@ -74,7 +74,6 @@ mod test { } #[test] - #[should_panic] // no_shared_cipher fn vs_openssl_as_client_ccm_sha256() { let cipher_suites = OpenSslCipherSuites { TLS_AES_128_GCM_SHA256: false, @@ -87,7 +86,6 @@ mod test { } #[test] - #[should_panic] // no_shared_cipher fn vs_openssl_as_client_ccm8_sha256() { let cipher_suites = OpenSslCipherSuites { TLS_AES_128_GCM_SHA256: false, @@ -118,7 +116,6 @@ mod test { vs_openssl_as_client(group_list, OpenSslCipherSuites::default()); } #[test] - #[should_panic] // no support fn vs_openssl_as_client_group_p521() { let mut group_list = OpenSslGroupsList::all_false(); group_list.P521 = true; @@ -131,7 +128,6 @@ mod test { vs_openssl_as_client(group_list, OpenSslCipherSuites::default()); } #[test] - #[should_panic] // no support fn vs_openssl_as_client_group_x448() { let mut group_list = OpenSslGroupsList::all_false(); group_list.X448 = true;