From d7ae18526452717fd7d00e1a827eb3b73606c2d8 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Tue, 26 Apr 2022 00:44:34 +0800 Subject: [PATCH] feat: add ssh agnet(not yet work) --- src/cmd_sshagent.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 3 +++ src/sshutil.rs | 20 ++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 src/cmd_sshagent.rs create mode 100644 src/sshutil.rs diff --git a/src/cmd_sshagent.rs b/src/cmd_sshagent.rs new file mode 100644 index 0000000..b98fda3 --- /dev/null +++ b/src/cmd_sshagent.rs @@ -0,0 +1,50 @@ +use clap::{App, Arg, ArgMatches, SubCommand}; +use openpgp_card::{KeyType, OpenPgp}; +use openpgp_card::crypto_data::PublicKeyMaterial; +use rust_util::util_clap::{Command, CommandError}; + +pub struct CommandImpl; + +impl Command for CommandImpl { + fn name(&self) -> &str { "ssh-agent" } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("SSH-Agent subcommand") + // .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).default_value("123456").help("OpenPGP card user pin")) + // .arg(Arg::with_name("in").short("i").long("in").takes_value(true).help("File in")) + .arg(Arg::with_name("pgp").long("pgp").help("Use PGP")) + .arg(Arg::with_name("sock-file").long("sock-file").default_value("connect.ssh").help("Sock file, usage SSH_AUTH_SOCK=sock-file ssh ...")) + } + + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let use_pgp = sub_arg_matches.is_present("pgp"); + let sock_file = sub_arg_matches.value_of("sock-file").unwrap(); + information!("Sock file: {}", sock_file); + + if use_pgp { + let mut card = crate::pgpcardutil::get_card()?; + let mut pgp = OpenPgp::new(&mut card); + let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}"); + let serial = trans.application_related_data() + .map(|d| d.application_id().map(|i| i.serial())) + .unwrap_or_else(|_| Ok(0)).unwrap_or_else(|_| 0); + let serial = hex::encode(serial.to_be_bytes()); + let public_key = opt_result!(trans.public_key(KeyType::Signing), "Cannot find signing key: {}"); + let rsa_public_key = match public_key { + PublicKeyMaterial::E(_) => return simple_error!("Not supports ec key"), + PublicKeyMaterial::R(rsa_public_key) => rsa_public_key, + _ => return simple_error!("Unknown key type"), + }; + let e = rsa_public_key.v(); + let n = rsa_public_key.n(); + + let comment = format!("pgp-card:{}", serial); + let ssh_string = crate::sshutil::generate_ssh_string(e, n, &comment); + information!("{}", ssh_string); + } + + information!("card-cli ssh-agent..."); + + Ok(None) + } +} diff --git a/src/main.rs b/src/main.rs index 658e7f7..da415de 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,7 @@ extern crate rust_util; use clap::{App, AppSettings, ArgMatches}; use rust_util::util_clap::{Command, CommandError}; +mod sshutil; mod fido; mod digest; mod rsautil; @@ -26,6 +27,7 @@ mod cmd_pivdecrypt; mod cmd_pivgenerate; mod cmd_chall; mod cmd_challconfig; +mod cmd_sshagent; pub struct DefaultCommandImpl; @@ -64,6 +66,7 @@ fn inner_main() -> CommandError { Box::new(cmd_pivgenerate::CommandImpl), Box::new(cmd_u2fregister::CommandImpl), Box::new(cmd_u2fsign::CommandImpl), + Box::new(cmd_sshagent::CommandImpl), ]; let mut app = App::new(env!("CARGO_PKG_NAME")) .version(env!("CARGO_PKG_VERSION")) diff --git a/src/sshutil.rs b/src/sshutil.rs new file mode 100644 index 0000000..149adb0 --- /dev/null +++ b/src/sshutil.rs @@ -0,0 +1,20 @@ +pub fn with_sign(mut vec: Vec) -> Vec { + if vec.len() > 0 && vec[0] >= 128 { + vec.insert(0, 0x00); + } + vec +} + +pub fn generate_ssh_string(e: &[u8], n: &[u8], comment: &str) -> String { + let mut ssh_key = vec![]; + let ssh_rsa_bytes = "ssh-rsa".as_bytes(); + ssh_key.extend_from_slice(&(ssh_rsa_bytes.len() as u32).to_be_bytes()[..]); + ssh_key.extend_from_slice(ssh_rsa_bytes); + let e = with_sign(e.to_vec()); + ssh_key.extend_from_slice(&(e.len() as u32).to_be_bytes()[..]); + ssh_key.extend_from_slice(&e); + let n = with_sign(n.to_vec()); + ssh_key.extend_from_slice(&(n.len() as u32).to_be_bytes()[..]); + ssh_key.extend_from_slice(&n); + format!("ssh-rsa {} {}", base64::encode(&ssh_key), comment) +} \ No newline at end of file