Coverage Report

Created: 2026-08-13 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}