1 // Copyright 2019 The CryptoCorrosion Contributors
2 // Copyright 2020 Developers of the Rand project.
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
10 //! The ChaCha random number generator.
12 use ppv_lite86
::{dispatch, dispatch_light128}
;
14 pub use ppv_lite86
::Machine
;
15 use ppv_lite86
::{vec128_storage, ArithOps, BitOps32, LaneWords4, MultiLane, StoreBytes, Vec4}
;
17 pub(crate) const BLOCK
: usize = 64;
18 pub(crate) const BLOCK64
: u64 = BLOCK
as u64;
19 const LOG2_BUFBLOCKS
: u64 = 2;
20 const BUFBLOCKS
: u64 = 1 << LOG2_BUFBLOCKS
;
21 pub(crate) const BUFSZ64
: u64 = BLOCK64
* BUFBLOCKS
;
22 pub(crate) const BUFSZ
: usize = BUFSZ64
as usize;
26 pub(crate) b
: vec128_storage
,
27 pub(crate) c
: vec128_storage
,
28 pub(crate) d
: vec128_storage
,
40 pub(crate) fn round
<V
: ArithOps
+ BitOps32
>(mut x
: State
<V
>) -> State
<V
> {
42 x
.d
= (x
.d ^ x
.a
).rotate_each_word_right16();
44 x
.b
= (x
.b ^ x
.c
).rotate_each_word_right20();
46 x
.d
= (x
.d ^ x
.a
).rotate_each_word_right24();
48 x
.b
= (x
.b ^ x
.c
).rotate_each_word_right25();
53 pub(crate) fn diagonalize
<V
: LaneWords4
>(mut x
: State
<V
>) -> State
<V
> {
54 x
.b
= x
.b
.shuffle_lane_words3012();
55 x
.c
= x
.c
.shuffle_lane_words2301();
56 x
.d
= x
.d
.shuffle_lane_words1230();
60 pub(crate) fn undiagonalize
<V
: LaneWords4
>(mut x
: State
<V
>) -> State
<V
> {
61 x
.b
= x
.b
.shuffle_lane_words1230();
62 x
.c
= x
.c
.shuffle_lane_words2301();
63 x
.d
= x
.d
.shuffle_lane_words3012();
69 pub fn new(key
: &[u8; 32], nonce
: &[u8]) -> Self {
70 init_chacha(key
, nonce
)
74 fn pos64
<M
: Machine
>(&self, m
: M
) -> u64 {
75 let d
: M
::u32x4
= m
.unpack(self.d
);
76 ((d
.extract(1) as u64) << 32) | d
.extract(0) as u64
79 /// Produce 4 blocks of output, advancing the state
81 pub fn refill4(&mut self, drounds
: u32, out
: &mut [u8; BUFSZ
]) {
82 refill_wide(self, drounds
, out
)
86 pub fn set_stream_param(&mut self, param
: u32, value
: u64) {
87 set_stream_param(self, param
, value
)
91 pub fn get_stream_param(&self, param
: u32) -> u64 {
92 get_stream_param(self, param
)
97 fn refill_wide_impl
<Mach
: Machine
>(
98 m
: Mach
, state
: &mut ChaCha
, drounds
: u32, out
: &mut [u8; BUFSZ
],
100 let k
= m
.vec([0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]);
101 let mut pos
= state
.pos64(m
);
102 let d0
: Mach
::u32x4
= m
.unpack(state
.d
);
104 let d1
= d0
.insert((pos
>> 32) as u32, 1).insert(pos
as u32, 0);
106 let d2
= d0
.insert((pos
>> 32) as u32, 1).insert(pos
as u32, 0);
108 let d3
= d0
.insert((pos
>> 32) as u32, 1).insert(pos
as u32, 0);
110 let b
= m
.unpack(state
.b
);
111 let c
= m
.unpack(state
.c
);
113 a
: Mach
::u32x4x4
::from_lanes([k
, k
, k
, k
]),
114 b
: Mach
::u32x4x4
::from_lanes([b
, b
, b
, b
]),
115 c
: Mach
::u32x4x4
::from_lanes([c
, c
, c
, c
]),
116 d
: m
.unpack(Mach
::u32x4x4
::from_lanes([d0
, d1
, d2
, d3
]).into()),
118 for _
in 0..drounds
{
120 x
= undiagonalize(round(diagonalize(x
)));
122 let mut pos
= state
.pos64(m
);
123 let d0
: Mach
::u32x4
= m
.unpack(state
.d
);
125 let d1
= d0
.insert((pos
>> 32) as u32, 1).insert(pos
as u32, 0);
127 let d2
= d0
.insert((pos
>> 32) as u32, 1).insert(pos
as u32, 0);
129 let d3
= d0
.insert((pos
>> 32) as u32, 1).insert(pos
as u32, 0);
131 let d4
= d0
.insert((pos
>> 32) as u32, 1).insert(pos
as u32, 0);
139 let sb
= m
.unpack(state
.b
);
140 let sc
= m
.unpack(state
.c
);
141 let sd
= [m
.unpack(state
.d
), d1
, d2
, d3
];
143 let mut words
= out
.chunks_exact_mut(16);
144 for ((((&a
, &b
), &c
), &d
), &sd
) in a
.iter().zip(&b
).zip(&c
).zip(&d
).zip(&sd
) {
145 (a
+ k
).write_le(words
.next().unwrap());
146 (b
+ sb
).write_le(words
.next().unwrap());
147 (c
+ sc
).write_le(words
.next().unwrap());
148 (d
+ sd
).write_le(words
.next().unwrap());
153 fn refill_wide(state
: &mut ChaCha
, drounds
: u32, out
: &mut [u8; BUFSZ
]) {
154 refill_wide_impl(m
, state
, drounds
, out
);
158 // Single-block, rounds-only; shared by try_apply_keystream for tails shorter than BUFSZ
159 // and XChaCha's setup step.
161 fn refill_narrow_rounds(state
: &mut ChaCha
, drounds
: u32) -> State
<vec128_storage
> {
162 let k
: Mach
::u32x4
= m
.vec([0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]);
165 b
: m
.unpack(state
.b
),
166 c
: m
.unpack(state
.c
),
167 d
: m
.unpack(state
.d
),
169 for _
in 0..drounds
{
171 x
= undiagonalize(round(diagonalize(x
)));
182 dispatch_light128
!(m
, Mach
, {
183 fn set_stream_param(state
: &mut ChaCha
, param
: u32, value
: u64) {
184 let d
: Mach
::u32x4
= m
.unpack(state
.d
);
186 .insert((value
>> 32) as u32, (param
<< 1) | 1)
187 .insert(value
as u32, param
<< 1)
192 dispatch_light128
!(m
, Mach
, {
193 fn get_stream_param(state
: &ChaCha
, param
: u32) -> u64 {
194 let d
: Mach
::u32x4
= m
.unpack(state
.d
);
195 ((d
.extract((param
<< 1) | 1) as u64) << 32) | d
.extract(param
<< 1) as u64
199 fn read_u32le(xs
: &[u8]) -> u32 {
200 assert_eq
!(xs
.len(), 4);
201 u32::from(xs
[0]) | (u32::from(xs
[1]) << 8) | (u32::from(xs
[2]) << 16) | (u32::from(xs
[3]) << 24)
204 dispatch_light128
!(m
, Mach
, {
205 fn init_chacha(key
: &[u8; 32], nonce
: &[u8]) -> ChaCha
{
208 if nonce
.len() == 12 {
209 read_u32le(&nonce
[0..4])
213 read_u32le(&nonce
[nonce
.len() - 8..nonce
.len() - 4]),
214 read_u32le(&nonce
[nonce
.len() - 4..]),
216 let key0
: Mach
::u32x4
= m
.read_le(&key
[..16]);
217 let key1
: Mach
::u32x4
= m
.read_le(&key
[16..]);
226 dispatch_light128
!(m
, Mach
, {
227 fn init_chacha_x(key
: &[u8; 32], nonce
: &[u8; 24], rounds
: u32) -> ChaCha
{
228 let key0
: Mach
::u32x4
= m
.read_le(&key
[..16]);
229 let key1
: Mach
::u32x4
= m
.read_le(&key
[16..]);
230 let nonce0
: Mach
::u32x4
= m
.read_le(&nonce
[..16]);
231 let mut state
= ChaCha
{
236 let x
= refill_narrow_rounds(&mut state
, rounds
);
237 let ctr_nonce1
= [0, 0, read_u32le(&nonce
[16..20]), read_u32le(&nonce
[20..24])];
240 state
.d
= ctr_nonce1
.into();