/rust/registry/src/index.crates.io-1949cf8c6b5b557f/chacha20-0.10.0/src/lib.rs
Line | Count | Source |
1 | | #![no_std] |
2 | | #![cfg_attr(docsrs, feature(doc_cfg))] |
3 | | #![doc = include_str!("../README.md")] |
4 | | #![doc( |
5 | | html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", |
6 | | html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg" |
7 | | )] |
8 | | |
9 | | //! # Usage |
10 | | //! |
11 | | //! Cipher functionality is accessed using traits from re-exported [`cipher`] crate, or as a set |
12 | | //! of random number generator types ending in `*Rng` which implement traits from the [`rand_core`] |
13 | | //! crate. |
14 | | //! |
15 | | //! This crate contains the following variants of the ChaCha20 core algorithm: |
16 | | //! |
17 | | //! - [`ChaCha20`]: standard IETF variant with 96-bit nonce |
18 | | //! - [`ChaCha8`] / [`ChaCha12`]: reduced round variants of ChaCha20 |
19 | | //! - [`XChaCha20`]: 192-bit extended nonce variant |
20 | | //! - [`XChaCha8`] / [`XChaCha12`]: reduced round variants of XChaCha20 |
21 | | //! - [`ChaCha20Legacy`]: "djb" variant with 64-bit nonce. |
22 | | //! **WARNING:** This implementation internally uses 32-bit counter, |
23 | | //! while the original implementation uses 64-bit counter. In other words, |
24 | | //! it does not allow encryption of more than 256 GiB of data. |
25 | | //! |
26 | | //! ## Example |
27 | | #![cfg_attr(feature = "cipher", doc = " ```")] |
28 | | #![cfg_attr(not(feature = "cipher"), doc = " ```ignore")] |
29 | | //! use chacha20::ChaCha20; |
30 | | //! // Import relevant traits |
31 | | //! use chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; |
32 | | //! use hex_literal::hex; |
33 | | //! |
34 | | //! let key = [0x42; 32]; |
35 | | //! let nonce = [0x24; 12]; |
36 | | //! let plaintext = hex!("00010203 04050607 08090A0B 0C0D0E0F"); |
37 | | //! let ciphertext = hex!("e405626e 4f1236b3 670ee428 332ea20e"); |
38 | | //! |
39 | | //! // Key and IV must be references to the `Array` type. |
40 | | //! // Here we use the `Into` trait to convert arrays into it. |
41 | | //! let mut cipher = ChaCha20::new(&key.into(), &nonce.into()); |
42 | | //! |
43 | | //! let mut buffer = plaintext.clone(); |
44 | | //! |
45 | | //! // apply keystream (encrypt) |
46 | | //! cipher.apply_keystream(&mut buffer); |
47 | | //! assert_eq!(buffer, ciphertext); |
48 | | //! |
49 | | //! let ciphertext = buffer.clone(); |
50 | | //! |
51 | | //! // ChaCha ciphers support seeking |
52 | | //! cipher.seek(0u32); |
53 | | //! |
54 | | //! // decrypt ciphertext by applying keystream again |
55 | | //! cipher.apply_keystream(&mut buffer); |
56 | | //! assert_eq!(buffer, plaintext); |
57 | | //! |
58 | | //! // stream ciphers can be used with streaming messages |
59 | | //! cipher.seek(0u32); |
60 | | //! for chunk in buffer.chunks_mut(3) { |
61 | | //! cipher.apply_keystream(chunk); |
62 | | //! } |
63 | | //! assert_eq!(buffer, ciphertext); |
64 | | //! ``` |
65 | | //! |
66 | | //! # Configuration Flags |
67 | | //! |
68 | | //! You can modify crate using the following configuration flags: |
69 | | //! |
70 | | //! - `chacha20_backend="avx2"`: force AVX2 backend on x86/x86_64 targets. |
71 | | //! Requires enabled AVX2 target feature. Ignored on non-x86(_64) targets. |
72 | | //! - `chacha20_backend="avx512": force AVX-512 backend on x86/x86_64 targets. |
73 | | //! Requires enabled AVX-512 target feature (MSRV 1.89). Ignored on non-x86(_64) targets. |
74 | | //! - `chacha20_backend="soft"`: force software backend. |
75 | | //! - `chacha20_backend="sse2"`: force SSE2 backend on x86/x86_64 targets. |
76 | | //! Requires enabled SSE2 target feature. Ignored on non-x86(-64) targets. |
77 | | //! |
78 | | //! The flags can be enabled using `RUSTFLAGS` environmental variable |
79 | | //! (e.g. `RUSTFLAGS='--cfg chacha20_backend="avx2"'`) or by modifying `.cargo/config.toml`: |
80 | | //! |
81 | | //! ```toml |
82 | | //! # In .cargo/config.toml |
83 | | //! [build] |
84 | | //! rustflags = ['--cfg', 'chacha20_backend="avx2"'] |
85 | | //! ``` |
86 | | //! |
87 | | //! ## AVX-512 support |
88 | | //! |
89 | | //! To use the MSRV 1.89 AVX-512 support, you must enable it using: `--cfg chacha20_avx512`. |
90 | | //! |
91 | | //! [ChaCha]: https://tools.ietf.org/html/rfc8439 |
92 | | //! [Salsa]: https://en.wikipedia.org/wiki/Salsa20 |
93 | | //! [`chacha20poly1305`]: https://docs.rs/chacha20poly1305 |
94 | | |
95 | | pub mod variants; |
96 | | |
97 | | mod backends; |
98 | | #[cfg(feature = "cipher")] |
99 | | mod chacha; |
100 | | #[cfg(feature = "legacy")] |
101 | | mod legacy; |
102 | | #[cfg(feature = "rng")] |
103 | | mod rng; |
104 | | #[cfg(feature = "xchacha")] |
105 | | mod xchacha; |
106 | | |
107 | | #[cfg(feature = "cipher")] |
108 | | pub use chacha::{ChaCha8, ChaCha12, ChaCha20, Key, KeyIvInit}; |
109 | | #[cfg(feature = "cipher")] |
110 | | pub use cipher; |
111 | | #[cfg(feature = "legacy")] |
112 | | pub use legacy::{ChaCha20Legacy, LegacyNonce}; |
113 | | #[cfg(feature = "rng")] |
114 | | pub use rand_core; |
115 | | #[cfg(feature = "rng")] |
116 | | pub use rng::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng, Seed, SerializedRngState}; |
117 | | #[cfg(feature = "xchacha")] |
118 | | pub use xchacha::{XChaCha8, XChaCha12, XChaCha20, XNonce, hchacha}; |
119 | | |
120 | | use cfg_if::cfg_if; |
121 | | use core::{fmt, marker::PhantomData}; |
122 | | use variants::Variant; |
123 | | |
124 | | #[cfg(feature = "cipher")] |
125 | | use cipher::{BlockSizeUser, StreamCipherCore, StreamCipherSeekCore, consts::U64}; |
126 | | #[cfg(feature = "zeroize")] |
127 | | use zeroize::{Zeroize, ZeroizeOnDrop}; |
128 | | |
129 | | /// State initialization constant ("expand 32-byte k") |
130 | | #[cfg(any(feature = "cipher", feature = "rng"))] |
131 | | const CONSTANTS: [u32; 4] = [0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]; |
132 | | |
133 | | /// Number of 32-bit words in the ChaCha state |
134 | | const STATE_WORDS: usize = 16; |
135 | | |
136 | | /// Marker type for a number of ChaCha rounds to perform. |
137 | | pub trait Rounds: Copy { |
138 | | /// The amount of rounds to perform |
139 | | const COUNT: usize; |
140 | | } |
141 | | |
142 | | /// 8-rounds |
143 | | #[derive(Copy, Clone, Debug)] |
144 | | pub struct R8; |
145 | | |
146 | | impl Rounds for R8 { |
147 | | const COUNT: usize = 4; |
148 | | } |
149 | | |
150 | | /// 12-rounds |
151 | | #[derive(Copy, Clone, Debug)] |
152 | | pub struct R12; |
153 | | |
154 | | impl Rounds for R12 { |
155 | | const COUNT: usize = 6; |
156 | | } |
157 | | |
158 | | /// 20-rounds |
159 | | #[derive(Copy, Clone, Debug)] |
160 | | pub struct R20; |
161 | | |
162 | | impl Rounds for R20 { |
163 | | const COUNT: usize = 10; |
164 | | } |
165 | | |
166 | | cfg_if! { |
167 | | if #[cfg(chacha20_backend = "soft")] { |
168 | | type Tokens = (); |
169 | | } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { |
170 | | cfg_if! { |
171 | | if #[cfg(all(chacha20_avx512, chacha20_backend = "avx512"))] { |
172 | | #[cfg(not(all(target_feature = "avx512f", target_feature = "avx512vl")))] |
173 | | compile_error!("You must enable `avx512f` and `avx512vl` target features with \ |
174 | | `chacha20_backend = "avx512"` configuration option"); |
175 | | type Tokens = (); |
176 | | } else if #[cfg(chacha20_backend = "avx2")] { |
177 | | #[cfg(not(target_feature = "avx2"))] |
178 | | compile_error!("You must enable `avx2` target feature with \ |
179 | | `chacha20_backend = "avx2"` configuration option"); |
180 | | type Tokens = (); |
181 | | } else if #[cfg(chacha20_backend = "sse2")] { |
182 | | #[cfg(not(target_feature = "sse2"))] |
183 | | compile_error!("You must enable `sse2` target feature with \ |
184 | | `chacha20_backend = "sse2"` configuration option"); |
185 | | type Tokens = (); |
186 | | } else { |
187 | | #[cfg(chacha20_avx512)] |
188 | | cpufeatures::new!(avx512_cpuid, "avx512f", "avx512vl"); |
189 | | cpufeatures::new!(avx2_cpuid, "avx2"); |
190 | | cpufeatures::new!(sse2_cpuid, "sse2"); |
191 | | #[cfg(chacha20_avx512)] |
192 | | type Tokens = (avx512_cpuid::InitToken, avx2_cpuid::InitToken, sse2_cpuid::InitToken); |
193 | | #[cfg(not(chacha20_avx512))] |
194 | | type Tokens = (avx2_cpuid::InitToken, sse2_cpuid::InitToken); |
195 | | } |
196 | | } |
197 | | } else { |
198 | | type Tokens = (); |
199 | | } |
200 | | } |
201 | | |
202 | | /// The ChaCha core function. |
203 | | pub struct ChaChaCore<R: Rounds, V: Variant> { |
204 | | /// Internal state of the core function |
205 | | state: [u32; STATE_WORDS], |
206 | | /// CPU target feature tokens |
207 | | #[allow(dead_code)] |
208 | | tokens: Tokens, |
209 | | /// Number of rounds to perform and the cipher variant |
210 | | _pd: PhantomData<(R, V)>, |
211 | | } |
212 | | |
213 | | impl<R: Rounds, V: Variant> ChaChaCore<R, V> { |
214 | | /// Constructs a ChaChaCore with the specified `key` and `iv`. |
215 | | /// |
216 | | /// You must ensure that the iv is of the correct size when using this method |
217 | | /// directly. |
218 | | /// |
219 | | /// # Panics |
220 | | /// If `iv.len()` is not equal to 4, 8, or 12. |
221 | | #[must_use] |
222 | | #[cfg(any(feature = "cipher", feature = "rng"))] |
223 | 0 | fn new_internal(key: &[u8; 32], iv: &[u8]) -> Self { |
224 | 0 | assert!(matches!(iv.len(), 4 | 8 | 12)); |
225 | | |
226 | 0 | let mut state = [0u32; STATE_WORDS]; |
227 | | |
228 | 0 | let ctr_size = size_of::<V::Counter>() / size_of::<u32>(); |
229 | 0 | let (const_dst, state_rem) = state.split_at_mut(4); |
230 | 0 | let (key_dst, state_rem) = state_rem.split_at_mut(8); |
231 | 0 | let (_ctr_dst, iv_dst) = state_rem.split_at_mut(ctr_size); |
232 | | |
233 | 0 | const_dst.copy_from_slice(&CONSTANTS); |
234 | | |
235 | | // TODO(tarcieri): when MSRV 1.88, use `[T]::as_chunks` to avoid panic |
236 | | #[allow(clippy::unwrap_used, reason = "MSRV TODO")] |
237 | | { |
238 | 0 | for (src, dst) in key.chunks_exact(4).zip(key_dst) { |
239 | 0 | *dst = u32::from_le_bytes(src.try_into().unwrap()); |
240 | 0 | } |
241 | | |
242 | 0 | assert_eq!(size_of_val(iv_dst), size_of_val(iv)); |
243 | 0 | for (src, dst) in iv.chunks_exact(4).zip(iv_dst) { |
244 | 0 | *dst = u32::from_le_bytes(src.try_into().unwrap()); |
245 | 0 | } |
246 | | } |
247 | | |
248 | | cfg_if! { |
249 | | if #[cfg(chacha20_backend = "soft")] { |
250 | | let tokens = (); |
251 | | } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { |
252 | | cfg_if! { |
253 | | if #[cfg(chacha20_backend = "avx512")] { |
254 | | let tokens = (); |
255 | | } else if #[cfg(chacha20_backend = "avx2")] { |
256 | | let tokens = (); |
257 | | } else if #[cfg(chacha20_backend = "sse2")] { |
258 | | let tokens = (); |
259 | | } else if #[cfg(chacha20_avx512)] { |
260 | | let tokens = (avx512_cpuid::init(), avx2_cpuid::init(), sse2_cpuid::init()); |
261 | | } else { |
262 | 0 | let tokens = (avx2_cpuid::init(), sse2_cpuid::init()); |
263 | | } |
264 | | } |
265 | | } else { |
266 | | let tokens = (); |
267 | | } |
268 | | } |
269 | 0 | Self { |
270 | 0 | state, |
271 | 0 | tokens, |
272 | 0 | _pd: PhantomData, |
273 | 0 | } |
274 | 0 | } Unexecuted instantiation: <chacha20::ChaChaCore<chacha20::R12, chacha20::variants::Legacy>>::new_internal Unexecuted instantiation: <chacha20::ChaChaCore<_, _>>::new_internal |
275 | | |
276 | | /// Get the current block position. |
277 | | #[inline(always)] |
278 | | #[must_use] |
279 | 0 | pub fn get_block_pos(&self) -> V::Counter { |
280 | 0 | V::get_block_pos(&self.state[12..]) |
281 | 0 | } Unexecuted instantiation: <chacha20::ChaChaCore<chacha20::R12, chacha20::variants::Legacy>>::get_block_pos Unexecuted instantiation: <chacha20::ChaChaCore<chacha20::R12, chacha20::variants::Legacy>>::get_block_pos Unexecuted instantiation: <chacha20::ChaChaCore<_, _>>::get_block_pos |
282 | | |
283 | | /// Set the block position. |
284 | | #[inline(always)] |
285 | 0 | pub fn set_block_pos(&mut self, pos: V::Counter) { |
286 | 0 | V::set_block_pos(&mut self.state[12..], pos); |
287 | 0 | } |
288 | | } |
289 | | |
290 | | impl<R: Rounds, V: Variant> fmt::Debug for ChaChaCore<R, V> { |
291 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
292 | 0 | write!( |
293 | 0 | f, |
294 | | "ChaChaCore<R: {}, V: {}-bit)> {{ ... }}", |
295 | | R::COUNT, |
296 | | size_of::<V::Counter>() * 8 |
297 | | ) |
298 | 0 | } |
299 | | } |
300 | | |
301 | | #[cfg(feature = "cipher")] |
302 | | impl<R: Rounds, V: Variant> StreamCipherSeekCore for ChaChaCore<R, V> { |
303 | | type Counter = V::Counter; |
304 | | |
305 | | #[inline(always)] |
306 | | fn get_block_pos(&self) -> Self::Counter { |
307 | | self.get_block_pos() |
308 | | } |
309 | | |
310 | | #[inline(always)] |
311 | | fn set_block_pos(&mut self, pos: Self::Counter) { |
312 | | self.set_block_pos(pos); |
313 | | } |
314 | | } |
315 | | |
316 | | #[cfg(feature = "cipher")] |
317 | | impl<R: Rounds, V: Variant> StreamCipherCore for ChaChaCore<R, V> { |
318 | | #[inline(always)] |
319 | | fn remaining_blocks(&self) -> Option<usize> { |
320 | | V::remaining_blocks(self.get_block_pos()) |
321 | | } |
322 | | |
323 | | fn process_with_backend( |
324 | | &mut self, |
325 | | f: impl cipher::StreamCipherClosure<BlockSize = Self::BlockSize>, |
326 | | ) { |
327 | | cfg_if! { |
328 | | if #[cfg(chacha20_backend = "soft")] { |
329 | | f.call(&mut backends::soft::Backend(self)); |
330 | | } else if #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] { |
331 | | cfg_if! { |
332 | | if #[cfg(all(chacha20_avx512, chacha20_backend = "avx512"))] { |
333 | | unsafe { |
334 | | backends::avx512::inner::<R, _, V>(&mut self.state, f); |
335 | | } |
336 | | } else if #[cfg(chacha20_backend = "avx2")] { |
337 | | unsafe { |
338 | | backends::avx2::inner::<R, _, V>(&mut self.state, f); |
339 | | } |
340 | | } else if #[cfg(chacha20_backend = "sse2")] { |
341 | | unsafe { |
342 | | backends::sse2::inner::<R, _, V>(&mut self.state, f); |
343 | | } |
344 | | } else { |
345 | | #[cfg(chacha20_avx512)] |
346 | | let (avx512_token, avx2_token, sse2_token) = self.tokens; |
347 | | #[cfg(not(chacha20_avx512))] |
348 | | let (avx2_token, sse2_token) = self.tokens; |
349 | | |
350 | | #[cfg(chacha20_avx512)] |
351 | | if avx512_token.get() { |
352 | | // SAFETY: runtime CPU feature detection above ensures this is valid |
353 | | unsafe { |
354 | | backends::avx512::inner::<R, _, V>(&mut self.state, f); |
355 | | } |
356 | | return; |
357 | | } |
358 | | if avx2_token.get() { |
359 | | // SAFETY: runtime CPU feature detection above ensures this is valid |
360 | | unsafe { |
361 | | backends::avx2::inner::<R, _, V>(&mut self.state, f); |
362 | | } |
363 | | } else if sse2_token.get() { |
364 | | // SAFETY: runtime CPU feature detection above ensures this is valid |
365 | | unsafe { |
366 | | backends::sse2::inner::<R, _, V>(&mut self.state, f); |
367 | | } |
368 | | } else { |
369 | | f.call(&mut backends::soft::Backend(self)); |
370 | | } |
371 | | } |
372 | | } |
373 | | } else if #[cfg(all(target_arch = "aarch64", target_feature = "neon"))] { |
374 | | // SAFETY: we have used conditional compilation to ensure NEON is available |
375 | | unsafe { |
376 | | backends::neon::inner::<R, _, V>(&mut self.state, f); |
377 | | } |
378 | | } else { |
379 | | f.call(&mut backends::soft::Backend(self)); |
380 | | } |
381 | | } |
382 | | } |
383 | | } |
384 | | |
385 | | #[cfg(feature = "cipher")] |
386 | | impl<R: Rounds, V: Variant> BlockSizeUser for ChaChaCore<R, V> { |
387 | | type BlockSize = U64; |
388 | | } |
389 | | |
390 | | #[cfg(feature = "zeroize")] |
391 | | impl<R: Rounds, V: Variant> Drop for ChaChaCore<R, V> { |
392 | | fn drop(&mut self) { |
393 | | self.state.zeroize(); |
394 | | } |
395 | | } |
396 | | |
397 | | #[cfg(feature = "zeroize")] |
398 | | impl<R: Rounds, V: Variant> ZeroizeOnDrop for ChaChaCore<R, V> {} |
399 | | |
400 | | /// The ChaCha20 quarter round function |
401 | | /// |
402 | | /// We located this function in the root of the crate as we want it to be available |
403 | | /// for the soft backend and for xchacha. |
404 | | #[allow(dead_code)] |
405 | 0 | pub(crate) fn quarter_round( |
406 | 0 | a: usize, |
407 | 0 | b: usize, |
408 | 0 | c: usize, |
409 | 0 | d: usize, |
410 | 0 | state: &mut [u32; STATE_WORDS], |
411 | 0 | ) { |
412 | 0 | state[a] = state[a].wrapping_add(state[b]); |
413 | 0 | state[d] ^= state[a]; |
414 | 0 | state[d] = state[d].rotate_left(16); |
415 | | |
416 | 0 | state[c] = state[c].wrapping_add(state[d]); |
417 | 0 | state[b] ^= state[c]; |
418 | 0 | state[b] = state[b].rotate_left(12); |
419 | | |
420 | 0 | state[a] = state[a].wrapping_add(state[b]); |
421 | 0 | state[d] ^= state[a]; |
422 | 0 | state[d] = state[d].rotate_left(8); |
423 | | |
424 | 0 | state[c] = state[c].wrapping_add(state[d]); |
425 | 0 | state[b] ^= state[c]; |
426 | 0 | state[b] = state[b].rotate_left(7); |
427 | 0 | } |