anubis_age/
simple.rs

1use std::io::{Read, Write};
2use std::iter;
3
4use crate::{
5    error::{DecryptError, EncryptError},
6    Decryptor, Encryptor, Identity, Recipient,
7};
8
9#[cfg(feature = "armor")]
10use crate::armor::{ArmoredReader, ArmoredWriter, Format};
11
12/// Encrypts the given plaintext to the given recipient.
13///
14/// To encrypt to more than one recipient, use [`Encryptor::with_recipients`].
15///
16/// This function returns binary ciphertext. To obtain an ASCII-armored text string, use
17/// [`encrypt_and_armor`].
18pub fn encrypt(recipient: &impl Recipient, plaintext: &[u8]) -> Result<Vec<u8>, EncryptError> {
19    let encryptor =
20        Encryptor::with_recipients(iter::once(recipient as _)).expect("we provided a recipient");
21
22    let mut ciphertext = Vec::with_capacity(plaintext.len());
23    let mut writer = encryptor.wrap_output(&mut ciphertext)?;
24    writer.write_all(plaintext)?;
25    writer.finish()?;
26
27    Ok(ciphertext)
28}
29
30/// Encrypts the given plaintext to the given recipient, and wraps the ciphertext in ASCII
31/// armor.
32///
33/// To encrypt to more than one recipient, use [`Encryptor::with_recipients`] along with
34/// [`ArmoredWriter`].
35#[cfg(feature = "armor")]
36#[cfg_attr(docsrs, doc(cfg(feature = "armor")))]
37pub fn encrypt_and_armor(
38    recipient: &impl Recipient,
39    plaintext: &[u8],
40) -> Result<String, EncryptError> {
41    let encryptor =
42        Encryptor::with_recipients(iter::once(recipient as _)).expect("we provided a recipient");
43
44    let mut ciphertext = Vec::with_capacity(plaintext.len());
45    let mut writer = encryptor.wrap_output(ArmoredWriter::wrap_output(
46        &mut ciphertext,
47        Format::AsciiArmor,
48    )?)?;
49    writer.write_all(plaintext)?;
50    writer.finish()?.finish()?;
51
52    Ok(String::from_utf8(ciphertext).expect("is armored"))
53}
54
55/// Decrypts the given ciphertext with the given identity.
56///
57/// If the `armor` feature flag is enabled, this will also handle armored age ciphertexts.
58///
59/// To attempt decryption with more than one identity, use [`Decryptor`] (as well as
60/// [`ArmoredReader`] if the `armor` feature flag is enabled).
61pub fn decrypt(identity: &impl Identity, ciphertext: &[u8]) -> Result<Vec<u8>, DecryptError> {
62    #[cfg(feature = "armor")]
63    let decryptor = Decryptor::new_buffered(ArmoredReader::new(ciphertext))?;
64
65    #[cfg(not(feature = "armor"))]
66    let decryptor = Decryptor::new_buffered(ciphertext)?;
67
68    let mut plaintext = vec![];
69    let mut reader = decryptor.decrypt(iter::once(identity as _))?;
70    reader.read_to_end(&mut plaintext)?;
71
72    Ok(plaintext)
73}
74
75#[cfg(test)]
76mod tests {
77    use super::{decrypt, encrypt};
78    use crate::pqc::mlkem;
79
80    #[cfg(feature = "armor")]
81    use super::encrypt_and_armor;
82
83    #[test]
84    fn mlkem_round_trip() {
85        let sk = mlkem::Identity::generate();
86        let pk = sk.to_public();
87        let test_msg = b"This is a test message. For testing.";
88
89        let encrypted = encrypt(&pk, test_msg).unwrap();
90        let decrypted = decrypt(&sk, &encrypted).unwrap();
91        assert_eq!(&decrypted[..], &test_msg[..]);
92    }
93
94    #[cfg(feature = "armor")]
95    #[test]
96    fn mlkem_round_trip_armor() {
97        let sk = mlkem::Identity::generate();
98        let pk = sk.to_public();
99        let test_msg = b"This is a test message. For testing.";
100
101        let encrypted = encrypt_and_armor(&pk, test_msg).unwrap();
102        assert!(encrypted.starts_with("-----BEGIN AGE ENCRYPTED FILE-----"));
103
104        let decrypted = decrypt(&sk, encrypted.as_bytes()).unwrap();
105        assert_eq!(&decrypted[..], &test_msg[..]);
106    }
107}