diff --git a/chacha20poly1305/Cargo.toml b/chacha20poly1305/Cargo.toml index 9985e345..572af1f4 100644 --- a/chacha20poly1305/Cargo.toml +++ b/chacha20poly1305/Cargo.toml @@ -9,7 +9,7 @@ ChaCha20Poly1305 Authenticated Encryption with Additional Data Algorithm (RFC 84 """ documentation = "chacha20poly1305" repository = "https://github.com/RustCrypto/AEADs" -keywords = ["crypto", "cipher", "aead"] +keywords = ["crypto", "cipher", "aead", "xchacha20", "xchacha20poly1305"] categories = ["cryptography", "no-std"] [dependencies] @@ -17,3 +17,7 @@ aead = { version = "0.1", git = "https://github.com/RustCrypto/traits" } chacha20 = { version = "0.2.1", features = ["zeroize"] } poly1305 = "0.2" zeroize = { version = "0.9", default-features = false } + +[features] +default = ["xchacha20poly1305"] +xchacha20poly1305 = ["chacha20/xchacha20"] diff --git a/chacha20poly1305/src/cipher.rs b/chacha20poly1305/src/cipher.rs new file mode 100644 index 00000000..7f8d866b --- /dev/null +++ b/chacha20poly1305/src/cipher.rs @@ -0,0 +1,122 @@ +//! Core AEAD cipher implementation for (X)ChaCha20Poly1305. + +// TODO(tarcieri): make this reusable for (X)Salsa20Poly1305 + +use aead::generic_array::GenericArray; +use aead::Error; +use alloc::vec::Vec; +use chacha20::stream_cipher::{SyncStreamCipher, SyncStreamCipherSeek}; +use core::convert::TryInto; +use poly1305::{Poly1305, Tag}; +use zeroize::Zeroizing; + +/// ChaCha20Poly1305 instantiated with a particular nonce +pub(crate) struct Cipher +where + C: SyncStreamCipher + SyncStreamCipherSeek, +{ + cipher: C, + mac: Poly1305, +} + +impl Cipher +where + C: SyncStreamCipher + SyncStreamCipherSeek, +{ + /// Instantiate the underlying cipher with a particular nonce + pub(crate) fn new(mut cipher: C) -> Self { + // Derive Poly1305 key from the first 32-bytes of the ChaCha20 keystream + let mut mac_key = Zeroizing::new([0u8; poly1305::KEY_SIZE]); + cipher.apply_keystream(&mut *mac_key); + let mac = Poly1305::new(&mac_key); + + // Set ChaCha20 counter to 1 + cipher.seek(chacha20::BLOCK_SIZE as u64); + + Self { cipher, mac } + } + + /// Encrypt the given message, allocating a vector for the resulting ciphertext + pub(crate) fn encrypt( + self, + associated_data: &[u8], + plaintext: &[u8], + ) -> Result, Error> { + let mut buffer = Vec::with_capacity(plaintext.len() + poly1305::BLOCK_SIZE); + buffer.extend_from_slice(plaintext); + + let tag = self.encrypt_in_place(associated_data, &mut buffer)?; + buffer.extend_from_slice(tag.code().as_slice()); + Ok(buffer) + } + + /// Encrypt the given message in-place, returning the authentication tag + pub(crate) fn encrypt_in_place( + mut self, + associated_data: &[u8], + buffer: &mut [u8], + ) -> Result { + if buffer.len() / chacha20::BLOCK_SIZE >= chacha20::MAX_BLOCKS { + return Err(Error); + } + + self.mac.input_padded(associated_data); + self.cipher.apply_keystream(buffer); + self.mac.input_padded(buffer); + self.authenticate_lengths(associated_data, buffer)?; + Ok(self.mac.result()) + } + + /// Decrypt the given message, allocating a vector for the resulting plaintext + pub(crate) fn decrypt( + self, + associated_data: &[u8], + ciphertext: &[u8], + ) -> Result, Error> { + if ciphertext.len() < poly1305::BLOCK_SIZE { + return Err(Error); + } + + let tag_start = ciphertext.len() - poly1305::BLOCK_SIZE; + let mut buffer = Vec::from(&ciphertext[..tag_start]); + let tag: [u8; poly1305::BLOCK_SIZE] = ciphertext[tag_start..].try_into().unwrap(); + self.decrypt_in_place(associated_data, &mut buffer, &tag)?; + + Ok(buffer) + } + + /// Decrypt the given message, first authenticating ciphertext integrity + /// and returning an error if it's been tampered with. + pub(crate) fn decrypt_in_place( + mut self, + associated_data: &[u8], + buffer: &mut [u8], + tag: &[u8; poly1305::BLOCK_SIZE], + ) -> Result<(), Error> { + if buffer.len() / chacha20::BLOCK_SIZE >= chacha20::MAX_BLOCKS { + return Err(Error); + } + + self.mac.input_padded(associated_data); + self.mac.input_padded(buffer); + self.authenticate_lengths(associated_data, buffer)?; + + // This performs a constant-time comparison using the `subtle` crate + if self.mac.result() == Tag::new(*GenericArray::from_slice(tag)) { + self.cipher.apply_keystream(buffer); + Ok(()) + } else { + Err(Error) + } + } + + /// Authenticate the lengths of the associated data and message + fn authenticate_lengths(&mut self, associated_data: &[u8], buffer: &[u8]) -> Result<(), Error> { + let associated_data_len: u64 = associated_data.len().try_into().map_err(|_| Error)?; + let buffer_len: u64 = buffer.len().try_into().map_err(|_| Error)?; + + self.mac.input(&associated_data_len.to_le_bytes()); + self.mac.input(&buffer_len.to_le_bytes()); + Ok(()) + } +} diff --git a/chacha20poly1305/src/lib.rs b/chacha20poly1305/src/lib.rs index e63801e8..2181b3d7 100644 --- a/chacha20poly1305/src/lib.rs +++ b/chacha20poly1305/src/lib.rs @@ -5,18 +5,25 @@ extern crate alloc; -pub use aead; +mod cipher; +#[cfg(feature = "xchacha20poly1305")] +mod xchacha20poly1305; -use aead::generic_array::typenum::{U0, U12, U16, U32}; -use aead::{generic_array::GenericArray, StatelessAead, Error, NewAead}; +pub use aead; +#[cfg(feature = "xchacha20poly1305")] +pub use xchacha20poly1305::XChaCha20Poly1305; + +use self::cipher::Cipher; +use aead::generic_array::{ + typenum::{U0, U12, U16, U32}, + GenericArray, +}; +use aead::{Error, NewAead, StatelessAead}; use alloc::vec::Vec; -use chacha20::stream_cipher::{NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek}; -use chacha20::ChaCha20; -use core::convert::TryInto; -use poly1305::{Poly1305, Tag}; -use zeroize::{Zeroize, Zeroizing}; +use chacha20::{stream_cipher::NewStreamCipher, ChaCha20}; +use zeroize::Zeroize; -/// ChaCha20Poly1305 AEAD +/// ChaCha20Poly1305 Authenticated Encryption with Additional Data (AEAD) #[derive(Clone)] pub struct ChaCha20Poly1305 { /// Secret key @@ -42,7 +49,7 @@ impl StatelessAead for ChaCha20Poly1305 { nonce: &GenericArray, plaintext: &[u8], ) -> Result, Error> { - CipherInstance::new(&self.key, nonce).encrypt(associated_data, plaintext) + Cipher::new(ChaCha20::new(&self.key, nonce)).encrypt(associated_data, plaintext) } fn decrypt( @@ -51,7 +58,7 @@ impl StatelessAead for ChaCha20Poly1305 { nonce: &GenericArray, ciphertext: &[u8], ) -> Result, Error> { - CipherInstance::new(&self.key, nonce).decrypt(associated_data, ciphertext) + Cipher::new(ChaCha20::new(&self.key, nonce)).decrypt(associated_data, ciphertext) } } @@ -60,98 +67,3 @@ impl Drop for ChaCha20Poly1305 { self.key.as_mut_slice().zeroize(); } } - -/// ChaCha20Poly1305 instantiated with a particular nonce -struct CipherInstance { - chacha20: ChaCha20, - poly1305: Poly1305, -} - -impl CipherInstance { - /// Instantiate the underlying cipher with a particular nonce - fn new(key: &GenericArray, nonce: &GenericArray) -> Self { - let mut chacha20 = ChaCha20::new(key, nonce); - - // Derive Poly1305 key from the first 32-bytes of the ChaCha20 keystream - let mut auth_key = Zeroizing::new([0u8; poly1305::KEY_SIZE]); - chacha20.apply_keystream(&mut *auth_key); - - // Set ChaCha20 counter to 1 - chacha20.seek(chacha20::BLOCK_SIZE as u64); - - let poly1305 = Poly1305::new(&auth_key); - Self { chacha20, poly1305 } - } - - /// Encrypt the given message, allocating a vector for the resulting ciphertext - fn encrypt(self, associated_data: &[u8], plaintext: &[u8]) -> Result, Error> { - let mut buffer = Vec::with_capacity(plaintext.len() + poly1305::BLOCK_SIZE); - buffer.extend_from_slice(plaintext); - - let tag = self.encrypt_in_place(associated_data, &mut buffer)?; - buffer.extend_from_slice(tag.code().as_slice()); - Ok(buffer) - } - - /// Encrypt the given message in-place, returning the authentication tag - fn encrypt_in_place(mut self, associated_data: &[u8], buffer: &mut [u8]) -> Result { - if buffer.len() / chacha20::BLOCK_SIZE >= chacha20::MAX_BLOCKS { - return Err(Error); - } - - self.poly1305.input_padded(associated_data); - self.chacha20.apply_keystream(buffer); - self.poly1305.input_padded(buffer); - self.authenticate_lengths(associated_data, buffer)?; - Ok(self.poly1305.result()) - } - - /// Decrypt the given message, allocating a vector for the resulting plaintext - fn decrypt(self, associated_data: &[u8], ciphertext: &[u8]) -> Result, Error> { - if ciphertext.len() < poly1305::BLOCK_SIZE { - return Err(Error); - } - - let tag_start = ciphertext.len() - poly1305::BLOCK_SIZE; - let mut buffer = Vec::from(&ciphertext[..tag_start]); - let tag: [u8; poly1305::BLOCK_SIZE] = ciphertext[tag_start..].try_into().unwrap(); - self.decrypt_in_place(associated_data, &mut buffer, &tag)?; - - Ok(buffer) - } - - /// Decrypt the given message, first authenticating ciphertext integrity - /// and returning an error if it's been tampered with. - fn decrypt_in_place( - mut self, - associated_data: &[u8], - buffer: &mut [u8], - tag: &[u8; poly1305::BLOCK_SIZE], - ) -> Result<(), Error> { - if buffer.len() / chacha20::BLOCK_SIZE >= chacha20::MAX_BLOCKS { - return Err(Error); - } - - self.poly1305.input_padded(associated_data); - self.poly1305.input_padded(buffer); - self.authenticate_lengths(associated_data, buffer)?; - - // This performs a constant-time comparison using the `subtle` crate - if self.poly1305.result() == Tag::new(*GenericArray::from_slice(tag)) { - self.chacha20.apply_keystream(buffer); - Ok(()) - } else { - Err(Error) - } - } - - /// Authenticate the lengths of the associated data and message - fn authenticate_lengths(&mut self, associated_data: &[u8], buffer: &[u8]) -> Result<(), Error> { - let associated_data_len: u64 = associated_data.len().try_into().map_err(|_| Error)?; - let buffer_len: u64 = buffer.len().try_into().map_err(|_| Error)?; - - self.poly1305.input(&associated_data_len.to_le_bytes()); - self.poly1305.input(&buffer_len.to_le_bytes()); - Ok(()) - } -} diff --git a/chacha20poly1305/src/xchacha20poly1305.rs b/chacha20poly1305/src/xchacha20poly1305.rs new file mode 100644 index 00000000..ad839210 --- /dev/null +++ b/chacha20poly1305/src/xchacha20poly1305.rs @@ -0,0 +1,74 @@ +//! XChaCha20Poly1305 is an extended nonce variant of ChaCha20Poly1305 + +use crate::cipher::Cipher; +use aead::generic_array::{ + typenum::{U0, U16, U24, U32}, + GenericArray, +}; +use aead::{Error, NewAead, StatelessAead}; +use alloc::vec::Vec; +use chacha20::{stream_cipher::NewStreamCipher, XChaCha20}; +use zeroize::Zeroize; + +/// XChaCha20Poly1305 is a ChaCha20Poly1305 variant with an extended +/// 192-bit (24-byte) nonce. +/// +/// The construction is an adaptation of the same techniques used by +/// XSalsa20 as described in the paper "Extending the Salsa20 Nonce", +/// applied to the 96-bit nonce variant of ChaCha20, and 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: +/// +/// +/// +/// The `xchacha20poly1305` Cargo feature must be enabled in order to use this +/// (which it is by default). +#[derive(Clone)] +pub struct XChaCha20Poly1305 { + /// Secret key + key: GenericArray, +} + +impl NewAead for XChaCha20Poly1305 { + type KeySize = U32; + + fn new(key: GenericArray) -> Self { + XChaCha20Poly1305 { key } + } +} + +impl StatelessAead for XChaCha20Poly1305 { + type NonceSize = U24; + type TagSize = U16; + type CiphertextOverhead = U0; + + fn encrypt( + &self, + associated_data: &[u8], + nonce: &GenericArray, + plaintext: &[u8], + ) -> Result, Error> { + Cipher::new(XChaCha20::new(&self.key, nonce)).encrypt(associated_data, plaintext) + } + + fn decrypt( + &self, + associated_data: &[u8], + nonce: &GenericArray, + ciphertext: &[u8], + ) -> Result, Error> { + Cipher::new(XChaCha20::new(&self.key, nonce)).decrypt(associated_data, ciphertext) + } +} + +impl Drop for XChaCha20Poly1305 { + fn drop(&mut self) { + self.key.as_mut_slice().zeroize(); + } +} diff --git a/chacha20poly1305/tests/lib.rs b/chacha20poly1305/tests/lib.rs index aa7995bc..a1f47d6d 100644 --- a/chacha20poly1305/tests/lib.rs +++ b/chacha20poly1305/tests/lib.rs @@ -1,81 +1,145 @@ -//! ChaCha20Poly1305 test vectors. -//! -//! From RFC 8439 Section 2.8.2: -//! +//! ChaCha20Poly1305 and XChaCha20Poly1305 tests -use chacha20poly1305::aead::{Aead, NewAead}; -use chacha20poly1305::aead::generic_array::GenericArray; -use chacha20poly1305::ChaCha20Poly1305; +macro_rules! impl_tests { + ($cipher:ty, $key:expr, $nonce:expr, $aad:expr, $plaintext:expr, $ciphertext:expr, $tag:expr) => { + #[test] + fn encrypt() { + let key = GenericArray::from_slice($key); + let nonce = GenericArray::from_slice($nonce); -const KEY: &[u8; 32] = &[ - 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, - 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, -]; + let mut cipher = <$cipher>::new(*key); + let ciphertext = cipher.encrypt($aad, nonce, $plaintext).unwrap(); -const NONCE: &[u8; 12] = &[ - 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, -]; + let tag_begins = ciphertext.len() - 16; + assert_eq!($ciphertext, &ciphertext[..tag_begins]); + assert_eq!($tag, &ciphertext[tag_begins..]); + } -const AAD: &[u8; 12] = &[ - 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, -]; - -const PLAINTEXT: &[u8] = b"Ladies and Gentlemen of the class of '99: \ - If I could offer you only one tip for the future, sunscreen would be it."; + #[test] + fn decrypt() { + let key = GenericArray::from_slice($key); + let nonce = GenericArray::from_slice($nonce); -const CIPHERTEXT: &[u8] = &[ - 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, 0xc2, - 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, 0x62, 0xd6, - 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, 0x92, 0x72, 0x8b, - 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, 0x7e, 0xcd, 0x3b, 0x36, - 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 0x98, 0x03, 0xae, 0xe3, 0x28, 0x09, 0x1b, 0x58, - 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, - 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, - 0x61, 0x16, -]; + let mut ciphertext = Vec::from($ciphertext); + ciphertext.extend_from_slice($tag); -const TAG: &[u8] = &[ - 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, 0x91, -]; + let mut cipher = <$cipher>::new(*key); + let plaintext = cipher.decrypt($aad, nonce, &ciphertext).unwrap(); -#[test] -fn encrypt() { - let key = GenericArray::from_slice(KEY); - let nonce = GenericArray::from_slice(NONCE); + assert_eq!($plaintext, plaintext.as_slice()); + } - let mut cipher = ChaCha20Poly1305::new(*key); - let ciphertext = cipher.encrypt(AAD, nonce, PLAINTEXT).unwrap(); + #[test] + fn decrypt_modified() { + let key = GenericArray::from_slice($key); + let nonce = GenericArray::from_slice($nonce); - let tag_begins = ciphertext.len() - 16; - assert_eq!(CIPHERTEXT, &ciphertext[..tag_begins]); - assert_eq!(TAG, &ciphertext[tag_begins..]); -} + let mut ciphertext = Vec::from($ciphertext); + ciphertext.extend_from_slice($tag); -#[test] -fn decrypt() { - let key = GenericArray::from_slice(KEY); - let nonce = GenericArray::from_slice(NONCE); + // Tweak the first byte + ciphertext[0] ^= 0xaa; - let mut ciphertext = Vec::from(CIPHERTEXT); - ciphertext.extend_from_slice(TAG); + let mut cipher = <$cipher>::new(*key); + assert!(cipher.decrypt($aad, nonce, &ciphertext).is_err()); + } + }; +} - let mut cipher = ChaCha20Poly1305::new(*key); - let plaintext = cipher.decrypt(AAD, nonce, &ciphertext).unwrap(); +// +// Test vectors common to RFC 8439 and `draft-arciszewski-xchacha` +// - assert_eq!(PLAINTEXT, plaintext.as_slice()); -} +const KEY: &[u8; 32] = &[ + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, +]; -#[test] -fn decrypt_modified() { - let key = GenericArray::from_slice(KEY); - let nonce = GenericArray::from_slice(NONCE); +const AAD: &[u8; 12] = &[ + 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, +]; - let mut ciphertext = Vec::from(CIPHERTEXT); - ciphertext.extend_from_slice(TAG); +const PLAINTEXT: &[u8] = b"Ladies and Gentlemen of the class of '99: \ + If I could offer you only one tip for the future, sunscreen would be it."; - // Tweak the first byte - ciphertext[0] ^= 0xaa; +/// ChaCha20Poly1305 test vectors. +/// +/// From RFC 8439 Section 2.8.2: +/// +mod chacha20 { + use super::{AAD, KEY, PLAINTEXT}; + use chacha20poly1305::aead::generic_array::GenericArray; + use chacha20poly1305::aead::{Aead, NewAead}; + use chacha20poly1305::ChaCha20Poly1305; + + const NONCE: &[u8; 12] = &[ + 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + ]; + + const CIPHERTEXT: &[u8] = &[ + 0xd3, 0x1a, 0x8d, 0x34, 0x64, 0x8e, 0x60, 0xdb, 0x7b, 0x86, 0xaf, 0xbc, 0x53, 0xef, 0x7e, + 0xc2, 0xa4, 0xad, 0xed, 0x51, 0x29, 0x6e, 0x08, 0xfe, 0xa9, 0xe2, 0xb5, 0xa7, 0x36, 0xee, + 0x62, 0xd6, 0x3d, 0xbe, 0xa4, 0x5e, 0x8c, 0xa9, 0x67, 0x12, 0x82, 0xfa, 0xfb, 0x69, 0xda, + 0x92, 0x72, 0x8b, 0x1a, 0x71, 0xde, 0x0a, 0x9e, 0x06, 0x0b, 0x29, 0x05, 0xd6, 0xa5, 0xb6, + 0x7e, 0xcd, 0x3b, 0x36, 0x92, 0xdd, 0xbd, 0x7f, 0x2d, 0x77, 0x8b, 0x8c, 0x98, 0x03, 0xae, + 0xe3, 0x28, 0x09, 0x1b, 0x58, 0xfa, 0xb3, 0x24, 0xe4, 0xfa, 0xd6, 0x75, 0x94, 0x55, 0x85, + 0x80, 0x8b, 0x48, 0x31, 0xd7, 0xbc, 0x3f, 0xf4, 0xde, 0xf0, 0x8e, 0x4b, 0x7a, 0x9d, 0xe5, + 0x76, 0xd2, 0x65, 0x86, 0xce, 0xc6, 0x4b, 0x61, 0x16, + ]; + + const TAG: &[u8] = &[ + 0x1a, 0xe1, 0x0b, 0x59, 0x4f, 0x09, 0xe2, 0x6a, 0x7e, 0x90, 0x2e, 0xcb, 0xd0, 0x60, 0x06, + 0x91, + ]; + + impl_tests!( + ChaCha20Poly1305, + KEY, + NONCE, + AAD, + PLAINTEXT, + CIPHERTEXT, + TAG + ); +} - let mut cipher = ChaCha20Poly1305::new(*key); - assert!(cipher.decrypt(AAD, nonce, &ciphertext).is_err()); +/// XChaCha20Poly1305 test vectors. +/// +/// From +mod xchacha20 { + use super::{AAD, KEY, PLAINTEXT}; + use chacha20poly1305::aead::generic_array::GenericArray; + use chacha20poly1305::aead::{Aead, NewAead}; + use chacha20poly1305::XChaCha20Poly1305; + + const NONCE: &[u8; 24] = &[ + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, + 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + ]; + + const CIPHERTEXT: &[u8] = &[ + 0xbd, 0x6d, 0x17, 0x9d, 0x3e, 0x83, 0xd4, 0x3b, 0x95, 0x76, 0x57, 0x94, 0x93, 0xc0, 0xe9, + 0x39, 0x57, 0x2a, 0x17, 0x00, 0x25, 0x2b, 0xfa, 0xcc, 0xbe, 0xd2, 0x90, 0x2c, 0x21, 0x39, + 0x6c, 0xbb, 0x73, 0x1c, 0x7f, 0x1b, 0x0b, 0x4a, 0xa6, 0x44, 0x0b, 0xf3, 0xa8, 0x2f, 0x4e, + 0xda, 0x7e, 0x39, 0xae, 0x64, 0xc6, 0x70, 0x8c, 0x54, 0xc2, 0x16, 0xcb, 0x96, 0xb7, 0x2e, + 0x12, 0x13, 0xb4, 0x52, 0x2f, 0x8c, 0x9b, 0xa4, 0x0d, 0xb5, 0xd9, 0x45, 0xb1, 0x1b, 0x69, + 0xb9, 0x82, 0xc1, 0xbb, 0x9e, 0x3f, 0x3f, 0xac, 0x2b, 0xc3, 0x69, 0x48, 0x8f, 0x76, 0xb2, + 0x38, 0x35, 0x65, 0xd3, 0xff, 0xf9, 0x21, 0xf9, 0x66, 0x4c, 0x97, 0x63, 0x7d, 0xa9, 0x76, + 0x88, 0x12, 0xf6, 0x15, 0xc6, 0x8b, 0x13, 0xb5, 0x2e, + ]; + + const TAG: &[u8] = &[ + 0xc0, 0x87, 0x59, 0x24, 0xc1, 0xc7, 0x98, 0x79, 0x47, 0xde, 0xaf, 0xd8, 0x78, 0x0a, 0xcf, + 0x49, + ]; + + impl_tests!( + XChaCha20Poly1305, + KEY, + NONCE, + AAD, + PLAINTEXT, + CIPHERTEXT, + TAG + ); }