Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chacha: integrate c2-chacha dependency #931

Merged
merged 5 commits into from
Jan 20, 2020
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
3 changes: 3 additions & 0 deletions rand_chacha/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.2.2] - 2020-01-20
- Integrate `c2-chacha`, reducing dependency count (#931)

## [0.2.1] - 2019-07-22
- Force enable the `simd` feature of `c2-chacha` (#845)

Expand Down
6 changes: 3 additions & 3 deletions rand_chacha/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rand_chacha"
version = "0.2.1"
version = "0.2.2"
authors = ["The Rand Project Developers", "The Rust Project Developers", "The CryptoCorrosion Contributors"]
license = "MIT OR Apache-2.0"
readme = "README.md"
Expand All @@ -20,9 +20,9 @@ appveyor = { repository = "rust-random/rand" }

[dependencies]
rand_core = { path = "../rand_core", version = "0.5" }
c2-chacha = { version = "0.2.2", default-features = false, features = ["simd"] }
ppv-lite86 = { version = "0.2.6", default-features = false, features = ["simd"] }

[features]
default = ["std", "simd"]
std = ["c2-chacha/std"]
std = ["ppv-lite86/std"]
simd = [] # deprecated
2 changes: 1 addition & 1 deletion rand_chacha/src/chacha.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#[cfg(feature = "std")] use std as core;

use self::core::fmt;
use c2_chacha::guts::ChaCha;
use crate::guts::ChaCha;
use rand_core::block::{BlockRng, BlockRngCore};
use rand_core::{CryptoRng, Error, RngCore, SeedableRng};

Expand Down
243 changes: 243 additions & 0 deletions rand_chacha/src/guts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
// Copyright 2019 The CryptoCorrosion Contributors
dhardy marked this conversation as resolved.
Show resolved Hide resolved
// Copyright 2020 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The ChaCha random number generator.

use ppv_lite86::{dispatch, dispatch_light128};

pub use ppv_lite86::Machine;
use ppv_lite86::{vec128_storage, ArithOps, BitOps32, LaneWords4, MultiLane, StoreBytes, Vec4};

pub(crate) const BLOCK: usize = 64;
pub(crate) const BLOCK64: u64 = BLOCK as u64;
const LOG2_BUFBLOCKS: u64 = 2;
const BUFBLOCKS: u64 = 1 << LOG2_BUFBLOCKS;
pub(crate) const BUFSZ64: u64 = BLOCK64 * BUFBLOCKS;
pub(crate) const BUFSZ: usize = BUFSZ64 as usize;

#[derive(Clone)]
pub struct ChaCha {
pub(crate) b: vec128_storage,
pub(crate) c: vec128_storage,
pub(crate) d: vec128_storage,
}

#[derive(Clone)]
pub struct State<V> {
pub(crate) a: V,
pub(crate) b: V,
pub(crate) c: V,
pub(crate) d: V,
}

#[inline(always)]
pub(crate) fn round<V: ArithOps + BitOps32>(mut x: State<V>) -> State<V> {
x.a += x.b;
x.d = (x.d ^ x.a).rotate_each_word_right16();
x.c += x.d;
x.b = (x.b ^ x.c).rotate_each_word_right20();
x.a += x.b;
x.d = (x.d ^ x.a).rotate_each_word_right24();
x.c += x.d;
x.b = (x.b ^ x.c).rotate_each_word_right25();
x
}

#[inline(always)]
pub(crate) fn diagonalize<V: LaneWords4>(mut x: State<V>) -> State<V> {
x.b = x.b.shuffle_lane_words3012();
x.c = x.c.shuffle_lane_words2301();
x.d = x.d.shuffle_lane_words1230();
x
}
#[inline(always)]
pub(crate) fn undiagonalize<V: LaneWords4>(mut x: State<V>) -> State<V> {
x.b = x.b.shuffle_lane_words1230();
x.c = x.c.shuffle_lane_words2301();
x.d = x.d.shuffle_lane_words3012();
x
}

impl ChaCha {
#[inline(always)]
pub fn new(key: &[u8; 32], nonce: &[u8]) -> Self {
init_chacha(key, nonce)
}

#[inline(always)]
fn pos64<M: Machine>(&self, m: M) -> u64 {
let d: M::u32x4 = m.unpack(self.d);
((d.extract(1) as u64) << 32) | d.extract(0) as u64
}

/// Produce 4 blocks of output, advancing the state
#[inline(always)]
pub fn refill4(&mut self, drounds: u32, out: &mut [u8; BUFSZ]) {
refill_wide(self, drounds, out)
}

#[inline(always)]
pub fn set_stream_param(&mut self, param: u32, value: u64) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might want to cut all the *_stream_param stuff--it's unused and not verified against any official test vectors.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do use this in set_word_pos and set_stream methods on the RNGs.

We have a couple of our own tests using these methods, although only with three constants in total.

set_stream_param(self, param, value)
}

#[inline(always)]
pub fn get_stream_param(&self, param: u32) -> u64 {
get_stream_param(self, param)
}
}

#[inline(always)]
fn refill_wide_impl<Mach: Machine>(
m: Mach, state: &mut ChaCha, drounds: u32, out: &mut [u8; BUFSZ],
) {
let k = m.vec([0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]);
let mut pos = state.pos64(m);
let d0: Mach::u32x4 = m.unpack(state.d);
pos += 1;
let d1 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0);
pos += 1;
let d2 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0);
pos += 1;
let d3 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0);

