diff --git a/Cargo.lock b/Cargo.lock index 1c6ae2d..403cd17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -191,6 +191,17 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "async-trait" +version = "0.1.88" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +dependencies = [ + "proc-macro2", + "quote 1.0.40", + "syn 2.0.101", +] + [[package]] name = "atty" version = "0.2.14" @@ -550,9 +561,12 @@ dependencies = [ "simpledateformat", "spki 0.7.3", "ssh-agent", + "ssh-agent-lib", + "ssh-key", "sshcerts", "swift-secure-enclave-tool-rs", "tabled", + "tokio 1.45.1", "u2f-hatter-fork", "which 7.0.3", "x509", @@ -825,6 +839,32 @@ dependencies = [ "cipher 0.3.0", ] +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version 0.4.1", + "subtle", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote 1.0.40", + "syn 2.0.101", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -1030,6 +1070,27 @@ dependencies = [ "spki 0.7.3", ] +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "signature 2.2.0", +] + +[[package]] +name = "ed25519-dalek" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" +dependencies = [ + "curve25519-dalek", + "ed25519", + "sha2 0.10.9", + "subtle", +] + [[package]] name = "either" version = "1.15.0" @@ -1174,6 +1235,12 @@ dependencies = [ "subtle", ] +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -1457,7 +1524,7 @@ dependencies = [ "http", "indexmap", "slab", - "tokio 1.45.0", + "tokio 1.45.1", "tokio-util", "tracing", ] @@ -1608,7 +1675,7 @@ dependencies = [ "itoa", "pin-project-lite", "socket2", - "tokio 1.45.0", + "tokio 1.45.1", "tower-service", "tracing", "want", @@ -1623,7 +1690,7 @@ dependencies = [ "bytes 1.10.1", "hyper", "native-tls", - "tokio 1.45.0", + "tokio 1.45.1", "tokio-native-tls", ] @@ -2531,7 +2598,7 @@ checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252" dependencies = [ "lock_api 0.3.4", "parking_lot_core 0.6.3", - "rustc_version", + "rustc_version 0.2.3", ] [[package]] @@ -2554,7 +2621,7 @@ dependencies = [ "cloudabi", "libc", "redox_syscall 0.1.57", - "rustc_version", + "rustc_version 0.2.3", "smallvec 0.6.14", "winapi 0.3.9", ] @@ -2947,6 +3014,15 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "raunch" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a67226789ccd419c55f5c826e04f7d58690b0593364f61bc8ed6e7dbbab49c5" +dependencies = [ + "libc", +] + [[package]] name = "rcgen" version = "0.11.3" @@ -3038,7 +3114,7 @@ dependencies = [ "serde_urlencoded", "sync_wrapper", "system-configuration", - "tokio 1.45.0", + "tokio 1.45.1", "tokio-native-tls", "tower-service", "url", @@ -3228,7 +3304,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.26", ] [[package]] @@ -3403,6 +3488,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + [[package]] name = "semver-parser" version = "0.7.0" @@ -3511,6 +3602,15 @@ dependencies = [ "serde", ] +[[package]] +name = "service-binding" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25663b9d8b83193ef83183edb3fd01e5447adb3e5baa35466ff9a6cd2408d6e8" +dependencies = [ + "raunch", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -3704,6 +3804,70 @@ dependencies = [ "tokio-uds", ] +[[package]] +name = "ssh-agent-lib" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf67cca8055cc41de60ac7344a98b9c8f9e2a9ee9b1adf3e142b78ec1e7abba" +dependencies = [ + "async-trait", + "byteorder", + "futures 0.3.31", + "log", + "secrecy", + "service-binding", + "signature 2.2.0", + "ssh-encoding", + "ssh-key", + "subtle", + "thiserror", + "tokio 1.45.1", + "tokio-util", +] + +[[package]] +name = "ssh-cipher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caac132742f0d33c3af65bfcde7f6aa8f62f0e991d80db99149eb9d44708784f" +dependencies = [ + "cipher 0.4.4", + "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 0.7.0", + "sha2 0.10.9", +] + +[[package]] +name = "ssh-key" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b86f5297f0f04d08cabaa0f6bff7cb6aec4d9c3b49d87990d63da9d9156a8c3" +dependencies = [ + "ed25519-dalek", + "num-bigint-dig", + "p256 0.13.2", + "p384 0.13.1", + "p521", + "rand_core 0.6.4", + "rsa 0.9.8", + "sec1 0.7.3", + "sha2 0.10.9", + "signature 2.2.0", + "ssh-cipher", + "ssh-encoding", + "subtle", + "zeroize", +] + [[package]] name = "sshcerts" version = "0.13.2" @@ -4080,9 +4244,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.45.0" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes 1.10.1", @@ -4153,7 +4317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", - "tokio 1.45.0", + "tokio 1.45.1", ] [[package]] @@ -4271,7 +4435,7 @@ dependencies = [ "futures-core", "futures-sink", "pin-project-lite", - "tokio 1.45.0", + "tokio 1.45.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 3939c05..baba2e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,8 @@ rsa = "0.9.8" which = "7.0.3" percent-encoding = "2.3.1" external-command-rs = "0.1.1" +ssh-agent-lib = { version = "0.5.1" } +ssh-key = { version = "0.6", features = ["ecdsa"] } +tokio = "1.45.1" #lazy_static = "1.4.0" -#ssh-key = "0.4.0" #ctap-hid-fido2 = "2.1.3" diff --git a/src/cmd_ssh_agent.rs b/src/cmd_ssh_agent.rs index 7d27e6e..ad9525e 100644 --- a/src/cmd_ssh_agent.rs +++ b/src/cmd_ssh_agent.rs @@ -1,91 +1,57 @@ -use std::error::Error; use std::fs::remove_file; use std::path::PathBuf; use clap::{App, Arg, ArgMatches, SubCommand}; -use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg::when_debug; -use rust_util::XResult; -use ssh_agent::proto::message::{self, Message}; -use ssh_agent::proto::public_key::PublicKey; -use ssh_agent::proto::{from_bytes, to_bytes, EcDsaPublicKey}; -use ssh_agent::Agent; - use crate::ecdsautil::{generate_ecdsa_keypair, EcdsaAlgorithm}; +use rust_util::util_clap::{Command, CommandError}; +use rust_util::XResult; +use ssh_agent_lib::agent::{listen, Session}; +use ssh_agent_lib::error::AgentError; +use ssh_agent_lib::proto::{Identity, SignRequest}; +use ssh_agent_lib::ssh_key::public::KeyData; +use ssh_agent_lib::ssh_key::{Algorithm, Signature}; +use tokio::net::UnixListener as Listener; -use crate::ecdsautil; - -struct SshAgent { +#[derive(Default, Clone)] +struct MySshAgent { private_key_pem: String, comment: String, } -impl SshAgent { +impl MySshAgent { fn new() -> XResult { let (_, private_key_pem, _, _, _) = generate_ecdsa_keypair(EcdsaAlgorithm::P256)?; - Ok(SshAgent { + Ok(MySshAgent { private_key_pem, comment: "test".to_string(), }) } - - fn handle_message(&self, request: Message) -> Result> { - debugging!("Request: {:?}", request); - let response = match request { - Message::RequestIdentities => { - let p256_private_key_d = - ecdsautil::parse_p256_private_key(&self.private_key_pem).unwrap(); - - let ec_dsa_public_key = EcDsaPublicKey { - identifier: "nistp256".to_string(), - q: p256_private_key_d, - }; - let public_key = PublicKey::EcDsa(ec_dsa_public_key); - let identities = vec![message::Identity { - pubkey_blob: to_bytes(&public_key)?, - comment: self.comment.clone(), - }]; - when_debug(|| { - debugging!("Return {} identities", identities.len()); - for (i, identity) in identities.iter().enumerate() { - debugging!("#{} - {:?}", i, identity) - } - }); - Ok(Message::IdentitiesAnswer(identities)) - } - Message::RemoveIdentity(ref _identity) => { - Err(From::from(format!("Not supported message: {:?}", request))) - } - Message::AddIdentity(ref _identity) => { - Err(From::from(format!("Not supported message: {:?}", request))) - } - Message::SignRequest(sign_request) => { - let pubkey: PublicKey = from_bytes(&sign_request.pubkey_blob)?; - - println!("{:?}", pubkey); - - Err(From::from(format!( - "Not supported message: {:?}", - sign_request - ))) - } - _ => Err(From::from(format!("Unknown message: {:?}", request))), - }; - debugging!("Response {:?}", response); - response - } } -impl Agent for SshAgent { - type Error = (); +#[ssh_agent_lib::async_trait] +impl Session for MySshAgent { + async fn request_identities(&mut self) -> Result, AgentError> { + debugging!("request_identities"); + // let p256_private_key_d = ecdsautil::parse_p256_private_key(&self.private_key_pem).unwrap(); + let public_key_point = hex::decode("0474b7b8dcac7587afc8c461e96d713d05a4caae9dc4188924697fcb8dec2b8001d337e9ff4da1fb30042fef53375bde0cbe4964c71298b9d56bd9131c347119f3").unwrap(); + Ok(vec![Identity { + pubkey: KeyData::Ecdsa( + ssh_key::public::EcdsaPublicKey::from_sec1_bytes(&public_key_point).unwrap(), + ), + comment: "".to_string(), + }]) + } - fn handle(&self, message: Message) -> Result { - debugging!("Message: {:?}", message); - self.handle_message(message).or_else(|error| { - warning!("Error handling message - {:?}", error); - Ok(Message::Failure) - }) + async fn sign(&mut self, request: SignRequest) -> Result { + debugging!("sign, request: {:?}", request); + // get the signature by signing `request.data` + let signature = vec![]; + Ok(Signature::new( + Algorithm::new("algorithm").map_err(AgentError::other)?, + signature, + ) + .map_err(AgentError::other)?) } } @@ -108,7 +74,7 @@ impl Command for CommandImpl { } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - failure!("Not works!"); + warning!("Not works yet."); debugging!("Sub args: {:?}", sub_arg_matches); @@ -125,17 +91,32 @@ impl Command for CommandImpl { Err(e) => warning!("Get canonicalized sock file path failed: {}", e), } - let ssh_agent = SshAgent::new()?; - // TODO information!("{}", &ssh_agent.ssh_string); + // let ssh_agent = SshAgent::new()?; + // // TODO information!("{}", &ssh_agent.ssh_string); let _ = remove_file(sock_file); information!("Start unix socket: {}", sock_file); - opt_result!( - ssh_agent.run_unix(sock_file), - "Run unix socket: {}, failed: {}", - sock_file - ); + + let rt = tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap(); + + rt.block_on(async move { + listen( + Listener::bind(sock_file).unwrap(), + MySshAgent::new().unwrap(), + ) + .await + .unwrap(); + }); + + // opt_result!( + // ssh_agent.run_unix(sock_file), + // "Run unix socket: {}, failed: {}", + // sock_file + // ); Ok(None) }