diff --git a/chacha20poly1305/Cargo.toml b/chacha20poly1305/Cargo.toml index 9150331c..cf57bbf7 100644 --- a/chacha20poly1305/Cargo.toml +++ b/chacha20poly1305/Cargo.toml @@ -28,13 +28,16 @@ zeroize = { version = "=1.3", default-features = false } aead = { version = "0.4", features = ["dev"], default-features = false } [features] -default = ["alloc", "chacha20", "xchacha20poly1305"] +default = ["alloc", "chacha20", "xchacha20"] std = ["aead/std", "alloc"] alloc = ["aead/alloc"] heapless = ["aead/heapless"] -reduced-round = ["chacha20"] stream = ["aead/stream"] -xchacha20poly1305 = ["chacha20/xchacha"] +xchacha20 = ["chacha20/xchacha"] +xchacha20poly1305 = ["xchacha20"] # alias +reduced-round = ["chacha20-reduced-round", "xchacha20-reduced-round"] +chacha20-reduced-round = ["chacha20"] +xchacha20-reduced-round = ["xchacha20"] force-soft = ["chacha20/force-soft", "poly1305/force-soft"] [package.metadata.docs.rs] diff --git a/chacha20poly1305/src/lib.rs b/chacha20poly1305/src/lib.rs index c6843f81..a8a9ef1a 100644 --- a/chacha20poly1305/src/lib.rs +++ b/chacha20poly1305/src/lib.rs @@ -3,14 +3,16 @@ //! cipher amenable to fast, constant-time implementations in software, based on //! the [ChaCha20][3] stream cipher and [Poly1305][4] universal hash function. //! -//! This crate contains pure Rust implementations of `ChaCha20Poly1305` +//! This crate contains pure Rust implementations of [`ChaCha20Poly1305`] //! (with optional AVX2 acceleration) as well as the following variants thereof: //! //! - [`XChaCha20Poly1305`] - ChaCha20Poly1305 variant with an extended 192-bit (24-byte) nonce. //! - [`ChaCha8Poly1305`] / [`ChaCha12Poly1305`] - non-standard, reduced-round variants //! (gated under the `reduced-round` Cargo feature). See the [Too Much Crypto][5] //! paper for background and rationale on when these constructions could be used. -//! When in doubt, prefer `ChaCha20Poly1305`. +//! When in doubt, prefer [`ChaCha20Poly1305`]. +//! - [`XChaCha8Poly1305`] / [`XChaCha12Poly1305`] - same as above, +//! but with an extended 192-bit (24-byte) nonce. //! //! ## Security Notes //! @@ -87,6 +89,51 @@ //! # } //! ``` //! +//! ## [`XChaCha20Poly1305`] +//! +//! ChaCha20Poly1305 variant with an extended 192-bit (24-byte) nonce. +//! +//! The `xchacha20` Cargo feature must be enabled in order to use this +//! (which it is by default). +//! +//! The construction is an adaptation of the same techniques used by +//! XSalsa20 as described in the paper "Extending the Salsa20 Nonce" +//! to the 96-bit nonce variant of ChaCha20, which derive a +//! separate subkey/nonce for each extended nonce: +//! +//! +//! +//! No authoritative specification exists for XChaCha20Poly1305, however the +//! construction has "rough consensus and running code" in the form of +//! several interoperable libraries and protocols (e.g. libsodium, WireGuard) +//! and is documented in an (expired) IETF draft, which also applies the +//! proof from the XSalsa20 paper to the construction in order to demonstrate +//! that XChaCha20 is secure if ChaCha20 is secure (see Section 3.1): +//! +//! +//! +//! It is worth noting that NaCl/libsodium's default "secretbox" algorithm is +//! XSalsa20Poly1305, not XChaCha20Poly1305, and thus not compatible with +//! this library. If you are interested in that construction, please see the +//! `xsalsa20poly1305` crate: +//! +//! +//! +//! # Usage +//! +//! ``` +//! use chacha20poly1305::{XChaCha20Poly1305, Key, XNonce}; +//! use chacha20poly1305::aead::{Aead, NewAead}; +//! +//! let key = Key::from_slice(b"an example very very secret key."); // 32-bytes +//! let aead = XChaCha20Poly1305::new(key); +//! +//! let nonce = XNonce::from_slice(b"extra long unique nonce!"); // 24-bytes; unique +//! let ciphertext = aead.encrypt(nonce, b"plaintext message".as_ref()).expect("encryption failure!"); +//! let plaintext = aead.decrypt(nonce, ciphertext.as_ref()).expect("decryption failure!"); +//! assert_eq!(&plaintext, b"plaintext message"); +//! ``` +//! //! [1]: https://tools.ietf.org/html/rfc8439 //! [2]: https://en.wikipedia.org/wiki/Authenticated_encryption //! [3]: https://github.com/RustCrypto/stream-ciphers/tree/master/chacha20 @@ -105,30 +152,33 @@ mod cipher; -#[cfg(feature = "xchacha20poly1305")] -mod xchacha20poly1305; - pub use aead; -#[cfg(feature = "xchacha20poly1305")] -pub use xchacha20poly1305::{XChaCha20Poly1305, XNonce}; - use self::cipher::Cipher; use ::cipher::{NewCipher, StreamCipher, StreamCipherSeek}; use aead::{ consts::{U0, U12, U16, U32}, - generic_array::GenericArray, + generic_array::{ArrayLength, GenericArray}, AeadCore, AeadInPlace, Error, NewAead, }; use core::marker::PhantomData; use zeroize::Zeroize; +#[cfg(feature = "xchacha20")] +use aead::consts::U24; + #[cfg(feature = "chacha20")] use chacha20::ChaCha20; -#[cfg(feature = "reduced-round")] +#[cfg(feature = "xchacha20")] +use chacha20::XChaCha20; + +#[cfg(feature = "chacha20-reduced-round")] use chacha20::{ChaCha12, ChaCha8}; +#[cfg(feature = "xchacha20-reduced-round")] +use chacha20::{XChaCha12, XChaCha8}; + /// Key type (256-bits/32-bytes). /// /// Implemented as an alias for [`GenericArray`]. @@ -142,6 +192,12 @@ pub type Key = GenericArray; /// Implemented as an alias for [`GenericArray`]. pub type Nonce = GenericArray; +/// XNonce type (192-bits/24-bytes). +/// +/// Implemented as an alias for [`GenericArray`]. +#[cfg(feature = "xchacha20")] +pub type XNonce = GenericArray; + /// Poly1305 tag. /// /// Implemented as an alias for [`GenericArray`]. @@ -150,24 +206,39 @@ pub type Tag = GenericArray; /// ChaCha20Poly1305 Authenticated Encryption with Additional Data (AEAD). #[cfg(feature = "chacha20")] #[cfg_attr(docsrs, doc(cfg(feature = "chacha20")))] -pub type ChaCha20Poly1305 = ChaChaPoly1305; +pub type ChaCha20Poly1305 = ChaChaPoly1305; + +/// XChaCha20Poly1305 Authenticated Encryption with Additional Data (AEAD). +#[cfg(feature = "xchacha20")] +#[cfg_attr(docsrs, doc(cfg(feature = "xchacha20")))] +pub type XChaCha20Poly1305 = ChaChaPoly1305; /// ChaCha8Poly1305 (reduced round variant) Authenticated Encryption with Additional Data (AEAD). -#[cfg(feature = "reduced-round")] +#[cfg(feature = "chacha20-reduced-round")] #[cfg_attr(docsrs, doc(cfg(feature = "reduced-round")))] -pub type ChaCha8Poly1305 = ChaChaPoly1305; +pub type ChaCha8Poly1305 = ChaChaPoly1305; + +/// XChaCha8Poly1305 (reduced round variant) Authenticated Encryption with Additional Data (AEAD). +#[cfg(feature = "xchacha20-reduced-round")] +#[cfg_attr(docsrs, doc(cfg(feature = "xchacha20-reduced-round")))] +pub type XChaCha8Poly1305 = ChaChaPoly1305; /// ChaCha12Poly1305 (reduced round variant) Authenticated Encryption with Additional Data (AEAD). -#[cfg(feature = "reduced-round")] +#[cfg(feature = "chacha20-reduced-round")] #[cfg_attr(docsrs, doc(cfg(feature = "reduced-round")))] -pub type ChaCha12Poly1305 = ChaChaPoly1305; +pub type ChaCha12Poly1305 = ChaChaPoly1305; + +/// XChaCha12Poly1305 (reduced round variant) Authenticated Encryption with Additional Data (AEAD). +#[cfg(feature = "xchacha20-reduced-round")] +#[cfg_attr(docsrs, doc(cfg(feature = "xchacha20-reduced-round")))] +pub type XChaCha12Poly1305 = ChaChaPoly1305; /// Generic ChaCha+Poly1305 Authenticated Encryption with Additional Data (AEAD) construction. /// /// See the [toplevel documentation](index.html) for a usage example. -pub struct ChaChaPoly1305 +pub struct ChaChaPoly1305 = U12> where - C: NewCipher + StreamCipher + StreamCipherSeek, + C: NewCipher + StreamCipher + StreamCipherSeek, { /// Secret key key: GenericArray, @@ -176,9 +247,10 @@ where stream_cipher: PhantomData, } -impl NewAead for ChaChaPoly1305 +impl NewAead for ChaChaPoly1305 where - C: NewCipher + StreamCipher + StreamCipherSeek, + C: NewCipher + StreamCipher + StreamCipherSeek, + N: ArrayLength, { type KeySize = U32; @@ -190,22 +262,24 @@ where } } -impl AeadCore for ChaChaPoly1305 +impl AeadCore for ChaChaPoly1305 where - C: NewCipher + StreamCipher + StreamCipherSeek, + C: NewCipher + StreamCipher + StreamCipherSeek, + N: ArrayLength, { - type NonceSize = U12; + type NonceSize = N; type TagSize = U16; type CiphertextOverhead = U0; } -impl AeadInPlace for ChaChaPoly1305 +impl AeadInPlace for ChaChaPoly1305 where - C: NewCipher + StreamCipher + StreamCipherSeek, + C: NewCipher + StreamCipher + StreamCipherSeek, + N: ArrayLength, { fn encrypt_in_place_detached( &self, - nonce: &Nonce, + nonce: &aead::Nonce, associated_data: &[u8], buffer: &mut [u8], ) -> Result { @@ -214,7 +288,7 @@ where fn decrypt_in_place_detached( &self, - nonce: &Nonce, + nonce: &aead::Nonce, associated_data: &[u8], buffer: &mut [u8], tag: &Tag, @@ -227,9 +301,10 @@ where } } -impl Clone for ChaChaPoly1305 +impl Clone for ChaChaPoly1305 where - C: NewCipher + StreamCipher + StreamCipherSeek, + C: NewCipher + StreamCipher + StreamCipherSeek, + N: ArrayLength, { fn clone(&self) -> Self { Self { @@ -239,9 +314,10 @@ where } } -impl Drop for ChaChaPoly1305 +impl Drop for ChaChaPoly1305 where - C: NewCipher + StreamCipher + StreamCipherSeek, + C: NewCipher + StreamCipher + StreamCipherSeek, + N: ArrayLength, { fn drop(&mut self) { self.key.as_mut_slice().zeroize(); diff --git a/chacha20poly1305/src/xchacha20poly1305.rs b/chacha20poly1305/src/xchacha20poly1305.rs deleted file mode 100644 index e030338a..00000000 --- a/chacha20poly1305/src/xchacha20poly1305.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! XChaCha20Poly1305 is an extended nonce variant of ChaCha20Poly1305. -//! -//! See [`XChaCha20Poly1305`] documentation for usage. - -pub use chacha20::XNonce; - -use crate::{cipher::Cipher, Key, Tag}; -use ::cipher::NewCipher; -use aead::{ - consts::{U0, U16, U24, U32}, - AeadCore, AeadInPlace, Error, NewAead, -}; -use chacha20::XChaCha20; -use zeroize::Zeroize; - -/// ChaCha20Poly1305 variant with an extended 192-bit (24-byte) nonce. -/// -/// The `xchacha20poly1305` Cargo feature must be enabled in order to use this -/// (which it is by default). -/// -/// The construction is an adaptation of the same techniques used by -/// XSalsa20 as described in the paper "Extending the Salsa20 Nonce" -/// to the 96-bit nonce variant of ChaCha20, which derive a -/// separate subkey/nonce for each extended nonce: -/// -/// -/// -/// No authoritative specification exists for XChaCha20Poly1305, however the -/// construction has "rough consensus and running code" in the form of -/// several interoperable libraries and protocols (e.g. libsodium, WireGuard) -/// and is documented in an (expired) IETF draft, which also applies the -/// proof from the XSalsa20 paper to the construction in order to demonstrate -/// that XChaCha20 is secure if ChaCha20 is secure (see Section 3.1): -/// -/// -/// -/// It is worth noting that NaCl/libsodium's default "secretbox" algorithm is -/// XSalsa20Poly1305, not XChaCha20Poly1305, and thus not compatible with -/// this library. If you are interested in that construction, please see the -/// `xsalsa20poly1305` crate: -/// -/// -/// -/// # Usage -/// -/// ``` -/// use chacha20poly1305::{XChaCha20Poly1305, Key, XNonce}; -/// use chacha20poly1305::aead::{Aead, NewAead}; -/// -/// let key = Key::from_slice(b"an example very very secret key."); // 32-bytes -/// let aead = XChaCha20Poly1305::new(key); -/// -/// let nonce = XNonce::from_slice(b"extra long unique nonce!"); // 24-bytes; unique -/// let ciphertext = aead.encrypt(nonce, b"plaintext message".as_ref()).expect("encryption failure!"); -/// let plaintext = aead.decrypt(nonce, ciphertext.as_ref()).expect("decryption failure!"); -/// assert_eq!(&plaintext, b"plaintext message"); -/// ``` -#[derive(Clone)] -#[cfg_attr(docsrs, doc(cfg(feature = "xchacha20poly1305")))] -pub struct XChaCha20Poly1305 { - /// Secret key - key: Key, -} - -impl NewAead for XChaCha20Poly1305 { - type KeySize = U32; - - fn new(key: &Key) -> Self { - XChaCha20Poly1305 { key: *key } - } -} - -impl AeadCore for XChaCha20Poly1305 { - type NonceSize = U24; - type TagSize = U16; - type CiphertextOverhead = U0; -} - -impl AeadInPlace for XChaCha20Poly1305 { - fn encrypt_in_place_detached( - &self, - nonce: &XNonce, - associated_data: &[u8], - buffer: &mut [u8], - ) -> Result { - Cipher::new(XChaCha20::new(&self.key, nonce)) - .encrypt_in_place_detached(associated_data, buffer) - } - - fn decrypt_in_place_detached( - &self, - nonce: &XNonce, - associated_data: &[u8], - buffer: &mut [u8], - tag: &Tag, - ) -> Result<(), Error> { - Cipher::new(XChaCha20::new(&self.key, nonce)).decrypt_in_place_detached( - associated_data, - buffer, - tag, - ) - } -} - -impl Drop for XChaCha20Poly1305 { - fn drop(&mut self) { - self.key.as_mut_slice().zeroize(); - } -}