Skip to main content

authy/vault/
crypto.rs

1use std::io::{Read, Write};
2
3use age::secrecy::ExposeSecret;
4use hkdf::Hkdf;
5use sha2::Sha256;
6
7use crate::error::{AuthyError, Result};
8
9/// Encrypt data using a passphrase via age.
10pub fn encrypt_with_passphrase(plaintext: &[u8], passphrase: &str) -> Result<Vec<u8>> {
11    let encryptor = age::Encryptor::with_user_passphrase(
12        age::secrecy::Secret::new(passphrase.to_string()),
13    );
14
15    let mut encrypted = vec![];
16    let mut writer = encryptor
17        .wrap_output(&mut encrypted)
18        .map_err(|e| AuthyError::Encryption(e.to_string()))?;
19    writer
20        .write_all(plaintext)
21        .map_err(|e| AuthyError::Encryption(e.to_string()))?;
22    writer
23        .finish()
24        .map_err(|e| AuthyError::Encryption(e.to_string()))?;
25
26    Ok(encrypted)
27}
28
29/// Decrypt data using a passphrase via age.
30pub fn decrypt_with_passphrase(ciphertext: &[u8], passphrase: &str) -> Result<Vec<u8>> {
31    let decryptor = match age::Decryptor::new(ciphertext)
32        .map_err(|e| AuthyError::Decryption(e.to_string()))?
33    {
34        age::Decryptor::Passphrase(d) => d,
35        _ => return Err(AuthyError::Decryption("Expected passphrase-encrypted data".into())),
36    };
37
38    let mut decrypted = vec![];
39    let mut reader = decryptor
40        .decrypt(
41            &age::secrecy::Secret::new(passphrase.to_string()),
42            None,
43        )
44        .map_err(|e| AuthyError::Decryption(e.to_string()))?;
45    reader
46        .read_to_end(&mut decrypted)
47        .map_err(|e| AuthyError::Decryption(e.to_string()))?;
48
49    Ok(decrypted)
50}
51
52/// Encrypt data using an age identity (keyfile).
53pub fn encrypt_with_keyfile(plaintext: &[u8], pubkey: &str) -> Result<Vec<u8>> {
54    let recipient: age::x25519::Recipient = pubkey
55        .parse()
56        .map_err(|e: &str| AuthyError::Encryption(e.to_string()))?;
57
58    let encryptor = age::Encryptor::with_recipients(vec![Box::new(recipient)])
59        .expect("recipients not empty");
60
61    let mut encrypted = vec![];
62    let mut writer = encryptor
63        .wrap_output(&mut encrypted)
64        .map_err(|e| AuthyError::Encryption(e.to_string()))?;
65    writer
66        .write_all(plaintext)
67        .map_err(|e| AuthyError::Encryption(e.to_string()))?;
68    writer
69        .finish()
70        .map_err(|e| AuthyError::Encryption(e.to_string()))?;
71
72    Ok(encrypted)
73}
74
75/// Decrypt data using an age identity (keyfile).
76pub fn decrypt_with_keyfile(ciphertext: &[u8], identity_str: &str) -> Result<Vec<u8>> {
77    let identity: age::x25519::Identity = identity_str
78        .parse()
79        .map_err(|e: &str| AuthyError::InvalidKeyfile(e.to_string()))?;
80
81    let decryptor = match age::Decryptor::new(ciphertext)
82        .map_err(|e| AuthyError::Decryption(e.to_string()))?
83    {
84        age::Decryptor::Recipients(d) => d,
85        _ => return Err(AuthyError::Decryption("Expected recipients-encrypted data".into())),
86    };
87
88    let mut decrypted = vec![];
89    let mut reader = decryptor
90        .decrypt(std::iter::once(&identity as &dyn age::Identity))
91        .map_err(|e| AuthyError::Decryption(e.to_string()))?;
92    reader
93        .read_to_end(&mut decrypted)
94        .map_err(|e| AuthyError::Decryption(e.to_string()))?;
95
96    Ok(decrypted)
97}
98
99/// Derive a sub-key using HKDF-SHA256.
100pub fn derive_key(master: &[u8], info: &[u8], output_len: usize) -> Vec<u8> {
101    let hk = Hkdf::<Sha256>::new(None, master);
102    let mut okm = vec![0u8; output_len];
103    hk.expand(info, &mut okm)
104        .expect("HKDF output length too large");
105    okm
106}
107
108/// Generate a new age keypair. Returns (secret_key_string, public_key_string).
109pub fn generate_keypair() -> (String, String) {
110    let identity = age::x25519::Identity::generate();
111    let secret_key = identity.to_string();
112    let public_key = identity.to_public().to_string();
113    (secret_key.expose_secret().clone(), public_key)
114}