feat: v0.3.4, encrypt supports --key-filter
This commit is contained in:
@@ -16,7 +16,7 @@ use yubikey::piv::{AlgorithmId, decrypt_data};
|
||||
use yubikey::YubiKey;
|
||||
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::config::TinyEncryptConfig;
|
||||
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()];
|
||||
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 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
None => return simple_error!("No envelops found"),
|
||||
Some(envelops) => if envelops.is_empty() {
|
||||
@@ -310,21 +310,13 @@ fn select_envelop(meta: &TinyEncryptMeta) -> XResult<&TinyEncryptEnvelop> {
|
||||
success!("Found {} envelops:", envelops.len());
|
||||
if envelops.len() == 1 {
|
||||
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: ");
|
||||
return Ok(selected_envelop);
|
||||
}
|
||||
|
||||
envelops.iter().enumerate().for_each(|(i, envelop)| {
|
||||
let kid = iff!(envelop.kid.is_empty(), "".into(), format!(", Kid: {}", envelop.kid));
|
||||
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,
|
||||
);
|
||||
println!("#{} {}", i + 1, util_envelop::format_envelop(envelop, &config));
|
||||
});
|
||||
|
||||
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 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::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};
|
||||
@@ -24,22 +24,25 @@ use crate::wrap_key::{WrapKey, WrapKeyHeader};
|
||||
pub struct CmdEncrypt {
|
||||
/// Files need to be decrypted
|
||||
pub paths: Vec<PathBuf>,
|
||||
/// Comment
|
||||
/// Plaintext comment
|
||||
#[arg(long, short = 'c')]
|
||||
pub comment: Option<String>,
|
||||
/// Encrypted comment
|
||||
#[arg(long, short = 'C')]
|
||||
pub encrypted_comment: Option<String>,
|
||||
/// Encryption profile
|
||||
/// Encryption profile (use default when --key-filter is assigned)
|
||||
#[arg(long, short = 'p')]
|
||||
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
|
||||
#[arg(long, short = 'x')]
|
||||
pub compress: bool,
|
||||
/// Compress level (from 0[none], 1[fast] .. 6[default] .. to 9[best])
|
||||
#[arg(long, short = 'L')]
|
||||
pub compress_level: Option<u32>,
|
||||
/// Compatible with 1.0
|
||||
/// Compatible with 1.0 (requires assign --disable-compress-meta)
|
||||
#[arg(long, short = '1')]
|
||||
pub compatible_with_1_0: bool,
|
||||
/// Remove source file
|
||||
@@ -53,7 +56,7 @@ pub struct CmdEncrypt {
|
||||
pub fn encrypt(cmd_encrypt: CmdEncrypt) -> XResult<()> {
|
||||
let config = TinyEncryptConfig::load(TINY_ENC_CONFIG_FILE)?;
|
||||
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"); }
|
||||
debugging!("Found envelops: {:?}", envelops);
|
||||
let envelop_tkids: Vec<_> = envelops.iter()
|
||||
@@ -306,7 +309,7 @@ fn encrypt_envelop_shared_secret(key: &[u8],
|
||||
Ok(TinyEncryptEnvelop {
|
||||
r#type: envelop.r#type,
|
||||
kid: envelop.kid.clone(),
|
||||
desc: envelop.desc.clone(),
|
||||
desc: None, // envelop.desc.clone(),
|
||||
encrypted_key: encoded_wrap_key,
|
||||
})
|
||||
}
|
||||
@@ -318,7 +321,7 @@ fn encrypt_envelop_pgp(key: &[u8], envelop: &TinyEncryptConfigEnvelop) -> XResul
|
||||
Ok(TinyEncryptEnvelop {
|
||||
r#type: envelop.r#type,
|
||||
kid: envelop.kid.clone(),
|
||||
desc: envelop.desc.clone(),
|
||||
desc: None, // envelop.desc.clone(),
|
||||
encrypted_key: util::encode_base64(&encrypted_key),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,12 +4,13 @@ use std::path::PathBuf;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
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 simpledateformat::format_human2;
|
||||
|
||||
use crate::consts::{DATE_TIME_FORMAT, TINY_ENC_AES_GCM, TINY_ENC_FILE_EXT};
|
||||
use crate::util_enc_file;
|
||||
use crate::{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};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
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);
|
||||
}
|
||||
|
||||
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!(
|
||||
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() {
|
||||
envelops.iter().enumerate().for_each(|(i, envelop)| {
|
||||
let kid = iff!(envelop.kid.is_empty(), "".into(), format!(", Kid: {}", envelop.kid));
|
||||
let desc = envelop.desc.as_ref().map(|desc| format!(", Desc: {}", desc)).unwrap_or_else(|| "".to_string());
|
||||
infos.push(format!("{}: {}{}{}",
|
||||
infos.push(format!("{}: {}",
|
||||
header(&format!("Envelop #{}", i + 1)),
|
||||
envelop.r#type.get_upper_name(),
|
||||
kid,
|
||||
desc
|
||||
util_envelop::format_envelop(envelop, &config)
|
||||
));
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ pub struct TinyEncryptConfig {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TinyEncryptConfigEnvelop {
|
||||
pub r#type: TinyEncryptEnvelopType,
|
||||
pub sid: Option<String>,
|
||||
pub kid: String,
|
||||
pub desc: Option<String>,
|
||||
pub args: Option<Vec<String>>,
|
||||
@@ -81,32 +82,66 @@ impl TinyEncryptConfig {
|
||||
}
|
||||
|
||||
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>> {
|
||||
let profile = profile.as_ref().map(String::as_str).unwrap_or("default");
|
||||
debugging!("Profile: {}", profile);
|
||||
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_type(&self, k_filter: &str) -> Vec<&TinyEncryptConfigEnvelop> {
|
||||
self.find_by_kid_or_filter(k_filter, |e| {
|
||||
k_filter == &format!("type:{}", &e.r#type.get_name())
|
||||
})
|
||||
}
|
||||
|
||||
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 {
|
||||
self.envelops.iter().for_each(|envelop| {
|
||||
let is_matched = (&envelop.kid == key_id)
|
||||
|| key_id == &format!("type:{}", &envelop.r#type.get_name());
|
||||
if is_matched {
|
||||
matched_envelops_map.insert(&envelop.kid, envelop);
|
||||
}
|
||||
});
|
||||
if let Some(sid) = &e.sid {
|
||||
return sid == kid;
|
||||
}
|
||||
f(e)
|
||||
}).collect()
|
||||
}
|
||||
|
||||
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()
|
||||
.copied()
|
||||
.collect();
|
||||
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| {
|
||||
if e1.r#type < e2.r#type { return Ordering::Greater; }
|
||||
|
||||
@@ -22,6 +22,7 @@ mod spec;
|
||||
mod crypto_aes;
|
||||
mod crypto_rsa;
|
||||
mod wrap_key;
|
||||
mod util_envelop;
|
||||
mod util_file;
|
||||
mod util_enc_file;
|
||||
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