p2panda_encryption/crypto/
aead.rs1use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, Key, KeyInit, Nonce};
7use thiserror::Error;
8
9pub type AeadNonce = [u8; 12];
11
12pub type AeadKey = [u8; 32];
14
15#[allow(deprecated, reason = "generic_array < v1.0 is deprecated")]
17pub fn aead_encrypt(
18 key: &AeadKey,
19 plaintext: &[u8],
20 nonce: AeadNonce,
21 aad: Option<&[u8]>,
22) -> Result<Vec<u8>, AeadError> {
23 let key = Key::from_slice(key);
25 let nonce = Nonce::from_slice(&nonce);
26 let mut ciphertext_with_tag: Vec<u8> = Vec::from(plaintext);
27 let cipher = ChaCha20Poly1305::new(key);
28 cipher
29 .encrypt_in_place(nonce, aad.unwrap_or_default(), &mut ciphertext_with_tag)
30 .map_err(AeadError::Encrypt)?;
31 Ok(ciphertext_with_tag)
32}
33
34#[allow(deprecated, reason = "generic_array < v1.0 is deprecated")]
36pub fn aead_decrypt(
37 key: &AeadKey,
38 ciphertext_with_tag: &[u8],
39 nonce: AeadNonce,
40 aad: Option<&[u8]>,
41) -> Result<Vec<u8>, AeadError> {
42 let key = Key::from_slice(key);
43 let nonce = Nonce::from_slice(&nonce);
44 let mut plaintext: Vec<u8> = Vec::from(ciphertext_with_tag);
45 let cipher = ChaCha20Poly1305::new(key);
46 cipher
47 .decrypt_in_place(nonce, aad.unwrap_or_default(), &mut plaintext)
48 .map_err(AeadError::Decrypt)?;
49 Ok(plaintext)
50}
51
52#[derive(Debug, Error)]
53pub enum AeadError {
54 #[error("plaintext could not be encrypted with aead: {0}")]
55 Encrypt(chacha20poly1305::Error),
56
57 #[error("ciphertext could not be decrypted with aead: {0}")]
58 Decrypt(chacha20poly1305::Error),
59}
60
61#[cfg(test)]
62mod tests {
63 use crate::crypto::Rng;
64
65 use super::{AeadError, AeadKey, AeadNonce, aead_decrypt, aead_encrypt};
66
67 #[test]
68 fn encrypt_decrypt() {
69 let rng = Rng::from_seed([1; 32]);
70
71 let key: AeadKey = rng.random_array().unwrap();
72 let nonce: AeadNonce = rng.random_array().unwrap();
73
74 let ciphertext = aead_encrypt(&key, b"Hello, Panda!", nonce, None).unwrap();
75 let plaintext = aead_decrypt(&key, &ciphertext, nonce, None).unwrap();
76
77 assert_eq!(plaintext, b"Hello, Panda!");
78 }
79
80 #[test]
81 fn decryption_failed() {
82 let rng = Rng::from_seed([1; 32]);
83
84 let key: AeadKey = rng.random_array().unwrap();
85 let nonce: AeadNonce = rng.random_array().unwrap();
86
87 let ciphertext = aead_encrypt(&key, b"Hello, Panda!", nonce, None).unwrap();
88
89 let invalid_key: AeadKey = rng.random_array().unwrap();
90 let invalid_nonce: AeadNonce = rng.random_array().unwrap();
91
92 assert!(matches!(
94 aead_decrypt(&invalid_key, &ciphertext, nonce, None),
95 Err(AeadError::Decrypt(chacha20poly1305::Error))
96 ));
97
98 assert!(matches!(
100 aead_decrypt(&key, &ciphertext, invalid_nonce, None),
101 Err(AeadError::Decrypt(chacha20poly1305::Error))
102 ));
103
104 assert!(matches!(
106 aead_decrypt(&key, &ciphertext, nonce, Some(b"invalid aad")),
107 Err(AeadError::Decrypt(chacha20poly1305::Error))
108 ));
109 }
110}