From 14a41586b62afb533f4fa7a4e336c96af5d1c79d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABtan=20Lehmann?= Date: Sun, 4 Feb 2024 11:17:41 +0100 Subject: [PATCH] remove util module that's more the core of the lib --- src/decrypt.rs | 2 +- src/edit.rs | 2 +- src/encrypt.rs | 2 +- src/env.rs | 2 +- src/keygen.rs | 2 +- src/lib.rs | 223 ++++++++++++++++++++++++++++++++++++++++++++++++- src/pubkey.rs | 2 +- src/util.rs | 221 ------------------------------------------------ 8 files changed, 228 insertions(+), 228 deletions(-) delete mode 100644 src/util.rs diff --git a/src/decrypt.rs b/src/decrypt.rs index 5e02e90..d502c8e 100644 --- a/src/decrypt.rs +++ b/src/decrypt.rs @@ -2,7 +2,7 @@ use serde_yaml as sy; use crate::cli::DecryptArgs; use crate::error::Result; -use crate::util::{decrypt_yaml, load_identities, stdin_or_file, stdout_or_file}; +use crate::{decrypt_yaml, load_identities, stdin_or_file, stdout_or_file}; pub fn decrypt(args: &DecryptArgs) -> Result<()> { let identities = load_identities(&args.keys, &args.key_files)?; diff --git a/src/edit.rs b/src/edit.rs index bc4670b..e847e00 100644 --- a/src/edit.rs +++ b/src/edit.rs @@ -8,7 +8,7 @@ use treediff::Mutable; use crate::cli::EditArgs; use crate::error::{IOResultExt, Result, YageError}; -use crate::util::{decrypt_yaml, encrypt_yaml, load_identities, load_recipients}; +use crate::{decrypt_yaml, encrypt_yaml, load_identities, load_recipients}; pub fn edit(args: &EditArgs) -> Result<()> { let identities = load_identities(&args.keys, &args.key_files)?; diff --git a/src/encrypt.rs b/src/encrypt.rs index 61291a6..a1f8066 100644 --- a/src/encrypt.rs +++ b/src/encrypt.rs @@ -2,7 +2,7 @@ use serde_yaml as sy; use crate::cli::EncryptArgs; use crate::error::Result; -use crate::util::{encrypt_yaml, load_recipients, stdin_or_file, stdout_or_file}; +use crate::{encrypt_yaml, load_recipients, stdin_or_file, stdout_or_file}; pub fn encrypt(args: &EncryptArgs) -> Result<()> { let recipients = load_recipients(&args.recipients, &args.recipient_files)?; diff --git a/src/env.rs b/src/env.rs index c0b075e..5ba1fb9 100644 --- a/src/env.rs +++ b/src/env.rs @@ -5,7 +5,7 @@ use serde_yaml as sy; use crate::cli::EnvArgs; use crate::error::{Result, YageError}; -use crate::util::{decrypt_yaml, load_identities, stdin_or_file}; +use crate::{decrypt_yaml, load_identities, stdin_or_file}; pub fn env(args: &EnvArgs) -> Result<()> { let identities = load_identities(&args.keys, &args.key_files)?; diff --git a/src/keygen.rs b/src/keygen.rs index 3ef1c23..4eab609 100644 --- a/src/keygen.rs +++ b/src/keygen.rs @@ -3,7 +3,7 @@ use age::x25519::Identity; use crate::cli::KeygenArgs; use crate::error::{IOResultExt, Result}; -use crate::util::{stdout_or_file, stdout_or_private_file}; +use crate::{stdout_or_file, stdout_or_private_file}; pub fn keygen(args: &KeygenArgs) -> Result<()> { let key = Identity::generate(); diff --git a/src/lib.rs b/src/lib.rs index 66a4425..abf36de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,4 +9,225 @@ pub mod env; pub mod error; pub mod keygen; pub mod pubkey; -pub mod util; + +use std::fs::{File, OpenOptions}; +use std::io::{stdin, stdout, BufRead, BufReader, Read, Write}; +#[cfg(unix)] +use std::os::unix::fs::OpenOptionsExt; +use std::path::Path; +use std::path::PathBuf; +use std::str::FromStr; + +use age::x25519; +use base64::prelude::*; +use path_absolutize::Absolutize; +use serde_yaml as sy; +use substring::Substring; + +use crate::error::{IOResultExt, Result, YageError}; + +pub fn stdout_or_file(path: &Path) -> Result> { + Ok(if path == Path::new("-") { + Box::new(stdout()) + } else { + Box::new(File::create(path).path_ctx(path)?) + }) +} + +pub fn stdout_or_private_file(path: &Path) -> Result> { + Ok(if path == Path::new("-") { + Box::new(stdout()) + } else { + // make sure the directory is private + let real_path = path.absolutize().path_ctx(path)?; + let dir = real_path.parent().ok_or(YageError::InvalidFileName { + path: path.to_owned(), + })?; + if let Err(e) = fs_mistrust::Mistrust::new().check_directory(dir) { + warn!("directory {dir:?} is not private: {e}"); + } + let mut file_opts = OpenOptions::new(); + file_opts.write(true).create_new(true); + #[cfg(unix)] + file_opts.mode(0o600); + Box::new(file_opts.open(path).path_ctx(path)?) + }) +} + +pub fn stdin_or_file(path: &Path) -> Result>> { + Ok(if path == Path::new("-") { + BufReader::new(Box::new(stdin())) + } else { + BufReader::new(Box::new(File::open(path).path_ctx(path)?)) + }) +} + +pub fn stdin_or_private_file(path: &Path) -> Result>> { + Ok(if path == Path::new("-") { + BufReader::new(Box::new(stdin())) + } else { + if let Err(e) = fs_mistrust::Mistrust::new() + .verifier() + .require_file() + .check(path) + { + warn!("file {path:?} is not private: {e}"); + } + BufReader::new(Box::new(File::open(path).path_ctx(path)?)) + }) +} + +pub fn is_yage_encoded(s: &str) -> bool { + s.starts_with("yage[") && s.ends_with(']') +} + +pub fn decrypt_yaml(value: &sy::Value, identities: &[x25519::Identity]) -> Result { + match value { + sy::Value::Mapping(mapping) => { + let mut output = sy::Mapping::new(); + for (key, value) in mapping { + let key = key.clone(); + let value = decrypt_yaml(value, identities)?; + output.insert(key, value); + } + Ok(sy::Value::Mapping(output)) + } + sy::Value::Sequence(sequence) => { + let mut output = Vec::new(); + for value in sequence { + let value = decrypt_yaml(value, identities)?; + output.push(value); + } + Ok(sy::Value::Sequence(output)) + } + sy::Value::String(encrypted) => { + let decrypted = decrypt_value(encrypted, identities)?; + Ok(decrypted) + } + _ => Ok(value.clone()), + } +} + +pub fn decrypt_value(s: &str, identities: &[x25519::Identity]) -> Result { + if is_yage_encoded(s) { + // remove the yage[…] prefix and suffix + let encoded = s.substring(5, s.len() - 1); + let encrypted = BASE64_STANDARD.decode(encoded)?; + let decryptor = match age::Decryptor::new(&encrypted[..])? { + age::Decryptor::Recipients(d) => Ok(d), + _ => Err(YageError::PassphraseUnsupported), + }?; + let mut decrypted = vec![]; + let mut reader = decryptor.decrypt(identities.iter().map(|i| i as &dyn age::Identity))?; + reader.read_to_end(&mut decrypted)?; + let value: sy::Value = sy::from_slice(&decrypted)?; + Ok(value) + } else { + Ok(sy::Value::String(s.to_owned())) + } +} + +pub fn load_identities(keys: &[String], key_files: &[PathBuf]) -> Result> { + let mut identities: Vec = Vec::new(); + for key in keys.iter() { + debug!("loading key: {key}"); + let key = x25519::Identity::from_str(key) + .map_err(|e| YageError::KeyParse { message: e.into() })?; + identities.push(key); + } + for key_file in key_files.iter() { + debug!("loading key file: {key_file:?}"); + let input = stdin_or_private_file(key_file)?; + let keys = age::IdentityFile::from_buffer(input).path_ctx(key_file)?; + for key in keys.into_identities() { + let age::IdentityFileEntry::Native(key) = key; + identities.push(key); + } + } + Ok(identities) +} + +pub fn encrypt_yaml(value: &sy::Value, recipients: &[x25519::Recipient]) -> Result { + match value { + sy::Value::Mapping(mapping) => { + let mut output = sy::Mapping::new(); + for (key, value) in mapping { + let key = key.clone(); + let value = encrypt_yaml(value, recipients)?; + output.insert(key, value); + } + Ok(sy::Value::Mapping(output)) + } + sy::Value::Sequence(sequence) => { + let mut output = Vec::new(); + for value in sequence { + let value = encrypt_yaml(value, recipients)?; + output.push(value); + } + Ok(sy::Value::Sequence(output)) + } + sy::Value::String(s) => { + let output = if is_yage_encoded(s) { + // keep the already encrypted value + s.to_owned() + } else { + encrypt_value(value, recipients)? + }; + Ok(sy::Value::String(output)) + } + sy::Value::Number(_) => { + let output = encrypt_value(value, recipients)?; + Ok(sy::Value::String(output)) + } + _ => Ok(value.clone()), + } +} + +pub fn encrypt_value(value: &sy::Value, recipients: &[x25519::Recipient]) -> Result { + type Recipients = Vec>; + let data = sy::to_string(value)?; + let recipients = recipients + .iter() + .map(|r| Box::new(r.clone()) as Box) + .collect::(); + let mut encrypted = vec![]; + let encryptor = age::Encryptor::with_recipients(recipients).ok_or(YageError::NoRecipients)?; + // let mut armored = ArmoredWriter::wrap_output(&mut encrypted, Format::AsciiArmor)?; + let mut writer = encryptor.wrap_output(&mut encrypted)?; + writer.write_all(data.as_bytes())?; + writer.finish()?; + let encoded = BASE64_STANDARD.encode(&encrypted); + Ok(format!("yage[{encoded}]")) +} + +pub fn load_recipients( + recipients: &[String], + recipients_paths: &[PathBuf], +) -> Result> { + let mut res: Vec = Vec::new(); + // read the recipient from the command line + for recipient in recipients.iter() { + debug!("loading recipient: {recipient}"); + let recipient = + x25519::Recipient::from_str(recipient).map_err(|e| YageError::RecipientParse { + recipient: recipient.to_owned(), + message: e.into(), + })?; + res.push(recipient); + } + // read the recipient from the files + for path in recipients_paths.iter() { + debug!("loading recipient file: {path:?}"); + let input = stdin_or_file(path)?; + for recipient in input.lines() { + let recipient = recipient.path_ctx(path)?; + let recipient = + x25519::Recipient::from_str(&recipient).map_err(|e| YageError::RecipientParse { + recipient: recipient.to_owned(), + message: e.into(), + })?; + res.push(recipient); + } + } + Ok(res) +} diff --git a/src/pubkey.rs b/src/pubkey.rs index 4bb81a0..036cd89 100644 --- a/src/pubkey.rs +++ b/src/pubkey.rs @@ -1,6 +1,6 @@ use crate::cli::PubkeyArgs; use crate::error::{IOResultExt, Result}; -use crate::util::{load_identities, stdout_or_file}; +use crate::{load_identities, stdout_or_file}; pub fn pubkey(args: &PubkeyArgs) -> Result<()> { let keys = load_identities(&args.keys, &args.key_files)?; diff --git a/src/util.rs b/src/util.rs deleted file mode 100644 index 88de41b..0000000 --- a/src/util.rs +++ /dev/null @@ -1,221 +0,0 @@ -use std::fs::{File, OpenOptions}; -use std::io::{stdin, stdout, BufRead, BufReader, Read, Write}; -#[cfg(unix)] -use std::os::unix::fs::OpenOptionsExt; -use std::path::Path; -use std::path::PathBuf; -use std::str::FromStr; - -use age::x25519; -use base64::prelude::*; -use path_absolutize::Absolutize; -use serde_yaml as sy; -use substring::Substring; - -use crate::error::{IOResultExt, Result, YageError}; - -pub fn stdout_or_file(path: &Path) -> Result> { - Ok(if path == Path::new("-") { - Box::new(stdout()) - } else { - Box::new(File::create(path).path_ctx(path)?) - }) -} - -pub fn stdout_or_private_file(path: &Path) -> Result> { - Ok(if path == Path::new("-") { - Box::new(stdout()) - } else { - // make sure the directory is private - let real_path = path.absolutize().path_ctx(path)?; - let dir = real_path.parent().ok_or(YageError::InvalidFileName { - path: path.to_owned(), - })?; - if let Err(e) = fs_mistrust::Mistrust::new().check_directory(dir) { - warn!("directory {dir:?} is not private: {e}"); - } - let mut file_opts = OpenOptions::new(); - file_opts.write(true).create_new(true); - #[cfg(unix)] - file_opts.mode(0o600); - Box::new(file_opts.open(path).path_ctx(path)?) - }) -} - -pub fn stdin_or_file(path: &Path) -> Result>> { - Ok(if path == Path::new("-") { - BufReader::new(Box::new(stdin())) - } else { - BufReader::new(Box::new(File::open(path).path_ctx(path)?)) - }) -} - -pub fn stdin_or_private_file(path: &Path) -> Result>> { - Ok(if path == Path::new("-") { - BufReader::new(Box::new(stdin())) - } else { - if let Err(e) = fs_mistrust::Mistrust::new() - .verifier() - .require_file() - .check(path) - { - warn!("file {path:?} is not private: {e}"); - } - BufReader::new(Box::new(File::open(path).path_ctx(path)?)) - }) -} - -pub fn is_yage_encoded(s: &str) -> bool { - s.starts_with("yage[") && s.ends_with(']') -} - -pub fn decrypt_yaml(value: &sy::Value, identities: &[x25519::Identity]) -> Result { - match value { - sy::Value::Mapping(mapping) => { - let mut output = sy::Mapping::new(); - for (key, value) in mapping { - let key = key.clone(); - let value = decrypt_yaml(value, identities)?; - output.insert(key, value); - } - Ok(sy::Value::Mapping(output)) - } - sy::Value::Sequence(sequence) => { - let mut output = Vec::new(); - for value in sequence { - let value = decrypt_yaml(value, identities)?; - output.push(value); - } - Ok(sy::Value::Sequence(output)) - } - sy::Value::String(encrypted) => { - let decrypted = decrypt_value(encrypted, identities)?; - Ok(decrypted) - } - _ => Ok(value.clone()), - } -} - -pub fn decrypt_value(s: &str, identities: &[x25519::Identity]) -> Result { - if is_yage_encoded(s) { - // remove the yage[…] prefix and suffix - let encoded = s.substring(5, s.len() - 1); - let encrypted = BASE64_STANDARD.decode(encoded)?; - let decryptor = match age::Decryptor::new(&encrypted[..])? { - age::Decryptor::Recipients(d) => Ok(d), - _ => Err(YageError::PassphraseUnsupported), - }?; - let mut decrypted = vec![]; - let mut reader = decryptor.decrypt(identities.iter().map(|i| i as &dyn age::Identity))?; - reader.read_to_end(&mut decrypted)?; - let value: sy::Value = sy::from_slice(&decrypted)?; - Ok(value) - } else { - Ok(sy::Value::String(s.to_owned())) - } -} - -pub fn load_identities(keys: &[String], key_files: &[PathBuf]) -> Result> { - let mut identities: Vec = Vec::new(); - for key in keys.iter() { - debug!("loading key: {key}"); - let key = x25519::Identity::from_str(key) - .map_err(|e| YageError::KeyParse { message: e.into() })?; - identities.push(key); - } - for key_file in key_files.iter() { - debug!("loading key file: {key_file:?}"); - let input = stdin_or_private_file(key_file)?; - let keys = age::IdentityFile::from_buffer(input).path_ctx(key_file)?; - for key in keys.into_identities() { - let age::IdentityFileEntry::Native(key) = key; - identities.push(key); - } - } - Ok(identities) -} - -pub fn encrypt_yaml(value: &sy::Value, recipients: &[x25519::Recipient]) -> Result { - match value { - sy::Value::Mapping(mapping) => { - let mut output = sy::Mapping::new(); - for (key, value) in mapping { - let key = key.clone(); - let value = encrypt_yaml(value, recipients)?; - output.insert(key, value); - } - Ok(sy::Value::Mapping(output)) - } - sy::Value::Sequence(sequence) => { - let mut output = Vec::new(); - for value in sequence { - let value = encrypt_yaml(value, recipients)?; - output.push(value); - } - Ok(sy::Value::Sequence(output)) - } - sy::Value::String(s) => { - let output = if is_yage_encoded(s) { - // keep the already encrypted value - s.to_owned() - } else { - encrypt_value(value, recipients)? - }; - Ok(sy::Value::String(output)) - } - sy::Value::Number(_) => { - let output = encrypt_value(value, recipients)?; - Ok(sy::Value::String(output)) - } - _ => Ok(value.clone()), - } -} - -pub fn encrypt_value(value: &sy::Value, recipients: &[x25519::Recipient]) -> Result { - type Recipients = Vec>; - let data = sy::to_string(value)?; - let recipients = recipients - .iter() - .map(|r| Box::new(r.clone()) as Box) - .collect::(); - let mut encrypted = vec![]; - let encryptor = age::Encryptor::with_recipients(recipients).ok_or(YageError::NoRecipients)?; - // let mut armored = ArmoredWriter::wrap_output(&mut encrypted, Format::AsciiArmor)?; - let mut writer = encryptor.wrap_output(&mut encrypted)?; - writer.write_all(data.as_bytes())?; - writer.finish()?; - let encoded = BASE64_STANDARD.encode(&encrypted); - Ok(format!("yage[{encoded}]")) -} - -pub fn load_recipients( - recipients: &[String], - recipients_paths: &[PathBuf], -) -> Result> { - let mut res: Vec = Vec::new(); - // read the recipient from the command line - for recipient in recipients.iter() { - debug!("loading recipient: {recipient}"); - let recipient = - x25519::Recipient::from_str(recipient).map_err(|e| YageError::RecipientParse { - recipient: recipient.to_owned(), - message: e.into(), - })?; - res.push(recipient); - } - // read the recipient from the files - for path in recipients_paths.iter() { - debug!("loading recipient file: {path:?}"); - let input = stdin_or_file(path)?; - for recipient in input.lines() { - let recipient = recipient.path_ctx(path)?; - let recipient = - x25519::Recipient::from_str(&recipient).map_err(|e| YageError::RecipientParse { - recipient: recipient.to_owned(), - message: e.into(), - })?; - res.push(recipient); - } - } - Ok(res) -}