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