1 //! Development-related functionality
2 
3 /// Test core functionality of synchronous stream cipher
4 #[macro_export]
5 #[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
6 macro_rules! stream_cipher_test {
7     ($name:ident, $cipher:ty, $test_name:expr) => {
8         #[test]
9         fn $name() {
10             use cipher::generic_array::GenericArray;
11             use cipher::{blobby::Blob4Iterator, NewCipher, StreamCipher};
12 
13             let data = include_bytes!(concat!("data/", $test_name, ".blb"));
14             for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() {
15                 let [key, iv, pt, ct] = row.unwrap();
16 
17                 for chunk_n in 1..256 {
18                     let mut mode = <$cipher>::new_from_slices(key, iv).unwrap();
19                     let mut pt = pt.to_vec();
20                     for chunk in pt.chunks_mut(chunk_n) {
21                         mode.apply_keystream(chunk);
22                     }
23                     if pt != &ct[..] {
24                         panic!(
25                             "Failed main test №{}, chunk size: {}\n\
26                             key:\t{:?}\n\
27                             iv:\t{:?}\n\
28                             plaintext:\t{:?}\n\
29                             ciphertext:\t{:?}\n",
30                             i, chunk_n, key, iv, pt, ct,
31                         );
32                     }
33                 }
34             }
35         }
36     };
37 }
38 
39 /// Test stream synchronous stream cipher seeking capabilities
40 #[macro_export]
41 #[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
42 macro_rules! stream_cipher_seek_test {
43     ($name:ident, $cipher:ty) => {
44         #[test]
45         fn $name() {
46             use cipher::generic_array::GenericArray;
47             use cipher::{NewCipher, StreamCipher, StreamCipherSeek};
48 
49             fn get_cipher() -> $cipher {
50                 <$cipher>::new(&Default::default(), &Default::default())
51             }
52 
53             const MAX_SEEK: usize = 512;
54 
55             let mut ct = [0u8; MAX_SEEK];
56             get_cipher().apply_keystream(&mut ct[..]);
57 
58             for n in 0..MAX_SEEK {
59                 let mut cipher = get_cipher();
60                 assert_eq!(cipher.current_pos::<usize>(), 0);
61                 cipher.seek(n);
62                 assert_eq!(cipher.current_pos::<usize>(), n);
63                 let mut buf = [0u8; MAX_SEEK];
64                 cipher.apply_keystream(&mut buf[n..]);
65                 assert_eq!(cipher.current_pos::<usize>(), MAX_SEEK);
66                 assert_eq!(&buf[n..], &ct[n..]);
67             }
68 
69             const MAX_CHUNK: usize = 128;
70             const MAX_LEN: usize = 1024;
71 
72             let mut buf = [0u8; MAX_CHUNK];
73             let mut cipher = get_cipher();
74             assert_eq!(cipher.current_pos::<usize>(), 0);
75             cipher.apply_keystream(&mut []);
76             assert_eq!(cipher.current_pos::<usize>(), 0);
77             for n in 1..MAX_CHUNK {
78                 assert_eq!(cipher.current_pos::<usize>(), 0);
79                 for m in 1.. {
80                     cipher.apply_keystream(&mut buf[..n]);
81                     assert_eq!(cipher.current_pos::<usize>(), n * m);
82                     if n * m > MAX_LEN {
83                         break;
84                     }
85                 }
86                 cipher.seek(0);
87             }
88         }
89     };
90 }
91 
92 /// Test core functionality of asynchronous stream cipher
93 #[macro_export]
94 #[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
95 macro_rules! stream_cipher_async_test {
96     ($name:ident, $test_name:expr, $cipher:ty) => {
97         #[test]
98         fn $name() {
99             use cipher::generic_array::GenericArray;
100             use cipher::{blobby::Blob4Iterator, AsyncStreamCipher, NewCipher};
101 
102             fn run_test(
103                 key: &[u8],
104                 iv: &[u8],
105                 plaintext: &[u8],
106                 ciphertext: &[u8],
107             ) -> Option<&'static str> {
108                 for n in 1..=plaintext.len() {
109                     let mut mode = <$cipher>::new_from_slices(key, iv).unwrap();
110                     let mut buf = plaintext.to_vec();
111                     for chunk in buf.chunks_mut(n) {
112                         mode.encrypt(chunk);
113                     }
114                     if buf != &ciphertext[..] {
115                         return Some("encrypt");
116                     }
117                 }
118 
119                 for n in 1..=plaintext.len() {
120                     let mut mode = <$cipher>::new_from_slices(key, iv).unwrap();
121                     let mut buf = ciphertext.to_vec();
122                     for chunk in buf.chunks_mut(n) {
123                         mode.decrypt(chunk);
124                     }
125                     if buf != &plaintext[..] {
126                         return Some("decrypt");
127                     }
128                 }
129 
130                 None
131             }
132 
133             let data = include_bytes!(concat!("data/", $test_name, ".blb"));
134 
135             for (i, row) in Blob4Iterator::new(data).unwrap().enumerate() {
136                 let [key, iv, pt, ct] = row.unwrap();
137                 if let Some(desc) = run_test(key, iv, pt, ct) {
138                     panic!(
139                         "\n\
140                          Failed test №{}: {}\n\
141                          key:\t{:?}\n\
142                          iv:\t{:?}\n\
143                          plaintext:\t{:?}\n\
144                          ciphertext:\t{:?}\n",
145                         i, desc, key, iv, pt, ct,
146                     );
147                 }
148             }
149         }
150     };
151 }
152 
153 /// Create synchronous stream cipher benchmarks
154 #[macro_export]
155 #[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
156 macro_rules! stream_cipher_sync_bench {
157     ($name:ident, $cipher:path, $data_len:expr) => {
158         #[bench]
159         pub fn $name(bh: &mut Bencher) {
160             let key = Default::default();
161             let nonce = Default::default();
162             let mut cipher = <$cipher>::new(&key, &nonce);
163             let mut data = get_data($data_len);
164 
165             bh.iter(|| {
166                 cipher.apply_keystream(&mut data);
167                 test::black_box(&data);
168             });
169             bh.bytes = data.len() as u64;
170         }
171     };
172     ($cipher:path) => {
173         extern crate test;
174 
175         use cipher::{generic_array::GenericArray, NewCipher, StreamCipher};
176         use test::Bencher;
177 
178         #[inline(never)]
179         fn get_data(n: usize) -> Vec<u8> {
180             vec![77; n]
181         }
182 
183         $crate::stream_cipher_sync_bench!(bench1_10, $cipher, 10);
184         $crate::stream_cipher_sync_bench!(bench2_100, $cipher, 100);
185         $crate::stream_cipher_sync_bench!(bench3_1000, $cipher, 1000);
186         $crate::stream_cipher_sync_bench!(bench4_10000, $cipher, 10000);
187         $crate::stream_cipher_sync_bench!(bench5_100000, $cipher, 100000);
188     };
189 }
190 
191 /// Create asynchronous stream cipher benchmarks
192 #[macro_export]
193 #[cfg_attr(docsrs, doc(cfg(feature = "dev")))]
194 macro_rules! stream_cipher_async_bench {
195     ($enc_name:ident, $dec_name:ident, $cipher:path, $data_len:expr) => {
196         #[bench]
197         pub fn $enc_name(bh: &mut Bencher) {
198             let key = Default::default();
199             let nonce = Default::default();
200             let mut cipher = <$cipher>::new(&key, &nonce);
201             let mut data = get_data($data_len);
202 
203             bh.iter(|| {
204                 cipher.encrypt(&mut data);
205                 test::black_box(&data);
206             });
207             bh.bytes = data.len() as u64;
208         }
209 
210         #[bench]
211         pub fn $dec_name(bh: &mut Bencher) {
212             let key = Default::default();
213             let nonce = Default::default();
214             let mut cipher = <$cipher>::new(&key, &nonce);
215             let mut data = get_data($data_len);
216 
217             bh.iter(|| {
218                 cipher.decrypt(&mut data);
219                 test::black_box(&data);
220             });
221             bh.bytes = data.len() as u64;
222         }
223     };
224     ($cipher:path) => {
225         extern crate test;
226 
227         use cipher::{generic_array::GenericArray, AsyncStreamCipher, NewCipher};
228         use test::Bencher;
229 
230         #[inline(never)]
231         fn get_data(n: usize) -> Vec<u8> {
232             vec![77; n]
233         }
234 
235         $crate::stream_cipher_async_bench!(encrypt_10, decrypt_10, $cipher, 10);
236         $crate::stream_cipher_async_bench!(encrypt_100, decrypt_100, $cipher, 100);
237         $crate::stream_cipher_async_bench!(encrypt_1000, decrypt_1000, $cipher, 1000);
238         $crate::stream_cipher_async_bench!(encrypt_10000, decrypt_10000, $cipher, 10000);
239         $crate::stream_cipher_async_bench!(encrypt_100000, decrypt_100000, $cipher, 100000);
240     };
241 }
242