dcrypt_algorithms/aead/xchacha20poly1305/
mod.rs1use crate::aead::chacha20poly1305::{
15 ChaCha20Poly1305, CHACHA20POLY1305_KEY_SIZE, CHACHA20POLY1305_TAG_SIZE,
16};
17use crate::error::{validate, Error, Result};
18use crate::stream::chacha::chacha20::{hchacha20, CHACHA20_NONCE_SIZE};
19use crate::types::nonce::XChaCha20Compatible;
20use crate::types::Nonce;
21#[cfg(not(feature = "std"))]
22use alloc::vec::Vec;
23use dcrypt_api::traits::AuthenticatedCipher;
24use dcrypt_common::security::{SecretBuffer, SecureZeroingType};
25use dcrypt_internal::zeroing::{Zeroize, ZeroizeOnDrop, Zeroizing};
26
27pub const XCHACHA20POLY1305_NONCE_SIZE: usize = 24;
29
30#[derive(Clone)]
35pub struct XChaCha20Poly1305 {
36 key: SecretBuffer<CHACHA20POLY1305_KEY_SIZE>,
37}
38
39impl Zeroize for XChaCha20Poly1305 {
40 fn zeroize(&mut self) {
41 self.key.zeroize();
42 }
43}
44
45impl Drop for XChaCha20Poly1305 {
46 fn drop(&mut self) {
47 self.zeroize();
48 }
49}
50
51impl ZeroizeOnDrop for XChaCha20Poly1305 {}
52
53impl XChaCha20Poly1305 {
54 pub fn new(key: &[u8; CHACHA20POLY1305_KEY_SIZE]) -> Self {
56 Self {
57 key: SecretBuffer::new(*key),
58 }
59 }
60
61 pub fn from_key(key: &[u8]) -> Result<Self> {
63 validate::length(
64 "XChaCha20Poly1305 key",
65 key.len(),
66 CHACHA20POLY1305_KEY_SIZE,
67 )?;
68
69 let mut key_bytes = Zeroizing::new([0u8; CHACHA20POLY1305_KEY_SIZE]);
70 key_bytes.copy_from_slice(&key[..CHACHA20POLY1305_KEY_SIZE]);
71 Ok(Self {
72 key: SecretBuffer::new(*key_bytes),
73 })
74 }
75
76 fn derive_subkey_and_nonce(
78 &self,
79 nonce: &[u8],
80 ) -> (
81 Zeroizing<[u8; CHACHA20POLY1305_KEY_SIZE]>,
82 [u8; CHACHA20_NONCE_SIZE],
83 ) {
84 let mut hchacha_nonce = [0u8; 16];
85 hchacha_nonce.copy_from_slice(&nonce[..16]);
86
87 let key: &[u8; CHACHA20POLY1305_KEY_SIZE] = self
88 .key
89 .as_ref()
90 .try_into()
91 .expect("SecretBuffer has the declared XChaCha20 key size");
92 let subkey = hchacha20(key, &hchacha_nonce);
93
94 let mut chacha_nonce = [0u8; CHACHA20_NONCE_SIZE];
97 chacha_nonce[4..].copy_from_slice(&nonce[16..24]);
98
99 hchacha_nonce.zeroize();
100 (subkey, chacha_nonce)
101 }
102
103 pub fn encrypt<const N: usize>(
105 &self,
106 nonce: &Nonce<N>,
107 plaintext: &[u8],
108 aad: Option<&[u8]>,
109 ) -> Result<Vec<u8>>
110 where
111 Nonce<N>: XChaCha20Compatible,
112 {
113 validate::length(
114 "XChaCha20Poly1305 nonce",
115 nonce.as_ref().len(),
116 XCHACHA20POLY1305_NONCE_SIZE,
117 )?;
118 let (subkey, chacha_nonce) = self.derive_subkey_and_nonce(nonce.as_ref());
119 ChaCha20Poly1305::new(&subkey).encrypt_with_nonce(&chacha_nonce, plaintext, aad)
120 }
121
122 pub fn decrypt<const N: usize>(
124 &self,
125 nonce: &Nonce<N>,
126 ciphertext: &[u8],
127 aad: Option<&[u8]>,
128 ) -> Result<Vec<u8>>
129 where
130 Nonce<N>: XChaCha20Compatible,
131 {
132 validate::length(
133 "XChaCha20Poly1305 nonce",
134 nonce.as_ref().len(),
135 XCHACHA20POLY1305_NONCE_SIZE,
136 )?;
137 let (subkey, chacha_nonce) = self.derive_subkey_and_nonce(nonce.as_ref());
138 ChaCha20Poly1305::new(&subkey)
139 .decrypt_with_nonce(&chacha_nonce, ciphertext, aad)
140 .map_err(|error| match error {
141 Error::Authentication { .. } => Error::Authentication {
142 algorithm: "XChaCha20Poly1305",
143 },
144 other => other,
145 })
146 }
147}
148
149impl SecureZeroingType for XChaCha20Poly1305 {
151 fn zeroed() -> Self {
152 Self {
153 key: SecretBuffer::zeroed(),
154 }
155 }
156
157 fn secure_clone(&self) -> Self {
158 Self {
159 key: self.key.secure_clone(),
160 }
161 }
162}
163
164impl AuthenticatedCipher for XChaCha20Poly1305 {
166 const TAG_SIZE: usize = CHACHA20POLY1305_TAG_SIZE;
167 const ALGORITHM_ID: &'static str = "XChaCha20Poly1305";
168}
169
170#[cfg(test)]
171mod tests;