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