feat: pgp decrypt

This commit is contained in:
2021-07-18 10:56:48 +08:00
parent 1d1e0ada87
commit 24449711e9
11 changed files with 131 additions and 14 deletions

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
test.txt
enc.txt
enc_key.pem
test_key.asc test_key.asc
.idea/ .idea/
# ---> Rust # ---> Rust

View File

@@ -2,6 +2,40 @@
A cli using https://github.com/mozilla/authenticator-rs 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: Awesome webauthn:
* https://github.com/herrjemand/awesome-webauthn * https://github.com/herrjemand/awesome-webauthn

View File

@@ -11,8 +11,8 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> { fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("Yubikey challenge-response hmac") 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").short("c").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-hex").short("x").long("challenge-hex").takes_value(true).help("Challenge HEX"))
.arg(Arg::with_name("json").long("json").help("JSON output")) .arg(Arg::with_name("json").long("json").help("JSON output"))
} }

View File

@@ -12,8 +12,8 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> { fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("Yubikey challenge-response hmac config") 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").short("s").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-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")) .arg(Arg::with_name("yes-config-chall").long("yes-config-chall").help("Config chall key"))
} }

View File

@@ -10,6 +10,7 @@ mod pgp;
mod pgpcardutil; mod pgpcardutil;
mod pgpcardlist; mod pgpcardlist;
mod pgpcardsign; mod pgpcardsign;
mod pgpcarddecrypt;
mod piv; mod piv;
mod chall; mod chall;
mod challconfig; mod challconfig;
@@ -32,6 +33,7 @@ fn inner_main() -> CommandError {
Box::new(pgp::CommandImpl), Box::new(pgp::CommandImpl),
Box::new(pgpcardlist::CommandImpl), Box::new(pgpcardlist::CommandImpl),
Box::new(pgpcardsign::CommandImpl), Box::new(pgpcardsign::CommandImpl),
Box::new(pgpcarddecrypt::CommandImpl),
Box::new(piv::CommandImpl), Box::new(piv::CommandImpl),
Box::new(chall::CommandImpl), Box::new(chall::CommandImpl),
Box::new(challconfig::CommandImpl), Box::new(challconfig::CommandImpl),

View File

@@ -18,7 +18,7 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> { fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card List subcommand") 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("detail").long("detail").help("Detail output"))
.arg(Arg::with_name("json").long("json").help("JSON output")) .arg(Arg::with_name("json").long("json").help("JSON output"))
} }

61
src/pgpcarddecrypt.rs Normal file
View File

@@ -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(())
}
}

View File

@@ -11,10 +11,10 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> { fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card List subcommand") 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("pass").short("p").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("sha256").short("2").long("sha256").takes_value(true).help("Digest SHA256 HEX"))
.arg(Arg::with_name("sha384").long("sha384").takes_value(true).help("Digest SHA384")) .arg(Arg::with_name("sha384").short("3").long("sha384").takes_value(true).help("Digest SHA384 HEX"))
.arg(Arg::with_name("sha512").long("sha512").takes_value(true).help("Digest SHA512")) .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")) .arg(Arg::with_name("json").long("json").help("JSON output"))
} }

View File

@@ -18,3 +18,20 @@ pub fn get_card_user_sw1_81(pass: &str) -> XResult<OpenPGPCardUser> {
} }
} }
pub fn get_card_user_sw1_82(pass: &str) -> XResult<OpenPGPCardUser> {
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),
}
}

View File

@@ -15,8 +15,8 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> { fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("Register subcommand") 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("app-id").short("a").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("timeout").short("t").long("timeout").default_value("10").help("Timeout in seconds"))
.arg(Arg::with_name("json").long("json").help("JSON output")) .arg(Arg::with_name("json").long("json").help("JSON output"))
} }

View File

@@ -14,9 +14,9 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "sign" } fn name(&self) -> &str { "sign" }
fn subcommand<'a>(&self) -> App<'a, 'a> { fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("Sign subcommand") 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("app-id").short("a").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("timeout").short("t").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("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 { fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let app_id = sub_arg_matches.value_of("app-id").unwrap(); let app_id = sub_arg_matches.value_of("app-id").unwrap();