feat: v0.7.0, add direct decrypt
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -1727,7 +1727,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-encrypt"
|
name = "tiny-encrypt"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm-stream",
|
"aes-gcm-stream",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiny-encrypt"
|
name = "tiny-encrypt"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "A simple and tiny file encrypt tool"
|
description = "A simple and tiny file encrypt tool"
|
||||||
|
|||||||
@@ -230,8 +230,8 @@ fn decrypt_file(file_in: &mut File, file_len: u64, file_out: &mut impl Write,
|
|||||||
last_block
|
last_block
|
||||||
};
|
};
|
||||||
opt_result!(file_out.write_all(&last_block), "Write file failed: {}");
|
opt_result!(file_out.write_all(&last_block), "Write file failed: {}");
|
||||||
debugging!("Decrypt finished, total: {} byte(s)", total_len);
|
|
||||||
progress.finish();
|
progress.finish();
|
||||||
|
debugging!("Decrypt finished, total: {} byte(s)", total_len);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
total_len += len as u64;
|
total_len += len as u64;
|
||||||
|
|||||||
120
src/cmd_directdecrypt.rs
Normal file
120
src/cmd_directdecrypt.rs
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
|
use clap::Args;
|
||||||
|
use rust_util::{debugging, information, opt_result, simple_error, success, warning, XResult};
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
|
use crate::crypto_cryptor::{Cryptor, KeyNonce};
|
||||||
|
use crate::util;
|
||||||
|
use crate::util_progress::Progress;
|
||||||
|
|
||||||
|
#[derive(Debug, Args)]
|
||||||
|
pub struct CmdDirectDecrypt {
|
||||||
|
/// Files in
|
||||||
|
#[arg(long, short = 'i')]
|
||||||
|
pub file_in: PathBuf,
|
||||||
|
/// Files output
|
||||||
|
#[arg(long, short = 'o')]
|
||||||
|
pub file_out: PathBuf,
|
||||||
|
/// Remove source file
|
||||||
|
#[arg(long, short = 'R')]
|
||||||
|
pub remove_file: bool,
|
||||||
|
/// Key in HEX
|
||||||
|
#[arg(long, short = 'k')]
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for CmdDirectDecrypt {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.key.zeroize();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const DIRECT_ENCRYPT_MAGIC: &str = "e2c50001";
|
||||||
|
|
||||||
|
// Format
|
||||||
|
// [4 bytes] - magic 0xe2c50001
|
||||||
|
// [32 bytes] - key digest
|
||||||
|
// [12 bytes] - nonce
|
||||||
|
pub fn direct_decrypt(cmd_direct_decrypt: CmdDirectDecrypt) -> XResult<()> {
|
||||||
|
let key = opt_result!(hex::decode(&cmd_direct_decrypt.key), "Parse key failed: {}");
|
||||||
|
if key.len() != 32 {
|
||||||
|
return simple_error!("Key length error, must be AES256.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut file_in = opt_result!(File::open(&cmd_direct_decrypt.file_in), "Open in file failed: {}");
|
||||||
|
let file_in_len = file_in.metadata().map(|m| m.len()).unwrap_or(0);
|
||||||
|
if fs::metadata(&cmd_direct_decrypt.file_out).is_ok() {
|
||||||
|
return simple_error!("Out file exists.");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut magic = [0_u8; 4];
|
||||||
|
opt_result!(file_in.read_exact(&mut magic), "Read magic failed: {}");
|
||||||
|
if hex::encode(magic) != DIRECT_ENCRYPT_MAGIC {
|
||||||
|
return simple_error!("File magic mismatch.");
|
||||||
|
}
|
||||||
|
let mut key_digest = [0_u8; 32];
|
||||||
|
opt_result!(file_in.read_exact(&mut key_digest), "Read key digest failed: {}");
|
||||||
|
if sha256::digest(&key) != hex::encode(key_digest) {
|
||||||
|
debugging!("Key digest mismatch: {} vs {}", sha256::digest(&key), hex::encode(key_digest));
|
||||||
|
return simple_error!("Key digest mismatch.");
|
||||||
|
}
|
||||||
|
let mut nonce = [0_u8; 12];
|
||||||
|
opt_result!(file_in.read_exact(&mut nonce), "Read nonce failed: {}");
|
||||||
|
|
||||||
|
let mut file_out = opt_result!(File::create(&cmd_direct_decrypt.file_out), "Create out file failed: {}");
|
||||||
|
let key_nonce = KeyNonce { k: &key, n: &nonce };
|
||||||
|
let instant = Instant::now();
|
||||||
|
let decrypted_len = opt_result!(
|
||||||
|
decrypt_file(&mut file_in, file_in_len, &mut file_out, Cryptor::Aes256Gcm, &key_nonce),
|
||||||
|
"Decrypt file {} -> {}, failed: {}",
|
||||||
|
cmd_direct_decrypt.file_in.display(),
|
||||||
|
cmd_direct_decrypt.file_out.display()
|
||||||
|
);
|
||||||
|
let elapsed_millis = instant.elapsed().as_millis();
|
||||||
|
success!("Decrypt file succeed: {}, file size: {} byte(s), elapsed: {} ms",
|
||||||
|
cmd_direct_decrypt.file_out.display(), decrypted_len, elapsed_millis);
|
||||||
|
|
||||||
|
util::zeroize(key);
|
||||||
|
nonce.zeroize();
|
||||||
|
drop(file_in);
|
||||||
|
drop(file_out);
|
||||||
|
|
||||||
|
if cmd_direct_decrypt.remove_file {
|
||||||
|
information!("Remove in file: {}", cmd_direct_decrypt.file_in.display());
|
||||||
|
if let Err(e) = fs::remove_file(&cmd_direct_decrypt.file_in) {
|
||||||
|
warning!("Remove in file failed: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn decrypt_file(file_in: &mut File, file_len: u64, file_out: &mut impl Write,
|
||||||
|
cryptor: Cryptor, key_nonce: &KeyNonce) -> XResult<u64> {
|
||||||
|
let mut total_len = 0_u64;
|
||||||
|
let mut buffer = [0u8; 1024 * 8];
|
||||||
|
let progress = Progress::new(file_len);
|
||||||
|
let mut decryptor = cryptor.decryptor(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: {}");
|
||||||
|
opt_result!(file_out.write_all(&last_block), "Write file failed: {}");
|
||||||
|
progress.finish();
|
||||||
|
debugging!("Decrypt finished, total: {} byte(s)", total_len);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
total_len += len as u64;
|
||||||
|
let decrypted = decryptor.update(&buffer[0..len]);
|
||||||
|
opt_result!(file_out.write_all(&decrypted), "Write file failed: {}");
|
||||||
|
progress.position(total_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(total_len)
|
||||||
|
}
|
||||||
@@ -3,6 +3,8 @@ pub use cmd_config::config;
|
|||||||
pub use cmd_decrypt::CmdDecrypt;
|
pub use cmd_decrypt::CmdDecrypt;
|
||||||
pub use cmd_decrypt::decrypt;
|
pub use cmd_decrypt::decrypt;
|
||||||
pub use cmd_decrypt::decrypt_single;
|
pub use cmd_decrypt::decrypt_single;
|
||||||
|
pub use cmd_directdecrypt::CmdDirectDecrypt;
|
||||||
|
pub use cmd_directdecrypt::direct_decrypt;
|
||||||
pub use cmd_encrypt::CmdEncrypt;
|
pub use cmd_encrypt::CmdEncrypt;
|
||||||
pub use cmd_encrypt::encrypt;
|
pub use cmd_encrypt::encrypt;
|
||||||
pub use cmd_encrypt::encrypt_single;
|
pub use cmd_encrypt::encrypt_single;
|
||||||
@@ -38,4 +40,5 @@ mod cmd_config;
|
|||||||
mod cmd_info;
|
mod cmd_info;
|
||||||
mod cmd_decrypt;
|
mod cmd_decrypt;
|
||||||
mod cmd_encrypt;
|
mod cmd_encrypt;
|
||||||
|
mod cmd_directdecrypt;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ extern crate core;
|
|||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use rust_util::XResult;
|
use rust_util::XResult;
|
||||||
|
|
||||||
use tiny_encrypt::{CmdConfig, CmdDecrypt, CmdEncrypt, CmdInfo, CmdVersion};
|
use tiny_encrypt::{CmdConfig, CmdDecrypt, CmdDirectDecrypt, CmdEncrypt, CmdInfo, CmdVersion};
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(name = "tiny-encrypt-rs")]
|
#[command(name = "tiny-encrypt-rs")]
|
||||||
@@ -21,6 +21,9 @@ enum Commands {
|
|||||||
/// Decrypt file(s)
|
/// Decrypt file(s)
|
||||||
#[command(arg_required_else_help = true, short_flag = 'd')]
|
#[command(arg_required_else_help = true, short_flag = 'd')]
|
||||||
Decrypt(CmdDecrypt),
|
Decrypt(CmdDecrypt),
|
||||||
|
/// Direct decrypt file(s)
|
||||||
|
#[command(arg_required_else_help = true)]
|
||||||
|
DirectDecrypt(CmdDirectDecrypt),
|
||||||
/// Show file info
|
/// Show file info
|
||||||
#[command(arg_required_else_help = true, short_flag = 'I')]
|
#[command(arg_required_else_help = true, short_flag = 'I')]
|
||||||
Info(CmdInfo),
|
Info(CmdInfo),
|
||||||
@@ -37,6 +40,7 @@ fn main() -> XResult<()> {
|
|||||||
match args.command {
|
match args.command {
|
||||||
Commands::Encrypt(cmd_encrypt) => tiny_encrypt::encrypt(cmd_encrypt),
|
Commands::Encrypt(cmd_encrypt) => tiny_encrypt::encrypt(cmd_encrypt),
|
||||||
Commands::Decrypt(cmd_decrypt) => tiny_encrypt::decrypt(cmd_decrypt),
|
Commands::Decrypt(cmd_decrypt) => tiny_encrypt::decrypt(cmd_decrypt),
|
||||||
|
Commands::DirectDecrypt(cmd_direct_decrypt) => tiny_encrypt::direct_decrypt(cmd_direct_decrypt),
|
||||||
Commands::Info(cmd_info) => tiny_encrypt::info(cmd_info),
|
Commands::Info(cmd_info) => tiny_encrypt::info(cmd_info),
|
||||||
Commands::Version(cmd_version) => tiny_encrypt::version(cmd_version),
|
Commands::Version(cmd_version) => tiny_encrypt::version(cmd_version),
|
||||||
Commands::Config(cmd_config) => tiny_encrypt::config(cmd_config),
|
Commands::Config(cmd_config) => tiny_encrypt::config(cmd_config),
|
||||||
|
|||||||
Reference in New Issue
Block a user