// You may not use this file except in accordance with one or both of these
// licenses.
-use std::io;
+use crate::io;
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
mod real_chacha {
- use std::cmp;
- use util::byte_utils::{slice_to_le32, le32_to_array};
+ use core::cmp;
+ use core::convert::TryInto;
#[derive(Clone, Copy, PartialEq, Eq)]
#[allow(non_camel_case_types)]
struct u32x4(pub u32, pub u32, pub u32, pub u32);
- impl ::std::ops::Add for u32x4 {
+ impl ::core::ops::Add for u32x4 {
type Output = u32x4;
+ #[inline]
fn add(self, rhs: u32x4) -> u32x4 {
u32x4(self.0.wrapping_add(rhs.0),
self.1.wrapping_add(rhs.1),
self.3.wrapping_add(rhs.3))
}
}
- impl ::std::ops::Sub for u32x4 {
+ impl ::core::ops::Sub for u32x4 {
type Output = u32x4;
+ #[inline]
fn sub(self, rhs: u32x4) -> u32x4 {
u32x4(self.0.wrapping_sub(rhs.0),
self.1.wrapping_sub(rhs.1),
self.3.wrapping_sub(rhs.3))
}
}
- impl ::std::ops::BitXor for u32x4 {
+ impl ::core::ops::BitXor for u32x4 {
type Output = u32x4;
+ #[inline]
fn bitxor(self, rhs: u32x4) -> u32x4 {
u32x4(self.0 ^ rhs.0, self.1 ^ rhs.1, self.2 ^ rhs.2, self.3 ^ rhs.3)
}
}
- impl ::std::ops::Shr<u32x4> for u32x4 {
+ impl ::core::ops::Shr<u8> for u32x4 {
type Output = u32x4;
- fn shr(self, rhs: u32x4) -> u32x4 {
- u32x4(self.0 >> rhs.0, self.1 >> rhs.1, self.2 >> rhs.2, self.3 >> rhs.3)
+ #[inline]
+ fn shr(self, shr: u8) -> u32x4 {
+ u32x4(self.0 >> shr, self.1 >> shr, self.2 >> shr, self.3 >> shr)
}
}
- impl ::std::ops::Shl<u32x4> for u32x4 {
+ impl ::core::ops::Shl<u8> for u32x4 {
type Output = u32x4;
- fn shl(self, rhs: u32x4) -> u32x4 {
- u32x4(self.0 << rhs.0, self.1 << rhs.1, self.2 << rhs.2, self.3 << rhs.3)
+ #[inline]
+ fn shl(self, shl: u8) -> u32x4 {
+ u32x4(self.0 << shl, self.1 << shl, self.2 << shl, self.3 << shl)
+ }
+ }
+ impl u32x4 {
+ #[inline]
+ fn from_bytes(bytes: &[u8]) -> Self {
+ assert_eq!(bytes.len(), 4*4);
+ Self (
+ u32::from_le_bytes(bytes[0*4..1*4].try_into().expect("len is 4")),
+ u32::from_le_bytes(bytes[1*4..2*4].try_into().expect("len is 4")),
+ u32::from_le_bytes(bytes[2*4..3*4].try_into().expect("len is 4")),
+ u32::from_le_bytes(bytes[3*4..4*4].try_into().expect("len is 4")),
+ )
}
}
d1,d2,d3,d4
];
for i in 0..lens.len() {
- $output[i*4..(i+1)*4].copy_from_slice(&le32_to_array(lens[i]));
+ $output[i*4..(i+1)*4].copy_from_slice(&lens[i].to_le_bytes());
}
}}
}
macro_rules! round{
($state: expr) => {{
$state.a = $state.a + $state.b;
- rotate!($state.d, $state.a, S16);
+ rotate!($state.d, $state.a, 16);
$state.c = $state.c + $state.d;
- rotate!($state.b, $state.c, S12);
+ rotate!($state.b, $state.c, 12);
$state.a = $state.a + $state.b;
- rotate!($state.d, $state.a, S8);
+ rotate!($state.d, $state.a, 8);
$state.c = $state.c + $state.d;
- rotate!($state.b, $state.c, S7);
+ rotate!($state.b, $state.c, 7);
}}
}
macro_rules! rotate {
- ($a: expr, $b: expr, $c:expr) => {{
+ ($a: expr, $b: expr, $rot: expr) => {{
let v = $a ^ $b;
- let r = S32 - $c;
+ let r = 32 - $rot;
let right = v >> r;
- $a = (v << $c) ^ right
+ $a = (v << $rot) ^ right
}}
}
- const S32:u32x4 = u32x4(32, 32, 32, 32);
- const S16:u32x4 = u32x4(16, 16, 16, 16);
- const S12:u32x4 = u32x4(12, 12, 12, 12);
- const S8:u32x4 = u32x4(8, 8, 8, 8);
- const S7:u32x4 = u32x4(7, 7, 7, 7);
-
impl ChaCha20 {
pub fn new(key: &[u8], nonce: &[u8]) -> ChaCha20 {
assert!(key.len() == 16 || key.len() == 32);
ChaCha20{ state: ChaCha20::expand(key, nonce), output: [0u8; BLOCK_SIZE], offset: 64 }
}
+ /// Get one block from a ChaCha stream.
+ pub fn get_single_block(key: &[u8; 32], nonce: &[u8; 16]) -> [u8; 32] {
+ let mut chacha = ChaCha20 { state: ChaCha20::expand(key, nonce), output: [0u8; BLOCK_SIZE], offset: 64 };
+ let mut chacha_bytes = [0; 32];
+ chacha.process_in_place(&mut chacha_bytes);
+ chacha_bytes
+ }
+
+ /// Encrypts `src` into `dest` using a single block from a ChaCha stream. Passing `dest` as
+ /// `src` in a second call will decrypt it.
+ pub fn encrypt_single_block(
+ key: &[u8; 32], nonce: &[u8; 16], dest: &mut [u8], src: &[u8]
+ ) {
+ debug_assert_eq!(dest.len(), src.len());
+ debug_assert!(dest.len() <= 32);
+
+ let block = ChaCha20::get_single_block(key, nonce);
+ for i in 0..dest.len() {
+ dest[i] = block[i] ^ src[i];
+ }
+ }
+
+ /// Same as `encrypt_single_block` only operates on a fixed-size input in-place.
+ pub fn encrypt_single_block_in_place(
+ key: &[u8; 32], nonce: &[u8; 16], bytes: &mut [u8; 32]
+ ) {
+ let block = ChaCha20::get_single_block(key, nonce);
+ for i in 0..bytes.len() {
+ bytes[i] = block[i] ^ bytes[i];
+ }
+ }
+
fn expand(key: &[u8], nonce: &[u8]) -> ChaChaState {
let constant = match key.len() {
16 => b"expand 16-byte k",
_ => unreachable!(),
};
ChaChaState {
- a: u32x4(
- slice_to_le32(&constant[0..4]),
- slice_to_le32(&constant[4..8]),
- slice_to_le32(&constant[8..12]),
- slice_to_le32(&constant[12..16])
- ),
- b: u32x4(
- slice_to_le32(&key[0..4]),
- slice_to_le32(&key[4..8]),
- slice_to_le32(&key[8..12]),
- slice_to_le32(&key[12..16])
- ),
+ a: u32x4::from_bytes(&constant[0..16]),
+ b: u32x4::from_bytes(&key[0..16]),
c: if key.len() == 16 {
- u32x4(
- slice_to_le32(&key[0..4]),
- slice_to_le32(&key[4..8]),
- slice_to_le32(&key[8..12]),
- slice_to_le32(&key[12..16])
- )
+ u32x4::from_bytes(&key[0..16])
} else {
- u32x4(
- slice_to_le32(&key[16..20]),
- slice_to_le32(&key[20..24]),
- slice_to_le32(&key[24..28]),
- slice_to_le32(&key[28..32])
- )
+ u32x4::from_bytes(&key[16..32])
},
d: if nonce.len() == 16 {
- u32x4(
- slice_to_le32(&nonce[0..4]),
- slice_to_le32(&nonce[4..8]),
- slice_to_le32(&nonce[8..12]),
- slice_to_le32(&nonce[12..16])
- )
+ u32x4::from_bytes(&nonce[0..16])
} else if nonce.len() == 12 {
- u32x4(
- 0,
- slice_to_le32(&nonce[0..4]),
- slice_to_le32(&nonce[4..8]),
- slice_to_le32(&nonce[8..12])
- )
+ let mut nonce4 = [0; 4*4];
+ nonce4[4..].copy_from_slice(nonce);
+ u32x4::from_bytes(&nonce4)
} else {
- u32x4(
- 0,
- 0,
- slice_to_le32(&nonce[0..4]),
- slice_to_le32(&nonce[4..8])
- )
+ let mut nonce4 = [0; 4*4];
+ nonce4[8..].copy_from_slice(nonce);
+ u32x4::from_bytes(&nonce4)
}
}
}
self.offset += count;
}
}
+
+ #[cfg(test)]
+ pub fn seek_to_block(&mut self, block_offset: u32) {
+ self.state.d.0 = block_offset;
+ self.update();
+ }
}
}
-#[cfg(not(feature = "fuzztarget"))]
+#[cfg(not(fuzzing))]
pub use self::real_chacha::ChaCha20;
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
mod fuzzy_chacha {
pub struct ChaCha20 {}
Self {}
}
+ pub fn get_single_block(_key: &[u8; 32], _nonce: &[u8; 16]) -> [u8; 32] {
+ [0; 32]
+ }
+
+ pub fn encrypt_single_block(
+ _key: &[u8; 32], _nonce: &[u8; 16], dest: &mut [u8], src: &[u8]
+ ) {
+ debug_assert_eq!(dest.len(), src.len());
+ debug_assert!(dest.len() <= 32);
+ }
+
+ pub fn encrypt_single_block_in_place(
+ _key: &[u8; 32], _nonce: &[u8; 16], _bytes: &mut [u8; 32]
+ ) {}
+
pub fn process(&mut self, input: &[u8], output: &mut [u8]) {
output.copy_from_slice(input);
}
pub fn process_in_place(&mut self, _input_output: &mut [u8]) {}
}
}
-#[cfg(feature = "fuzztarget")]
+#[cfg(fuzzing)]
pub use self::fuzzy_chacha::ChaCha20;
pub(crate) struct ChaChaReader<'a, R: io::Read> {
#[cfg(test)]
mod test {
- use std::iter::repeat;
+ use crate::prelude::*;
+ use core::iter::repeat;
use super::ChaCha20;
+ use std::convert::TryInto;
#[test]
fn test_chacha20_256_tls_vectors() {
assert_eq!(output, tv.keystream);
}
}
+
+ #[test]
+ fn get_single_block() {
+ // Test that `get_single_block` (which takes a 16-byte nonce) is equivalent to getting a block
+ // using a 12-byte nonce, with the block starting at the counter offset given by the remaining 4
+ // bytes.
+ let key = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ];
+ let nonce_16bytes = [
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b
+ ];
+ let counter_pos = &nonce_16bytes[..4];
+ let nonce_12bytes = &nonce_16bytes[4..];
+
+ // Initialize a ChaCha20 instance with its counter starting at 0.
+ let mut chacha20 = ChaCha20::new(&key, nonce_12bytes);
+ // Seek its counter to the block at counter_pos.
+ chacha20.seek_to_block(u32::from_le_bytes(counter_pos.try_into().unwrap()));
+ let mut block_bytes = [0; 32];
+ chacha20.process_in_place(&mut block_bytes);
+
+ assert_eq!(ChaCha20::get_single_block(&key, &nonce_16bytes), block_bytes);
+ }
+
+ #[test]
+ fn encrypt_single_block() {
+ let key = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ];
+ let nonce = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ ];
+ let bytes = [1; 32];
+
+ let mut encrypted_bytes = [0; 32];
+ ChaCha20::encrypt_single_block(&key, &nonce, &mut encrypted_bytes, &bytes);
+
+ let mut decrypted_bytes = [0; 32];
+ ChaCha20::encrypt_single_block(&key, &nonce, &mut decrypted_bytes, &encrypted_bytes);
+
+ assert_eq!(bytes, decrypted_bytes);
+ }
+
+ #[test]
+ fn encrypt_single_block_in_place() {
+ let key = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ];
+ let nonce = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ ];
+ let unencrypted_bytes = [1; 32];
+ let mut bytes = unencrypted_bytes;
+
+ ChaCha20::encrypt_single_block_in_place(&key, &nonce, &mut bytes);
+ assert_ne!(bytes, unencrypted_bytes);
+
+ ChaCha20::encrypt_single_block_in_place(&key, &nonce, &mut bytes);
+ assert_eq!(bytes, unencrypted_bytes);
+ }
}