feat: ssh agent, not works yet

This commit is contained in:
2025-05-25 22:39:02 +08:00
parent ecf034376d
commit f870c07387
3 changed files with 235 additions and 88 deletions

186
Cargo.lock generated
View File

@@ -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]]

View File

@@ -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"

View File

@@ -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<Self> {
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<Message, Box<dyn Error>> {
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<Vec<Identity>, 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<Message, ()> {
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<Signature, AgentError> {
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)
}