diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 86943009..0accfb55 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,6 +19,9 @@ jobs: - name: Build (no features enabled) run: cargo build --verbose + - name: Build (OpenSSL only) + run: cargo build --no-default-features --features openssl --verbose + - name: Build (all features enabled) run: cargo build --verbose --all-features @@ -39,6 +42,9 @@ jobs: - name: Clippy (no features enabled) run: cargo clippy -- -D warnings + - name: Clippy (OpenSSL only) + run: cargo clippy --no-default-features --features openssl -- -D warnings + - name: Clippy (all features enabled) run: cargo clippy --all-features -- -D warnings @@ -55,6 +61,13 @@ jobs: env: RUST_BACKTRACE: 1 + - name: Test (OpenSSL only) + run: | + eval `ssh-agent` + cargo test --verbose --no-default-features --features openssl + env: + RUST_BACKTRACE: 1 + - name: Test (all features enabled) run: | eval `ssh-agent` diff --git a/.gitignore b/.gitignore index abb7748a..c9992760 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ target Cargo.lock .cargo-ok +/.devcontainer diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..15c0c271 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1823 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aho-corasick" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" + +[[package]] +name = "async-trait" +version = "0.1.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + +[[package]] +name = "bcrypt-pbkdf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3806a8db60cf56efee531616a34a6aaa9a114d6da2add861b0fa4a188881b2c7" +dependencies = [ + "blowfish", + "pbkdf2", + "sha2 0.10.6", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[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.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[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 = "block-padding" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] + +[[package]] +name = "blowfish" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" +dependencies = [ + "byteorder", + "cipher", +] + +[[package]] +name = "bumpalo" +version = "3.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b1ce199063694f33ffb7dd4e0ee620741495c32833cde5aa08f02a0bf96f0c8" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" + +[[package]] +name = "cbc" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[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 = "chrono" +version = "0.4.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-integer", + "num-traits", + "time", + "wasm-bindgen", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[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 0.6.4", + "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 = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "cxx" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61f1b6389c3fe1c316bf8a4dccc90a38208354b330925bce1f74a6c4756eb93" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12cee708e8962df2aeb38f594aae5d827c022b6460ac71a7a3e2c3c2aae5a07b" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.15", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7944172ae7e4068c533afbb984114a56c46e9ccddda550499caa222902c7f7bb" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2345488264226bf682893e25de0769f3360aac9957980ec49361b083ddaa5bc5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "data-encoding" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.9", + "zeroize", +] + +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "flate2" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[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-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[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 = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + +[[package]] +name = "hex-literal" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebdb29d2ea9ed0083cd8cece49bbd968021bd99b0849edb4a9a7ee0fdf6a4e0" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.142" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" + +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "md5" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[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.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +dependencies = [ + "libc", + "log", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.45.0", +] + +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "rand 0.8.5", +] + +[[package]] +name = "num-complex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e0d21255c828d6f128a1e41534206671e8c3ea0c62f32291e808dc82cff17d" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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-rational" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "openssl" +version = "0.10.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b8574602df80f7b85fdfc5392fa884a4e3b3f4f35402c070ab34c3d3f78d56" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "openssl-src" +version = "111.25.3+1.1.1t" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924757a6a226bf60da5f7dd0311a34d2b52283dd82ddeb103208ddc66362f80c" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e17f59264b2809d77ae94f0e1ebabc434773f370d6ca667bd223ea10e06cc7e" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "password-hash" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +dependencies = [ + "digest 0.10.6", + "hmac", + "password-hash", + "sha2 0.10.6", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +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 = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[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 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.9", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom 0.2.9", + "redox_syscall", + "thiserror", +] + +[[package]] +name = "regex" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af83e617f331cc6ae2da5443c602dfa5af81e517212d9d611a5b3ba1777b5370" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5996294f19bd3aae0453a862ad728f60e6600695733dd5df01da90c54363a3c" + +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "russh" +version = "0.37.1" +dependencies = [ + "aes", + "aes-gcm", + "anyhow", + "async-trait", + "bitflags", + "byteorder", + "chacha20", + "ctr", + "curve25519-dalek", + "digest 0.10.6", + "env_logger", + "flate2", + "futures", + "generic-array", + "hex-literal", + "hmac", + "log", + "num-bigint", + "once_cell", + "openssl", + "poly1305", + "rand 0.8.5", + "russh-cryptovec", + "russh-keys", + "russh-sftp", + "sha1", + "sha2 0.10.6", + "subtle", + "thiserror", + "tokio", + "tokio-util", +] + +[[package]] +name = "russh-config" +version = "0.7.0" +dependencies = [ + "dirs-next", + "futures", + "log", + "thiserror", + "tokio", + "whoami", +] + +[[package]] +name = "russh-cryptovec" +version = "0.7.0" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "russh-keys" +version = "0.37.1" +dependencies = [ + "aes", + "bcrypt-pbkdf", + "bit-vec", + "block-padding", + "byteorder", + "cbc", + "ctr", + "data-encoding", + "dirs", + "ed25519-dalek", + "env_logger", + "futures", + "hmac", + "inout", + "log", + "md5", + "num-bigint", + "num-integer", + "openssl", + "pbkdf2", + "rand 0.7.3", + "rand_core 0.5.1", + "russh-cryptovec", + "serde", + "sha2 0.10.6", + "tempdir", + "thiserror", + "tokio", + "tokio-stream", + "yasna", +] + +[[package]] +name = "russh-sftp" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a3325f5b4dcf6e9392008a8ea57ce16e42ed87291a319e95d60614c473e1ca" +dependencies = [ + "async-trait", + "bitflags", + "bytes", + "chrono", + "log", + "num", + "num-derive", + "num-traits", + "thiserror", + "tokio", +] + +[[package]] +name = "scratch" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1" + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "sha2" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if", + "cpufeatures", + "digest 0.9.0", + "opaque-debug", +] + +[[package]] +name = "sha2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest 0.10.6", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + +[[package]] +name = "slab" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +dependencies = [ + "autocfg", +] + +[[package]] +name = "socket2" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempdir" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" +dependencies = [ + "rand 0.4.6", + "remove_dir_all", +] + +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tokio" +version = "1.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c786bf8134e5a3a166db9b29ab8f48134739014a3eca7bc6bfa95d673b136f" +dependencies = [ + "autocfg", + "bytes", + "libc", + "mio", + "num_cpus", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[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 2.0.15", +] + +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "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", +] + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-width" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" + +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +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.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "whoami" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c70234412ca409cc04e864e89523cb0fc37f5e1344ebed5a3ebf4192b6b9f68" +dependencies = [ + "wasm-bindgen", + "web-sys", +] + +[[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-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[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" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "yasna" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" +dependencies = [ + "bit-vec", + "num-bigint", +] + +[[package]] +name = "zeroize" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +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 2.0.15", +] diff --git a/russh-config/src/lib.rs b/russh-config/src/lib.rs index cdf4d95a..3f422fd1 100644 --- a/russh-config/src/lib.rs +++ b/russh-config/src/lib.rs @@ -94,18 +94,16 @@ pub fn parse_path>(path: P, host: &str) -> Result } #[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Default)] pub enum AddKeysToAgent { Yes, Confirm, Ask, + #[default] No, } -impl Default for AddKeysToAgent { - fn default() -> Self { - AddKeysToAgent::No - } -} + pub fn parse(file: &str, host: &str) -> Result { let mut config: Option = None; diff --git a/russh-keys/Cargo.toml b/russh-keys/Cargo.toml index 23a845d1..ae293984 100644 --- a/russh-keys/Cargo.toml +++ b/russh-keys/Cargo.toml @@ -28,25 +28,25 @@ repository = "https://github.com/warp-tech/russh" version = "0.37.1" [dependencies] -aes = "0.8" +aes = { version = "0.8", optional = true } bcrypt-pbkdf = "0.9" bit-vec = "0.6" -cbc = "0.1" -ctr = "0.9" -block-padding = { version = "0.3", features = ["std"] } +cbc = { version = "0.1", optional = true } +ctr = { version = "0.9", optional = true } +block-padding = { version = "0.3", features = ["std"], optional = true } byteorder = "1.4" data-encoding = "2.3" dirs = "4.0" -ed25519-dalek = "1.0" +ed25519-dalek = { version = "1.0", optional = true } futures = "0.3" -hmac = "0.12" +hmac = { version = "0.12", optional = true } inout = { version = "0.1", features = ["std"] } log = "0.4" md5 = "0.7" num-bigint = "0.4" num-integer = "0.1" -openssl = { version = "0.10", optional = true } -pbkdf2 = "0.11" +openssl = { version = "^0.10.40", optional = true } +pbkdf2 = { version = "0.11", optional = true } rand = "0.7" rand_core = { version = "0.5", features = ["std"] } russh-cryptovec = { version = "0.7.0", path = "../cryptovec" } @@ -63,7 +63,17 @@ tokio-stream = { version = "0.1", features = ["net"] } yasna = { version = "0.5.0", features = ["bit-vec", "num-bigint"] } [features] +default = ["rs-crypto"] vendored-openssl = ["openssl", "openssl/vendored"] +rs-crypto = [ + "dep:aes", + "dep:cbc", + "dep:ctr", + "dep:block-padding", + "dep:hmac", + "dep:pbkdf2", + "dep:ed25519-dalek", +] [dev-dependencies] env_logger = "0.9" diff --git a/russh-keys/src/agent/client.rs b/russh-keys/src/agent/client.rs index 0358fb8b..96e65335 100644 --- a/russh-keys/src/agent/client.rs +++ b/russh-keys/src/agent/client.rs @@ -100,6 +100,7 @@ impl AgentClient { self.buf.push(msg::ADD_ID_CONSTRAINED) } match *key { + #[cfg(feature = "rs-crypto")] key::KeyPair::Ed25519(ref pair) => { self.buf.extend_ssh_string(b"ssh-ed25519"); self.buf.extend_ssh_string(pair.public.as_bytes()); @@ -262,6 +263,7 @@ impl AgentClient { hash: SignatureHash::SHA2_512, }) } + #[cfg(feature = "rs-crypto")] b"ssh-ed25519" => keys.push(PublicKey::Ed25519( ed25519_dalek::PublicKey::from_bytes(r.read_string()?)?, )), @@ -319,6 +321,7 @@ impl AgentClient { key_blob(public, &mut self.buf)?; self.buf.extend_ssh_string(data); debug!("public = {:?}", public); + #[allow(unreachable_patterns)] // may not be unreachable depending on build flags let hash = match public { #[cfg(feature = "openssl")] PublicKey::RSA { hash, .. } => match hash { @@ -515,6 +518,7 @@ fn key_blob(public: &key::PublicKey, buf: &mut CryptoVec) -> Result<(), Error> { #[allow(clippy::indexing_slicing)] // length is known BigEndian::write_u32(&mut buf[5..], (len1 - len0) as u32); } + #[cfg(feature = "rs-crypto")] PublicKey::Ed25519(ref p) => { buf.extend(&[0, 0, 0, 0]); let len0 = buf.len(); diff --git a/russh-keys/src/agent/server.rs b/russh-keys/src/agent/server.rs index d4f80eba..4690e817 100644 --- a/russh-keys/src/agent/server.rs +++ b/russh-keys/src/agent/server.rs @@ -233,9 +233,11 @@ impl Result { + #[cfg(feature = "rs-crypto")] let pos0 = r.position; let t = r.read_string()?; let (blob, key) = match t { + #[cfg(feature = "rs-crypto")] b"ssh-ed25519" => { let public_ = r.read_string()?; let pos1 = r.position; @@ -357,7 +359,7 @@ impl) -> Result) -> Result { - #[allow(clippy::unwrap_used)] // parameters are static - let cipher = cbc::Decryptor::::new_from_slices(key, iv).unwrap(); - let n = cipher.decrypt_padded_mut::(&mut dec)?.len(); - dec.truncate(n) - } - b"aes256-cbc" => { - #[allow(clippy::unwrap_used)] // parameters are static - let cipher = cbc::Decryptor::::new_from_slices(key, iv).unwrap(); - let n = cipher.decrypt_padded_mut::(&mut dec)?.len(); - dec.truncate(n) - } - b"aes128-ctr" => { - #[allow(clippy::unwrap_used)] // parameters are static - let mut cipher = Ctr64BE::::new_from_slices(key, iv).unwrap(); - cipher.apply_keystream(&mut dec); - dec.truncate(secret_key.len()) + #[cfg(feature = "rs-crypto")] + { + use aes::cipher::block_padding::NoPadding; + use aes::cipher::{BlockDecryptMut, KeyIvInit, StreamCipher}; + use aes::*; + use ctr::Ctr64BE; + + match ciphername { + b"aes128-cbc" => { + #[allow(clippy::unwrap_used)] // parameters are static + let cipher = cbc::Decryptor::::new_from_slices(key, iv).unwrap(); + let n = cipher.decrypt_padded_mut::(&mut dec)?.len(); + dec.truncate(n) + } + b"aes256-cbc" => { + #[allow(clippy::unwrap_used)] // parameters are static + let cipher = cbc::Decryptor::::new_from_slices(key, iv).unwrap(); + let n = cipher.decrypt_padded_mut::(&mut dec)?.len(); + dec.truncate(n) + } + b"aes128-ctr" => { + #[allow(clippy::unwrap_used)] // parameters are static + let mut cipher = Ctr64BE::::new_from_slices(key, iv).unwrap(); + cipher.apply_keystream(&mut dec); + dec.truncate(secret_key.len()) + } + b"aes256-ctr" => { + #[allow(clippy::unwrap_used)] // parameters are static + let mut cipher = Ctr64BE::::new_from_slices(key, iv).unwrap(); + cipher.apply_keystream(&mut dec); + dec.truncate(secret_key.len()) + } + _ => {} } - b"aes256-ctr" => { - #[allow(clippy::unwrap_used)] // parameters are static - let mut cipher = Ctr64BE::::new_from_slices(key, iv).unwrap(); - cipher.apply_keystream(&mut dec); - dec.truncate(secret_key.len()) + } + #[cfg(all(feature = "openssl", not(feature = "rs-crypto")))] + { + use openssl::symm::{decrypt, Cipher}; + dec = match ciphername { + b"aes128-cbc" => decrypt(Cipher::aes_128_cbc(), key, Some(iv), &dec)?, + b"aes256-cbc" => decrypt(Cipher::aes_256_cbc(), key, Some(iv), &dec)?, + b"aes128-ctr" => decrypt(Cipher::aes_128_ctr(), key, Some(iv), &dec)?, + b"aes256-ctr" => decrypt(Cipher::aes_256_ctr(), key, Some(iv), &dec)?, + _ => dec, } - _ => {} } Ok(dec) } else { diff --git a/russh-keys/src/format/pkcs5.rs b/russh-keys/src/format/pkcs5.rs index 0e5a2a5e..0cc5ede0 100644 --- a/russh-keys/src/format/pkcs5.rs +++ b/russh-keys/src/format/pkcs5.rs @@ -1,5 +1,3 @@ -use aes::*; - use super::Encryption; use crate::{key, Error}; @@ -11,8 +9,7 @@ pub fn decode_pkcs5( password: Option<&str>, enc: Encryption, ) -> Result { - use aes::cipher::{BlockDecryptMut, KeyIvInit}; - use block_padding::Pkcs7; + use openssl::symm::{decrypt, Cipher}; if let Some(pass) = password { let sec = match enc { @@ -23,10 +20,7 @@ pub fn decode_pkcs5( let md5 = c.compute(); #[allow(clippy::unwrap_used)] // AES parameters are static - let c = cbc::Decryptor::::new_from_slices(&md5.0, &iv[..]).unwrap(); - let mut dec = secret.to_vec(); - c.decrypt_padded_mut::(&mut dec)?; - dec + decrypt(Cipher::aes_128_cbc(), &md5.0, Some(&iv[..]), secret)? } Encryption::Aes256Cbc(_) => unimplemented!(), }; diff --git a/russh-keys/src/format/pkcs8.rs b/russh-keys/src/format/pkcs8.rs index 8e05c1c3..39331d63 100644 --- a/russh-keys/src/format/pkcs8.rs +++ b/russh-keys/src/format/pkcs8.rs @@ -1,14 +1,10 @@ -use std::borrow::Cow; - -use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit}; +#[cfg(feature = "rs-crypto")] use bit_vec::BitVec; -use block_padding::{NoPadding, Pkcs7}; #[cfg(feature = "openssl")] use openssl::pkey::Private; #[cfg(feature = "openssl")] use openssl::rsa::Rsa; -#[cfg(test)] -use rand_core::OsRng; +use std::borrow::Cow; use yasna::BERReaderSeq; use {std, yasna}; @@ -116,6 +112,7 @@ fn asn1_read_aes256cbc( Ok(Ok(Encryption::Aes256Cbc(i))) } +#[cfg(feature = "rs-crypto")] fn write_key_v1(writer: &mut yasna::DERWriterSeq, secret: &ed25519_dalek::SecretKey) { let public = ed25519_dalek::PublicKey::from(secret); writer.next().write_u32(1); @@ -144,26 +141,30 @@ fn read_key_v1(reader: &mut BERReaderSeq) -> Result { let oid = reader .next() .read_sequence(|reader| reader.next().read_oid())?; - if oid.components().as_slice() == ED25519 { - use ed25519_dalek::{Keypair, PublicKey, SecretKey}; - let secret = { - let s = yasna::parse_der(&reader.next().read_bytes()?, |reader| reader.read_bytes())?; - - s.get(..ed25519_dalek::SECRET_KEY_LENGTH) - .ok_or(Error::KeyIsCorrupt) - .and_then(|s| SecretKey::from_bytes(s).map_err(|_| Error::CouldNotReadKey))? - }; - let public = { - let public = reader - .next() - .read_tagged(yasna::Tag::context(1), |reader| reader.read_bitvec())? - .to_bytes(); - PublicKey::from_bytes(&public).map_err(|_| Error::CouldNotReadKey)? - }; - Ok(key::KeyPair::Ed25519(Keypair { public, secret })) - } else { - Err(Error::CouldNotReadKey) + if oid.components().as_slice() == ED25519 && cfg!(feature = "rs-crypto") { + #[cfg(feature = "rs-crypto")] + { + use ed25519_dalek::{Keypair, PublicKey, SecretKey}; + let secret = { + let s = + yasna::parse_der(&reader.next().read_bytes()?, |reader| reader.read_bytes())?; + + s.get(..ed25519_dalek::SECRET_KEY_LENGTH) + .ok_or(Error::KeyIsCorrupt) + .and_then(|s| SecretKey::from_bytes(s).map_err(|_| Error::CouldNotReadKey))? + }; + let public = { + let public = reader + .next() + .read_tagged(yasna::Tag::context(1), |reader| reader.read_bitvec())? + .to_bytes(); + PublicKey::from_bytes(&public).map_err(|_| Error::CouldNotReadKey)? + }; + return Ok(key::KeyPair::Ed25519(Keypair { public, secret })); + } } + + Err(Error::CouldNotReadKey) } #[cfg(feature = "openssl")] @@ -254,7 +255,10 @@ fn read_key_v0(_: &mut BERReaderSeq) -> Result { } #[test] +#[allow(clippy::unwrap_used)] +#[cfg(feature = "rs-crypto")] fn test_read_write_pkcs8() { + use rand_core::OsRng; let ed25519_dalek::Keypair { public, secret } = ed25519_dalek::Keypair::generate(&mut OsRng {}); assert_eq!( public.as_bytes(), @@ -271,9 +275,54 @@ fn test_read_write_pkcs8() { } } -use aes::*; use yasna::models::ObjectIdentifier; +#[cfg(feature = "rs-crypto")] +fn pbkdf2(password: &[u8], salt: &[u8], rounds: u32, key: &mut [u8]) -> Result<(), Error> { + pbkdf2::pbkdf2::>(password, salt, rounds, key); + Ok(()) +} + +#[cfg(all(feature = "openssl", not(feature = "rs-crypto")))] +fn pbkdf2(password: &[u8], salt: &[u8], rounds: u32, key: &mut [u8]) -> Result<(), Error> { + openssl::pkcs5::pbkdf2_hmac( + password, + salt, + rounds as usize, + openssl::hash::MessageDigest::sha256(), + key, + )?; + Ok(()) +} + +#[cfg(all(feature = "openssl", not(feature = "rs-crypto")))] +fn encrypt_key(key: &[u8], iv: &[u8], plaintext: &mut [u8]) -> Result, Error> { + use openssl::cipher::Cipher; + use openssl::cipher_ctx::*; + + let mut ctx = CipherCtx::new()?; + ctx.encrypt_init(Some(Cipher::aes_256_cbc()), Some(key), Some(iv))?; + ctx.set_padding(false); + + let mut output = vec![]; + ctx.cipher_update_vec(plaintext, &mut output)?; + ctx.cipher_final_vec(&mut output)?; + Ok(output) +} + +#[cfg(feature = "rs-crypto")] +fn encrypt_key(key: &[u8], iv: &[u8], plaintext: &mut [u8]) -> Result, Error> { + use aes::cipher::{BlockEncryptMut, KeyIvInit}; + use aes::*; + use block_padding::NoPadding; + + #[allow(clippy::unwrap_used)] // parameters are static + let c = cbc::Encryptor::::new_from_slices(key, iv).unwrap(); + let n = plaintext.len(); + let enc = c.encrypt_padded_mut::(plaintext, n)?; + Ok(enc.to_vec()) +} + /// Encode a password-protected PKCS#8-encoded private key. pub fn encode_pkcs8_encrypted( pass: &[u8], @@ -287,16 +336,12 @@ pub fn encode_pkcs8_encrypted( let mut iv = [0; 16]; rng.fill_bytes(&mut iv); let mut dkey = [0; 32]; // AES256-CBC - pbkdf2::pbkdf2::>(pass, &salt, rounds, &mut dkey); + pbkdf2(pass, &salt, rounds, &mut dkey)?; let mut plaintext = encode_pkcs8(key); let padding_len = 32 - (plaintext.len() % 32); plaintext.extend(std::iter::repeat(padding_len as u8).take(padding_len)); - - #[allow(clippy::unwrap_used)] // parameters are static - let c = cbc::Encryptor::::new_from_slices(&dkey, &iv).unwrap(); - let n = plaintext.len(); - let encrypted = c.encrypt_padded_mut::(&mut plaintext, n)?; + let encrypted = encrypt_key(&dkey, &iv, &mut plaintext)?; Ok(yasna::construct_der(|writer| { writer.write_sequence(|writer| { @@ -308,7 +353,7 @@ pub fn encode_pkcs8_encrypted( asn1_write_pbes2(writer.next(), rounds as u64, &salt, &iv) }); // Ciphertext - writer.next().write_bytes(encrypted) + writer.next().write_bytes(&encrypted) }) })) } @@ -317,6 +362,7 @@ pub fn encode_pkcs8_encrypted( pub fn encode_pkcs8(key: &key::KeyPair) -> Vec { yasna::construct_der(|writer| { writer.write_sequence(|writer| match *key { + #[cfg(feature = "rs-crypto")] key::KeyPair::Ed25519(ref pair) => write_key_v1(writer, &pair.secret), #[cfg(feature = "openssl")] key::KeyPair::RSA { ref key, .. } => write_key_v0(writer, key), @@ -377,7 +423,7 @@ impl KeyDerivation { fn derive(&self, password: &[u8], key: &mut [u8]) -> Result<(), Error> { match *self { KeyDerivation::Pbkdf2 { ref salt, rounds } => { - pbkdf2::pbkdf2::>(password, salt, rounds as u32, key) + pbkdf2(password, salt, rounds as u32, key)? // pbkdf2_hmac(password, salt, rounds as usize, digest, key)? } } @@ -418,7 +464,11 @@ impl Encryption { } } + #[cfg(feature = "rs-crypto")] fn decrypt(&self, key: &[u8], ciphertext: &[u8]) -> Result, Error> { + use aes::cipher::{BlockDecryptMut, KeyIvInit}; + use aes::*; + use block_padding::Pkcs7; match *self { Encryption::Aes128Cbc(ref iv) => { #[allow(clippy::unwrap_used)] // parameters are static @@ -434,6 +484,19 @@ impl Encryption { } } } + + #[cfg(all(feature = "openssl", not(feature = "rs-crypto")))] + fn decrypt(&self, key: &[u8], ciphertext: &[u8]) -> Result, Error> { + use openssl::symm::{decrypt, Cipher}; + match *self { + Encryption::Aes128Cbc(ref iv) => { + Ok(decrypt(Cipher::aes_128_cbc(), key, Some(iv), ciphertext)?) + } + Encryption::Aes256Cbc(ref iv) => { + Ok(decrypt(Cipher::aes_256_cbc(), key, Some(iv), ciphertext)?) + } + } + } } enum KeyDerivation { diff --git a/russh-keys/src/key.rs b/russh-keys/src/key.rs index 4270da09..7fbe167e 100644 --- a/russh-keys/src/key.rs +++ b/russh-keys/src/key.rs @@ -13,11 +13,8 @@ // limitations under the License. // use serde::{Serialize, Deserialize}; -use ed25519_dalek::ed25519::signature::Signature as EdSignature; -use ed25519_dalek::{Signer, Verifier}; #[cfg(feature = "openssl")] use openssl::pkey::{Private, Public}; -use rand::rngs::OsRng; use russh_cryptovec::CryptoVec; use crate::encoding::{Encoding, Reader}; @@ -109,6 +106,7 @@ impl SignatureHash { #[derive(Eq, Debug, Clone)] pub enum PublicKey { #[doc(hidden)] + #[cfg(feature = "rs-crypto")] Ed25519(ed25519_dalek::PublicKey), #[doc(hidden)] #[cfg(feature = "openssl")] @@ -123,8 +121,9 @@ impl PartialEq for PublicKey { match (self, other) { #[cfg(feature = "openssl")] (Self::RSA { key: a, .. }, Self::RSA { key: b, .. }) => a == b, + #[cfg(feature = "rs-crypto")] (Self::Ed25519(a), Self::Ed25519(b)) => a == b, - #[cfg(feature = "openssl")] + #[allow(unreachable_patterns)] _ => false, } } @@ -137,7 +136,6 @@ pub struct OpenSSLPKey(pub openssl::pkey::PKey); #[cfg(feature = "openssl")] use std::cmp::{Eq, PartialEq}; -use std::convert::TryInto; #[cfg(feature = "openssl")] impl PartialEq for OpenSSLPKey { fn eq(&self, b: &OpenSSLPKey) -> bool { @@ -157,6 +155,7 @@ impl PublicKey { /// Parse a public key in SSH format. pub fn parse(algo: &[u8], pubkey: &[u8]) -> Result { match algo { + #[cfg(feature = "rs-crypto")] b"ssh-ed25519" => { let mut p = pubkey.reader(0); let key_algo = p.read_string()?; @@ -208,6 +207,7 @@ impl PublicKey { /// Algorithm name for that key. pub fn name(&self) -> &'static str { match *self { + #[cfg(feature = "rs-crypto")] PublicKey::Ed25519(_) => ED25519.0, #[cfg(feature = "openssl")] PublicKey::RSA { ref hash, .. } => hash.name().0, @@ -217,9 +217,14 @@ impl PublicKey { /// Verify a signature. pub fn verify_detached(&self, buffer: &[u8], sig: &[u8]) -> bool { match self { - PublicKey::Ed25519(ref public) => ed25519_dalek::Signature::from_bytes(sig) - .and_then(|sig| public.verify(buffer, &sig)) - .is_ok(), + #[cfg(feature = "rs-crypto")] + PublicKey::Ed25519(ref public) => { + use ed25519_dalek::Verifier; + + ed25519_dalek::Signature::from_bytes(sig) + .and_then(|sig| public.verify(buffer, &sig)) + .is_ok() + } #[cfg(feature = "openssl")] PublicKey::RSA { ref key, ref hash } => { @@ -246,6 +251,7 @@ impl PublicKey { #[cfg(feature = "openssl")] pub fn set_algorithm(&mut self, algorithm: &[u8]) { + #[allow(irrefutable_let_patterns)] // depending on the build flag, it may be refutable if let PublicKey::RSA { ref mut hash, .. } = self { if algorithm == b"rsa-sha2-512" { *hash = SignatureHash::SHA2_512 @@ -273,6 +279,7 @@ impl Verify for PublicKey { /// Public key exchange algorithms. #[allow(clippy::large_enum_variant)] pub enum KeyPair { + #[cfg(feature = "rs-crypto")] Ed25519(ed25519_dalek::Keypair), #[cfg(feature = "openssl")] RSA { @@ -285,6 +292,7 @@ impl Clone for KeyPair { fn clone(&self) -> Self { match self { #[allow(clippy::expect_used)] + #[cfg(feature = "rs-crypto")] Self::Ed25519(kp) => Self::Ed25519( ed25519_dalek::Keypair::from_bytes(&kp.to_bytes()) .expect("expected to clone keypair"), @@ -301,6 +309,7 @@ impl Clone for KeyPair { impl std::fmt::Debug for KeyPair { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match *self { + #[cfg(feature = "rs-crypto")] KeyPair::Ed25519(ref key) => write!( f, "Ed25519 {{ public: {:?}, secret: (hidden) }}", @@ -322,6 +331,7 @@ impl KeyPair { /// Copy the public key of this algorithm. pub fn clone_public_key(&self) -> Result { Ok(match self { + #[cfg(feature = "rs-crypto")] KeyPair::Ed25519(ref key) => PublicKey::Ed25519(key.public), #[cfg(feature = "openssl")] KeyPair::RSA { ref key, ref hash } => { @@ -339,6 +349,7 @@ impl KeyPair { /// Name of this key algorithm. pub fn name(&self) -> &'static str { match *self { + #[cfg(feature = "rs-crypto")] KeyPair::Ed25519(_) => ED25519.0, #[cfg(feature = "openssl")] KeyPair::RSA { ref hash, .. } => hash.name().0, @@ -346,7 +357,9 @@ impl KeyPair { } /// Generate a key pair. + #[cfg(feature = "rs-crypto")] pub fn generate_ed25519() -> Option { + use rand::rngs::OsRng; let keypair = ed25519_dalek::Keypair::generate(&mut OsRng {}); assert_eq!( keypair.public.as_bytes(), @@ -365,11 +378,16 @@ impl KeyPair { pub fn sign_detached(&self, to_sign: &[u8]) -> Result { match self { #[allow(clippy::unwrap_used)] - KeyPair::Ed25519(ref secret) => Ok(Signature::Ed25519(SignatureBytes( - ed25519_dalek::ed25519::signature::Signature::as_bytes(&secret.sign(to_sign)) - .try_into() - .unwrap(), - ))), + #[cfg(feature = "rs-crypto")] + KeyPair::Ed25519(ref secret) => { + use ed25519_dalek::Signer; + use std::convert::TryInto; + Ok(Signature::Ed25519(SignatureBytes( + ed25519_dalek::ed25519::signature::Signature::as_bytes(&secret.sign(to_sign)) + .try_into() + .unwrap(), + ))) + } #[cfg(feature = "openssl")] KeyPair::RSA { ref key, ref hash } => Ok(Signature::RSA { bytes: rsa_signature(hash, key, to_sign)?, @@ -388,7 +406,10 @@ impl KeyPair { to_sign: H, ) -> Result<(), Error> { match self { + #[cfg(feature = "rs-crypto")] KeyPair::Ed25519(ref secret) => { + use ed25519_dalek::ed25519::signature::Signature as EdSignature; + use ed25519_dalek::Signer; let signature = secret.sign(to_sign.as_ref()); buffer.push_u32_be( @@ -416,7 +437,11 @@ impl KeyPair { /// `add_signature`. pub fn add_self_signature(&self, buffer: &mut CryptoVec) -> Result<(), Error> { match self { + #[cfg(feature = "rs-crypto")] KeyPair::Ed25519(ref secret) => { + use ed25519_dalek::ed25519::signature::Signature; + use ed25519_dalek::Signer; + let signature = secret.sign(buffer); buffer.push_u32_be((ED25519.0.len() + signature.as_bytes().len() + 8) as u32); buffer.extend_ssh_string(ED25519.0.as_bytes()); @@ -439,6 +464,7 @@ impl KeyPair { #[cfg(feature = "openssl")] pub fn with_signature_hash(&self, hash: SignatureHash) -> Option { match self { + #[cfg(feature = "rs-crypto")] KeyPair::Ed25519(_) => None, #[cfg(feature = "openssl")] KeyPair::RSA { key, .. } => Some(KeyPair::RSA { @@ -480,6 +506,7 @@ pub fn parse_public_key( ) -> Result { let mut pos = p.reader(0); let t = pos.read_string()?; + #[cfg(feature = "rs-crypto")] if t == b"ssh-ed25519" { if let Ok(pubkey) = pos.read_string() { let p = ed25519_dalek::PublicKey::from_bytes(pubkey).map_err(Error::from)?; diff --git a/russh-keys/src/lib.rs b/russh-keys/src/lib.rs index 34591f27..b366b1fa 100644 --- a/russh-keys/src/lib.rs +++ b/russh-keys/src/lib.rs @@ -68,12 +68,10 @@ use std::fs::{File, OpenOptions}; use std::io::{BufRead, BufReader, Read, Seek, SeekFrom, Write}; use std::path::Path; -use aes::cipher::block_padding::UnpadError; -use aes::cipher::inout::PadError; use byteorder::{BigEndian, WriteBytesExt}; use data_encoding::BASE64_MIME; use thiserror::Error; -use log::debug; +use log::{debug, info}; pub mod encoding; pub mod key; @@ -94,6 +92,7 @@ pub enum Error { #[error("Unsupported key type")] UnsupportedKeyType(Vec), /// The type of the key is unsupported + #[cfg(feature = "rs-crypto")] #[error("Invalid Ed25519 key data")] Ed25519KeyError(#[from] ed25519_dalek::SignatureError), /// The key is encrypted (should supply a password?) @@ -129,11 +128,13 @@ pub enum Error { #[error(transparent)] Openssl(#[from] openssl::error::ErrorStack), + #[cfg(feature = "rs-crypto")] #[error(transparent)] - Pad(#[from] PadError), + Pad(#[from] aes::cipher::inout::PadError), + #[cfg(feature = "rs-crypto")] #[error(transparent)] - Unpad(#[from] UnpadError), + Unpad(#[from] aes::cipher::block_padding::UnpadError), #[error("Base64 decoding error: {0}")] Decode(#[from] data_encoding::DecodeError), @@ -206,6 +207,7 @@ impl PublicKeyBase64 for key::PublicKey { fn public_key_bytes(&self) -> Vec { let mut s = Vec::new(); match *self { + #[cfg(feature = "rs-crypto")] key::PublicKey::Ed25519(ref publickey) => { let name = b"ssh-ed25519"; #[allow(clippy::unwrap_used)] // Vec<>.write can't fail @@ -241,6 +243,7 @@ impl PublicKeyBase64 for key::KeyPair { s.write_u32::(name.len() as u32).unwrap(); s.extend_from_slice(name); match *self { + #[cfg(feature = "rs-crypto")] key::KeyPair::Ed25519(ref key) => { let public = key.public.as_bytes(); #[allow(clippy::unwrap_used)] // Vec<>.write can't fail @@ -280,9 +283,9 @@ pub fn load_secret_key>( } fn is_base64_char(c: char) -> bool { - ('a'..='z').contains(&c) - || ('A'..='Z').contains(&c) - || ('0'..='9').contains(&c) + c.is_ascii_lowercase() + || c.is_ascii_uppercase() + || c.is_ascii_digit() || c == '/' || c == '+' || c == '=' @@ -364,10 +367,10 @@ pub fn check_known_hosts_path>( debug!("{:?} {:?}", h, k); let host_matches = h.split(',').any(|x| x == host_port); if host_matches { - if &parse_public_key_base64(k)? == pubkey { - return Ok(true); - } else { - return Err(Error::KeyChanged { line }); + match parse_public_key_base64(k) { + Ok(k) if &k == pubkey => return Ok(true), + Ok(_) => return Err(Error::KeyChanged { line }), + Err(e) => info!("host file line '{}' failed to parse: {}", k, e), } } } @@ -410,7 +413,7 @@ pub fn check_known_hosts(host: &str, port: u16, pubkey: &key::PublicKey) -> Resu known_host_file.push("known_hosts"); check_known_hosts_path(host, port, pubkey, &known_host_file) } else { - Err(Error::NoHomeDir.into()) + Err(Error::NoHomeDir) } } @@ -431,11 +434,9 @@ mod test { use std::fs::File; use std::io::Write; - #[cfg(feature = "openssl")] - use futures::Future; - use super::*; + #[cfg(feature = "rs-crypto")] const ED25519_KEY: &str = "-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABDLGyfA39 J2FcJygtYqi5ISAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIN+Wjn4+4Fcvl2Jl @@ -446,6 +447,7 @@ e+JpiSq66Z6GIt0801skPh20jxOO3F52SoX1IeO5D5PXfZrfSZlw6S8c7bwyp2FHxDewRx -----END OPENSSH PRIVATE KEY-----"; // password is 'test' + #[cfg(feature = "rs-crypto")] const ED25519_AESCTR_KEY: &str = "-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jdHIAAAAGYmNyeXB0AAAAGAAAABD1phlku5 A2G7Q9iP+DcOc9AAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIHeLC1lWiCYrXsf/ @@ -485,12 +487,14 @@ QR+u0AypRPmzHnOPAAAAEXJvb3RAMTQwOTExNTQ5NDBkAQ== -----END OPENSSH PRIVATE KEY-----"; #[test] + #[cfg(feature = "rs-crypto")] fn test_decode_ed25519_secret_key() { env_logger::try_init().unwrap_or(()); decode_secret_key(ED25519_KEY, Some("blabla")).unwrap(); } #[test] + #[cfg(feature = "rs-crypto")] fn test_decode_ed25519_aesctr_secret_key() { env_logger::try_init().unwrap_or(()); decode_secret_key(ED25519_AESCTR_KEY, Some("test")).unwrap(); @@ -504,8 +508,8 @@ QR+u0AypRPmzHnOPAAAAEXJvb3RAMTQwOTExNTQ5NDBkAQ== } #[test] - #[cfg(feature = "openssl")] - fn test_fingerprint() { + #[cfg(feature = "rs-crypto")] + fn test_fingerprint_ed25519() { let key = parse_public_key_base64( "AAAAC3NzaC1lZDI1NTE5AAAAILagOJFgwaMNhBWQINinKOXmqS4Gh5NgxgriXwdOoINJ", ) @@ -516,6 +520,19 @@ QR+u0AypRPmzHnOPAAAAEXJvb3RAMTQwOTExNTQ5NDBkAQ== ); } + #[test] + #[cfg(feature = "openssl")] + fn test_fingerprint_rsa() { + let key = parse_public_key_base64( + "AAAAB3NzaC1yc2EAAAADAQABAAAAgQC5eB1ws6hP7GYUyz89DPqrAFf7VW3GCOQsT/m2v1BhlzSxmBb9gSs9BxRyOUN3HtDcD0B+zqRKa/RqLIkemkdhitfrPiCqeWMzdKC+GIiKwxAgeUpNq1FmyJlwetHDlKi92MrnGwaTXvKDyIoV2xDJS2OAhmRIRM3nhrXUXZeiJQ==", + ) + .unwrap(); + assert_eq!( + key.fingerprint(), + "cmZL3+aAKXnUlEb02r847o2zlHLBLkiY5I0qbG21zZo" + ); + } + #[test] fn test_check_known_hosts() { env_logger::try_init().unwrap_or(()); @@ -523,35 +540,59 @@ QR+u0AypRPmzHnOPAAAAEXJvb3RAMTQwOTExNTQ5NDBkAQ== let path = dir.path().join("known_hosts"); { let mut f = File::create(&path).unwrap(); - f.write(b"[localhost]:13265 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJdD7y3aLq454yWBdwLWbieU1ebz9/cu7/QEXn9OIeZJ\n#pijul.org,37.120.161.53 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G2sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X\npijul.org,37.120.161.53 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G1sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X\n").unwrap(); + f.write_all(b"[localhost]:13265 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIJdD7y3aLq454yWBdwLWbieU1ebz9/cu7/QEXn9OIeZJ\n#pijul.org,37.120.161.53 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G2sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X\npijul.org,37.120.161.53 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G1sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X\npijul.org,37.120.161.53 ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC5eB1ws6hP7GYUyz89DPqrAFf7VW3GCOQsT/m2v1BhlzSxmBb9gSs9BxRyOUN3HtDcD0B+zqRKa/RqLIkemkdhitfrPiCqeWMzdKC+GIiKwxAgeUpNq1FmyJlwetHDlKi92MrnGwaTXvKDyIoV2xDJS2OAhmRIRM3nhrXUXZeiJQ==").unwrap(); } - // Valid key, non-standard port. - let host = "localhost"; - let port = 13265; - let hostkey = parse_public_key_base64( - "AAAAC3NzaC1lZDI1NTE5AAAAIJdD7y3aLq454yWBdwLWbieU1ebz9/cu7/QEXn9OIeZJ", - ) - .unwrap(); - assert!(check_known_hosts_path(host, port, &hostkey, &path).unwrap()); - - // Valid key, several hosts, port 22 - let host = "pijul.org"; - let port = 22; - let hostkey = parse_public_key_base64( - "AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G1sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X", - ) - .unwrap(); - assert!(check_known_hosts_path(host, port, &hostkey, &path).unwrap()); + #[cfg(feature = "openssl")] + { + // Valid key + let host = "pijul.org"; + let port = 22; + let hostkey = parse_public_key_base64( + "AAAAB3NzaC1yc2EAAAADAQABAAAAgQC5eB1ws6hP7GYUyz89DPqrAFf7VW3GCOQsT/m2v1BhlzSxmBb9gSs9BxRyOUN3HtDcD0B+zqRKa/RqLIkemkdhitfrPiCqeWMzdKC+GIiKwxAgeUpNq1FmyJlwetHDlKi92MrnGwaTXvKDyIoV2xDJS2OAhmRIRM3nhrXUXZeiJQ==", + ) + .unwrap(); + assert!(check_known_hosts_path(host, port, &hostkey, &path).unwrap()); + + // Invalid key + let host = "pijul.org"; + let port = 22; + let hostkey = parse_public_key_base64( + "AAAAB3NzaC1yc2EAAAADAQABAAAAgQD4p+jQjU/ZO2i444sGs//zjcg1P4T6XyExOXWT7RZ/XmITo5aAQICYgyFKF/NTU8WrWewNbxw/OHzmHGyL6BJEvqtCRT4a4ufgf+hpAchnYNK+3Ee9dWBOHIo93jGdC/I5q+3WbzBK4gtCLkQJQWWH/2whBym7zyR2JMA0s396dQ==", + ) + .unwrap(); + assert!(check_known_hosts_path(host, port, &hostkey, &path).is_err()); + } - // Now with the key in a comment above, check that it's not recognized - let host = "pijul.org"; - let port = 22; - let hostkey = parse_public_key_base64( - "AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G2sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X", - ) - .unwrap(); - assert!(check_known_hosts_path(host, port, &hostkey, &path).is_err()); + #[cfg(feature = "rs-crypto")] + { + // Valid key, non-standard port. + let host = "localhost"; + let port = 13265; + let hostkey = parse_public_key_base64( + "AAAAC3NzaC1lZDI1NTE5AAAAIJdD7y3aLq454yWBdwLWbieU1ebz9/cu7/QEXn9OIeZJ", + ) + .unwrap(); + assert!(check_known_hosts_path(host, port, &hostkey, &path).unwrap()); + + // Valid key, several hosts, port 22 + let host = "pijul.org"; + let port = 22; + let hostkey = parse_public_key_base64( + "AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G1sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X", + ) + .unwrap(); + assert!(check_known_hosts_path(host, port, &hostkey, &path).unwrap()); + + // Now with the key in a comment above, check that it's not recognized + let host = "pijul.org"; + let port = 22; + let hostkey = parse_public_key_base64( + "AAAAC3NzaC1lZDI1NTE5AAAAIA6rWI3G2sz07DnfFlrouTcysQlj2P+jpNSOEWD9OJ3X", + ) + .unwrap(); + assert!(check_known_hosts_path(host, port, &hostkey, &path).is_err()); + } } #[test] @@ -813,17 +854,17 @@ Cog3JMeTrb3LiPHgN6gU2P30MRp6L1j1J/MtlOAr5rux client.add_identity(&key, &[]).await?; client.request_identities().await?; let buf = russh_cryptovec::CryptoVec::from_slice(b"blabla"); - let len = buf.len(); + let _len = buf.len(); let (_, buf) = client.sign_request(&public, buf).await; - let buf = buf?; - let (a, b) = buf.split_at(len); - match key { - key::KeyPair::Ed25519 { .. } => { + let _buf = buf?; + #[cfg(feature = "rs-crypto")] + #[allow(irrefutable_let_patterns)] + { + let (a, b) = _buf.split_at(_len); + if let key::KeyPair::Ed25519 { .. } = key { let sig = &b[b.len() - 64..]; assert!(public.verify_detached(a, sig)); } - #[cfg(feature = "openssl")] - _ => {} } Ok::<(), Error>(()) }) @@ -833,6 +874,7 @@ Cog3JMeTrb3LiPHgN6gU2P30MRp6L1j1J/MtlOAr5rux } #[test] + #[cfg(feature = "rs-crypto")] #[cfg(unix)] fn test_client_agent_ed25519() { let key = decode_secret_key(ED25519_KEY, Some("blabla")).unwrap(); @@ -841,6 +883,7 @@ Cog3JMeTrb3LiPHgN6gU2P30MRp6L1j1J/MtlOAr5rux #[test] #[cfg(feature = "openssl")] + #[cfg(unix)] fn test_client_agent_rsa() { let key = decode_secret_key(PKCS8_ENCRYPTED, Some("blabla")).unwrap(); test_client_agent(key) @@ -848,6 +891,7 @@ Cog3JMeTrb3LiPHgN6gU2P30MRp6L1j1J/MtlOAr5rux #[test] #[cfg(feature = "openssl")] + #[cfg(unix)] fn test_client_agent_openssh_rsa() { let key = decode_secret_key(RSA_KEY, None).unwrap(); test_client_agent(key) @@ -896,16 +940,16 @@ Cog3JMeTrb3LiPHgN6gU2P30MRp6L1j1J/MtlOAr5rux .await?; client.request_identities().await?; let buf = russh_cryptovec::CryptoVec::from_slice(b"blabla"); - let len = buf.len(); + let _len = buf.len(); let (_, buf) = client.sign_request(&public, buf).await; - let buf = buf?; - let (a, b) = buf.split_at(len); - match key { - key::KeyPair::Ed25519 { .. } => { + let _buf = buf?; + #[cfg(feature = "rs-crypto")] + { + let (a, b) = _buf.split_at(_len); + if let key::KeyPair::Ed25519 { .. } = key { let sig = &b[b.len() - 64..]; assert!(public.verify_detached(a, sig)); } - _ => {} } Ok::<(), Error>(()) }) diff --git a/russh/Cargo.toml b/russh/Cargo.toml index 38aaa56f..25fae186 100644 --- a/russh/Cargo.toml +++ b/russh/Cargo.toml @@ -13,20 +13,29 @@ version = "0.37.1" rust-version = "1.60" [features] -default = ["flate2"] +default = ["flate2", "rs-crypto"] openssl = ["russh-keys/openssl", "dep:openssl"] vendored-openssl = ["openssl/vendored", "russh-keys/vendored-openssl"] +rs-crypto = [ + "russh-keys/rs-crypto", + "dep:aes", + "dep:aes-gcm", + "dep:curve25519-dalek", + "dep:poly1305", + "dep:ctr", + "dep:chacha20", +] [dependencies] -aes = "0.8" -aes-gcm = "0.10" +aes = { version = "0.8", optional = true } +aes-gcm = { version = "0.10", optional = true } async-trait = "0.1" bitflags = "1.2" byteorder = "1.3" -chacha20 = "0.9" -curve25519-dalek = "3.2" -poly1305 = "0.8" -ctr = "0.9" +chacha20 = { version = "0.9", optional = true } +curve25519-dalek = { version = "3.2", optional = true } +poly1305 = { version = "0.8", optional = true } +ctr = { version = "0.9", optional = true } digest = "0.10" flate2 = { version = "1.0", optional = true } futures = "0.3" @@ -34,10 +43,10 @@ generic-array = "0.14" hmac = "0.12" log = "0.4" once_cell = "1.13" -openssl = { version = "0.10", optional = true } +openssl = { version = "^0.10.40", optional = true } rand = "0.8" russh-cryptovec = { version = "0.7.0", path = "../cryptovec" } -russh-keys = { version = "0.37.1", path = "../russh-keys" } +russh-keys = { version = "0.37.1", path = "../russh-keys", default-features = false } sha1 = "0.10" sha2 = "0.10" hex-literal = "0.3" diff --git a/russh/examples/echoserver.rs b/russh/examples/echoserver.rs index 356f1b3d..7ed470a6 100644 --- a/russh/examples/echoserver.rs +++ b/russh/examples/echoserver.rs @@ -17,7 +17,7 @@ async fn main() { connection_timeout: Some(std::time::Duration::from_secs(3600)), auth_rejection_time: std::time::Duration::from_secs(3), auth_rejection_time_initial: Some(std::time::Duration::from_secs(0)), - keys: vec![russh_keys::key::KeyPair::generate_ed25519().unwrap()], + keys: vec![generate_keypair()], ..Default::default() }; let config = Arc::new(config); @@ -112,3 +112,13 @@ impl server::Handler for Server { Ok((self, true, session)) } } + +#[cfg(feature = "rs-crypto")] +fn generate_keypair() -> russh_keys::key::KeyPair { + russh_keys::key::KeyPair::generate_ed25519().unwrap() +} + +#[cfg(not(feature = "rs-crypto"))] +fn generate_keypair() -> russh_keys::key::KeyPair { + russh_keys::key::KeyPair::generate_rsa(1024, russh_keys::key::SignatureHash::SHA2_512).unwrap() +} diff --git a/russh/examples/sftp_server.rs b/russh/examples/sftp_server.rs index 6c55c84c..5efbc05a 100644 --- a/russh/examples/sftp_server.rs +++ b/russh/examples/sftp_server.rs @@ -90,20 +90,12 @@ impl russh::server::Handler for SshSession { } } +#[derive(Default)] struct SftpSession { version: Option, root_dir_read_done: bool, } -impl Default for SftpSession { - fn default() -> Self { - Self { - version: None, - root_dir_read_done: false, - } - } -} - #[async_trait] impl russh_sftp::server::Handler for SftpSession { type Error = StatusCode; @@ -184,7 +176,7 @@ async fn main() { let config = russh::server::Config { auth_rejection_time: Duration::from_secs(3), auth_rejection_time_initial: Some(Duration::from_secs(0)), - keys: vec![KeyPair::generate_ed25519().unwrap()], + keys: vec![generate_keypair()], connection_timeout: Some(Duration::from_secs(3600)), ..Default::default() }; @@ -205,3 +197,13 @@ async fn main() { .await .unwrap(); } + +#[cfg(feature = "rs-crypto")] +fn generate_keypair() -> KeyPair { + KeyPair::generate_ed25519().unwrap() +} + +#[cfg(not(feature = "rs-crypto"))] +fn generate_keypair() -> KeyPair { + KeyPair::generate_rsa(1024, russh_keys::key::SignatureHash::SHA2_512).unwrap() +} diff --git a/russh/src/cipher/aes_openssh.rs b/russh/src/cipher/aes_openssh.rs new file mode 100644 index 00000000..8e81dba3 --- /dev/null +++ b/russh/src/cipher/aes_openssh.rs @@ -0,0 +1,202 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +use openssl::{cipher::CipherRef, cipher_ctx::CipherCtx}; + +use rand::RngCore; + +use super::super::Error; +use super::PACKET_LENGTH_LEN; +use crate::mac::{Mac, MacAlgorithm}; + +pub struct AesSshCipher(pub fn() -> &'static CipherRef); + +#[allow(clippy::expect_used)] +impl super::Cipher for AesSshCipher { + fn key_len(&self) -> usize { + self.0().key_length() + } + + fn nonce_len(&self) -> usize { + self.0().iv_length() + } + + fn needs_mac(&self) -> bool { + true + } + + fn make_opening_key( + &self, + key: &[u8], + iv: &[u8], + mac_key: &[u8], + mac: &dyn MacAlgorithm, + ) -> Result, Error> { + let mut ctx = CipherCtx::new().expect("expected to make openssl cipher"); + ctx.decrypt_init(Some(self.0()), Some(key), Some(iv))?; + + Ok(Box::new(OpeningKey { + ctx, + key: key.to_vec(), + iv: iv.to_vec(), + cipher: self.0(), + mac: mac.make_mac(mac_key), + })) + } + + fn make_sealing_key( + &self, + key: &[u8], + iv: &[u8], + mac_key: &[u8], + mac: &dyn MacAlgorithm, + ) -> Result, Error> { + let mut ctx = CipherCtx::new().expect("expected to make openssl cipher"); + ctx.encrypt_init(Some(self.0()), Some(key), Some(iv))?; + + Ok(Box::new(SealingKey { + ctx, + mac: mac.make_mac(mac_key), + })) + } +} + +pub struct OpeningKey { + ctx: CipherCtx, + key: Vec, + iv: Vec, + cipher: &'static CipherRef, + mac: Box, +} + +pub struct SealingKey { + ctx: CipherCtx, + mac: Box, +} + +#[allow(clippy::expect_used)] +impl super::OpeningKey for OpeningKey { + fn decrypt_packet_length( + &self, + _sequence_number: u32, + mut encrypted_packet_length: [u8; 4], + ) -> Result<[u8; 4], Error> { + if self.mac.is_etm() { + Ok(encrypted_packet_length) + } else { + let mut ctx = CipherCtx::new().expect("expected to make openssl cipher"); + ctx.decrypt_init(Some(self.cipher), Some(&self.key), Some(&self.iv))?; + + let input = encrypted_packet_length; + let n = ctx.cipher_update(&input, Some(&mut encrypted_packet_length))?; + #[allow(clippy::indexing_slicing)] + ctx.cipher_final(&mut encrypted_packet_length[n..])?; + Ok(encrypted_packet_length) + } + } + + fn tag_len(&self) -> usize { + self.mac.mac_len() + } + + fn open<'a>( + &mut self, + sequence_number: u32, + ciphertext_in_plaintext_out: &'a mut [u8], + tag: &[u8], + ) -> Result<&'a [u8], Error> { + let input = ciphertext_in_plaintext_out.to_vec(); + if self.mac.is_etm() { + if !self.mac.verify(sequence_number, &input, tag) { + return Err(Error::PacketAuth); + } + #[allow(clippy::indexing_slicing)] + self.ctx.cipher_update( + &input[PACKET_LENGTH_LEN..], + Some(&mut ciphertext_in_plaintext_out[PACKET_LENGTH_LEN..]), + )?; + } else { + self.ctx + .cipher_update(&input, Some(ciphertext_in_plaintext_out))?; + + if !self + .mac + .verify(sequence_number, ciphertext_in_plaintext_out, tag) + { + return Err(Error::PacketAuth); + } + } + + Ok(ciphertext_in_plaintext_out) + } +} + +#[allow(clippy::expect_used)] +impl super::SealingKey for SealingKey { + fn padding_length(&self, payload: &[u8]) -> usize { + // note: the .blocksize() method reports 1 for CTR, which is not what we need... + let block_size = 16; + + let pll = if self.mac.is_etm() { + 0 + } else { + PACKET_LENGTH_LEN + }; + + let extra_len = PACKET_LENGTH_LEN + super::PADDING_LENGTH_LEN + self.mac.mac_len(); + + let padding_len = if payload.len() + extra_len <= super::MINIMUM_PACKET_LEN { + super::MINIMUM_PACKET_LEN - payload.len() - super::PADDING_LENGTH_LEN - pll + } else { + block_size - ((pll + super::PADDING_LENGTH_LEN + payload.len()) % block_size) + }; + if padding_len < PACKET_LENGTH_LEN { + padding_len + block_size + } else { + padding_len + } + } + + fn fill_padding(&self, padding_out: &mut [u8]) { + rand::thread_rng().fill_bytes(padding_out); + } + + fn tag_len(&self) -> usize { + self.mac.mac_len() + } + + fn seal( + &mut self, + sequence_number: u32, + plaintext_in_ciphertext_out: &mut [u8], + tag_out: &mut [u8], + ) { + let plaintext = plaintext_in_ciphertext_out.to_vec(); + #[allow(clippy::indexing_slicing)] + if self.mac.is_etm() { + self.ctx + .cipher_update( + &plaintext[PACKET_LENGTH_LEN..], + Some(&mut plaintext_in_ciphertext_out[PACKET_LENGTH_LEN..]), + ) + .expect("cipher update should not fail"); + self.mac + .compute(sequence_number, plaintext_in_ciphertext_out, tag_out); + } else { + self.mac.compute(sequence_number, &plaintext, tag_out); + self.ctx + .cipher_update(&plaintext, Some(plaintext_in_ciphertext_out)) + .expect("cipher update should not fail"); + } + } +} diff --git a/russh/src/cipher/block.rs b/russh/src/cipher/block.rs index ccd6a4de..865402c6 100644 --- a/russh/src/cipher/block.rs +++ b/russh/src/cipher/block.rs @@ -44,15 +44,15 @@ impl su n: &[u8], m: &[u8], mac: &dyn MacAlgorithm, - ) -> Box { + ) -> Result, Error> { let mut key = GenericArray::::default(); let mut nonce = GenericArray::::default(); key.clone_from_slice(k); nonce.clone_from_slice(n); - Box::new(OpeningKey { + Ok(Box::new(OpeningKey { cipher: C::new(&key, &nonce), mac: mac.make_mac(m), - }) + })) } fn make_sealing_key( @@ -61,15 +61,15 @@ impl su n: &[u8], m: &[u8], mac: &dyn MacAlgorithm, - ) -> Box { + ) -> Result, Error> { let mut key = GenericArray::::default(); let mut nonce = GenericArray::::default(); key.clone_from_slice(k); nonce.clone_from_slice(n); - Box::new(SealingKey { + Ok(Box::new(SealingKey { cipher: C::new(&key, &nonce), mac: mac.make_mac(m), - }) + })) } } @@ -88,14 +88,14 @@ impl super::OpeningKey for OpeningKe &self, _sequence_number: u32, mut encrypted_packet_length: [u8; 4], - ) -> [u8; 4] { + ) -> Result<[u8; 4], Error> { if self.mac.is_etm() { - encrypted_packet_length + Ok(encrypted_packet_length) } else { // Work around uncloneable Aes<> let mut cipher: C = unsafe { std::ptr::read(&self.cipher as *const C) }; cipher.apply_keystream(&mut encrypted_packet_length); - encrypted_packet_length + Ok(encrypted_packet_length) } } diff --git a/russh/src/cipher/chacha20poly1305.rs b/russh/src/cipher/chacha20poly1305.rs index cab3eece..4d12e1c3 100644 --- a/russh/src/cipher/chacha20poly1305.rs +++ b/russh/src/cipher/chacha20poly1305.rs @@ -48,12 +48,12 @@ impl super::Cipher for SshChacha20Poly1305Cipher { _: &[u8], _: &[u8], _: &dyn MacAlgorithm, - ) -> Box { + ) -> Result, Error> { let mut k1 = Key::default(); let mut k2 = Key::default(); k1.clone_from_slice(&k[KeyLength::to_usize()..]); k2.clone_from_slice(&k[..KeyLength::to_usize()]); - Box::new(OpeningKey { k1, k2 }) + Ok(Box::new(OpeningKey { k1, k2 })) } #[allow(clippy::indexing_slicing)] // length checked @@ -63,12 +63,12 @@ impl super::Cipher for SshChacha20Poly1305Cipher { _: &[u8], _: &[u8], _: &dyn MacAlgorithm, - ) -> Box { + ) -> Result, Error> { let mut k1 = Key::default(); let mut k2 = Key::default(); k1.clone_from_slice(&k[KeyLength::to_usize()..]); k2.clone_from_slice(&k[..KeyLength::to_usize()]); - Box::new(SealingKey { k1, k2 }) + Ok(Box::new(SealingKey { k1, k2 })) } } @@ -95,11 +95,11 @@ impl super::OpeningKey for OpeningKey { &self, sequence_number: u32, mut encrypted_packet_length: [u8; 4], - ) -> [u8; 4] { + ) -> Result<[u8; 4], Error> { let nonce = make_counter(sequence_number); let mut cipher = ChaCha20Legacy::new(&self.k1, &nonce); cipher.apply_keystream(&mut encrypted_packet_length); - encrypted_packet_length + Ok(encrypted_packet_length) } fn tag_len(&self) -> usize { diff --git a/russh/src/cipher/clear.rs b/russh/src/cipher/clear.rs index ddd552db..96f00f4d 100644 --- a/russh/src/cipher/clear.rs +++ b/russh/src/cipher/clear.rs @@ -32,8 +32,8 @@ impl super::Cipher for Clear { _: &[u8], _: &[u8], _: &dyn MacAlgorithm, - ) -> Box { - Box::new(Key {}) + ) -> Result, Error> { + Ok(Box::new(Key {})) } fn make_sealing_key( @@ -42,14 +42,14 @@ impl super::Cipher for Clear { _: &[u8], _: &[u8], _: &dyn MacAlgorithm, - ) -> Box { - Box::new(Key {}) + ) -> Result, Error> { + Ok(Box::new(Key {})) } } impl super::OpeningKey for Key { - fn decrypt_packet_length(&self, _seqn: u32, packet_length: [u8; 4]) -> [u8; 4] { - packet_length + fn decrypt_packet_length(&self, _seqn: u32, packet_length: [u8; 4]) -> Result<[u8; 4], Error> { + Ok(packet_length) } fn tag_len(&self) -> usize { diff --git a/russh/src/cipher/gcm.rs b/russh/src/cipher/gcm.rs index f737716c..5b82bbd4 100644 --- a/russh/src/cipher/gcm.rs +++ b/russh/src/cipher/gcm.rs @@ -45,15 +45,15 @@ impl super::Cipher for GcmCipher { n: &[u8], _: &[u8], _: &dyn MacAlgorithm, - ) -> Box { + ) -> Result, Error> { let mut key = GenericArray::::default(); key.clone_from_slice(k); let mut nonce = GenericArray::::default(); nonce.clone_from_slice(n); - Box::new(OpeningKey { + Ok(Box::new(OpeningKey { nonce, cipher: Aes256Gcm::new(&key), - }) + })) } fn make_sealing_key( @@ -62,15 +62,15 @@ impl super::Cipher for GcmCipher { n: &[u8], _: &[u8], _: &dyn MacAlgorithm, - ) -> Box { + ) -> Result, Error> { let mut key = GenericArray::::default(); key.clone_from_slice(k); let mut nonce = GenericArray::::default(); nonce.clone_from_slice(n); - Box::new(SealingKey { + Ok(Box::new(SealingKey { nonce, cipher: Aes256Gcm::new(&key), - }) + })) } } @@ -112,8 +112,8 @@ impl super::OpeningKey for OpeningKey { &self, _sequence_number: u32, encrypted_packet_length: [u8; 4], - ) -> [u8; 4] { - encrypted_packet_length + ) -> Result<[u8; 4], Error> { + Ok(encrypted_packet_length) } fn tag_len(&self) -> usize { diff --git a/russh/src/cipher/mod.rs b/russh/src/cipher/mod.rs index 1251253d..184fd6c2 100644 --- a/russh/src/cipher/mod.rs +++ b/russh/src/cipher/mod.rs @@ -16,12 +16,11 @@ //! This module exports cipher names for use with [Preferred]. use std::collections::HashMap; use std::fmt::Debug; +#[cfg(feature = "rs-crypto")] use std::marker::PhantomData; use std::num::Wrapping; -use aes::{Aes128, Aes192, Aes256}; use byteorder::{BigEndian, ByteOrder}; -use ctr::Ctr128BE; use once_cell::sync::Lazy; use tokio::io::{AsyncRead, AsyncReadExt}; use log::debug; @@ -30,13 +29,23 @@ use crate::mac::MacAlgorithm; use crate::sshbuffer::SSHBuffer; use crate::Error; +pub(crate) mod clear; + +#[cfg(feature = "openssl")] +pub(crate) mod aes_openssh; +#[cfg(feature = "rs-crypto")] pub(crate) mod block; +#[cfg(feature = "rs-crypto")] pub(crate) mod chacha20poly1305; -pub(crate) mod clear; +#[cfg(feature = "rs-crypto")] pub(crate) mod gcm; + +#[cfg(feature = "rs-crypto")] use block::SshBlockCipher; +#[cfg(feature = "rs-crypto")] use chacha20poly1305::SshChacha20Poly1305Cipher; use clear::Clear; +#[cfg(feature = "rs-crypto")] use gcm::GcmCipher; pub(crate) trait Cipher { @@ -53,14 +62,14 @@ pub(crate) trait Cipher { nonce: &[u8], mac_key: &[u8], mac: &dyn MacAlgorithm, - ) -> Box; + ) -> Result, Error>; fn make_sealing_key( &self, key: &[u8], nonce: &[u8], mac_key: &[u8], mac: &dyn MacAlgorithm, - ) -> Box; + ) -> Result, Error>; } /// `clear` @@ -79,10 +88,29 @@ pub const CHACHA20_POLY1305: Name = Name("chacha20-poly1305@openssh.com"); pub const NONE: Name = Name("none"); static _CLEAR: Clear = Clear {}; -static _AES_128_CTR: SshBlockCipher> = SshBlockCipher(PhantomData); -static _AES_192_CTR: SshBlockCipher> = SshBlockCipher(PhantomData); -static _AES_256_CTR: SshBlockCipher> = SshBlockCipher(PhantomData); + +#[cfg(all(feature = "openssl", not(feature = "rs-crypto")))] +static _AES_128_CTR: aes_openssh::AesSshCipher = + aes_openssh::AesSshCipher(openssl::cipher::Cipher::aes_128_ctr); +#[cfg(feature = "rs-crypto")] +static _AES_128_CTR: SshBlockCipher> = SshBlockCipher(PhantomData); + +#[cfg(all(feature = "openssl", not(feature = "rs-crypto")))] +static _AES_192_CTR: aes_openssh::AesSshCipher = + aes_openssh::AesSshCipher(openssl::cipher::Cipher::aes_192_ctr); +#[cfg(feature = "rs-crypto")] +static _AES_192_CTR: SshBlockCipher> = SshBlockCipher(PhantomData); + +#[cfg(all(feature = "openssl", not(feature = "rs-crypto")))] +static _AES_256_CTR: aes_openssh::AesSshCipher = + aes_openssh::AesSshCipher(openssl::cipher::Cipher::aes_256_ctr); +#[cfg(feature = "rs-crypto")] +static _AES_256_CTR: SshBlockCipher> = SshBlockCipher(PhantomData); + +#[cfg(feature = "rs-crypto")] static _AES_256_GCM: GcmCipher = GcmCipher {}; + +#[cfg(feature = "rs-crypto")] static _CHACHA20_POLY1305: SshChacha20Poly1305Cipher = SshChacha20Poly1305Cipher {}; pub(crate) static CIPHERS: Lazy> = @@ -93,7 +121,9 @@ pub(crate) static CIPHERS: Lazy [u8; 4]; + fn decrypt_packet_length( + &self, + seqn: u32, + encrypted_packet_length: [u8; 4], + ) -> Result<[u8; 4], Error>; fn tag_len(&self) -> usize; @@ -190,7 +224,7 @@ pub(crate) async fn read<'a, R: AsyncRead + Unpin>( buffer.buffer.clear(); buffer.buffer.extend(&len); debug!("reading, seqn = {:?}", seqn); - let len = cipher.decrypt_packet_length(seqn, len); + let len = cipher.decrypt_packet_length(seqn, len)?; buffer.len = BigEndian::read_u32(&len) as usize + cipher.tag_len(); debug!("reading, clear len = {:?}", buffer.len); } diff --git a/russh/src/compression.rs b/russh/src/compression.rs index 20aff2cc..63b43e92 100644 --- a/russh/src/compression.rs +++ b/russh/src/compression.rs @@ -71,7 +71,7 @@ impl Compress { &mut self, input: &'a [u8], _: &'a mut russh_cryptovec::CryptoVec, - ) -> Result<&'a [u8], Error> { + ) -> Result<&'a [u8], crate::Error> { Ok(input) } } @@ -82,7 +82,7 @@ impl Decompress { &mut self, input: &'a [u8], _: &'a mut russh_cryptovec::CryptoVec, - ) -> Result<&'a [u8], Error> { + ) -> Result<&'a [u8], crate::Error> { Ok(input) } } diff --git a/russh/src/kex/mod.rs b/russh/src/kex/mod.rs index fbcbd43a..50d114e7 100644 --- a/russh/src/kex/mod.rs +++ b/russh/src/kex/mod.rs @@ -15,6 +15,7 @@ //! //! This module exports kex algorithm names for use with [Preferred]. +#[cfg(feature = "rs-crypto")] mod curve25519; mod dh; mod none; @@ -22,6 +23,7 @@ use std::cell::RefCell; use std::collections::HashMap; use std::fmt::Debug; +#[cfg(feature = "rs-crypto")] use curve25519::Curve25519KexType; use dh::{DhGroup14Sha1KexType, DhGroup14Sha256KexType, DhGroup1Sha1KexType}; use digest::Digest; @@ -98,6 +100,7 @@ pub const EXTENSION_SUPPORT_AS_CLIENT: Name = Name("ext-info-c"); /// `ext-info-s` pub const EXTENSION_SUPPORT_AS_SERVER: Name = Name("ext-info-s"); +#[cfg(feature = "rs-crypto")] const _CURVE25519: Curve25519KexType = Curve25519KexType {}; const _DH_G1_SHA1: DhGroup1Sha1KexType = DhGroup1Sha1KexType {}; const _DH_G14_SHA1: DhGroup14Sha1KexType = DhGroup14Sha1KexType {}; @@ -107,6 +110,7 @@ const _NONE: none::NoneKexType = none::NoneKexType {}; pub(crate) static KEXES: Lazy> = Lazy::new(|| { let mut h: HashMap<&'static Name, &(dyn KexType + Send + Sync)> = HashMap::new(); + #[cfg(feature = "rs-crypto")] h.insert(&CURVE25519, &_CURVE25519); h.insert(&DH_G14_SHA256, &_DH_G14_SHA256); h.insert(&DH_G14_SHA1, &_DH_G14_SHA1); @@ -214,7 +218,7 @@ pub(crate) fn compute_keys( )?; let local_to_remote = - cipher.make_sealing_key(&key, &nonce, &mac, *local_to_remote_mac); + cipher.make_sealing_key(&key, &nonce, &mac, *local_to_remote_mac)?; compute_key(remote_to_local, &mut key, cipher.key_len())?; compute_key(remote_to_local_nonce, &mut nonce, cipher.nonce_len())?; @@ -224,7 +228,7 @@ pub(crate) fn compute_keys( remote_to_local_mac.key_len(), )?; let remote_to_local = - cipher.make_opening_key(&key, &nonce, &mac, *remote_to_local_mac); + cipher.make_opening_key(&key, &nonce, &mac, *remote_to_local_mac)?; Ok(super::cipher::CipherPair { local_to_remote, diff --git a/russh/src/key.rs b/russh/src/key.rs index ca00aea2..9240ce88 100644 --- a/russh/src/key.rs +++ b/russh/src/key.rs @@ -24,6 +24,7 @@ pub trait PubKey { impl PubKey for PublicKey { fn push_to(&self, buffer: &mut CryptoVec) { match self { + #[cfg(feature = "rs-crypto")] PublicKey::Ed25519(ref public) => { buffer.push_u32_be((ED25519.0.len() + public.as_bytes().len() + 8) as u32); buffer.extend_ssh_string(ED25519.0.as_bytes()); @@ -47,6 +48,7 @@ impl PubKey for PublicKey { impl PubKey for KeyPair { fn push_to(&self, buffer: &mut CryptoVec) { match self { + #[cfg(feature = "rs-crypto")] KeyPair::Ed25519(ref key) => { let public = key.public.as_bytes(); buffer.push_u32_be((ED25519.0.len() + public.len() + 8) as u32); diff --git a/russh/src/lib.rs b/russh/src/lib.rs index f0f3f279..df083056 100644 --- a/russh/src/lib.rs +++ b/russh/src/lib.rs @@ -96,9 +96,9 @@ use std::fmt::{Debug, Display, Formatter}; -use thiserror::Error; use parsing::ChannelOpenConfirmation; pub use russh_cryptovec::CryptoVec; +use thiserror::Error; mod auth; @@ -271,9 +271,11 @@ pub enum Error { Utf8(#[from] std::str::Utf8Error), #[error(transparent)] + #[cfg(feature = "flate2")] Compress(#[from] flate2::CompressError), #[error(transparent)] + #[cfg(feature = "flate2")] Decompress(#[from] flate2::DecompressError), #[error(transparent)] @@ -467,6 +469,7 @@ impl ChannelParams { } #[cfg(test)] +#[allow(clippy::unwrap_used, clippy::panic)] mod test_compress { use std::collections::HashMap; use std::sync::{Arc, Mutex}; @@ -478,18 +481,27 @@ mod test_compress { use super::*; use crate::server::Msg; + #[cfg(feature = "rs-crypto")] + fn geneate_keypair() -> russh_keys::key::KeyPair { + russh_keys::key::KeyPair::generate_ed25519().unwrap() + } + + #[cfg(all(feature = "openssl", not(feature = "rs-crypto")))] + fn geneate_keypair() -> russh_keys::key::KeyPair { + russh_keys::key::KeyPair::generate_rsa(2048, russh_keys::key::SignatureHash::SHA2_256) + .unwrap() + } + #[tokio::test] async fn compress_local_test() { let _ = env_logger::try_init(); - let client_key = russh_keys::key::KeyPair::generate_ed25519().unwrap(); + let client_key = geneate_keypair(); let mut config = server::Config::default(); config.preferred = Preferred::COMPRESSED; config.connection_timeout = None; // Some(std::time::Duration::from_secs(3)); config.auth_rejection_time = std::time::Duration::from_secs(3); - config - .keys - .push(russh_keys::key::KeyPair::generate_ed25519().unwrap()); + config.keys.push(geneate_keypair()); let config = Arc::new(config); let mut sh = Server { clients: Arc::new(Mutex::new(HashMap::new())), @@ -505,8 +517,10 @@ mod test_compress { server::run_stream(config, socket, server).await.unwrap(); }); - let mut config = client::Config::default(); - config.preferred = Preferred::COMPRESSED; + let config = client::Config { + preferred: Preferred::COMPRESSED, + ..Default::default() + }; let config = Arc::new(config); dbg!(&addr); @@ -602,7 +616,7 @@ mod test_compress { use futures::Future; #[cfg(test)] -async fn test_session( +async fn test_session( client_handler: CH, server_handler: SH, run_client: RC, @@ -612,22 +626,33 @@ async fn test_session( RS: FnOnce(crate::server::Handle) -> F2 + Send + Sync + 'static, F1: Future> + Send + Sync + 'static, F2: Future + Send + Sync + 'static, - CH: crate::client::Handler + Send + Sync + 'static, - SH: crate::server::Handler + Send + Sync + 'static, + CERR: std::fmt::Debug + Send, + SERR: std::fmt::Debug + Send, + CH: crate::client::Handler + Send + Sync + 'static, + SH: crate::server::Handler + Send + Sync + 'static, { use std::sync::Arc; + use crate::*; - let _ = env_logger::try_init(); + #[cfg(feature = "rs-crypto")] + fn generate_keypair() -> russh_keys::key::KeyPair { + russh_keys::key::KeyPair::generate_ed25519().unwrap() + } - let client_key = russh_keys::key::KeyPair::generate_ed25519().unwrap(); + #[cfg(not(feature = "rs-crypto"))] + fn generate_keypair() -> russh_keys::key::KeyPair { + russh_keys::key::KeyPair::generate_rsa(2048, russh_keys::key::SignatureHash::SHA2_256) + .unwrap() + } + + let _ = env_logger::try_init(); + let client_key = generate_keypair(); let mut config = server::Config::default(); config.connection_timeout = None; config.auth_rejection_time = std::time::Duration::from_secs(3); - config - .keys - .push(russh_keys::key::KeyPair::generate_ed25519().unwrap()); + config.keys.push(generate_keypair()); let config = Arc::new(config); let socket = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); let addr = socket.local_addr().unwrap(); @@ -637,18 +662,17 @@ async fn test_session( let server_join = tokio::spawn(async move { let (socket, _) = socket.accept().await.unwrap(); - let session = server::run_stream(config, socket, server_handler) + + server::run_stream(config, socket, server_handler) .await .map_err(|_| ()) - .unwrap(); - session + .unwrap() }); let client_join = tokio::spawn(async move { let config = Arc::new(client::Config::default()); let mut session = client::connect(config, addr, client_handler) .await - .map_err(|_| ()) .unwrap(); let authenticated = session .authenticate_publickey( @@ -904,7 +928,7 @@ mod test_channels { ChannelMsg::Data { data } => { channel.data(&data[..]).await.unwrap(); channel.close().await.unwrap(); - break + break; } _ => {} } diff --git a/russh/src/negotiation.rs b/russh/src/negotiation.rs index c3c03ef6..65993fcc 100644 --- a/russh/src/negotiation.rs +++ b/russh/src/negotiation.rs @@ -53,6 +53,7 @@ pub struct Preferred { } const KEX_ORDER: &[kex::Name] = &[ + #[cfg(feature = "rs-crypto")] kex::CURVE25519, kex::DH_G14_SHA256, kex::DH_G14_SHA1, @@ -62,7 +63,9 @@ const KEX_ORDER: &[kex::Name] = &[ ]; const CIPHER_ORDER: &[cipher::Name] = &[ + #[cfg(feature = "rs-crypto")] cipher::CHACHA20_POLY1305, + #[cfg(feature = "rs-crypto")] cipher::AES_256_GCM, cipher::AES_256_CTR, cipher::AES_192_CTR, @@ -82,8 +85,17 @@ const HMAC_ORDER: &[mac::Name] = &[ impl Preferred { #[cfg(feature = "openssl")] pub const DEFAULT: Preferred = Preferred { - kex: &[kex::CURVE25519, kex::DH_G14_SHA256], - key: &[key::ED25519, key::RSA_SHA2_256, key::RSA_SHA2_512], + kex: &[ + #[cfg(feature = "rs-crypto")] + kex::CURVE25519, + kex::DH_G14_SHA256, + ], + key: &[ + #[cfg(feature = "rs-crypto")] + key::ED25519, + key::RSA_SHA2_256, + key::RSA_SHA2_512, + ], cipher: CIPHER_ORDER, mac: HMAC_ORDER, compression: &["none", "zlib", "zlib@openssh.com"], @@ -100,7 +112,12 @@ impl Preferred { pub const COMPRESSED: Preferred = Preferred { kex: KEX_ORDER, - key: &[key::ED25519, key::RSA_SHA2_256, key::RSA_SHA2_512], + key: &[ + #[cfg(feature = "rs-crypto")] + key::ED25519, + key::RSA_SHA2_256, + key::RSA_SHA2_512, + ], cipher: CIPHER_ORDER, mac: HMAC_ORDER, compression: &["zlib", "zlib@openssh.com", "none"], @@ -125,14 +142,15 @@ impl Named for () { } } -#[cfg(not(feature = "openssl"))] +#[cfg(feature = "rs-crypto")] use russh_keys::key::ED25519; #[cfg(feature = "openssl")] -use russh_keys::key::{ED25519, SSH_RSA}; +use russh_keys::key::SSH_RSA; impl Named for PublicKey { fn name(&self) -> &'static str { match self { + #[cfg(feature = "rs-crypto")] PublicKey::Ed25519(_) => ED25519.0, #[cfg(feature = "openssl")] PublicKey::RSA { .. } => SSH_RSA.0, @@ -143,6 +161,7 @@ impl Named for PublicKey { impl Named for KeyPair { fn name(&self) -> &'static str { match self { + #[cfg(feature = "rs-crypto")] KeyPair::Ed25519 { .. } => ED25519.0, #[cfg(feature = "openssl")] KeyPair::RSA { ref hash, .. } => hash.name().0, diff --git a/russh/src/server/mod.rs b/russh/src/server/mod.rs index 9d632a19..9b9e44a7 100644 --- a/russh/src/server/mod.rs +++ b/russh/src/server/mod.rs @@ -116,7 +116,7 @@ use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; -use log::error; +use log::{error, info}; use async_trait::async_trait; use futures::future::Future; use russh_keys::key; @@ -796,11 +796,12 @@ where .write_all(&write_buffer.buffer[..]) .await .map_err(crate::Error::from)?; - + info!("wrote id"); // Reading SSH id and allocating a session. let mut stream = SshRead::new(stream); let (sender, receiver) = tokio::sync::mpsc::channel(config.event_buffer_size); let common = read_ssh_id(config, &mut stream).await?; + info!("read other id"); let handle = server::session::Handle { sender }; let session = Session { target_window_size: common.config.window_size, @@ -814,6 +815,7 @@ where let join = tokio::spawn(session.run(stream, handler)); + info!("session is running"); Ok(RunningSession { handle, join }) }