diff --git a/Cargo.lock b/Cargo.lock index 7f55430..9907af3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,12 +106,33 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bitflags" version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -139,12 +160,114 @@ 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "der" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.16.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]] name = "env_filter" version = "0.1.0" @@ -168,6 +291,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 = "foreign-types" version = "0.3.2" @@ -272,24 +405,90 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" 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 = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "humantime" 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "log" version = "0.4.20" @@ -322,6 +521,53 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", + "libm", +] + [[package]] name = "object" version = "0.32.2" @@ -375,6 +621,53 @@ dependencies = [ "vcpkg", ] +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -387,12 +680,48 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.78" @@ -411,6 +740,35 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "regex" version = "1.10.3" @@ -440,6 +798,37 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "sha2", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -447,23 +836,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "serde" -version = "1.0.196" +name = "sec1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" dependencies = [ - "serde_derive", + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", ] [[package]] -name = "serde_derive" -version = "1.0.196" +name = "sha2" +version = "0.10.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" dependencies = [ - "proc-macro2", - "quote", - "syn", + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", ] [[package]] @@ -475,6 +879,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + [[package]] name = "socket2" version = "0.5.5" @@ -485,6 +895,22 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "ssh-agent-lib" version = "0.3.0-pre" @@ -496,11 +922,61 @@ dependencies = [ "futures", "log", "openssl", - "serde", + "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.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01f8f4ea73476c0aa5d5e6a75ce1e8634e2c3f82005ef3bbed21547ac57f2bf7" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "syn" version = "2.0.48" @@ -571,6 +1047,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -589,6 +1071,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -726,3 +1214,9 @@ name = "windows_x86_64_msvc" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/Cargo.toml b/Cargo.toml index e8c4d68..f304a7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,11 @@ authors = [ repository = "https://github.com/wiktor-k/ssh-agent.rs" edition = "2021" +[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 } @@ -21,6 +23,9 @@ futures = { version = "0.3.30", optional = true } 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"] } +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 17b0962..b05bad7 100644 --- a/examples/key_storage.rs +++ b/examples/key_storage.rs @@ -4,10 +4,12 @@ use tokio::net::UnixListener; use ssh_agent_lib::agent::Agent; use ssh_agent_lib::proto::message::{self, Message, SignRequest}; -use ssh_agent_lib::proto::private_key::{PrivateKey, RsaPrivateKey}; -use ssh_agent_lib::proto::public_key::PublicKey; -use ssh_agent_lib::proto::signature::{self, Signature}; -use ssh_agent_lib::proto::{from_bytes, to_bytes}; +use ssh_agent_lib::proto::signature::{self}; +use ssh_key::{ + private::{KeypairData, PrivateKey, RsaKeypair}, + public::PublicKey, + Algorithm, HashAlg, Signature, +}; use std::error::Error; use std::fs::remove_file; @@ -73,22 +75,26 @@ 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 digest; if sign_request.flags & signature::RSA_SHA2_512 != 0 { - algorithm = "rsa-sha2-512"; + algorithm = Algorithm::Rsa { + hash: Some(HashAlg::Sha512), + }; digest = MessageDigest::sha512(); } else if sign_request.flags & signature::RSA_SHA2_256 != 0 { - algorithm = "rsa-sha2-256"; + algorithm = Algorithm::Rsa { + hash: Some(HashAlg::Sha256), + }; digest = MessageDigest::sha256(); } else { - algorithm = "ssh-rsa"; + algorithm = Algorithm::Rsa { hash: None }; digest = MessageDigest::sha1(); } @@ -96,10 +102,7 @@ impl KeyStorage { let mut signer = Signer::new(digest, &keypair)?; signer.update(&sign_request.data)?; - Ok(Signature { - algorithm: algorithm.to_string(), - blob: signer.sign_to_vec()?, - }) + Ok(Signature::new(algorithm, signer.sign_to_vec()?).unwrap()) } _ => Err(From::from("Signature for key type not implemented")), } @@ -115,27 +118,30 @@ impl KeyStorage { let mut identities = vec![]; for identity in self.identities.read().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))), @@ -157,13 +163,13 @@ impl Agent for KeyStorage { } } -fn rsa_openssl_from_ssh(ssh_rsa: &RsaPrivateKey) -> Result, Box> { - let n = BigNum::from_slice(&ssh_rsa.n)?; - let e = BigNum::from_slice(&ssh_rsa.e)?; - let d = BigNum::from_slice(&ssh_rsa.d)?; - let qi = BigNum::from_slice(&ssh_rsa.iqmp)?; - let p = BigNum::from_slice(&ssh_rsa.p)?; - let q = BigNum::from_slice(&ssh_rsa.q)?; +fn rsa_openssl_from_ssh(ssh_rsa: &RsaKeypair) -> Result, Box> { + let n = BigNum::from_slice(&ssh_rsa.public.n.as_bytes())?; + let e = BigNum::from_slice(&ssh_rsa.public.e.as_bytes())?; + let d = BigNum::from_slice(&ssh_rsa.private.d.as_bytes())?; + let qi = BigNum::from_slice(&ssh_rsa.private.iqmp.as_bytes())?; + let p = BigNum::from_slice(&ssh_rsa.private.p.as_bytes())?; + let q = BigNum::from_slice(&ssh_rsa.private.q.as_bytes())?; let dp = &d % &(&p - &BigNum::from_u32(1)?); let dq = &d % &(&q - &BigNum::from_u32(1)?); diff --git a/src/agent.rs b/src/agent.rs index 0244866..a6db2b3 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}; use tokio::net::{TcpListener, TcpStream, UnixListener, UnixStream}; use tokio_util::codec::{Decoder, Encoder, Framed}; @@ -16,7 +17,6 @@ use std::sync::Arc; use super::error::AgentError; use super::proto::message::Message; -use super::proto::{from_bytes, to_bytes}; struct MessageCodec; @@ -37,7 +37,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)) } @@ -47,7 +47,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 4d0c313..f8db530 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,19 +1,25 @@ -use super::proto::error::ProtoError; use std::io; #[derive(Debug)] pub enum AgentError { User, - 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) 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() {