chacha20poly1305/lib.rs
1#![no_std]
2#![cfg_attr(docsrs, feature(doc_cfg))]
3#![doc = include_str!("../README.md")]
4#![doc(
5 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
6 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
7)]
8#![warn(missing_docs, rust_2018_idioms)]
9
10//! ## Supported Algorithms
11//!
12//! This crate contains pure Rust implementations of [`ChaCha20Poly1305`]
13//! (with optional AVX2 acceleration) as well as the following variants thereof:
14//!
15//! - [`XChaCha20Poly1305`] - ChaCha20Poly1305 variant with an extended 192-bit (24-byte) nonce.
16//! - [`ChaCha8Poly1305`] / [`ChaCha12Poly1305`] - non-standard, reduced-round variants
17//! (gated under the `reduced-round` Cargo feature). See the [Too Much Crypto][5]
18//! paper for background and rationale on when these constructions could be used.
19//! When in doubt, prefer [`ChaCha20Poly1305`].
20//! - [`XChaCha8Poly1305`] / [`XChaCha12Poly1305`] - same as above,
21//! but with an extended 192-bit (24-byte) nonce.
22//!
23//! # Usage
24//!
25#![cfg_attr(all(feature = "getrandom", feature = "std"), doc = "```")]
26#![cfg_attr(not(all(feature = "getrandom", feature = "std")), doc = "```ignore")]
27//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
28//! use chacha20poly1305::{
29//! aead::{Aead, AeadCore, KeyInit, OsRng},
30//! ChaCha20Poly1305, Nonce
31//! };
32//!
33//! let key = ChaCha20Poly1305::generate_key(&mut OsRng);
34//! let cipher = ChaCha20Poly1305::new(&key);
35//! let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng); // 96-bits; unique per message
36//! let ciphertext = cipher.encrypt(&nonce, b"plaintext message".as_ref())?;
37//! let plaintext = cipher.decrypt(&nonce, ciphertext.as_ref())?;
38//! assert_eq!(&plaintext, b"plaintext message");
39//! # Ok(())
40//! # }
41//! ```
42//!
43//! ## In-place Usage (eliminates `alloc` requirement)
44//!
45//! This crate has an optional `alloc` feature which can be disabled in e.g.
46//! microcontroller environments that don't have a heap.
47//!
48//! The [`AeadInPlace::encrypt_in_place`] and [`AeadInPlace::decrypt_in_place`]
49//! methods accept any type that impls the [`aead::Buffer`] trait which
50//! contains the plaintext for encryption or ciphertext for decryption.
51//!
52//! Note that if you enable the `heapless` feature of this crate,
53//! you will receive an impl of [`aead::Buffer`] for `heapless::Vec`
54//! (re-exported from the [`aead`] crate as [`aead::heapless::Vec`]),
55//! which can then be passed as the `buffer` parameter to the in-place encrypt
56//! and decrypt methods:
57//!
58#![cfg_attr(
59 all(feature = "getrandom", feature = "heapless", feature = "std"),
60 doc = "```"
61)]
62#![cfg_attr(
63 not(all(feature = "getrandom", feature = "heapless", feature = "std")),
64 doc = "```ignore"
65)]
66//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
67//! use chacha20poly1305::{
68//! aead::{AeadCore, AeadInPlace, KeyInit, OsRng, heapless::Vec},
69//! ChaCha20Poly1305, Nonce,
70//! };
71//!
72//! let key = ChaCha20Poly1305::generate_key(&mut OsRng);
73//! let cipher = ChaCha20Poly1305::new(&key);
74//! let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng); // 96-bits; unique per message
75//!
76//! let mut buffer: Vec<u8, 128> = Vec::new(); // Note: buffer needs 16-bytes overhead for auth tag
77//! buffer.extend_from_slice(b"plaintext message");
78//!
79//! // Encrypt `buffer` in-place, replacing the plaintext contents with ciphertext
80//! cipher.encrypt_in_place(&nonce, b"", &mut buffer)?;
81//!
82//! // `buffer` now contains the message ciphertext
83//! assert_ne!(&buffer, b"plaintext message");
84//!
85//! // Decrypt `buffer` in-place, replacing its ciphertext context with the original plaintext
86//! cipher.decrypt_in_place(&nonce, b"", &mut buffer)?;
87//! assert_eq!(&buffer, b"plaintext message");
88//! # Ok(())
89//! # }
90//! ```
91//!
92//! ## [`XChaCha20Poly1305`]
93//!
94//! ChaCha20Poly1305 variant with an extended 192-bit (24-byte) nonce.
95//!
96//! The construction is an adaptation of the same techniques used by
97//! XSalsa20 as described in the paper "Extending the Salsa20 Nonce"
98//! to the 96-bit nonce variant of ChaCha20, which derive a
99//! separate subkey/nonce for each extended nonce:
100//!
101//! <https://cr.yp.to/snuffle/xsalsa-20081128.pdf>
102//!
103//! No authoritative specification exists for XChaCha20Poly1305, however the
104//! construction has "rough consensus and running code" in the form of
105//! several interoperable libraries and protocols (e.g. libsodium, WireGuard)
106//! and is documented in an (expired) IETF draft, which also applies the
107//! proof from the XSalsa20 paper to the construction in order to demonstrate
108//! that XChaCha20 is secure if ChaCha20 is secure (see Section 3.1):
109//!
110//! <https://tools.ietf.org/html/draft-arciszewski-xchacha-03>
111//!
112//! It is worth noting that NaCl/libsodium's default "secretbox" algorithm is
113//! XSalsa20Poly1305, not XChaCha20Poly1305, and thus not compatible with
114//! this library. If you are interested in that construction, please see the
115//! `xsalsa20poly1305` crate:
116//!
117//! <https://docs.rs/xsalsa20poly1305/>
118//!
119//! # Usage
120//!
121#![cfg_attr(all(feature = "getrandom", feature = "std"), doc = "```")]
122#![cfg_attr(not(all(feature = "getrandom", feature = "std")), doc = "```ignore")]
123//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
124//! use chacha20poly1305::{
125//! aead::{Aead, AeadCore, KeyInit, OsRng},
126//! XChaCha20Poly1305, XNonce
127//! };
128//!
129//! let key = XChaCha20Poly1305::generate_key(&mut OsRng);
130//! let cipher = XChaCha20Poly1305::new(&key);
131//! let nonce = XChaCha20Poly1305::generate_nonce(&mut OsRng); // 192-bits; unique per message
132//! let ciphertext = cipher.encrypt(&nonce, b"plaintext message".as_ref())?;
133//! let plaintext = cipher.decrypt(&nonce, ciphertext.as_ref())?;
134//! assert_eq!(&plaintext, b"plaintext message");
135//! # Ok(())
136//! # }
137//! ```
138
139mod cipher;
140
141pub use aead::{self, consts, AeadCore, AeadInPlace, Error, KeyInit, KeySizeUser};
142
143use self::cipher::Cipher;
144use ::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};
145use aead::{
146 consts::{U0, U12, U16, U24, U32},
147 generic_array::{ArrayLength, GenericArray},
148};
149use core::marker::PhantomData;
150use zeroize::{Zeroize, ZeroizeOnDrop};
151
152use chacha20::{ChaCha20, XChaCha20};
153
154#[cfg(feature = "reduced-round")]
155use chacha20::{ChaCha12, ChaCha8, XChaCha12, XChaCha8};
156
157/// Key type (256-bits/32-bytes).
158///
159/// Implemented as an alias for [`GenericArray`].
160///
161/// All [`ChaChaPoly1305`] variants (including `XChaCha20Poly1305`) use this
162/// key type.
163pub type Key = GenericArray<u8, U32>;
164
165/// Nonce type (96-bits/12-bytes).
166///
167/// Implemented as an alias for [`GenericArray`].
168pub type Nonce = GenericArray<u8, U12>;
169
170/// XNonce type (192-bits/24-bytes).
171///
172/// Implemented as an alias for [`GenericArray`].
173pub type XNonce = GenericArray<u8, U24>;
174
175/// Poly1305 tag.
176///
177/// Implemented as an alias for [`GenericArray`].
178pub type Tag = GenericArray<u8, U16>;
179
180/// ChaCha20Poly1305 Authenticated Encryption with Additional Data (AEAD).
181pub type ChaCha20Poly1305 = ChaChaPoly1305<ChaCha20, U12>;
182
183/// XChaCha20Poly1305 Authenticated Encryption with Additional Data (AEAD).
184pub type XChaCha20Poly1305 = ChaChaPoly1305<XChaCha20, U24>;
185
186/// ChaCha8Poly1305 (reduced round variant) Authenticated Encryption with Additional Data (AEAD).
187#[cfg(feature = "reduced-round")]
188#[cfg_attr(docsrs, doc(cfg(feature = "reduced-round")))]
189pub type ChaCha8Poly1305 = ChaChaPoly1305<ChaCha8, U12>;
190
191/// ChaCha12Poly1305 (reduced round variant) Authenticated Encryption with Additional Data (AEAD).
192#[cfg(feature = "reduced-round")]
193#[cfg_attr(docsrs, doc(cfg(feature = "reduced-round")))]
194pub type ChaCha12Poly1305 = ChaChaPoly1305<ChaCha12, U12>;
195
196/// XChaCha8Poly1305 (reduced round variant) Authenticated Encryption with Additional Data (AEAD).
197#[cfg(feature = "reduced-round")]
198#[cfg_attr(docsrs, doc(cfg(feature = "reduced-round")))]
199pub type XChaCha8Poly1305 = ChaChaPoly1305<XChaCha8, U24>;
200
201/// XChaCha12Poly1305 (reduced round variant) Authenticated Encryption with Additional Data (AEAD).
202#[cfg(feature = "reduced-round")]
203#[cfg_attr(docsrs, doc(cfg(feature = "reduced-round")))]
204pub type XChaCha12Poly1305 = ChaChaPoly1305<XChaCha12, U24>;
205
206/// Generic ChaCha+Poly1305 Authenticated Encryption with Additional Data (AEAD) construction.
207///
208/// See the [toplevel documentation](index.html) for a usage example.
209pub struct ChaChaPoly1305<C, N: ArrayLength<u8> = U12> {
210 /// Secret key.
211 key: Key,
212
213 /// ChaCha stream cipher.
214 stream_cipher: PhantomData<C>,
215
216 /// Nonce size.
217 nonce_size: PhantomData<N>,
218}
219
220impl<C, N> KeySizeUser for ChaChaPoly1305<C, N>
221where
222 N: ArrayLength<u8>,
223{
224 type KeySize = U32;
225}
226
227impl<C, N> KeyInit for ChaChaPoly1305<C, N>
228where
229 N: ArrayLength<u8>,
230{
231 #[inline]
232 fn new(key: &Key) -> Self {
233 Self {
234 key: *key,
235 stream_cipher: PhantomData,
236 nonce_size: PhantomData,
237 }
238 }
239}
240
241impl<C, N> AeadCore for ChaChaPoly1305<C, N>
242where
243 N: ArrayLength<u8>,
244{
245 type NonceSize = N;
246 type TagSize = U16;
247 type CiphertextOverhead = U0;
248}
249
250impl<C, N> AeadInPlace for ChaChaPoly1305<C, N>
251where
252 C: KeyIvInit<KeySize = U32, IvSize = N> + StreamCipher + StreamCipherSeek,
253 N: ArrayLength<u8>,
254{
255 fn encrypt_in_place_detached(
256 &self,
257 nonce: &aead::Nonce<Self>,
258 associated_data: &[u8],
259 buffer: &mut [u8],
260 ) -> Result<Tag, Error> {
261 Cipher::new(C::new(&self.key, nonce)).encrypt_in_place_detached(associated_data, buffer)
262 }
263
264 fn decrypt_in_place_detached(
265 &self,
266 nonce: &aead::Nonce<Self>,
267 associated_data: &[u8],
268 buffer: &mut [u8],
269 tag: &Tag,
270 ) -> Result<(), Error> {
271 Cipher::new(C::new(&self.key, nonce)).decrypt_in_place_detached(
272 associated_data,
273 buffer,
274 tag,
275 )
276 }
277}
278
279impl<C, N> Clone for ChaChaPoly1305<C, N>
280where
281 N: ArrayLength<u8>,
282{
283 fn clone(&self) -> Self {
284 Self {
285 key: self.key,
286 stream_cipher: PhantomData,
287 nonce_size: PhantomData,
288 }
289 }
290}
291
292impl<C, N> Drop for ChaChaPoly1305<C, N>
293where
294 N: ArrayLength<u8>,
295{
296 fn drop(&mut self) {
297 self.key.as_mut_slice().zeroize();
298 }
299}
300
301impl<C, N: ArrayLength<u8>> ZeroizeOnDrop for ChaChaPoly1305<C, N> {}