From 80e3a3540eae58f242a17a7fbe5d8d281e09ece7 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sat, 28 Oct 2023 00:10:27 +0800 Subject: [PATCH] feat: v0.5.4, many updates --- Cargo.lock | 24 +++++++++++------------- Cargo.toml | 2 +- src/cmd_decrypt.rs | 33 ++++++++++++++++++--------------- src/cmd_encrypt.rs | 29 ++++++++++++++++++----------- src/cmd_info.rs | 34 ++++++++++++++++++++++++++++------ src/cmd_version.rs | 1 + src/compress.rs | 4 ++++ src/config.rs | 12 ++++++------ src/crypto_cryptor.rs | 35 +++++++++++++++++++++-------------- src/crypto_simple.rs | 26 ++++++++++++++------------ src/lib.rs | 41 +++++++++++++++++++++++++++++++++++++++++ src/main.rs | 42 ++++++------------------------------------ src/spec.rs | 10 +++++----- src/util_enc_file.rs | 16 ++++++++-------- src/util_env.rs | 5 ++++- 15 files changed, 186 insertions(+), 128 deletions(-) create mode 100644 src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4091f25..5faaccf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -377,9 +377,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fbc60abd742b35f2492f808e1abbb83d45f72db402e14c55057edc9c7b1e9e4" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" dependencies = [ "libc", ] @@ -1334,16 +1334,14 @@ dependencies = [ [[package]] name = "rsa" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab43bb47d23c1a631b4b680199a45255dce26fa9ab2fa902581f624ff13e6a8" +checksum = "86ef35bf3e7fe15a53c4ab08a998e42271eab13eb0db224126bc7bc4c4bad96d" dependencies = [ - "byteorder", "const-oid", "digest", "num-bigint-dig", "num-integer", - "num-iter", "num-traits", "pkcs1", "pkcs8", @@ -1416,9 +1414,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.20" +version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" +checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ "bitflags 2.4.1", "errno", @@ -1470,18 +1468,18 @@ checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090" [[package]] name = "serde" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" +checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.189" +version = "1.0.190" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" +checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" dependencies = [ "proc-macro2", "quote", @@ -1729,7 +1727,7 @@ dependencies = [ [[package]] name = "tiny-encrypt" -version = "0.5.3" +version = "0.5.4" dependencies = [ "aes-gcm-stream", "base64", diff --git a/Cargo.toml b/Cargo.toml index abbe5c2..2378734 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "tiny-encrypt" -version = "0.5.3" +version = "0.5.4" 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 e094bc3..961476c 100644 --- a/src/cmd_decrypt.rs +++ b/src/cmd_decrypt.rs @@ -22,7 +22,7 @@ use crate::consts::{ ENC_CHACHA20_POLY1305_P256, ENC_CHACHA20_POLY1305_P384, ENC_CHACHA20_POLY1305_X25519, SALT_COMMENT, TINY_ENC_CONFIG_FILE, TINY_ENC_FILE_EXT, }; -use crate::crypto_cryptor::Cryptor; +use crate::crypto_cryptor::{Cryptor, KeyNonce}; use crate::spec::{EncEncryptedMeta, TinyEncryptEnvelop, TinyEncryptEnvelopType, TinyEncryptMeta}; use crate::util::SecVec; use crate::util_digest::DigestWrite; @@ -114,7 +114,7 @@ pub fn decrypt_single(config: &Option, util::require_tiny_enc_file_and_exists(path)?; let mut file_in = opt_result!(File::open(path), "Open file: {} failed: {}", &path_display); - let meta = opt_result!( + let (_, meta) = opt_result!( util_enc_file::read_tiny_encrypt_meta_and_normalize(&mut file_in), "Read file: {}, failed: {}", &path_display); debugging!("Found meta: {}", serde_json::to_string_pretty(&meta).unwrap()); @@ -136,12 +136,13 @@ pub fn decrypt_single(config: &Option, let key = SecVec(try_decrypt_key(config, selected_envelop, pin, slot)?); let nonce = SecVec(opt_result!(util::decode_base64(&meta.nonce), "Decode nonce failed: {}")); + let key_nonce = KeyNonce { k: &key.0, n: &nonce.0 }; // debugging!("Decrypt key: {}", hex::encode(&key.0)); debugging!("Decrypt nonce: {}", hex::encode(&nonce.0)); - let enc_meta = parse_encrypted_meta(&meta, cryptor, &key.0, &nonce.0)?; - parse_encrypted_comment(&meta, cryptor, &key.0, &nonce.0)?; + let enc_meta = parse_encrypted_meta(&meta, cryptor, &key_nonce)?; + parse_encrypted_comment(&meta, cryptor, &key_nonce)?; // Decrypt to output if cmd_decrypt.direct_print { @@ -152,7 +153,7 @@ pub fn decrypt_single(config: &Option, let mut output: Vec = Vec::with_capacity(10 * 1024); let _ = decrypt_file( - &mut file_in, meta.file_length, &mut output, cryptor, &key.0, &nonce.0, meta.compress, + &mut file_in, meta.file_length, &mut output, cryptor, &key_nonce, meta.compress, )?; match String::from_utf8(output) { Err(_) => warning!("File is not UTF-8 content."), @@ -169,7 +170,7 @@ pub fn decrypt_single(config: &Option, if cmd_decrypt.digest_file { let mut digest_write = DigestWrite::from_algo(digest_algorithm)?; let _ = decrypt_file( - &mut file_in, meta.file_length, &mut digest_write, cryptor, &key.0, &nonce.0, meta.compress, + &mut file_in, meta.file_length, &mut digest_write, cryptor, &key_nonce, meta.compress, )?; let digest = digest_write.digest(); success!("File digest {}: {}", digest_algorithm.to_uppercase(), hex::encode(digest)); @@ -187,7 +188,7 @@ pub fn decrypt_single(config: &Option, let mut file_out = File::create(path_out)?; let _ = decrypt_file( - &mut file_in, meta.file_length, &mut file_out, cryptor, &key.0, &nonce.0, meta.compress, + &mut file_in, meta.file_length, &mut file_out, cryptor, &key_nonce, meta.compress, )?; drop(file_out); util_file::update_out_file_time(enc_meta, path_out); @@ -200,11 +201,11 @@ pub fn decrypt_single(config: &Option, } fn decrypt_file(file_in: &mut File, file_len: u64, file_out: &mut impl Write, - cryptor: Cryptor, key: &[u8], nonce: &[u8], compress: bool) -> XResult { + cryptor: Cryptor, key_nonce: &KeyNonce, compress: bool) -> XResult { 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)?; + let mut decryptor = cryptor.decryptor(key_nonce)?; let mut gz_decoder = GzStreamDecoder::new(); loop { let len = opt_result!(file_in.read(&mut buffer), "Read file failed: {}"); @@ -237,11 +238,11 @@ fn decrypt_file(file_in: &mut File, file_len: u64, file_out: &mut impl Write, Ok(total_len) } -fn parse_encrypted_comment(meta: &TinyEncryptMeta, crypto: Cryptor, key: &[u8], nonce: &[u8]) -> XResult<()> { +fn parse_encrypted_comment(meta: &TinyEncryptMeta, crypto: Cryptor, key_nonce: &KeyNonce) -> XResult<()> { if let Some(encrypted_comment) = &meta.encrypted_comment { match util::decode_base64(encrypted_comment) { Err(e) => warning!("Decode encrypted comment failed: {}", e), - Ok(ec_bytes) => match crypto_simple::try_decrypt_with_salt(crypto, key, nonce, SALT_COMMENT, &ec_bytes) { + Ok(ec_bytes) => match crypto_simple::try_decrypt_with_salt(crypto, key_nonce, SALT_COMMENT, &ec_bytes) { Err(e) => warning!("Decrypt encrypted comment failed: {}", e), Ok(decrypted_comment_bytes) => match String::from_utf8(decrypted_comment_bytes.clone()) { Err(_) => success!("Encrypted message hex: {}", hex::encode(&decrypted_comment_bytes)), @@ -253,7 +254,7 @@ fn parse_encrypted_comment(meta: &TinyEncryptMeta, crypto: Cryptor, key: &[u8], Ok(()) } -fn parse_encrypted_meta(meta: &TinyEncryptMeta, cryptor: Cryptor, key: &[u8], nonce: &[u8]) -> XResult> { +fn parse_encrypted_meta(meta: &TinyEncryptMeta, cryptor: Cryptor, key_nonce: &KeyNonce) -> XResult> { let enc_encrypted_meta = match &meta.encrypted_meta { None => return Ok(None), Some(enc_encrypted_meta) => enc_encrypted_meta, @@ -261,7 +262,7 @@ fn parse_encrypted_meta(meta: &TinyEncryptMeta, cryptor: Cryptor, key: &[u8], no let enc_encrypted_meta_bytes = opt_result!( util::decode_base64(enc_encrypted_meta), "Decode enc-encrypted-meta failed: {}"); let enc_meta = opt_result!( - EncEncryptedMeta::unseal(cryptor, key, nonce, &enc_encrypted_meta_bytes), "Unseal enc-encrypted-meta failed: {}"); + EncEncryptedMeta::unseal(cryptor, key_nonce, &enc_encrypted_meta_bytes), "Unseal enc-encrypted-meta failed: {}"); debugging!("Encrypted meta: {:?}", enc_meta); if let Some(filename) = &enc_meta.filename { information!("Source filename: {}", filename); @@ -320,8 +321,9 @@ fn try_decrypt_key_ecdh(config: &Option, slot_id, ), "Decrypt via PIV card failed: {}"); let key = util::simple_kdf(shared_secret.as_slice()); + let key_nonce = KeyNonce { k: &key, n: &wrap_key.nonce }; let decrypted_key = crypto_simple::decrypt( - cryptor, &key, &wrap_key.nonce, &wrap_key.encrypted_data)?; + cryptor, &key_nonce, &wrap_key.encrypted_data)?; util::zeroize(pin); util::zeroize(key); util::zeroize(shared_secret); @@ -346,8 +348,9 @@ fn try_decrypt_key_ecdh_pgp_x25519(envelop: &TinyEncryptEnvelop, pin: &Option XResult<()> { Ok(()) } -fn encrypt_single(path: &PathBuf, envelops: &[&TinyEncryptConfigEnvelop], cmd_encrypt: &CmdEncrypt) -> XResult { +pub fn encrypt_single(path: &PathBuf, envelops: &[&TinyEncryptConfigEnvelop], cmd_encrypt: &CmdEncrypt) -> XResult { + let path_display = format!("{}", path.display()); + let path_out = format!("{}{}", path_display, TINY_ENC_FILE_EXT); + encrypt_single_file_out(path, &path_out, envelops, cmd_encrypt) +} + +pub fn encrypt_single_file_out(path: &PathBuf, path_out: &str, envelops: &[&TinyEncryptConfigEnvelop], cmd_encrypt: &CmdEncrypt) -> XResult { let path_display = format!("{}", path.display()); if path_display.ends_with(TINY_ENC_FILE_EXT) { information!("Tiny enc file skipped: {}", path_display); @@ -126,17 +132,17 @@ fn encrypt_single(path: &PathBuf, envelops: &[&TinyEncryptConfigEnvelop], cmd_en let mut file_in = opt_result!(File::open(path), "Open file: {} failed: {}", &path_display); - let path_out = format!("{}{}", path_display, TINY_ENC_FILE_EXT); - util::require_file_not_exists(path_out.as_str())?; + util::require_file_not_exists(path_out)?; let (key, nonce) = util::make_key256_and_nonce(); + let key_nonce = KeyNonce { k: &key.0, n: &nonce.0 }; let envelops = encrypt_envelops(cryptor, &key.0, envelops)?; let encrypted_comment = match &cmd_encrypt.encrypted_comment { None => None, Some(encrypted_comment) => Some(util::encode_base64( &crypto_simple::encrypt_with_salt( - cryptor, &key.0, &nonce.0, SALT_COMMENT, encrypted_comment.as_bytes())?)) + cryptor, &key_nonce, SALT_COMMENT, encrypted_comment.as_bytes())?)) }; let file_metadata = opt_result!(fs::metadata(path), "Read file: {} meta failed: {}", path.display()); @@ -146,7 +152,7 @@ fn encrypt_single(path: &PathBuf, envelops: &[&TinyEncryptConfigEnvelop], cmd_en m_time: file_metadata.modified().ok().and_then(|t| t.to_millis()), }; let enc_encrypted_meta_bytes = opt_result!(enc_encrypted_meta.seal( - cryptor, &key.0, &nonce.0), "Seal enc-encrypted-meta failed: {}"); + cryptor, &key_nonce), "Seal enc-encrypted-meta failed: {}"); let enc_metadata = EncMetadata { comment: cmd_encrypt.comment.clone(), @@ -172,7 +178,7 @@ fn encrypt_single(path: &PathBuf, envelops: &[&TinyEncryptConfigEnvelop], cmd_en } } - let mut file_out = File::create(&path_out)?; + let mut file_out = File::create(path_out)?; let compress_meta = !cmd_encrypt.disable_compress_meta; let _ = util_enc_file::write_tiny_encrypt_meta(&mut file_out, &encrypt_meta, compress_meta)?; @@ -183,7 +189,7 @@ fn encrypt_single(path: &PathBuf, envelops: &[&TinyEncryptConfigEnvelop], cmd_en let start = Instant::now(); encrypt_file( &mut file_in, file_metadata.len(), &mut file_out, cryptor, - &key.0, &nonce.0, &compress_level, + &key_nonce, &compress_level, )?; drop(file_out); let encrypt_duration = start.elapsed(); @@ -216,7 +222,7 @@ fn process_compatible_with_1_0(mut encrypt_meta: TinyEncryptMeta) -> XResult) -> XResult { + key_nonce: &KeyNonce, compress_level: &Option) -> XResult { let compress = compress_level.is_some(); let mut total_len = 0_u64; let mut write_len = 0_u64; @@ -231,7 +237,7 @@ fn encrypt_file(file_in: &mut File, file_len: u64, file_out: &mut impl Write, cr } }; let progress = Progress::new(file_len); - let mut encryptor = cryptor.encryptor(key, nonce)?; + let mut encryptor = cryptor.encryptor(key_nonce)?; loop { let len = opt_result!(file_in.read(&mut buffer), "Read file failed: {}"); if len == 0 { @@ -334,9 +340,10 @@ fn encrypt_envelop_shared_secret(cryptor: Cryptor, envelop: &TinyEncryptConfigEnvelop) -> XResult { let shared_key = util::simple_kdf(shared_secret); let nonce = util::make_nonce(); + let key_nonce = KeyNonce { k: &shared_key, n: &nonce.0 }; let encrypted_key = crypto_simple::encrypt( - cryptor, &shared_key, &nonce.0, key)?; + cryptor, &key_nonce, key)?; let wrap_key = WrapKey { header: WrapKeyHeader { diff --git a/src/cmd_info.rs b/src/cmd_info.rs index 9ec28d8..ded5b97 100644 --- a/src/cmd_info.rs +++ b/src/cmd_info.rs @@ -4,13 +4,15 @@ use std::path::PathBuf; use std::time::{Duration, SystemTime}; use clap::Args; -use rust_util::{opt_result, simple_error, success, util_time, warning, XResult}; +use rust_util::{debugging, failure, iff, opt_result, simple_error, success, util_msg, util_size, util_time, XResult}; +use rust_util::util_msg::MessageType; use rust_util::util_time::UnixEpochTime; use simpledateformat::format_human2; -use crate::{util_enc_file, util_envelop}; +use crate::{util, util_enc_file, util_envelop}; use crate::config::TinyEncryptConfig; use crate::consts::{DATE_TIME_FORMAT, TINY_ENC_AES_GCM, TINY_ENC_CONFIG_FILE, TINY_ENC_FILE_EXT}; +use crate::wrap_key::WrapKey; #[derive(Debug, Args)] pub struct CmdInfo { @@ -25,7 +27,7 @@ pub fn info(cmd_info: CmdInfo) -> XResult<()> { for (i, path) in cmd_info.paths.iter().enumerate() { if i > 0 { println!("{}", "-".repeat(88)); } if let Err(e) = info_single(path, &cmd_info) { - warning!("Parse Tiny Encrypt file info failed: {}", e); + failure!("Parse Tiny Encrypt file info failed: {}", e); } } println!(); @@ -40,7 +42,9 @@ pub fn info_single(path: &PathBuf, cmd_info: &CmdInfo) -> XResult<()> { let config = TinyEncryptConfig::load(TINY_ENC_CONFIG_FILE).ok(); let mut file_in = opt_result!(File::open(path), "Open file: {} failed: {}", &path_display); - let meta = opt_result!( + let file_in_len = file_in.metadata().map(|m| m.len()).unwrap_or(0); + + let (meta_len, meta) = opt_result!( util_enc_file::read_tiny_encrypt_meta_and_normalize(&mut file_in), "Read file: {}, failed: {}", &path_display ); @@ -53,7 +57,20 @@ pub fn info_single(path: &PathBuf, cmd_info: &CmdInfo) -> XResult<()> { infos.push("Tiny Encrypt File Info".to_string()); let compressed = if meta.compress { " [compressed]" } else { "" }; infos.push(format!("{}: {}{}", header("File name"), path_display, compressed)); - infos.push(format!("{}: {} bytes", header("File size"), meta.file_length)); + + if meta.compress && file_in_len > (2 + 4 + meta_len as u64) { + let actual_file_in_len = file_in_len - 2 - 4 - meta_len as u64; + infos.push(format!("{}: {}, after compressed {}, ratio: {}%", + header("File size"), + util_size::get_display_size(meta.file_length as i64), + util_size::get_display_size(actual_file_in_len as i64), + util::ratio(actual_file_in_len, meta.file_length) + )); + } else { + infos.push(format!("{}: {}", header("File size"), + util_size::get_display_size(meta.file_length as i64))); + } + infos.push(format!("{}: Version: {}, Agent: {}", header("File summary"), meta.version, meta.user_agent) ); @@ -77,6 +94,11 @@ pub fn info_single(path: &PathBuf, cmd_info: &CmdInfo) -> XResult<()> { header(&format!("Envelop #{}", i + 1)), util_envelop::format_envelop(envelop, &config) )); + util_msg::when(MessageType::DEBUG, || { + if let Ok(wrap_key) = WrapKey::parse(&envelop.encrypted_key) { + debugging!("Wrap key: {}", serde_json::to_string(&wrap_key).expect("SHOULD NOT HAPPEN")); + } + }); }) } @@ -105,5 +127,5 @@ fn header(h: &str) -> String { } fn to_yes_or_no(opt: &Option) -> String { - opt.as_ref().map(|_| "YES".to_string()).unwrap_or_else(|| "NO".to_string()) + iff!(opt.is_some(), "YES", "NO").to_string() } \ No newline at end of file diff --git a/src/cmd_version.rs b/src/cmd_version.rs index adc9ab2..08df834 100644 --- a/src/cmd_version.rs +++ b/src/cmd_version.rs @@ -1,5 +1,6 @@ use clap::Args; use rust_util::XResult; + use crate::util; #[derive(Debug, Args)] diff --git a/src/compress.rs b/src/compress.rs index 4bee024..8c3887b 100644 --- a/src/compress.rs +++ b/src/compress.rs @@ -4,6 +4,10 @@ use flate2::Compression; use flate2::write::{GzDecoder, GzEncoder}; use rust_util::{simple_error, XResult}; +pub fn compress_default(message: &[u8]) -> XResult> { + compress(Compression::default(), message) +} + pub fn compress(compression: Compression, message: &[u8]) -> XResult> { let mut encoder = GzStreamEncoder::new(compression); let mut buff = encoder.update(message)?; diff --git a/src/config.rs b/src/config.rs index 135cff3..bf66513 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering; use std::collections::HashMap; use std::fs; -use rust_util::{debugging, iff, opt_result, simple_error, XResult}; +use rust_util::{debugging, opt_result, simple_error, XResult}; use rust_util::util_file::resolve_file_path; use serde::{Deserialize, Serialize}; @@ -57,20 +57,20 @@ impl TinyEncryptConfig { ); let mut config: TinyEncryptConfig = opt_result!( serde_json::from_str(&config_contents),"Parse file: {}, failed: {}", file); - let mut splitted_profiles = HashMap::new(); + let mut splited_profiles = HashMap::new(); for (k, v) in config.profiles.into_iter() { if !k.contains(',') { - splitted_profiles.insert(k, v); + splited_profiles.insert(k, v); } else { k.split(',') .map(|k| k.trim()) .filter(|k| !k.is_empty()) .for_each(|k| { - splitted_profiles.insert(k.to_string(), v.clone()); + splited_profiles.insert(k.to_string(), v.clone()); }); } } - config.profiles = splitted_profiles; + config.profiles = splited_profiles; Ok(config) } @@ -84,7 +84,7 @@ impl TinyEncryptConfig { pub fn find_by_kid(&self, kid: &str) -> Option<&TinyEncryptConfigEnvelop> { let config_envelops = self.find_by_kid_or_filter(kid, |_| false); - iff!(config_envelops.is_empty(), None, Some(config_envelops[0])) + config_envelops.first().map(|e| *e) } pub fn find_by_kid_or_type(&self, k_filter: &str) -> Vec<&TinyEncryptConfigEnvelop> { diff --git a/src/crypto_cryptor.rs b/src/crypto_cryptor.rs index d4aa54e..2d6cdfa 100644 --- a/src/crypto_cryptor.rs +++ b/src/crypto_cryptor.rs @@ -5,6 +5,12 @@ use zeroize::Zeroize; use crate::{consts, util_env}; +pub struct KeyNonce<'a, 'b> { + pub k: &'a [u8], + pub n: &'b [u8], +} + + #[derive(Debug, Copy, Clone)] pub enum Cryptor { Aes256Gcm, @@ -28,12 +34,12 @@ impl Cryptor { name.to_string() } - pub fn encryptor(self, key: &[u8], nonce: &[u8]) -> XResult> { - get_encryptor(self, key, nonce) + pub fn encryptor(self, key_nonce: &KeyNonce) -> XResult> { + get_encryptor(self, key_nonce) } - pub fn decryptor(self, key: &[u8], nonce: &[u8]) -> XResult> { - get_decryptor(self, key, nonce) + pub fn decryptor(self, key_nonce: &KeyNonce) -> XResult> { + get_decryptor(self, key_nonce) } } @@ -64,34 +70,34 @@ pub trait Decryptor { } } -fn get_encryptor(crypto: Cryptor, key: &[u8], nonce: &[u8]) -> XResult> { +fn get_encryptor(crypto: Cryptor, key_nonce: &KeyNonce) -> XResult> { match crypto { Cryptor::Aes256Gcm => { - let mut key: [u8; 32] = opt_result!(key.try_into(), "Bad AES 256 key: {}"); - let aes256_gcm_stream_encryptor = Aes256GcmStreamEncryptor::new(key, nonce); + let mut key: [u8; 32] = opt_result!(key_nonce.k.try_into(), "Bad AES 256 key: {}"); + let aes256_gcm_stream_encryptor = Aes256GcmStreamEncryptor::new(key, key_nonce.n); key.zeroize(); Ok(Box::new(Aes256GcmEncryptor { aes256_gcm_stream_encryptor, })) } Cryptor::ChaCha20Poly1305 => Ok(Box::new(ChaCha20Poly1305Encryptor { - chacha20_poly1305_stream_encryptor: ChaCha20Poly1305StreamEncryptor::new(key, nonce)?, + chacha20_poly1305_stream_encryptor: ChaCha20Poly1305StreamEncryptor::new(key_nonce.k, key_nonce.n)?, })) } } -fn get_decryptor(crypto: Cryptor, key: &[u8], nonce: &[u8]) -> XResult> { +fn get_decryptor(crypto: Cryptor, key_nonce: &KeyNonce) -> XResult> { match crypto { Cryptor::Aes256Gcm => { - let mut key: [u8; 32] = opt_result!(key.try_into(), "Bad AES 256 key: {}"); - let aes256_gcm_stream_decryptor = Aes256GcmStreamDecryptor::new(key, nonce); + let mut key: [u8; 32] = opt_result!(key_nonce.k.try_into(), "Bad AES 256 key: {}"); + let aes256_gcm_stream_decryptor = Aes256GcmStreamDecryptor::new(key, key_nonce.n); key.zeroize(); Ok(Box::new(Aes256GcmDecryptor { aes256_gcm_stream_decryptor, })) } Cryptor::ChaCha20Poly1305 => Ok(Box::new(ChaCha20Poly1305Decryptor { - chacha20_poly1305_stream_decryptor: ChaCha20Poly1305StreamDecryptor::new(key, nonce)?, + chacha20_poly1305_stream_decryptor: ChaCha20Poly1305StreamDecryptor::new(key_nonce.k, key_nonce.n)?, })) } } @@ -170,9 +176,10 @@ pub fn get_cryptor_by_encryption_algorithm(encryption_algorithm: &Option fn test_cryptor() { let key = [0u8; 32]; let nonce = [0u8; 12]; - let ciphertext = Cryptor::Aes256Gcm.encryptor(&key, &nonce).unwrap() + let key_nonce = KeyNonce { k: &key, n: &nonce }; + let ciphertext = Cryptor::Aes256Gcm.encryptor(&key_nonce).unwrap() .encrypt(b"hello world"); - let plaintext = Cryptor::Aes256Gcm.decryptor(&key, &nonce).unwrap() + let plaintext = Cryptor::Aes256Gcm.decryptor(&key_nonce).unwrap() .decrypt(&ciphertext).unwrap(); assert_eq!(b"hello world", plaintext.as_slice()); diff --git a/src/crypto_simple.rs b/src/crypto_simple.rs index f27e002..170694f 100644 --- a/src/crypto_simple.rs +++ b/src/crypto_simple.rs @@ -1,26 +1,28 @@ use rust_util::XResult; -use crate::crypto_cryptor::Cryptor; +use crate::crypto_cryptor::{Cryptor, KeyNonce}; -pub fn try_decrypt_with_salt(crypto: Cryptor, key: &[u8], nonce: &[u8], salt: &[u8], message: &[u8]) -> XResult> { - let new_nonce = build_salted_nonce(nonce, salt); - if let Ok(decrypted) = decrypt(crypto, key, &new_nonce, message) { +pub fn try_decrypt_with_salt(crypto: Cryptor, key_nonce: &KeyNonce, salt: &[u8], message: &[u8]) -> XResult> { + let new_nonce = build_salted_nonce(key_nonce.n, salt); + let new_key_nonce = KeyNonce { k: key_nonce.k, n: &new_nonce }; + if let Ok(decrypted) = decrypt(crypto, &new_key_nonce, message) { return Ok(decrypted); } - decrypt(crypto, key, nonce, message) + decrypt(crypto, key_nonce, message) } -pub fn decrypt(crypto: Cryptor, key: &[u8], nonce: &[u8], message: &[u8]) -> XResult> { - crypto.decryptor(key, nonce)?.decrypt(message) +pub fn decrypt(crypto: Cryptor, key_nonce: &KeyNonce, message: &[u8]) -> XResult> { + crypto.decryptor(key_nonce)?.decrypt(message) } -pub fn encrypt_with_salt(crypto: Cryptor, key: &[u8], nonce: &[u8], salt: &[u8], message: &[u8]) -> XResult> { - let new_nonce = build_salted_nonce(nonce, salt); - encrypt(crypto, key, &new_nonce, message) +pub fn encrypt_with_salt(crypto: Cryptor, key_nonce: &KeyNonce, salt: &[u8], message: &[u8]) -> XResult> { + let new_nonce = build_salted_nonce(key_nonce.n, salt); + let new_key_nonce = KeyNonce { k: key_nonce.k, n: &new_nonce }; + encrypt(crypto, &new_key_nonce, message) } -pub fn encrypt(crypto: Cryptor, key: &[u8], nonce: &[u8], message: &[u8]) -> XResult> { - Ok(crypto.encryptor(key, nonce)?.encrypt(message)) +pub fn encrypt(crypto: Cryptor, key_nonce: &KeyNonce, message: &[u8]) -> XResult> { + Ok(crypto.encryptor(key_nonce)?.encrypt(message)) } fn build_salted_nonce(nonce: &[u8], salt: &[u8]) -> Vec { diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f1f9ada --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,41 @@ +pub use cmd_config::CmdConfig; +pub use cmd_config::config; +pub use cmd_decrypt::CmdDecrypt; +pub use cmd_decrypt::decrypt; +pub use cmd_decrypt::decrypt_single; +pub use cmd_encrypt::CmdEncrypt; +pub use cmd_encrypt::encrypt; +pub use cmd_encrypt::encrypt_single; +pub use cmd_encrypt::encrypt_single_file_out; +pub use cmd_info::CmdInfo; +pub use cmd_info::info; +pub use cmd_info::info_single; +pub use cmd_version::CmdVersion; +pub use cmd_version::version; + +mod consts; +mod util; +mod util_env; +mod util_digest; +mod util_progress; +mod util_piv; +mod util_pgp; +mod util_p256; +mod util_p384; +mod util_x25519; +mod compress; +mod config; +mod spec; +mod crypto_simple; +mod crypto_rsa; +mod crypto_cryptor; +mod wrap_key; +mod util_envelop; +mod util_file; +mod util_enc_file; +mod cmd_version; +mod cmd_config; +mod cmd_info; +mod cmd_decrypt; +mod cmd_encrypt; + diff --git a/src/main.rs b/src/main.rs index 9472054..b7889ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,37 +3,7 @@ extern crate core; use clap::{Parser, Subcommand}; use rust_util::XResult; -use crate::cmd_config::CmdConfig; -use crate::cmd_decrypt::CmdDecrypt; -use crate::cmd_encrypt::CmdEncrypt; -use crate::cmd_info::CmdInfo; -use crate::cmd_version::CmdVersion; - -mod consts; -mod util; -mod util_env; -mod util_digest; -mod util_progress; -mod util_piv; -mod util_pgp; -mod util_p256; -mod util_p384; -mod util_x25519; -mod compress; -mod config; -mod spec; -mod crypto_simple; -mod crypto_rsa; -mod crypto_cryptor; -mod wrap_key; -mod util_envelop; -mod util_file; -mod util_enc_file; -mod cmd_version; -mod cmd_config; -mod cmd_info; -mod cmd_decrypt; -mod cmd_encrypt; +use tiny_encrypt::{CmdConfig, CmdDecrypt, CmdEncrypt, CmdInfo, CmdVersion}; #[derive(Debug, Parser)] #[command(name = "tiny-encrypt-rs")] @@ -65,10 +35,10 @@ enum Commands { fn main() -> XResult<()> { let args = Cli::parse(); match args.command { - Commands::Encrypt(cmd_encrypt) => cmd_encrypt::encrypt(cmd_encrypt), - Commands::Decrypt(cmd_decrypt) => cmd_decrypt::decrypt(cmd_decrypt), - Commands::Info(cmd_info) => cmd_info::info(cmd_info), - Commands::Version(cmd_version) => cmd_version::version(cmd_version), - Commands::Config(cmd_config) => cmd_config::config(cmd_config), + Commands::Encrypt(cmd_encrypt) => tiny_encrypt::encrypt(cmd_encrypt), + Commands::Decrypt(cmd_decrypt) => tiny_encrypt::decrypt(cmd_decrypt), + Commands::Info(cmd_info) => tiny_encrypt::info(cmd_info), + Commands::Version(cmd_version) => tiny_encrypt::version(cmd_version), + Commands::Config(cmd_config) => tiny_encrypt::config(cmd_config), } } \ No newline at end of file diff --git a/src/spec.rs b/src/spec.rs index 491aa9f..eba3c7e 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use crate::{compress, crypto_simple}; use crate::consts::SALT_META; -use crate::crypto_cryptor::Cryptor; +use crate::crypto_cryptor::{Cryptor, KeyNonce}; use crate::util::{encode_base64, get_user_agent}; pub const TINY_ENCRYPT_VERSION_10: &str = "1.0"; @@ -105,21 +105,21 @@ pub struct EncEncryptedMeta { } impl EncEncryptedMeta { - pub fn unseal(crypto: Cryptor, key: &[u8], nonce: &[u8], message: &[u8]) -> XResult { + pub fn unseal(crypto: Cryptor, key_nonce: &KeyNonce, message: &[u8]) -> XResult { let mut decrypted = opt_result!(crypto_simple::try_decrypt_with_salt( - crypto, key, nonce, SALT_META, message), "Decrypt failed: {}"); + crypto, key_nonce, SALT_META, message), "Decrypt failed: {}"); decrypted = opt_result!(compress::decompress(&decrypted), "Decode faield: {}"); let meta = opt_result!( serde_json::from_slice::(&decrypted), "Parse failed: {}"); Ok(meta) } - pub fn seal(&self, crypto: Cryptor, key: &[u8], nonce: &[u8]) -> XResult> { + pub fn seal(&self, crypto: Cryptor, key_nonce: &KeyNonce) -> XResult> { let mut encrypted_meta_json = serde_json::to_vec(self).unwrap(); encrypted_meta_json = opt_result!( compress::compress(Compression::default(), &encrypted_meta_json), "Compress failed: {}"); let encrypted = opt_result!(crypto_simple::encrypt_with_salt( - crypto, key, nonce, SALT_META, encrypted_meta_json.as_slice()), "Encrypt failed: {}"); + crypto, key_nonce, SALT_META, encrypted_meta_json.as_slice()), "Encrypt failed: {}"); Ok(encrypted) } } diff --git a/src/util_enc_file.rs b/src/util_enc_file.rs index e3fac04..00749f5 100644 --- a/src/util_enc_file.rs +++ b/src/util_enc_file.rs @@ -1,6 +1,5 @@ use std::io::{Read, Write}; -use flate2::Compression; use rust_util::{debugging, iff, opt_result, simple_error, XResult}; use crate::compress; @@ -13,7 +12,7 @@ pub fn write_tiny_encrypt_meta(w: &mut impl Write, meta: &TinyEncryptMeta, compr let mut encrypted_meta_bytes = opt_result!(serde_json::to_vec(&meta), "Generate meta json bytes failed: {}"); if compress_meta { encrypted_meta_bytes = opt_result!( - compress::compress(Compression::default(), &encrypted_meta_bytes), "Compress encrypted meta failed: {}"); + compress::compress_default(&encrypted_meta_bytes), "Compress encrypted meta failed: {}"); } let encrypted_meta_bytes_len = encrypted_meta_bytes.len() as u32; debugging!("Encrypted meta len: {}", encrypted_meta_bytes_len); @@ -23,16 +22,17 @@ pub fn write_tiny_encrypt_meta(w: &mut impl Write, meta: &TinyEncryptMeta, compr Ok(encrypted_meta_bytes.len() + 2 + 4) } -pub fn read_tiny_encrypt_meta_and_normalize(r: &mut impl Read) -> XResult { - let mut meta = read_tiny_encrypt_meta(r); - let _ = meta.as_mut().map(|meta| meta.normalize()); - meta +pub fn read_tiny_encrypt_meta_and_normalize(r: &mut impl Read) -> XResult<(u32, TinyEncryptMeta)> { + let mut meta_and_len = read_tiny_encrypt_meta(r); + let _ = meta_and_len.as_mut().map(|meta| meta.1.normalize()); + meta_and_len } -pub fn read_tiny_encrypt_meta(r: &mut impl Read) -> XResult { +pub fn read_tiny_encrypt_meta(r: &mut impl Read) -> XResult<(u32, TinyEncryptMeta)> { let mut tag_buff = [0_u8; 2]; opt_result!(r.read_exact(&mut tag_buff), "Read tag failed: {}"); let tag = u16::from_be_bytes(tag_buff); + debugging!("Found tag: {}", tag); let is_normal_tiny_enc = tag == TINY_ENC_MAGIC_TAG; let is_compressed_tiny_enc = tag == TINY_ENC_COMPRESSED_MAGIC_TAG; if !is_normal_tiny_enc && !is_compressed_tiny_enc { @@ -57,5 +57,5 @@ pub fn read_tiny_encrypt_meta(r: &mut impl Read) -> XResult { } debugging!("Encrypted meta: {}", String::from_utf8_lossy(&meta_buff)); - Ok(opt_result!(serde_json::from_slice(&meta_buff), "Parse meta failed: {}")) + Ok((length, opt_result!(serde_json::from_slice(&meta_buff), "Parse meta failed: {}"))) } diff --git a/src/util_env.rs b/src/util_env.rs index 3f7b93e..10a12b6 100644 --- a/src/util_env.rs +++ b/src/util_env.rs @@ -1,15 +1,18 @@ use std::env; +use rust_util::{debugging, warning}; + use crate::consts; pub fn get_default_encryption_algorithm() -> Option<&'static str> { let env_default_algorithm = env::var(consts::TINY_ENCRYPT_ENV_DEFAULT_ALGORITHM).ok(); + debugging!("Environment variable {} = {:?}", consts::TINY_ENCRYPT_ENV_DEFAULT_ALGORITHM, env_default_algorithm); if let Some(env_algorithm) = env_default_algorithm { let lower_default_algorithm = env_algorithm.to_lowercase(); match lower_default_algorithm.as_str() { "aes" | "aes/gcm" => return Some(consts::TINY_ENC_AES_GCM), "chacha20" | "chacha20/poly1305" => return Some(consts::TINY_ENC_CHACHA20_POLY1305), - _ => {} + _ => { warning!("Not matched any algorithm: {}", env_algorithm); } } } None