Files
card-cli/src/cmd_pgpcarddecrypt.rs

97 lines
3.9 KiB
Rust

use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use openpgp_card::crypto_data::Cryptogram;
use rust_util::{util_msg, XResult};
use rust_util::util_clap::{Command, CommandError};
use crate::{pgpcardutil, pinutil};
use crate::util::{base64_encode, read_stdin, try_decode};
#[derive(Debug, Clone, Copy)]
enum EncryptAlgo {
Rsa,
Ecdh,
}
impl EncryptAlgo {
fn from_str(algo: &str) -> XResult<Self> {
match algo {
"rsa" => Ok(Self::Rsa),
"x25519" | "ecdh" => Ok(Self::Ecdh),
_ => simple_error!("Unknown algo: {}", algo),
}
}
}
pub struct CommandImpl;
impl Command for CommandImpl {
fn name(&self) -> &str { "pgp-card-decrypt" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card decrypt subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("OpenPGP card user pin"))
.arg(Arg::with_name("pass").long("pass").takes_value(true).help("[deprecated] now OpenPGP card user pin"))
.arg(Arg::with_name("ciphertext").short("c").long("ciphertext").takes_value(true).help("Cipher text (HEX or Base64)"))
.arg(Arg::with_name("stdin").long("stdin").help("Standard input (Ciphertext)"))
.arg(Arg::with_name("algo").long("algo").takes_value(true).help("Algo: RSA, X25519/ECDH"))
.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 { util_msg::set_logger_std_out(false); }
let pin_opt = sub_arg_matches.value_of("pass").or_else(|| sub_arg_matches.value_of("pin"));
let pin_opt = pinutil::get_pin(pin_opt);
let pin_opt = pin_opt.as_deref();
let pin = opt_value_result!(pin_opt, "User pin must be assigned");
if pin.len() < 6 { return simple_error!("User pin length:{}, must >= 6!", pin.len()); }
let ciphertext = sub_arg_matches.value_of("ciphertext");
let algo = sub_arg_matches.value_of("algo").unwrap_or("rsa").to_lowercase();
let algo = EncryptAlgo::from_str(&algo)?;
let ciphertext_bytes = if let Some(ciphertext) = ciphertext {
opt_result!(try_decode(ciphertext), "Decode cipher failed: {}")
} else if sub_arg_matches.is_present("stdin") {
read_stdin()?
} else {
return simple_error!("--ciphertext must be assigned");
};
let mut pgp = pgpcardutil::get_openpgp_card()?;
let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}");
opt_result!(trans.verify_pw1_user(pin.as_ref()), "User pin verify failed: {}");
success!("User pin verify success!");
let text = match algo {
EncryptAlgo::Rsa => trans.decipher(Cryptogram::RSA(&ciphertext_bytes))?,
EncryptAlgo::Ecdh => trans.decipher(Cryptogram::ECDH(&ciphertext_bytes))?,
};
success!("Clear text HEX: {}", hex::encode(&text));
success!("Clear text base64: {}", base64_encode(&text));
let text_opt = String::from_utf8(text.clone()).ok();
if let Some(text) = &text_opt {
success!("Clear text UTF-8: {}", text);
}
if json_output {
let mut json = BTreeMap::<&'_ str, String>::new();
json.insert("cipher_hex", hex::encode(&ciphertext_bytes));
json.insert("cipher_base64", base64_encode(&ciphertext_bytes));
json.insert("text_hex", hex::encode(&text));
json.insert("text_base64", base64_encode(&text));
if let Some(text) = text_opt {
json.insert("text_utf8", text);
}
println!("{}", serde_json::to_string_pretty(&json).unwrap());
}
Ok(None)
}
}