1#![no_std]
2#![doc = include_str!("../README.md")]
3#![doc(
4 html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
5 html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
6)]
7#![cfg_attr(docsrs, feature(doc_cfg))]
8
9pub mod variants;
10
11mod backends;
12#[cfg(feature = "cipher")]
13mod chacha;
14#[cfg(feature = "legacy")]
15mod legacy;
16#[cfg(feature = "rng")]
17mod rng;
18#[cfg(feature = "xchacha")]
19mod xchacha;
20
21#[cfg(feature = "cipher")]
22pub use chacha::{ChaCha8, ChaCha12, ChaCha20, Key, Nonce};
23#[cfg(feature = "cipher")]
24pub use cipher;
25#[cfg(feature = "cipher")]
26pub use cipher::KeyIvInit;
27#[cfg(feature = "legacy")]
28pub use legacy::{ChaCha20Legacy, ChaCha20LegacyCore, LegacyNonce};
29#[cfg(feature = "rng")]
30pub use rand_core;
31#[cfg(feature = "rng")]
32pub use rng::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng, Seed, SerializedRngState};
33#[cfg(feature = "xchacha")]
34pub use xchacha::{XChaCha8, XChaCha12, XChaCha20, XNonce, hchacha};
35
36use cfg_if::cfg_if;
37use core::{fmt, marker::PhantomData};
38use variants::Variant;
39
40#[cfg(feature = "cipher")]
41use cipher::{BlockSizeUser, StreamCipherCore, StreamCipherSeekCore, consts::U64};
42#[cfg(feature = "zeroize")]
43use zeroize::{Zeroize, ZeroizeOnDrop};
44
45#[cfg(any(feature = "cipher", feature = "rng"))]
47const CONSTANTS: [u32; 4] = [0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574];
48
49const STATE_WORDS: usize = 16;
51
52pub trait Rounds: Copy {
54 const COUNT: usize;
56}
57
58#[derive(Copy, Clone, Debug)]
60pub struct R8;
61
62impl Rounds for R8 {
63 const COUNT: usize = 4;
64}
65
66#[derive(Copy, Clone, Debug)]
68pub struct R12;
69
70impl Rounds for R12 {
71 const COUNT: usize = 6;
72}
73
74#[derive(Copy, Clone, Debug)]
76pub struct R20;
77
78impl Rounds for R20 {
79 const COUNT: usize = 10;
80}
81
82cfg_if! {
83 if #[cfg(chacha20_backend = "soft")] {
84 type Tokens = ();
85 } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
86 cfg_if! {
87 if #[cfg(all(chacha20_avx512, chacha20_backend = "avx512"))] {
88 #[cfg(not(all(target_feature = "avx512f", target_feature = "avx512vl")))]
89 compile_error!("You must enable `avx512f` and `avx512vl` target features with \
90 `chacha20_backend = "avx512"` configuration option");
91 type Tokens = ();
92 } else if #[cfg(chacha20_backend = "avx2")] {
93 #[cfg(not(target_feature = "avx2"))]
94 compile_error!("You must enable `avx2` target feature with \
95 `chacha20_backend = "avx2"` configuration option");
96 type Tokens = ();
97 } else if #[cfg(chacha20_backend = "sse2")] {
98 #[cfg(not(target_feature = "sse2"))]
99 compile_error!("You must enable `sse2` target feature with \
100 `chacha20_backend = "sse2"` configuration option");
101 type Tokens = ();
102 } else {
103 #[cfg(chacha20_avx512)]
104 cpufeatures::new!(avx512_cpuid, "avx512f", "avx512vl");
105 cpufeatures::new!(avx2_cpuid, "avx2");
106 cpufeatures::new!(sse2_cpuid, "sse2");
107 #[cfg(chacha20_avx512)]
108 type Tokens = (avx512_cpuid::InitToken, avx2_cpuid::InitToken, sse2_cpuid::InitToken);
109 #[cfg(not(chacha20_avx512))]
110 type Tokens = (avx2_cpuid::InitToken, sse2_cpuid::InitToken);
111 }
112 }
113 } else {
114 type Tokens = ();
115 }
116}
117
118pub struct ChaChaCore<R: Rounds, V: Variant> {
120 state: [u32; STATE_WORDS],
122 #[allow(dead_code)]
124 tokens: Tokens,
125 _pd: PhantomData<(R, V)>,
127}
128
129impl<R: Rounds, V: Variant> ChaChaCore<R, V> {
130 #[must_use]
138 #[cfg(any(feature = "cipher", feature = "rng"))]
139 fn new_internal(key: &[u8; 32], iv: &[u8]) -> Self {
140 assert!(matches!(iv.len(), 4 | 8 | 12));
141
142 let mut state = [0u32; STATE_WORDS];
143
144 let ctr_size = size_of::<V::Counter>() / size_of::<u32>();
145 let (const_dst, state_rem) = state.split_at_mut(4);
146 let (key_dst, state_rem) = state_rem.split_at_mut(8);
147 let (_ctr_dst, iv_dst) = state_rem.split_at_mut(ctr_size);
148
149 const_dst.copy_from_slice(&CONSTANTS);
150
151 #[allow(clippy::unwrap_used, reason = "MSRV TODO")]
153 {
154 for (src, dst) in key.chunks_exact(4).zip(key_dst) {
155 *dst = u32::from_le_bytes(src.try_into().unwrap());
156 }
157
158 assert_eq!(size_of_val(iv_dst), size_of_val(iv));
159 for (src, dst) in iv.chunks_exact(4).zip(iv_dst) {
160 *dst = u32::from_le_bytes(src.try_into().unwrap());
161 }
162 }
163
164 cfg_if! {
165 if #[cfg(chacha20_backend = "soft")] {
166 let tokens = ();
167 } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
168 cfg_if! {
169 if #[cfg(chacha20_backend = "avx512")] {
170 let tokens = ();
171 } else if #[cfg(chacha20_backend = "avx2")] {
172 let tokens = ();
173 } else if #[cfg(chacha20_backend = "sse2")] {
174 let tokens = ();
175 } else if #[cfg(chacha20_avx512)] {
176 let tokens = (avx512_cpuid::init(), avx2_cpuid::init(), sse2_cpuid::init());
177 } else {
178 let tokens = (avx2_cpuid::init(), sse2_cpuid::init());
179 }
180 }
181 } else {
182 let tokens = ();
183 }
184 }
185 Self {
186 state,
187 tokens,
188 _pd: PhantomData,
189 }
190 }
191
192 #[inline(always)]
194 #[must_use]
195 pub fn get_block_pos(&self) -> V::Counter {
196 V::get_block_pos(&self.state[12..])
197 }
198
199 #[inline(always)]
201 pub fn set_block_pos(&mut self, pos: V::Counter) {
202 V::set_block_pos(&mut self.state[12..], pos);
203 }
204}
205
206impl<R: Rounds, V: Variant> fmt::Debug for ChaChaCore<R, V> {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 write!(
209 f,
210 "ChaChaCore<R: {}, V: {}-bit)> {{ ... }}",
211 R::COUNT,
212 size_of::<V::Counter>() * 8
213 )
214 }
215}
216
217#[cfg(feature = "cipher")]
218impl<R: Rounds, V: Variant> StreamCipherSeekCore for ChaChaCore<R, V> {
219 type Counter = V::Counter;
220
221 #[inline(always)]
222 fn get_block_pos(&self) -> Self::Counter {
223 self.get_block_pos()
224 }
225
226 #[inline(always)]
227 fn set_block_pos(&mut self, pos: Self::Counter) {
228 self.set_block_pos(pos);
229 }
230}
231
232#[cfg(feature = "cipher")]
233impl<R: Rounds, V: Variant> StreamCipherCore for ChaChaCore<R, V> {
234 #[inline(always)]
235 fn remaining_blocks(&self) -> Option<usize> {
236 V::remaining_blocks(self.get_block_pos())
237 }
238
239 fn process_with_backend(
240 &mut self,
241 f: impl cipher::StreamCipherClosure<BlockSize = Self::BlockSize>,
242 ) {
243 cfg_if! {
244 if #[cfg(chacha20_backend = "soft")] {
245 f.call(&mut backends::soft::Backend(self));
246 } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] {
247 cfg_if! {
248 if #[cfg(all(chacha20_avx512, chacha20_backend = "avx512"))] {
249 unsafe {
250 backends::avx512::inner::<R, _, V>(&mut self.state, f);
251 }
252 } else if #[cfg(chacha20_backend = "avx2")] {
253 unsafe {
254 backends::avx2::inner::<R, _, V>(&mut self.state, f);
255 }
256 } else if #[cfg(chacha20_backend = "sse2")] {
257 unsafe {
258 backends::sse2::inner::<R, _, V>(&mut self.state, f);
259 }
260 } else {
261 #[cfg(chacha20_avx512)]
262 let (avx512_token, avx2_token, sse2_token) = self.tokens;
263 #[cfg(not(chacha20_avx512))]
264 let (avx2_token, sse2_token) = self.tokens;
265
266 #[cfg(chacha20_avx512)]
267 if avx512_token.get() {
268 unsafe {
270 backends::avx512::inner::<R, _, V>(&mut self.state, f);
271 }
272 return;
273 }
274 if avx2_token.get() {
275 unsafe {
277 backends::avx2::inner::<R, _, V>(&mut self.state, f);
278 }
279 } else if sse2_token.get() {
280 unsafe {
282 backends::sse2::inner::<R, _, V>(&mut self.state, f);
283 }
284 } else {
285 f.call(&mut backends::soft::Backend(self));
286 }
287 }
288 }
289 } else if #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] {
290 unsafe {
292 backends::neon::inner::<R, _, V>(&mut self.state, f);
293 }
294 } else {
295 f.call(&mut backends::soft::Backend(self));
296 }
297 }
298 }
299}
300
301#[cfg(feature = "cipher")]
302impl<R: Rounds, V: Variant> BlockSizeUser for ChaChaCore<R, V> {
303 type BlockSize = U64;
304}
305
306#[cfg(feature = "zeroize")]
307impl<R: Rounds, V: Variant> Drop for ChaChaCore<R, V> {
308 fn drop(&mut self) {
309 self.state.zeroize();
310 }
311}
312
313#[cfg(feature = "zeroize")]
314impl<R: Rounds, V: Variant> ZeroizeOnDrop for ChaChaCore<R, V> {}
315
316#[allow(dead_code)]
321pub(crate) fn quarter_round(
322 a: usize,
323 b: usize,
324 c: usize,
325 d: usize,
326 state: &mut [u32; STATE_WORDS],
327) {
328 state[a] = state[a].wrapping_add(state[b]);
329 state[d] ^= state[a];
330 state[d] = state[d].rotate_left(16);
331
332 state[c] = state[c].wrapping_add(state[d]);
333 state[b] ^= state[c];
334 state[b] = state[b].rotate_left(12);
335
336 state[a] = state[a].wrapping_add(state[b]);
337 state[d] ^= state[a];
338 state[d] = state[d].rotate_left(8);
339
340 state[c] = state[c].wrapping_add(state[d]);
341 state[b] ^= state[c];
342 state[b] = state[b].rotate_left(7);
343}