Skip to main content

envx_secure/
crypto.rs

1//! Thin wrappers around the [`age`] passphrase encrypt/decrypt primitives.
2//!
3//! Both functions operate on raw byte slices so they are agnostic about file
4//! format; the higher-level file I/O lives in [`crate::commands::encrypt`].
5
6use age::secrecy::SecretString;
7use anyhow::{Context, Result};
8use std::io::{Read, Write};
9
10/// Encrypt `data` with `passphrase` using age passphrase mode.
11///
12/// Returns the raw ciphertext bytes (armored age format).
13///
14/// # Errors
15///
16/// Propagates errors from the age encryptor if the underlying I/O fails.
17pub fn encrypt(data: &[u8], passphrase: &str) -> Result<Vec<u8>> {
18    let secret = SecretString::from(passphrase.to_string());
19    let encryptor = age::Encryptor::with_user_passphrase(secret);
20
21    let mut output = Vec::new();
22    let mut writer = encryptor
23        .wrap_output(&mut output)
24        .context("failed to create age encryptor")?;
25
26    writer
27        .write_all(data)
28        .context("failed to write plaintext")?;
29    writer.finish().context("failed to finalize encryption")?;
30
31    Ok(output)
32}
33
34/// Decrypt age-encrypted `data` with `passphrase`.
35///
36/// `data` must have been produced by [`encrypt`] (passphrase mode only —
37/// public-key recipients are rejected).
38///
39/// # Errors
40///
41/// Returns an error if:
42/// - the age header cannot be parsed,
43/// - the file was encrypted with a public-key recipient instead of a passphrase,
44/// - the passphrase is wrong, or
45/// - the underlying I/O fails.
46pub fn decrypt(data: &[u8], passphrase: &str) -> Result<Vec<u8>> {
47    let secret = SecretString::from(passphrase.to_string());
48
49    let decryptor = match age::Decryptor::new(data).context("failed to parse age header")? {
50        age::Decryptor::Passphrase(d) => d,
51        _ => anyhow::bail!("expected passphrase-encrypted file"),
52    };
53
54    let mut reader = decryptor
55        .decrypt(&secret, None)
56        .context("decryption failed — wrong passphrase?")?;
57
58    let mut output = Vec::new();
59    reader
60        .read_to_end(&mut output)
61        .context("failed to read decrypted data")?;
62
63    Ok(output)
64}