let b = m.unpack(state.b);
let c = m.unpack(state.c);
let mut x = State {
a: Mach::u32x4x4::from_lanes([k, k, k, k]),
b: Mach::u32x4x4::from_lanes([b, b, b, b]),
c: Mach::u32x4x4::from_lanes([c, c, c, c]),
d: m.unpack(Mach::u32x4x4::from_lanes([d0, d1, d2, d3]).into()),
};
for _ in 0..drounds {
x = round(x);
x = undiagonalize(round(diagonalize(x)));
}
let mut pos = state.pos64(m);
let d0: Mach::u32x4 = m.unpack(state.d);
pos += 1;
let d1 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0);
pos += 1;
let d2 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0);
pos += 1;
let d3 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0);
pos += 1;
let d4 = d0.insert((pos >> 32) as u32, 1).insert(pos as u32, 0);

let (a, b, c, d) = (
x.a.to_lanes(),
x.b.to_lanes(),
x.c.to_lanes(),
x.d.to_lanes(),
);
let sb = m.unpack(state.b);
let sc = m.unpack(state.c);
let sd = [m.unpack(state.d), d1, d2, d3];
state.d = d4.into();
let mut words = out.chunks_exact_mut(16);
for ((((&a, &b), &c), &d), &sd) in a.iter().zip(&b).zip(&c).zip(&d).zip(&sd) {
(a + k).write_le(words.next().unwrap());
(b + sb).write_le(words.next().unwrap());
(c + sc).write_le(words.next().unwrap());
(d + sd).write_le(words.next().unwrap());
}
}

dispatch!(m, Mach, {
fn refill_wide(state: &mut ChaCha, drounds: u32, out: &mut [u8; BUFSZ]) {
refill_wide_impl(m, state, drounds, out);
}
});

// Single-block, rounds-only; shared by try_apply_keystream for tails shorter than BUFSZ
// and XChaCha's setup step.
dispatch!(m, Mach, {
fn refill_narrow_rounds(state: &mut ChaCha, drounds: u32) -> State<vec128_storage> {
let k: Mach::u32x4 = m.vec([0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]);
let mut x = State {
a: k,
b: m.unpack(state.b),
c: m.unpack(state.c),
d: m.unpack(state.d),
};
for _ in 0..drounds {
x = round(x);
x = undiagonalize(round(diagonalize(x)));
}
State {
a: x.a.into(),
b: x.b.into(),
c: x.c.into(),
d: x.d.into(),
}
}
});

dispatch_light128!(m, Mach, {
fn set_stream_param(state: &mut ChaCha, param: u32, value: u64) {
let d: Mach::u32x4 = m.unpack(state.d);
state.d = d
.insert((value >> 32) as u32, (param << 1) | 1)
.insert(value as u32, param << 1)
.into();
}
});

dispatch_light128!(m, Mach, {
fn get_stream_param(state: &ChaCha, param: u32) -> u64 {
let d: Mach::u32x4 = m.unpack(state.d);
((d.extract((param << 1) | 1) as u64) << 32) | d.extract(param << 1) as u64
}
});

fn read_u32le(xs: &[u8]) -> u32 {
assert_eq!(xs.len(), 4);
u32::from(xs[0]) | (u32::from(xs[1]) << 8) | (u32::from(xs[2]) << 16) | (u32::from(xs[3]) << 24)
}

dispatch_light128!(m, Mach, {
fn init_chacha(key: &[u8; 32], nonce: &[u8]) -> ChaCha {
let ctr_nonce = [
0,
if nonce.len() == 12 {
read_u32le(&nonce[0..4])
} else {
0
},
read_u32le(&nonce[nonce.len() - 8..nonce.len() - 4]),
read_u32le(&nonce[nonce.len() - 4..]),
];
let key0: Mach::u32x4 = m.read_le(&key[..16]);
let key1: Mach::u32x4 = m.read_le(&key[16..]);
ChaCha {
b: key0.into(),
c: key1.into(),
d: ctr_nonce.into(),
}
}
});

dispatch_light128!(m, Mach, {
fn init_chacha_x(key: &[u8; 32], nonce: &[u8; 24], rounds: u32) -> ChaCha {
let key0: Mach::u32x4 = m.read_le(&key[..16]);
let key1: Mach::u32x4 = m.read_le(&key[16..]);
let nonce0: Mach::u32x4 = m.read_le(&nonce[..16]);
let mut state = ChaCha {
b: key0.into(),
c: key1.into(),
d: nonce0.into(),
};
let x = refill_narrow_rounds(&mut state, rounds);
let ctr_nonce1 = [0, 0, read_u32le(&nonce[16..20]), read_u32le(&nonce[20..24])];
state.b = x.a;
state.c = x.d;
state.d = ctr_nonce1.into();
state
}
});
1 change: 1 addition & 0 deletions rand_chacha/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
pub use rand_core;

mod chacha;
mod guts;

pub use crate::chacha::{
ChaCha12Core, ChaCha12Rng, ChaCha20Core, ChaCha20Rng, ChaCha8Core, ChaCha8Rng,
Expand Down