From 74482ae91896c263a492d150ea25abde2154ad74 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Thu, 28 Apr 2022 00:24:39 +0800 Subject: [PATCH] feat: v1.3.0, ssh agent works --- Cargo.lock | 3 +- Cargo.toml | 3 +- src/cmd_sshagent.rs | 68 +++++++++++++++++++++++++++++++++++++-------- src/main.rs | 2 ++ 4 files changed, 63 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cab6091..d15a36b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,7 +384,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.2.1" +version = "1.3.0" dependencies = [ "authenticator", "base64 0.13.0", @@ -392,6 +392,7 @@ dependencies = [ "clap", "digest 0.10.3", "hex", + "lazy_static", "openpgp-card", "openpgp-card-pcsc", "openpgp-card-sequoia", diff --git a/Cargo.toml b/Cargo.toml index f0f01ac..8878248 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.2.1" +version = "1.3.0" authors = ["Hatter Jiang "] edition = "2018" @@ -31,5 +31,6 @@ yubico_manager = "0.9" x509 = "0.2" x509-parser = "0.13" ssh-agent = { version = "0.2.3", features = ["agent"] } +lazy_static = "1.4.0" #ssh-key = "0.4.0" #ctap-hid-fido2 = "2.1.3" diff --git a/src/cmd_sshagent.rs b/src/cmd_sshagent.rs index b64c4d0..bb88575 100644 --- a/src/cmd_sshagent.rs +++ b/src/cmd_sshagent.rs @@ -1,21 +1,28 @@ use std::error::Error; use std::fs::remove_file; +use std::path::PathBuf; +use std::sync::Mutex; use clap::{App, Arg, ArgMatches, SubCommand}; use openpgp_card::{KeyType, OpenPgp}; -use openpgp_card::crypto_data::PublicKeyMaterial; +use openpgp_card::crypto_data::{Hash, PublicKeyMaterial}; use openpgp_card_pcsc::PcscBackend; +use openssl::hash::MessageDigest; use rust_util::util_clap::{Command, CommandError}; use rust_util::XResult; use ssh_agent::Agent; -use ssh_agent::proto::{RsaPublicKey, to_bytes}; -use ssh_agent::proto::message::{self, Message, SignRequest}; +use ssh_agent::proto::{from_bytes, RsaPublicKey, signature, Signature, to_bytes}; +use ssh_agent::proto::message::{self, Message}; use ssh_agent::proto::public_key::PublicKey; +use crate::digest::{copy_sha256, copy_sha512}; use crate::sshutil::with_sign; +lazy_static! { + static ref CARD: Mutex> = Mutex::new(None); +} + struct SshAgent { - card: PcscBackend, public_key: PublicKey, comment: String, ssh_string: String, @@ -47,8 +54,11 @@ impl SshAgent { let comment = format!("pgp-card:{}", serial); (public_key, comment.clone(), crate::sshutil::generate_ssh_string(e, n, &comment)) }; + { + let mut card_mutex = CARD.lock().unwrap(); + *card_mutex = Some(card); + } Ok(Self { - card, public_key, comment, ssh_string, @@ -72,10 +82,41 @@ impl SshAgent { Message::AddIdentity(ref _identity) => { Err(From::from(format!("Not supported message: {:?}", request))) } - // Message::SignRequest(request) => { - // let signature = to_bytes(&self.sign(&request)?)?; - // Ok(Message::SignResponse(signature)) - // } + Message::SignRequest(sign_request) => { + let pubkey: PublicKey = from_bytes(&sign_request.pubkey_blob)?; + if self.public_key != pubkey { + return Err(From::from(format!("Unknown public key: {:?}", sign_request))); + } + debugging!("To be signed data: {:?}", &sign_request.data); + + let (algorithm, hash) = if sign_request.flags & signature::RSA_SHA2_512 != 0 { + let hash = opt_result!(openssl::hash::hash(MessageDigest::sha512(), &sign_request.data), "Calc digest failed: {}"); + ("rsa-sha2-512", Hash::SHA512(copy_sha512(&hash).unwrap())) + } else if sign_request.flags & signature::RSA_SHA2_256 != 0 { + let hash = opt_result!(openssl::hash::hash(MessageDigest::sha256(), &sign_request.data), "Calc digest failed: {}"); + ("rsa-sha2-256", Hash::SHA256(copy_sha256(&hash).unwrap())) + } else { + return Err(From::from(format!("Not supported sign flags: {:?}", sign_request.flags))); + }; + + information!("SSH request, algorithm: {}", algorithm); + let mut card_mutex = CARD.lock().unwrap(); + let card_mut = match card_mutex.as_mut() { + Some(card) => card, + None => return Err(From::from("Illegal card status: none")), + }; + let mut pgp = OpenPgp::new(card_mut); + let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}"); + opt_result!(trans.verify_pw1_sign("123456".as_bytes()), "User sign pin verify failed: {}"); + let sig = opt_result!(trans.signature_for_hash(hash), "Sign OpenPGP card failed: {}"); + + debugging!("Signature: {:?}", sig); + success!("SSH request sign success"); + Ok(Message::SignResponse(to_bytes(&Signature { + algorithm: algorithm.to_string(), + blob: sig, + })?)) + } _ => Err(From::from(format!("Unknown message: {:?}", request))) }; debugging!("Response {:?}", response); @@ -101,8 +142,7 @@ impl Command for CommandImpl { 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("pin").short("p").long("pin").default_value("123456").help("OpenPGP card user pin")) .arg(Arg::with_name("pgp").long("pgp").help("Use PGP")) .arg(Arg::with_name("piv").long("piv").help("Use PIV")) .arg(Arg::with_name("sock-file").long("sock-file").default_value("connect.ssh").help("Sock file, usage SSH_AUTH_SOCK=sock-file ssh ...")) @@ -118,6 +158,12 @@ impl Command for CommandImpl { let sock_file = sub_arg_matches.value_of("sock-file").unwrap(); information!("Sock file: {}", sock_file); + let sock_file_path = PathBuf::from(sock_file); + match std::fs::canonicalize(sock_file_path) { + Ok(canonicalized_sock_file_path) => information!("SSH_AUTH_SOCK={}", canonicalized_sock_file_path.to_str().unwrap_or_else(|| "-")), + Err(e) => warning!("Get canonicalized sock file path failed: {}", e), + } + if use_pgp { let ssh_agent = SshAgent::new()?; information!("{}", &ssh_agent.ssh_string); diff --git a/src/main.rs b/src/main.rs index da415de..ae29f35 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ #[macro_use] extern crate rust_util; +#[macro_use] +extern crate lazy_static; use clap::{App, AppSettings, ArgMatches}; use rust_util::util_clap::{Command, CommandError};