dcrypt_algorithms/aead/xchacha20poly1305/
mod.rs1use crate::aead::chacha20poly1305::{
8 ChaCha20Poly1305, CHACHA20POLY1305_KEY_SIZE, CHACHA20POLY1305_TAG_SIZE,
9};
10use crate::error::{validate, Result};
11use crate::stream::chacha::chacha20::{ChaCha20, CHACHA20_NONCE_SIZE};
12use crate::types::nonce::XChaCha20Compatible;
13use crate::types::Nonce;
14#[cfg(not(feature = "std"))]
15use alloc::vec::Vec;
16use dcrypt_api::traits::AuthenticatedCipher;
17use dcrypt_common::security::{SecretBuffer, SecureZeroingType};
18use zeroize::{Zeroize, ZeroizeOnDrop};
19
20pub const XCHACHA20POLY1305_NONCE_SIZE: usize = 24;
22
23#[derive(Clone, Zeroize, ZeroizeOnDrop)]
25pub struct XChaCha20Poly1305 {
26 key: SecretBuffer<CHACHA20POLY1305_KEY_SIZE>,
27}
28
29impl XChaCha20Poly1305 {
30 pub fn new(key: &[u8; CHACHA20POLY1305_KEY_SIZE]) -> Self {
32 Self {
33 key: SecretBuffer::new(*key),
34 }
35 }
36
37 pub fn from_key(key: &[u8]) -> Result<Self> {
39 validate::length(
40 "XChaCha20Poly1305 key",
41 key.len(),
42 CHACHA20POLY1305_KEY_SIZE,
43 )?;
44
45 let mut key_bytes = [0u8; CHACHA20POLY1305_KEY_SIZE];
46 key_bytes.copy_from_slice(&key[..CHACHA20POLY1305_KEY_SIZE]);
47 Ok(Self {
48 key: SecretBuffer::new(key_bytes),
49 })
50 }
51
52 pub fn encrypt<const N: usize>(
54 &self,
55 nonce: &Nonce<N>,
56 plaintext: &[u8],
57 aad: Option<&[u8]>,
58 ) -> Result<Vec<u8>>
59 where
60 Nonce<N>: XChaCha20Compatible,
61 {
62 let mut subkey = [0u8; CHACHA20POLY1305_KEY_SIZE];
64 let mut nonce_prefix = [0u8; CHACHA20_NONCE_SIZE];
65
66 let nonce_bytes = nonce.as_ref();
68 validate::length(
69 "XChaCha20Poly1305 nonce",
70 nonce_bytes.len(),
71 XCHACHA20POLY1305_NONCE_SIZE,
72 )?;
73
74 nonce_prefix.copy_from_slice(&nonce_bytes[..CHACHA20_NONCE_SIZE]);
75
76 let nonce_obj = Nonce::<CHACHA20_NONCE_SIZE>::new(nonce_prefix);
78
79 let key_array: &[u8; CHACHA20POLY1305_KEY_SIZE] = self
81 .key
82 .as_ref()
83 .try_into()
84 .expect("SecretBuffer has correct size");
85
86 let mut chacha = ChaCha20::new(key_array, &nonce_obj);
88 chacha.keystream(&mut subkey);
89
90 let chacha_poly = ChaCha20Poly1305::new(&subkey);
92
93 let mut truncated_nonce = [0u8; CHACHA20_NONCE_SIZE];
95 truncated_nonce.copy_from_slice(&nonce_bytes[12..24]);
96
97 let result = chacha_poly.encrypt_with_nonce(&truncated_nonce, plaintext, aad)?;
98
99 subkey.zeroize();
101
102 Ok(result)
103 }
104
105 pub fn decrypt<const N: usize>(
107 &self,
108 nonce: &Nonce<N>,
109 ciphertext: &[u8],
110 aad: Option<&[u8]>,
111 ) -> Result<Vec<u8>>
112 where
113 Nonce<N>: XChaCha20Compatible,
114 {
115 let mut subkey = [0u8; CHACHA20POLY1305_KEY_SIZE];
117 let mut nonce_prefix = [0u8; CHACHA20_NONCE_SIZE];
118
119 let nonce_bytes = nonce.as_ref();
121 validate::length(
122 "XChaCha20Poly1305 nonce",
123 nonce_bytes.len(),
124 XCHACHA20POLY1305_NONCE_SIZE,
125 )?;
126
127 nonce_prefix.copy_from_slice(&nonce_bytes[..CHACHA20_NONCE_SIZE]);
128
129 let nonce_obj = Nonce::<CHACHA20_NONCE_SIZE>::new(nonce_prefix);
131
132 let key_array: &[u8; CHACHA20POLY1305_KEY_SIZE] = self
134 .key
135 .as_ref()
136 .try_into()
137 .expect("SecretBuffer has correct size");
138
139 let mut chacha = ChaCha20::new(key_array, &nonce_obj);
141 chacha.keystream(&mut subkey);
142
143 let chacha_poly = ChaCha20Poly1305::new(&subkey);
144
145 let mut truncated_nonce = [0u8; CHACHA20_NONCE_SIZE];
146 truncated_nonce.copy_from_slice(&nonce_bytes[12..24]);
147
148 let result = chacha_poly.decrypt_with_nonce(&truncated_nonce, ciphertext, aad)?;
149
150 subkey.zeroize();
152
153 Ok(result)
154 }
155
156 pub fn encrypt_with_zero_nonce(
158 &self,
159 plaintext: &[u8],
160 associated_data: Option<&[u8]>,
161 ) -> Result<Vec<u8>> {
162 let zero_nonce = Nonce::<XCHACHA20POLY1305_NONCE_SIZE>::zeroed();
163 self.encrypt(&zero_nonce, plaintext, associated_data)
164 }
165
166 pub fn decrypt_with_zero_nonce(
168 &self,
169 ciphertext: &[u8],
170 associated_data: Option<&[u8]>,
171 ) -> Result<Vec<u8>> {
172 let zero_nonce = Nonce::<XCHACHA20POLY1305_NONCE_SIZE>::zeroed();
173 self.decrypt(&zero_nonce, ciphertext, associated_data)
174 }
175}
176
177impl SecureZeroingType for XChaCha20Poly1305 {
179 fn zeroed() -> Self {
180 Self {
181 key: SecretBuffer::zeroed(),
182 }
183 }
184
185 fn secure_clone(&self) -> Self {
186 Self {
187 key: self.key.secure_clone(),
188 }
189 }
190}
191
192impl AuthenticatedCipher for XChaCha20Poly1305 {
194 const TAG_SIZE: usize = CHACHA20POLY1305_TAG_SIZE;
195 const ALGORITHM_ID: &'static str = "XChaCha20Poly1305";
196}
197
198#[cfg(test)]
199mod tests;