pub mod keys;
pub mod store;
pub mod util;
pub mod value;
use anyhow::{Context, Result};
use std::{
env, fs,
path::{Path, PathBuf},
};
pub use keys::{Identity, Recipient};
pub fn default_key_dir() -> PathBuf {
env::var_os("XDG_CONFIG_HOME")
.map(PathBuf::from)
.unwrap_or_else(|| {
PathBuf::from(env::var("HOME").unwrap_or_else(|_| ".".into())).join(".config")
})
.join("open_envault")
.join("keys")
}
fn atomic_write(path: &Path, content: &[u8]) -> Result<()> {
let parent = path.parent().unwrap_or_else(|| Path::new("."));
let file_name = path
.file_name()
.map(|n| n.to_string_lossy().into_owned())
.unwrap_or_else(|| "out".into());
let tmp = parent.join(format!(".{file_name}.{}.tmp", std::process::id()));
{
let mut file = fs::File::create(&tmp)?;
std::io::Write::write_all(&mut file, content)?;
file.sync_all()?;
}
fs::rename(&tmp, path)?;
Ok(())
}
pub fn encrypt(content: &str, path: &Path, recipients: &[String]) -> Result<()> {
let text = store::encrypt(content, &store::recipients_from_strings(recipients)?)?;
atomic_write(path, text.as_bytes())
}
pub fn decrypt_for(path: &Path, environment: Option<&str>) -> Result<String> {
let mut identities = keys::identities_from_env();
if let Some(name) = environment {
let key_path = default_key_dir().join(format!("{name}.txt"));
if key_path.is_file()
&& let Ok(identity) = keys::read_identity_file(&key_path)
{
identities.push(identity);
}
}
if identities.is_empty() {
anyhow::bail!(
"no age identity available; set OPENENCRYPT_AGE_KEY/SOPS_AGE_KEY or a key file"
);
}
let text = fs::read_to_string(path)
.with_context(|| format!("read encrypted file {}", path.display()))?;
store::decrypt(&text, &identities)
}
pub fn decrypt(path: &Path) -> Result<String> {
decrypt_for(path, None)
}
pub fn recipients_of(path: &Path) -> Result<Vec<String>> {
let text = fs::read_to_string(path)
.with_context(|| format!("read encrypted file {}", path.display()))?;
store::list_recipients(&text)
}
pub fn generate_key(path: &Path) -> Result<String> {
let (text, _public) = keys::generate_identity()?;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(path, text.as_bytes())
.with_context(|| format!("write key file {}", path.display()))?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
fs::set_permissions(path, fs::Permissions::from_mode(0o600))
.with_context(|| format!("chmod key file {}", path.display()))?;
}
Ok(text)
}