1use std::{
9 hint::black_box,
10 time::{Duration, Instant},
11};
12
13use crypto::{
14 Aead, Hasher, StreamCipher,
15 aes::{Aes256Ctr, Aes256Gcm},
16 ascon::{AsconAead128, AsconHash256},
17 blake3::Blake3,
18 chacha::{ChaCha8Djb, ChaCha8Poly1305, ChaCha12Djb, ChaCha20Blake3, ChaCha20Djb, ChaCha20Poly1305},
19 hmac::Hmac,
20 poly1305::Poly1305,
21 sha2::{Sha256, Sha512},
22 sha3::{Kmac256, Sha3_256, Sha3_512, Shake256},
23};
24
25const DATA_SIZES: &[usize] = &[64, 1024, 16 * 1024, 64 * 1024, 1024 * 1024];
26
27const KEY: [u8; 32] = [
28 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
29 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
30];
31
32const KEY_16: [u8; 16] = [
33 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
34];
35
36const NONCE_8: [u8; 8] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
37const NONCE_12: [u8; 12] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C];
38const NONCE_16: [u8; 16] = [
39 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10,
40];
41const NONCE_32: [u8; 32] = [
42 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13,
43 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
44];
45
46const WARMUP_MS: u64 = 5000;
47const BENCH_MS: u64 = 5000;
48
49fn main() {
50 let mut results = Vec::new();
51
52 bench_aead(&mut results);
56
57 print_results(&results);
58}
59
60fn section(title: &str) {
61 eprintln!("\n////////////////////////////////////////////////////////////////////////////////");
62 eprintln!("// {title}");
63 eprintln!("////////////////////////////////////////////////////////////////////////////////\n");
64}
65
66fn format_size(s: usize) -> String {
67 if s >= 1024 * 1024 {
68 format!("{}MiB", s / (1024 * 1024))
69 } else if s >= 1024 {
70 format!("{}KiB", s / 1024)
71 } else {
72 format!("{}B", s)
73 }
74}
75
76fn benchmark<F>(name: &str, size: usize, mut f: F) -> f64
77where
78 F: FnMut(),
79{
80 eprint!(" {:<30} {:>8} ", name, format_size(size));
81
82 let warmup_end = Instant::now() + Duration::from_millis(WARMUP_MS);
83 while Instant::now() < warmup_end {
84 f();
85 }
86
87 let mut elapsed = Duration::ZERO;
88 let mut iters: u64 = 0;
89 let bench_end = Instant::now() + Duration::from_millis(BENCH_MS);
90
91 while Instant::now() < bench_end {
92 let start = Instant::now();
93 f();
94 elapsed += start.elapsed();
95 iters += 1;
96 }
97
98 let total_bytes = size as u64 * iters;
99 let secs = elapsed.as_secs_f64();
100 let mbs = if secs > 0.0 {
101 total_bytes as f64 / secs / 1_048_576.0
102 } else {
103 0.0
104 };
105
106 eprintln!("{:>8.1} MB/s", mbs);
107 mbs
108}
109
110fn bench_stream_ciphers(results: &mut Vec<(&str, usize, &str, f64)>) {
111 section("STREAM CIPHERS");
112
113 for &size in DATA_SIZES {
114 let mbs = benchmark("AES-256-CTR", size, || {
115 let mut cipher = Aes256Ctr::new(&KEY);
116 let mut buf = vec![0xA5u8; size];
117 cipher.xor_keystream(black_box(&mut buf));
118 });
119 results.push(("stream", size, "AES-256-CTR", mbs));
120
121 let mbs = benchmark("ChaCha8", size, || {
122 let mut cipher = ChaCha8Djb::new(&KEY, &NONCE_8);
123 let mut buf = vec![0xA5u8; size];
124 cipher.xor_keystream(black_box(&mut buf));
125 });
126 results.push(("stream", size, "ChaCha8", mbs));
127
128 let mbs = benchmark("ChaCha12", size, || {
129 let mut cipher = ChaCha12Djb::new(&KEY, &NONCE_8);
130 let mut buf = vec![0xA5u8; size];
131 cipher.xor_keystream(black_box(&mut buf));
132 });
133 results.push(("stream", size, "ChaCha12", mbs));
134
135 let mbs = benchmark("ChaCha20", size, || {
136 let mut cipher = ChaCha20Djb::new(&KEY, &NONCE_8);
137 let mut buf = vec![0xA5u8; size];
138 cipher.xor_keystream(black_box(&mut buf));
139 });
140 results.push(("stream", size, "ChaCha20", mbs));
141
142 eprintln!();
143 }
144}
145
146fn bench_hashes(results: &mut Vec<(&str, usize, &str, f64)>) {
147 section("HASH FUNCTIONS");
148
149 for &size in DATA_SIZES {
150 let data = vec![0xA5u8; size];
151 let data2 = data.clone();
152 let mbs = benchmark("SHA-256", size, || {
153 let _ = Sha256::hash(black_box(&data2));
154 });
155 results.push(("hash", size, "SHA-256", mbs));
156
157 let data2 = data.clone();
158 let mbs = benchmark("SHA-512", size, || {
159 let _ = Sha512::hash(black_box(&data2));
160 });
161 results.push(("hash", size, "SHA-512", mbs));
162
163 let data2 = data.clone();
164 let mbs = benchmark("SHA3-256", size, || {
165 let _ = Sha3_256::hash(black_box(&data2));
166 });
167 results.push(("hash", size, "SHA3-256", mbs));
168
169 let data2 = data.clone();
170 let mbs = benchmark("SHA3-512", size, || {
171 let _ = Sha3_512::hash(black_box(&data2));
172 });
173 results.push(("hash", size, "SHA3-512", mbs));
174
175 let data2 = data.clone();
176 let mbs = benchmark("SHAKE256", size, || {
177 let _ = <Shake256 as Hasher>::hash(black_box(&data2));
178 });
179 results.push(("hash", size, "SHAKE256", mbs));
180
181 let data2 = data.clone();
182 let mbs = benchmark("BLAKE3", size, || {
183 let _ = Blake3::hash(black_box(&data2));
184 });
185 results.push(("hash", size, "BLAKE3", mbs));
186
187 let data2 = data.clone();
188 let mbs = benchmark("Ascon-Hash256", size, || {
189 let _ = AsconHash256::hash(black_box(&data2));
190 });
191 results.push(("hash", size, "Ascon-Hash256", mbs));
192
193 eprintln!();
194 }
195}
196
197fn bench_macs(results: &mut Vec<(&str, usize, &str, f64)>) {
198 section("MACs");
199
200 let hmac_key = b"rust-stdx-crypto-bench-key";
201 let customization = b"rust-stdx";
202
203 for &size in DATA_SIZES {
204 let data = vec![0xA3u8; size];
205
206 let data2 = data.clone();
207 let mbs = benchmark("HMAC-SHA256", size, || {
208 let mut hmac = Hmac::<Sha256>::new(black_box(hmac_key));
209 hmac.update(black_box(&data2));
210 let _ = hmac.finalize();
211 });
212 results.push(("mac", size, "HMAC-SHA256", mbs));
213
214 let data2 = data.clone();
215 let mbs = benchmark("HMAC-SHA512", size, || {
216 let mut hmac = Hmac::<Sha512>::new(black_box(hmac_key));
217 hmac.update(black_box(&data2));
218 let _ = hmac.finalize();
219 });
220 results.push(("mac", size, "HMAC-SHA512", mbs));
221
222 let data2 = data.clone();
223 let mbs = benchmark("KMAC256", size, || {
224 let mut kmac = Kmac256::new(black_box(&KEY), black_box(customization));
225 kmac.update(black_box(&data2));
226 let mut out = [0u8; 32];
227 kmac.finalize_into(&mut out);
228 black_box(out);
229 });
230 results.push(("mac", size, "KMAC256", mbs));
231
232 let data2 = data.clone();
233 let mbs = benchmark("Poly1305", size, || {
234 let out = Poly1305::mac(&KEY, &data2);
235 black_box(out);
236 });
237 results.push(("mac", size, "Poly1305", mbs));
238
239 let data2 = data.clone();
240 let mbs = benchmark("BLAKE3-keyed", size, || {
241 let out = Blake3::keyed_hash(black_box(&KEY), black_box(&data2));
242 black_box(out);
243 });
244 results.push(("mac", size, "BLAKE3-keyed", mbs));
245
246 eprintln!();
247 }
248}
249
250fn bench_aead(results: &mut Vec<(&str, usize, &str, f64)>) {
251 section("AEADs");
252
253 let aes = Aes256Gcm::new(&KEY);
254 let chacha8poly1305 = ChaCha8Poly1305::new(&KEY);
255 let chacha20poly1305 = ChaCha20Poly1305::new(&KEY);
256 let chacha_blake3 = ChaCha20Blake3::new(&KEY);
257 let ascon_aead = AsconAead128::new(&KEY_16);
258
259 for &size in DATA_SIZES {
260 let mbs = benchmark("AES-256-GCM-encrypt", size, || {
261 let mut buf = vec![0xA5u8; size];
262 let _tag = aes.encrypt_in_place(&mut buf, &NONCE_12[..], &[]);
263 });
264 results.push(("aead", size, "AES-256-GCM-encrypt", mbs));
265
266 let mut data = vec![0xA5u8; size];
267 let tag = aes.encrypt_in_place(&mut data, &NONCE_12[..], &[]);
268 let mbs = benchmark("AES-256-GCM-decrypt", size, || {
269 let mut buf = data.clone();
270 let _ = aes.decrypt_in_place(&mut buf, &NONCE_12[..], &[], tag.as_ref());
271 });
272 results.push(("aead", size, "AES-256-GCM-decrypt", mbs));
273
274 let mbs = benchmark("ChaCha8-Poly1305-encrypt", size, || {
275 let mut buf = vec![0xA5u8; size];
276 let _tag = chacha8poly1305.encrypt_in_place(&mut buf, &NONCE_12[..], &[]);
277 });
278 results.push(("aead", size, "ChaCha8-Poly1305-encrypt", mbs));
279
280 let mut data = vec![0xA5u8; size];
281 let tag = chacha8poly1305.encrypt_in_place(&mut data, &NONCE_12[..], &[]);
282 let mbs = benchmark("ChaCha8-Poly1305-decrypt", size, || {
283 let mut buf = data.clone();
284 let _ = chacha20poly1305.decrypt_in_place(&mut buf, &NONCE_12[..], &[], tag.as_ref());
285 });
286 results.push(("aead", size, "ChaCha8-Poly1305-decrypt", mbs));
287
288 let mbs = benchmark("ChaCha20-Poly1305-encrypt", size, || {
289 let mut buf = vec![0xA5u8; size];
290 let _tag = chacha20poly1305.encrypt_in_place(&mut buf, &NONCE_12[..], &[]);
291 });
292 results.push(("aead", size, "ChaCha20-Poly1305-encrypt", mbs));
293
294 let mut data = vec![0xA5u8; size];
295 let tag = chacha20poly1305.encrypt_in_place(&mut data, &NONCE_12[..], &[]);
296 let mbs = benchmark("ChaCha20-Poly1305-decrypt", size, || {
297 let mut buf = data.clone();
298 let _ = chacha20poly1305.decrypt_in_place(&mut buf, &NONCE_12[..], &[], tag.as_ref());
299 });
300 results.push(("aead", size, "ChaCha20-Poly1305-decrypt", mbs));
301
302 let mbs = benchmark("ChaCha20-BLAKE3-encrypt", size, || {
303 let mut buf = vec![0xA5u8; size];
304 let _tag = chacha_blake3.encrypt_in_place(&mut buf, &NONCE_32[..], &[]);
305 });
306 results.push(("aead", size, "ChaCha20-B3-encrypt", mbs));
307
308 let mut data = vec![0xA5u8; size];
309 let tag = chacha_blake3.encrypt_in_place(&mut data, &NONCE_32[..], &[]);
310 let mbs = benchmark("ChaCha20-BLAKE3-decrypt", size, || {
311 let mut buf = data.clone();
312 let _ = chacha_blake3.decrypt_in_place(&mut buf, &NONCE_32[..], &[], tag.as_ref());
313 });
314 results.push(("aead", size, "ChaCha20-B3-decrypt", mbs));
315
316 let mbs = benchmark("Ascon-AEAD128-encrypt", size, || {
317 let mut buf = vec![0xA5u8; size];
318 let _tag = ascon_aead.encrypt_in_place(&mut buf, &NONCE_16[..], &[]);
319 });
320 results.push(("aead", size, "Ascon-AEAD128-encrypt", mbs));
321
322 let mut data = vec![0xA5u8; size];
323 let tag = ascon_aead.encrypt_in_place(&mut data, &NONCE_16[..], &[]);
324 let mbs = benchmark("Ascon-AEAD128-decrypt", size, || {
325 let mut buf = data.clone();
326 let _ = ascon_aead.decrypt_in_place(&mut buf, &NONCE_16[..], &[], tag.as_ref());
327 });
328 results.push(("aead", size, "Ascon-AEAD128-decrypt", mbs));
329
330 eprintln!();
331 }
332}
333
334fn print_results(results: &[(&str, usize, &str, f64)]) {
335 println!("\n\n========== SUMMARY ==========");
336 println!("{:<30} {:>10} {:>16}", "Algorithm", "Size (B)", "Throughput");
337 println!("{:-<30} {:-<10} {:-<16}", "", "", "");
338
339 for &(_cat, size, name, mbs) in results {
340 println!("{:<30} {:>10} {:>10.1} MB/s", name, size, mbs);
341 }
342}