diff --git a/chacha20/src/lib.rs b/chacha20/src/lib.rs index 3271b75d..618b9f74 100644 --- a/chacha20/src/lib.rs +++ b/chacha20/src/lib.rs @@ -12,6 +12,7 @@ //! //! - `ChaCha20`: standard IETF variant with 96-bit nonce //! - `ChaCha20Legacy`: (gated under the `legacy` feature) "djb" variant with 64-bit nonce +//! - `ChaCha8` / `ChaCha12`: reduced round variants of ChaCha20 //! - `XChaCha20`: (gated under the `xchacha20` feature) 192-bit extended nonce variant //! //! # Security Warning @@ -86,7 +87,9 @@ use stream_cipher::{LoopError, NewStreamCipher, SyncStreamCipher, SyncStreamCiph pub use self::xchacha20::XChaCha20; #[cfg(feature = "rng")] -pub use rng::{ChaCha20Rng, ChaCha20RngCore}; +pub use rng::{ + ChaCha12Rng, ChaCha12RngCore, ChaCha20Rng, ChaCha20RngCore, ChaCha8Rng, ChaCha8RngCore, +}; /// Size of a ChaCha20 block in bytes pub const BLOCK_SIZE: usize = 64; @@ -108,49 +111,66 @@ const STATE_WORDS: usize = 16; //pub(crate) const SIGMA: &[u8; 16] = b"expand 32-byte k"; const CONSTANTS: [u32; 4] = [0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]; -/// The ChaCha20 stream cipher (RFC 8439 version with 96-bit nonce) -/// -/// Use `ChaCha20Legacy` for the legacy (a.k.a. "djb") construction with a -/// 64-bit nonce. -#[cfg(feature = "stream-cipher")] -pub struct ChaCha20(Cipher); - -#[cfg(feature = "stream-cipher")] -impl NewStreamCipher for ChaCha20 { - /// Key size in bytes - type KeySize = U32; - - /// Nonce size in bytes - type NonceSize = U12; - - fn new(key: &GenericArray, iv: &GenericArray) -> Self { - let block = Block::new( - key.as_ref().try_into().unwrap(), - iv[4..12].try_into().unwrap(), - 20, - ); - let counter = initial_counter(iv[..4].try_into().unwrap()); - ChaCha20(Cipher::new(block, counter)) +macro_rules! impl_chacha { + ($name:ident, $rounds:expr, $doc:expr) => { + #[cfg(feature = "stream-cipher")] + #[doc = $doc] + pub struct $name(Cipher); + + #[cfg(feature = "stream-cipher")] + impl NewStreamCipher for $name { + /// Key size in bytes + type KeySize = U32; + + /// Nonce size in bytes + type NonceSize = U12; + + fn new(key: &GenericArray, iv: &GenericArray) -> Self { + let block = Block::new( + key.as_ref().try_into().unwrap(), + iv[4..12].try_into().unwrap(), + $rounds, + ); + let counter = initial_counter(iv[..4].try_into().unwrap()); + $name(Cipher::new(block, counter)) + } + } + + #[cfg(feature = "stream-cipher")] + impl SyncStreamCipher for $name { + fn try_apply_keystream(&mut self, data: &mut [u8]) -> Result<(), LoopError> { + self.0.try_apply_keystream(data) + } + } + + #[cfg(feature = "stream-cipher")] + impl SyncStreamCipherSeek for $name { + fn current_pos(&self) -> u64 { + self.0.current_pos() + } + + fn seek(&mut self, pos: u64) { + self.0.seek(pos); + } + } } } -#[cfg(feature = "stream-cipher")] -impl SyncStreamCipher for ChaCha20 { - fn try_apply_keystream(&mut self, data: &mut [u8]) -> Result<(), LoopError> { - self.0.try_apply_keystream(data) - } -} - -#[cfg(feature = "stream-cipher")] -impl SyncStreamCipherSeek for ChaCha20 { - fn current_pos(&self) -> u64 { - self.0.current_pos() - } - - fn seek(&mut self, pos: u64) { - self.0.seek(pos); - } -} +impl_chacha!( + ChaCha8, + 8, + "The ChaCha8 stream cipher (8-round variant of ChaCha20)" +); +impl_chacha!( + ChaCha12, + 12, + "The ChaCha20 stream cipher (12-round variant of ChaCha20)" +); +impl_chacha!( + ChaCha20, + 20, + "The ChaCha20 stream cipher (RFC 8439 version with 96-bit nonce)" +); /// Get initial counter value for the given IV prefix #[cfg(feature = "stream-cipher")] diff --git a/chacha20/src/rng.rs b/chacha20/src/rng.rs index 1204c5db..e6b59fff 100644 --- a/chacha20/src/rng.rs +++ b/chacha20/src/rng.rs @@ -6,69 +6,93 @@ use rand_core::{Error, RngCore, SeedableRng}; use crate::{block::Block, BLOCK_SIZE, KEY_SIZE, STATE_WORDS}; -/// Random number generator over the ChaCha20 stream cipher. -#[derive(Clone, Debug)] -pub struct ChaCha20Rng(BlockRng); +macro_rules! impl_chacha_rng { + ($name:ident, $core:ident, $rounds:expr, $doc:expr) => { + #[doc = $doc] + #[derive(Clone, Debug)] + pub struct $name(BlockRng<$core>); -impl SeedableRng for ChaCha20Rng { - type Seed = [u8; KEY_SIZE]; + impl SeedableRng for $name { + type Seed = [u8; KEY_SIZE]; - #[inline] - fn from_seed(seed: Self::Seed) -> Self { - let core = ChaCha20RngCore::from_seed(seed); - Self(BlockRng::new(core)) - } -} + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + let core = $core::from_seed(seed); + Self(BlockRng::new(core)) + } + } -impl RngCore for ChaCha20Rng { - #[inline] - fn next_u32(&mut self) -> u32 { - self.0.next_u32() - } + impl RngCore for $name { + #[inline] + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } - #[inline] - fn next_u64(&mut self) -> u64 { - self.0.next_u64() - } + #[inline] + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } - #[inline] - fn fill_bytes(&mut self, bytes: &mut [u8]) { - self.0.fill_bytes(bytes) - } + #[inline] + fn fill_bytes(&mut self, bytes: &mut [u8]) { + self.0.fill_bytes(bytes) + } - #[inline] - fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { - self.0.try_fill_bytes(bytes) - } -} + #[inline] + fn try_fill_bytes(&mut self, bytes: &mut [u8]) -> Result<(), Error> { + self.0.try_fill_bytes(bytes) + } + } -/// Core of the [`ChaCha20Rng`] random number generator, for use with -/// [`rand_core::block::BlockRng`]. -#[derive(Clone, Debug)] -pub struct ChaCha20RngCore { - block: Block, - counter: u64, -} + #[doc = "Core random number generator, for use with [`rand_core::block::BlockRng`]"] + #[derive(Clone, Debug)] + pub struct $core { + block: Block, + counter: u64, + } -impl SeedableRng for ChaCha20RngCore { - type Seed = [u8; KEY_SIZE]; + impl SeedableRng for $core { + type Seed = [u8; KEY_SIZE]; - #[inline] - fn from_seed(seed: Self::Seed) -> Self { - let block = Block::new(&seed, Default::default(), 20); - Self { block, counter: 0 } - } -} + #[inline] + fn from_seed(seed: Self::Seed) -> Self { + let block = Block::new(&seed, Default::default(), $rounds); + Self { block, counter: 0 } + } + } -impl BlockRngCore for ChaCha20RngCore { - type Item = u32; - type Results = [u32; STATE_WORDS]; + impl BlockRngCore for $core { + type Item = u32; + type Results = [u32; STATE_WORDS]; - fn generate(&mut self, results: &mut Self::Results) { - // TODO(tarcieri): eliminate unsafety (replace w\ [u8; BLOCK_SIZE) - self.block.generate(self.counter, unsafe { - slice::from_raw_parts_mut(results.as_mut_ptr() as *mut u8, BLOCK_SIZE) - }); - self.counter += 1; + fn generate(&mut self, results: &mut Self::Results) { + // TODO(tarcieri): eliminate unsafety (replace w\ [u8; BLOCK_SIZE) + self.block.generate(self.counter, unsafe { + slice::from_raw_parts_mut(results.as_mut_ptr() as *mut u8, BLOCK_SIZE) + }); + self.counter += 1; + } + } } } + +impl_chacha_rng!( + ChaCha8Rng, + ChaCha8RngCore, + 8, + "Random number generator over the ChaCha8 stream cipher." +); + +impl_chacha_rng!( + ChaCha12Rng, + ChaCha12RngCore, + 12, + "Random number generator over the ChaCha12 stream cipher." +); + +impl_chacha_rng!( + ChaCha20Rng, + ChaCha20RngCore, + 20, + "Random number generator over the ChaCha20 stream cipher." +);