chacha20/
xchacha.rs
1use super::{ChaChaCore, Key, Nonce, CONSTANTS, STATE_WORDS};
4use cipher::{
5 consts::{U10, U16, U24, U32, U4, U6, U64},
6 generic_array::{typenum::Unsigned, GenericArray},
7 BlockSizeUser, IvSizeUser, KeyIvInit, KeySizeUser, StreamCipherCore, StreamCipherCoreWrapper,
8 StreamCipherSeekCore, StreamClosure,
9};
10
11#[cfg(feature = "zeroize")]
12use cipher::zeroize::ZeroizeOnDrop;
13
14pub type XNonce = GenericArray<u8, U24>;
16
17pub type XChaCha20 = StreamCipherCoreWrapper<XChaChaCore<U10>>;
33pub type XChaCha12 = StreamCipherCoreWrapper<XChaChaCore<U6>>;
35pub type XChaCha8 = StreamCipherCoreWrapper<XChaChaCore<U4>>;
37
38pub struct XChaChaCore<R: Unsigned>(ChaChaCore<R>);
40
41impl<R: Unsigned> KeySizeUser for XChaChaCore<R> {
42 type KeySize = U32;
43}
44
45impl<R: Unsigned> IvSizeUser for XChaChaCore<R> {
46 type IvSize = U24;
47}
48
49impl<R: Unsigned> BlockSizeUser for XChaChaCore<R> {
50 type BlockSize = U64;
51}
52
53impl<R: Unsigned> KeyIvInit for XChaChaCore<R> {
54 fn new(key: &Key, iv: &XNonce) -> Self {
55 let subkey = hchacha::<R>(key, iv[..16].as_ref().into());
56 let mut padded_iv = Nonce::default();
57 padded_iv[4..].copy_from_slice(&iv[16..]);
58 XChaChaCore(ChaChaCore::new(&subkey, &padded_iv))
59 }
60}
61
62impl<R: Unsigned> StreamCipherCore for XChaChaCore<R> {
63 #[inline(always)]
64 fn remaining_blocks(&self) -> Option<usize> {
65 self.0.remaining_blocks()
66 }
67
68 #[inline(always)]
69 fn process_with_backend(&mut self, f: impl StreamClosure<BlockSize = Self::BlockSize>) {
70 self.0.process_with_backend(f);
71 }
72}
73
74impl<R: Unsigned> StreamCipherSeekCore for XChaChaCore<R> {
75 type Counter = u32;
76
77 #[inline(always)]
78 fn get_block_pos(&self) -> u32 {
79 self.0.get_block_pos()
80 }
81
82 #[inline(always)]
83 fn set_block_pos(&mut self, pos: u32) {
84 self.0.set_block_pos(pos);
85 }
86}
87
88#[cfg(feature = "zeroize")]
89#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
90impl<R: Unsigned> ZeroizeOnDrop for XChaChaCore<R> {}
91
92pub fn hchacha<R: Unsigned>(key: &Key, input: &GenericArray<u8, U16>) -> GenericArray<u8, U32> {
107 let mut state = [0u32; STATE_WORDS];
108 state[..4].copy_from_slice(&CONSTANTS);
109
110 let key_chunks = key.chunks_exact(4);
111 for (v, chunk) in state[4..12].iter_mut().zip(key_chunks) {
112 *v = u32::from_le_bytes(chunk.try_into().unwrap());
113 }
114 let input_chunks = input.chunks_exact(4);
115 for (v, chunk) in state[12..16].iter_mut().zip(input_chunks) {
116 *v = u32::from_le_bytes(chunk.try_into().unwrap());
117 }
118
119 for _ in 0..R::USIZE {
121 quarter_round(0, 4, 8, 12, &mut state);
123 quarter_round(1, 5, 9, 13, &mut state);
124 quarter_round(2, 6, 10, 14, &mut state);
125 quarter_round(3, 7, 11, 15, &mut state);
126
127 quarter_round(0, 5, 10, 15, &mut state);
129 quarter_round(1, 6, 11, 12, &mut state);
130 quarter_round(2, 7, 8, 13, &mut state);
131 quarter_round(3, 4, 9, 14, &mut state);
132 }
133
134 let mut output = GenericArray::default();
135
136 for (chunk, val) in output[..16].chunks_exact_mut(4).zip(&state[..4]) {
137 chunk.copy_from_slice(&val.to_le_bytes());
138 }
139
140 for (chunk, val) in output[16..].chunks_exact_mut(4).zip(&state[12..]) {
141 chunk.copy_from_slice(&val.to_le_bytes());
142 }
143
144 output
145}
146
147fn quarter_round(a: usize, b: usize, c: usize, d: usize, state: &mut [u32; STATE_WORDS]) {
150 state[a] = state[a].wrapping_add(state[b]);
151 state[d] ^= state[a];
152 state[d] = state[d].rotate_left(16);
153
154 state[c] = state[c].wrapping_add(state[d]);
155 state[b] ^= state[c];
156 state[b] = state[b].rotate_left(12);
157
158 state[a] = state[a].wrapping_add(state[b]);
159 state[d] ^= state[a];
160 state[d] = state[d].rotate_left(8);
161
162 state[c] = state[c].wrapping_add(state[d]);
163 state[b] ^= state[c];
164 state[b] = state[b].rotate_left(7);
165}
166
167#[cfg(test)]
168mod hchacha20_tests {
169 use super::*;
170 use hex_literal::hex;
171
172 #[test]
175 fn test_vector() {
176 const KEY: [u8; 32] = hex!(
177 "000102030405060708090a0b0c0d0e0f"
178 "101112131415161718191a1b1c1d1e1f"
179 );
180
181 const INPUT: [u8; 16] = hex!("000000090000004a0000000031415927");
182
183 const OUTPUT: [u8; 32] = hex!(
184 "82413b4227b27bfed30e42508a877d73"
185 "a0f9e4d58a74a853c12ec41326d3ecdc"
186 );
187
188 let actual = hchacha::<U10>(
189 GenericArray::from_slice(&KEY),
190 &GenericArray::from_slice(&INPUT),
191 );
192 assert_eq!(actual.as_slice(), &OUTPUT);
193 }
194}