diff --git a/Cargo.lock b/Cargo.lock index def85fd..517233a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1657,7 +1657,7 @@ dependencies = [ [[package]] name = "tiny-encrypt" -version = "0.8.1" +version = "0.8.2" dependencies = [ "aes-gcm-stream", "base64", diff --git a/Cargo.toml b/Cargo.toml index d72c78b..a1a697f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tiny-encrypt" -version = "0.8.1" +version = "0.8.2" edition = "2021" license = "MIT" description = "A simple and tiny file encrypt tool" diff --git a/src/cmd_decrypt.rs b/src/cmd_decrypt.rs index 8cc5956..995e221 100644 --- a/src/cmd_decrypt.rs +++ b/src/cmd_decrypt.rs @@ -188,11 +188,27 @@ pub fn decrypt_single(config: &Option, None => return Ok(0), Some(output) => output, }; - let editor = get_file_editor(); - let temp_file = create_edit_temp_file(&file_content, path_out)?; + let (secure_editor, editor) = get_file_editor(); + let temp_cryptor = Cryptor::Aes256Gcm; + let temp_encryption_key_nonce = util::make_key256_and_nonce(); + let temp_key_nonce = KeyNonce { k: temp_encryption_key_nonce.0.as_ref(), n: temp_encryption_key_nonce.1.as_ref() }; + let write_file_content = if secure_editor { + let mut encryptor = temp_cryptor.encryptor(&temp_key_nonce)?; + encryptor.encrypt(file_content.as_bytes()) + } else { + file_content.as_bytes().to_vec() + }; + let temp_file = create_edit_temp_file(&write_file_content, path_out)?; let do_edit_file = || -> XResult<()> { - let temp_file_content = run_file_editor_and_wait_content(&editor, &temp_file)?; + let temp_file_content_bytes = run_file_editor_and_wait_content(&editor, &temp_file, secure_editor, &temp_encryption_key_nonce)?; + let temp_file_content_bytes = if secure_editor { + let mut decryptor = temp_cryptor.decryptor(&temp_key_nonce)?; + decryptor.decrypt(&temp_file_content_bytes)? + } else { + temp_file_content_bytes + }; + let temp_file_content = opt_result!(String::from_utf8(temp_file_content_bytes), "Read temp file failed: {}"); if temp_file_content == file_content { information!("Temp file is not changed."); return Ok(()); @@ -260,32 +276,43 @@ pub fn decrypt_single(config: &Option, Ok(meta.file_length) } -fn run_file_editor_and_wait_content(editor: &str, temp_file: &PathBuf) -> XResult { +fn run_file_editor_and_wait_content(editor: &str, temp_file: &PathBuf, secure_editor: bool, temp_encryption_key_nonce: &(SecVec, SecVec)) -> XResult> { let mut command = Command::new(editor); command.arg(temp_file.to_str().expect("Get temp file path failed.")); + if secure_editor { + command.arg("aes-256-gcm"); + command.arg(&hex::encode(&temp_encryption_key_nonce.0)); + command.arg(&hex::encode(&temp_encryption_key_nonce.1)); + } + debugging!("Run cmd: {:?}", command); let run_cmd_result = util_cmd::run_command_and_wait(&mut command); debugging!("Run cmd result: {:?}", run_cmd_result); let run_cmd_exit_status = opt_result!(run_cmd_result, "Run cmd {} failed: {}", editor); if !run_cmd_exit_status.success() { return simple_error!("Run cmd {} failed: {:?}", editor, run_cmd_exit_status.code()); } - Ok(opt_result!(fs::read_to_string(&temp_file), "Read file failed: {}")) + Ok(opt_result!(fs::read(temp_file), "Read file failed: {}")) } -fn get_file_editor() -> String { - match env::var("EDITOR") { - Ok(editor) => editor, - Err(_) => { +fn get_file_editor() -> (bool, String) { + if let Ok(secure_editor) = env::var("SECURE_EDITOR") { + // cmd "aes-256-gcm" + information!("Found secure editor: {}", &secure_editor); + return (true, secure_editor); + } + match env::var("EDITOR").ok() { + Some(editor) => (false, editor), + None => { warning!("EDITOR is not assigned, use default editor vi"); - "vi".to_string() + (false, "vi".to_string()) } } } -fn create_edit_temp_file(file_content: &str, path_out: &str) -> XResult { +fn create_edit_temp_file(file_content: &[u8], path_out: &str) -> XResult { let temp_dir = temp_dir(); let current_millis = util_time::get_current_millis(); - let temp_file = temp_dir.join(&format!("tmp_file_{}_{}", current_millis, path_out)); + let temp_file = temp_dir.join(format!("tmp_file_{}_{}", current_millis, path_out)); information!("Temp file: {}", temp_file.display()); opt_result!(fs::write(&temp_file, file_content), "Write temp file failed: {}"); Ok(temp_file) @@ -303,13 +330,13 @@ fn decrypt_limited_content_to_vec(mut file_in: &mut File, let mut output: Vec = Vec::with_capacity(10 * 1024); let _ = decrypt_file( - &mut file_in, meta.file_length, &mut output, cryptor, &key_nonce, meta.compress, + &mut file_in, meta.file_length, &mut output, cryptor, key_nonce, meta.compress, )?; match String::from_utf8(output) { Err(_) => failure!("File content is not UTF-8 encoded."), Ok(output) => return Ok(Some(output)), } - return Ok(None); + Ok(None) } fn decrypt_file(file_in: &mut impl Read, file_len: u64, file_out: &mut impl Write, @@ -491,6 +518,10 @@ fn select_envelop<'a>(meta: &'a TinyEncryptMeta, key_id: &Option, config }; success!("Found {} envelops:", envelops.len()); + if let Some(envelop) = match_envelop_by_key_id(envelops, key_id, config) { + return Ok(envelop); + } + if envelops.len() == 1 { let selected_envelop = &envelops[0]; success!("Auto selected envelop: #{} {}", 1, util_envelop::format_envelop(selected_envelop, config)); @@ -498,25 +529,6 @@ fn select_envelop<'a>(meta: &'a TinyEncryptMeta, key_id: &Option, config return Ok(selected_envelop); } - if let Some(key_id) = key_id { - for envelop in envelops { - let is_sid_matched = match config { - None => false, - Some(config) => match config.find_by_kid(&envelop.kid) { - None => false, - Some(config) => match &config.sid { - None => false, - Some(sid) => sid == key_id, - }, - } - }; - if is_sid_matched || (&envelop.kid == key_id) { - information!("Matched envelop: {}", util_envelop::format_envelop(envelop, config)); - return Ok(envelop); - } - } - } - envelops.iter().enumerate().for_each(|(i, envelop)| { println_ex!("#{} {}", i + 1, util_envelop::format_envelop(envelop, config)); }); @@ -525,4 +537,22 @@ fn select_envelop<'a>(meta: &'a TinyEncryptMeta, key_id: &Option, config let selected_envelop = &envelops[envelop_number - 1]; success!("Selected envelop: #{} {}", envelop_number, selected_envelop.r#type.get_upper_name()); Ok(selected_envelop) -} \ No newline at end of file +} + +fn match_envelop_by_key_id<'a>(envelops: &'a Vec, key_id: &Option, config: &Option) -> Option<&'a TinyEncryptEnvelop> { + if let Some(key_id) = key_id { + for envelop in envelops { + let is_sid_matched = config.as_ref().and_then(|config| { + config.find_by_kid(&envelop.kid).and_then(|config_envelop| { + config_envelop.sid.as_ref().map(|sid| sid == key_id) + }) + }).unwrap_or(false); + + if is_sid_matched || (&envelop.kid == key_id) { + information!("Matched envelop: {}", util_envelop::format_envelop(envelop, config)); + return Some(envelop); + } + } + } + None +}