/rust/registry/src/index.crates.io-1949cf8c6b5b557f/chacha20-0.10.1/src/lib.rs
Line | Count | Source |
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 | | |
9 | | pub mod variants; |
10 | | |
11 | | mod backends; |
12 | | #[cfg(feature = "cipher")] |
13 | | mod chacha; |
14 | | #[cfg(feature = "legacy")] |
15 | | mod legacy; |
16 | | #[cfg(feature = "rng")] |
17 | | mod rng; |
18 | | #[cfg(feature = "xchacha")] |
19 | | mod xchacha; |
20 | | |
21 | | #[cfg(feature = "cipher")] |
22 | | pub use chacha::{ChaCha8, ChaCha12, ChaCha20, Key, Nonce}; |
23 | | #[cfg(feature = "cipher")] |
24 | | pub use cipher; |
25 | | #[cfg(feature = "cipher")] |
26 | | pub use cipher::KeyIvInit; |
27 | | #[cfg(feature = "legacy")] |
28 | | pub use legacy::{ChaCha20Legacy, ChaCha20LegacyCore, LegacyNonce}; |
29 | | #[cfg(feature = "rng")] |
30 | | pub use rand_core; |
31 | | #[cfg(feature = "rng")] |
32 | | pub use rng::{ChaCha8Rng, ChaCha12Rng, ChaCha20Rng, Seed, SerializedRngState}; |
33 | | #[cfg(feature = "xchacha")] |
34 | | pub use xchacha::{XChaCha8, XChaCha12, XChaCha20, XNonce, hchacha}; |
35 | | |
36 | | use cfg_if::cfg_if; |
37 | | use core::{fmt, marker::PhantomData}; |
38 | | use variants::Variant; |
39 | | |
40 | | #[cfg(feature = "cipher")] |
41 | | use cipher::{BlockSizeUser, StreamCipherCore, StreamCipherSeekCore, consts::U64}; |
42 | | #[cfg(feature = "zeroize")] |
43 | | use zeroize::{Zeroize, ZeroizeOnDrop}; |
44 | | |
45 | | /// State initialization constant ("expand 32-byte k") |
46 | | #[cfg(any(feature = "cipher", feature = "rng"))] |
47 | | const CONSTANTS: [u32; 4] = [0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574]; |
48 | | |
49 | | /// Number of 32-bit words in the ChaCha state |
50 | | const STATE_WORDS: usize = 16; |
51 | | |
52 | | /// Marker type for a number of ChaCha rounds to perform. |
53 | | pub trait Rounds: Copy { |
54 | | /// The amount of rounds to perform |
55 | | const COUNT: usize; |
56 | | } |
57 | | |
58 | | /// 8-rounds |
59 | | #[derive(Copy, Clone, Debug)] |
60 | | pub struct R8; |
61 | | |
62 | | impl Rounds for R8 { |
63 | | const COUNT: usize = 4; |
64 | | } |
65 | | |
66 | | /// 12-rounds |
67 | | #[derive(Copy, Clone, Debug)] |
68 | | pub struct R12; |
69 | | |
70 | | impl Rounds for R12 { |
71 | | const COUNT: usize = 6; |
72 | | } |
73 | | |
74 | | /// 20-rounds |
75 | | #[derive(Copy, Clone, Debug)] |
76 | | pub struct R20; |
77 | | |
78 | | impl Rounds for R20 { |
79 | | const COUNT: usize = 10; |
80 | | } |
81 | | |
82 | | cfg_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 | | |
118 | | /// The ChaCha core function. |
119 | | pub struct ChaChaCore<R: Rounds, V: Variant> { |
120 | | /// Internal state of the core function |
121 | | state: [u32; STATE_WORDS], |
122 | | /// CPU target feature tokens |
123 | | #[allow(dead_code)] |
124 | | tokens: Tokens, |
125 | | /// Number of rounds to perform and the cipher variant |
126 | | _pd: PhantomData<(R, V)>, |
127 | | } |
128 | | |
129 | | impl<R: Rounds, V: Variant> ChaChaCore<R, V> { |
130 | | /// Constructs a ChaChaCore with the specified `key` and `iv`. |
131 | | /// |
132 | | /// You must ensure that the iv is of the correct size when using this method |
133 | | /// directly. |
134 | | /// |
135 | | /// # Panics |
136 | | /// If `iv.len()` is not equal to 4, 8, or 12. |
137 | | #[must_use] |
138 | | #[cfg(any(feature = "cipher", feature = "rng"))] |
139 | 0 | fn new_internal(key: &[u8; 32], iv: &[u8]) -> Self { |
140 | 0 | assert!(matches!(iv.len(), 4 | 8 | 12)); |
141 | | |
142 | 0 | let mut state = [0u32; STATE_WORDS]; |
143 | | |
144 | 0 | let ctr_size = size_of::<V::Counter>() / size_of::<u32>(); |
145 | 0 | let (const_dst, state_rem) = state.split_at_mut(4); |
146 | 0 | let (key_dst, state_rem) = state_rem.split_at_mut(8); |
147 | 0 | let (_ctr_dst, iv_dst) = state_rem.split_at_mut(ctr_size); |
148 | | |
149 | 0 | const_dst.copy_from_slice(&CONSTANTS); |
150 | | |
151 | | // TODO(tarcieri): when MSRV 1.88, use `[T]::as_chunks` to avoid panic |
152 | | #[allow(clippy::unwrap_used, reason = "MSRV TODO")] |
153 | | { |
154 | 0 | for (src, dst) in key.chunks_exact(4).zip(key_dst) { |
155 | 0 | *dst = u32::from_le_bytes(src.try_into().unwrap()); |
156 | 0 | } |
157 | | |
158 | 0 | assert_eq!(size_of_val(iv_dst), size_of_val(iv)); |
159 | 0 | for (src, dst) in iv.chunks_exact(4).zip(iv_dst) { |
160 | 0 | *dst = u32::from_le_bytes(src.try_into().unwrap()); |
161 | 0 | } |
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 | 0 | let tokens = (avx2_cpuid::init(), sse2_cpuid::init()); |
179 | | } |
180 | | } |
181 | | } else { |
182 | | let tokens = (); |
183 | | } |
184 | | } |
185 | 0 | Self { |
186 | 0 | state, |
187 | 0 | tokens, |
188 | 0 | _pd: PhantomData, |
189 | 0 | } |
190 | 0 | } Unexecuted instantiation: <chacha20::ChaChaCore<chacha20::R12, chacha20::variants::Legacy>>::new_internal Unexecuted instantiation: <chacha20::ChaChaCore<_, _>>::new_internal |
191 | | |
192 | | /// Get the current block position. |
193 | | #[inline(always)] |
194 | | #[must_use] |
195 | 0 | pub fn get_block_pos(&self) -> V::Counter { |
196 | 0 | V::get_block_pos(&self.state[12..]) |
197 | 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 |
198 | | |
199 | | /// Set the block position. |
200 | | #[inline(always)] |
201 | 0 | pub fn set_block_pos(&mut self, pos: V::Counter) { |
202 | 0 | V::set_block_pos(&mut self.state[12..], pos); |
203 | 0 | } |
204 | | } |
205 | | |
206 | | impl<R: Rounds, V: Variant> fmt::Debug for ChaChaCore<R, V> { |
207 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
208 | 0 | write!( |
209 | 0 | f, |
210 | 0 | "ChaChaCore<R: {}, V: {}-bit)> {{ ... }}", |
211 | | R::COUNT, |
212 | | size_of::<V::Counter>() * 8 |
213 | | ) |
214 | 0 | } |
215 | | } |
216 | | |
217 | | #[cfg(feature = "cipher")] |
218 | | impl<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")] |
233 | | impl<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 | | // SAFETY: runtime CPU feature detection above ensures this is valid |
269 | | unsafe { |
270 | | backends::avx512::inner::<R, _, V>(&mut self.state, f); |
271 | | } |
272 | | return; |
273 | | } |
274 | | if avx2_token.get() { |
275 | | // SAFETY: runtime CPU feature detection above ensures this is valid |
276 | | unsafe { |
277 | | backends::avx2::inner::<R, _, V>(&mut self.state, f); |
278 | | } |
279 | | } else if sse2_token.get() { |
280 | | // SAFETY: runtime CPU feature detection above ensures this is valid |
281 | | 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 | | // SAFETY: we have used conditional compilation to ensure NEON is available |
291 | | 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")] |
302 | | impl<R: Rounds, V: Variant> BlockSizeUser for ChaChaCore<R, V> { |
303 | | type BlockSize = U64; |
304 | | } |
305 | | |
306 | | #[cfg(feature = "zeroize")] |
307 | | impl<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")] |
314 | | impl<R: Rounds, V: Variant> ZeroizeOnDrop for ChaChaCore<R, V> {} |
315 | | |
316 | | /// The ChaCha20 quarter round function |
317 | | /// |
318 | | /// We located this function in the root of the crate as we want it to be available |
319 | | /// for the soft backend and for xchacha. |
320 | | #[allow(dead_code)] |
321 | 0 | pub(crate) fn quarter_round( |
322 | 0 | a: usize, |
323 | 0 | b: usize, |
324 | 0 | c: usize, |
325 | 0 | d: usize, |
326 | 0 | state: &mut [u32; STATE_WORDS], |
327 | 0 | ) { |
328 | 0 | state[a] = state[a].wrapping_add(state[b]); |
329 | 0 | state[d] ^= state[a]; |
330 | 0 | state[d] = state[d].rotate_left(16); |
331 | | |
332 | 0 | state[c] = state[c].wrapping_add(state[d]); |
333 | 0 | state[b] ^= state[c]; |
334 | 0 | state[b] = state[b].rotate_left(12); |
335 | | |
336 | 0 | state[a] = state[a].wrapping_add(state[b]); |
337 | 0 | state[d] ^= state[a]; |
338 | 0 | state[d] = state[d].rotate_left(8); |
339 | | |
340 | 0 | state[c] = state[c].wrapping_add(state[d]); |
341 | 0 | state[b] ^= state[c]; |
342 | 0 | state[b] = state[b].rotate_left(7); |
343 | 0 | } |