Skip to main content

chacha20/
lib.rs

1#![no_std]
2#![doc = "# RustCrypto: ChaCha20\n\n[![Crate][crate-image]][crate-link]\n[![Docs][docs-image]][docs-link]\n[![Build Status][build-image]][build-link]\n![Apache2/MIT licensed][license-image]\n![Rust Version][rustc-image]\n[![Project Chat][chat-image]][chat-link]\n[![HAZMAT][hazmat-image]][hazmat-link]\n\nImplementation of the [ChaCha] family of stream ciphers.\n\nChaCha improves upon the previous [Salsa] family of stream ciphers\nwith increased per-round diffusion at no cost to performance.\n\nThis crate also contains an implementation of the [XChaCha] family of stream ciphers\nwith an extended 192-bit (24-byte) nonce, gated under the `xchacha` Cargo feature,\nand \"legacy\" (a.k.a \"djb\") variant with 64-bit nonce, gated under the `legacy` crate feature.\n\n**WARNING:** This implementation internally uses 32-bit counter,\nwhile the original \"legacy\" variant implementation uses 64-bit counter.\nIn other words, it does not allow encryption of more than 256 GiB of data.\n\n## Security\n\n### \u{26a0}\u{fe0f} Warning: [Hazmat!][hazmat-link]\n\nThis crate does not ensure ciphertexts are authentic (i.e. by using a MAC to\nverify ciphertext integrity), which can lead to serious vulnerabilities\nif used incorrectly!\n\nTo avoid this, use an [AEAD] mode based on ChaCha20, e.g. [`chacha20poly1305`].\nSee the [RustCrypto/AEADs] repository for more information.\n\nUSE AT YOUR OWN RISK!\n\n### Notes\n\nThis crate has received one [security audit by NCC Group][NCC-AUDIT], with no significant\nfindings. We would like to thank [MobileCoin] for funding the audit.\n\nAll implementations contained in the crate (along with the underlying ChaCha20\nstream cipher itself) are designed to execute in constant time.\n\n## Examples\n\n```rust\n// This example requires `cipher` crate feature\n#[cfg(feature = \"cipher\")] {\n\nuse chacha20::ChaCha20;\nuse chacha20::cipher::{KeyIvInit, StreamCipher, StreamCipherSeek};\nuse hex_literal::hex;\n\nlet key = [0x42; 32];\nlet nonce = [0x24; 12];\nlet plaintext = hex!(\"000102030405060708090A0B0C0D0E0F\");\nlet ciphertext = hex!(\"e405626e4f1236b3670ee428332ea20e\");\n\n// Key and IV must be references to the `Array` type.\n// Here we use the `Into` trait to convert arrays into it.\nlet mut cipher = ChaCha20::new(&key.into(), &nonce.into());\n\nlet mut buffer = plaintext;\n\n// apply keystream (encrypt)\ncipher.apply_keystream(&mut buffer);\nassert_eq!(buffer, ciphertext);\n\nlet ciphertext = buffer;\n\n// ChaCha ciphers support seeking\ncipher.seek(0u32);\n\n// decrypt ciphertext by applying keystream again\ncipher.apply_keystream(&mut buffer);\nassert_eq!(buffer, plaintext);\n\n// stream ciphers can be used with streaming messages\ncipher.seek(0u32);\nfor chunk in buffer.chunks_mut(3) {\n    cipher.apply_keystream(chunk);\n}\nassert_eq!(buffer, ciphertext);\n}\n```\n\n## Configuration Flags\n\nYou can modify crate using the following configuration flags:\n\n- `chacha20_backend=\"avx2\"`: force AVX2 backend on x86/x86_64 targets.\n  Requires enabled AVX2 target feature. Ignored on non-x86(_64) targets.\n- `chacha20_backend=\"avx512\"`: force AVX-512 backend on x86/x86_64 targets.\n  Requires enabled AVX-512 target feature (MSRV 1.89). Ignored on non-x86(_64) targets.\n- `chacha20_backend=\"soft\"`: force software backend.\n- `chacha20_backend=\"sse2\"`: force SSE2 backend on x86/x86_64 targets.\n  Requires enabled SSE2 target feature. Ignored on non-x86(-64) targets.\n\nTo use the MSRV 1.89 AVX-512 support with autodetection, you must enable it using\n`chacha20_avx512` configuration flag.\n\nThe flags can be enabled using `RUSTFLAGS` environmental variable\n(e.g. `RUSTFLAGS=\'--cfg chacha20_backend=\"avx2\"\'`) or by modifying `.cargo/config.toml`.\n\n## License\n\nLicensed under either of:\n\n- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)\n- [MIT license](http://opensource.org/licenses/MIT)\n\nat your option.\n\n### Contribution\n\nUnless you explicitly state otherwise, any contribution intentionally submitted\nfor inclusion in the work by you, as defined in the Apache-2.0 license, shall be\ndual licensed as above, without any additional terms or conditions.\n\n[//]: # (badges)\n\n[crate-image]: https://img.shields.io/crates/v/chacha20.svg\n[crate-link]: https://crates.io/crates/chacha20\n[docs-image]: https://docs.rs/chacha20/badge.svg\n[docs-link]: https://docs.rs/chacha20/\n[build-image]: https://github.com/RustCrypto/stream-ciphers/actions/workflows/chacha20.yml/badge.svg\n[build-link]: https://github.com/RustCrypto/stream-ciphers/actions/workflows/chacha20.yml\n[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg\n[rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg\n[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg\n[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260049-stream-ciphers\n[hazmat-image]: https://img.shields.io/badge/crypto-hazmat%E2%9A%A0-red.svg\n[hazmat-link]: https://github.com/RustCrypto/meta/blob/master/HAZMAT.md\n\n[//]: # (footnotes)\n\n[ChaCha]: https://en.wikipedia.org/wiki/Salsa20#ChaCha_variant\n[Salsa]: https://en.wikipedia.org/wiki/Salsa20\n[XChaCha]: https://tools.ietf.org/html/draft-arciszewski-xchacha-02\n[AEAD]: https://en.wikipedia.org/wiki/Authenticated_encryption\n[`chacha20poly1305`]: https://docs.rs/chacha20poly1305\n[RustCrypto/AEADs]: https://github.com/RustCrypto/AEADs\n[NCC-AUDIT]: https://web.archive.org/web/20240108154854/https://research.nccgroup.com/wp-content/uploads/2020/02/NCC_Group_MobileCoin_RustCrypto_AESGCM_ChaCha20Poly1305_Implementation_Review_2020-02-12_v1.0.pdf\n[MobileCoin]: https://www.mobilecoin.com/\n"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/// State initialization constant ("expand 32-byte k")
46#[cfg(any(feature = "cipher", feature = "rng"))]
47const CONSTANTS: [u32; 4] = [0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574];
48
49/// Number of 32-bit words in the ChaCha state
50const STATE_WORDS: usize = 16;
51
52/// Marker type for a number of ChaCha rounds to perform.
53pub trait Rounds: Copy {
54    /// The amount of rounds to perform
55    const COUNT: usize;
56}
57
58/// 8-rounds
59#[derive(#[automatically_derived]impl ::core::marker::Copy for R8 { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for R8 {
}
#[automatically_derived]impl ::core::clone::Clone for R8 {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::fmt::Debug for R8 {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "R8")
    }
}Debug)]
60pub struct R8;
61
62impl Rounds for R8 {
63    const COUNT: usize = 4;
64}
65
66/// 12-rounds
67#[derive(#[automatically_derived]impl ::core::marker::Copy for R12 { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for R12 {
}
#[automatically_derived]impl ::core::clone::Clone for R12 {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::fmt::Debug for R12 {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "R12")
    }
}Debug)]
68pub struct R12;
69
70impl Rounds for R12 {
71    const COUNT: usize = 6;
72}
73
74/// 20-rounds
75#[derive(#[automatically_derived]impl ::core::marker::Copy for R20 { }Copy, #[automatically_derived]#[doc(hidden)]unsafe impl ::core::clone::TrivialClone
    for R20 {
}
#[automatically_derived]impl ::core::clone::Clone for R20 {
    #[inline]
    #[rustc_trivial_field_reads]
    fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]impl ::core::fmt::Debug for R20 {
    #[inline]
    #[rustc_trivial_field_reads]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "R20")
    }
}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                mod avx2_cpuid {
    use core::sync::atomic::{AtomicU8, Ordering::Relaxed};
    const UNINIT: u8 = u8::MAX;
    static STORAGE: AtomicU8 = AtomicU8::new(UNINIT);
    /// Initialization token
    pub struct InitToken(());
    #[automatically_derived]
    impl ::core::marker::Copy for InitToken { }
    #[automatically_derived]
    #[doc(hidden)]
    unsafe impl ::core::clone::TrivialClone for InitToken { }
    #[automatically_derived]
    impl ::core::clone::Clone for InitToken {
        #[inline]
        #[rustc_trivial_field_reads]
        fn clone(&self) -> Self {
            let _: ::core::clone::AssertParamIsClone<()>;
            *self
        }
    }
    #[automatically_derived]
    impl ::core::fmt::Debug for InitToken {
        #[inline]
        #[rustc_trivial_field_reads]
        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
            ::core::fmt::Formatter::debug_struct_field1_finish(f, "InitToken",
                &&self.0)
        }
    }
    impl InitToken {
        /// Initialize token, performing CPU feature detection.
        pub fn init() -> Self { init() }
        /// Initialize token and return a `bool` indicating if the feature is supported.
        pub fn init_get() -> (Self, bool) { init_get() }
        /// Get initialized value.
        #[inline(always)]
        pub fn get(&self) -> bool { { { { STORAGE.load(Relaxed) == 1 } } } }
    }
    /// Get stored value and initialization token,
    /// initializing underlying storage if needed.
    #[inline]
    pub fn init_get() -> (InitToken, bool) {
        let res =
            {
                {
                    {
                        #[cold]
                        fn init_inner() -> bool {
                            let res =
                                {
                                    use core::arch::x86_64::{
                                        __cpuid, __cpuid_count, CpuidResult,
                                    };
                                    unsafe fn cpuid(leaf: u32) -> CpuidResult { __cpuid(leaf) }
                                    unsafe fn cpuid_count(leaf: u32, sub_leaf: u32)
                                        -> CpuidResult {
                                        __cpuid_count(leaf, sub_leaf)
                                    }
                                    let cr =
                                        unsafe { [cpuid(1), cpuid_count(7, 0), cpuid_count(7, 1)] };
                                    ({
                                            let reg_cap =
                                                match "ymm" {
                                                    "xmm" => {
                                                        use core::arch::x86_64 as arch;
                                                        let xmask = 0b11 << 26;
                                                        let xsave = cr[0].ecx & xmask == xmask;
                                                        if xsave {
                                                            let xcr0 =
                                                                unsafe { arch::_xgetbv(arch::_XCR_XFEATURE_ENABLED_MASK) };
                                                            (xcr0 & 0b10) == 0b10
                                                        } else { false }
                                                    }
                                                    "ymm" => {
                                                        use core::arch::x86_64 as arch;
                                                        let xmask = 0b11 << 26;
                                                        let xsave = cr[0].ecx & xmask == xmask;
                                                        if xsave {
                                                            let xcr0 =
                                                                unsafe { arch::_xgetbv(arch::_XCR_XFEATURE_ENABLED_MASK) };
                                                            (xcr0 & 0b110) == 0b110
                                                        } else { false }
                                                    }
                                                    "zmm" => {
                                                        use core::arch::x86_64 as arch;
                                                        let xmask = 0b11 << 26;
                                                        let xsave = cr[0].ecx & xmask == xmask;
                                                        if xsave {
                                                            let xcr0 =
                                                                unsafe { arch::_xgetbv(arch::_XCR_XFEATURE_ENABLED_MASK) };
                                                            (xcr0 & 0b1110_0110) == 0b1110_0110
                                                        } else { false }
                                                    }
                                                    _ => true,
                                                };
                                            reg_cap & (cr[1].ebx & (1 << 5) != 0) &
                                                (cr[0].ecx & (1 << 28) != 0)
                                        }) & true
                                };
                            STORAGE.store(res as u8, Relaxed);
                            res
                        }
                        let val = STORAGE.load(Relaxed);
                        if val == UNINIT { init_inner() } else { val == 1 }
                    }
                }
            };
        (InitToken(()), res)
    }
    /// Initialize underlying storage if needed and get initialization token.
    #[inline]
    pub fn init() -> InitToken { init_get().0 }
    /// Initialize underlying storage if needed and get stored value.
    #[inline]
    pub fn get() -> bool { init_get().1 }
}cpufeatures::new!(avx2_cpuid, "avx2");
106                mod sse2_cpuid {
    use core::sync::atomic::{AtomicU8, Ordering::Relaxed};
    const UNINIT: u8 = u8::MAX;
    static STORAGE: AtomicU8 = AtomicU8::new(UNINIT);
    /// Initialization token
    pub struct InitToken(());
    #[automatically_derived]
    impl ::core::marker::Copy for InitToken { }
    #[automatically_derived]
    #[doc(hidden)]
    unsafe impl ::core::clone::TrivialClone for InitToken { }
    #[automatically_derived]
    impl ::core::clone::Clone for InitToken {
        #[inline]
        #[rustc_trivial_field_reads]
        fn clone(&self) -> Self {
            let _: ::core::clone::AssertParamIsClone<()>;
            *self
        }
    }
    #[automatically_derived]
    impl ::core::fmt::Debug for InitToken {
        #[inline]
        #[rustc_trivial_field_reads]
        fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
            ::core::fmt::Formatter::debug_struct_field1_finish(f, "InitToken",
                &&self.0)
        }
    }
    impl InitToken {
        /// Initialize token, performing CPU feature detection.
        pub fn init() -> Self { init() }
        /// Initialize token and return a `bool` indicating if the feature is supported.
        pub fn init_get() -> (Self, bool) { init_get() }
        /// Get initialized value.
        #[inline(always)]
        pub fn get(&self) -> bool { { true } }
    }
    /// Get stored value and initialization token,
    /// initializing underlying storage if needed.
    #[inline]
    pub fn init_get() -> (InitToken, bool) {
        let res = { true };
        (InitToken(()), res)
    }
    /// Initialize underlying storage if needed and get initialization token.
    #[inline]
    pub fn init() -> InitToken { init_get().0 }
    /// Initialize underlying storage if needed and get stored value.
    #[inline]
    pub fn get() -> bool { init_get().1 }
}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.
119pub 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
129impl<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    fn new_internal(key: &[u8; 32], iv: &[u8]) -> Self {
140        if !#[allow(non_exhaustive_omitted_patterns)] match iv.len() {
            4 | 8 | 12 => true,
            _ => false,
        } {
    ::core::panicking::panic("assertion failed: matches!(iv.len(), 4 | 8 | 12)")
};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        // TODO(tarcieri): when MSRV 1.88, use `[T]::as_chunks` to avoid panic
152        #[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            {
    match (&size_of_val(iv_dst), &size_of_val(iv)) {
        (left_val, right_val) => {
            if !(*left_val == *right_val) {
                let kind = ::core::panicking::AssertKind::Eq;
                ::core::panicking::assert_failed(kind, &*left_val,
                    &*right_val, ::core::option::Option::None);
            }
        }
    }
};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    /// Get the current block position.
193    #[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    /// Set the block position.
200    #[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        f.write_fmt(format_args!("ChaChaCore<R: {0}, V: {1}-bit)> {{ ... }}",
        R::COUNT, size_of::<V::Counter>() * 8))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                            // 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")]
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/// 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)]
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}