dcrypt_algorithms/aead/xchacha20poly1305/
mod.rs1use crate::aead::chacha20poly1305::{CHACHA20POLY1305_KEY_SIZE, CHACHA20POLY1305_TAG_SIZE};
14use crate::error::{validate, Result};
15use crate::types::nonce::XChaCha20Compatible;
16use crate::types::Nonce;
17#[cfg(not(feature = "std"))]
18use alloc::vec::Vec;
19use chacha20poly1305::aead::{Aead, KeyInit, Payload};
20use chacha20poly1305::{XChaCha20Poly1305 as BackendXChaCha20Poly1305, XNonce};
21use dcrypt_api::traits::AuthenticatedCipher;
22use dcrypt_common::security::{SecretBuffer, SecureZeroingType};
23use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
24
25pub const XCHACHA20POLY1305_NONCE_SIZE: usize = 24;
27
28#[derive(Clone, Zeroize, ZeroizeOnDrop)]
33pub struct XChaCha20Poly1305 {
34 key: SecretBuffer<CHACHA20POLY1305_KEY_SIZE>,
35}
36
37impl XChaCha20Poly1305 {
38 pub fn new(key: &[u8; CHACHA20POLY1305_KEY_SIZE]) -> Self {
40 Self {
41 key: SecretBuffer::new(*key),
42 }
43 }
44
45 pub fn from_key(key: &[u8]) -> Result<Self> {
47 validate::length(
48 "XChaCha20Poly1305 key",
49 key.len(),
50 CHACHA20POLY1305_KEY_SIZE,
51 )?;
52
53 let mut key_bytes = Zeroizing::new([0u8; CHACHA20POLY1305_KEY_SIZE]);
54 key_bytes.copy_from_slice(&key[..CHACHA20POLY1305_KEY_SIZE]);
55 Ok(Self {
56 key: SecretBuffer::new(*key_bytes),
57 })
58 }
59
60 pub fn encrypt<const N: usize>(
62 &self,
63 nonce: &Nonce<N>,
64 plaintext: &[u8],
65 aad: Option<&[u8]>,
66 ) -> Result<Vec<u8>>
67 where
68 Nonce<N>: XChaCha20Compatible,
69 {
70 validate::length(
71 "XChaCha20Poly1305 nonce",
72 nonce.as_ref().len(),
73 XCHACHA20POLY1305_NONCE_SIZE,
74 )?;
75 let cipher = BackendXChaCha20Poly1305::new_from_slice(self.key.as_ref())
76 .map_err(|_| crate::error::Error::param("key", "invalid XChaCha20 key"))?;
77 cipher
78 .encrypt(
79 XNonce::from_slice(nonce.as_ref()),
80 Payload {
81 msg: plaintext,
82 aad: aad.unwrap_or(&[]),
83 },
84 )
85 .map_err(|_| crate::error::Error::Processing {
86 operation: "XChaCha20Poly1305 encryption",
87 details: "message is too long",
88 })
89 }
90
91 pub fn decrypt<const N: usize>(
93 &self,
94 nonce: &Nonce<N>,
95 ciphertext: &[u8],
96 aad: Option<&[u8]>,
97 ) -> Result<Vec<u8>>
98 where
99 Nonce<N>: XChaCha20Compatible,
100 {
101 validate::length(
102 "XChaCha20Poly1305 nonce",
103 nonce.as_ref().len(),
104 XCHACHA20POLY1305_NONCE_SIZE,
105 )?;
106 let cipher = BackendXChaCha20Poly1305::new_from_slice(self.key.as_ref())
107 .map_err(|_| crate::error::Error::param("key", "invalid XChaCha20 key"))?;
108 cipher
109 .decrypt(
110 XNonce::from_slice(nonce.as_ref()),
111 Payload {
112 msg: ciphertext,
113 aad: aad.unwrap_or(&[]),
114 },
115 )
116 .map_err(|_| crate::error::Error::Authentication {
117 algorithm: "XChaCha20Poly1305",
118 })
119 }
120}
121
122impl SecureZeroingType for XChaCha20Poly1305 {
124 fn zeroed() -> Self {
125 Self {
126 key: SecretBuffer::zeroed(),
127 }
128 }
129
130 fn secure_clone(&self) -> Self {
131 Self {
132 key: self.key.secure_clone(),
133 }
134 }
135}
136
137impl AuthenticatedCipher for XChaCha20Poly1305 {
139 const TAG_SIZE: usize = CHACHA20POLY1305_TAG_SIZE;
140 const ALGORITHM_ID: &'static str = "XChaCha20Poly1305";
141}
142
143#[cfg(test)]
144mod tests;