bc_crypto/
symmetric_encryption.rs1use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, KeyInit};
2
3use crate::Result;
4
5pub const SYMMETRIC_KEY_SIZE: usize = 32;
6pub const SYMMETRIC_NONCE_SIZE: usize = 12;
7pub const SYMMETRIC_AUTH_SIZE: usize = 16;
8
9pub fn aead_chacha20_poly1305_encrypt_with_aad(
14 plaintext: impl AsRef<[u8]>,
15 key: &[u8; SYMMETRIC_KEY_SIZE],
16 nonce: &[u8; SYMMETRIC_NONCE_SIZE],
17 aad: impl AsRef<[u8]>,
18) -> (Vec<u8>, [u8; SYMMETRIC_AUTH_SIZE]) {
19 let cipher = ChaCha20Poly1305::new(key.into());
20 let mut buffer = plaintext.as_ref().to_vec();
21 let auth = cipher
22 .encrypt_in_place_detached(nonce.into(), aad.as_ref(), &mut buffer)
23 .unwrap();
24 (buffer, auth.to_vec().try_into().unwrap())
25}
26
27pub fn aead_chacha20_poly1305_encrypt(
31 plaintext: impl AsRef<[u8]>,
32 key: &[u8; SYMMETRIC_KEY_SIZE],
33 nonce: &[u8; SYMMETRIC_NONCE_SIZE],
34) -> (Vec<u8>, [u8; SYMMETRIC_AUTH_SIZE]) {
35 aead_chacha20_poly1305_encrypt_with_aad(plaintext, key, nonce, [])
36}
37
38pub fn aead_chacha20_poly1305_decrypt_with_aad<D1, D2>(
43 ciphertext: D1,
44 key: &[u8; SYMMETRIC_KEY_SIZE],
45 nonce: &[u8; SYMMETRIC_NONCE_SIZE],
46 aad: D2,
47 auth: &[u8; SYMMETRIC_AUTH_SIZE],
48) -> Result<Vec<u8>>
49where
50 D1: AsRef<[u8]>,
51 D2: AsRef<[u8]>,
52{
53 let cipher = ChaCha20Poly1305::new(key.into());
54 let mut buffer = ciphertext.as_ref().to_vec();
55 cipher.decrypt_in_place_detached(
56 nonce.into(),
57 aad.as_ref(),
58 &mut buffer,
59 auth.into(),
60 )?;
61 Ok(buffer)
62}
63
64pub fn aead_chacha20_poly1305_decrypt<D>(
68 ciphertext: D,
69 key: &[u8; SYMMETRIC_KEY_SIZE],
70 nonce: &[u8; SYMMETRIC_NONCE_SIZE],
71 auth: &[u8; SYMMETRIC_AUTH_SIZE],
72) -> Result<Vec<u8>>
73where
74 D: AsRef<[u8]>,
75{
76 aead_chacha20_poly1305_decrypt_with_aad(ciphertext, key, nonce, [], auth)
77}
78
79#[cfg(test)]
80mod tests {
81 use bc_rand::random_data;
82 use hex_literal::hex;
83
84 use super::{
85 aead_chacha20_poly1305_decrypt_with_aad,
86 aead_chacha20_poly1305_encrypt_with_aad,
87 };
88 use crate::SYMMETRIC_AUTH_SIZE;
89
90 const PLAINTEXT: &[u8] = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
91 const AAD: [u8; 12] = hex!("50515253c0c1c2c3c4c5c6c7");
92 const KEY: [u8; 32] = hex!(
93 "808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f"
94 );
95 const NONCE: [u8; 12] = hex!("070000004041424344454647");
96 const CIPHERTEXT: [u8; 114] = hex!(
97 "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad675945585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b6116"
98 );
99 const AUTH: [u8; 16] = hex!("1ae10b594f09e26a7e902ecbd0600691");
100
101 fn encrypted() -> (Vec<u8>, [u8; SYMMETRIC_AUTH_SIZE]) {
102 aead_chacha20_poly1305_encrypt_with_aad(PLAINTEXT, &KEY, &NONCE, AAD)
103 }
104
105 #[test]
106 fn test_rfc_test_vector() {
107 let (ciphertext, auth) = encrypted();
108 assert_eq!(ciphertext, CIPHERTEXT);
109 assert_eq!(auth, AUTH);
110
111 let decrypted_plaintext = aead_chacha20_poly1305_decrypt_with_aad(
112 &ciphertext,
113 &KEY,
114 &NONCE,
115 AAD,
116 &auth,
117 )
118 .unwrap();
119 assert_eq!(PLAINTEXT, decrypted_plaintext.as_slice());
120 }
121
122 #[test]
123 fn test_random_key_and_nonce() {
124 let key = random_data(32).try_into().unwrap();
125 let nonce = random_data(12).try_into().unwrap();
126 let (ciphertext, auth) = aead_chacha20_poly1305_encrypt_with_aad(
127 PLAINTEXT, &key, &nonce, AAD,
128 );
129 let decrypted_plaintext = aead_chacha20_poly1305_decrypt_with_aad(
130 ciphertext, &key, &nonce, AAD, &auth,
131 )
132 .unwrap();
133 assert_eq!(PLAINTEXT, decrypted_plaintext.as_slice());
134 }
135
136 #[test]
137 fn test_empty_data() {
138 let key = random_data(32).try_into().unwrap();
139 let nonce = random_data(12).try_into().unwrap();
140 let (ciphertext, auth) =
141 aead_chacha20_poly1305_encrypt_with_aad([], &key, &nonce, []);
142 let decrypted_plaintext = aead_chacha20_poly1305_decrypt_with_aad(
143 ciphertext,
144 &key,
145 &nonce,
146 [],
147 &auth,
148 )
149 .unwrap();
150 assert_eq!(Vec::<u8>::new(), decrypted_plaintext.as_slice());
151 }
152}