From e60f49180145c8d82edf0c03466d77ec58f1b2a6 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Tue, 26 Dec 2023 23:31:42 +0800 Subject: [PATCH] feat: add gpg command encrypt/decrypt support --- src/lib.rs | 1 + src/util_gpg.rs | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/util_gpg.rs diff --git a/src/lib.rs b/src/lib.rs index d5aa324..18d13ab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,7 @@ mod util_progress; mod util_piv; #[cfg(feature = "smartcard")] mod util_pgp; +mod util_gpg; mod util_ecdh; mod compress; mod config; diff --git a/src/util_gpg.rs b/src/util_gpg.rs new file mode 100644 index 0000000..3df7a84 --- /dev/null +++ b/src/util_gpg.rs @@ -0,0 +1,94 @@ +// Command encrypt use GnuPG(GPG) +/* +echo message | gpg -r KEY_ID -e -a --no-comment --comment "tiny-encrypt-1.6.0 - KEY_ID" + +Success message: +-----BEGIN PGP MESSAGE----- +Comment: tiny-encrypt-1.6.0 - C0FAD5E563B80E819603B0D9FFC2A910806894FD + +hF4DcCBclRkzzAMSAQdA6nLd40IPxZF62Q54t2bpwvFXsG0Wy6SYxGEp1/K6rWgw +jgSx2ZiCntadkFrH35MJAYOx/DVW6ngxIic8hO+liBZfqI1lv7vlvVfs4sAe1bqK +0kcBDqp5SGNx2ENiDA4IbqDAp7JppQpEZrWJd2FGdbKviRyprVYgGILGJcMZQVNJ +agJfGGj7HPf5IbffrWWWyfE7oNCkSDZ2bw== +=tANz +-----END PGP MESSAGE----- + +Failed message: +gpg: C0FAD5E563B80E819603B0D9FFC2A910806894FF: skipped: No public key +gpg: [stdin]: encryption failed: No public key +*/ + +// Command decrypt use GnuPG(GPG) +/* +echo '-----BEGIN PGP MESSAGE----- +Comment: tiny-encrypt-1.6.0 + +hF4DcCBclRkzzAMSAQdAESdgetyKsgdAR6kps5ThpP2TcZB0hyGrmDqGj/1+lXIw +c9cam+BxFkDT7mZafuls0tV4MwHwKi2z1gQFNgTWuC45rpXyK7BFg74Rua+qLzvJ +0kcBpKSZIvQ/lX8JQ4hM41k6ymeYBQMC2nzmhwl/g9NBFyn5+dlEzDiZvL8YyQFT +IDGbLcEBW7a0B02ZKZ4ELyIDp94hdcbhrg== +=QEBj +-----END PGP MESSAGE-----' | gpg -d + +Failed message: +gpg: encrypted with 256-bit ECDH key, ID 70205C951933CC03, created 2023-10-05 + "Hatter Jiang (2023) " +gpg: public key decryption failed: Operation cancelled +gpg: decryption failed: No secret key + */ + +use std::process::{Command, Stdio}; + +use rust_util::{opt_result, opt_value_result, simple_error, XResult}; + +pub fn gpg_encrypt(key_id: &str, message: &[u8]) -> XResult { + let message_hex = hex::encode(message); + let echo = opt_result!(Command::new("echo").arg(&message_hex) + .stdout(Stdio::piped()) + .spawn(), "echo message failed: {}"); + let echo_stdout = opt_value_result!(echo.stdout, "Get echo stdout failed: none"); + + let mut cmd = Command::new("gpg"); + let encrypt_result = cmd + .args([ + "-e", "-a", "--no-comment", + "-r", &key_id, + "--comment", &format!("tiny-encrypt-v{} - {}", env!("CARGO_PKG_VERSION"), &key_id) + ]) + .stdin(Stdio::from(echo_stdout)) + .output(); + let encrypt_output = opt_result!(encrypt_result, "Encrypt GPG failed: {}"); + let stdout = String::from_utf8_lossy(&encrypt_output.stdout).to_string(); + let stderr = String::from_utf8_lossy(&encrypt_output.stderr).to_string(); + if !encrypt_output.status.success() { + return simple_error!( + "GPG encrypt failed: {:?}, stdout: {}, stderr: {}", + encrypt_output.status.code(), stdout, stderr + ); + } + Ok(stdout) +} + +pub fn gpg_decrypt(message: &str) -> XResult> { + let echo = opt_result!(Command::new("echo").arg(&message) + .stdout(Stdio::piped()) + .spawn(), "echo message failed: {}"); + let echo_stdout = opt_value_result!(echo.stdout, "Get echo stdout failed: none"); + + let mut cmd = Command::new("gpg"); + let encrypt_result = cmd + .arg("-d") + .stdin(Stdio::from(echo_stdout)) + .output(); + let decrypt_output = opt_result!(encrypt_result, "Decrypt GPG failed: {}"); + let stdout = String::from_utf8_lossy(&decrypt_output.stdout).to_string(); + let stderr = String::from_utf8_lossy(&decrypt_output.stderr).to_string(); + if !decrypt_output.status.success() { + return simple_error!( + "GPG decrypt failed: {:?}, stdout: {}, stderr: {}", + decrypt_output.status.code(), stdout, stderr + ); + } + let decrypted = opt_result!(hex::decode(&stdout.trim()), "Decode decrypted message failed: {}"); + Ok(decrypted) +}