feat: v0.3.4, encrypt supports --key-filter
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2144,7 +2144,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiny-encrypt"
|
name = "tiny-encrypt"
|
||||||
version = "0.3.3"
|
version = "0.3.4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm-stream",
|
"aes-gcm-stream",
|
||||||
"base64",
|
"base64",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "tiny-encrypt"
|
name = "tiny-encrypt"
|
||||||
version = "0.3.3"
|
version = "0.3.4"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "A simple and tiny file encrypt tool"
|
description = "A simple and tiny file encrypt tool"
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use yubikey::piv::{AlgorithmId, decrypt_data};
|
|||||||
use yubikey::YubiKey;
|
use yubikey::YubiKey;
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use crate::{util, util_enc_file, util_file, util_pgp, util_piv};
|
use crate::{util, util_enc_file, util_envelop, util_file, util_pgp, util_piv};
|
||||||
use crate::compress::GzStreamDecoder;
|
use crate::compress::GzStreamDecoder;
|
||||||
use crate::config::TinyEncryptConfig;
|
use crate::config::TinyEncryptConfig;
|
||||||
use crate::consts::{
|
use crate::consts::{
|
||||||
@@ -99,7 +99,7 @@ pub fn decrypt_single(config: &Option<TinyEncryptConfig>,
|
|||||||
let path_out = &path_display[0..path_display.len() - TINY_ENC_FILE_EXT.len()];
|
let path_out = &path_display[0..path_display.len() - TINY_ENC_FILE_EXT.len()];
|
||||||
util::require_file_not_exists(path_out)?;
|
util::require_file_not_exists(path_out)?;
|
||||||
|
|
||||||
let selected_envelop = select_envelop(&meta)?;
|
let selected_envelop = select_envelop(&meta, config)?;
|
||||||
|
|
||||||
let key = try_decrypt_key(config, selected_envelop, pin, slot)?;
|
let key = try_decrypt_key(config, selected_envelop, pin, slot)?;
|
||||||
let nonce = opt_result!(util::decode_base64(&meta.nonce), "Decode nonce failed: {}");
|
let nonce = opt_result!(util::decode_base64(&meta.nonce), "Decode nonce failed: {}");
|
||||||
@@ -297,7 +297,7 @@ fn try_decrypt_key_pgp(envelop: &TinyEncryptEnvelop, pin: &Option<String>) -> XR
|
|||||||
Ok(key)
|
Ok(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_envelop(meta: &TinyEncryptMeta) -> XResult<&TinyEncryptEnvelop> {
|
fn select_envelop<'a>(meta: &'a TinyEncryptMeta, config: &Option<TinyEncryptConfig>) -> XResult<&'a TinyEncryptEnvelop> {
|
||||||
let envelops = match &meta.envelops {
|
let envelops = match &meta.envelops {
|
||||||
None => return simple_error!("No envelops found"),
|
None => return simple_error!("No envelops found"),
|
||||||
Some(envelops) => if envelops.is_empty() {
|
Some(envelops) => if envelops.is_empty() {
|
||||||
@@ -310,21 +310,13 @@ fn select_envelop(meta: &TinyEncryptMeta) -> XResult<&TinyEncryptEnvelop> {
|
|||||||
success!("Found {} envelops:", envelops.len());
|
success!("Found {} envelops:", envelops.len());
|
||||||
if envelops.len() == 1 {
|
if envelops.len() == 1 {
|
||||||
let selected_envelop = &envelops[0];
|
let selected_envelop = &envelops[0];
|
||||||
success!("Auto selected envelop: #{} {}", 1, selected_envelop.r#type.get_upper_name());
|
success!("Auto selected envelop: #{} {}", 1, util_envelop::format_envelop(selected_envelop, &config));
|
||||||
util::read_line("Press enter to continue: ");
|
util::read_line("Press enter to continue: ");
|
||||||
return Ok(selected_envelop);
|
return Ok(selected_envelop);
|
||||||
}
|
}
|
||||||
|
|
||||||
envelops.iter().enumerate().for_each(|(i, envelop)| {
|
envelops.iter().enumerate().for_each(|(i, envelop)| {
|
||||||
let kid = iff!(envelop.kid.is_empty(), "".into(), format!(", Kid: {}", envelop.kid));
|
println!("#{} {}", i + 1, util_envelop::format_envelop(envelop, &config));
|
||||||
let desc = envelop.desc.as_ref()
|
|
||||||
.map(|desc| format!(", Desc: {}", desc))
|
|
||||||
.unwrap_or_else(|| "".to_string());
|
|
||||||
println!("#{} {}{}{}", i + 1,
|
|
||||||
envelop.r#type.get_upper_name(),
|
|
||||||
kid,
|
|
||||||
desc,
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let envelop_number = util::read_number("Please select an envelop:", 1, envelops.len());
|
let envelop_number = util::read_number("Please select an envelop:", 1, envelops.len());
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use rust_util::{debugging, failure, iff, information, opt_result, simple_error,
|
|||||||
use rust_util::util_time::UnixEpochTime;
|
use rust_util::util_time::UnixEpochTime;
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use crate::{util_enc_file, util, util_ecdh, util_p384, util_x25519};
|
use crate::{util, util_ecdh, util_enc_file, util_p384, util_x25519};
|
||||||
use crate::compress::GzStreamEncoder;
|
use crate::compress::GzStreamEncoder;
|
||||||
use crate::config::{TinyEncryptConfig, TinyEncryptConfigEnvelop};
|
use crate::config::{TinyEncryptConfig, TinyEncryptConfigEnvelop};
|
||||||
use crate::consts::{ENC_AES256_GCM_P256, ENC_AES256_GCM_P384, ENC_AES256_GCM_X25519, SALT_COMMENT, TINY_ENC_CONFIG_FILE, TINY_ENC_FILE_EXT};
|
use crate::consts::{ENC_AES256_GCM_P256, ENC_AES256_GCM_P384, ENC_AES256_GCM_X25519, SALT_COMMENT, TINY_ENC_CONFIG_FILE, TINY_ENC_FILE_EXT};
|
||||||
@@ -24,22 +24,25 @@ use crate::wrap_key::{WrapKey, WrapKeyHeader};
|
|||||||
pub struct CmdEncrypt {
|
pub struct CmdEncrypt {
|
||||||
/// Files need to be decrypted
|
/// Files need to be decrypted
|
||||||
pub paths: Vec<PathBuf>,
|
pub paths: Vec<PathBuf>,
|
||||||
/// Comment
|
/// Plaintext comment
|
||||||
#[arg(long, short = 'c')]
|
#[arg(long, short = 'c')]
|
||||||
pub comment: Option<String>,
|
pub comment: Option<String>,
|
||||||
/// Encrypted comment
|
/// Encrypted comment
|
||||||
#[arg(long, short = 'C')]
|
#[arg(long, short = 'C')]
|
||||||
pub encrypted_comment: Option<String>,
|
pub encrypted_comment: Option<String>,
|
||||||
/// Encryption profile
|
/// Encryption profile (use default when --key-filter is assigned)
|
||||||
#[arg(long, short = 'p')]
|
#[arg(long, short = 'p')]
|
||||||
pub profile: Option<String>,
|
pub profile: Option<String>,
|
||||||
|
/// Encryption key filter (key_id or type:TYPE(e.g. ecdh, pgp, ecdh-p384, pgp-ed25519), multiple joined by ',')
|
||||||
|
#[arg(long, short = 'k')]
|
||||||
|
pub key_filter: Option<String>,
|
||||||
/// Compress before encrypt
|
/// Compress before encrypt
|
||||||
#[arg(long, short = 'x')]
|
#[arg(long, short = 'x')]
|
||||||
pub compress: bool,
|
pub compress: bool,
|
||||||
/// Compress level (from 0[none], 1[fast] .. 6[default] .. to 9[best])
|
/// Compress level (from 0[none], 1[fast] .. 6[default] .. to 9[best])
|
||||||
#[arg(long, short = 'L')]
|
#[arg(long, short = 'L')]
|
||||||
pub compress_level: Option<u32>,
|
pub compress_level: Option<u32>,
|
||||||
/// Compatible with 1.0
|
/// Compatible with 1.0 (requires assign --disable-compress-meta)
|
||||||
#[arg(long, short = '1')]
|
#[arg(long, short = '1')]
|
||||||
pub compatible_with_1_0: bool,
|
pub compatible_with_1_0: bool,
|
||||||
/// Remove source file
|
/// Remove source file
|
||||||
@@ -53,7 +56,7 @@ pub struct CmdEncrypt {
|
|||||||
pub fn encrypt(cmd_encrypt: CmdEncrypt) -> XResult<()> {
|
pub fn encrypt(cmd_encrypt: CmdEncrypt) -> XResult<()> {
|
||||||
let config = TinyEncryptConfig::load(TINY_ENC_CONFIG_FILE)?;
|
let config = TinyEncryptConfig::load(TINY_ENC_CONFIG_FILE)?;
|
||||||
debugging!("Found tiny encrypt config: {:?}", config);
|
debugging!("Found tiny encrypt config: {:?}", config);
|
||||||
let envelops = config.find_envelops(&cmd_encrypt.profile)?;
|
let envelops = config.find_envelops(&cmd_encrypt.profile, &cmd_encrypt.key_filter)?;
|
||||||
if envelops.is_empty() { return simple_error!("Cannot find any valid envelops"); }
|
if envelops.is_empty() { return simple_error!("Cannot find any valid envelops"); }
|
||||||
debugging!("Found envelops: {:?}", envelops);
|
debugging!("Found envelops: {:?}", envelops);
|
||||||
let envelop_tkids: Vec<_> = envelops.iter()
|
let envelop_tkids: Vec<_> = envelops.iter()
|
||||||
@@ -306,7 +309,7 @@ fn encrypt_envelop_shared_secret(key: &[u8],
|
|||||||
Ok(TinyEncryptEnvelop {
|
Ok(TinyEncryptEnvelop {
|
||||||
r#type: envelop.r#type,
|
r#type: envelop.r#type,
|
||||||
kid: envelop.kid.clone(),
|
kid: envelop.kid.clone(),
|
||||||
desc: envelop.desc.clone(),
|
desc: None, // envelop.desc.clone(),
|
||||||
encrypted_key: encoded_wrap_key,
|
encrypted_key: encoded_wrap_key,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -318,7 +321,7 @@ fn encrypt_envelop_pgp(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResul
|
|||||||
Ok(TinyEncryptEnvelop {
|
Ok(TinyEncryptEnvelop {
|
||||||
r#type: envelop.r#type,
|
r#type: envelop.r#type,
|
||||||
kid: envelop.kid.clone(),
|
kid: envelop.kid.clone(),
|
||||||
desc: envelop.desc.clone(),
|
desc: None, // envelop.desc.clone(),
|
||||||
encrypted_key: util::encode_base64(&encrypted_key),
|
encrypted_key: util::encode_base64(&encrypted_key),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,13 @@ use std::path::PathBuf;
|
|||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
|
||||||
use clap::Args;
|
use clap::Args;
|
||||||
use rust_util::{util_time, iff, opt_result, simple_error, success, warning, XResult};
|
use rust_util::{opt_result, simple_error, success, util_time, warning, XResult};
|
||||||
use rust_util::util_time::UnixEpochTime;
|
use rust_util::util_time::UnixEpochTime;
|
||||||
use simpledateformat::format_human2;
|
use simpledateformat::format_human2;
|
||||||
|
|
||||||
use crate::consts::{DATE_TIME_FORMAT, TINY_ENC_AES_GCM, TINY_ENC_FILE_EXT};
|
use crate::{util_enc_file, util_envelop};
|
||||||
use crate::util_enc_file;
|
use crate::config::TinyEncryptConfig;
|
||||||
|
use crate::consts::{DATE_TIME_FORMAT, TINY_ENC_AES_GCM, TINY_ENC_CONFIG_FILE, TINY_ENC_FILE_EXT};
|
||||||
|
|
||||||
#[derive(Debug, Args)]
|
#[derive(Debug, Args)]
|
||||||
pub struct CmdInfo {
|
pub struct CmdInfo {
|
||||||
@@ -37,6 +38,7 @@ pub fn info_single(path: &PathBuf, cmd_info: &CmdInfo) -> XResult<()> {
|
|||||||
return simple_error!("Not a Tiny Encrypt file: {}", path_display);
|
return simple_error!("Not a Tiny Encrypt file: {}", path_display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let config = TinyEncryptConfig::load(TINY_ENC_CONFIG_FILE).ok();
|
||||||
let mut file_in = opt_result!(File::open(path), "Open file: {} failed: {}", &path_display);
|
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
|
util_enc_file::read_tiny_encrypt_meta_and_normalize(&mut file_in), "Read file: {}, failed: {}", &path_display
|
||||||
@@ -71,13 +73,9 @@ pub fn info_single(path: &PathBuf, cmd_info: &CmdInfo) -> XResult<()> {
|
|||||||
|
|
||||||
if let Some(envelops) = meta.envelops.as_ref() {
|
if let Some(envelops) = meta.envelops.as_ref() {
|
||||||
envelops.iter().enumerate().for_each(|(i, envelop)| {
|
envelops.iter().enumerate().for_each(|(i, envelop)| {
|
||||||
let kid = iff!(envelop.kid.is_empty(), "".into(), format!(", Kid: {}", envelop.kid));
|
infos.push(format!("{}: {}",
|
||||||
let desc = envelop.desc.as_ref().map(|desc| format!(", Desc: {}", desc)).unwrap_or_else(|| "".to_string());
|
|
||||||
infos.push(format!("{}: {}{}{}",
|
|
||||||
header(&format!("Envelop #{}", i + 1)),
|
header(&format!("Envelop #{}", i + 1)),
|
||||||
envelop.r#type.get_upper_name(),
|
util_envelop::format_envelop(envelop, &config)
|
||||||
kid,
|
|
||||||
desc
|
|
||||||
));
|
));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ pub struct TinyEncryptConfig {
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct TinyEncryptConfigEnvelop {
|
pub struct TinyEncryptConfigEnvelop {
|
||||||
pub r#type: TinyEncryptEnvelopType,
|
pub r#type: TinyEncryptEnvelopType,
|
||||||
|
pub sid: Option<String>,
|
||||||
pub kid: String,
|
pub kid: String,
|
||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
pub args: Option<Vec<String>>,
|
pub args: Option<Vec<String>>,
|
||||||
@@ -81,32 +82,66 @@ impl TinyEncryptConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_by_kid(&self, kid: &str) -> Option<&TinyEncryptConfigEnvelop> {
|
pub fn find_by_kid(&self, kid: &str) -> Option<&TinyEncryptConfigEnvelop> {
|
||||||
self.envelops.iter().find(|e| e.kid == kid)
|
let config_envelops = self.find_by_kid_or_filter(kid, |_| false);
|
||||||
|
if config_envelops.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(config_envelops[0])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_envelops(&self, profile: &Option<String>) -> XResult<Vec<&TinyEncryptConfigEnvelop>> {
|
pub fn find_by_kid_or_type(&self, k_filter: &str) -> Vec<&TinyEncryptConfigEnvelop> {
|
||||||
let profile = profile.as_ref().map(String::as_str).unwrap_or("default");
|
self.find_by_kid_or_filter(k_filter, |e| {
|
||||||
debugging!("Profile: {}", profile);
|
k_filter == &format!("type:{}", &e.r#type.get_name())
|
||||||
let mut matched_envelops_map = HashMap::new();
|
})
|
||||||
if let Some(key_ids) = self.profiles.get(profile) {
|
}
|
||||||
if key_ids.is_empty() {
|
|
||||||
return simple_error!("Profile: {} contains no valid envelopes", profile);
|
pub fn find_by_kid_or_filter<F>(&self, kid: &str, f: F) -> Vec<&TinyEncryptConfigEnvelop>
|
||||||
|
where F: Fn(&TinyEncryptConfigEnvelop) -> bool {
|
||||||
|
self.envelops.iter().filter(|e| {
|
||||||
|
if e.kid == kid {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
for key_id in key_ids {
|
if let Some(sid) = &e.sid {
|
||||||
self.envelops.iter().for_each(|envelop| {
|
return sid == kid;
|
||||||
let is_matched = (&envelop.kid == key_id)
|
}
|
||||||
|| key_id == &format!("type:{}", &envelop.r#type.get_name());
|
f(e)
|
||||||
if is_matched {
|
}).collect()
|
||||||
matched_envelops_map.insert(&envelop.kid, envelop);
|
}
|
||||||
}
|
|
||||||
});
|
pub fn find_envelops(&self, profile: &Option<String>, key_filter: &Option<String>) -> XResult<Vec<&TinyEncryptConfigEnvelop>> {
|
||||||
|
debugging!("Profile: {:?}", profile);
|
||||||
|
debugging!("Key filter: {:?}", key_filter);
|
||||||
|
let mut matched_envelops_map = HashMap::new();
|
||||||
|
let mut key_ids = vec![];
|
||||||
|
if key_filter.is_none() || profile.is_some() {
|
||||||
|
let profile = profile.as_ref().map(String::as_str).unwrap_or("default");
|
||||||
|
if let Some(kids) = self.profiles.get(profile) {
|
||||||
|
kids.iter().for_each(|k| key_ids.push(k.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if let Some(key_filter) = key_filter {
|
||||||
|
key_filter.split(",").for_each(|k| {
|
||||||
|
let k = k.trim();
|
||||||
|
if !k.is_empty() {
|
||||||
|
key_ids.push(k.to_string());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if key_ids.is_empty() {
|
||||||
|
return simple_error!("Profile or key filter cannot find valid envelopes");
|
||||||
|
}
|
||||||
|
for key_id in &key_ids {
|
||||||
|
for envelop in self.find_by_kid_or_type(key_id) {
|
||||||
|
matched_envelops_map.insert(&envelop.kid, envelop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut envelops: Vec<_> = matched_envelops_map.values()
|
let mut envelops: Vec<_> = matched_envelops_map.values()
|
||||||
.copied()
|
.copied()
|
||||||
.collect();
|
.collect();
|
||||||
if envelops.is_empty() {
|
if envelops.is_empty() {
|
||||||
return simple_error!("Profile: {} has no valid envelopes found", profile);
|
return simple_error!("Profile or key filter cannot find valid envelopes");
|
||||||
}
|
}
|
||||||
envelops.sort_by(|e1, e2| {
|
envelops.sort_by(|e1, e2| {
|
||||||
if e1.r#type < e2.r#type { return Ordering::Greater; }
|
if e1.r#type < e2.r#type { return Ordering::Greater; }
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ mod spec;
|
|||||||
mod crypto_aes;
|
mod crypto_aes;
|
||||||
mod crypto_rsa;
|
mod crypto_rsa;
|
||||||
mod wrap_key;
|
mod wrap_key;
|
||||||
|
mod util_envelop;
|
||||||
mod util_file;
|
mod util_file;
|
||||||
mod util_enc_file;
|
mod util_enc_file;
|
||||||
mod cmd_version;
|
mod cmd_version;
|
||||||
|
|||||||
25
src/util_envelop.rs
Normal file
25
src/util_envelop.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use rust_util::iff;
|
||||||
|
|
||||||
|
use crate::config::TinyEncryptConfig;
|
||||||
|
use crate::spec::TinyEncryptEnvelop;
|
||||||
|
|
||||||
|
pub fn format_envelop(envelop: &TinyEncryptEnvelop, config: &Option<TinyEncryptConfig>) -> String {
|
||||||
|
let kid = iff!(envelop.kid.is_empty(), "".into(), format!(", Kid: {}", envelop.kid));
|
||||||
|
let envelop_desc = get_envelop_desc(&kid, envelop, &config);
|
||||||
|
let desc = envelop_desc.as_ref()
|
||||||
|
.map(|desc| format!(", Desc: {}", desc))
|
||||||
|
.unwrap_or_else(|| "".to_string());
|
||||||
|
format!("{}{}{}", envelop.r#type.get_upper_name(), kid, desc)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_envelop_desc(kid: &str, envelop: &TinyEncryptEnvelop, config: &Option<TinyEncryptConfig>) -> Option<String> {
|
||||||
|
if let Some(desc) = &envelop.desc {
|
||||||
|
return Some(desc.to_string());
|
||||||
|
}
|
||||||
|
if let Some(config) = config {
|
||||||
|
if let Some(config_envelop) = config.find_by_kid(kid) {
|
||||||
|
return config_envelop.desc.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user