doot/crates/doot-cli/src/commands/decrypt.rs

50 lines
1.6 KiB
Rust

use doot_core::{Config, encryption::AgeEncryption};
use std::io::Write;
use std::path::PathBuf;
/// Extracts the AGE-SECRET-KEY line from identity file content (filters comments).
fn extract_identity_key(raw: &str) -> String {
raw.lines()
.find(|line| line.starts_with("AGE-SECRET-KEY-"))
.map(|s| s.trim().to_string())
.unwrap_or_else(|| raw.trim().to_string())
}
/// Decrypts an age-encrypted file to stdout (default) or to a file with --output.
#[tracing::instrument(skip_all, fields(file = %file.display()))]
pub fn run(
file: PathBuf,
identity: Option<PathBuf>,
output: Option<PathBuf>,
) -> anyhow::Result<()> {
let config = Config::default();
let identity_raw = if let Some(path) = identity {
std::fs::read_to_string(&path)?
} else if let Ok(key) = std::env::var("DOOT_AGE_IDENTITY") {
key
} else if config.identity_file.exists() {
std::fs::read_to_string(&config.identity_file)?
} else {
anyhow::bail!(
"no identity specified. use --identity, DOOT_AGE_IDENTITY env var, or {}",
config.identity_file.display()
);
};
let identity_key = extract_identity_key(&identity_raw);
tracing::debug!(file = %file.display(), "decrypting file");
let encryption = AgeEncryption::new().with_identity(&identity_key)?;
let data = std::fs::read(&file)?;
let decrypted = encryption.decrypt(&data)?;
if let Some(out_path) = output {
std::fs::write(&out_path, &decrypted)?;
eprintln!("decrypted {} -> {}", file.display(), out_path.display());
} else {
std::io::stdout().write_all(&decrypted)?;
}
Ok(())
}