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#[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(#[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#[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#[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);
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 {
pub fn init() -> Self { init() }
pub fn init_get() -> (Self, bool) { init_get() }
#[inline(always)]
pub fn get(&self) -> bool { { { { STORAGE.load(Relaxed) == 1 } } } }
}
#[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)
}
#[inline]
pub fn init() -> InitToken { init_get().0 }
#[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);
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 {
pub fn init() -> Self { init() }
pub fn init_get() -> (Self, bool) { init_get() }
#[inline(always)]
pub fn get(&self) -> bool { { true } }
}
#[inline]
pub fn init_get() -> (InitToken, bool) {
let res = { true };
(InitToken(()), res)
}
#[inline]
pub fn init() -> InitToken { init_get().0 }
#[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
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 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 #[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 #[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 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 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}