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
12pub 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#[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
55pub 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}