1use crate::guts::generic_array::typenum::{Unsigned, U10, U12, U24, U32, U4, U6, U8};
2use crate::guts::generic_array::{ArrayLength, GenericArray};
3use crate::guts::{ChaCha, Machine, BLOCK, BLOCK64, BUFSZ};
4use core::cmp;
5use core::convert::TryInto;
6pub use cipher::stream as stream_cipher;
7use cipher::stream::{
8 LoopError, NewStreamCipher, OverflowError, SeekNum, SyncStreamCipher, SyncStreamCipherSeek,
9};
10
11const BIG_LEN: u64 = 0;
12const SMALL_LEN: u64 = 1 << 32;
13
14#[derive(Clone)]
15pub struct Buffer {
16 pub state: ChaCha,
17 pub out: [u8; BLOCK],
18 pub have: i8,
19 pub len: u64,
20 pub fresh: bool,
21}
22
23#[derive(Default)]
24pub struct X;
25#[derive(Default)]
26pub struct O;
27#[derive(Clone)]
28pub struct ChaChaAny<NonceSize, Rounds, IsX> {
29 pub state: Buffer,
30 pub _nonce_size: NonceSize,
31 pub _rounds: Rounds,
32 pub _is_x: IsX,
33}
34
35impl Buffer {
36 fn try_apply_keystream<EnableWide: AsBool>(
37 &mut self,
38 mut data: &mut [u8],
39 drounds: u32,
40 ) -> Result<(), ()> {
41 if self.have < 0 {
45 self.state.refill(drounds, &mut self.out);
46 self.have += BLOCK as i8;
47 self.len -= 1;
49 }
50 let mut have = self.have as usize;
51 let have_ready = cmp::min(have, data.len());
52 let datalen = (data.len() - have_ready) as u64;
55 let blocks_needed = datalen / BLOCK64 + u64::from(datalen % BLOCK64 != 0);
56 let (l, o) = self.len.overflowing_sub(blocks_needed);
57 if o && !self.fresh {
58 return Err(());
59 }
60 self.len = l;
61 self.fresh &= blocks_needed == 0;
62 let (d0, d1) = data.split_at_mut(have_ready);
64 for (data_b, key_b) in d0.iter_mut().zip(&self.out[(BLOCK - have)..]) {
65 *data_b ^= *key_b;
66 }
67 data = d1;
68 have -= have_ready;
69 if EnableWide::BOOL {
71 let (d0, d1) = data.split_at_mut(data.len() & !(BUFSZ - 1));
72 for dd in d0.chunks_exact_mut(BUFSZ) {
73 let mut buf = [0; BUFSZ];
74 self.state.refill4(drounds, &mut buf);
75 for (data_b, key_b) in dd.iter_mut().zip(buf.iter()) {
76 *data_b ^= *key_b;
77 }
78 }
79 data = d1;
80 }
81 for dd in data.chunks_mut(BLOCK) {
83 self.state.refill(drounds, &mut self.out);
84 for (data_b, key_b) in dd.iter_mut().zip(self.out.iter()) {
85 *data_b ^= *key_b;
86 }
87 have = BLOCK - dd.len();
88 }
89 self.have = have as i8;
90 Ok(())
91 }
92}
93
94dispatch_light128!(m, Mach, {
95 fn seek64(buf: &mut Buffer, ct: u64) {
96 let blockct = ct / BLOCK64;
97 buf.len = BIG_LEN.wrapping_sub(blockct);
98 buf.fresh = blockct == 0;
99 buf.have = -((ct % BLOCK64) as i8);
100 buf.state.seek64(m, blockct);
101 }
102});
103
104dispatch_light128!(m, Mach, {
105 fn seek32(buf: &mut Buffer, ct: u64) {
106 let blockct = ct / BLOCK64;
107 assert!(blockct < SMALL_LEN || (blockct == SMALL_LEN && ct % BLOCK64 == 0));
108 buf.len = SMALL_LEN - blockct;
109 buf.have = -((ct % BLOCK64) as i8);
110 buf.state.seek32(m, blockct as u32);
111 }
112});
113
114#[cfg(test)]
115impl<NonceSize, Rounds: Unsigned, IsX> ChaChaAny<NonceSize, Rounds, IsX> {
116 pub fn try_apply_keystream_narrow(&mut self, data: &mut [u8]) -> Result<(), ()> {
117 self.state
118 .try_apply_keystream::<WideDisabled>(data, Rounds::U32)
119 }
120}
121
122impl<NonceSize, Rounds> ChaChaAny<NonceSize, Rounds, O>
123where
124 NonceSize: Unsigned + ArrayLength<u8> + Default,
125 Rounds: Default,
126{
127 #[inline]
128 fn new(key: &GenericArray<u8, U32>, nonce: &GenericArray<u8, NonceSize>) -> Self {
129 let nonce_len = nonce.len();
130 ChaChaAny {
131 state: Buffer {
132 state: init_chacha(key, nonce),
133 out: [0; BLOCK],
134 have: 0,
135 len: if nonce_len == 12 { SMALL_LEN } else { BIG_LEN },
136 fresh: nonce_len != 12,
137 },
138 _nonce_size: Default::default(),
139 _rounds: Default::default(),
140 _is_x: Default::default(),
141 }
142 }
143}
144
145impl<Rounds: Unsigned + Default> ChaChaAny<U24, Rounds, X> {
146 fn new(key: &GenericArray<u8, U32>, nonce: &GenericArray<u8, U24>) -> Self {
147 ChaChaAny {
148 state: Buffer {
149 state: init_chacha_x(key, nonce, Rounds::U32),
150 out: [0; BLOCK],
151 have: 0,
152 len: BIG_LEN,
153 fresh: true,
154 },
155 _nonce_size: Default::default(),
156 _rounds: Default::default(),
157 _is_x: Default::default(),
158 }
159 }
160}
161
162impl<NonceSize: Unsigned, Rounds, IsX> ChaChaAny<NonceSize, Rounds, IsX> {
163 #[inline(always)]
164 fn seek(&mut self, ct: u64) {
165 if NonceSize::U32 != 12 {
166 seek64(&mut self.state, ct);
167 } else {
168 seek32(&mut self.state, ct);
169 }
170 }
171}
172
173impl<NonceSize, Rounds: Unsigned, IsX> ChaChaAny<NonceSize, Rounds, IsX> {
174 #[inline]
175 fn try_apply_keystream(&mut self, data: &mut [u8]) -> Result<(), ()> {
176 self.state
177 .try_apply_keystream::<WideEnabled>(data, Rounds::U32)
178 }
179}
180
181impl<NonceSize, Rounds> NewStreamCipher for ChaChaAny<NonceSize, Rounds, O>
182where
183 NonceSize: Unsigned + ArrayLength<u8> + Default,
184 Rounds: Default,
185{
186 type KeySize = U32;
187 type NonceSize = NonceSize;
188 #[inline]
189 fn new(
190 key: &GenericArray<u8, Self::KeySize>,
191 nonce: &GenericArray<u8, Self::NonceSize>,
192 ) -> Self {
193 Self::new(key, nonce)
194 }
195}
196
197impl<Rounds: Unsigned + Default> NewStreamCipher for ChaChaAny<U24, Rounds, X> {
198 type KeySize = U32;
199 type NonceSize = U24;
200 #[inline]
201 fn new(
202 key: &GenericArray<u8, Self::KeySize>,
203 nonce: &GenericArray<u8, Self::NonceSize>,
204 ) -> Self {
205 Self::new(key, nonce)
206 }
207}
208
209impl<NonceSize: Unsigned, Rounds, IsX> SyncStreamCipherSeek for ChaChaAny<NonceSize, Rounds, IsX> {
210 #[inline]
211 fn try_current_pos<T: SeekNum>(&self) -> Result<T, OverflowError> {
212 unimplemented!()
213 }
214 #[inline(always)]
215 fn try_seek<T: SeekNum>(&mut self, pos: T) -> Result<(), LoopError> {
216 pos.try_into()
217 .map_err(|_| LoopError)
218 .map(|ct| Self::seek(self, ct))
219 }
220}
221
222impl<NonceSize, Rounds: Unsigned, IsX> SyncStreamCipher for ChaChaAny<NonceSize, Rounds, IsX> {
223 #[inline]
224 fn try_apply_keystream(&mut self, data: &mut [u8]) -> Result<(), LoopError> {
225 Self::try_apply_keystream(self, data).map_err(|_| LoopError)
226 }
227}
228
229trait AsBool {
230 const BOOL: bool;
231}
232struct WideEnabled;
233impl AsBool for WideEnabled {
234 const BOOL: bool = true;
235}
236#[cfg(test)]
237struct WideDisabled;
238#[cfg(test)]
239impl AsBool for WideDisabled {
240 const BOOL: bool = false;
241}
242
243dispatch_light128!(m, Mach, {
244 fn init_chacha(key: &GenericArray<u8, U32>, nonce: &[u8]) -> ChaCha {
245 let ctr_nonce = [
246 0,
247 if nonce.len() == 12 {
248 u32::from_le_bytes(nonce[0..4].try_into().unwrap())
249 } else {
250 0
251 },
252 u32::from_le_bytes(nonce[nonce.len() - 8..nonce.len() - 4].try_into().unwrap()),
253 u32::from_le_bytes(nonce[nonce.len() - 4..].try_into().unwrap()),
254 ];
255 let key0: Mach::u32x4 = m.read_le(&key[..16]);
256 let key1: Mach::u32x4 = m.read_le(&key[16..]);
257 ChaCha {
258 b: key0.into(),
259 c: key1.into(),
260 d: ctr_nonce.into(),
261 }
262 }
263});
264
265dispatch_light128!(m, Mach, {
266 fn init_chacha_x(
267 key: &GenericArray<u8, U32>,
268 nonce: &GenericArray<u8, U24>,
269 rounds: u32,
270 ) -> ChaCha {
271 let key0: Mach::u32x4 = m.read_le(&key[..16]);
272 let key1: Mach::u32x4 = m.read_le(&key[16..]);
273 let nonce0: Mach::u32x4 = m.read_le(&nonce[..16]);
274 let mut state = ChaCha {
275 b: key0.into(),
276 c: key1.into(),
277 d: nonce0.into(),
278 };
279 let x = state.refill_rounds(rounds);
280 let ctr_nonce1 = [
281 0,
282 0,
283 u32::from_le_bytes(nonce[16..20].try_into().unwrap()),
284 u32::from_le_bytes(nonce[20..24].try_into().unwrap()),
285 ];
286 state.b = x.a;
287 state.c = x.d;
288 state.d = ctr_nonce1.into();
289 state
290 }
291});
292
293pub type Ietf = ChaChaAny<U12, U10, O>;
295pub type ChaCha8 = ChaChaAny<U8, U4, O>;
297pub type ChaCha12 = ChaChaAny<U8, U6, O>;
299pub type ChaCha20 = ChaChaAny<U8, U10, O>;
301pub type XChaCha8 = ChaChaAny<U24, U4, X>;
304pub type XChaCha12 = ChaChaAny<U24, U6, X>;
307pub type XChaCha20 = ChaChaAny<U24, U10, X>;
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[test]
316 fn chacha20_case_1() {
317 let key = hex!("fa44478c59ca70538e3549096ce8b523232c50d9e8e8d10c203ef6c8d07098a5");
318 let nonce = hex!("8d3a0d6d7827c007");
319 let expected = hex!("
320 1546a547ff77c5c964e44fd039e913c6395c8f19d43efaa880750f6687b4e6e2d8f42f63546da2d133b5aa2f1ef3f218b6c72943089e4012
321 210c2cbed0e8e93498a6825fc8ff7a504f26db33b6cbe36299436244c9b2eff88302c55933911b7d5dea75f2b6d4761ba44bb6f814c9879d
322 2ba2ac8b178fa1104a368694872339738ffb960e33db39efb8eaef885b910eea078e7a1feb3f8185dafd1455b704d76da3a0ce4760741841
323 217bba1e4ece760eaf68617133431feb806c061173af6b8b2a23be90c5d145cc258e3c119aab2800f0c7bc1959dae75481712cab731b7dfd
324 783fa3a228f9968aaea68f36a92f43c9b523337a55b97bcaf5f5774447bf41e8");
325 let mut state = ChaCha20::new(
326 GenericArray::from_slice(&key),
327 GenericArray::from_slice(&nonce),
328 );
329 let offset = 0x3fffffff70u64;
330 assert!((offset >> 38) != ((offset + 240) >> 38)); state.seek(offset);
332 let mut result = [0; 256];
333 state.apply_keystream(&mut result);
334 assert_eq!(&expected[..], &result[..]);
335 }
336
337 #[test]
338 fn chacha12_case_1() {
339 let key = hex!("27fc120b013b829f1faeefd1ab417e8662f43e0d73f98de866e346353180fdb7");
340 let nonce = hex!("db4b4a41d8df18aa");
341 let expected = hex!("
342 5f3c8c190a78ab7fe808cae9cbcb0a9837c893492d963a1c2eda6c1558b02c83fc02a44cbbb7e6204d51d1c2430e9c0b58f2937bf593840c
343 850bda9051a1f051ddf09d2a03ebf09f01bdba9da0b6da791b2e645641047d11ebf85087d4de5c015fddd044");
344 let mut state = ChaCha12::new(
345 GenericArray::from_slice(&key),
346 GenericArray::from_slice(&nonce),
347 );
348 let mut result = [0u8; 100];
349 state.apply_keystream(&mut result);
350 assert_eq!(&expected[..], &result[..]);
351 }
352
353 #[test]
354 fn chacha8_case_1() {
355 let key = hex!("641aeaeb08036b617a42cf14e8c5d2d115f8d7cb6ea5e28b9bfaf83e038426a7");
356 let nonce = hex!("a14a1168271d459b");
357 let mut state = ChaCha8::new(
358 GenericArray::from_slice(&key),
359 GenericArray::from_slice(&nonce),
360 );
361 let expected = hex!(
362 "1721c044a8a6453522dddb3143d0be3512633ca3c79bf8ccc3594cb2c2f310f7bd544f55ce0db38123412d6c45207d5cf9af0c6c680cce1f
363 7e43388d1b0346b7133c59fd6af4a5a568aa334ccdc38af5ace201df84d0a3ca225494ca6209345fcf30132e");
364 let mut result = [0u8; 100];
365 state.apply_keystream(&mut result);
366 assert_eq!(&expected[..], &result[..]);
367 }
368
369 #[test]
370 fn test_ietf() {
371 let key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
372 let nonce = hex!("000000090000004a00000000");
373 let expected = hex!(
374 "
375 10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4e
376 d2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"
377 );
378 let mut state = Ietf::new(
379 GenericArray::from_slice(&key),
380 GenericArray::from_slice(&nonce),
381 );
382 let mut result = [0; 64];
383 state.seek(64);
384 state.apply_keystream(&mut result);
385 assert_eq!(&expected[..], &result[..]);
386 }
387
388 #[test]
389 fn rfc_7539_case_1() {
390 let key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
391 let nonce = hex!("000000090000004a00000000");
392 let mut state = Ietf::new(
393 GenericArray::from_slice(&key),
394 GenericArray::from_slice(&nonce),
395 );
396 let mut result = [0; 128];
397 state.apply_keystream(&mut result);
398 let expected = hex!(
399 "10f1e7e4d13b5915500fdd1fa32071c4c7d1f4c733c068030422aa9ac3d46c4e
400 d2826446079faa0914c2d705d98b02a2b5129cd1de164eb9cbd083e8a2503c4e"
401 );
402 assert_eq!(&expected[..], &result[64..]);
403 }
404
405 #[test]
406 fn rfc_7539_case_2() {
407 let key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
408 let nonce = hex!("000000000000004a00000000");
409 let mut state = Ietf::new(
410 GenericArray::from_slice(&key),
411 GenericArray::from_slice(&nonce),
412 );
413 let plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
414 let mut buf = [0u8; 178];
415 buf[64..].copy_from_slice(plaintext);
416 state.apply_keystream(&mut buf);
417 let expected = hex!("
418 6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab
419 8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42
420 874d");
421 assert_eq!(&expected[..], &buf[64..]);
422 }
423
424 #[test]
425 fn rfc_7539_case_2_chunked() {
426 let key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f");
427 let nonce = hex!("000000000000004a00000000");
428 let mut state = Ietf::new(
429 GenericArray::from_slice(&key),
430 GenericArray::from_slice(&nonce),
431 );
432 let plaintext = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
433 let mut buf = [0u8; 178];
434 buf[64..].copy_from_slice(plaintext);
435 state.apply_keystream(&mut buf[..40]);
436 state.apply_keystream(&mut buf[40..78]);
437 state.apply_keystream(&mut buf[78..79]);
438 state.apply_keystream(&mut buf[79..128]);
439 state.apply_keystream(&mut buf[128..]);
440 let expected = hex!("
441 6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab
442 8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42
443 874d");
444 assert_eq!(&expected[..], &buf[64..]);
445 }
446
447 #[test]
448 fn xchacha20_case_1() {
449 let key = hex!("82f411a074f656c66e7dbddb0a2c1b22760b9b2105f4ffdbb1d4b1e824e21def");
450 let nonce = hex!("3b07ca6e729eb44a510b7a1be51847838a804f8b106b38bd");
451 let mut state = XChaCha20::new(
452 GenericArray::from_slice(&key),
453 GenericArray::from_slice(&nonce),
454 );
455 let mut xs = [0u8; 100];
456 state.apply_keystream(&mut xs);
457 let expected = hex!("
458 201863970b8e081f4122addfdf32f6c03e48d9bc4e34a59654f49248b9be59d3eaa106ac3376e7e7d9d1251f2cbf61ef27000f3d19afb76b
459 9c247151e7bc26467583f520518eccd2055ccd6cc8a195953d82a10c2065916778db35da2be44415d2f5efb0");
460 assert_eq!(&expected[..], &xs[..]);
461 }
462
463 #[test]
464 fn seek_off_end() {
465 let mut st = Ietf::new(
466 GenericArray::from_slice(&[0xff; 32]),
467 GenericArray::from_slice(&[0; 12]),
468 );
469 st.seek(0x40_0000_0000);
470
471 assert!(st.try_apply_keystream(&mut [0u8; 1]).is_err());
472 }
473
474 #[test]
475 fn read_last_bytes() {
476 let mut st = Ietf::new(
477 GenericArray::from_slice(&[0xff; 32]),
478 GenericArray::from_slice(&[0; 12]),
479 );
480
481 st.seek(0x40_0000_0000 - 10);
482 st.apply_keystream(&mut [0u8; 10]);
483 assert!(st.try_apply_keystream(&mut [0u8; 1]).is_err());
484
485 st.seek(0x40_0000_0000 - 10);
486 assert!(st.try_apply_keystream(&mut [0u8; 11]).is_err());
487 }
488
489 #[test]
490 fn seek_consistency() {
491 let mut st = Ietf::new(
492 GenericArray::from_slice(&[50; 32]),
493 GenericArray::from_slice(&[44; 12]),
494 );
495
496 let mut continuous = [0u8; 1000];
497 st.apply_keystream(&mut continuous);
498
499 let mut chunks = [0u8; 1000];
500
501 st.seek(128);
502 st.apply_keystream(&mut chunks[128..300]);
503
504 st.seek(0);
505 st.apply_keystream(&mut chunks[0..10]);
506
507 st.seek(300);
508 st.apply_keystream(&mut chunks[300..533]);
509
510 st.seek(533);
511 st.apply_keystream(&mut chunks[533..]);
512
513 st.seek(10);
514 st.apply_keystream(&mut chunks[10..128]);
515
516 assert_eq!(&continuous[..], &chunks[..]);
517 }
518
519 #[test]
520 fn wide_matches_narrow() {
521 let key = hex!("fa44478c59ca70538e3549096ce8b523232c50d9e8e8d10c203ef6c8d07098a5");
522 let nonce = hex!("8d3a0d6d7827c007");
523 let mut buf = [0; 2048];
524 let mut state = ChaCha20::new(
525 GenericArray::from_slice(&key),
526 GenericArray::from_slice(&nonce),
527 );
528
529 let lens = [
530 2048, 2047, 1537, 1536, 1535, 1025, 1024, 1023, 768, 513, 512, 511, 200, 100, 50,
531 ];
532
533 for &len in &lens {
534 let buf = &mut buf[0..len];
535
536 state.seek(0);
538 state.apply_keystream(buf);
539 state.seek(0);
540 state.try_apply_keystream_narrow(buf).unwrap();
542 for &byte in buf.iter() {
543 assert_eq!(byte, 0);
544 }
545
546 let offset = 0x3fffffff70u64;
548 state.seek(offset);
549 state.apply_keystream(buf);
550 state.seek(offset);
552 state.try_apply_keystream_narrow(buf).unwrap();
553 for &byte in buf.iter() {
554 assert_eq!(byte, 0);
555 }
556 }
557 }
558}