Skip to main content

wasm_benchmarks_crypto/
wasm_benchmarks_crypto.rs

1// wasm32 crypto benchmark suite (hash, mac, stream cipher, AEAD).
2//
3// Compile (with simd): RUSTFLAGS="-C target-feature=+simd128" cargo build --target=wasm32-wasip1 -p wasm_benchmarks_crypto --release
4// Run: node tools/wasm_runner/wasm_runner.ts target/wasm32-wasip1/release/wasm_benchmarks_crypto.wasm
5//
6// Or: RUSTFLAGS="-C target-feature=+simd128" cargo run --target=wasm32-wasip1 -p wasm_benchmarks_crypto
7
8use 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_hashes(&mut results);
53    // bench_macs(&mut results);
54    // bench_stream_ciphers(&mut results);
55    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}