From 24449711e98b90b61e934bc3e4ca9a454a29e022 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 18 Jul 2021 10:56:48 +0800 Subject: [PATCH] feat: pgp decrypt --- .gitignore | 3 +++ README.md | 34 ++++++++++++++++++++++++ src/chall.rs | 4 +-- src/challconfig.rs | 4 +-- src/main.rs | 2 ++ src/pgp.rs | 2 +- src/pgpcarddecrypt.rs | 61 +++++++++++++++++++++++++++++++++++++++++++ src/pgpcardsign.rs | 8 +++--- src/pgpcardutil.rs | 17 ++++++++++++ src/register.rs | 4 +-- src/sign.rs | 6 ++--- 11 files changed, 131 insertions(+), 14 deletions(-) create mode 100644 src/pgpcarddecrypt.rs diff --git a/.gitignore b/.gitignore index 6c529b6..bd2996b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +test.txt +enc.txt +enc_key.pem test_key.asc .idea/ # ---> Rust diff --git a/README.md b/README.md index 3e8d19c..769bb2f 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,40 @@ A cli using https://github.com/mozilla/authenticator-rs + +# PGP + +## decrypt text + +sample public key +``` +-----BEGIN PUBLIC KEY----- +MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApUM8M+QRMUw0dIvXISFx +43j4h9CK38Y9HD6kPcc3Z0dCGPiFy7Ze0OQebPWHyUZ2YmqsdyzFuOQuV9P2pxxj +/WLIgRqZV8Jk8tWhtAjOOvm0MTc2rg+EJHfa+zhX4eFEMsj4DvQBMJDXiKnpXTM/ +j7oMKpIUQHqfXBwsEJHLmHZTLeEBEYKcZXTAmuu3WdxK5jvEc02Xt2hZ1fBs0M9e +/2EMe3t69aH4/rabiBjF2h9Jde15wrJMxXaCCWJqYhbBS0CJ3BdjkAqOIpcqPXva +xiJN1pNpK8ejA9Q4Nmx7pxnvfv+hCPkWXZS3r/BWZ9lFZc8uErQEbB4gLgko8jOl +fQF7cYqtZEs69qY8nnIUBsqZYfAp+bQd2xCFSbEZAl+OrtGzfVjD9YFMPy02+xRg +v2N3KT3KHHvuU7WxrvffrshP2fwDuG2MBlmcq1suAKxA0cYPSyajceEqw/3ogSp7 +7SYx41rT8EWLmTvU0CHzCsuf/O7sDWZRfxatAzWhBBhnKCPqzizpOQOqm8XhCt74 +FfnabPpHM9XUjoQIPrTssyS3eWqynzJiAqez6v2LK2fhL7IkcLtvt5p59Y+KY4I6 +YQ09iUh7lKJHRhkgTomUurJHieVHMWFGIHofEC+nU6pGIUh0P7Nr0Gz45GJTwWGd +hW53WfImja+b5kwwyqUikyMCAwEAAQ== +-----END PUBLIC KEY----- +``` + +encrypt +``` +openssl rsautl -encrypt -pubin -inkey enc_key.pem -in test.txt -out enc.txt -pkcs +``` + +decrypt +``` +$ cargo r -- pgp-card-decrypt -c $(cat enc.txt | xxd -ps -c 11111) +``` + + Awesome webauthn: * https://github.com/herrjemand/awesome-webauthn diff --git a/src/chall.rs b/src/chall.rs index e60e96e..832b30f 100644 --- a/src/chall.rs +++ b/src/chall.rs @@ -11,8 +11,8 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("Yubikey challenge-response hmac") - .arg(Arg::with_name("challenge").long("challenge").takes_value(true).help("Challenge")) - .arg(Arg::with_name("challenge-hex").long("challenge-hex").takes_value(true).help("Challenge HEX")) + .arg(Arg::with_name("challenge").short("c").long("challenge").takes_value(true).help("Challenge")) + .arg(Arg::with_name("challenge-hex").short("x").long("challenge-hex").takes_value(true).help("Challenge HEX")) .arg(Arg::with_name("json").long("json").help("JSON output")) } diff --git a/src/challconfig.rs b/src/challconfig.rs index 86fd6e4..bbae27e 100644 --- a/src/challconfig.rs +++ b/src/challconfig.rs @@ -12,8 +12,8 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("Yubikey challenge-response hmac config") - .arg(Arg::with_name("secret").long("secret").takes_value(true).help("Secret")) - .arg(Arg::with_name("secret-hex").long("secret-hex").takes_value(true).help("Secret HEX")) + .arg(Arg::with_name("secret").short("s").long("secret").takes_value(true).help("Secret")) + .arg(Arg::with_name("secret-hex").short("x").long("secret-hex").takes_value(true).help("Secret HEX")) .arg(Arg::with_name("yes-config-chall").long("yes-config-chall").help("Config chall key")) } diff --git a/src/main.rs b/src/main.rs index 9f8ec97..f64f55f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,7 @@ mod pgp; mod pgpcardutil; mod pgpcardlist; mod pgpcardsign; +mod pgpcarddecrypt; mod piv; mod chall; mod challconfig; @@ -32,6 +33,7 @@ fn inner_main() -> CommandError { Box::new(pgp::CommandImpl), Box::new(pgpcardlist::CommandImpl), Box::new(pgpcardsign::CommandImpl), + Box::new(pgpcarddecrypt::CommandImpl), Box::new(piv::CommandImpl), Box::new(chall::CommandImpl), Box::new(challconfig::CommandImpl), diff --git a/src/pgp.rs b/src/pgp.rs index 3310b57..9c75b27 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -18,7 +18,7 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("OpenPGP Card List subcommand") - .arg(Arg::with_name("in").long("in").takes_value(true).help("File input")) + .arg(Arg::with_name("in").short("i").long("in").takes_value(true).help("File input")) .arg(Arg::with_name("detail").long("detail").help("Detail output")) .arg(Arg::with_name("json").long("json").help("JSON output")) } diff --git a/src/pgpcarddecrypt.rs b/src/pgpcarddecrypt.rs new file mode 100644 index 0000000..ba7e109 --- /dev/null +++ b/src/pgpcarddecrypt.rs @@ -0,0 +1,61 @@ +use clap::{ArgMatches, SubCommand, App, Arg}; +use crate::cmd::{Command, CommandError}; +use openpgp_card::DecryptMe; +use std::collections::BTreeMap; + +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 List subcommand") + .arg(Arg::with_name("pass").short("p").long("pass").takes_value(true).default_value("123456").help("OpenPGP card password")) + .arg(Arg::with_name("cipher").short("c").long("cipher").takes_value(true).help("Cipher text HEX")) + .arg(Arg::with_name("cipher-base64").short("b").long("cipher-base64").takes_value(true).help("Cipher text base64")) + .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 pass = sub_arg_matches.value_of("pass"); + let pass = match pass { + Some(p) => p, + None => return simple_error!("Pass must be assigned"), + }; + let cipher = sub_arg_matches.value_of("cipher"); + let cipher_base64 = sub_arg_matches.value_of("cipher-base64"); + + let mut json = BTreeMap::<&'_ str, String>::new(); + + let cipher_bytes = if let Some(cipher) = cipher { + opt_result!(hex::decode(cipher), "Decode cipher failed: {}") + } else if let Some(cipher_base64) = cipher_base64 { + opt_result!(base64::decode(cipher_base64), "Decode cipher-base64 failed: {}") + } else { + return simple_error!("cipher or cipher-base64 must assign one"); + }; + + let user = crate::pgpcardutil::get_card_user_sw1_82(pass)?; + let text = user.decrypt(DecryptMe::RSA(&cipher_bytes))?; + success!("Clear text HEX: {}", hex::encode(&text)); + success!("Clear text base64: {}", base64::encode(&text)); + success!("Clear text UTF-8: {}", String::from_utf8_lossy(&text)); + if json_output { + json.insert("cipher_hex", hex::encode(&cipher_bytes)); + json.insert("cipher_base64", base64::encode(&cipher_bytes)); + json.insert("text_hex", hex::encode(&text)); + json.insert("text_utf8", String::from_utf8_lossy(&text).to_string()); + } + + + if json_output { + println!("{}", serde_json::to_string_pretty(&json).unwrap()); + } + + Ok(()) + } +} diff --git a/src/pgpcardsign.rs b/src/pgpcardsign.rs index 2c8721e..261e742 100644 --- a/src/pgpcardsign.rs +++ b/src/pgpcardsign.rs @@ -11,10 +11,10 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("OpenPGP Card List subcommand") - .arg(Arg::with_name("pass").long("pass").takes_value(true).default_value("123456").help("OpenPGP card password")) - .arg(Arg::with_name("sha256").long("sha256").takes_value(true).help("Digest SHA256")) - .arg(Arg::with_name("sha384").long("sha384").takes_value(true).help("Digest SHA384")) - .arg(Arg::with_name("sha512").long("sha512").takes_value(true).help("Digest SHA512")) + .arg(Arg::with_name("pass").short("p").long("pass").takes_value(true).default_value("123456").help("OpenPGP card password")) + .arg(Arg::with_name("sha256").short("2").long("sha256").takes_value(true).help("Digest SHA256 HEX")) + .arg(Arg::with_name("sha384").short("3").long("sha384").takes_value(true).help("Digest SHA384 HEX")) + .arg(Arg::with_name("sha512").short("5").long("sha512").takes_value(true).help("Digest SHA512 HEX")) .arg(Arg::with_name("json").long("json").help("JSON output")) } diff --git a/src/pgpcardutil.rs b/src/pgpcardutil.rs index 12cc831..4a98253 100644 --- a/src/pgpcardutil.rs +++ b/src/pgpcardutil.rs @@ -18,3 +18,20 @@ pub fn get_card_user_sw1_81(pass: &str) -> XResult { } } +pub fn get_card_user_sw1_82(pass: &str) -> XResult { + match OpenPGPCard::list_cards() { + Ok(list) => { + // pw1_82 for decrypt + // PKCSv1.5 + if list.is_empty() { + return simple_error!("Cannot find any card"); + } + match list.into_iter().next().unwrap().verify_pw1_82(pass) { + Result::Ok(user) => Ok(user), + Result::Err(_) => simple_error!("Verify pw1_82 OpenPGP card failed"), + } + } + Err(e) => simple_error!("Read OpenPGP card failed: {}", e), + } +} + diff --git a/src/register.rs b/src/register.rs index 5a3cbb4..6ecd062 100644 --- a/src/register.rs +++ b/src/register.rs @@ -15,8 +15,8 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("Register subcommand") - .arg(Arg::with_name("app-id").long("app-id").default_value("https://example.com").help("App id")) - .arg(Arg::with_name("timeout").long("timeout").default_value("10").help("Timeout in seconds")) + .arg(Arg::with_name("app-id").short("a").long("app-id").default_value("https://example.com").help("App id")) + .arg(Arg::with_name("timeout").short("t").long("timeout").default_value("10").help("Timeout in seconds")) .arg(Arg::with_name("json").long("json").help("JSON output")) } diff --git a/src/sign.rs b/src/sign.rs index 20ddbbe..dea6e9f 100644 --- a/src/sign.rs +++ b/src/sign.rs @@ -14,9 +14,9 @@ impl Command for CommandImpl { fn name(&self) -> &str { "sign" } fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("Sign subcommand") - .arg(Arg::with_name("app-id").long("app-id").default_value("https://example.com").help("App id")) - .arg(Arg::with_name("timeout").long("timeout").default_value("10").help("Timeout in seconds")) - .arg(Arg::with_name("key-handle").long("key-handle").takes_value(true).multiple(true).help("Key handle")) + .arg(Arg::with_name("app-id").short("a").long("app-id").default_value("https://example.com").help("App id")) + .arg(Arg::with_name("timeout").short("t").long("timeout").default_value("10").help("Timeout in seconds")) + .arg(Arg::with_name("key-handle").short("k").long("key-handle").takes_value(true).multiple(true).help("Key handle")) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { let app_id = sub_arg_matches.value_of("app-id").unwrap();