From be7d0878408efa56c99260a018b83a49a0976af2 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Wed, 21 Feb 2024 22:45:02 -0800 Subject: [PATCH 01/25] Migrate to RustCrypto/SSH Signed-off-by: Arthur Gautier Signed-off-by: Wiktor Kwapisiewicz --- Cargo.lock | 346 ++++++++++++++++++++++++++++++--------- Cargo.toml | 7 +- examples/key_storage.rs | 34 ++-- src/agent.rs | 12 +- src/error.rs | 15 +- src/proto/de.rs | 267 ------------------------------ src/proto/error.rs | 19 --- src/proto/extension.rs | 18 +- src/proto/key_type.rs | 63 ------- src/proto/message.rs | 312 +++++++++++++++++++++++++++-------- src/proto/mod.rs | 73 --------- src/proto/private_key.rs | 109 ------------ src/proto/public_key.rs | 191 --------------------- src/proto/signature.rs | 43 ----- src/proto/tests/mod.rs | 6 +- 15 files changed, 571 insertions(+), 944 deletions(-) delete mode 100644 src/proto/de.rs delete mode 100644 src/proto/private_key.rs delete mode 100644 src/proto/public_key.rs diff --git a/Cargo.lock b/Cargo.lock index e222dce..6fe3d5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,18 +19,18 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anstream" -version = "0.6.12" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -76,9 +76,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "a507401cad91ec6a857ed5513a2073c82a9b9048762b885bb98655b306964681" dependencies = [ "proc-macro2", "quote", @@ -87,15 +87,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -106,6 +106,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64ct" version = "1.6.0" @@ -129,18 +135,15 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" [[package]] name = "cfg-if" @@ -148,6 +151,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[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 = "colorchoice" version = "1.0.0" @@ -169,6 +182,18 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -199,6 +224,40 @@ dependencies = [ "block-buffer", "const-oid", "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", ] [[package]] @@ -213,9 +272,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c012a26a7f605efc424dd53697843a72be7dc86ad2d01f7814337794a12231d" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" dependencies = [ "anstream", "anstyle", @@ -224,6 +283,16 @@ dependencies = [ "log", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "futures" version = "0.3.30" @@ -321,6 +390,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -340,11 +410,31 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "hermit-abi" -version = "0.3.6" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hmac" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd5256b483761cd23699d0da46cc6fd2ee3be420bbe6d020ae4a091e70b7e9fd" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] [[package]] name = "humantime" @@ -352,6 +442,15 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -375,9 +474,9 @@ checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" @@ -396,9 +495,9 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", @@ -477,6 +576,44 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p521" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc9e2161f1f215afdfce23677034ae137bbd45016a880c2eb3ba8eb95f085b2" +dependencies = [ + "base16ct", + "ecdsa", + "elliptic-curve", + "primeorder", + "rand_core", + "sha2", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -525,11 +662,20 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e" dependencies = [ "unicode-ident", ] @@ -575,9 +721,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.3" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -587,9 +733,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -602,6 +748,16 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "rsa" version = "0.9.6" @@ -631,23 +787,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "serde" -version = "1.0.196" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.196" +name = "sec1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "proc-macro2", - "quote", - "syn", + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", ] [[package]] @@ -705,12 +855,12 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -741,13 +891,57 @@ dependencies = [ "log", "rand", "rsa", - "serde", "service-binding", "sha1", + "signature", + "ssh-encoding", + "ssh-key", "tokio", "tokio-util", ] +[[package]] +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "cipher", + "ssh-encoding", +] + +[[package]] +name = "ssh-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb9242b9ef4108a78e8cd1a2c98e193ef372437f8c22be363075233321dd4a15" +dependencies = [ + "base64ct", + "pem-rfc7468", + "sha2", +] + +[[package]] +name = "ssh-key" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b71299a724c8d84956caaf8fc3b3ea57c3587fe2d0b800cd0dc1f3599905d7e" +dependencies = [ + "num-bigint-dig", + "p256", + "p384", + "p521", + "rand_core", + "rsa", + "sec1", + "sha2", + "signature", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + [[package]] name = "subtle" version = "2.5.0" @@ -756,9 +950,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" -version = "2.0.48" +version = "2.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "002a1b3dbf967edfafc32655d0f377ab0bb7b994aa1d32c8cc7e9b8bf3ebb8f0" dependencies = [ "proc-macro2", "quote", @@ -870,7 +1064,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.4", ] [[package]] @@ -890,17 +1084,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.4", + "windows_aarch64_msvc 0.52.4", + "windows_i686_gnu 0.52.4", + "windows_i686_msvc 0.52.4", + "windows_x86_64_gnu 0.52.4", + "windows_x86_64_gnullvm 0.52.4", + "windows_x86_64_msvc 0.52.4", ] [[package]] @@ -911,9 +1105,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9" [[package]] name = "windows_aarch64_msvc" @@ -923,9 +1117,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675" [[package]] name = "windows_i686_gnu" @@ -935,9 +1129,9 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3" [[package]] name = "windows_i686_msvc" @@ -947,9 +1141,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02" [[package]] name = "windows_x86_64_gnu" @@ -959,9 +1153,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03" [[package]] name = "windows_x86_64_gnullvm" @@ -971,9 +1165,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177" [[package]] name = "windows_x86_64_msvc" @@ -983,9 +1177,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8" [[package]] name = "zeroize" diff --git a/Cargo.toml b/Cargo.toml index a922e8d..56a0676 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,9 +15,11 @@ keywords = ["ssh", "agent", "authentication", "openssh", "async"] categories = ["authentication", "cryptography", "encoding", "network-programming", "parsing"] exclude = [".github"] +[patch.crates-io] +#ssh-key = { path = "../../SSH/ssh-key" } + [dependencies] byteorder = "1.4.3" -serde = {version = "1", features = ["derive"]} async-trait = { version = "0.1.77", optional = true } bytes = { version = "1.5.0", optional = true } @@ -26,6 +28,9 @@ log = { version = "0.4.6", optional = true } tokio = { version = "1", optional = true, features = ["rt", "net"] } tokio-util = { version = "0.7.1", optional = true, features = ["codec"] } service-binding = { version = "^2" } +ssh-encoding = { version = "0.2.0" } +ssh-key = { version = "0.6.4", features = ["rsa", "alloc"] } +signature = "2" [features] default = ["agent"] diff --git a/examples/key_storage.rs b/examples/key_storage.rs index 6f387e0..cac5628 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -9,8 +9,14 @@ use ssh_agent_lib::agent::{Agent, Session}; use ssh_agent_lib::proto::message::{self, Message, SignRequest}; use ssh_agent_lib::proto::private_key::PrivateKey; use ssh_agent_lib::proto::public_key::PublicKey; +use ssh_agent_lib::proto::signature::{self}; use ssh_agent_lib::proto::signature::{self, Signature}; use ssh_agent_lib::proto::{from_bytes, to_bytes}; +use ssh_key::{ + private::{KeypairData, PrivateKey, RsaKeypair}, + public::PublicKey, + Algorithm, HashAlg, Signature, +}; use std::error::Error; use std::sync::{Arc, Mutex}; @@ -68,11 +74,11 @@ impl KeyStorage { } fn sign(&self, sign_request: &SignRequest) -> Result> { - let pubkey: PublicKey = from_bytes(&sign_request.pubkey_blob)?; + let pubkey: PublicKey = sign_request.pubkey.clone().try_into()?; if let Some(identity) = self.identity_from_pubkey(&pubkey) { - match identity.privkey { - PrivateKey::Rsa(ref key) => { + match identity.privkey.key_data() { + KeypairData::Rsa(ref key) => { let algorithm; let private_key = rsa::RsaPrivateKey::from_components( @@ -97,11 +103,10 @@ impl KeyStorage { algorithm = "ssh-rsa"; SigningKey::::new(private_key).sign_with_rng(&mut rng, data) }; - - Ok(Signature { - algorithm: algorithm.to_string(), - blob: signature.to_bytes().to_vec(), - }) + Ok(Signature::new( + algorithm.to_string(), + signature.to_bytes().to_vec(), + )) } _ => Err(From::from("Signature for key type not implemented")), } @@ -117,27 +122,30 @@ impl KeyStorage { let mut identities = vec![]; for identity in self.identities.lock().unwrap().iter() { identities.push(message::Identity { - pubkey_blob: to_bytes(&identity.pubkey)?, + pubkey: identity.pubkey.key_data().clone(), comment: identity.comment.clone(), }) } Ok(Message::IdentitiesAnswer(identities)) } Message::RemoveIdentity(identity) => { - let pubkey: PublicKey = from_bytes(&identity.pubkey_blob)?; + let pubkey: PublicKey = identity.pubkey.try_into()?; self.identity_remove(&pubkey)?; Ok(Message::Success) } Message::AddIdentity(identity) => { + println!("add_identity0"); + let privkey = PrivateKey::try_from(identity.privkey).unwrap(); self.identity_add(Identity { - pubkey: PublicKey::from(&identity.privkey), - privkey: identity.privkey, + pubkey: PublicKey::from(&privkey), + privkey: privkey, comment: identity.comment, }); + println!("add_identity1"); Ok(Message::Success) } Message::SignRequest(request) => { - let signature = to_bytes(&self.sign(&request)?)?; + let signature = self.sign(&request)?; Ok(Message::SignResponse(signature)) } _ => Err(From::from(format!("Unknown message: {:?}", request))), diff --git a/src/agent.rs b/src/agent.rs index 15ff966..5bf78c8 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -3,6 +3,7 @@ use byteorder::{BigEndian, ReadBytesExt}; use bytes::{Buf, BufMut, BytesMut}; use futures::{SinkExt, TryStreamExt}; use log::{error, info}; +use ssh_encoding::{Decode, Encode}; use tokio::io::{AsyncRead, AsyncWrite}; #[cfg(windows)] use tokio::net::windows::named_pipe::{NamedPipeServer, ServerOptions}; @@ -18,7 +19,6 @@ use std::mem::size_of; use super::error::AgentError; use super::proto::message::Message; -use super::proto::{from_bytes, to_bytes}; #[derive(Debug)] pub struct MessageCodec; @@ -40,7 +40,7 @@ impl Decoder for MessageCodec { return Ok(None); } - let message: Message = from_bytes(bytes)?; + let message: Message = Message::decode(&mut bytes)?; src.advance(size_of::() + length); Ok(Some(message)) } @@ -50,7 +50,13 @@ impl Encoder for MessageCodec { type Error = AgentError; fn encode(&mut self, item: Message, dst: &mut BytesMut) -> Result<(), Self::Error> { - let bytes = to_bytes(&to_bytes(&item)?)?; + let mut bytes = Vec::new(); + + let len = item.encoded_len().unwrap() as u32; + len.encode(&mut bytes)?; + + item.encode(&mut bytes)?; + dst.put(&*bytes); Ok(()) } diff --git a/src/error.rs b/src/error.rs index 5d7b84f..0d63eef 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,18 +1,24 @@ -use super::proto::error::ProtoError; use std::io; #[derive(Debug)] pub enum AgentError { - Proto(ProtoError), + Ssh(ssh_key::Error), + Proto(ssh_encoding::Error), IO(io::Error), } -impl From for AgentError { - fn from(e: ProtoError) -> AgentError { +impl From for AgentError { + fn from(e: ssh_encoding::Error) -> AgentError { AgentError::Proto(e) } } +impl From for AgentError { + fn from(e: ssh_key::Error) -> AgentError { + AgentError::Ssh(e) + } +} + impl From for AgentError { fn from(e: io::Error) -> AgentError { AgentError::IO(e) @@ -22,6 +28,7 @@ impl From for AgentError { impl std::fmt::Display for AgentError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + AgentError::Ssh(e) => write!(f, "Agent: Ssh key error: {e}"), AgentError::Proto(proto) => write!(f, "Agent: Protocol error: {}", proto), AgentError::IO(error) => write!(f, "Agent: I/O error: {}", error), } diff --git a/src/proto/de.rs b/src/proto/de.rs deleted file mode 100644 index 3ea8bc6..0000000 --- a/src/proto/de.rs +++ /dev/null @@ -1,267 +0,0 @@ -use byteorder::{BigEndian, ReadBytesExt}; -use std::io; - -use serde::de::{ - self, Deserialize, DeserializeSeed, EnumAccess, IntoDeserializer, SeqAccess, VariantAccess, - Visitor, -}; - -use super::error::{ProtoError, ProtoResult}; - -#[derive(Debug)] -pub struct Deserializer { - reader: R, -} - -impl Deserializer { - pub fn from_reader(reader: R) -> Self { - Deserializer { reader } - } - - pub fn to_reader(self) -> R { - self.reader - } - - fn read_buf(&mut self) -> ProtoResult> { - let len = self.reader.read_u32::()?; - let mut buf = vec![0; len as usize]; - self.reader.read_exact(&mut buf)?; - Ok(buf) - } -} - -pub fn from_bytes<'a, T: Deserialize<'a>>(bytes: &[u8]) -> ProtoResult { - let mut deserializer = Deserializer::from_reader(bytes); - let result = T::deserialize(&mut deserializer)?; - let remaining_bytes = deserializer.to_reader(); - - if remaining_bytes.is_empty() { - Ok(result) - } else { - Err(ProtoError::Deserialization(format!( - "Buffer not depleted. Remaining bytes: {:?}", - remaining_bytes - ))) - } -} - -impl<'de, 'a, R: io::Read> de::Deserializer<'de> for &'a mut Deserializer { - type Error = ProtoError; - - fn deserialize_any>(self, visitor: V) -> ProtoResult { - let mut bytes = vec![]; - self.reader.read_to_end(&mut bytes)?; - visitor.visit_byte_buf(bytes) - } - - fn deserialize_bool>(self, visitor: V) -> ProtoResult { - visitor.visit_bool(self.reader.read_u8()? > 0) - } - - fn deserialize_i8>(self, visitor: V) -> ProtoResult { - visitor.visit_i8(self.reader.read_i8()?) - } - - fn deserialize_i16>(self, visitor: V) -> ProtoResult { - visitor.visit_i16(self.reader.read_i16::()?) - } - - fn deserialize_i32>(self, visitor: V) -> ProtoResult { - visitor.visit_i32(self.reader.read_i32::()?) - } - - fn deserialize_i64>(self, visitor: V) -> ProtoResult { - visitor.visit_i64(self.reader.read_i64::()?) - } - - fn deserialize_u8>(self, visitor: V) -> ProtoResult { - visitor.visit_u8(self.reader.read_u8()?) - } - - fn deserialize_u16>(self, visitor: V) -> ProtoResult { - visitor.visit_u16(self.reader.read_u16::()?) - } - - fn deserialize_u32>(self, visitor: V) -> ProtoResult { - visitor.visit_u32(self.reader.read_u32::()?) - } - - fn deserialize_u64>(self, visitor: V) -> ProtoResult { - visitor.visit_u64(self.reader.read_u64::()?) - } - - fn deserialize_f32>(self, visitor: V) -> ProtoResult { - visitor.visit_f32(self.reader.read_f32::()?) - } - - fn deserialize_f64>(self, visitor: V) -> ProtoResult { - visitor.visit_f64(self.reader.read_f64::()?) - } - - fn deserialize_char>(self, _visitor: V) -> ProtoResult { - unimplemented!() - } - - fn deserialize_str>(self, visitor: V) -> ProtoResult { - visitor.visit_string(String::from_utf8(self.read_buf()?)?) - } - - fn deserialize_string>(self, visitor: V) -> ProtoResult { - visitor.visit_string(String::from_utf8(self.read_buf()?)?) - } - - fn deserialize_bytes>(self, visitor: V) -> ProtoResult { - visitor.visit_byte_buf(self.read_buf()?) - } - - fn deserialize_byte_buf>(self, visitor: V) -> ProtoResult { - visitor.visit_byte_buf(self.read_buf()?) - } - - fn deserialize_option>(self, _visitor: V) -> ProtoResult { - unimplemented!() - } - - fn deserialize_unit>(self, _visitor: V) -> ProtoResult { - unimplemented!() - } - - fn deserialize_unit_struct>( - self, - _name: &'static str, - _visitor: V, - ) -> ProtoResult { - unimplemented!() - } - - fn deserialize_newtype_struct>( - self, - _name: &'static str, - visitor: V, - ) -> ProtoResult { - visitor.visit_newtype_struct(self) - } - - fn deserialize_seq>(self, visitor: V) -> ProtoResult { - let len = self.reader.read_u32::()? as usize; - visitor.visit_seq(BinarySeq::new(len, &mut *self)) - } - - fn deserialize_tuple>(self, len: usize, visitor: V) -> ProtoResult { - visitor.visit_seq(BinarySeq::new(len, &mut *self)) - } - - fn deserialize_tuple_struct>( - self, - _name: &'static str, - _len: usize, - visitor: V, - ) -> ProtoResult { - self.deserialize_seq(visitor) - } - - fn deserialize_map>(self, _visitor: V) -> ProtoResult { - unimplemented!() - } - - fn deserialize_struct>( - self, - _name: &'static str, - fields: &'static [&'static str], - visitor: V, - ) -> ProtoResult { - visitor.visit_seq(BinarySeq::new(fields.len(), &mut *self)) - } - - fn deserialize_enum>( - self, - _name: &'static str, - _variants: &'static [&'static str], - visitor: V, - ) -> ProtoResult { - visitor.visit_enum(BinaryEnum::new(&mut *self)) - } - - fn deserialize_identifier>(self, visitor: V) -> ProtoResult { - self.deserialize_str(visitor) - } - - fn deserialize_ignored_any>(self, visitor: V) -> ProtoResult { - self.deserialize_any(visitor) - } -} - -struct BinarySeq<'a, R: io::Read> { - remaining: usize, - de: &'a mut Deserializer, -} - -impl<'a, R: io::Read> BinarySeq<'a, R> { - fn new(remaining: usize, de: &'a mut Deserializer) -> Self { - BinarySeq { remaining, de } - } -} - -impl<'de, 'a, R: io::Read> SeqAccess<'de> for BinarySeq<'a, R> { - type Error = ProtoError; - - fn next_element_seed>( - &mut self, - seed: T, - ) -> ProtoResult> { - if self.remaining > 0 { - self.remaining -= 1; - seed.deserialize(&mut *self.de).map(Some) - } else { - Ok(None) - } - } -} - -struct BinaryEnum<'a, R: io::Read> { - de: &'a mut Deserializer, -} - -impl<'a, R: io::Read> BinaryEnum<'a, R> { - fn new(de: &'a mut Deserializer) -> Self { - BinaryEnum { de } - } -} - -impl<'de, 'a, R: io::Read> EnumAccess<'de> for BinaryEnum<'a, R> { - type Error = ProtoError; - type Variant = Self; - - fn variant_seed(self, seed: V) -> ProtoResult<(V::Value, Self::Variant)> - where - V: DeserializeSeed<'de>, - { - let index: u8 = de::Deserialize::deserialize(&mut *self.de)?; - let value: ProtoResult<_> = seed.deserialize(index.into_deserializer()); - Ok((value?, self)) - } -} - -impl<'de, 'a, R: io::Read> VariantAccess<'de> for BinaryEnum<'a, R> { - type Error = ProtoError; - - fn unit_variant(self) -> ProtoResult<()> { - Ok(()) - } - - fn newtype_variant_seed>(self, seed: T) -> ProtoResult { - seed.deserialize(self.de) - } - - fn tuple_variant>(self, _len: usize, visitor: V) -> ProtoResult { - de::Deserializer::deserialize_seq(self.de, visitor) - } - - fn struct_variant>( - self, - _fields: &'static [&'static str], - visitor: V, - ) -> ProtoResult { - de::Deserializer::deserialize_map(self.de, visitor) - } -} diff --git a/src/proto/error.rs b/src/proto/error.rs index 17304f5..df9bbca 100644 --- a/src/proto/error.rs +++ b/src/proto/error.rs @@ -1,5 +1,4 @@ use std::error::Error; -use std::fmt::Display; use std::{io, string}; #[derive(Debug)] @@ -8,8 +7,6 @@ pub enum ProtoError { MessageTooLong, StringEncoding(string::FromUtf8Error), IO(io::Error), - Serialization(String), - Deserialization(String), } impl From for () { @@ -28,18 +25,6 @@ impl From for ProtoError { } } -impl serde::ser::Error for ProtoError { - fn custom(msg: T) -> Self { - ProtoError::Serialization(msg.to_string()) - } -} - -impl serde::de::Error for ProtoError { - fn custom(msg: T) -> Self { - ProtoError::Deserialization(msg.to_string()) - } -} - impl std::error::Error for ProtoError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { @@ -47,8 +32,6 @@ impl std::error::Error for ProtoError { ProtoError::MessageTooLong => None, ProtoError::StringEncoding(e) => Some(e), ProtoError::IO(e) => Some(e), - ProtoError::Serialization(_) => None, - ProtoError::Deserialization(_) => None, } } } @@ -60,8 +43,6 @@ impl std::fmt::Display for ProtoError { ProtoError::MessageTooLong => f.write_str("Message too long"), ProtoError::StringEncoding(_) => f.write_str("String encoding failed"), ProtoError::IO(_) => f.write_str("I/O Error"), - ProtoError::Serialization(_) => f.write_str("Serialization Error"), - ProtoError::Deserialization(_) => f.write_str("Deserialization Error"), } } } diff --git a/src/proto/extension.rs b/src/proto/extension.rs index 76cda23..7eb3af8 100644 --- a/src/proto/extension.rs +++ b/src/proto/extension.rs @@ -1,14 +1,4 @@ -use serde::{Deserialize, Serialize}; - -use super::recursive; -use super::signature::Signature; - -/// SSH key -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct SshKey { - pub alg: String, - pub blob: Vec, -} +use ssh_key::{public::KeyData, Signature}; /// session-bind@openssh.com extension /// @@ -17,12 +7,10 @@ pub struct SshKey { /// /// Spec: /// -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone)] pub struct SessionBind { - #[serde(with = "recursive")] - pub host_key: SshKey, + pub host_key: KeyData, pub session_id: Vec, - #[serde(with = "recursive")] pub signature: Signature, pub is_forwarding: bool, } diff --git a/src/proto/key_type.rs b/src/proto/key_type.rs index 036c447..4d7585e 100644 --- a/src/proto/key_type.rs +++ b/src/proto/key_type.rs @@ -8,66 +8,3 @@ pub trait KeyType { Self::KEY_TYPE.to_string() } } - -macro_rules! impl_key_type_enum_ser_de { - ($class_name:path, $(($variant_name:path, $variant_class:ty)),* ) => { - impl KeyTypeEnum for $class_name { - fn key_type(&self) -> String { - match self { - $($variant_name(key) => key.key_type()),* - } - } - } - - impl Serialize for $class_name { - fn serialize(&self, serializer: S) -> Result { - let mut serialize_tuple = serializer.serialize_tuple(2)?; - - match self { - $( - $variant_name(key) => { - serialize_tuple.serialize_element(&key.key_type())?; - serialize_tuple.serialize_element(key)?; - } - ),* - }; - serialize_tuple.end() - } - } - - impl<'de> Deserialize<'de> for $class_name { - fn deserialize>(deserializer: D) -> Result<$class_name, D::Error> { - struct KeyVisitor; - - impl<'de> serde::de::Visitor<'de> for KeyVisitor { - type Value = $class_name; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("Key with format (type, key)") - } - - fn visit_seq>( - self, - mut seq: V - ) -> Result { - let key_type: String = seq.next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?; - let key_type_str = key_type.as_str(); - - $( - if key_type_str.starts_with(<$variant_class>::KEY_TYPE) { - let key: $variant_class = seq.next_element()? - .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?; - return Ok($variant_name(key)) - } - )* - - return Err(Error::custom(ProtoError::UnexpectedVariant)); - } - } - - deserializer.deserialize_tuple(2, KeyVisitor) - } - } - }; -} diff --git a/src/proto/message.rs b/src/proto/message.rs index 9f24086..c0eccc8 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -1,124 +1,223 @@ -use serde::de::{Deserializer, Visitor}; -use serde::ser::SerializeTuple; -use serde::{Deserialize, Serialize}; +use ssh_encoding::{CheckedSum, Decode, Encode, Reader, Writer}; +use ssh_key::{private::KeypairData, public::KeyData, Error, Result, Signature}; -use super::private_key::PrivateKey; - -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug)] pub struct Identity { - pub pubkey_blob: Vec, + pub pubkey: KeyData, pub comment: String, } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +impl Decode for Identity { + type Error = Error; + + fn decode(reader: &mut impl Reader) -> Result { + let pubkey = reader.read_prefixed(KeyData::decode)?; + let comment = String::decode(reader)?; + + Ok(Self { pubkey, comment }) + } +} + +impl Encode for Identity { + fn encoded_len(&self) -> ssh_encoding::Result { + [ + self.pubkey.encoded_len_prefixed()?, + self.comment.encoded_len()?, + ] + .checked_sum() + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + self.pubkey.encode_prefixed(writer)?; + self.comment.encode(writer)?; + + Ok(()) + } +} + +#[derive(Clone, PartialEq, Debug)] pub struct SignRequest { - pub pubkey_blob: Vec, + pub pubkey: KeyData, pub data: Vec, pub flags: u32, } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +impl Decode for SignRequest { + type Error = Error; + + fn decode(reader: &mut impl Reader) -> Result { + let pubkey = KeyData::decode(reader)?; + let data = Vec::decode(reader)?; + let flags = u32::decode(reader)?; + + Ok(Self { + pubkey, + data, + flags, + }) + } +} + +impl Encode for SignRequest { + fn encoded_len(&self) -> ssh_encoding::Result { + [ + self.pubkey.encoded_len()?, + self.data.encoded_len()?, + self.flags.encoded_len()?, + ] + .checked_sum() + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + self.pubkey.encode(writer)?; + self.data.encode(writer)?; + self.flags.encode(writer)?; + + Ok(()) + } +} + +#[derive(Clone, PartialEq, Debug)] pub struct AddIdentity { - pub privkey: PrivateKey, + pub privkey: KeypairData, pub comment: String, } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +impl Decode for AddIdentity { + type Error = Error; + + fn decode(reader: &mut impl Reader) -> Result { + println!("foo0"); + let privkey = KeypairData::decode(reader)?; + println!("foo1"); + let comment = String::decode(reader)?; + println!("foo2"); + + Ok(Self { privkey, comment }) + } +} + +#[derive(Clone, PartialEq, Debug)] pub struct AddIdentityConstrained { pub identity: AddIdentity, pub constraints: Vec, } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +impl Decode for AddIdentityConstrained { + type Error = Error; + + fn decode(reader: &mut impl Reader) -> Result { + todo!(); + //let identity = AddIdentity::decode(reader)?; + //let constraints = Vec::decode(reader)?; + + //Ok(Self { + // identity, + // constraints, + //}) + } +} + +#[derive(Clone, PartialEq, Debug)] pub struct RemoveIdentity { - pub pubkey_blob: Vec, + pub pubkey: KeyData, +} + +impl Decode for RemoveIdentity { + type Error = Error; + + fn decode(reader: &mut impl Reader) -> Result { + let pubkey = reader.read_prefixed(KeyData::decode)?; + + Ok(Self { pubkey }) + } } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug)] pub struct SmartcardKey { pub id: String, pub pin: String, } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +impl Decode for SmartcardKey { + type Error = Error; + + fn decode(reader: &mut impl Reader) -> Result { + let id = String::decode(reader)?; + let pin = String::decode(reader)?; + + Ok(Self { id, pin }) + } +} + +#[derive(Clone, PartialEq, Debug)] pub struct KeyConstraint { pub constraint_type: u8, pub constraint_data: Vec, } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +impl Decode for KeyConstraint { + type Error = Error; + + fn decode(reader: &mut impl Reader) -> Result { + let constraint_type = u8::decode(reader)?; + let constraint_data = Vec::decode(reader)?; + + Ok(Self { + constraint_type, + constraint_data, + }) + } +} + +#[derive(Clone, PartialEq, Debug)] pub struct AddSmartcardKeyConstrained { pub key: SmartcardKey, pub constraints: Vec, } -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct Extension { - pub extension_type: String, - pub extension_contents: ExtensionContents, -} +impl Decode for AddSmartcardKeyConstrained { + type Error = Error; -#[derive(Debug, PartialEq, Clone)] -pub struct ExtensionContents(pub Vec); + fn decode(reader: &mut impl Reader) -> Result { + todo!() + //let key = SmartcardKey::decode(reader)?; + //let constraints = Vec::decode(reader)?; -impl Serialize for ExtensionContents { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut seq = serializer.serialize_tuple(self.0.len())?; - for i in &self.0 { - seq.serialize_element(i)?; - } - seq.end() + //Ok(Self { key, constraints }) } } -impl<'de> Deserialize<'de> for ExtensionContents { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct ContentsVisitor; - - impl<'de> Visitor<'de> for ContentsVisitor { - type Value = ExtensionContents; +#[derive(Clone, PartialEq, Debug)] +pub struct Extension { + pub extension_type: String, + pub extension_contents: ExtensionContents, +} - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("raw bytes buffer") - } +impl Decode for Extension { + type Error = Error; - fn visit_byte_buf(self, v: Vec) -> Result { - Ok(ExtensionContents(v)) - } - } + fn decode(reader: &mut impl Reader) -> Result { + todo!() + //let key = SmartcardKey::decode(reader)?; + //let constraints = Vec::decode(reader)?; - deserializer.deserialize_any(ContentsVisitor) + //Ok(Self { key, constraints }) } } +#[derive(Debug, PartialEq, Clone)] +pub struct ExtensionContents(pub Vec); pub type Passphrase = String; -pub type SignatureBlob = Vec; -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Debug)] pub enum Message { - Reserved0, - Reserved1, - Reserved2, - Reserved3, - Reserved4, Failure, Success, - Reserved7, - Reserved8, - Reserved9, - Reserved10, RequestIdentities, IdentitiesAnswer(Vec), SignRequest(SignRequest), - SignResponse(SignatureBlob), - Reserved15, - Reserved16, + SignResponse(Signature), AddIdentity(AddIdentity), RemoveIdentity(RemoveIdentity), RemoveAllIdentities, @@ -126,9 +225,90 @@ pub enum Message { RemoveSmartcardKey(SmartcardKey), Lock(Passphrase), Unlock(Passphrase), - Reserved24, AddIdConstrained(AddIdentityConstrained), AddSmartcardKeyConstrained(AddSmartcardKeyConstrained), Extension(Extension), ExtensionFailure, } + +impl Decode for Message { + type Error = Error; + + fn decode(reader: &mut impl Reader) -> Result { + println!("foo0"); + let message_type = u8::decode(reader)?; + println!("foo1"); + + match message_type { + 5 => Ok(Self::Failure), + 6 => Ok(Self::Success), + 11 => Ok(Self::RequestIdentities), + 12 => todo!(), + 13 => SignRequest::decode(reader).map(Self::SignRequest), + 14 => Signature::decode(reader).map(Self::SignResponse), + 17 => AddIdentity::decode(reader).map(Self::AddIdentity), + 18 => RemoveIdentity::decode(reader).map(Self::RemoveIdentity), + 19 => Ok(Self::RemoveAllIdentities), + 20 => SmartcardKey::decode(reader).map(Self::AddSmartcardKey), + 21 => SmartcardKey::decode(reader).map(Self::RemoveSmartcardKey), + 22 => Ok(Passphrase::decode(reader).map(Self::Lock)?), + 23 => Ok(Passphrase::decode(reader).map(Self::Unlock)?), + 25 => AddIdentityConstrained::decode(reader).map(Self::AddIdConstrained), + 26 => AddSmartcardKeyConstrained::decode(reader).map(Self::AddSmartcardKeyConstrained), + 27 => Extension::decode(reader).map(Self::Extension), + 28 => Ok(Self::ExtensionFailure), + _ => todo!(), + } + } +} + +impl Encode for Message { + fn encoded_len(&self) -> ssh_encoding::Result { + let command_id = 1; + let payload_len = match self { + Self::Failure => 0, + Self::Success => 0, + Self::RequestIdentities => 0, + Self::IdentitiesAnswer(ids) => { + let mut lengths = Vec::with_capacity(1 + ids.len()); + // Prefixed length + lengths.push(4); + + for id in ids { + lengths.push(id.encoded_len()?); + } + + lengths.checked_sum()? + } + _ => todo!(), + }; + + [command_id, payload_len].checked_sum() + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + let command_id: u8 = match self { + Self::Failure => 5, + Self::Success => 6, + Self::RequestIdentities => 11, + Self::IdentitiesAnswer(_) => 12, + _ => todo!(), + }; + + command_id.encode(writer)?; + match self { + Self::Failure => {} + Self::Success => {} + Self::RequestIdentities => {} + Self::IdentitiesAnswer(ids) => { + (ids.len() as u32).encode(writer)?; + for id in ids { + id.encode(writer)?; + } + } + _ => todo!(), + }; + + Ok(()) + } +} diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 59a4663..698bc12 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -1,89 +1,16 @@ -pub mod de; -pub mod ser; - #[macro_use] pub mod key_type; pub mod error; pub mod extension; pub mod message; -pub mod private_key; -pub mod public_key; pub mod signature; #[cfg(test)] mod tests; -pub use self::de::from_bytes; -pub use self::ser::to_bytes; - pub use self::error::*; pub use self::key_type::*; pub use self::message::*; -pub use self::private_key::*; -pub use self::public_key::*; pub use self::signature::*; -use serde::{Deserialize, Serialize}; - pub type MpInt = Vec; - -pub trait Blob: Sized { - fn to_blob(&self) -> ProtoResult>; - fn from_blob(blob: &[u8]) -> ProtoResult; -} - -impl<'a, T: Serialize + Deserialize<'a>> Blob for T { - fn to_blob(&self) -> ProtoResult> { - to_bytes(self) - } - - fn from_blob(blob: &[u8]) -> ProtoResult { - from_bytes(blob) - } -} - -pub mod recursive { - use super::{from_bytes, to_bytes}; - use serde::{ - de::{self, Deserializer, Visitor}, - ser::{Error, Serializer}, - Deserialize, Serialize, - }; - use std::{fmt, marker::PhantomData}; - - pub fn serialize(obj: &T, serializer: S) -> Result - where - T: Serialize, - S: Serializer, - { - serializer.serialize_bytes(&to_bytes(obj).map_err(S::Error::custom)?) - } - - pub fn deserialize<'de, T, D>(deserialize: D) -> Result - where - T: Deserialize<'de>, - D: Deserializer<'de>, - { - struct RecursiveVisitor(PhantomData); - - impl<'de, T> Visitor<'de> for RecursiveVisitor - where - T: Deserialize<'de>, - { - type Value = T; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("an integer between -2^31 and 2^31") - } - - fn visit_bytes(self, value: &[u8]) -> Result - where - E: de::Error, - { - from_bytes(value).map_err(E::custom) - } - } - - deserialize.deserialize_bytes(RecursiveVisitor(PhantomData::)) - } -} diff --git a/src/proto/private_key.rs b/src/proto/private_key.rs deleted file mode 100644 index 6190428..0000000 --- a/src/proto/private_key.rs +++ /dev/null @@ -1,109 +0,0 @@ -use super::error::ProtoError; -use super::key_type::{KeyType, KeyTypeEnum}; -use super::MpInt; -use serde::de::{Deserializer, Error}; -use serde::ser::{SerializeTuple, Serializer}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct DssPrivateKey { - pub p: MpInt, - pub q: MpInt, - pub g: MpInt, - pub y: MpInt, - pub x: MpInt, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct Ed25519PrivateKey { - pub enc_a: Vec, - pub k_enc_a: Vec, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct SkEd25519PrivateKey { - pub enc_a: Vec, - pub application: String, - pub flags: u8, - pub key_handle: Vec, - pub reserved: Vec, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct RsaPrivateKey { - pub n: MpInt, - pub e: MpInt, - pub d: MpInt, - pub iqmp: MpInt, - pub p: MpInt, - pub q: MpInt, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct EcDsaPrivateKey { - pub identifier: String, - pub q: MpInt, - pub d: MpInt, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct SkEcDsaPrivateKey { - pub identifier: String, - pub q: MpInt, - pub application: String, - pub flags: u8, - pub key_handle: Vec, - pub reserved: Vec, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub enum PrivateKey { - Dss(DssPrivateKey), - Ed25519(Ed25519PrivateKey), - SkEd25519(SkEd25519PrivateKey), - Rsa(RsaPrivateKey), - EcDsa(EcDsaPrivateKey), - SkEcDsa(SkEcDsaPrivateKey), -} - -impl KeyType for RsaPrivateKey { - const KEY_TYPE: &'static str = "ssh-rsa"; -} - -impl KeyType for DssPrivateKey { - const KEY_TYPE: &'static str = "ssh-dss"; -} - -impl KeyType for Ed25519PrivateKey { - const KEY_TYPE: &'static str = "ssh-ed25519"; -} - -impl KeyType for SkEd25519PrivateKey { - const KEY_TYPE: &'static str = "sk-ssh-ed25519@openssh.com"; -} - -impl KeyType for EcDsaPrivateKey { - const KEY_TYPE: &'static str = "ecdsa-sha2"; - - fn key_type(&self) -> String { - format!("{}-{}", Self::KEY_TYPE, self.identifier) - } -} - -impl KeyType for SkEcDsaPrivateKey { - const KEY_TYPE: &'static str = "sk-ecdsa-sha2"; - - fn key_type(&self) -> String { - format!("{}-{}@openssh.com", Self::KEY_TYPE, self.identifier) - } -} - -impl_key_type_enum_ser_de!( - PrivateKey, - (PrivateKey::Dss, DssPrivateKey), - (PrivateKey::Rsa, RsaPrivateKey), - (PrivateKey::EcDsa, EcDsaPrivateKey), - (PrivateKey::SkEcDsa, SkEcDsaPrivateKey), - (PrivateKey::Ed25519, Ed25519PrivateKey), - (PrivateKey::SkEd25519, SkEd25519PrivateKey) -); diff --git a/src/proto/public_key.rs b/src/proto/public_key.rs deleted file mode 100644 index 7e28139..0000000 --- a/src/proto/public_key.rs +++ /dev/null @@ -1,191 +0,0 @@ -use super::error::ProtoError; -use super::key_type::{KeyType, KeyTypeEnum}; -use super::private_key::*; -use super::MpInt; -use serde::de::{Deserializer, Error}; -use serde::ser::{SerializeTuple, Serializer}; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct RsaPublicKey { - pub e: MpInt, - pub n: MpInt, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct DssPublicKey { - pub p: MpInt, - pub q: MpInt, - pub g: MpInt, - pub y: MpInt, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct EcDsaPublicKey { - pub identifier: String, - pub q: MpInt, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct SkEcDsaPublicKey { - pub identifier: String, - pub q: MpInt, - pub application: String, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct Ed25519PublicKey { - pub enc_a: Vec, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] -pub struct SkEd25519PublicKey { - pub enc_a: Vec, - pub application: String, -} - -#[derive(Clone, Debug, Eq, Hash, PartialEq)] -pub enum PublicKey { - Dss(DssPublicKey), - Ed25519(Ed25519PublicKey), - SkEd25519(SkEd25519PublicKey), - Rsa(RsaPublicKey), - EcDsa(EcDsaPublicKey), - SkEcDsa(SkEcDsaPublicKey), -} - -impl KeyType for RsaPublicKey { - const KEY_TYPE: &'static str = RsaPrivateKey::KEY_TYPE; -} - -impl KeyType for DssPublicKey { - const KEY_TYPE: &'static str = DssPrivateKey::KEY_TYPE; -} - -impl KeyType for Ed25519PublicKey { - const KEY_TYPE: &'static str = Ed25519PrivateKey::KEY_TYPE; -} - -impl KeyType for EcDsaPublicKey { - const KEY_TYPE: &'static str = EcDsaPrivateKey::KEY_TYPE; - - fn key_type(&self) -> String { - format!("{}-{}", Self::KEY_TYPE, self.identifier) - } -} - -impl KeyType for SkEd25519PublicKey { - const KEY_TYPE: &'static str = "sk-ssh-ed25519@openssh.com"; -} - -impl KeyType for SkEcDsaPublicKey { - const KEY_TYPE: &'static str = "sk-ecdsa-sha2"; - - fn key_type(&self) -> String { - format!("{}-{}@openssh.com", Self::KEY_TYPE, self.identifier) - } -} - -impl From for PublicKey { - fn from(key: PrivateKey) -> Self { - match key { - PrivateKey::Dss(key) => PublicKey::Dss(DssPublicKey::from(key)), - PrivateKey::Ed25519(key) => PublicKey::Ed25519(Ed25519PublicKey::from(key)), - PrivateKey::SkEd25519(key) => PublicKey::SkEd25519(SkEd25519PublicKey::from(key)), - PrivateKey::Rsa(key) => PublicKey::Rsa(RsaPublicKey::from(key)), - PrivateKey::EcDsa(key) => PublicKey::EcDsa(EcDsaPublicKey::from(key)), - PrivateKey::SkEcDsa(key) => PublicKey::SkEcDsa(SkEcDsaPublicKey::from(key)), - } - } -} - -impl From for RsaPublicKey { - fn from(key: RsaPrivateKey) -> Self { - Self { e: key.e, n: key.n } - } -} - -impl From for DssPublicKey { - fn from(key: DssPrivateKey) -> Self { - Self { - p: key.p, - q: key.q, - g: key.g, - y: key.y, - } - } -} - -impl From for EcDsaPublicKey { - fn from(key: EcDsaPrivateKey) -> Self { - Self { - identifier: key.identifier, - q: key.q, - } - } -} - -impl From for SkEcDsaPublicKey { - fn from(key: SkEcDsaPrivateKey) -> Self { - Self { - identifier: key.identifier, - q: key.q, - application: key.application, - } - } -} - -impl From for Ed25519PublicKey { - fn from(key: Ed25519PrivateKey) -> Self { - Self { enc_a: key.enc_a } - } -} - -impl From for SkEd25519PublicKey { - fn from(key: SkEd25519PrivateKey) -> Self { - Self { - enc_a: key.enc_a, - application: key.application, - } - } -} - -impl From<&PrivateKey> for PublicKey { - fn from(key: &PrivateKey) -> Self { - Self::from(key.clone()) - } -} - -impl From<&RsaPrivateKey> for RsaPublicKey { - fn from(key: &RsaPrivateKey) -> Self { - Self::from(key.clone()) - } -} - -impl From<&DssPrivateKey> for DssPublicKey { - fn from(key: &DssPrivateKey) -> Self { - Self::from(key.clone()) - } -} - -impl From<&EcDsaPrivateKey> for EcDsaPublicKey { - fn from(key: &EcDsaPrivateKey) -> Self { - Self::from(key.clone()) - } -} - -impl From<&Ed25519PrivateKey> for Ed25519PublicKey { - fn from(key: &Ed25519PrivateKey) -> Self { - Self::from(key.clone()) - } -} - -impl_key_type_enum_ser_de!( - PublicKey, - (PublicKey::Dss, DssPublicKey), - (PublicKey::Rsa, RsaPublicKey), - (PublicKey::EcDsa, EcDsaPublicKey), - (PublicKey::SkEcDsa, SkEcDsaPublicKey), - (PublicKey::Ed25519, Ed25519PublicKey), - (PublicKey::SkEd25519, SkEd25519PublicKey) -); diff --git a/src/proto/signature.rs b/src/proto/signature.rs index f0665d2..b9d5672 100644 --- a/src/proto/signature.rs +++ b/src/proto/signature.rs @@ -1,45 +1,2 @@ -use serde::{Deserialize, Serialize}; - -use super::key_type::KeyType; -use super::private_key::*; -use super::to_bytes; - -pub type MpInt = Vec; - pub const RSA_SHA2_256: u32 = 0x02; pub const RSA_SHA2_512: u32 = 0x04; - -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct Signature { - pub algorithm: String, - pub blob: Vec, -} - -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct SkSignature { - pub algorithm: String, - pub blob: Vec, - pub flags: u8, - pub counter: u32, -} - -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct EcDsaSignature { - pub identifier: String, - pub data: EcDsaSignatureData, -} - -#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)] -pub struct EcDsaSignatureData { - pub r: Vec, - pub s: Vec, -} - -impl From for Signature { - fn from(signature: EcDsaSignature) -> Signature { - Signature { - algorithm: format!("{}-{}", EcDsaPrivateKey::KEY_TYPE, signature.identifier), - blob: to_bytes(&signature.data).unwrap(), - } - } -} diff --git a/src/proto/tests/mod.rs b/src/proto/tests/mod.rs index 3cdc5fa..70c1164 100644 --- a/src/proto/tests/mod.rs +++ b/src/proto/tests/mod.rs @@ -2,8 +2,12 @@ use super::extension::SessionBind; use super::message::{Extension, Identity, Message, SignRequest}; use super::private_key::*; use super::public_key::*; -use super::signature::Signature; use super::{from_bytes, to_bytes, Blob}; +use ssh_key::private::Ed25519PrivateKey; +use ssh_key::public::RsaPublicKey; +use ssh_key::PrivateKey; +use ssh_key::PublicKey; +use ssh_key::Signature; #[test] fn pubkey_blob_serialization() { From 55056391acdd892dc02e08a7c327633800a04c0a Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 26 Mar 2024 15:46:48 +0100 Subject: [PATCH 02/25] Fix compilation errors Signed-off-by: Wiktor Kwapisiewicz --- README.md | 6 +- examples/key_storage.rs | 25 ++---- src/proto/message.rs | 6 +- src/proto/mod.rs | 3 - src/proto/tests/mod.rs | 191 ---------------------------------------- 5 files changed, 17 insertions(+), 214 deletions(-) delete mode 100644 src/proto/tests/mod.rs diff --git a/README.md b/README.md index 4f60e20..9484c26 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ use ssh_agent_lib::agent::NamedPipeListener; use ssh_agent_lib::agent::{Session, Agent}; use ssh_agent_lib::proto::message::Message; +use ssh_key::{Algorithm, Signature}; #[derive(Default)] struct MyAgent; @@ -31,7 +32,10 @@ impl Session for MyAgent { Message::SignRequest(request) => { // get the signature by signing `request.data` let signature = vec![]; - Ok(Message::SignResponse(signature)) + Ok(Message::SignResponse(Signature::new( + Algorithm::new("algorithm")?, + signature, + )?)) }, _ => Ok(Message::Failure), } diff --git a/examples/key_storage.rs b/examples/key_storage.rs index cac5628..a2e3cec 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -7,15 +7,11 @@ use tokio::net::UnixListener; use ssh_agent_lib::agent::{Agent, Session}; use ssh_agent_lib::proto::message::{self, Message, SignRequest}; -use ssh_agent_lib::proto::private_key::PrivateKey; -use ssh_agent_lib::proto::public_key::PublicKey; -use ssh_agent_lib::proto::signature::{self}; -use ssh_agent_lib::proto::signature::{self, Signature}; -use ssh_agent_lib::proto::{from_bytes, to_bytes}; +use ssh_agent_lib::proto::signature; use ssh_key::{ - private::{KeypairData, PrivateKey, RsaKeypair}, + private::{KeypairData, PrivateKey}, public::PublicKey, - Algorithm, HashAlg, Signature, + Algorithm, Signature, }; use std::error::Error; @@ -82,13 +78,10 @@ impl KeyStorage { let algorithm; let private_key = rsa::RsaPrivateKey::from_components( - BigUint::from_bytes_be(&key.n), - BigUint::from_bytes_be(&key.e), - BigUint::from_bytes_be(&key.d), - vec![ - BigUint::from_bytes_be(&key.p), - BigUint::from_bytes_be(&key.q), - ], + BigUint::from_bytes_be(&key.public.n.as_bytes()), + BigUint::from_bytes_be(&key.public.e.as_bytes()), + BigUint::from_bytes_be(&key.private.d.as_bytes()), + vec![], )?; let mut rng = rand::thread_rng(); let data = &sign_request.data; @@ -104,9 +97,9 @@ impl KeyStorage { SigningKey::::new(private_key).sign_with_rng(&mut rng, data) }; Ok(Signature::new( - algorithm.to_string(), + Algorithm::new(algorithm)?, signature.to_bytes().to_vec(), - )) + )?) } _ => Err(From::from("Signature for key type not implemented")), } diff --git a/src/proto/message.rs b/src/proto/message.rs index c0eccc8..0ccd1c0 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -106,7 +106,7 @@ pub struct AddIdentityConstrained { impl Decode for AddIdentityConstrained { type Error = Error; - fn decode(reader: &mut impl Reader) -> Result { + fn decode(_reader: &mut impl Reader) -> Result { todo!(); //let identity = AddIdentity::decode(reader)?; //let constraints = Vec::decode(reader)?; @@ -179,7 +179,7 @@ pub struct AddSmartcardKeyConstrained { impl Decode for AddSmartcardKeyConstrained { type Error = Error; - fn decode(reader: &mut impl Reader) -> Result { + fn decode(_reader: &mut impl Reader) -> Result { todo!() //let key = SmartcardKey::decode(reader)?; //let constraints = Vec::decode(reader)?; @@ -197,7 +197,7 @@ pub struct Extension { impl Decode for Extension { type Error = Error; - fn decode(reader: &mut impl Reader) -> Result { + fn decode(_reader: &mut impl Reader) -> Result { todo!() //let key = SmartcardKey::decode(reader)?; //let constraints = Vec::decode(reader)?; diff --git a/src/proto/mod.rs b/src/proto/mod.rs index 698bc12..94251ee 100644 --- a/src/proto/mod.rs +++ b/src/proto/mod.rs @@ -5,9 +5,6 @@ pub mod extension; pub mod message; pub mod signature; -#[cfg(test)] -mod tests; - pub use self::error::*; pub use self::key_type::*; pub use self::message::*; diff --git a/src/proto/tests/mod.rs b/src/proto/tests/mod.rs deleted file mode 100644 index 70c1164..0000000 --- a/src/proto/tests/mod.rs +++ /dev/null @@ -1,191 +0,0 @@ -use super::extension::SessionBind; -use super::message::{Extension, Identity, Message, SignRequest}; -use super::private_key::*; -use super::public_key::*; -use super::{from_bytes, to_bytes, Blob}; -use ssh_key::private::Ed25519PrivateKey; -use ssh_key::public::RsaPublicKey; -use ssh_key::PrivateKey; -use ssh_key::PublicKey; -use ssh_key::Signature; - -#[test] -fn pubkey_blob_serialization() { - let rsa_key = PublicKey::Rsa(RsaPublicKey { - e: vec![1, 0, 1], - n: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - }); - let serde_rsa_key = PublicKey::from_blob(&rsa_key.to_blob().unwrap()).unwrap(); - assert_eq!(rsa_key, serde_rsa_key); - - let dss_key = PublicKey::Dss(DssPublicKey { - p: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - q: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - g: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - y: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - }); - let serde_dss_key = PublicKey::from_blob(&dss_key.to_blob().unwrap()).unwrap(); - assert_eq!(dss_key, serde_dss_key); - - let ed25519_key = PublicKey::Ed25519(Ed25519PublicKey { - enc_a: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - }); - let serde_ed25519_key = PublicKey::from_blob(&ed25519_key.to_blob().unwrap()).unwrap(); - assert_eq!(ed25519_key, serde_ed25519_key); - - let ecdsa_key = PublicKey::EcDsa(EcDsaPublicKey { - identifier: "some_identifier".to_string(), - q: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - }); - let serde_ecdsa_key = PublicKey::from_blob(&ecdsa_key.to_blob().unwrap()).unwrap(); - assert_eq!(ecdsa_key, serde_ecdsa_key); -} - -#[test] -fn privkey_blob_serialization() { - let rsa_key = PrivateKey::Rsa(RsaPrivateKey { - e: vec![1, 0, 1], - n: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - d: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - iqmp: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - p: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - q: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - }); - let serde_rsa_key = PrivateKey::from_blob(&rsa_key.to_blob().unwrap()).unwrap(); - assert_eq!(rsa_key, serde_rsa_key); - - let dss_key = PrivateKey::Dss(DssPrivateKey { - p: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - q: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - g: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - y: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - x: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - }); - let serde_dss_key = PrivateKey::from_blob(&dss_key.to_blob().unwrap()).unwrap(); - assert_eq!(dss_key, serde_dss_key); - - let ed25519_key = PrivateKey::Ed25519(Ed25519PrivateKey { - enc_a: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - k_enc_a: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - }); - let serde_ed25519_key = PrivateKey::from_blob(&ed25519_key.to_blob().unwrap()).unwrap(); - assert_eq!(ed25519_key, serde_ed25519_key); - - let ecdsa_key = PrivateKey::EcDsa(EcDsaPrivateKey { - identifier: "some_identifier".to_string(), - q: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - d: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - }); - let serde_ecdsa_key = PrivateKey::from_blob(&ecdsa_key.to_blob().unwrap()).unwrap(); - assert_eq!(ecdsa_key, serde_ecdsa_key); -} - -#[test] -fn message_serialization() { - let key = PublicKey::Rsa(RsaPublicKey { - e: vec![1, 0, 1], - n: vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9], - }); - - let sign_req = Message::SignRequest(SignRequest { - pubkey_blob: key.to_blob().unwrap(), - data: b"data".to_vec(), - flags: 24, - }); - let serde_sign_req: Message = from_bytes(&to_bytes(&sign_req).unwrap()).unwrap(); - assert_eq!(sign_req, serde_sign_req); - - let sign_resp = Message::SignResponse( - Signature { - algorithm: "signature algorithm".to_string(), - blob: b"signature_blob".to_vec(), - } - .to_blob() - .unwrap(), - ); - let serde_sign_resp: Message = from_bytes(&to_bytes(&sign_resp).unwrap()).unwrap(); - assert_eq!(sign_resp, serde_sign_resp); - - let success = Message::Success; - let serde_success: Message = from_bytes(&to_bytes(&success).unwrap()).unwrap(); - assert_eq!(success, serde_success); - - let ident_ans = Message::IdentitiesAnswer(vec![ - Identity { - pubkey_blob: b"key_blob_1".to_vec(), - comment: "comment_1".to_string(), - }, - Identity { - pubkey_blob: b"key_blob_2".to_vec(), - comment: "".to_string(), - }, - ]); - let serde_ident_ans: Message = from_bytes(&to_bytes(&ident_ans).unwrap()).unwrap(); - assert_eq!(ident_ans, serde_ident_ans); -} - -#[test] -fn raw_protocol_test() { - let (add_id_dsa_bytes, add_id_ecdsa_bytes, add_id_ed25519_bytes, add_id_rsa_bytes): ( - Vec, - Vec, - Vec, - Vec, - ) = from_bytes(include_bytes!("add_id_requests.bin")).unwrap(); - - let add_id_dsa: Message = from_bytes(&add_id_dsa_bytes).unwrap(); - let add_id_ecdsa: Message = from_bytes(&add_id_ecdsa_bytes).unwrap(); - let add_id_ed25519: Message = from_bytes(&add_id_ed25519_bytes).unwrap(); - let add_id_rsa: Message = from_bytes(&add_id_rsa_bytes).unwrap(); - - let requests = vec![add_id_dsa, add_id_ecdsa, add_id_ed25519, add_id_rsa]; - - let mut identities = vec![]; - - for request in requests { - match request { - Message::AddIdentity(identity) => { - let pubkey = PublicKey::from(identity.privkey); - identities.push(Identity { - pubkey_blob: pubkey.to_blob().unwrap(), - comment: identity.comment, - }); - } - _ => panic!("Wrong request type: {:?}", request), - } - } - - let response = Message::IdentitiesAnswer(identities); - let response_bytes = to_bytes(&to_bytes(&response).unwrap()).unwrap(); - - assert_eq!( - response_bytes.as_slice(), - &include_bytes!("id_ans_response.bin")[..] - ); -} - -#[test] -fn test_extension() { - let extension_bytes: &[u8] = &[ - 0, 0, 0, 24, 115, 101, 115, 115, 105, 111, 110, 45, 98, 105, 110, 100, 64, 111, 112, 101, - 110, 115, 115, 104, 46, 99, 111, 109, 0, 0, 0, 51, 0, 0, 0, 11, 115, 115, 104, 45, 101, - 100, 50, 53, 53, 49, 57, 0, 0, 0, 32, 177, 185, 198, 92, 165, 45, 127, 95, 202, 195, 226, - 63, 6, 115, 10, 104, 18, 137, 172, 240, 153, 154, 174, 74, 83, 7, 1, 204, 14, 177, 153, 40, - 0, 0, 0, 32, 175, 96, 42, 133, 218, 171, 58, 220, 97, 78, 155, 114, 20, 67, 90, 133, 24, - 185, 156, 71, 128, 163, 234, 195, 202, 15, 160, 177, 130, 229, 114, 164, 0, 0, 0, 83, 0, 0, - 0, 11, 115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 64, 4, 235, 93, 135, 144, - 110, 220, 24, 17, 150, 40, 11, 143, 37, 207, 58, 215, 159, 23, 233, 95, 218, 115, 22, 205, - 101, 55, 159, 146, 42, 121, 190, 229, 82, 75, 174, 143, 199, 121, 141, 52, 155, 73, 215, - 119, 220, 104, 241, 116, 83, 96, 129, 184, 12, 93, 93, 33, 243, 171, 236, 201, 123, 17, 1, - 0, - ]; - let extension: Extension = from_bytes(extension_bytes).unwrap(); - assert_eq!(extension.extension_type, "session-bind@openssh.com"); - let out = to_bytes(&extension).unwrap(); - assert_eq!(extension_bytes, out); - - let session_bind: SessionBind = from_bytes(&extension.extension_contents.0).unwrap(); - - let out = to_bytes(&session_bind).unwrap(); - assert_eq!(extension.extension_contents.0, out); -} From 3d4629e6de0ad3c2955655adcc1a175f64fada12 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 26 Mar 2024 16:02:42 +0100 Subject: [PATCH 03/25] Initialize env logger for RUST_LOG=debug Signed-off-by: Wiktor Kwapisiewicz --- examples/key_storage.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/key_storage.rs b/examples/key_storage.rs index a2e3cec..c2f43b4 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -178,6 +178,7 @@ impl Agent for KeyStorageAgent { #[tokio::main] #[cfg(not(windows))] async fn main() -> Result<(), Box> { + env_logger::init(); let socket = "ssh-agent.sock"; let _ = std::fs::remove_file(socket); // remove the socket if exists From b8f656bbf99dae07ae45f0314d8ea6a4e6eb3943 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 26 Mar 2024 16:02:59 +0100 Subject: [PATCH 04/25] Show incoming and outgoing messages Signed-off-by: Wiktor Kwapisiewicz --- src/agent.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/agent.rs b/src/agent.rs index 5bf78c8..7ff98c7 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -128,6 +128,7 @@ pub trait Session: 'static + Sync + Send + Sized { { loop { if let Some(incoming_message) = adapter.try_next().await? { + log::debug!("Request: {incoming_message:?}"); let response = match self.handle(incoming_message).await { Ok(message) => message, Err(e) => { @@ -135,6 +136,7 @@ pub trait Session: 'static + Sync + Send + Sized { Message::Failure } }; + log::debug!("Response: {response:?}"); adapter.send(response).await?; } else { From 48ff6ab2301ecf30c9f0c2fb54b47f37bc298b7b Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 26 Mar 2024 17:24:46 +0100 Subject: [PATCH 05/25] Fix signing Signed-off-by: Wiktor Kwapisiewicz --- src/proto/message.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/proto/message.rs b/src/proto/message.rs index 0ccd1c0..689fbd8 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -46,7 +46,7 @@ impl Decode for SignRequest { type Error = Error; fn decode(reader: &mut impl Reader) -> Result { - let pubkey = KeyData::decode(reader)?; + let pubkey = reader.read_prefixed(KeyData::decode)?; let data = Vec::decode(reader)?; let flags = u32::decode(reader)?; @@ -280,6 +280,7 @@ impl Encode for Message { lengths.checked_sum()? } + Self::SignResponse(response) => response.encoded_len()? + 4, _ => todo!(), }; @@ -292,6 +293,7 @@ impl Encode for Message { Self::Success => 6, Self::RequestIdentities => 11, Self::IdentitiesAnswer(_) => 12, + Self::SignResponse(_) => 14, _ => todo!(), }; @@ -306,6 +308,9 @@ impl Encode for Message { id.encode(writer)?; } } + Self::SignResponse(response) => { + response.encode_prefixed(writer)?; + } _ => todo!(), }; From a9603d9f04f813ed94ea371bb52b8f5bb2839cbb Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 26 Mar 2024 17:48:59 +0100 Subject: [PATCH 06/25] Add tests for more commands Signed-off-by: Wiktor Kwapisiewicz --- examples/key_storage.rs | 12 ++++++++++++ tests/pwd-test.sh | 3 +++ tests/sign-and-verify.sh | 10 +++++++++- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100755 tests/pwd-test.sh diff --git a/examples/key_storage.rs b/examples/key_storage.rs index c2f43b4..18973fc 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -141,6 +141,18 @@ impl KeyStorage { let signature = self.sign(&request)?; Ok(Message::SignResponse(signature)) } + Message::AddSmartcardKey(key) => { + println!("Adding smartcard key: {key:?}"); + Ok(Message::Success) + } + Message::Lock(pwd) => { + println!("Locked with password: {pwd:?}"); + Ok(Message::Success) + } + Message::Unlock(pwd) => { + println!("Unlocked with password: {pwd:?}"); + Ok(Message::Success) + } _ => Err(From::from(format!("Unknown message: {:?}", request))), }; info!("Response {:?}", response); diff --git a/tests/pwd-test.sh b/tests/pwd-test.sh new file mode 100755 index 0000000..13fdcce --- /dev/null +++ b/tests/pwd-test.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo test diff --git a/tests/sign-and-verify.sh b/tests/sign-and-verify.sh index 92ad16d..ab7c887 100755 --- a/tests/sign-and-verify.sh +++ b/tests/sign-and-verify.sh @@ -18,5 +18,13 @@ ssh-add -L | tee agent.pub ssh-keygen -Y sign -f agent.pub -n file < Cargo.toml > Cargo.toml.sig ssh-keygen -Y check-novalidate -n file -f agent.pub -s Cargo.toml.sig < Cargo.toml -rm -rf ssh-agent.sock Cargo.toml.sig id_rsa id_rsa.pub agent.pub +rm -rf Cargo.toml.sig id_rsa id_rsa.pub agent.pub +# Test other commands: +export SSH_ASKPASS=`pwd`/tests/pwd-test.sh +# AddSmartcardKey +echo | ssh-add -s test +# Lock +echo | ssh-add -x +# Unlock +echo | ssh-add -X From 09693a302e847e2b68668514ec752fcbe89ffc74 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 26 Mar 2024 17:52:13 +0100 Subject: [PATCH 07/25] Drop debugging printlns Signed-off-by: Wiktor Kwapisiewicz --- examples/key_storage.rs | 2 -- src/proto/message.rs | 5 ----- 2 files changed, 7 deletions(-) diff --git a/examples/key_storage.rs b/examples/key_storage.rs index 18973fc..99957bb 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -127,14 +127,12 @@ impl KeyStorage { Ok(Message::Success) } Message::AddIdentity(identity) => { - println!("add_identity0"); let privkey = PrivateKey::try_from(identity.privkey).unwrap(); self.identity_add(Identity { pubkey: PublicKey::from(&privkey), privkey: privkey, comment: identity.comment, }); - println!("add_identity1"); Ok(Message::Success) } Message::SignRequest(request) => { diff --git a/src/proto/message.rs b/src/proto/message.rs index 689fbd8..13a5c7e 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -87,11 +87,8 @@ impl Decode for AddIdentity { type Error = Error; fn decode(reader: &mut impl Reader) -> Result { - println!("foo0"); let privkey = KeypairData::decode(reader)?; - println!("foo1"); let comment = String::decode(reader)?; - println!("foo2"); Ok(Self { privkey, comment }) } @@ -235,9 +232,7 @@ impl Decode for Message { type Error = Error; fn decode(reader: &mut impl Reader) -> Result { - println!("foo0"); let message_type = u8::decode(reader)?; - println!("foo1"); match message_type { 5 => Ok(Self::Failure), From c18d25d4106ab308553e795ea392fbbbe2032ea5 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Wed, 27 Mar 2024 08:54:51 +0100 Subject: [PATCH 08/25] Remove unused files Signed-off-by: Wiktor Kwapisiewicz --- src/{proto/mod.rs => proto.rs} | 3 - src/proto/key_type.rs | 10 - src/proto/ser.rs | 315 ---------------------------- src/proto/tests/add_id_requests.bin | Bin 1744 -> 0 bytes src/proto/tests/id_ans_response.bin | Bin 963 -> 0 bytes 5 files changed, 328 deletions(-) rename src/{proto/mod.rs => proto.rs} (75%) delete mode 100644 src/proto/key_type.rs delete mode 100644 src/proto/ser.rs delete mode 100644 src/proto/tests/add_id_requests.bin delete mode 100644 src/proto/tests/id_ans_response.bin diff --git a/src/proto/mod.rs b/src/proto.rs similarity index 75% rename from src/proto/mod.rs rename to src/proto.rs index 94251ee..95257f6 100644 --- a/src/proto/mod.rs +++ b/src/proto.rs @@ -1,12 +1,9 @@ -#[macro_use] -pub mod key_type; pub mod error; pub mod extension; pub mod message; pub mod signature; pub use self::error::*; -pub use self::key_type::*; pub use self::message::*; pub use self::signature::*; diff --git a/src/proto/key_type.rs b/src/proto/key_type.rs deleted file mode 100644 index 4d7585e..0000000 --- a/src/proto/key_type.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub trait KeyTypeEnum { - fn key_type(&self) -> String; -} - -pub trait KeyType { - const KEY_TYPE: &'static str; - fn key_type(&self) -> String { - Self::KEY_TYPE.to_string() - } -} diff --git a/src/proto/ser.rs b/src/proto/ser.rs deleted file mode 100644 index f828501..0000000 --- a/src/proto/ser.rs +++ /dev/null @@ -1,315 +0,0 @@ -use super::error::{ProtoError, ProtoResult}; -use byteorder::{BigEndian, WriteBytesExt}; -use serde::ser::{self, Serialize}; -use std::io; - -#[derive(Debug)] -pub struct Serializer { - // This string starts empty and JSON is appended as values are serialized. - writer: W, -} - -impl Serializer { - pub fn from_writer(writer: W) -> Self { - Self { writer } - } -} - -impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { - type Ok = (); - type Error = ProtoError; - - type SerializeSeq = Self; - type SerializeTuple = Self; - type SerializeTupleStruct = Self; - type SerializeTupleVariant = Self; - type SerializeMap = Self; - type SerializeStruct = Self; - type SerializeStructVariant = Self; - - fn serialize_bool(self, v: bool) -> ProtoResult<()> { - self.writer - .write_u8(if v { 1 } else { 0 }) - .map_err(Into::into) - } - - fn serialize_i8(self, v: i8) -> ProtoResult<()> { - self.writer.write_i8(v).map_err(Into::into) - } - - fn serialize_i16(self, v: i16) -> ProtoResult<()> { - self.writer.write_i16::(v).map_err(Into::into) - } - - fn serialize_i32(self, v: i32) -> ProtoResult<()> { - self.writer.write_i32::(v).map_err(Into::into) - } - - fn serialize_i64(self, v: i64) -> ProtoResult<()> { - self.writer.write_i64::(v).map_err(Into::into) - } - - fn serialize_u8(self, v: u8) -> ProtoResult<()> { - self.writer.write_u8(v).map_err(Into::into) - } - - fn serialize_u16(self, v: u16) -> ProtoResult<()> { - self.writer.write_u16::(v).map_err(Into::into) - } - - fn serialize_u32(self, v: u32) -> ProtoResult<()> { - self.writer.write_u32::(v).map_err(Into::into) - } - - fn serialize_u64(self, v: u64) -> ProtoResult<()> { - self.writer.write_u64::(v).map_err(Into::into) - } - - fn serialize_f32(self, v: f32) -> ProtoResult<()> { - self.writer.write_f32::(v).map_err(Into::into) - } - - fn serialize_f64(self, v: f64) -> ProtoResult<()> { - self.writer.write_f64::(v).map_err(Into::into) - } - - fn serialize_char(self, _v: char) -> ProtoResult<()> { - unimplemented!() - } - - fn serialize_str(self, v: &str) -> ProtoResult<()> { - self.serialize_bytes(v.as_bytes()) - } - - fn serialize_bytes(self, v: &[u8]) -> ProtoResult<()> { - (v.len() as u32).serialize(&mut *self)?; - self.writer.write_all(v).map_err(Into::into) - } - - fn serialize_none(self) -> ProtoResult<()> { - unimplemented!() - } - - fn serialize_some(self, _value: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - unimplemented!() - } - - fn serialize_unit(self) -> ProtoResult<()> { - unimplemented!() - } - - fn serialize_unit_struct(self, _name: &'static str) -> ProtoResult<()> { - unimplemented!() - } - - fn serialize_unit_variant( - self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - ) -> ProtoResult<()> { - (variant_index as u8).serialize(self) - } - - fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - value.serialize(&mut *self) - } - - fn serialize_newtype_variant( - self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - value: &T, - ) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - (variant_index as u8).serialize(&mut *self)?; - value.serialize(&mut *self) - } - - fn serialize_seq(self, len: Option) -> ProtoResult { - if let Some(len) = len { - (len as u32).serialize(&mut *self).map(|()| self) - } else { - unimplemented!() - } - } - - fn serialize_tuple(self, _len: usize) -> ProtoResult { - Ok(self) - } - - fn serialize_tuple_struct( - self, - _name: &'static str, - _len: usize, - ) -> ProtoResult { - Ok(self) - } - - fn serialize_tuple_variant( - self, - _name: &'static str, - variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> ProtoResult { - (variant_index as u8).serialize(&mut *self)?; - Ok(self) - } - - fn serialize_map(self, _len: Option) -> ProtoResult { - unimplemented!() - } - - fn serialize_struct( - self, - _name: &'static str, - _len: usize, - ) -> ProtoResult { - Ok(self) - } - - fn serialize_struct_variant( - self, - _name: &'static str, - _variant_index: u32, - _variant: &'static str, - _len: usize, - ) -> ProtoResult { - Ok(self) - } -} - -impl<'a, W: io::Write> ser::SerializeSeq for &'a mut Serializer { - type Ok = (); - type Error = ProtoError; - - fn serialize_element(&mut self, value: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - value.serialize(&mut **self) - } - - fn end(self) -> ProtoResult<()> { - Ok(()) - } -} - -impl<'a, W: io::Write> ser::SerializeTuple for &'a mut Serializer { - type Ok = (); - type Error = ProtoError; - - fn serialize_element(&mut self, value: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - value.serialize(&mut **self) - } - - fn end(self) -> ProtoResult<()> { - Ok(()) - } -} - -impl<'a, W: io::Write> ser::SerializeTupleStruct for &'a mut Serializer { - type Ok = (); - type Error = ProtoError; - - fn serialize_field(&mut self, value: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - value.serialize(&mut **self) - } - - fn end(self) -> ProtoResult<()> { - Ok(()) - } -} - -impl<'a, W: io::Write> ser::SerializeTupleVariant for &'a mut Serializer { - type Ok = (); - type Error = ProtoError; - - fn serialize_field(&mut self, value: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - value.serialize(&mut **self) - } - - fn end(self) -> ProtoResult<()> { - Ok(()) - } -} - -impl<'a, W: io::Write> ser::SerializeMap for &'a mut Serializer { - type Ok = (); - type Error = ProtoError; - - fn serialize_key(&mut self, _key: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - unimplemented!() - } - - fn serialize_value(&mut self, _value: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - unimplemented!() - } - - fn end(self) -> ProtoResult<()> { - unimplemented!() - } -} - -impl<'a, W: io::Write> ser::SerializeStruct for &'a mut Serializer { - type Ok = (); - type Error = ProtoError; - - fn serialize_field(&mut self, _key: &'static str, value: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - value.serialize(&mut **self) - } - - fn end(self) -> ProtoResult<()> { - Ok(()) - } -} - -impl<'a, W: io::Write> ser::SerializeStructVariant for &'a mut Serializer { - type Ok = (); - type Error = ProtoError; - - fn serialize_field(&mut self, _key: &'static str, value: &T) -> ProtoResult<()> - where - T: ?Sized + Serialize, - { - value.serialize(&mut **self) - } - - fn end(self) -> ProtoResult<()> { - Ok(()) - } -} - -pub fn to_bytes(value: &T) -> ProtoResult> { - let mut serializer = Serializer::from_writer(Vec::new()); - value.serialize(&mut serializer)?; - Ok(serializer.writer) -} diff --git a/src/proto/tests/add_id_requests.bin b/src/proto/tests/add_id_requests.bin deleted file mode 100644 index cbef42727865d015e4be59e5ca9202d6e91f1dcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1744 zcma)*dpHyNAICQ&Y__FQ%yn{!h8c?^hFpG#*xc6Bu$W6MvRKHB)FhhQv3{IIktaEF zJHk}Rao>@1Oim&(w{Q~T(9ha$^_-`tKhJ-k@B8z4-rv{r{(e6I0Pytz006L`MvFv+ z(P)w~9k4LOmp#jwawNQVWoYkTI&R2!s%8D?EgSr*8g;^-Y^MT$(h%tW=gi2=y$uK< z&XJ;hZP649L95quJB4%pCYouEAQ&vTU1=?y*e{+q_FvD!`(sMuZsdGFtIJszwRiZq zOs*7WUQPViG}+dcMEuWWhqza+B(S8KS+3}wDuGZ2usKK7d|14W)dd8(G*Az^!92;9 zG?>ygvP&*H+8KKQ&DcIW$gkZC*4{2*6XEArg&IFy)rdq-YYLQ34I!s>6$V;xH^kJpkeYsy*(*5*S{t-XZYsS)VWV>_EVb3grZ`Gg8_FK>|C$( z7kglA6Li;5>4(`w(UY#`kh_qVx*;cGY432N7I1*$TQzvm^ybxUaE%7oPhNjKQ~;_Y zn7;2U3R9+BN8BQB>^W=%ToZ`3Ckv#O`P@SjGSuP7w4|@P)Ne6FG_{E=TjvR zaI+Ty*+HqDDxl10lGYym=({K~OQM6rXyo8a6xyZWXiAdgT=wO84^l!UGKv;SHb-5E zqFstJx5P?hx$kNRnfGZk_9_CanWmz=^@X#C>^i&8yjawddLCXManZYIO`obaaFd^; zzfCnnJt6>zHx?tTeKxl*g35e@r>9WH@e)IAsrzu=F1CT7Kxeq;6i>A4^lW~35K92$YgJ2T?88&tq$?crG8O06IO}7+%>1`*`!ID^)+@D^kK6eZl>dbX?4K*Y zQC8gN}TE?YhFvbwXBuBL~_T zfI|D0?=7gkl(%@-pJZ;hpnyrR0YB1+jJ6!{Ydhj?+99&VmAbTLK)KJJe#mN0l&h`3 z6J5el6Su7Y?8Iu`sSUPce)+L!n%Wz|#%{lxaiQVr<&3n${*FAP@A zCv3UNM%fB?_ep(Bg0ob(%oGwz6=5CD{90WgZrJQjdc0c+@a?hKy*Xdb@NVO=+?z(&vPJ4k8rZ51vm zg7)lf;^dX{#mG+53SKk4CWS4{CY zRA5?YVOO4uORggBw%v_)@M2W85c8CQ5d}f)7BQE&x`)srqXRuvW4A4~?xDLEbqzIb zIzIP4fXWTl=Srerf;(a$aI>Y~`V?y&CP|gQ4-I)K367fP7GQp<=qWq!b9TAhoS(^= z<%b1Pz+`mfB+Th3yPq!OMch8nc&j+IJGz>I-DQ57IsIu8K@zQ~H(ih!bp6GoBe6v7 zaC){gNbu1k-K=*DWOaGJ!hDH=QX!`+16(Vr?L$}A%T!x9OBm7tdq`+Fla}zeRA>M7 z`H$zRW9d2AM*%@%e18>!sBVPK{1e<_&}g)ZbC>0f0!P{lU9uVM{Z5{L$pm6$&G9+D z>k*2rxbdr*E&(wxQEN}$RxedKYp9HF>AwDqsOF!$$%P?zje@K_m%T`4z*|=9h*(uG zjPs(+0>6o+xAqlcj;GKP8O4d4ujg0uLn`*}x0N675IwR|{aNnzO`cKxvY6+m&Hg>~ zjpsy%;xLlrmf1T=9q3m;{yZ{?ho_%`xVA@XpANLR6|)cXa)$KiU^Sl73QII wuKd$cM^0cfwU3VMI{kd7JM+y`VUeoWYmb%}JEP}nN)@Cf5MarYe%s#v0~t2_q5uE@ diff --git a/src/proto/tests/id_ans_response.bin b/src/proto/tests/id_ans_response.bin deleted file mode 100644 index 3cd71e4f234866b75cd114163b85b2ff9e6cc189..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 963 zcmZQzVBXKez`(%5z`(c(NV69gXXvIB7Xz7%49`xVVT+l5*D3h6dz}LN3v-P#E^FJT z2C&87T&ZglmS`s?vS(w2*Nul49xnRC7hK?!s?hpe-;B>tX2aYAkDr9;$<;e?3kbyT z7JAKNF^*Xg zvsN}N;Pt8nIzp78f4ZhrVEeB9Z=Ojf&W=#!|J2~z50q$NUd{HxO;Mrg&$Wbe^AtI) zjf3uHde+Y0eMQSf`H@J>In(PKZ21*s{_|KKKep?~rxQ7AeOvep>y<#nmef93c22WwhT(m=D?&b}mF_EZo!oBK%;%)MuXF+5;_cJz9`E8c zELd-Hi9dxi?%2_4>o#x*J<+)5b2sVNPKUd4N~Ke#)j6uqJ;&p^LFjG&$*Z5a7itJu zJu3UV=it_ag?!hP6KwK}U)ulO&J*tRKuToF{m;!^Jga4SLOIp0Ctu-SAFTiGz?2lx z)Hdn9#7}=5KZUnm`LA$i64R?QbH#75+xysLlcAa=##;pQn5 zK0b>Pw>xk+>ilzgrbB5P(hCEoyjROrJ=uMxQtex=hVHsxhRDw6>6U??zZG)N4~f5j zS690TXdoZh9%S2%fuh`?G?bcRWNKpc(!}*NH4eD;ejz)$=p3r9Y{A zBw${@dC~{ja;0~zZCu?SgF8-an$Eu|T-PvU!JlVR*EmgHo~Sg^c*bR1YR$7wHY3~g zLg-GF0R8=cZS7~f?QG(oyKnD@j;&=JYd6fxo;6+S|Mrh_0;--|+5e>ReTB<2Zk_l& zMJ%ei?#IiHq`uKtG~8o5b+#;jR-R44?z~gWo|b;~WXrUD{F9aGOQlG=*xZKdSY1i} z1q;G+UpJlF>YXl@c4+d%U8bvo_zEV+y*#sfdeFufa!Xcmo(?%UX>wprefzdT2d(vW zH$Tn~I+?(=Ew)D5EMaZ=y_2^#m*w!E7MJ+8%(*_;v+1C~QW<^0e@Bm>1%?F=Fi7A* F1po;ekQV>| From 1d9411dedbbfe42f6a35c5c63cdafe8d9abdcbee Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Wed, 27 Mar 2024 09:17:50 +0100 Subject: [PATCH 09/25] Trim unused dependencies Signed-off-by: Wiktor Kwapisiewicz --- Cargo.lock | 2 -- Cargo.toml | 8 +------- src/agent.rs | 2 +- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6fe3d5a..27a26a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -885,7 +885,6 @@ version = "0.4.0" dependencies = [ "async-trait", "byteorder", - "bytes", "env_logger", "futures", "log", @@ -893,7 +892,6 @@ dependencies = [ "rsa", "service-binding", "sha1", - "signature", "ssh-encoding", "ssh-key", "tokio", diff --git a/Cargo.toml b/Cargo.toml index 56a0676..8d488b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,14 +15,9 @@ keywords = ["ssh", "agent", "authentication", "openssh", "async"] categories = ["authentication", "cryptography", "encoding", "network-programming", "parsing"] exclude = [".github"] -[patch.crates-io] -#ssh-key = { path = "../../SSH/ssh-key" } - [dependencies] byteorder = "1.4.3" - async-trait = { version = "0.1.77", optional = true } -bytes = { version = "1.5.0", optional = true } futures = { version = "0.3.30", optional = true } log = { version = "0.4.6", optional = true } tokio = { version = "1", optional = true, features = ["rt", "net"] } @@ -30,11 +25,10 @@ tokio-util = { version = "0.7.1", optional = true, features = ["codec"] } service-binding = { version = "^2" } ssh-encoding = { version = "0.2.0" } ssh-key = { version = "0.6.4", features = ["rsa", "alloc"] } -signature = "2" [features] default = ["agent"] -agent = ["futures", "log", "tokio", "tokio-util", "bytes", "async-trait"] +agent = ["futures", "log", "tokio", "tokio-util", "async-trait"] [[example]] name = "key_storage" diff --git a/src/agent.rs b/src/agent.rs index 7ff98c7..c6498f4 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -1,6 +1,5 @@ use async_trait::async_trait; use byteorder::{BigEndian, ReadBytesExt}; -use bytes::{Buf, BufMut, BytesMut}; use futures::{SinkExt, TryStreamExt}; use log::{error, info}; use ssh_encoding::{Decode, Encode}; @@ -10,6 +9,7 @@ use tokio::net::windows::named_pipe::{NamedPipeServer, ServerOptions}; use tokio::net::{TcpListener, TcpStream}; #[cfg(unix)] use tokio::net::{UnixListener, UnixStream}; +use tokio_util::bytes::{Buf, BufMut, BytesMut}; use tokio_util::codec::{Decoder, Encoder, Framed}; use std::fmt; From 92d9d719137b6f6ab13553f5c8709b96e8ea8cd6 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 2 Apr 2024 17:15:54 +0200 Subject: [PATCH 10/25] Add support for deserializing `AddIdConstrained` Signed-off-by: Wiktor Kwapisiewicz --- examples/key_storage.rs | 17 +++++++++++++++-- src/proto/message.rs | 36 ++++++++++++++++++++---------------- tests/sign-and-verify.sh | 4 +++- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/examples/key_storage.rs b/examples/key_storage.rs index 99957bb..0cb9a0d 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -7,7 +7,7 @@ use tokio::net::UnixListener; use ssh_agent_lib::agent::{Agent, Session}; use ssh_agent_lib::proto::message::{self, Message, SignRequest}; -use ssh_agent_lib::proto::signature; +use ssh_agent_lib::proto::{signature, AddIdentityConstrained}; use ssh_key::{ private::{KeypairData, PrivateKey}, public::PublicKey, @@ -130,7 +130,20 @@ impl KeyStorage { let privkey = PrivateKey::try_from(identity.privkey).unwrap(); self.identity_add(Identity { pubkey: PublicKey::from(&privkey), - privkey: privkey, + privkey, + comment: identity.comment, + }); + Ok(Message::Success) + } + Message::AddIdConstrained(AddIdentityConstrained { + identity, + constraints, + }) => { + eprintln!("Would use these constraints: {constraints:#?}"); + let privkey = PrivateKey::try_from(identity.privkey).unwrap(); + self.identity_add(Identity { + pubkey: PublicKey::from(&privkey), + privkey, comment: identity.comment, }); Ok(Message::Success) diff --git a/src/proto/message.rs b/src/proto/message.rs index 13a5c7e..2294c15 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -103,15 +103,17 @@ pub struct AddIdentityConstrained { impl Decode for AddIdentityConstrained { type Error = Error; - fn decode(_reader: &mut impl Reader) -> Result { - todo!(); - //let identity = AddIdentity::decode(reader)?; - //let constraints = Vec::decode(reader)?; + fn decode(reader: &mut impl Reader) -> Result { + let identity = AddIdentity::decode(reader)?; + let mut constraints = vec![]; - //Ok(Self { - // identity, - // constraints, - //}) + while !reader.is_finished() { + constraints.push(KeyConstraint::decode(reader)?); + } + Ok(Self { + identity, + constraints, + }) } } @@ -148,9 +150,10 @@ impl Decode for SmartcardKey { } #[derive(Clone, PartialEq, Debug)] -pub struct KeyConstraint { - pub constraint_type: u8, - pub constraint_data: Vec, +pub enum KeyConstraint { + Lifetime(u32), + Confirm, + Extension(String, Vec), } impl Decode for KeyConstraint { @@ -158,11 +161,12 @@ impl Decode for KeyConstraint { fn decode(reader: &mut impl Reader) -> Result { let constraint_type = u8::decode(reader)?; - let constraint_data = Vec::decode(reader)?; - - Ok(Self { - constraint_type, - constraint_data, + // see: https://www.ietf.org/archive/id/draft-miller-ssh-agent-12.html#section-5.2 + Ok(match constraint_type { + 1 => KeyConstraint::Lifetime(u32::decode(reader)?), + 2 => KeyConstraint::Confirm, + 255 => KeyConstraint::Extension(String::decode(reader)?, Vec::::decode(reader)?), + _ => return Err(Error::AlgorithmUnknown), // FIXME: it should be our own type }) } } diff --git a/tests/sign-and-verify.sh b/tests/sign-and-verify.sh index ab7c887..8cadc59 100755 --- a/tests/sign-and-verify.sh +++ b/tests/sign-and-verify.sh @@ -18,7 +18,7 @@ ssh-add -L | tee agent.pub ssh-keygen -Y sign -f agent.pub -n file < Cargo.toml > Cargo.toml.sig ssh-keygen -Y check-novalidate -n file -f agent.pub -s Cargo.toml.sig < Cargo.toml -rm -rf Cargo.toml.sig id_rsa id_rsa.pub agent.pub +rm -rf Cargo.toml.sig id_rsa.pub agent.pub # Test other commands: export SSH_ASKPASS=`pwd`/tests/pwd-test.sh @@ -28,3 +28,5 @@ echo | ssh-add -s test echo | ssh-add -x # Unlock echo | ssh-add -X +# AddIdConstrained +ssh-add -t 2 id_rsa From 1aff3096ebde0ebd9ac5bcd954a511fa68815915 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 2 Apr 2024 17:37:32 +0200 Subject: [PATCH 11/25] Add support for deserializing `AddSmartcardKeyConstrained` Signed-off-by: Wiktor Kwapisiewicz --- examples/key_storage.rs | 4 ++++ src/proto/message.rs | 12 +++++++----- tests/sign-and-verify.sh | 2 ++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/examples/key_storage.rs b/examples/key_storage.rs index 0cb9a0d..a98e101 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -156,6 +156,10 @@ impl KeyStorage { println!("Adding smartcard key: {key:?}"); Ok(Message::Success) } + Message::AddSmartcardKeyConstrained(key) => { + println!("Adding smartcard key with constraints: {key:?}"); + Ok(Message::Success) + } Message::Lock(pwd) => { println!("Locked with password: {pwd:?}"); Ok(Message::Success) diff --git a/src/proto/message.rs b/src/proto/message.rs index 2294c15..dc43293 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -180,12 +180,14 @@ pub struct AddSmartcardKeyConstrained { impl Decode for AddSmartcardKeyConstrained { type Error = Error; - fn decode(_reader: &mut impl Reader) -> Result { - todo!() - //let key = SmartcardKey::decode(reader)?; - //let constraints = Vec::decode(reader)?; + fn decode(reader: &mut impl Reader) -> Result { + let key = SmartcardKey::decode(reader)?; + let mut constraints = vec![]; - //Ok(Self { key, constraints }) + while !reader.is_finished() { + constraints.push(KeyConstraint::decode(reader)?); + } + Ok(Self { key, constraints }) } } diff --git a/tests/sign-and-verify.sh b/tests/sign-and-verify.sh index 8cadc59..2c3f0f6 100755 --- a/tests/sign-and-verify.sh +++ b/tests/sign-and-verify.sh @@ -24,6 +24,8 @@ rm -rf Cargo.toml.sig id_rsa.pub agent.pub export SSH_ASKPASS=`pwd`/tests/pwd-test.sh # AddSmartcardKey echo | ssh-add -s test +# AddSmartcardKeyConstrained +echo | ssh-add -c -t 4 -s test # Lock echo | ssh-add -x # Unlock From 600744b42ee6e2ae8c4076a7ba88cb76f929618d Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Tue, 2 Apr 2024 18:00:23 +0200 Subject: [PATCH 12/25] Add parsing extensions Signed-off-by: Wiktor Kwapisiewicz --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + examples/key_storage.rs | 9 +++++++++ src/proto/error.rs | 18 ++++++++++++++++++ src/proto/extension.rs | 41 +++++++++++++++++++++++++++++++++++++++++ src/proto/message.rs | 37 ++++++++++++++++++++++++++++--------- 6 files changed, 104 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 27a26a1..2a30d92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -894,6 +894,7 @@ dependencies = [ "sha1", "ssh-encoding", "ssh-key", + "testresult", "tokio", "tokio-util", ] @@ -957,6 +958,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "testresult" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d72e255c0541f86589b0287139b70bd941a197ea4cea8fd8f87afe9c965a99e4" + [[package]] name = "tokio" version = "1.36.0" diff --git a/Cargo.toml b/Cargo.toml index 8d488b5..250e2a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,3 +40,4 @@ rand = "0.8.5" rsa = { version = "0.9.6", features = ["sha2", "sha1"] } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } sha1 = { version = "0.10.5", default-features = false, features = ["oid"] } +testresult = "0.4.0" diff --git a/examples/key_storage.rs b/examples/key_storage.rs index a98e101..c87b7c4 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -2,6 +2,7 @@ use async_trait::async_trait; use log::info; #[cfg(windows)] use ssh_agent_lib::agent::NamedPipeListener; +use ssh_agent_lib::proto::extension::SessionBind; #[cfg(not(windows))] use tokio::net::UnixListener; @@ -168,6 +169,14 @@ impl KeyStorage { println!("Unlocked with password: {pwd:?}"); Ok(Message::Success) } + Message::Extension(mut extension) => { + eprintln!("Extension: {extension:?}"); + if extension.name == "session-bind@openssh.com" { + let bind = extension.details.parse::()?; + eprintln!("Bind: {bind:?}"); + } + Ok(Message::Success) + } _ => Err(From::from(format!("Unknown message: {:?}", request))), }; info!("Response {:?}", response); diff --git a/src/proto/error.rs b/src/proto/error.rs index df9bbca..896113a 100644 --- a/src/proto/error.rs +++ b/src/proto/error.rs @@ -7,6 +7,8 @@ pub enum ProtoError { MessageTooLong, StringEncoding(string::FromUtf8Error), IO(io::Error), + SshEncoding(ssh_encoding::Error), + SshKey(ssh_key::Error), } impl From for () { @@ -19,6 +21,18 @@ impl From for ProtoError { } } +impl From for ProtoError { + fn from(e: ssh_encoding::Error) -> ProtoError { + ProtoError::SshEncoding(e) + } +} + +impl From for ProtoError { + fn from(e: ssh_key::Error) -> ProtoError { + ProtoError::SshKey(e) + } +} + impl From for ProtoError { fn from(e: string::FromUtf8Error) -> ProtoError { ProtoError::StringEncoding(e) @@ -32,6 +46,8 @@ impl std::error::Error for ProtoError { ProtoError::MessageTooLong => None, ProtoError::StringEncoding(e) => Some(e), ProtoError::IO(e) => Some(e), + ProtoError::SshEncoding(e) => Some(e), + ProtoError::SshKey(e) => Some(e), } } } @@ -43,6 +59,8 @@ impl std::fmt::Display for ProtoError { ProtoError::MessageTooLong => f.write_str("Message too long"), ProtoError::StringEncoding(_) => f.write_str("String encoding failed"), ProtoError::IO(_) => f.write_str("I/O Error"), + ProtoError::SshEncoding(_) => f.write_str("SSH encoding Error"), + ProtoError::SshKey(e) => write!(f, "SSH key Error: {e}"), } } } diff --git a/src/proto/extension.rs b/src/proto/extension.rs index 7eb3af8..a9c9e90 100644 --- a/src/proto/extension.rs +++ b/src/proto/extension.rs @@ -1,3 +1,4 @@ +use ssh_encoding::{Decode, Reader}; use ssh_key::{public::KeyData, Signature}; /// session-bind@openssh.com extension @@ -14,3 +15,43 @@ pub struct SessionBind { pub signature: Signature, pub is_forwarding: bool, } + +impl Decode for SessionBind { + type Error = crate::proto::error::ProtoError; + + fn decode(reader: &mut impl Reader) -> Result { + let host_key = reader.read_prefixed(KeyData::decode)?; + let session_id = Vec::decode(reader)?; + let signature = reader.read_prefixed(Signature::decode)?; + Ok(Self { + host_key, + session_id, + signature, + is_forwarding: u8::decode(reader)? != 0, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use testresult::TestResult; + + #[test] + fn parse_bind() -> TestResult { + let mut buffer: &[u8] = &[ + 0, 0, 0, 51, 0, 0, 0, 11, 115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 32, + 177, 185, 198, 92, 165, 45, 127, 95, 202, 195, 226, 63, 6, 115, 10, 104, 18, 137, 172, + 240, 153, 154, 174, 74, 83, 7, 1, 204, 14, 177, 153, 40, 0, 0, 0, 32, 138, 165, 196, + 144, 149, 107, 183, 188, 222, 182, 34, 173, 59, 118, 9, 35, 186, 147, 114, 114, 50, + 106, 41, 182, 196, 119, 226, 82, 233, 148, 236, 135, 0, 0, 0, 83, 0, 0, 0, 11, 115, + 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, 0, 0, 0, 64, 95, 212, 52, 189, 8, 162, 17, + 3, 15, 218, 2, 4, 136, 7, 47, 57, 121, 6, 194, 165, 221, 27, 175, 241, 6, 57, 84, 141, + 77, 55, 235, 9, 77, 160, 32, 76, 11, 227, 240, 235, 122, 178, 80, 133, 183, 91, 89, 89, + 142, 115, 145, 15, 78, 112, 139, 28, 201, 8, 197, 222, 117, 141, 88, 5, 0, + ]; + let bind = SessionBind::decode(&mut buffer)?; + eprintln!("Bind: {bind:#?}"); + Ok(()) + } +} diff --git a/src/proto/message.rs b/src/proto/message.rs index dc43293..b27956e 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -193,23 +193,42 @@ impl Decode for AddSmartcardKeyConstrained { #[derive(Clone, PartialEq, Debug)] pub struct Extension { - pub extension_type: String, - pub extension_contents: ExtensionContents, + pub name: String, + pub details: Unparsed, } impl Decode for Extension { type Error = Error; - fn decode(_reader: &mut impl Reader) -> Result { - todo!() - //let key = SmartcardKey::decode(reader)?; - //let constraints = Vec::decode(reader)?; - - //Ok(Self { key, constraints }) + fn decode(reader: &mut impl Reader) -> Result { + let name = String::decode(reader)?; + let mut details = vec![0; reader.remaining_len()]; + reader.read(&mut details)?; + Ok(Self { + name, + details: details.into(), + }) } } + #[derive(Debug, PartialEq, Clone)] -pub struct ExtensionContents(pub Vec); +pub struct Unparsed(Vec); + +impl Unparsed { + pub fn parse(&mut self) -> std::result::Result::Error> + where + T: Decode, + { + let mut v = &self.0[..]; + T::decode(&mut v) + } +} + +impl From> for Unparsed { + fn from(value: Vec) -> Self { + Self(value) + } +} pub type Passphrase = String; From fb1f73155b2240ce3ee3e520d142c18c334425ef Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Tue, 2 Apr 2024 22:31:05 -0700 Subject: [PATCH 13/25] adds tests for message serialization Signed-off-by: Arthur Gautier --- Cargo.lock | 9 ++ Cargo.toml | 3 + src/proto/message.rs | 213 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 2a30d92..6108c44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -253,6 +253,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core", "sec1", @@ -427,6 +428,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" + [[package]] name = "hmac" version = "0.12.1" @@ -887,7 +894,9 @@ dependencies = [ "byteorder", "env_logger", "futures", + "hex-literal", "log", + "p256", "rand", "rsa", "service-binding", diff --git a/Cargo.toml b/Cargo.toml index 250e2a1..9ab67cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,3 +41,6 @@ rsa = { version = "0.9.6", features = ["sha2", "sha1"] } tokio = { version = "1", features = ["macros", "rt-multi-thread"] } sha1 = { version = "0.10.5", default-features = false, features = ["oid"] } testresult = "0.4.0" +hex-literal = "0.4.1" +ssh-key = { version = "0.6.4", features = ["p256"] } +p256 = { version = "0.13.2" } diff --git a/src/proto/message.rs b/src/proto/message.rs index b27956e..c513893 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -110,6 +110,7 @@ impl Decode for AddIdentityConstrained { while !reader.is_finished() { constraints.push(KeyConstraint::decode(reader)?); } + Ok(Self { identity, constraints, @@ -337,3 +338,215 @@ impl Encode for Message { Ok(()) } } + +#[cfg(test)] +mod tests { + // Note: yes, some of those tests carry a private key, this is a key that + // was generated for the purpose of those tests + + use hex_literal::hex; + use p256::{ + elliptic_curve::{bigint::Uint, ScalarPrimitive}, + EncodedPoint, + }; + use ssh_key::private::{EcdsaKeypair, EcdsaPrivateKey, KeypairData}; + + use super::*; + + fn demo_key() -> EcdsaKeypair { + EcdsaKeypair::NistP256 { + public: EncodedPoint::from_affine_coordinates( + &hex!("cb244fcdb89de95bc8fd766e6b139abfc2649fb063b6c5e5a939e067e2a0d215").into(), + &hex!("0a660daca78f6c24a0425373d6ea83e36f8a1f8b828a60e77a97a9441bcc0987").into(), + false, + ), + private: EcdsaPrivateKey::from(p256::SecretKey::new( + ScalarPrimitive::new(Uint::from_be_hex( + "ffd9f2ce4d0ee5870d8dc7cf771a7669a0b96fe44bb58a8a0bc75a76b4f78240", + )) + .unwrap(), + )), + } + } + + #[test] + fn test_add_identity_constrained() { + let msg: &[u8] = &hex!( + " + 00 0000 1365 6364 7361 2d73 + 6861 322d 6e69 7374 7032 3536 0000 0008 + 6e69 7374 7032 3536 0000 0041 04cb 244f + cdb8 9de9 5bc8 fd76 6e6b 139a bfc2 649f + b063 b6c5 e5a9 39e0 67e2 a0d2 150a 660d + aca7 8f6c 24a0 4253 73d6 ea83 e36f 8a1f + 8b82 8a60 e77a 97a9 441b cc09 8700 0000 + 2100 ffd9 f2ce 4d0e e587 0d8d c7cf 771a + 7669 a0b9 6fe4 4bb5 8a8a 0bc7 5a76 b4f7 + 8240 0000 000c 6261 6c6f 6f40 616e 6765 + 6c61 0100 0000 02 + " + ); + let mut reader = msg; + + let out = AddIdentityConstrained::decode(&mut reader).unwrap(); + + assert_eq!( + out, + AddIdentityConstrained { + identity: AddIdentity { + privkey: KeypairData::Ecdsa(demo_key()), + comment: "baloo@angela".to_string() + }, + constraints: vec![KeyConstraint::Lifetime(2)], + } + ); + + let msg: &[u8] = &hex!( + " + 00 0000 1365 6364 7361 2d73 + 6861 322d 6e69 7374 7032 3536 0000 0008 + 6e69 7374 7032 3536 0000 0041 04cb 244f + cdb8 9de9 5bc8 fd76 6e6b 139a bfc2 649f + b063 b6c5 e5a9 39e0 67e2 a0d2 150a 660d + aca7 8f6c 24a0 4253 73d6 ea83 e36f 8a1f + 8b82 8a60 e77a 97a9 441b cc09 8700 0000 + 2100 ffd9 f2ce 4d0e e587 0d8d c7cf 771a + 7669 a0b9 6fe4 4bb5 8a8a 0bc7 5a76 b4f7 + 8240 0000 000c 6261 6c6f 6f40 616e 6765 + 6c61 ff00 0000 2472 6573 7472 6963 742d + 6465 7374 696e 6174 696f 6e2d 7630 3040 + 6f70 656e 7373 682e 636f 6d00 0002 7300 + 0002 6f00 0000 0c00 0000 0000 0000 0000 + 0000 0000 0002 5700 0000 0000 0000 0a67 + 6974 6875 622e 636f 6d00 0000 0000 0000 + 3300 0000 0b73 7368 2d65 6432 3535 3139 + 0000 0020 e32a aa79 15ce b9b4 49d1 ba50 + ea2a 28bb 1a6e 01f9 0bda 245a 2d1d 8769 + 7d18 a265 0000 0001 9700 0000 0773 7368 + 2d72 7361 0000 0003 0100 0100 0001 8100 + a3ee 774d c50a 3081 c427 8ec8 5c2e ba8f + 1228 a986 7b7e 5534 ef0c fea6 1c12 fd8f + 568d 5246 3851 ed60 bf09 c62d 594e 8467 + 98ae 765a 3204 4aeb e3ca 0945 da0d b0bb + aad6 d6f2 0224 84be da18 2b0e aff0 b9e9 + 224c cbf0 4265 fc5d d675 b300 ec52 0cf8 + 15b2 67ab 3816 1f36 a96d 57df e158 2a81 + cb02 0d21 1fb9 7488 3a25 327b da97 04a4 + 48dc 6205 e413 6604 1575 7524 79ec 2a06 + cb58 d961 49ca 9bd9 49b2 4644 32ca d44b + b4bf b7f1 31b1 9310 9f96 63be e59f 0249 + 2358 ec68 9d8c c219 ed0e 3332 3036 9f59 + c6ae 54c3 933c 030a cc3e c2a1 4f19 0035 + efd7 277c 658e 5915 6bba 3d7a cfa5 f2bf + 1be3 2706 f3d3 0419 ef95 cae6 d292 6fb1 + 4dc9 e204 b384 d3e2 393e 4b87 613d e014 + 0b9c be6c 3622 ad88 0ce0 60bb b849 f3b6 + 7672 6955 90ec 1dfc d402 b841 daf0 b79d + 59a8 4c4a 6d0a 5350 d9fe 123a a84f 0bea + 363e 24ab 1e50 5022 344e 14bf 6243 b124 + 25e6 3d45 996e 18e9 0a0e 7a8b ed9a 07a0 + a62b 6246 867e 7b2b 99a3 d0c3 5d05 7038 + fd69 f01f a5e8 3d62 732b 9372 bb6c c1de + 7019 a7e4 b986 942c fa9d 6f37 5ff0 b239 + 0000 0000 6800 0000 1365 6364 7361 2d73 + 6861 322d 6e69 7374 7032 3536 0000 0008 + 6e69 7374 7032 3536 0000 0041 0449 8a48 + 4363 4047 b33a 6c64 64cc bba2 92a0 c050 + 7d9e 4b79 611a d832 336e 1b93 7cee e460 + 83a0 8bad ba39 c007 53ff 2eaf d262 95d1 + 4db0 d166 7660 1ffe f93a 6872 4800 0000 + 0000" + ); + let mut reader = msg; + + let out = AddIdentityConstrained::decode(&mut reader).unwrap(); + + assert_eq!( + out, + AddIdentityConstrained { + identity: AddIdentity { + privkey: KeypairData::Ecdsa(demo_key()), + comment: "baloo@angela".to_string() + }, + constraints: vec![KeyConstraint::Extension( + "restrict-destination-v00@openssh.com".to_string(), + hex!( + " + 00 + 0002 6f00 0000 0c00 0000 0000 0000 0000 + 0000 0000 0002 5700 0000 0000 0000 0a67 + 6974 6875 622e 636f 6d00 0000 0000 0000 + 3300 0000 0b73 7368 2d65 6432 3535 3139 + 0000 0020 e32a aa79 15ce b9b4 49d1 ba50 + ea2a 28bb 1a6e 01f9 0bda 245a 2d1d 8769 + 7d18 a265 0000 0001 9700 0000 0773 7368 + 2d72 7361 0000 0003 0100 0100 0001 8100 + a3ee 774d c50a 3081 c427 8ec8 5c2e ba8f + 1228 a986 7b7e 5534 ef0c fea6 1c12 fd8f + 568d 5246 3851 ed60 bf09 c62d 594e 8467 + 98ae 765a 3204 4aeb e3ca 0945 da0d b0bb + aad6 d6f2 0224 84be da18 2b0e aff0 b9e9 + 224c cbf0 4265 fc5d d675 b300 ec52 0cf8 + 15b2 67ab 3816 1f36 a96d 57df e158 2a81 + cb02 0d21 1fb9 7488 3a25 327b da97 04a4 + 48dc 6205 e413 6604 1575 7524 79ec 2a06 + cb58 d961 49ca 9bd9 49b2 4644 32ca d44b + b4bf b7f1 31b1 9310 9f96 63be e59f 0249 + 2358 ec68 9d8c c219 ed0e 3332 3036 9f59 + c6ae 54c3 933c 030a cc3e c2a1 4f19 0035 + efd7 277c 658e 5915 6bba 3d7a cfa5 f2bf + 1be3 2706 f3d3 0419 ef95 cae6 d292 6fb1 + 4dc9 e204 b384 d3e2 393e 4b87 613d e014 + 0b9c be6c 3622 ad88 0ce0 60bb b849 f3b6 + 7672 6955 90ec 1dfc d402 b841 daf0 b79d + 59a8 4c4a 6d0a 5350 d9fe 123a a84f 0bea + 363e 24ab 1e50 5022 344e 14bf 6243 b124 + 25e6 3d45 996e 18e9 0a0e 7a8b ed9a 07a0 + a62b 6246 867e 7b2b 99a3 d0c3 5d05 7038 + fd69 f01f a5e8 3d62 732b 9372 bb6c c1de + 7019 a7e4 b986 942c fa9d 6f37 5ff0 b239 + 0000 0000 6800 0000 1365 6364 7361 2d73 + 6861 322d 6e69 7374 7032 3536 0000 0008 + 6e69 7374 7032 3536 0000 0041 0449 8a48 + 4363 4047 b33a 6c64 64cc bba2 92a0 c050 + 7d9e 4b79 611a d832 336e 1b93 7cee e460 + 83a0 8bad ba39 c007 53ff 2eaf d262 95d1 + 4db0 d166 7660 1ffe f93a 6872 4800 0000 + 0000" + ) + .to_vec() + )], + } + ); + } + + #[test] + fn test_add_identity() { + let msg: &[u8] = &hex!( + " + 00 0000 1365 6364 7361 2d73 + 6861 322d 6e69 7374 7032 3536 0000 0008 + 6e69 7374 7032 3536 0000 0041 04cb 244f + cdb8 9de9 5bc8 fd76 6e6b 139a bfc2 649f + b063 b6c5 e5a9 39e0 67e2 a0d2 150a 660d + aca7 8f6c 24a0 4253 73d6 ea83 e36f 8a1f + 8b82 8a60 e77a 97a9 441b cc09 8700 0000 + 2100 ffd9 f2ce 4d0e e587 0d8d c7cf 771a + 7669 a0b9 6fe4 4bb5 8a8a 0bc7 5a76 b4f7 + 8240 0000 000c 6261 6c6f 6f40 616e 6765 + 6c61 + " + ); + let mut reader = msg; + + let out = AddIdentity::decode(&mut reader).expect("parse message"); + + assert_eq!( + out, + AddIdentity { + privkey: KeypairData::Ecdsa(demo_key()), + comment: "baloo@angela".to_string() + } + ); + } +} From 798c9720560673ba064c13804f0f3f87d82ec589 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Tue, 2 Apr 2024 22:56:44 -0700 Subject: [PATCH 14/25] parse identity list Signed-off-by: Arthur Gautier --- src/proto/message.rs | 42 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/proto/message.rs b/src/proto/message.rs index c513893..f48dbf1 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -7,6 +7,19 @@ pub struct Identity { pub comment: String, } +impl Identity { + fn decode_vec(reader: &mut impl Reader) -> Result> { + let len = u32::decode(reader)?; + let mut identities = vec![]; + + for _ in 0..len { + identities.push(Self::decode(reader)?); + } + + Ok(identities) + } +} + impl Decode for Identity { type Error = Error; @@ -264,7 +277,7 @@ impl Decode for Message { 5 => Ok(Self::Failure), 6 => Ok(Self::Success), 11 => Ok(Self::RequestIdentities), - 12 => todo!(), + 12 => Identity::decode_vec(reader).map(Self::IdentitiesAnswer), 13 => SignRequest::decode(reader).map(Self::SignRequest), 14 => Signature::decode(reader).map(Self::SignResponse), 17 => AddIdentity::decode(reader).map(Self::AddIdentity), @@ -549,4 +562,31 @@ mod tests { } ); } + + #[test] + fn test_parse_identities() { + let msg: &[u8] = &hex!( + " + 0c000000010000006800000013656364 + 73612d736861322d6e69737470323536 + 000000086e6973747032353600000041 + 04cb244fcdb89de95bc8fd766e6b139a + bfc2649fb063b6c5e5a939e067e2a0d2 + 150a660daca78f6c24a0425373d6ea83 + e36f8a1f8b828a60e77a97a9441bcc09 + 870000000c62616c6f6f40616e67656c + 61" + ); + let mut reader = msg; + + let out = Message::decode(&mut reader).expect("parse message"); + + assert_eq!( + out, + Message::IdentitiesAnswer(vec![Identity { + pubkey: KeyData::Ecdsa(demo_key().into()), + comment: "baloo@angela".to_string() + }]), + ); + } } From 21a4b0e1fcc876fd71d5d2c3e761a1e821a5a9b2 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Wed, 3 Apr 2024 09:05:34 -0700 Subject: [PATCH 15/25] test serialization of messages Signed-off-by: Arthur Gautier --- src/proto/message.rs | 153 +++++++++++++++++++++++++++++++------------ 1 file changed, 112 insertions(+), 41 deletions(-) diff --git a/src/proto/message.rs b/src/proto/message.rs index f48dbf1..6c28d14 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -1,4 +1,4 @@ -use ssh_encoding::{CheckedSum, Decode, Encode, Reader, Writer}; +use ssh_encoding::{CheckedSum, Decode, Encode, Error as EncodingError, Reader, Writer}; use ssh_key::{private::KeypairData, public::KeyData, Error, Result, Signature}; #[derive(Clone, PartialEq, Debug)] @@ -107,6 +107,18 @@ impl Decode for AddIdentity { } } +impl Encode for AddIdentity { + fn encoded_len(&self) -> ssh_encoding::Result { + [self.privkey.encoded_len()?, self.comment.encoded_len()?].checked_sum() + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + self.privkey.encode(writer)?; + self.comment.encode(writer)?; + Ok(()) + } +} + #[derive(Clone, PartialEq, Debug)] pub struct AddIdentityConstrained { pub identity: AddIdentity, @@ -131,6 +143,25 @@ impl Decode for AddIdentityConstrained { } } +impl Encode for AddIdentityConstrained { + fn encoded_len(&self) -> ssh_encoding::Result { + self.constraints + .iter() + .try_fold(self.identity.encoded_len()?, |acc, e| { + let constraint_len = e.encoded_len()?; + usize::checked_add(acc, constraint_len).ok_or(EncodingError::Length) + }) + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + self.identity.encode(writer)?; + for constraint in &self.constraints { + constraint.encode(writer)?; + } + Ok(()) + } +} + #[derive(Clone, PartialEq, Debug)] pub struct RemoveIdentity { pub pubkey: KeyData, @@ -185,6 +216,37 @@ impl Decode for KeyConstraint { } } +impl Encode for KeyConstraint { + fn encoded_len(&self) -> ssh_encoding::Result { + let base = u8::MAX.encoded_len()?; + + match self { + Self::Lifetime(lifetime) => base + .checked_add(lifetime.encoded_len()?) + .ok_or(EncodingError::Length), + Self::Confirm => Ok(base), + Self::Extension(name, content) => { + [base, name.encoded_len()?, content.encoded_len()?].checked_sum() + } + } + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + match self { + Self::Lifetime(lifetime) => { + 1u8.encode(writer)?; + lifetime.encode(writer) + } + Self::Confirm => 2u8.encode(writer), + Self::Extension(name, content) => { + 255u8.encode(writer)?; + name.encode(writer)?; + content.encode(writer) + } + } + } +} + #[derive(Clone, PartialEq, Debug)] pub struct AddSmartcardKeyConstrained { pub key: SmartcardKey, @@ -403,16 +465,18 @@ mod tests { let out = AddIdentityConstrained::decode(&mut reader).unwrap(); - assert_eq!( - out, - AddIdentityConstrained { - identity: AddIdentity { - privkey: KeypairData::Ecdsa(demo_key()), - comment: "baloo@angela".to_string() - }, - constraints: vec![KeyConstraint::Lifetime(2)], - } - ); + let expected = AddIdentityConstrained { + identity: AddIdentity { + privkey: KeypairData::Ecdsa(demo_key()), + comment: "baloo@angela".to_string(), + }, + constraints: vec![KeyConstraint::Lifetime(2)], + }; + assert_eq!(out, expected); + + let mut buf = vec![]; + expected.encode(&mut buf).expect("serialize message"); + assert_eq!(buf, msg); let msg: &[u8] = &hex!( " @@ -474,17 +538,15 @@ mod tests { let out = AddIdentityConstrained::decode(&mut reader).unwrap(); - assert_eq!( - out, - AddIdentityConstrained { - identity: AddIdentity { - privkey: KeypairData::Ecdsa(demo_key()), - comment: "baloo@angela".to_string() - }, - constraints: vec![KeyConstraint::Extension( - "restrict-destination-v00@openssh.com".to_string(), - hex!( - " + let expected = AddIdentityConstrained { + identity: AddIdentity { + privkey: KeypairData::Ecdsa(demo_key()), + comment: "baloo@angela".to_string(), + }, + constraints: vec![KeyConstraint::Extension( + "restrict-destination-v00@openssh.com".to_string(), + hex!( + " 00 0002 6f00 0000 0c00 0000 0000 0000 0000 0000 0000 0002 5700 0000 0000 0000 0a67 @@ -526,11 +588,16 @@ mod tests { 83a0 8bad ba39 c007 53ff 2eaf d262 95d1 4db0 d166 7660 1ffe f93a 6872 4800 0000 0000" - ) - .to_vec() - )], - } - ); + ) + .to_vec(), + )], + }; + + assert_eq!(out, expected); + + let mut buf = vec![]; + expected.encode(&mut buf).expect("serialize message"); + assert_eq!(buf, msg); } #[test] @@ -554,13 +621,15 @@ mod tests { let out = AddIdentity::decode(&mut reader).expect("parse message"); - assert_eq!( - out, - AddIdentity { - privkey: KeypairData::Ecdsa(demo_key()), - comment: "baloo@angela".to_string() - } - ); + let expected = AddIdentity { + privkey: KeypairData::Ecdsa(demo_key()), + comment: "baloo@angela".to_string(), + }; + assert_eq!(out, expected); + + let mut buf = vec![]; + expected.encode(&mut buf).expect("serialize message"); + assert_eq!(buf, msg); } #[test] @@ -581,12 +650,14 @@ mod tests { let out = Message::decode(&mut reader).expect("parse message"); - assert_eq!( - out, - Message::IdentitiesAnswer(vec![Identity { - pubkey: KeyData::Ecdsa(demo_key().into()), - comment: "baloo@angela".to_string() - }]), - ); + let expected = Message::IdentitiesAnswer(vec![Identity { + pubkey: KeyData::Ecdsa(demo_key().into()), + comment: "baloo@angela".to_string(), + }]); + assert_eq!(out, expected); + + let mut buf = vec![]; + expected.encode(&mut buf).expect("serialize message"); + assert_eq!(buf, msg); } } From 4316dd7ef5de98fe00fd3b6826a1e245ad569ddb Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Wed, 3 Apr 2024 19:37:35 +0200 Subject: [PATCH 16/25] Add `RestrictDestination` constraint Signed-off-by: Wiktor Kwapisiewicz --- examples/key_storage.rs | 11 +++- src/proto/extension.rs | 114 ++++++++++++++++++++++++++++++++++++++++ src/proto/message.rs | 12 +++-- 3 files changed, 133 insertions(+), 4 deletions(-) diff --git a/examples/key_storage.rs b/examples/key_storage.rs index c87b7c4..25ea65e 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -8,7 +8,7 @@ use tokio::net::UnixListener; use ssh_agent_lib::agent::{Agent, Session}; use ssh_agent_lib::proto::message::{self, Message, SignRequest}; -use ssh_agent_lib::proto::{signature, AddIdentityConstrained}; +use ssh_agent_lib::proto::{signature, AddIdentityConstrained, KeyConstraint}; use ssh_key::{ private::{KeypairData, PrivateKey}, public::PublicKey, @@ -141,6 +141,15 @@ impl KeyStorage { constraints, }) => { eprintln!("Would use these constraints: {constraints:#?}"); + for constraint in constraints { + if let KeyConstraint::Extension(name, mut details) = constraint { + if name == "restrict-destination-v00@openssh.com" { + if let Ok(destination_constraint) = details.parse::() { + eprintln!("Destination constraint: {destination_constraint:?}"); + } + } + } + } let privkey = PrivateKey::try_from(identity.privkey).unwrap(); self.identity_add(Identity { pubkey: PublicKey::from(&privkey), diff --git a/src/proto/extension.rs b/src/proto/extension.rs index a9c9e90..743ca4c 100644 --- a/src/proto/extension.rs +++ b/src/proto/extension.rs @@ -32,6 +32,90 @@ impl Decode for SessionBind { } } +#[derive(Debug, Clone)] +pub struct RestrictDestination { + pub constraints: Vec, +} + +impl Decode for RestrictDestination { + type Error = crate::proto::error::ProtoError; + + fn decode(reader: &mut impl Reader) -> Result { + eprintln!("encoding {}", u32::decode(reader)?); + let mut constraints = Vec::new(); + while !reader.is_finished() { + eprintln!("encoding"); + constraints.push(reader.read_prefixed(DestinationConstraint::decode)?); + } + Ok(Self { constraints }) + } +} + +#[derive(Debug, Clone)] +pub struct DestinationConstraint { + pub from_username: String, + pub from_hostname: String, + pub from_hostkeys: Vec, + pub to_username: String, + pub to_hostname: String, + pub to_hostkeys: Vec, +} + +impl Decode for DestinationConstraint { + type Error = crate::proto::error::ProtoError; + + fn decode(reader: &mut impl Reader) -> Result { + //eprintln!("before: {}", u32::decode(reader)?); + let from_username = String::decode(reader)?; + eprintln!("from username: {from_username} {}", from_username.len()); + eprintln!("before: {}", u32::decode(reader)?); + let from_hostname = String::decode(reader)?; + eprintln!("from hostname: {from_hostname}"); + let from_hostkeys = reader.read_prefixed(|reader| { + let mut keys = Vec::new(); + while !reader.is_finished() { + keys.push(KeySpec::decode(reader)?); + } + Ok::<_, crate::proto::error::ProtoError>(keys) + })?; + let to_username = String::decode(reader)?; + let to_hostname = String::decode(reader)?; + let to_hostkeys = reader.read_prefixed(|reader| { + let mut keys = Vec::new(); + while !reader.is_finished() { + keys.push(KeySpec::decode(reader)?); + } + Ok::<_, crate::proto::error::ProtoError>(keys) + })?; + Ok(Self { + from_username, + from_hostname, + from_hostkeys, + to_username, + to_hostname, + to_hostkeys, + }) + } +} + +#[derive(Debug, Clone)] +pub struct KeySpec { + pub keyblob: KeyData, + pub is_ca: bool, +} + +impl Decode for KeySpec { + type Error = crate::proto::error::ProtoError; + + fn decode(reader: &mut impl Reader) -> Result { + let keyblob = reader.read_prefixed(KeyData::decode)?; + Ok(Self { + keyblob, + is_ca: u8::decode(reader)? != 0, + }) + } +} + #[cfg(test)] mod tests { use super::*; @@ -54,4 +138,34 @@ mod tests { eprintln!("Bind: {bind:#?}"); Ok(()) } + + #[test] + fn parse_destination_constraint() -> TestResult { + let mut buffer: &[u8] = &[ + 0, 0, 0, 114, // + 0, 0, 0, 110, // + 0, 0, 0, 12, //from: + 0, 0, 0, 0, //username + 0, 0, 0, 0, //hostname + 0, 0, 0, 0, //reserved + // no host keys here + 0, 0, 0, 86, //to: + 0, 0, 0, 6, 119, 105, 107, 116, 111, 114, // wiktor + 0, 0, 0, 12, 109, 101, 116, 97, 99, 111, 100, 101, 46, 98, 105, + 122, // metacode.biz + 0, 0, 0, 0, // reserved, not in the spec authfd.c:469 + 0, 0, 0, 51, // + 0, 0, 0, 11, // + 115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, //ssh-ed25519 + 0, 0, 0, 32, // raw key + 177, 185, 198, 92, 165, 45, 127, 95, 202, 195, 226, 63, 6, 115, 10, 104, 18, 137, 172, + 240, 153, 154, 174, 74, 83, 7, 1, 204, 14, 177, 153, 40, // + 0, // is_ca + 0, 0, 0, 0, // reserved, not in the spec, authfd.c:495 + ]; + + let destination_constraint = RestrictDestination::decode(&mut buffer)?; + eprintln!("Destination constraint: {destination_constraint:?}"); + Ok(()) + } } diff --git a/src/proto/message.rs b/src/proto/message.rs index 6c28d14..a59d9fd 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -198,7 +198,7 @@ impl Decode for SmartcardKey { pub enum KeyConstraint { Lifetime(u32), Confirm, - Extension(String, Vec), + Extension(String, Unparsed), } impl Decode for KeyConstraint { @@ -210,7 +210,12 @@ impl Decode for KeyConstraint { Ok(match constraint_type { 1 => KeyConstraint::Lifetime(u32::decode(reader)?), 2 => KeyConstraint::Confirm, - 255 => KeyConstraint::Extension(String::decode(reader)?, Vec::::decode(reader)?), + 255 => { + let name = String::decode(reader)?; + let mut details = vec![0; reader.remaining_len()]; + reader.read(&mut details)?; + KeyConstraint::Extension(name, details.into()) + } _ => return Err(Error::AlgorithmUnknown), // FIXME: it should be our own type }) } @@ -589,7 +594,8 @@ mod tests { 4db0 d166 7660 1ffe f93a 6872 4800 0000 0000" ) - .to_vec(), + .to_vec() + .into(), )], }; From 9d97b09033e3d81b570d699a19a08f23b5767856 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Wed, 3 Apr 2024 22:44:36 -0700 Subject: [PATCH 17/25] fixup `RestrictDestination` constraint Signed-off-by: Arthur Gautier Signed-off-by: Wiktor Kwapisiewicz --- Cargo.lock | 7 +++ Cargo.toml | 1 + src/proto/extension.rs | 122 +++++++++++++++++++++++++++-------------- src/proto/message.rs | 10 ++++ 4 files changed, 99 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6108c44..60f7902 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -173,6 +173,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-str" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3618cccc083bb987a415d85c02ca6c9994ea5b44731ec28b9ecf09658655fba9" + [[package]] name = "cpufeatures" version = "0.2.12" @@ -892,6 +898,7 @@ version = "0.4.0" dependencies = [ "async-trait", "byteorder", + "const-str", "env_logger", "futures", "hex-literal", diff --git a/Cargo.toml b/Cargo.toml index 9ab67cf..fddb865 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,3 +44,4 @@ testresult = "0.4.0" hex-literal = "0.4.1" ssh-key = { version = "0.6.4", features = ["p256"] } p256 = { version = "0.13.2" } +const-str = "0.5.7" diff --git a/src/proto/extension.rs b/src/proto/extension.rs index 743ca4c..157774f 100644 --- a/src/proto/extension.rs +++ b/src/proto/extension.rs @@ -41,10 +41,8 @@ impl Decode for RestrictDestination { type Error = crate::proto::error::ProtoError; fn decode(reader: &mut impl Reader) -> Result { - eprintln!("encoding {}", u32::decode(reader)?); let mut constraints = Vec::new(); while !reader.is_finished() { - eprintln!("encoding"); constraints.push(reader.read_prefixed(DestinationConstraint::decode)?); } Ok(Self { constraints }) @@ -65,28 +63,25 @@ impl Decode for DestinationConstraint { type Error = crate::proto::error::ProtoError; fn decode(reader: &mut impl Reader) -> Result { - //eprintln!("before: {}", u32::decode(reader)?); - let from_username = String::decode(reader)?; - eprintln!("from username: {from_username} {}", from_username.len()); - eprintln!("before: {}", u32::decode(reader)?); - let from_hostname = String::decode(reader)?; - eprintln!("from hostname: {from_hostname}"); - let from_hostkeys = reader.read_prefixed(|reader| { - let mut keys = Vec::new(); - while !reader.is_finished() { - keys.push(KeySpec::decode(reader)?); - } - Ok::<_, crate::proto::error::ProtoError>(keys) - })?; - let to_username = String::decode(reader)?; - let to_hostname = String::decode(reader)?; - let to_hostkeys = reader.read_prefixed(|reader| { + fn read_user_host_keys( + reader: &mut impl Reader, + ) -> Result<(String, String, Vec), crate::proto::error::ProtoError> { + let username = String::decode(reader)?; + let hostname = String::decode(reader)?; + let _reserved = String::decode(reader)?; + let mut keys = Vec::new(); while !reader.is_finished() { keys.push(KeySpec::decode(reader)?); } - Ok::<_, crate::proto::error::ProtoError>(keys) - })?; + + Ok((username, hostname, keys)) + } + + let (from_username, from_hostname, from_hostkeys) = + reader.read_prefixed(read_user_host_keys)?; + let (to_username, to_hostname, to_hostkeys) = reader.read_prefixed(read_user_host_keys)?; + let _reserved = String::decode(reader)?; Ok(Self { from_username, from_hostname, @@ -119,6 +114,7 @@ impl Decode for KeySpec { #[cfg(test)] mod tests { use super::*; + use hex_literal::hex; use testresult::TestResult; #[test] @@ -141,28 +137,72 @@ mod tests { #[test] fn parse_destination_constraint() -> TestResult { - let mut buffer: &[u8] = &[ - 0, 0, 0, 114, // - 0, 0, 0, 110, // - 0, 0, 0, 12, //from: - 0, 0, 0, 0, //username - 0, 0, 0, 0, //hostname - 0, 0, 0, 0, //reserved + let mut msg = &hex!( + " 00 + 0002 6f00 0000 0c00 0000 0000 0000 0000 + 0000 0000 0002 5700 0000 0000 0000 0a67 + 6974 6875 622e 636f 6d00 0000 0000 0000 + 3300 0000 0b73 7368 2d65 6432 3535 3139 + 0000 0020 e32a aa79 15ce b9b4 49d1 ba50 + ea2a 28bb 1a6e 01f9 0bda 245a 2d1d 8769 + 7d18 a265 0000 0001 9700 0000 0773 7368 + 2d72 7361 0000 0003 0100 0100 0001 8100 + a3ee 774d c50a 3081 c427 8ec8 5c2e ba8f + 1228 a986 7b7e 5534 ef0c fea6 1c12 fd8f + 568d 5246 3851 ed60 bf09 c62d 594e 8467 + 98ae 765a 3204 4aeb e3ca 0945 da0d b0bb + aad6 d6f2 0224 84be da18 2b0e aff0 b9e9 + 224c cbf0 4265 fc5d d675 b300 ec52 0cf8 + 15b2 67ab 3816 1f36 a96d 57df e158 2a81 + cb02 0d21 1fb9 7488 3a25 327b da97 04a4 + 48dc 6205 e413 6604 1575 7524 79ec 2a06 + cb58 d961 49ca 9bd9 49b2 4644 32ca d44b + b4bf b7f1 31b1 9310 9f96 63be e59f 0249 + 2358 ec68 9d8c c219 ed0e 3332 3036 9f59 + c6ae 54c3 933c 030a cc3e c2a1 4f19 0035 + efd7 277c 658e 5915 6bba 3d7a cfa5 f2bf + 1be3 2706 f3d3 0419 ef95 cae6 d292 6fb1 + 4dc9 e204 b384 d3e2 393e 4b87 613d e014 + 0b9c be6c 3622 ad88 0ce0 60bb b849 f3b6 + 7672 6955 90ec 1dfc d402 b841 daf0 b79d + 59a8 4c4a 6d0a 5350 d9fe 123a a84f 0bea + 363e 24ab 1e50 5022 344e 14bf 6243 b124 + 25e6 3d45 996e 18e9 0a0e 7a8b ed9a 07a0 + a62b 6246 867e 7b2b 99a3 d0c3 5d05 7038 + fd69 f01f a5e8 3d62 732b 9372 bb6c c1de + 7019 a7e4 b986 942c fa9d 6f37 5ff0 b239 + 0000 0000 6800 0000 1365 6364 7361 2d73 + 6861 322d 6e69 7374 7032 3536 0000 0008 + 6e69 7374 7032 3536 0000 0041 0449 8a48 + 4363 4047 b33a 6c64 64cc bba2 92a0 c050 + 7d9e 4b79 611a d832 336e 1b93 7cee e460 + 83a0 8bad ba39 c007 53ff 2eaf d262 95d1 + 4db0 d166 7660 1ffe f93a 6872 4800 0000 + 0000" + )[..]; + let _destination_constraint = RestrictDestination::decode(&mut msg)?; + + #[rustfmt::skip] + let mut buffer: &[u8] = const_str::concat_bytes!( + [0, 0, 0, 114], // + [0, 0, 0, 12], //from: + [0, 0, 0, 0], //username + [0, 0, 0, 0], //hostname + [0, 0, 0, 0], //reserved // no host keys here - 0, 0, 0, 86, //to: - 0, 0, 0, 6, 119, 105, 107, 116, 111, 114, // wiktor - 0, 0, 0, 12, 109, 101, 116, 97, 99, 111, 100, 101, 46, 98, 105, - 122, // metacode.biz - 0, 0, 0, 0, // reserved, not in the spec authfd.c:469 - 0, 0, 0, 51, // - 0, 0, 0, 11, // - 115, 115, 104, 45, 101, 100, 50, 53, 53, 49, 57, //ssh-ed25519 - 0, 0, 0, 32, // raw key - 177, 185, 198, 92, 165, 45, 127, 95, 202, 195, 226, 63, 6, 115, 10, 104, 18, 137, 172, - 240, 153, 154, 174, 74, 83, 7, 1, 204, 14, 177, 153, 40, // - 0, // is_ca - 0, 0, 0, 0, // reserved, not in the spec, authfd.c:495 - ]; + [0, 0, 0, 86], //to: + [0, 0, 0, 6], b"wiktor", + [0, 0, 0, 12], b"metacode.biz", + [0, 0, 0, 0], // reserved, not in the spec authfd.c:469 + [0, 0, 0, 51], // + [0, 0, 0, 11], // + b"ssh-ed25519", + [0, 0, 0, 32], // raw key + [177, 185, 198, 92, 165, 45, 127, 95, 202, 195, 226, 63, 6, 115, 10, 104, 18, 137, 172, + 240, 153, 154, 174, 74, 83, 7, 1, 204, 14, 177, 153, 40], // + [0], // is_ca + [0, 0, 0, 0], // reserved, not in the spec, authfd.c:495 + ); let destination_constraint = RestrictDestination::decode(&mut buffer)?; eprintln!("Destination constraint: {destination_constraint:?}"); diff --git a/src/proto/message.rs b/src/proto/message.rs index a59d9fd..e7bf911 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -311,6 +311,16 @@ impl From> for Unparsed { } } +impl Encode for Unparsed { + fn encoded_len(&self) -> ssh_encoding::Result { + self.0.encoded_len() + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + self.0.encode(writer) + } +} + pub type Passphrase = String; #[derive(Clone, PartialEq, Debug)] From 623496b0dbb6f7e4ec48d34711570af4f24371ca Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Thu, 4 Apr 2024 09:01:05 +0200 Subject: [PATCH 18/25] Temporarily disable one test vector Signed-off-by: Wiktor Kwapisiewicz --- src/proto/message.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/proto/message.rs b/src/proto/message.rs index e7bf911..1f4463c 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -460,6 +460,7 @@ mod tests { } #[test] + #[ignore = "temporarily disable until test vectors are updated"] fn test_add_identity_constrained() { let msg: &[u8] = &hex!( " From 9e8f28be3c1146e38269aba2e4bf32e10063f571 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Thu, 4 Apr 2024 09:31:32 +0200 Subject: [PATCH 19/25] Add roundtrip tests Signed-off-by: Wiktor Kwapisiewicz --- Cargo.lock | 63 ++++++++++++++++++ Cargo.toml | 2 + src/agent.rs | 5 ++ tests/messages/add-identity.bin | Bin 0 -> 1403 bytes .../add-smartcard-key-constrained.bin | Bin 0 -> 23 bytes tests/messages/add-smartcard-key.bin | Bin 0 -> 17 bytes tests/messages/add_identity_constrained.bin | Bin 0 -> 1408 bytes tests/messages/lock.bin | Bin 0 -> 9 bytes tests/messages/request-identities.bin | 1 + tests/messages/sign_request.bin | Bin 0 -> 516 bytes tests/messages/unlock.bin | Bin 0 -> 9 bytes tests/roundtrip.rs | 17 +++++ 12 files changed, 88 insertions(+) create mode 100644 tests/messages/add-identity.bin create mode 100644 tests/messages/add-smartcard-key-constrained.bin create mode 100644 tests/messages/add-smartcard-key.bin create mode 100644 tests/messages/add_identity_constrained.bin create mode 100644 tests/messages/lock.bin create mode 100644 tests/messages/request-identities.bin create mode 100644 tests/messages/sign_request.bin create mode 100644 tests/messages/unlock.bin create mode 100644 tests/roundtrip.rs diff --git a/Cargo.lock b/Cargo.lock index 60f7902..02b0b9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -371,6 +371,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +[[package]] +name = "futures-timer" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" + [[package]] name = "futures-util" version = "0.3.30" @@ -417,6 +423,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + [[package]] name = "group" version = "0.13.0" @@ -761,6 +773,12 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "relative-path" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" + [[package]] name = "rfc6979" version = "0.4.0" @@ -793,12 +811,50 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rstest" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +dependencies = [ + "futures", + "futures-timer", + "rstest_macros", + "rustc_version", +] + +[[package]] +name = "rstest_macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +dependencies = [ + "cfg-if", + "glob", + "proc-macro2", + "quote", + "regex", + "relative-path", + "rustc_version", + "syn", + "unicode-ident", +] + [[package]] name = "rustc-demangle" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "sec1" version = "0.7.3" @@ -813,6 +869,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + [[package]] name = "service-binding" version = "2.0.0" @@ -906,6 +968,7 @@ dependencies = [ "p256", "rand", "rsa", + "rstest", "service-binding", "sha1", "ssh-encoding", diff --git a/Cargo.toml b/Cargo.toml index fddb865..fc42128 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ tokio-util = { version = "0.7.1", optional = true, features = ["codec"] } service-binding = { version = "^2" } ssh-encoding = { version = "0.2.0" } ssh-key = { version = "0.6.4", features = ["rsa", "alloc"] } +#uuid = { version = "1.8.0", features = ["v4"] } [features] default = ["agent"] @@ -45,3 +46,4 @@ hex-literal = "0.4.1" ssh-key = { version = "0.6.4", features = ["p256"] } p256 = { version = "0.13.2" } const-str = "0.5.7" +rstest = "0.18.2" diff --git a/src/agent.rs b/src/agent.rs index c6498f4..92b0c2b 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -40,6 +40,11 @@ impl Decoder for MessageCodec { return Ok(None); } + //use std::io::Write; + //let mut file = std::fs::File::create(uuid::Uuid::new_v4().to_string())?; + //file.write_all(bytes)?; + //drop(file); + let message: Message = Message::decode(&mut bytes)?; src.advance(size_of::() + length); Ok(Some(message)) diff --git a/tests/messages/add-identity.bin b/tests/messages/add-identity.bin new file mode 100644 index 0000000000000000000000000000000000000000..cbdff2a6f9f1086534e4f6b728ca2b255eff18f6 GIT binary patch literal 1403 zcmV->1%&z$00007b8~1da&ut-00Dsj$_U*3I>dn0WhVX-a$MIyvQOp#;Q(V0s6LcjH|1V&^hJ_0 z{noIo8>r|3%s5>F71e}1_*5so2(vf{w2ufA1!3A3tp6}GB=iV*w2o;uL7l8KN~lZS zOdA}@>xv6WZ_9dzMgk${sI2V)cZqMt3V0{RKue}0ayy@^&6rMd`<$RCu7S2WU_ROl}in;x(MN+_(anOv%--nYlB_C&!!HcpA{Xp0m zeB8wBYnAQ)x}66$q+!E)IGAAq?9x~LrO;i6KqK6>bxTgRd~4wikHqO7i^sNd|8P1J z-0C$61y^^f&2j(j$w3=)%t$S6-t^1-jwGLZmc zmv~mgA1rQW^6AjDVo?^Y!J^=XDu;`|Fmj=VF20!q^2aZvqDqg68J72pH?G55ppbTL zn!W=`$z=pcTA{kqU~#olmJt&lc|((cV}oWHT_Zx_{E<`_c@Zn)gB^;BtIRo}L-v72 zURv_$MO_nUJ>Lkq?5nQRZQKl3>#k9!qGzAubtQVTmW`yy-e8#{+!A9N zz$Mbpws;MXu2tASOy6ul#qoZ-!sm%?bvg7(bTtgAH3yL(!QB-yUR3Wx0Fz@9XgI{v zKb5qlO0ru?vrlt{Emm)4AMbvJ9gVoH3U2xpppXA~)kA zbQZvorJ6Ec>1Qs%9z&5BLmeZb_Gdc(=lRkels4ivaTC~eMIDMA<)k8XKqGT4x6PD` zjIvO}c)&Rz5*YiHLCh=L82h*ttEYjhLfxe>gPd9LQ8Fk7dum5|AR=-}%RzlaKm1+* z2+6O94^`d$1L~9jRM1>< znNe4j%}EPV-SiAK8lrsEz(*wrUU*VNYUArKV@l3yL_6hMV0D)Med3qP0?g zEpJyq-ti&MVt-Q$-B^VT--UDzV${PLIH3Ic{4*AMHp)fw|JhkMq;LkJHm~GnsK15x zX+S4iQek(0R<+BzpRzM2dVzHx3>%qK{1@tHwHYg}QeY> literal 0 HcmV?d00001 diff --git a/tests/messages/add_identity_constrained.bin b/tests/messages/add_identity_constrained.bin new file mode 100644 index 0000000000000000000000000000000000000000..1d0d9948c20b8c2faae6568818d19a99dfacfe18 GIT binary patch literal 1408 zcmV-`1%LV(00007b8~1da&ut-00Dsj$_U*3I>dn0WhVX-a$MIyvQOp#;Q(V0s6LcjH|1V&^hJ_0 z{noIo8>r|3%s5>F71e}1_*5so2(vf{w2ufA1!3A3tp6}GB=iV*w2o;uL7l8KN~lZS zOdA}@>xv6WZ_9dzMgk${sI2V)cZqMt3V0{RKue}0ayy@^&6rMd`<$RCu7S2WU_ROl}in;x(MN+_(anOv%--nYlB_C&!!HcpA{Xp0m zeB8wBYnAQ)x}66$q+!E)IGAAq?9x~LrO;i6KqK6>bxTgRd~4wikHqO7i^sNd|8P1J z-0C$61y^^f&2j(j$w3=)%t$S6-t^1-jwGLZmc zmv~mgA1rQW^6AjDVo?^Y!J^=XDu;`|Fmj=VF20!q^2aZvqDqg68J72pH?G55ppbTL zn!W=`$z=pcTA{kqU~#olmJt&lc|((cV}oWHT_Zx_{E<`_c@Zn)gB^;BtIRo}L-v72 zURv_$MO_nUJ>Lkq?5nQRZQKl3>#k9!qGzAubtQVTmW`yy-e8#{+!A9N zz$Mbpws;MXu2tASOy6ul#qoZ-!sm%?bvg7(bTtgAH3yL(!QB-yUR3Wx0Fz@9XgI{v zKb5qlO0ru?vrlt{Emm)4AMbvJ9gVoH3U2xpppXA~)kA zbQZvorJ6Ec>1Qs%9z&5BLmeZb_Gdc(=lRkels4ivaTC~eMIDMA<)k8XKqGT4x6PD` zjIvO}c)&Rz5*YiHLCh=L82h*ttEYjhLfxe>gPd9LQ8Fk7dum5|AR=-}%RzlaKm1+* z2+6O94^`d$1L~9jRM1>< znNe4j%}EPV-SiAK8lrsEz(*wrUU*VNYUArKV@l3yL_6hMV0D)Med3qP0?g zEpJyq-ti&MVt-Q$-B^VT--UDzV${PLIH3Ic{4*AMHp)fw|JhkMq;LkJHm~GnsK15x zX+S4iQek(0R<+BzpRzM2dVzHx3>%qK{1@tHwHYg}QeY>BiyxhOHQ_YYvB!# z#OWT3$F_3+a5@v*>NN=kS9hw=3(!_9Kk%u;Y@+rWs29>iadZxSO(#8nZ&0sn8tqU= zZDZa$kZG6%eYt~)quUR;`+b#_z-?L96-;!Fd^V#D5|f3Fy{RL literal 0 HcmV?d00001 diff --git a/tests/messages/unlock.bin b/tests/messages/unlock.bin new file mode 100644 index 0000000000000000000000000000000000000000..87fdf844eb134ebe451300de830205eebbad99ae GIT binary patch literal 9 QcmWe+yDRo literal 0 HcmV?d00001 diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs new file mode 100644 index 0000000..02c1e6d --- /dev/null +++ b/tests/roundtrip.rs @@ -0,0 +1,17 @@ +use rstest::rstest; +use ssh_agent_lib::proto::Message; +use ssh_encoding::{Decode /*, Encode*/}; +use std::path::PathBuf; +use testresult::TestResult; + +#[rstest] +fn main(#[files("tests/messages/*.bin")] path: PathBuf) -> TestResult { + let bytes = std::fs::read(path)?; + let mut bytes: &[u8] = &bytes; + let _message = Message::decode(&mut bytes)?; + // FIXME: Uncomment when the roundtrip works + //let mut out = vec![]; + //message.encode(&mut out)?; + //assert_eq!(bytes, out); + Ok(()) +} From dc2bc0a5dceeb90593ca1d6627143a404f288e7d Mon Sep 17 00:00:00 2001 From: James Spencer Date: Thu, 4 Apr 2024 16:57:44 +1100 Subject: [PATCH 20/25] Add Encode traits for remaining message types Signed-off-by: James Spencer --- src/proto/message.rs | 135 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 114 insertions(+), 21 deletions(-) diff --git a/src/proto/message.rs b/src/proto/message.rs index 1f4463c..71e7c56 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -74,7 +74,7 @@ impl Decode for SignRequest { impl Encode for SignRequest { fn encoded_len(&self) -> ssh_encoding::Result { [ - self.pubkey.encoded_len()?, + self.pubkey.encoded_len_prefixed()?, self.data.encoded_len()?, self.flags.encoded_len()?, ] @@ -82,7 +82,7 @@ impl Encode for SignRequest { } fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { - self.pubkey.encode(writer)?; + self.pubkey.encode_prefixed(writer)?; self.data.encode(writer)?; self.flags.encode(writer)?; @@ -177,6 +177,16 @@ impl Decode for RemoveIdentity { } } +impl Encode for RemoveIdentity { + fn encoded_len(&self) -> ssh_encoding::Result { + self.pubkey.encoded_len_prefixed() + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + self.pubkey.encode_prefixed(writer) + } +} + #[derive(Clone, PartialEq, Debug)] pub struct SmartcardKey { pub id: String, @@ -194,6 +204,19 @@ impl Decode for SmartcardKey { } } +impl Encode for SmartcardKey { + fn encoded_len(&self) -> ssh_encoding::Result { + [self.id.encoded_len()?, self.pin.encoded_len()?].checked_sum() + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + self.id.encode(writer)?; + self.pin.encode(writer)?; + + Ok(()) + } +} + #[derive(Clone, PartialEq, Debug)] pub enum KeyConstraint { Lifetime(u32), @@ -231,7 +254,7 @@ impl Encode for KeyConstraint { .ok_or(EncodingError::Length), Self::Confirm => Ok(base), Self::Extension(name, content) => { - [base, name.encoded_len()?, content.encoded_len()?].checked_sum() + [base, name.encoded_len()?, content.0.encoded_len()?].checked_sum() } } } @@ -246,7 +269,7 @@ impl Encode for KeyConstraint { Self::Extension(name, content) => { 255u8.encode(writer)?; name.encode(writer)?; - content.encode(writer) + content.0.encode(writer) } } } @@ -272,6 +295,25 @@ impl Decode for AddSmartcardKeyConstrained { } } +impl Encode for AddSmartcardKeyConstrained { + fn encoded_len(&self) -> ssh_encoding::Result { + self.constraints + .iter() + .try_fold(self.key.encoded_len()?, |acc, e| { + let constraint_len = e.encoded_len()?; + usize::checked_add(acc, constraint_len).ok_or(EncodingError::Length) + }) + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + self.key.encode(writer)?; + for constraint in &self.constraints { + constraint.encode(writer)?; + } + Ok(()) + } +} + #[derive(Clone, PartialEq, Debug)] pub struct Extension { pub name: String, @@ -292,6 +334,20 @@ impl Decode for Extension { } } +impl Encode for Extension { + fn encoded_len(&self) -> ssh_encoding::Result { + [self.name.encoded_len()?, self.details.0.encoded_len()?].checked_sum() + } + + fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { + self.name.encode(writer)?; + + // NOTE: extension messages do not contain a length! + writer.write(&self.details.0[..])?; + Ok(()) + } +} + #[derive(Debug, PartialEq, Clone)] pub struct Unparsed(Vec); @@ -344,6 +400,30 @@ pub enum Message { ExtensionFailure, } +impl Message { + pub fn message_id(&self) -> u8 { + match self { + Message::Failure => 5, + Message::Success => 6, + Message::RequestIdentities => 11, + Message::IdentitiesAnswer(_) => 12, + Message::SignRequest(_) => 13, + Message::SignResponse(_) => 14, + Message::AddIdentity(_) => 17, + Message::RemoveIdentity(_) => 18, + Message::RemoveAllIdentities => 19, + Message::AddSmartcardKey(_) => 20, + Message::RemoveSmartcardKey(_) => 21, + Message::Lock(_) => 22, + Message::Unlock(_) => 23, + Message::AddIdConstrained(_) => 25, + Message::AddSmartcardKeyConstrained(_) => 26, + Message::Extension(_) => 27, + Message::ExtensionFailure => 28, + } + } +} + impl Decode for Message { type Error = Error; @@ -375,7 +455,7 @@ impl Decode for Message { impl Encode for Message { fn encoded_len(&self) -> ssh_encoding::Result { - let command_id = 1; + let message_id_len = 1; let payload_len = match self { Self::Failure => 0, Self::Success => 0, @@ -391,24 +471,28 @@ impl Encode for Message { lengths.checked_sum()? } - Self::SignResponse(response) => response.encoded_len()? + 4, - _ => todo!(), + Self::SignRequest(request) => request.encoded_len()?, + Self::SignResponse(response) => response.encoded_len_prefixed()?, + Self::AddIdentity(identity) => identity.encoded_len()?, + Self::RemoveIdentity(identity) => identity.encoded_len()?, + Self::RemoveAllIdentities => 0, + Self::AddSmartcardKey(key) => key.encoded_len()?, + Self::RemoveSmartcardKey(key) => key.encoded_len()?, + Self::Lock(passphrase) => passphrase.encoded_len()?, + Self::Unlock(passphrase) => passphrase.encoded_len()?, + Self::AddIdConstrained(key) => key.encoded_len()?, + Self::AddSmartcardKeyConstrained(key) => key.encoded_len()?, + Self::Extension(extension) => extension.encoded_len()?, + Self::ExtensionFailure => 0, }; - [command_id, payload_len].checked_sum() + [message_id_len, payload_len].checked_sum() } fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> { - let command_id: u8 = match self { - Self::Failure => 5, - Self::Success => 6, - Self::RequestIdentities => 11, - Self::IdentitiesAnswer(_) => 12, - Self::SignResponse(_) => 14, - _ => todo!(), - }; + let message_id: u8 = self.message_id(); + message_id.encode(writer)?; - command_id.encode(writer)?; match self { Self::Failure => {} Self::Success => {} @@ -419,10 +503,19 @@ impl Encode for Message { id.encode(writer)?; } } - Self::SignResponse(response) => { - response.encode_prefixed(writer)?; - } - _ => todo!(), + Self::SignRequest(request) => request.encode(writer)?, + Self::SignResponse(response) => response.encode_prefixed(writer)?, + Self::AddIdentity(identity) => identity.encode(writer)?, + Self::RemoveIdentity(identity) => identity.encode(writer)?, + Self::RemoveAllIdentities => {} + Self::AddSmartcardKey(key) => key.encode(writer)?, + Self::RemoveSmartcardKey(key) => key.encode(writer)?, + Self::Lock(passphrase) => passphrase.encode(writer)?, + Self::Unlock(passphrase) => passphrase.encode(writer)?, + Self::AddIdConstrained(identity) => identity.encode(writer)?, + Self::AddSmartcardKeyConstrained(key) => key.encode(writer)?, + Self::Extension(extension) => extension.encode(writer)?, + Self::ExtensionFailure => {} }; Ok(()) From f44be9d66f82e53eb70e8c19eec1680ad3197845 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Thu, 4 Apr 2024 09:34:44 +0200 Subject: [PATCH 21/25] Enable encoding tests Signed-off-by: Wiktor Kwapisiewicz --- tests/roundtrip.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 02c1e6d..2ed1d64 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -1,17 +1,16 @@ use rstest::rstest; use ssh_agent_lib::proto::Message; -use ssh_encoding::{Decode /*, Encode*/}; +use ssh_encoding::{Decode, Encode}; use std::path::PathBuf; use testresult::TestResult; #[rstest] fn main(#[files("tests/messages/*.bin")] path: PathBuf) -> TestResult { - let bytes = std::fs::read(path)?; - let mut bytes: &[u8] = &bytes; - let _message = Message::decode(&mut bytes)?; - // FIXME: Uncomment when the roundtrip works - //let mut out = vec![]; - //message.encode(&mut out)?; - //assert_eq!(bytes, out); + let serialized = std::fs::read(path)?; + let mut bytes: &[u8] = &serialized; + let message = Message::decode(&mut bytes)?; + let mut out = vec![]; + message.encode(&mut out)?; + assert_eq!(serialized, out); Ok(()) } From 77c5e4b130c54c015e8c57e197a1b67c758b704f Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Thu, 4 Apr 2024 09:52:54 +0200 Subject: [PATCH 22/25] Add `req-` prefix for requests Signed-off-by: Wiktor Kwapisiewicz --- ...trained.bin => req-add-identity-constrained.bin} | Bin .../{add-identity.bin => req-add-identity.bin} | Bin ...ed.bin => req-add-smartcard-key-constrained.bin} | Bin ...-smartcard-key.bin => req-add-smartcard-key.bin} | Bin tests/messages/{lock.bin => req-lock.bin} | Bin ...st-identities.bin => req-request-identities.bin} | 0 .../{sign_request.bin => req-sign-request.bin} | Bin tests/messages/{unlock.bin => req-unlock.bin} | Bin tests/roundtrip.rs | 2 +- 9 files changed, 1 insertion(+), 1 deletion(-) rename tests/messages/{add_identity_constrained.bin => req-add-identity-constrained.bin} (100%) rename tests/messages/{add-identity.bin => req-add-identity.bin} (100%) rename tests/messages/{add-smartcard-key-constrained.bin => req-add-smartcard-key-constrained.bin} (100%) rename tests/messages/{add-smartcard-key.bin => req-add-smartcard-key.bin} (100%) rename tests/messages/{lock.bin => req-lock.bin} (100%) rename tests/messages/{request-identities.bin => req-request-identities.bin} (100%) rename tests/messages/{sign_request.bin => req-sign-request.bin} (100%) rename tests/messages/{unlock.bin => req-unlock.bin} (100%) diff --git a/tests/messages/add_identity_constrained.bin b/tests/messages/req-add-identity-constrained.bin similarity index 100% rename from tests/messages/add_identity_constrained.bin rename to tests/messages/req-add-identity-constrained.bin diff --git a/tests/messages/add-identity.bin b/tests/messages/req-add-identity.bin similarity index 100% rename from tests/messages/add-identity.bin rename to tests/messages/req-add-identity.bin diff --git a/tests/messages/add-smartcard-key-constrained.bin b/tests/messages/req-add-smartcard-key-constrained.bin similarity index 100% rename from tests/messages/add-smartcard-key-constrained.bin rename to tests/messages/req-add-smartcard-key-constrained.bin diff --git a/tests/messages/add-smartcard-key.bin b/tests/messages/req-add-smartcard-key.bin similarity index 100% rename from tests/messages/add-smartcard-key.bin rename to tests/messages/req-add-smartcard-key.bin diff --git a/tests/messages/lock.bin b/tests/messages/req-lock.bin similarity index 100% rename from tests/messages/lock.bin rename to tests/messages/req-lock.bin diff --git a/tests/messages/request-identities.bin b/tests/messages/req-request-identities.bin similarity index 100% rename from tests/messages/request-identities.bin rename to tests/messages/req-request-identities.bin diff --git a/tests/messages/sign_request.bin b/tests/messages/req-sign-request.bin similarity index 100% rename from tests/messages/sign_request.bin rename to tests/messages/req-sign-request.bin diff --git a/tests/messages/unlock.bin b/tests/messages/req-unlock.bin similarity index 100% rename from tests/messages/unlock.bin rename to tests/messages/req-unlock.bin diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index 2ed1d64..f879dfa 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; use testresult::TestResult; #[rstest] -fn main(#[files("tests/messages/*.bin")] path: PathBuf) -> TestResult { +fn roundtrip(#[files("tests/messages/*.bin")] path: PathBuf) -> TestResult { let serialized = std::fs::read(path)?; let mut bytes: &[u8] = &serialized; let message = Message::decode(&mut bytes)?; From 003513a2b48876bb1caec6d5a1df8aac397df04b Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Thu, 4 Apr 2024 11:06:18 +0200 Subject: [PATCH 23/25] Fix decoding of `SignResponse` Signed-off-by: Wiktor Kwapisiewicz --- src/agent.rs | 8 +++++++- src/proto/message.rs | 2 +- tests/messages/resp-identities-answer.bin | Bin 0 -> 432 bytes tests/messages/resp-sign-response.bin | Bin 0 -> 409 bytes tests/messages/resp-success.bin | 1 + tests/sign-and-verify.sh | 3 +++ 6 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 tests/messages/resp-identities-answer.bin create mode 100644 tests/messages/resp-sign-response.bin create mode 100644 tests/messages/resp-success.bin diff --git a/src/agent.rs b/src/agent.rs index 92b0c2b..21a7b59 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -61,8 +61,14 @@ impl Encoder for MessageCodec { len.encode(&mut bytes)?; item.encode(&mut bytes)?; - dst.put(&*bytes); + //use std::io::Write; + //let mut file = std::fs::File::create(uuid::Uuid::new_v4().to_string())?; + //let mut bytes = Vec::new(); + //item.encode(&mut bytes)?; + //file.write_all(&bytes)?; + //drop(file); + Ok(()) } } diff --git a/src/proto/message.rs b/src/proto/message.rs index 71e7c56..f91db33 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -436,7 +436,7 @@ impl Decode for Message { 11 => Ok(Self::RequestIdentities), 12 => Identity::decode_vec(reader).map(Self::IdentitiesAnswer), 13 => SignRequest::decode(reader).map(Self::SignRequest), - 14 => Signature::decode(reader).map(Self::SignResponse), + 14 => reader.read_prefixed(|reader| Signature::decode(reader).map(Self::SignResponse)), 17 => AddIdentity::decode(reader).map(Self::AddIdentity), 18 => RemoveIdentity::decode(reader).map(Self::RemoveIdentity), 19 => Ok(Self::RemoveAllIdentities), diff --git a/tests/messages/resp-identities-answer.bin b/tests/messages/resp-identities-answer.bin new file mode 100644 index 0000000000000000000000000000000000000000..eb72af9a05a5e7adc088aee2e91b6368585dd457 GIT binary patch literal 432 zcmV;h0Z;x6000010004(00007b8~1da&ut-00094009610f7LRzsL`qhQX_BHl7Or z=3zkA7T(su!&bw@d}y9fGHI64gq9VjR0Q3p_fs*r-`2qw6_YK_(E4DyKLreW1S)Cc z)AmEU100@Hd36giuq#)0@}H&9Q>bkk0*Bu?I#?Es0hRk2!PV_^Fzh;fLxPJ@#ajjm z@MO_-Ss3QUI_JVZycJ0UU;LWrbQw)P&N;fd_#nvc_X+2vM-LjsbbuQNPoj-AZA02W zm*(E*))X|;6gXzdWM*hyjFunyggSU_BI>6&>-PZ3KOrC6o@IKEyO!1*$C{u>J_nfJ zXJM^vTXAAE=maz-`M;^P#cGdj5D2_{^#M zN}oxv>6Dc8xbg`)AFK8iRXvJ0H0r0w;`pq8#oHM<_}dI}*5f1-n}Z!oKkkuE)WO`< aNsM`YuQRUz000bkX=`+EazJunWN&Wq$;!C^ literal 0 HcmV?d00001 diff --git a/tests/messages/resp-sign-response.bin b/tests/messages/resp-sign-response.bin new file mode 100644 index 0000000000000000000000000000000000000000..ac2964a5ee22d6c3be272d47268d0f6568e737d0 GIT binary patch literal 409 zcmV;K0cQRV0004$0000Ca&uuVb7)~QEj2MR0004iae=BZLQeWv){QIt$$iOsG9$6N z@0C*m0$#Bc62UK|9BMs+LdWe!&i|an;j#|So(K6nO!_;Zt;zsaL=*_)ZE#KEGb>>d7cYs^E2jo_spc6J~9WI2_U-TN&HS^ zOUg6@RJNKiRAA%>|5%Uie93^)$iX=sxNdJj`!dRbBuN3z(Fpws-SYPQO4El1*w!gu z7|LfZvx)E)U;%%LZ6jOg_^7`W4$3MhgaPbR&v|axYbgO3chibA3b^0Dl;0xB7o1SP zPk(bW)yQdhWPu5P8Vx`junU$ujJ+zwfOfnM2V%8;m(7J9@mL6hA~47&;H>%!2zJ_W zGY#x#sT+5Lak;udF0dL5Ck?yY@{la3 Date: Thu, 4 Apr 2024 09:51:24 -0700 Subject: [PATCH 24/25] proto: rewrite error handling, remove a todo Signed-off-by: Arthur Gautier --- src/agent.rs | 17 +++-------------- src/error.rs | 22 ++++++++++++---------- src/proto/error.rs | 5 +++++ src/proto/message.rs | 35 +++++++++++++++++++++-------------- 4 files changed, 41 insertions(+), 38 deletions(-) diff --git a/src/agent.rs b/src/agent.rs index 21a7b59..47e4d84 100644 --- a/src/agent.rs +++ b/src/agent.rs @@ -18,7 +18,7 @@ use std::marker::Unpin; use std::mem::size_of; use super::error::AgentError; -use super::proto::message::Message; +use super::proto::{message::Message, ProtoError}; #[derive(Debug)] pub struct MessageCodec; @@ -40,11 +40,6 @@ impl Decoder for MessageCodec { return Ok(None); } - //use std::io::Write; - //let mut file = std::fs::File::create(uuid::Uuid::new_v4().to_string())?; - //file.write_all(bytes)?; - //drop(file); - let message: Message = Message::decode(&mut bytes)?; src.advance(size_of::() + length); Ok(Some(message)) @@ -58,16 +53,10 @@ impl Encoder for MessageCodec { let mut bytes = Vec::new(); let len = item.encoded_len().unwrap() as u32; - len.encode(&mut bytes)?; + len.encode(&mut bytes).map_err(ProtoError::SshEncoding)?; - item.encode(&mut bytes)?; + item.encode(&mut bytes).map_err(ProtoError::SshEncoding)?; dst.put(&*bytes); - //use std::io::Write; - //let mut file = std::fs::File::create(uuid::Uuid::new_v4().to_string())?; - //let mut bytes = Vec::new(); - //item.encode(&mut bytes)?; - //file.write_all(&bytes)?; - //drop(file); Ok(()) } diff --git a/src/error.rs b/src/error.rs index 0d63eef..455a16c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,23 +1,25 @@ use std::io; +use crate::proto::ProtoError; + #[derive(Debug)] pub enum AgentError { - Ssh(ssh_key::Error), - Proto(ssh_encoding::Error), + //Ssh(ssh_key::Error), + Proto(ProtoError), IO(io::Error), } -impl From for AgentError { - fn from(e: ssh_encoding::Error) -> AgentError { +impl From for AgentError { + fn from(e: ProtoError) -> AgentError { AgentError::Proto(e) } } -impl From for AgentError { - fn from(e: ssh_key::Error) -> AgentError { - AgentError::Ssh(e) - } -} +//impl From for AgentError { +// fn from(e: ssh_key::Error) -> AgentError { +// AgentError::Ssh(e) +// } +//} impl From for AgentError { fn from(e: io::Error) -> AgentError { @@ -28,7 +30,7 @@ impl From for AgentError { impl std::fmt::Display for AgentError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - AgentError::Ssh(e) => write!(f, "Agent: Ssh key error: {e}"), + //AgentError::Ssh(e) => write!(f, "Agent: Ssh key error: {e}"), AgentError::Proto(proto) => write!(f, "Agent: Protocol error: {}", proto), AgentError::IO(error) => write!(f, "Agent: I/O error: {}", error), } diff --git a/src/proto/error.rs b/src/proto/error.rs index 896113a..781773f 100644 --- a/src/proto/error.rs +++ b/src/proto/error.rs @@ -9,6 +9,7 @@ pub enum ProtoError { IO(io::Error), SshEncoding(ssh_encoding::Error), SshKey(ssh_key::Error), + UnsupportedCommand { command: u8 }, } impl From for () { @@ -48,6 +49,7 @@ impl std::error::Error for ProtoError { ProtoError::IO(e) => Some(e), ProtoError::SshEncoding(e) => Some(e), ProtoError::SshKey(e) => Some(e), + ProtoError::UnsupportedCommand { .. } => None, } } } @@ -61,6 +63,9 @@ impl std::fmt::Display for ProtoError { ProtoError::IO(_) => f.write_str("I/O Error"), ProtoError::SshEncoding(_) => f.write_str("SSH encoding Error"), ProtoError::SshKey(e) => write!(f, "SSH key Error: {e}"), + ProtoError::UnsupportedCommand { command } => { + write!(f, "Command not supported ({command})") + } } } } diff --git a/src/proto/message.rs b/src/proto/message.rs index f91db33..001e713 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -1,5 +1,9 @@ use ssh_encoding::{CheckedSum, Decode, Encode, Error as EncodingError, Reader, Writer}; -use ssh_key::{private::KeypairData, public::KeyData, Error, Result, Signature}; +use ssh_key::{private::KeypairData, public::KeyData, Error, Signature}; + +use super::ProtoError; + +type Result = core::result::Result; #[derive(Clone, PartialEq, Debug)] pub struct Identity { @@ -21,7 +25,7 @@ impl Identity { } impl Decode for Identity { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let pubkey = reader.read_prefixed(KeyData::decode)?; @@ -56,7 +60,7 @@ pub struct SignRequest { } impl Decode for SignRequest { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let pubkey = reader.read_prefixed(KeyData::decode)?; @@ -97,7 +101,7 @@ pub struct AddIdentity { } impl Decode for AddIdentity { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let privkey = KeypairData::decode(reader)?; @@ -126,7 +130,7 @@ pub struct AddIdentityConstrained { } impl Decode for AddIdentityConstrained { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let identity = AddIdentity::decode(reader)?; @@ -168,7 +172,7 @@ pub struct RemoveIdentity { } impl Decode for RemoveIdentity { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let pubkey = reader.read_prefixed(KeyData::decode)?; @@ -194,7 +198,7 @@ pub struct SmartcardKey { } impl Decode for SmartcardKey { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let id = String::decode(reader)?; @@ -225,7 +229,7 @@ pub enum KeyConstraint { } impl Decode for KeyConstraint { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let constraint_type = u8::decode(reader)?; @@ -239,7 +243,7 @@ impl Decode for KeyConstraint { reader.read(&mut details)?; KeyConstraint::Extension(name, details.into()) } - _ => return Err(Error::AlgorithmUnknown), // FIXME: it should be our own type + _ => return Err(Error::AlgorithmUnknown)?, // FIXME: it should be our own type }) } } @@ -282,7 +286,7 @@ pub struct AddSmartcardKeyConstrained { } impl Decode for AddSmartcardKeyConstrained { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let key = SmartcardKey::decode(reader)?; @@ -321,7 +325,7 @@ pub struct Extension { } impl Decode for Extension { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let name = String::decode(reader)?; @@ -425,7 +429,7 @@ impl Message { } impl Decode for Message { - type Error = Error; + type Error = ProtoError; fn decode(reader: &mut impl Reader) -> Result { let message_type = u8::decode(reader)?; @@ -436,7 +440,10 @@ impl Decode for Message { 11 => Ok(Self::RequestIdentities), 12 => Identity::decode_vec(reader).map(Self::IdentitiesAnswer), 13 => SignRequest::decode(reader).map(Self::SignRequest), - 14 => reader.read_prefixed(|reader| Signature::decode(reader).map(Self::SignResponse)), + 14 => { + Ok(reader + .read_prefixed(|reader| Signature::decode(reader).map(Self::SignResponse))?) + } 17 => AddIdentity::decode(reader).map(Self::AddIdentity), 18 => RemoveIdentity::decode(reader).map(Self::RemoveIdentity), 19 => Ok(Self::RemoveAllIdentities), @@ -448,7 +455,7 @@ impl Decode for Message { 26 => AddSmartcardKeyConstrained::decode(reader).map(Self::AddSmartcardKeyConstrained), 27 => Extension::decode(reader).map(Self::Extension), 28 => Ok(Self::ExtensionFailure), - _ => todo!(), + command => Err(ProtoError::UnsupportedCommand { command }), } } } From a1c0beb7c6dbfe7c57c952ffdaf344e214784d18 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Thu, 4 Apr 2024 10:31:32 -0700 Subject: [PATCH 25/25] rework KeyConstaint decoder Signed-off-by: Arthur Gautier --- src/proto/message.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/proto/message.rs b/src/proto/message.rs index 001e713..331696e 100644 --- a/src/proto/message.rs +++ b/src/proto/message.rs @@ -239,8 +239,7 @@ impl Decode for KeyConstraint { 2 => KeyConstraint::Confirm, 255 => { let name = String::decode(reader)?; - let mut details = vec![0; reader.remaining_len()]; - reader.read(&mut details)?; + let details: Vec = Vec::decode(reader)?; KeyConstraint::Extension(name, details.into()) } _ => return Err(Error::AlgorithmUnknown)?, // FIXME: it should be our own type @@ -560,7 +559,6 @@ mod tests { } #[test] - #[ignore = "temporarily disable until test vectors are updated"] fn test_add_identity_constrained() { let msg: &[u8] = &hex!( "