feat: PGP decrypt works
This commit is contained in:
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user