Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 61 additions & 41 deletions chacha20/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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<u8, U32>, iv: &GenericArray<u8, U12>) -> 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<u8, U32>, iv: &GenericArray<u8, U12>) -> 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")]
Expand Down
130 changes: 77 additions & 53 deletions chacha20/src/rng.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ChaCha20RngCore>);
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."
);