feat: PGP decrypt works

This commit is contained in:
2023-09-07 01:08:12 +08:00
parent 34fd7d0e47
commit 8939237d99
5 changed files with 176 additions and 17 deletions

View File

@@ -1,22 +1,81 @@
use std::path::PathBuf;
use std::fs;
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
use base64::Engine;
use base64::engine::general_purpose;
use openpgp_card::crypto_data::Cryptogram;
use openpgp_card::OpenPgp;
use rust_util::{debugging, failure, opt_result, simple_error, success, XResult};
use crate::card::get_card;
use crate::file;
use rust_util::{debugging, failure, opt_result, simple_error, success, util_term, XResult};
pub fn decrypt(path: PathBuf, pin: &Option<String>) -> XResult<()> {
use crate::{file, util};
use crate::card::get_card;
use crate::spec::{TinyEncryptEnvelop, TinyEncryptMeta};
use crate::util::{decode_base64, TINY_ENC_FILE_EXT};
pub fn decrypt(path: &PathBuf, pin: &Option<String>) -> XResult<()> {
let path_display = format!("{}", path.display());
if !path_display.ends_with(TINY_ENC_FILE_EXT) {
return simple_error!("File is not tiny encrypt file: {}", &path_display);
}
let mut file_in = opt_result!(File::open(path), "Open file: {} failed: {}", &path_display);
let mut meta = opt_result!(file::read_tiny_encrypt_meta(&mut file_in), "Read file: {}, failed: {}", &path_display);
meta.normalize();
debugging!("Found meta: {}", serde_json::to_string_pretty(&meta).unwrap());
let path_out = &path_display[0..path_display.len() - TINY_ENC_FILE_EXT.len()];
if let Ok(_) = fs::metadata(path_out) {
return simple_error!("Output file: {} exists", path_out);
}
let mut card = match get_card() {
debugging!("Found meta: {}", serde_json::to_string_pretty(&meta).unwrap());
let selected_envelop = select_envelop(&meta)?;
let key = try_decrypt_key(selected_envelop, pin)?;
let nonce = opt_result!( decode_base64(&meta.nonce), "Decode nonce failed: {}");
debugging!("Decrypt key: {}", hex::encode(&key));
debugging!("Decrypt nonce: {}", hex::encode(&nonce));
let mut file_out = File::create(path_out)?;
let _ = decrypt_file(&mut file_in, &mut file_out, &key, &nonce)?;
Ok(())
}
fn decrypt_file(file_in: &mut File, file_out: &mut File, key: &[u8], nonce: &[u8]) -> XResult<usize> {
let mut total_len = 0;
let mut buffer = [0u8; 1024 * 8];
let key = opt_result!(key.try_into(), "Key is not 32 bytes: {}");
let mut decryptor = aes_gcm_stream::Aes256GcmStreamDecryptor::new(key, &nonce);
loop {
let len = opt_result!(file_in.read(&mut buffer), "Read file failed: {}");
if len == 0 {
let last_block = opt_result!(decryptor.finalize(), "Decrypt file failed: {}");
if !last_block.is_empty() {
opt_result!(file_out.write_all(&last_block), "Write file failed: {}");
}
success!("Decrypt finished, total bytes: {}", total_len);
break;
} else {
total_len += len;
let decrypted = decryptor.update(&buffer[0..len]);
if !decrypted.is_empty() {
opt_result!(file_out.write_all(&decrypted), "Write file failed: {}");
}
}
}
Ok(total_len)
}
fn try_decrypt_key(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XResult<Vec<u8>> {
match envelop.r#type.to_lowercase().as_str() {
"pgp" => try_decrypt_key_pgp(envelop, pin),
unknown_type => return simple_error!("Unknown or not supported type: {}", unknown_type)
}
}
fn try_decrypt_key_pgp(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XResult<Vec<u8>> {
let card = match get_card() {
Err(e) => {
failure!("Get PGP card failed: {}", e);
return simple_error!("Get card failed: {}", e);
@@ -26,20 +85,62 @@ pub fn decrypt(path: PathBuf, pin: &Option<String>) -> XResult<()> {
let mut pgp = OpenPgp::new(card);
let mut trans = opt_result!(pgp.transaction(), "Open card failed: {}");
let pin = pin.as_ref().map(|s| s.as_str()).unwrap_or_else(|| "123456");
let pin = read_pin(pin);
if let Err(e) = trans.verify_pw1_user(pin.as_ref()) {
failure!("Verify user pin failed: {}", e);
return simple_error!("User pin verify failed: {}", e);
}
success!("User pin verify success!");
let pgp_envelop = meta.pgp_envelop.unwrap();
let pgp_envelop = &envelop.encrypted_key;
debugging!("PGP envelop: {}", &pgp_envelop);
let pgp_envelop_bytes = opt_result!(general_purpose::STANDARD.decode(&pgp_envelop), "Decode PGP envelop failed: {}");
let pgp_envelop_bytes = opt_result!(decode_base64(&pgp_envelop), "Decode PGP envelop failed: {}");
let key = trans.decipher(Cryptogram::RSA(&pgp_envelop_bytes))?;
Ok(key)
}
success!("{}", hex::encode(&key));
fn read_pin(pin: &Option<String>) -> String {
match pin {
Some(pin) => pin.to_string(),
None => if util_term::read_yes_no("Use default PIN 123456, please confirm") {
"123456".into()
} else {
rpassword::prompt_password("Please input PIN: ").expect("Read PIN failed")
}
}
}
Ok(())
fn select_envelop(meta: &TinyEncryptMeta) -> XResult<&TinyEncryptEnvelop> {
let envelops = match &meta.envelops {
None => return simple_error!("No envelops found"),
Some(envelops) => if envelops.is_empty() {
return simple_error!("No envelops found");
} else {
envelops
},
};
success!("Found {} envelops:", envelops.len());
if envelops.len() == 1 {
let selected_envelop = &envelops[0];
success!("Auto selected envelop: #{} {}", 1, selected_envelop.r#type.to_uppercase());
return Ok(selected_envelop);
}
envelops.iter().enumerate().for_each(|(i, envelop)| {
println!("#{} {}{}", i + 1,
envelop.r#type.to_uppercase(),
if envelop.kid.is_empty() {
"".into()
} else {
format!(", Kid: {}", envelop.kid)
}
);
});
let envelop_number = util::read_number("Please select an envelop:", 1, envelops.len());
let selected_envelop = &envelops[envelop_number - 1];
success!("Selected envelop: #{} {}", envelop_number, selected_envelop.r#type.to_uppercase());
Ok(selected_envelop)
}