agent_vault/core/
crypto.rs1use std::io::{Read, Write};
2
3use secrecy::{ExposeSecret, SecretString};
4
5use crate::error::VaultError;
6
7pub fn generate_keypair() -> (SecretString, String) {
9 let identity = age::x25519::Identity::generate();
10 let secret = identity.to_string();
11 let public = identity.to_public().to_string();
12 (SecretString::from(secret.expose_secret().to_string()), public)
13}
14
15pub fn parse_identity(key_str: &str) -> Result<age::x25519::Identity, VaultError> {
17 key_str
18 .parse::<age::x25519::Identity>()
19 .map_err(|e| VaultError::AgeKey(e.to_string()))
20}
21
22pub fn parse_recipient(key_str: &str) -> Result<age::x25519::Recipient, VaultError> {
24 key_str
25 .parse::<age::x25519::Recipient>()
26 .map_err(|e| VaultError::AgeKey(e.to_string()))
27}
28
29pub fn encrypt(plaintext: &[u8], recipients: &[age::x25519::Recipient]) -> Result<Vec<u8>, VaultError> {
31 let encryptor = age::Encryptor::with_recipients(
32 recipients.iter().map(|r| r as &dyn age::Recipient),
33 )
34 .map_err(|e| VaultError::AgeEncrypt(e.to_string()))?;
35
36 let mut encrypted = vec![];
37 let mut writer = encryptor
38 .wrap_output(&mut encrypted)
39 .map_err(|e| VaultError::AgeEncrypt(e.to_string()))?;
40 writer
41 .write_all(plaintext)
42 .map_err(|e| VaultError::AgeEncrypt(e.to_string()))?;
43 writer
44 .finish()
45 .map_err(|e| VaultError::AgeEncrypt(e.to_string()))?;
46
47 Ok(encrypted)
48}
49
50pub fn decrypt(ciphertext: &[u8], identity: &age::x25519::Identity) -> Result<SecretString, VaultError> {
52 let decryptor = age::Decryptor::new(ciphertext)
53 .map_err(|e| VaultError::AgeDecrypt(e.to_string()))?;
54
55 let mut reader = decryptor
56 .decrypt(std::iter::once(identity as &dyn age::Identity))
57 .map_err(|e| VaultError::AgeDecrypt(e.to_string()))?;
58
59 let mut plaintext = String::new();
60 reader
61 .read_to_string(&mut plaintext)
62 .map_err(|e| VaultError::AgeDecrypt(e.to_string()))?;
63
64 Ok(SecretString::from(plaintext))
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70
71 #[test]
72 fn test_roundtrip() {
73 let (secret, public) = generate_keypair();
74 let recipient = parse_recipient(&public).unwrap();
75 let identity = parse_identity(secret.expose_secret()).unwrap();
76
77 let plaintext = b"hello world";
78 let ciphertext = encrypt(plaintext, &[recipient]).unwrap();
79 let decrypted = decrypt(&ciphertext, &identity).unwrap();
80 assert_eq!(decrypted.expose_secret(), "hello world");
81 }
82
83 #[test]
84 fn test_multi_recipient() {
85 let (secret1, public1) = generate_keypair();
86 let (secret2, public2) = generate_keypair();
87 let r1 = parse_recipient(&public1).unwrap();
88 let r2 = parse_recipient(&public2).unwrap();
89 let id1 = parse_identity(secret1.expose_secret()).unwrap();
90 let id2 = parse_identity(secret2.expose_secret()).unwrap();
91
92 let ciphertext = encrypt(b"multi", &[r1, r2]).unwrap();
93 assert_eq!(decrypt(&ciphertext, &id1).unwrap().expose_secret(), "multi");
94 assert_eq!(decrypt(&ciphertext, &id2).unwrap().expose_secret(), "multi");
95 }
96}