1#![no_std]
45#![cfg_attr(feature="nightly", feature(repr_simd))]
46#![cfg_attr(feature="nightly", feature(test))]
47
48extern crate keystream;
49
50#[cfg(all(test, feature="bench"))]
51extern crate test;
52
53pub use keystream::{KeyStream, SeekableKeyStream};
54pub use keystream::Error;
55use core::cmp::min;
56use core::convert::TryInto;
57
58#[derive(Clone)]
85pub struct ChaCha {
86 input: [u32; 16],
87 output: [u8; 64],
88 offset: u8,
89 rounds: u8,
90 large_block_counter: bool,
91}
92
93impl ChaCha {
94 pub fn new_ietf(key: &[u8; 32], nonce: &[u8; 12]) -> ChaCha {
99 ChaCha {
100 input: [
101 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
102 u32::from_le_bytes(key[ 0.. 4].try_into().unwrap()),
103 u32::from_le_bytes(key[ 4.. 8].try_into().unwrap()),
104 u32::from_le_bytes(key[ 8..12].try_into().unwrap()),
105 u32::from_le_bytes(key[12..16].try_into().unwrap()),
106 u32::from_le_bytes(key[16..20].try_into().unwrap()),
107 u32::from_le_bytes(key[20..24].try_into().unwrap()),
108 u32::from_le_bytes(key[24..28].try_into().unwrap()),
109 u32::from_le_bytes(key[28..32].try_into().unwrap()),
110 0, u32::from_le_bytes(nonce[ 0.. 4].try_into().unwrap()),
112 u32::from_le_bytes(nonce[ 4.. 8].try_into().unwrap()),
113 u32::from_le_bytes(nonce[ 8..12].try_into().unwrap()),
114 ],
115 output: [0; 64],
116 offset: 255,
117 large_block_counter: false,
118 rounds: 20,
119 }
120 }
121
122 pub fn new_chacha20(key: &[u8; 32], nonce: &[u8; 8]) -> ChaCha {
127 ChaCha {
128 input: [
129 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
130 u32::from_le_bytes(key[ 0.. 4].try_into().unwrap()),
131 u32::from_le_bytes(key[ 4.. 8].try_into().unwrap()),
132 u32::from_le_bytes(key[ 8..12].try_into().unwrap()),
133 u32::from_le_bytes(key[12..16].try_into().unwrap()),
134 u32::from_le_bytes(key[16..20].try_into().unwrap()),
135 u32::from_le_bytes(key[20..24].try_into().unwrap()),
136 u32::from_le_bytes(key[24..28].try_into().unwrap()),
137 u32::from_le_bytes(key[28..32].try_into().unwrap()),
138 0, 0,
140 u32::from_le_bytes(nonce[ 0.. 4].try_into().unwrap()),
141 u32::from_le_bytes(nonce[ 4.. 8].try_into().unwrap()),
142 ],
143 output: [0; 64],
144 offset: 255,
145 large_block_counter: true,
146 rounds: 20,
147 }
148 }
149
150 pub fn new_chacha12(key: &[u8; 32], nonce: &[u8; 8]) -> ChaCha {
155 let mut st = ChaCha::new_chacha20(key, nonce);
156 st.rounds = 12;
157 st
158 }
159
160 pub fn new_chacha8(key: &[u8; 32], nonce: &[u8; 8]) -> ChaCha {
165 let mut st = ChaCha::new_chacha20(key, nonce);
166 st.rounds = 8;
167 st
168 }
169
170 pub fn new_xchacha20(key: &[u8; 32], nonce: &[u8; 24]) -> ChaCha {
176 let mut st = [
177 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
178 u32::from_le_bytes(key[ 0.. 4].try_into().unwrap()),
179 u32::from_le_bytes(key[ 4.. 8].try_into().unwrap()),
180 u32::from_le_bytes(key[ 8..12].try_into().unwrap()),
181 u32::from_le_bytes(key[12..16].try_into().unwrap()),
182 u32::from_le_bytes(key[16..20].try_into().unwrap()),
183 u32::from_le_bytes(key[20..24].try_into().unwrap()),
184 u32::from_le_bytes(key[24..28].try_into().unwrap()),
185 u32::from_le_bytes(key[28..32].try_into().unwrap()),
186 u32::from_le_bytes(nonce[ 0.. 4].try_into().unwrap()),
187 u32::from_le_bytes(nonce[ 4.. 8].try_into().unwrap()),
188 u32::from_le_bytes(nonce[ 8..12].try_into().unwrap()),
189 u32::from_le_bytes(nonce[12..16].try_into().unwrap()),
190 ];
191 permute_general(20, &mut st, false, None);
192
193 ChaCha {
194 input: [
195 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
196 st[ 0], st[ 1], st[ 2], st[ 3],
197 st[12], st[13], st[14], st[15],
198 0, 0,
199 u32::from_le_bytes(nonce[16..20].try_into().unwrap()),
200 u32::from_le_bytes(nonce[20..24].try_into().unwrap()),
201 ],
202 output: [0; 64],
203 offset: 255,
204 large_block_counter: true,
205 rounds: 20,
206 }
207 }
208}
209
210#[cfg_attr(feature="nightly", repr(simd))]
211#[derive(Copy, Clone)]
212struct Row(u32, u32, u32, u32);
213
214impl Row {
215 fn add(self, x: Row) -> Row {
216 Row(
217 self.0.wrapping_add(x.0),
218 self.1.wrapping_add(x.1),
219 self.2.wrapping_add(x.2),
220 self.3.wrapping_add(x.3)
221 )
222 }
223
224 fn xor(self, x: Row) -> Row {
225 Row(self.0^x.0, self.1^x.1, self.2^x.2, self.3^x.3)
226 }
227
228 fn or(self, x: Row) -> Row {
229 Row(self.0|x.0, self.1|x.1, self.2|x.2, self.3|x.3)
230 }
231
232 fn shift_left(self, bit_distance: usize) -> Row {
233 Row(self.0<<bit_distance, self.1<<bit_distance, self.2<<bit_distance, self.3<<bit_distance)
234 }
235
236 fn shift_right(self, bit_distance: usize) -> Row {
237 Row(self.0>>bit_distance, self.1>>bit_distance, self.2>>bit_distance, self.3>>bit_distance)
238 }
239
240 fn roll_left(self, bit_distance: usize) -> Row {
241 let lefted = self.shift_left(bit_distance);
242 let righted = self.shift_right(32 - bit_distance);
243 lefted.or(righted)
244 }
245
246 fn shuffle_left_1(self) -> Row {
247 Row(self.1, self.2, self.3, self.0)
248 }
249
250 fn shuffle_left_2(self) -> Row {
251 Row(self.2, self.3, self.0, self.1)
252 }
253
254 fn shuffle_left_3(self) -> Row {
255 Row(self.3, self.0, self.1, self.2)
256 }
257
258}
259
260#[inline(always)]
263fn permute_general(mut rounds: u8, xs: &mut [u32; 16], do_add: bool, bs: Option<&mut [u8; 64]>) {
264 let mut a = Row(xs[ 0], xs[ 1], xs[ 2], xs[ 3]);
265 let mut b = Row(xs[ 4], xs[ 5], xs[ 6], xs[ 7]);
266 let mut c = Row(xs[ 8], xs[ 9], xs[10], xs[11]);
267 let mut d = Row(xs[12], xs[13], xs[14], xs[15]);
268
269 loop {
270 rounds = rounds.wrapping_sub(1);
271
272 a = a.add(b); d = a.xor(d); d = d.roll_left(16);
273 c = c.add(d); b = b.xor(c); b = b.roll_left(12);
274 a = a.add(b); d = a.xor(d); d = d.roll_left( 8);
275 c = c.add(d); b = b.xor(c); b = b.roll_left( 7);
276
277 if rounds%2==1 {
280 b = b.shuffle_left_1();
284 c = c.shuffle_left_2();
285 d = d.shuffle_left_3();
286 } else {
287 b = b.shuffle_left_3();
291 c = c.shuffle_left_2();
292 d = d.shuffle_left_1();
293 if rounds==0 {
294 break;
295 }
296 }
297 }
298 if do_add {
299 a = a.add(Row(xs[ 0], xs[ 1], xs[ 2], xs[ 3]));
300 b = b.add(Row(xs[ 4], xs[ 5], xs[ 6], xs[ 7]));
301 c = c.add(Row(xs[ 8], xs[ 9], xs[10], xs[11]));
302 d = d.add(Row(xs[12], xs[13], xs[14], xs[15]));
303 }
304
305 if let Some(bs) = bs {
306 bs[ 0.. 4].copy_from_slice(&a.0.to_le_bytes());
307 bs[ 4.. 8].copy_from_slice(&a.1.to_le_bytes());
308 bs[ 8..12].copy_from_slice(&a.2.to_le_bytes());
309 bs[12..16].copy_from_slice(&a.3.to_le_bytes());
310 bs[16..20].copy_from_slice(&b.0.to_le_bytes());
311 bs[20..24].copy_from_slice(&b.1.to_le_bytes());
312 bs[24..28].copy_from_slice(&b.2.to_le_bytes());
313 bs[28..32].copy_from_slice(&b.3.to_le_bytes());
314 bs[32..36].copy_from_slice(&c.0.to_le_bytes());
315 bs[36..40].copy_from_slice(&c.1.to_le_bytes());
316 bs[40..44].copy_from_slice(&c.2.to_le_bytes());
317 bs[44..48].copy_from_slice(&c.3.to_le_bytes());
318 bs[48..52].copy_from_slice(&d.0.to_le_bytes());
319 bs[52..56].copy_from_slice(&d.1.to_le_bytes());
320 bs[56..60].copy_from_slice(&d.2.to_le_bytes());
321 bs[60..64].copy_from_slice(&d.3.to_le_bytes());
322 } else {
323 xs[ 0] = a.0; xs[ 1] = a.1; xs[ 2] = a.2; xs[ 3] = a.3;
324 xs[ 4] = b.0; xs[ 5] = b.1; xs[ 6] = b.2; xs[ 7] = b.3;
325 xs[ 8] = c.0; xs[ 9] = c.1; xs[10] = c.2; xs[11] = c.3;
326 xs[12] = d.0; xs[13] = d.1; xs[14] = d.2; xs[15] = d.3;
327 }
328}
329
330pub fn permute(rounds: u8, xs: &mut [u32; 16]) {
332 permute_general(rounds, xs, false, None)
333}
334
335pub fn permute_and_add(rounds: u8, xs: &mut [u32; 16]) {
338 permute_general(rounds, xs, true, None)
339}
340
341
342impl ChaCha {
343 fn increment_counter(&mut self) -> Result<(), Error> {
344 if self.input[12] != 0 {
345 let (incremented_low, overflow) = self.input[12].overflowing_add(1);
348
349 self.input[12] = incremented_low;
350 self.input[13] = self.input[13].wrapping_add((overflow & self.large_block_counter) as u32);
351 } else {
352 if self.offset == 255 {
356 self.input[12] = 1;
357 self.offset = 64;
358 } else if self.input[13]==0 || !self.large_block_counter {
359 return Err(Error::EndReached);
361 } else {
362 self.input[12] = 1;
363 }
364 }
365
366 Ok( () )
367 }
368}
369
370impl KeyStream for ChaCha {
371 fn xor_read(&mut self, dest: &mut [u8]) -> Result<(), Error> {
372 let dest = if self.offset < 64 {
373 let from_existing = min(dest.len(), 64 - self.offset as usize);
374 for (dest_byte, output_byte) in dest.iter_mut().zip(self.output[self.offset as usize..].iter()) {
375 *dest_byte = *dest_byte ^ *output_byte;
376 }
377 self.offset += from_existing as u8;
378 &mut dest[from_existing..]
379 } else {
380 dest
381 };
382
383 for dest_chunk in dest.chunks_mut(64) {
384 let mut output_buf = self.input;
385 permute_general(self.rounds, &mut output_buf, true, None);
386 self.increment_counter()?;
387 if dest_chunk.len() == 64 {
388 for idx in 0..16 {
389 let word = u32::from_le_bytes(dest_chunk[idx*4..idx*4+4].try_into().unwrap()) ^ output_buf[idx];
390 dest_chunk[idx*4..idx*4+4].copy_from_slice(&word.to_le_bytes());
391 }
392 } else {
393 for idx in 0..16 {
394 self.output[idx*4..idx*4+4].copy_from_slice(&output_buf[idx].to_le_bytes());
395 }
396 for (dest_byte, output_byte) in dest_chunk.iter_mut().zip(self.output.iter()) {
397 *dest_byte = *dest_byte ^ output_byte;
398 }
399 self.offset = dest_chunk.len() as u8;
400 }
401 }
402
403 Ok( () )
404 }
405}
406
407impl SeekableKeyStream for ChaCha {
408 fn seek_to(&mut self, byte_offset: u64) -> Result<(), Error> {
409 if self.large_block_counter {
411 self.input[12] = (byte_offset >> 6) as u32;
412 self.input[13] = (byte_offset >> 38) as u32;
413 } else {
414 if byte_offset>=64*0x1_0000_0000 {
415 self.input[12] = 0;
417 self.offset = 64;
418 return Err(Error::EndReached);
419 } else {
420 self.input[12] = (byte_offset >> 6) as u32;
421 }
422 }
423
424 self.offset = (byte_offset & 0x3f) as u8;
425 permute_general(self.rounds, &mut self.input, true, Some(&mut self.output));
426
427 let (incremented_low, overflow) = self.input[12].overflowing_add(1);
428 self.input[12] = incremented_low;
429 self.input[13] = self.input[13].wrapping_add(if overflow {
430 if self.large_block_counter { 1 } else { 0 }
431 } else { 0 });
432
433 Ok( () )
434 }
435}
436
437
438#[cold]
440pub fn selftest() {
441 let key = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
442 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
443 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
444 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f];
445 let nonce = [0x00, 0x00, 0x00, 0x09,
446 0x00, 0x00, 0x00, 0x4a,
447 0x00, 0x00, 0x00, 0x00];
448 let expected = [0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15,
449 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
450 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03,
451 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
452 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09,
453 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
454 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9,
455 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e];
456
457 let mut result = [0u8; 64];
458 let mut state = ChaCha::new_ietf(&key, &nonce);
459 state.seek_to(64).unwrap();
460 state.xor_read(&mut result).unwrap();
461 assert_eq!(result, expected);
462}
463
464
465#[cfg(test)]
466mod tests {
467use super::*;
468
469#[test]
470fn do_selftest() {
471 selftest();
472}
473
474#[test]
475fn rfc_7539_permute_20() {
476 let mut xs = [
477 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
478 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
479 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
480 0x00000001, 0x09000000, 0x4a000000, 0x00000000,
481 ];
482
483 permute(20, &mut xs);
484
485 assert_eq!(xs, [
486 0x837778ab, 0xe238d763, 0xa67ae21e, 0x5950bb2f,
487 0xc4f2d0c7, 0xfc62bb2f, 0x8fa018fc, 0x3f5ec7b7,
488 0x335271c2, 0xf29489f3, 0xeabda8fc, 0x82e46ebd,
489 0xd19c12b4, 0xb04e16de, 0x9e83d0cb, 0x4e3c50a2,
490 ]);
491}
492
493#[test]
494fn rfc_7539_permute_and_add_20() {
495 let mut xs = [
496 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
497 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
498 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
499 0x00000001, 0x09000000, 0x4a000000, 0x00000000,
500 ];
501
502 permute_and_add(20, &mut xs);
503
504 assert_eq!(xs, [
505 0xe4e7f110, 0x15593bd1, 0x1fdd0f50, 0xc47120a3,
506 0xc7f4d1c7, 0x0368c033, 0x9aaa2204, 0x4e6cd4c3,
507 0x466482d2, 0x09aa9f07, 0x05d7c214, 0xa2028bd9,
508 0xd19c12b5, 0xb94e16de, 0xe883d0cb, 0x4e3c50a2,
509 ]);
510}
511
512#[test]
513fn rfc_7539_case_1() {
514 let mut st = ChaCha::new_ietf(
515 &[
516 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
517 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,
518 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
519 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f
520 ], &[
521 0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x4a,
522 0x00,0x00,0x00,0x00
523 ]
524 );
525
526 let mut buf = [0u8; 128];
527 st.xor_read(&mut buf).unwrap();
528 assert_eq!(buf[64..].to_vec(), [
529 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4,
530 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e,
531 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2,
532 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e,
533 ].to_vec());
534}
535
536#[test]
537fn rfc_7539_case_2() {
538 let mut st = ChaCha::new_ietf(
539 &[
540 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
541 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
542 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
543 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
544 ], &[
545 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
546 0x00, 0x00, 0x00, 0x00
547 ]
548 );
549
550 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.";
551 let mut buf = [0u8; 178];
552 for (dest, src) in buf[64..].iter_mut().zip(plaintext.iter()) {
553 *dest = *src;
554 }
555 st.xor_read(&mut buf[..]).unwrap();
556
557 assert_eq!(buf[64..].to_vec(), [
558 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
559 0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
560 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
561 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
562 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
563 0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
564 0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
565 0x87, 0x4d,
566 ].to_vec());
567}
568
569#[test]
570fn rfc_7539_case_2_chunked() {
571 let mut st = ChaCha::new_ietf(
572 &[
573 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
574 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
575 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
576 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f
577 ], &[
578 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a,
579 0x00, 0x00, 0x00, 0x00
580 ]
581 );
582
583 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.";
584 let mut buf = [0u8; 178];
585 for (dest, src) in buf[64..].iter_mut().zip(plaintext.iter()) {
586 *dest = *src;
587 }
588 st.xor_read(&mut buf[..40]).unwrap();
589 st.xor_read(&mut buf[40..78]).unwrap();
590 st.xor_read(&mut buf[78..79]).unwrap();
591 st.xor_read(&mut buf[79..128]).unwrap();
592 st.xor_read(&mut buf[128..]).unwrap();
593
594 assert_eq!(buf[64..].to_vec(), [
595 0x6e, 0x2e, 0x35, 0x9a, 0x25, 0x68, 0xf9, 0x80, 0x41, 0xba, 0x07, 0x28, 0xdd, 0x0d, 0x69, 0x81,
596 0xe9, 0x7e, 0x7a, 0xec, 0x1d, 0x43, 0x60, 0xc2, 0x0a, 0x27, 0xaf, 0xcc, 0xfd, 0x9f, 0xae, 0x0b,
597 0xf9, 0x1b, 0x65, 0xc5, 0x52, 0x47, 0x33, 0xab, 0x8f, 0x59, 0x3d, 0xab, 0xcd, 0x62, 0xb3, 0x57,
598 0x16, 0x39, 0xd6, 0x24, 0xe6, 0x51, 0x52, 0xab, 0x8f, 0x53, 0x0c, 0x35, 0x9f, 0x08, 0x61, 0xd8,
599 0x07, 0xca, 0x0d, 0xbf, 0x50, 0x0d, 0x6a, 0x61, 0x56, 0xa3, 0x8e, 0x08, 0x8a, 0x22, 0xb6, 0x5e,
600 0x52, 0xbc, 0x51, 0x4d, 0x16, 0xcc, 0xf8, 0x06, 0x81, 0x8c, 0xe9, 0x1a, 0xb7, 0x79, 0x37, 0x36,
601 0x5a, 0xf9, 0x0b, 0xbf, 0x74, 0xa3, 0x5b, 0xe6, 0xb4, 0x0b, 0x8e, 0xed, 0xf2, 0x78, 0x5e, 0x42,
602 0x87, 0x4d,
603 ].to_vec());
604}
605
606#[test]
607fn xchacha20_case_1() {
608 let mut stream = ChaCha::new_xchacha20(
609 &[
610 0x82, 0xf4, 0x11, 0xa0, 0x74, 0xf6, 0x56, 0xc6,
611 0x6e, 0x7d, 0xbd, 0xdb, 0x0a, 0x2c, 0x1b, 0x22,
612 0x76, 0x0b, 0x9b, 0x21, 0x05, 0xf4, 0xff, 0xdb,
613 0xb1, 0xd4, 0xb1, 0xe8, 0x24, 0xe2, 0x1d, 0xef,
614 ],
615 &[
616 0x3b, 0x07, 0xca, 0x6e, 0x72, 0x9e, 0xb4, 0x4a,
617 0x51, 0x0b, 0x7a, 0x1b, 0xe5, 0x18, 0x47, 0x83,
618 0x8a, 0x80, 0x4f, 0x8b, 0x10, 0x6b, 0x38, 0xbd,
619 ]
620 );
621
622 let mut xs = [0u8; 100];
623 stream.xor_read(&mut xs).unwrap();
624
625 assert_eq!(xs.to_vec(), [
626 0x20, 0x18, 0x63, 0x97, 0x0b, 0x8e, 0x08, 0x1f, 0x41, 0x22,
627 0xad, 0xdf, 0xdf, 0x32, 0xf6, 0xc0, 0x3e, 0x48, 0xd9, 0xbc,
628 0x4e, 0x34, 0xa5, 0x96, 0x54, 0xf4, 0x92, 0x48, 0xb9, 0xbe,
629 0x59, 0xd3, 0xea, 0xa1, 0x06, 0xac, 0x33, 0x76, 0xe7, 0xe7,
630 0xd9, 0xd1, 0x25, 0x1f, 0x2c, 0xbf, 0x61, 0xef, 0x27, 0x00,
631 0x0f, 0x3d, 0x19, 0xaf, 0xb7, 0x6b, 0x9c, 0x24, 0x71, 0x51,
632 0xe7, 0xbc, 0x26, 0x46, 0x75, 0x83, 0xf5, 0x20, 0x51, 0x8e,
633 0xcc, 0xd2, 0x05, 0x5c, 0xcd, 0x6c, 0xc8, 0xa1, 0x95, 0x95,
634 0x3d, 0x82, 0xa1, 0x0c, 0x20, 0x65, 0x91, 0x67, 0x78, 0xdb,
635 0x35, 0xda, 0x2b, 0xe4, 0x44, 0x15, 0xd2, 0xf5, 0xef, 0xb0,
636 ].to_vec());
637}
638
639#[test]
640fn chacha12_case_1() {
641 let mut stream = ChaCha::new_chacha12(
642 &[
643 0x27, 0xfc, 0x12, 0x0b, 0x01, 0x3b, 0x82, 0x9f,
644 0x1f, 0xae, 0xef, 0xd1, 0xab, 0x41, 0x7e, 0x86,
645 0x62, 0xf4, 0x3e, 0x0d, 0x73, 0xf9, 0x8d, 0xe8,
646 0x66, 0xe3, 0x46, 0x35, 0x31, 0x80, 0xfd, 0xb7,
647 ],
648 &[
649 0xdb, 0x4b, 0x4a, 0x41, 0xd8, 0xdf, 0x18, 0xaa
650 ]
651 );
652
653 let mut xs = [0u8; 100];
654 stream.xor_read(&mut xs).unwrap();
655
656 assert_eq!(xs.to_vec(), [
657 0x5f, 0x3c, 0x8c, 0x19, 0x0a, 0x78, 0xab, 0x7f,
658 0xe8, 0x08, 0xca, 0xe9, 0xcb, 0xcb, 0x0a, 0x98,
659 0x37, 0xc8, 0x93, 0x49, 0x2d, 0x96, 0x3a, 0x1c,
660 0x2e, 0xda, 0x6c, 0x15, 0x58, 0xb0, 0x2c, 0x83,
661 0xfc, 0x02, 0xa4, 0x4c, 0xbb, 0xb7, 0xe6, 0x20,
662 0x4d, 0x51, 0xd1, 0xc2, 0x43, 0x0e, 0x9c, 0x0b,
663 0x58, 0xf2, 0x93, 0x7b, 0xf5, 0x93, 0x84, 0x0c,
664 0x85, 0x0b, 0xda, 0x90, 0x51, 0xa1, 0xf0, 0x51,
665 0xdd, 0xf0, 0x9d, 0x2a, 0x03, 0xeb, 0xf0, 0x9f,
666 0x01, 0xbd, 0xba, 0x9d, 0xa0, 0xb6, 0xda, 0x79,
667 0x1b, 0x2e, 0x64, 0x56, 0x41, 0x04, 0x7d, 0x11,
668 0xeb, 0xf8, 0x50, 0x87, 0xd4, 0xde, 0x5c, 0x01,
669 0x5f, 0xdd, 0xd0, 0x44,
670 ].to_vec());
671}
672
673
674#[test]
675fn chacha8_case_1() {
676 let mut stream = ChaCha::new_chacha8(
677 &[
678 0x64, 0x1a, 0xea, 0xeb, 0x08, 0x03, 0x6b, 0x61,
679 0x7a, 0x42, 0xcf, 0x14, 0xe8, 0xc5, 0xd2, 0xd1,
680 0x15, 0xf8, 0xd7, 0xcb, 0x6e, 0xa5, 0xe2, 0x8b,
681 0x9b, 0xfa, 0xf8, 0x3e, 0x03, 0x84, 0x26, 0xa7,
682 ],
683 &[
684 0xa1, 0x4a, 0x11, 0x68, 0x27, 0x1d, 0x45, 0x9b,
685 ]
686 );
687
688 let mut xs = [0u8; 100];
689 stream.xor_read(&mut xs).unwrap();
690
691 assert_eq!(xs.to_vec(), [
692 0x17, 0x21, 0xc0, 0x44, 0xa8, 0xa6, 0x45, 0x35,
693 0x22, 0xdd, 0xdb, 0x31, 0x43, 0xd0, 0xbe, 0x35,
694 0x12, 0x63, 0x3c, 0xa3, 0xc7, 0x9b, 0xf8, 0xcc,
695 0xc3, 0x59, 0x4c, 0xb2, 0xc2, 0xf3, 0x10, 0xf7,
696 0xbd, 0x54, 0x4f, 0x55, 0xce, 0x0d, 0xb3, 0x81,
697 0x23, 0x41, 0x2d, 0x6c, 0x45, 0x20, 0x7d, 0x5c,
698 0xf9, 0xaf, 0x0c, 0x6c, 0x68, 0x0c, 0xce, 0x1f,
699 0x7e, 0x43, 0x38, 0x8d, 0x1b, 0x03, 0x46, 0xb7,
700 0x13, 0x3c, 0x59, 0xfd, 0x6a, 0xf4, 0xa5, 0xa5,
701 0x68, 0xaa, 0x33, 0x4c, 0xcd, 0xc3, 0x8a, 0xf5,
702 0xac, 0xe2, 0x01, 0xdf, 0x84, 0xd0, 0xa3, 0xca,
703 0x22, 0x54, 0x94, 0xca, 0x62, 0x09, 0x34, 0x5f,
704 0xcf, 0x30, 0x13, 0x2e,
705 ].to_vec());
706}
707
708#[test]
709fn chacha20_case_1() {
710 let mut stream = ChaCha::new_chacha20(
711 &[
712 0xfa, 0x44, 0x47, 0x8c, 0x59, 0xca, 0x70, 0x53,
713 0x8e, 0x35, 0x49, 0x09, 0x6c, 0xe8, 0xb5, 0x23,
714 0x23, 0x2c, 0x50, 0xd9, 0xe8, 0xe8, 0xd1, 0x0c,
715 0x20, 0x3e, 0xf6, 0xc8, 0xd0, 0x70, 0x98, 0xa5
716 ],
717 &[
718 0x8d, 0x3a, 0x0d, 0x6d, 0x78, 0x27, 0xc0, 0x07
719 ]
720 );
721
722 let offset = 274877906800u64;
723 assert!((offset>>38) != ((offset+240)>>38)); stream.seek_to(offset).unwrap();
726
727 let mut xs = [0u8; 256];
728 stream.xor_read(&mut xs).unwrap();
729
730 assert_eq!(xs.to_vec(), [
731 0x15, 0x46, 0xa5, 0x47, 0xff, 0x77, 0xc5, 0xc9,
732 0x64, 0xe4, 0x4f, 0xd0, 0x39, 0xe9, 0x13, 0xc6,
733 0x39, 0x5c, 0x8f, 0x19, 0xd4, 0x3e, 0xfa, 0xa8,
734 0x80, 0x75, 0x0f, 0x66, 0x87, 0xb4, 0xe6, 0xe2,
735 0xd8, 0xf4, 0x2f, 0x63, 0x54, 0x6d, 0xa2, 0xd1,
736 0x33, 0xb5, 0xaa, 0x2f, 0x1e, 0xf3, 0xf2, 0x18,
737 0xb6, 0xc7, 0x29, 0x43, 0x08, 0x9e, 0x40, 0x12,
738 0x21, 0x0c, 0x2c, 0xbe, 0xd0, 0xe8, 0xe9, 0x34,
739 0x98, 0xa6, 0x82, 0x5f, 0xc8, 0xff, 0x7a, 0x50,
740 0x4f, 0x26, 0xdb, 0x33, 0xb6, 0xcb, 0xe3, 0x62,
741 0x99, 0x43, 0x62, 0x44, 0xc9, 0xb2, 0xef, 0xf8,
742 0x83, 0x02, 0xc5, 0x59, 0x33, 0x91, 0x1b, 0x7d,
743 0x5d, 0xea, 0x75, 0xf2, 0xb6, 0xd4, 0x76, 0x1b,
744 0xa4, 0x4b, 0xb6, 0xf8, 0x14, 0xc9, 0x87, 0x9d,
745 0x2b, 0xa2, 0xac, 0x8b, 0x17, 0x8f, 0xa1, 0x10,
746 0x4a, 0x36, 0x86, 0x94, 0x87, 0x23, 0x39, 0x73,
747 0x8f, 0xfb, 0x96, 0x0e, 0x33, 0xdb, 0x39, 0xef,
748 0xb8, 0xea, 0xef, 0x88, 0x5b, 0x91, 0x0e, 0xea,
749 0x07, 0x8e, 0x7a, 0x1f, 0xeb, 0x3f, 0x81, 0x85,
750 0xda, 0xfd, 0x14, 0x55, 0xb7, 0x04, 0xd7, 0x6d,
751 0xa3, 0xa0, 0xce, 0x47, 0x60, 0x74, 0x18, 0x41,
752 0x21, 0x7b, 0xba, 0x1e, 0x4e, 0xce, 0x76, 0x0e,
753 0xaf, 0x68, 0x61, 0x71, 0x33, 0x43, 0x1f, 0xeb,
754 0x80, 0x6c, 0x06, 0x11, 0x73, 0xaf, 0x6b, 0x8b,
755 0x2a, 0x23, 0xbe, 0x90, 0xc5, 0xd1, 0x45, 0xcc,
756 0x25, 0x8e, 0x3c, 0x11, 0x9a, 0xab, 0x28, 0x00,
757 0xf0, 0xc7, 0xbc, 0x19, 0x59, 0xda, 0xe7, 0x54,
758 0x81, 0x71, 0x2c, 0xab, 0x73, 0x1b, 0x7d, 0xfd,
759 0x78, 0x3f, 0xa3, 0xa2, 0x28, 0xf9, 0x96, 0x8a,
760 0xae, 0xa6, 0x8f, 0x36, 0xa9, 0x2f, 0x43, 0xc9,
761 0xb5, 0x23, 0x33, 0x7a, 0x55, 0xb9, 0x7b, 0xca,
762 0xf5, 0xf5, 0x77, 0x44, 0x47, 0xbf, 0x41, 0xe8,
763 ].to_vec());
764}
765
766#[test]
767fn seek_off_end() {
768 let mut st = ChaCha::new_ietf(&[0xff; 32], &[0; 12]);
769
770 assert_eq!(st.seek_to(0x40_0000_0000), Err(Error::EndReached));
771 assert_eq!(st.xor_read(&mut [0u8; 1]), Err(Error::EndReached));
772
773 assert_eq!(st.seek_to(1), Ok(()));
774 assert!(st.xor_read(&mut [0u8; 1]).is_ok());
775}
776
777#[test]
778fn read_last_bytes() {
779 let mut st = ChaCha::new_ietf(&[0xff; 32], &[0; 12]);
780
781 st.seek_to(0x40_0000_0000 - 10).expect("should be able to seek to near the end");
782 st.xor_read(&mut [0u8; 10]).expect("should be able to read last 10 bytes");
783 assert!(st.xor_read(&mut [0u8; 1]).is_err());
784 assert!(st.xor_read(&mut [0u8; 10]).is_err());
785
786 st.seek_to(0x40_0000_0000 - 10).unwrap();
787 assert!(st.xor_read(&mut [0u8; 11]).is_err());
788}
789
790#[test]
791fn seek_consistency() {
792 let mut st = ChaCha::new_ietf(&[0x50; 32], &[0x44; 12]);
793
794 let mut continuous = [0u8; 1000];
795 st.xor_read(&mut continuous).unwrap();
796
797 let mut chunks = [0u8; 1000];
798
799 st.seek_to(128).unwrap();
800 st.xor_read(&mut chunks[128..300]).unwrap();
801
802 st.seek_to(0).unwrap();
803 st.xor_read(&mut chunks[0..10]).unwrap();
804
805 st.seek_to(300).unwrap();
806 st.xor_read(&mut chunks[300..533]).unwrap();
807
808 st.seek_to(533).unwrap();
809 st.xor_read(&mut chunks[533..]).unwrap();
810
811 st.seek_to(10).unwrap();
812 st.xor_read(&mut chunks[10..128]).unwrap();
813
814 assert_eq!(continuous.to_vec(), chunks.to_vec());
815
816 assert!(st.seek_to(0x40_0000_0000).is_err());
818 let mut small = [0u8; 100];
819 st.seek_to(0).unwrap();
820 st.xor_read(&mut small).unwrap();
821 assert_eq!(small.to_vec(), continuous[..100].to_vec());
822}
823
824} #[cfg(all(test, feature="bench"))]
828mod bench {
829 use super::{ChaCha, KeyStream};
830 use test::Bencher;
831
832 #[bench]
833 pub fn chacha20(bh: &mut Bencher) {
834 let mut stream = ChaCha::new_chacha20(&[0; 32], &[0; 8]);
835 let mut buf = [0u8; 1024];
836 bh.bytes = buf.len() as u64;
837 bh.iter(|| {
838 let _ = stream.xor_read(&mut buf);
839 });
840 }
841}