Skip to main content

git_ark/
crypto.rs

1use anyhow::{anyhow, Result};
2use std::io::{Read, Write};
3
4pub fn encrypt(recipient: &str, plaintext: &[u8]) -> Result<Vec<u8>> {
5    let recipient: age::x25519::Recipient = recipient
6        .parse()
7        .map_err(|e| anyhow!("invalid age recipient: {e}"))?;
8    let encryptor = age::Encryptor::with_recipients(vec![Box::new(recipient)])
9        .ok_or_else(|| anyhow!("no recipients provided"))?;
10    let mut out = Vec::new();
11    let mut writer = encryptor.wrap_output(&mut out)?;
12    writer.write_all(plaintext)?;
13    writer.finish()?;
14    Ok(out)
15}
16
17pub fn decrypt(identity: &str, ciphertext: &[u8]) -> Result<Vec<u8>> {
18    let identity: age::x25519::Identity = identity
19        .parse()
20        .map_err(|e| anyhow!("invalid age identity: {e}"))?;
21    let decryptor = match age::Decryptor::new(ciphertext)? {
22        age::Decryptor::Recipients(d) => d,
23        age::Decryptor::Passphrase(_) => {
24            return Err(anyhow!("passphrase-encrypted payloads are not supported"))
25        }
26    };
27    let mut reader = decryptor.decrypt(std::iter::once(&identity as &dyn age::Identity))?;
28    let mut out = Vec::new();
29    reader.read_to_end(&mut out)?;
30    Ok(out)
31}
32
33#[cfg(test)]
34mod tests {
35    use age::secrecy::ExposeSecret;
36
37    // A throwaway keypair generated in-test.
38    fn keypair() -> (String, String) {
39        let id = age::x25519::Identity::generate();
40        let pk = id.to_public();
41        (pk.to_string(), id.to_string().expose_secret().to_string())
42    }
43
44    #[test]
45    fn round_trips() {
46        let (pk, sk) = keypair();
47        let msg = b"the whole repo bundle bytes";
48        let ct = super::encrypt(&pk, msg).unwrap();
49        assert_ne!(&ct[..], &msg[..]);
50        let pt = super::decrypt(&sk, &ct).unwrap();
51        assert_eq!(pt, msg);
52    }
53
54    #[test]
55    fn wrong_key_fails() {
56        let (pk, _sk) = keypair();
57        let (_pk2, sk2) = keypair();
58        let ct = super::encrypt(&pk, b"secret").unwrap();
59        assert!(super::decrypt(&sk2, &ct).is_err());
60    }
61}