From 27dca7af84e81c5e95c6fba804052a47bf78a403 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sat, 9 Apr 2022 17:54:13 +0800 Subject: [PATCH] feat: v1.1.9, add rsa-verify sub command --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cmd_rsaverify.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 2 ++ 4 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 src/cmd_rsaverify.rs diff --git a/Cargo.lock b/Cargo.lock index 61ca983..869539f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,7 +384,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.1.8" +version = "1.1.9" dependencies = [ "authenticator", "base64 0.13.0", diff --git a/Cargo.toml b/Cargo.toml index 5daa79f..4ecf263 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.1.8" +version = "1.1.9" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_rsaverify.rs b/src/cmd_rsaverify.rs new file mode 100644 index 0000000..167bfcf --- /dev/null +++ b/src/cmd_rsaverify.rs @@ -0,0 +1,83 @@ +use std::fs::File; + +use clap::{App, Arg, ArgMatches, SubCommand}; +use openssl::hash::MessageDigest; +use openssl::pkey::PKey; +use openssl::rsa::Rsa; +use openssl::sign::Verifier; +use rust_util::util_clap::{Command, CommandError}; +use rust_util::XResult; + +use crate::digest::sha256_bytes; + +pub struct CommandImpl; + +// https://docs.rs/openssl/0.10.36/openssl/encrypt/index.html +impl Command for CommandImpl { + fn name(&self) -> &str { "rsa-verify" } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()).about("RSA Verify subcommand") + .arg(Arg::with_name("pub-key-in").long("pub-key-in").takes_value(true).help("Public key in")) + .arg(Arg::with_name("signature").long("signature").takes_value(true).help("Signature HEX")) + .arg(Arg::with_name("in").short("i").long("in").takes_value(true).help("File in")) + .arg(Arg::with_name("hash").long("hash").takes_value(true).possible_values(&[ + "sha256", "sha384", "sha512" + ]).default_value("sha256").help("Hash")) + // .arg(Arg::with_name("json").long("json").help("JSON output")) + } + + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + // let json_output = sub_arg_matches.is_present("json"); + // if json_output { rust_util::util_msg::set_logger_std_out(false); } + + let pub_key_in = opt_value_result!(sub_arg_matches.value_of("pub-key-in"), "Require public key in"); + let pub_key_bytes = opt_result!(std::fs::read(pub_key_in), "Read file: {}, failed: {}", pub_key_in); + + // let mut json = BTreeMap::new(); + + let keypair = opt_result!(Rsa::public_key_from_pem(&pub_key_bytes), "Parse RSA failed: {}"); + let pub_key_der = opt_result!(keypair.public_key_to_der(), "RSA public key to der failed: {}"); + let pub_key_fingerprint = hex::encode(sha256_bytes(&pub_key_der)); + let keypair = opt_result!(PKey::from_rsa(keypair), "RSA to PKey failed: {}"); + + let signature = if let Some(signature) = sub_arg_matches.value_of("signature") { + opt_result!(hex::decode(signature), "Decode signature HEX failed: {}") + } else { + return simple_error!("Signature is required, --signature argument!"); + }; + + let file_in = opt_value_result!(sub_arg_matches.value_of("in"), "File in --in required"); + information!("File in: {}", file_in); + information!("Public key fingerprint: {}", pub_key_fingerprint); + let hashes = sub_arg_matches.values_of("hash").expect("Cannot get hashes"); + for hash in hashes { + information!("Hash: {}", hash); + let digest = get_digest(hash)?; + let mut verifier = opt_result!(Verifier::new(digest, &keypair), "Verifier new failed: {}"); + let mut f = opt_result!(File::open(file_in), "Open file: {}, failed: {}", file_in); + opt_result!(std::io::copy(&mut f, &mut verifier), "Verifier failed: {}"); + let result = opt_result!(verifier.verify(&signature), "Verifier verify failed: {}"); + if result { + success!("Verify success"); + } else { + failure!("Verify failed") + } + } + + // if json_output { + // println!("{}", serde_json::to_string_pretty(&json).unwrap()); + // } + + Ok(None) + } +} + +fn get_digest(hash: &str) -> XResult { + Ok(match hash { + "sha256" => MessageDigest::sha256(), + "sha384" => MessageDigest::sha384(), + "sha512" => MessageDigest::sha512(), + _ => return simple_error!("Unknown hash: {}", hash), + }) +} diff --git a/src/main.rs b/src/main.rs index 31b14a0..8a823ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ mod cmd_u2fregister; mod cmd_u2fsign; mod cmd_rsaencrypt; mod cmd_rsadecrypt; +mod cmd_rsaverify; mod cmd_pgp; mod cmd_pgpcardadmin; mod cmd_pgpcardlist; @@ -48,6 +49,7 @@ fn inner_main() -> CommandError { Box::new(cmd_challconfig::CommandImpl), Box::new(cmd_rsaencrypt::CommandImpl), Box::new(cmd_rsadecrypt::CommandImpl), + Box::new(cmd_rsaverify::CommandImpl), Box::new(cmd_pgp::CommandImpl), Box::new(cmd_pgpcardadmin::CommandImpl), Box::new(cmd_pgpcardlist::CommandImpl),