November 8, 2026 · kixelated
Cryptography Slop
I’m one of the OG “rewrite it in Rust” guys. I love Rust.
Unfortunately, I wrote my last line of Rust 6 months ago. I still want Claude to write everything in Rust, but it’s difficult when rustc turns my desktop into lava.
I spent some tokens investigating on how to speed up MoQ build.
One finding was that aws-lc-rs takes 60s+ to build.
wtf why does a single crate take a full minute to build?
Despite the suffix, aws-lc-rs is actually a C library in disguise.
Same with the next best crypto library: ring.
wtf is C doing in my Rust library.
THIS WILL NOT STAND.
I did what any sane software engineer does in current_year,
I pulled a 9-year old gaming PC out of the closet, installed Claude, and ran /goal rewrite it in rust *.
* and a lot of assembly
WOW FAST
rind is vibe slopped crypto.
It started as fork of Graviola and quickly morphed into a monsterish combination of ring and aws-lc-rs and libcrux.
aws-lc-rs's
packets per core
AES-128-GCM, the geometric mean over CPUs. Arrow Lake: 1.71×, Zen 3: 1.77×, Coffee Lake: 1.16×, Apple M4: 1.12×. aws-lc is Amazon's BoringSSL fork. On Mac, rind holds DIT (constant-time mode) across each batch.
Data
| Library | Arrow Lake, 32 B | Arrow Lake, 1350 B | Zen 3, 32 B | Zen 3, 1350 B | Coffee Lake, 32 B | Coffee Lake, 1350 B | Apple M4, 32 B | Apple M4, 1350 B |
|---|---|---|---|---|---|---|---|---|
| rind | 19.7 ns | 92.7 ns | 38.7 ns | 165 ns | 43.2 ns | 246 ns | 10.8 ns | 161 ns |
| aws-lc-rs | 37.5 ns | 159 ns | 78.2 ns | 291 ns | 88.8 ns | 285 ns | 49.7 ns | 181 ns |
| ring | 41.1 ns | 119 ns | 56.3 ns | 191 ns | 70.7 ns | 277 ns | 41.8 ns | 210 ns |
| RustCrypto | 73.1 ns | 278 ns | 97.0 ns | 434 ns | 100 ns | 547 ns | 23.9 ns | 212 ns |
| graviola | 42.1 ns | 226 ns | 69.6 ns | 295 ns | 71.4 ns | 400 ns | 79.0 ns | 216 ns |
| libcrux | 171 ns | 1.98 µs | 321 ns | 4.72 µs | 282 ns | 3.49 µs | 51.9 ns | 600 ns |
| OpenSSL | 56.8 ns | 160 ns | 115 ns | 326 ns | 156 ns | 352 ns | 66.8 ns | 196 ns |
AES-128-GCM seal, criterion's estimate from rind's benchmark report at 00610aff, run 2026-11-06 and 2026-11-07: Arrow Lake is Intel(R) Core(TM) Ultra 7 270K Plus, Zen 3 is AMD Ryzen 9 5900XT 16-Core Processor, Coffee Lake is Intel(R) Core(TM) i7-8700 CPU @ 3.20GHz, Apple M4 is Apple M4. On Mac, rind holds DIT across the timed loop, as a caller batching packets does.
^ This is a big deal because AES-128-GCM is the most popular TLS cipher suite by far. Even a tiny reduction in CPU usage has huge impact when multiplied across every HTTPS request in the world*.
* this benchmark is HTTP/3 specific. HTTP/1 and HTTP/2 use different payload sizes. There be a lot of variance depending on the architecture.
WOW FAST
Rust gets dunked on for compile speeds but nerds forget that C/C++ can also be terrible when uncached.
Have you ever tried to cold compile Chromium? It takes literal hours.
faster to
build
The geometric mean over CPUs. Arrow Lake: 3.9×, Coffee Lake: 3.2×, Apple M4: 5.2×.
Data
| CPU | Build | Time | CPU time | Binary | Tools |
|---|---|---|---|---|---|
| Arrow Lake | rind | 3.4 s | 8.0 s | 0.84 MiB | rustc only |
| Arrow Lake | aws-lc-rs | 11.8 s | 37.1 s | 2.85 MiB | needs C compiler |
| Arrow Lake | ring | 5.0 s | 7.3 s | 0.71 MiB | needs C compiler |
| Arrow Lake | RustCrypto | 2.8 s | 15.8 s | 0.52 MiB | rustc only |
| Arrow Lake | graviola | 2.1 s | 4.5 s | 0.87 MiB | rustc only |
| Arrow Lake | libcrux | 5.8 s | 13.4 s | 0.71 MiB | rustc only |
| Arrow Lake | OpenSSL | 3.4 s | 8.7 s | 0.36 MiB | needs C compiler, pkg-config |
| Arrow Lake | openssl-vendored | 24.7 s | 161.4 s | 4.90 MiB | needs C compiler, make, perl |
| Arrow Lake | rustls + rind | 4.2 s | 21.1 s | 2.86 MiB | rustc only |
| Arrow Lake | rustls + rind-lean | 4.5 s | 23.2 s | 1.74 MiB | rustc only |
| Arrow Lake | rustls + aws-lc-rs | 16.6 s | 55.2 s | 3.92 MiB | needs C compiler |
| Arrow Lake | rustls + ring | 7.8 s | 19.3 s | 1.85 MiB | needs C compiler |
| Coffee Lake | rind | 7.3 s | 12.4 s | 0.84 MiB | rustc only |
| Coffee Lake | aws-lc-rs | 26.2 s | 62.9 s | 2.85 MiB | needs C compiler |
| Coffee Lake | ring | 9.3 s | 12.1 s | 0.71 MiB | needs C compiler |
| Coffee Lake | RustCrypto | 8.3 s | 24.0 s | 0.52 MiB | rustc only |
| Coffee Lake | graviola | 3.8 s | 6.5 s | 0.87 MiB | rustc only |
| Coffee Lake | libcrux | 9.7 s | 20.2 s | 0.71 MiB | rustc only |
| Coffee Lake | OpenSSL | 5.8 s | 11.6 s | 0.36 MiB | needs C compiler, pkg-config |
| Coffee Lake | openssl-vendored | 82.3 s | 220.2 s | 4.90 MiB | needs C compiler, make, perl |
| Coffee Lake | rustls + rind | 11.3 s | 31.5 s | 2.82 MiB | rustc only |
| Coffee Lake | rustls + rind-lean | 11.8 s | 32.8 s | 1.74 MiB | rustc only |
| Coffee Lake | rustls + aws-lc-rs | 36.6 s | 88.7 s | 3.92 MiB | needs C compiler |
| Coffee Lake | rustls + ring | 16.4 s | 30.5 s | 1.85 MiB | needs C compiler |
| Apple M4 | rind | 4.0 s | 7.5 s | 0.64 MiB | rustc only |
| Apple M4 | aws-lc-rs | 29.0 s | 92.1 s | 2.04 MiB | needs C compiler |
| Apple M4 | ring | 8.2 s | 10.2 s | 0.61 MiB | needs C compiler |
| Apple M4 | RustCrypto | 5.1 s | 16.0 s | 0.44 MiB | rustc only |
| Apple M4 | graviola | 3.1 s | 5.5 s | 0.78 MiB | rustc only |
| Apple M4 | libcrux | 8.0 s | 17.7 s | 0.56 MiB | rustc only |
| Apple M4 | OpenSSL | 4.6 s | 10.2 s | 0.34 MiB | needs C compiler |
| Apple M4 | openssl-vendored | 131.7 s | 420.9 s | 3.58 MiB | needs C compiler, make, perl |
| Apple M4 | rustls + rind | 6.5 s | 21.6 s | 2.07 MiB | rustc only |
| Apple M4 | rustls + rind-lean | 6.7 s | 22.1 s | 1.47 MiB | rustc only |
| Apple M4 | rustls + aws-lc-rs | 34.0 s | 109.0 s | 2.93 MiB | needs C compiler |
| Apple M4 | rustls + ring | 12.2 s | 22.9 s | 1.53 MiB | needs C compiler |
rind's admin/build-cost: the median of cold builds with an empty target directory and a warm download cache. Arrow Lake: Core Ultra 7 270K Plus, 12 jobs, 3 runs, 2026-11-07, at 9e7f6a5a; Coffee Lake: Core i7-8700, 3 jobs, 3 runs, 2026-11-06, at f95b91da; Apple M4: Apple M4, 4 jobs, 3 runs, 2026-11-07, at 9e7f6a5a. A tool counts when the build fails without it.
Most of the time these builds are cached anyway but IDK it’s still nice to see smaller number.
Wow fast
Having accomplished my goal of making MoQ faster, I let my 9-year-old gaming PC take a vacation.
lul jk I ran: /goal: Support every algorithm, and make it the fastest.
timings where rind is fastest,
or within 10%
Squares: Arrow Lake, Zen 3, Coffee Lake, Apple M4. Blank: not offered. On Mac, rind holds DIT (constant-time mode) across each batch.
Data
| Operation | rind | aws-lc-rs | ring | graviola | OpenSSL | RustCrypto | libcrux |
|---|---|---|---|---|---|---|---|
| full handshake, X25519MLKEM768 | 172 µs / 261 µs / 302 µs / 174 µs | 234 µs / 332 µs / 458 µs / 212 µs | - / - / - / - | 188 µs / 343 µs / 376 µs / 179 µs | 439 µs / 803 µs / 831 µs / 463 µs | - / - / - / - | - / - / - / - |
| full handshake, X25519 | 151 µs / 228 µs / 260 µs / 151 µs | 202 µs / 292 µs / 370 µs / 183 µs | 221 µs / 328 µs / 338 µs / 184 µs | 161 µs / 239 µs / 316 µs / 159 µs | 366 µs / 574 µs / 687 µs / 375 µs | - / - / - / - | - / - / - / - |
| full handshake, P-256 | 175 µs / 243 µs / 296 µs / 172 µs | 228 µs / 304 µs / 417 µs / 206 µs | 220 µs / 282 µs / 328 µs / 202 µs | 192 µs / 270 µs / 369 µs / 190 µs | 401 µs / 624 µs / 760 µs / 401 µs | - / - / - / - | - / - / - / - |
| full handshake, Ed25519 certificate | 122 µs / 187 µs / 213 µs / 129 µs | 167 µs / 233 µs / 306 µs / 150 µs | 202 µs / 293 µs / 325 µs / 155 µs | 124 µs / 186 µs / 250 µs / 124 µs | 368 µs / 640 µs / 747 µs / 383 µs | - / - / - / - | - / - / - / - |
| full handshake, RSA-2048 certificate | 433 µs / 550 µs / 614 µs / 373 µs | 480 µs / 646 µs / 758 µs / 409 µs | 488 µs / 647 µs / 678 µs / 447 µs | 440 µs / 568 µs / 687 µs / 674 µs | 671 µs / 992 µs / 1.01 ms / 644 µs | - / - / - / - | - / - / - / - |
| resumed handshake, X25519 | 54.4 µs / 77.2 µs / 121 µs / 65.9 µs | 101 µs / 133 µs / 229 µs / 89.8 µs | 111 µs / 158 µs / 200 µs / 87.2 µs | 55.6 µs / 77.3 µs / 160 µs / 61.6 µs | 232 µs / 371 µs / 497 µs / 253 µs | - / - / - / - | - / - / - / - |
| 1 MB transfer, AES-128-GCM | 168 µs / 289 µs / 441 µs / 309 µs | 233 µs / 458 µs / 443 µs / 313 µs | 170 µs / 282 µs / 449 µs / 373 µs | 290 µs / 424 µs / 579 µs / 334 µs | 251 µs / 470 µs / 491 µs / 340 µs | - / - / - / - | - / - / - / - |
| 1 MB transfer, AES-256-GCM | 185 µs / 293 µs / 545 µs / 349 µs | 265 µs / 461 µs / 558 µs / 347 µs | 189 µs / 290 µs / 567 µs / 387 µs | 338 µs / 490 µs / 691 µs / 401 µs | 286 µs / 533 µs / 597 µs / 372 µs | - / - / - / - | - / - / - / - |
| 1 MB transfer, ChaCha20-Poly1305 | 597 µs / 689 µs / 924 µs / 1.11 ms | 595 µs / 688 µs / 928 µs / 1.10 ms | 590 µs / 693 µs / 925 µs / 1.10 ms | 1.16 ms / 1.37 ms / 1.64 ms / 1.73 ms | 658 µs / 783 µs / 984 µs / 1.11 ms | - / - / - / - | - / - / - / - |
| AES-128-GCM seal, 32 B | 19.7 ns / 38.7 ns / 43.2 ns / 10.8 ns | 37.5 ns / 78.2 ns / 88.8 ns / 49.7 ns | 41.1 ns / 56.3 ns / 70.7 ns / 41.8 ns | 42.1 ns / 69.6 ns / 71.4 ns / 79.0 ns | 56.8 ns / 115 ns / 156 ns / 66.8 ns | 73.1 ns / 97.0 ns / 100 ns / 23.9 ns | 171 ns / 321 ns / 282 ns / 51.9 ns |
| AES-128-GCM seal, 1350 B | 92.7 ns / 165 ns / 246 ns / 161 ns | 159 ns / 291 ns / 285 ns / 181 ns | 119 ns / 191 ns / 277 ns / 210 ns | 226 ns / 295 ns / 400 ns / 216 ns | 160 ns / 326 ns / 352 ns / 196 ns | 278 ns / 434 ns / 547 ns / 212 ns | 1.98 µs / 4.72 µs / 3.49 µs / 600 ns |
| AES-128-GCM seal, 16 KB | 799 ns / 1.38 µs / 2.33 µs / 1.43 µs | 1.32 µs / 2.63 µs / 2.42 µs / 1.44 µs | 818 ns / 1.39 µs / 2.38 µs / 1.94 µs | 2.05 µs / 2.57 µs / 3.75 µs / 1.60 µs | 1.35 µs / 2.61 µs / 2.45 µs / 1.51 µs | 2.49 µs / 4.28 µs / 5.62 µs / 2.26 µs | 22.0 µs / 53.9 µs / 39.9 µs / 6.78 µs |
| AES-128-GCM open, 32 B | 15.1 ns / 44.4 ns / 37.6 ns / 12.6 ns | 37.8 ns / 72.4 ns / 90.4 ns / 47.0 ns | 41.1 ns / 57.9 ns / 97.7 ns / 39.2 ns | 36.3 ns / 64.6 ns / 68.7 ns / 83.9 ns | 59.0 ns / 118 ns / 159 ns / 65.6 ns | 78.0 ns / 102 ns / 104 ns / 27.6 ns | 162 ns / 317 ns / 303 ns / 61.4 ns |
| AES-128-GCM open, 1350 B | 89.5 ns / 174 ns / 236 ns / 178 ns | 151 ns / 298 ns / 285 ns / 191 ns | 114 ns / 185 ns / 296 ns / 243 ns | 175 ns / 305 ns / 358 ns / 225 ns | 162 ns / 333 ns / 355 ns / 199 ns | 283 ns / 451 ns / 564 ns / 232 ns | 1.99 µs / 3.92 µs / 3.48 µs / 618 ns |
| AES-128-GCM open, 16 KB | 793 ns / 1.44 µs / 2.52 µs / 1.56 µs | 1.26 µs / 2.72 µs / 2.60 µs / 1.58 µs | 814 ns / 1.45 µs / 2.62 µs / 2.16 µs | 1.40 µs / 2.65 µs / 3.39 µs / 1.71 µs | 1.29 µs / 2.77 µs / 2.85 µs / 1.65 µs | 2.51 µs / 4.29 µs / 5.75 µs / 2.43 µs | 21.9 µs / 44.3 µs / 41.7 µs / 6.82 µs |
| AES-256-GCM seal, 32 B | 21.4 ns / 41.1 ns / 48.6 ns / 11.9 ns | 39.2 ns / 81.7 ns / 95.4 ns / 53.4 ns | 43.2 ns / 56.9 ns / 74.8 ns / 44.8 ns | 44.5 ns / 74.4 ns / 77.6 ns / 79.0 ns | 61.2 ns / 118 ns / 161 ns / 67.1 ns | 78.2 ns / 101 ns / 107 ns / 24.9 ns | 217 ns / 426 ns / 344 ns / 51.8 ns |
| AES-256-GCM seal, 1350 B | 107 ns / 184 ns / 325 ns / 188 ns | 175 ns / 311 ns / 363 ns / 201 ns | 134 ns / 205 ns / 353 ns / 219 ns | 257 ns / 337 ns / 476 ns / 258 ns | 184 ns / 345 ns / 437 ns / 220 ns | 313 ns / 489 ns / 623 ns / 239 ns | 2.87 µs / 6.91 µs / 4.50 µs / 630 ns |
| AES-256-GCM seal, 16 KB | 954 ns / 1.51 µs / 3.22 µs / 1.69 µs | 1.58 µs / 2.78 µs / 3.30 µs / 1.70 µs | 975 ns / 1.53 µs / 3.25 µs / 2.03 µs | 2.46 µs / 2.98 µs / 4.71 µs / 2.13 µs | 1.60 µs / 2.82 µs / 3.34 µs / 1.77 µs | 2.90 µs / 4.78 µs / 6.45 µs / 2.63 µs | 32.9 µs / 80.6 µs / 50.9 µs / 7.08 µs |
| AES-256-GCM open, 32 B | 16.4 ns / 48.4 ns / 44.7 ns / 13.1 ns | 37.8 ns / 78.6 ns / 101 ns / 48.6 ns | 43.0 ns / 60.8 ns / 107 ns / 39.4 ns | 37.7 ns / 67.5 ns / 73.1 ns / 84.3 ns | 61.1 ns / 122 ns / 172 ns / 67.6 ns | 82.3 ns / 109 ns / 118 ns / 40.8 ns | 215 ns / 441 ns / 363 ns / 65.7 ns |
| AES-256-GCM open, 1350 B | 98.9 ns / 192 ns / 328 ns / 197 ns | 172 ns / 322 ns / 422 ns / 213 ns | 121 ns / 204 ns / 388 ns / 247 ns | 201 ns / 341 ns / 510 ns / 263 ns | 189 ns / 354 ns / 643 ns / 224 ns | 318 ns / 508 ns / 677 ns / 278 ns | 2.85 µs / 6.79 µs / 4.74 µs / 663 ns |
| AES-256-GCM open, 16 KB | 899 ns / 1.60 µs / 3.62 µs / 1.81 µs | 1.55 µs / 2.92 µs / 3.69 µs / 1.82 µs | 933 ns / 1.62 µs / 3.70 µs / 2.23 µs | 1.71 µs / 3.04 µs / 4.54 µs / 2.19 µs | 1.58 µs / 2.98 µs / 3.87 µs / 1.93 µs | 2.92 µs / 4.93 µs / 7.23 µs / 2.77 µs | 32.6 µs / 79.2 µs / 54.0 µs / 7.06 µs |
| ChaCha20-Poly1305 seal, 32 B | 98.5 ns / 114 ns / 129 ns / 145 ns | 103 ns / 119 ns / 149 ns / 157 ns | 98.9 ns / 113 ns / 130 ns / 145 ns | 177 ns / 216 ns / 232 ns / 193 ns | 175 ns / 257 ns / 337 ns / 216 ns | 1.05 µs / 1.18 µs / 1.21 µs / 340 ns | 157 ns / 220 ns / 301 ns / 170 ns |
| ChaCha20-Poly1305 seal, 1350 B | 440 ns / 533 ns / 718 ns / 741 ns | 446 ns / 532 ns / 755 ns / 759 ns | 448 ns / 527 ns / 714 ns / 741 ns | 890 ns / 1.08 µs / 1.29 µs / 1.21 µs | 587 ns / 745 ns / 971 ns / 826 ns | 1.64 µs / 1.93 µs / 2.18 µs / 1.75 µs | 1.77 µs / 2.34 µs / 3.16 µs / 1.94 µs |
| ChaCha20-Poly1305 seal, 16 KB | 4.19 µs / 4.68 µs / 6.81 µs / 7.50 µs | 4.13 µs / 4.78 µs / 6.63 µs / 7.50 µs | 4.12 µs / 4.68 µs / 6.60 µs / 7.57 µs | 8.29 µs / 10.1 µs / 12.5 µs / 12.6 µs | 4.35 µs / 5.14 µs / 6.64 µs / 7.37 µs | 8.03 µs / 10.1 µs / 12.6 µs / 15.0 µs | 19.3 µs / 26.7 µs / 35.5 µs / 22.1 µs |
| XChaCha20-Poly1305 seal, 32 B | 186 ns / 243 ns / 256 ns / 897 ns | 199 ns / 267 ns / 278 ns / 871 ns | - / - / - / - | 255 ns / 357 ns / 335 ns / 905 ns | - / - / - / - | 1.15 µs / 1.36 µs / 1.27 µs / 1.06 µs | 244 ns / 351 ns / 400 ns / 900 ns |
| XChaCha20-Poly1305 seal, 1350 B | 529 ns / 775 ns / 804 ns / 1.47 µs | 539 ns / 723 ns / 831 ns / 1.45 µs | - / - / - / - | 974 ns / 1.31 µs / 1.32 µs / 1.94 µs | - / - / - / - | 1.73 µs / 2.18 µs / 2.20 µs / 2.44 µs | 1.83 µs / 2.70 µs / 3.16 µs / 2.71 µs |
| XChaCha20-Poly1305 seal, 16 KB | 4.17 µs / 5.23 µs / 6.38 µs / 8.23 µs | 4.23 µs / 5.37 µs / 6.44 µs / 8.31 µs | - / - / - / - | 8.40 µs / 10.8 µs / 11.9 µs / 13.2 µs | - / - / - / - | 8.15 µs / 11.4 µs / 12.0 µs / 15.6 µs | 19.6 µs / 28.7 µs / 33.4 µs / 22.4 µs |
| AES-128 header protection | 1.71 ns / 3.99 ns / 4.73 ns / 2.81 ns | 3.25 ns / 5.79 ns / 9.22 ns / 2.82 ns | 3.46 ns / 6.94 ns / 8.19 ns / 3.17 ns | - / - / - / - | - / - / - / - | - / - / - / - | - / - / - / - |
| AES-256 header protection | 2.08 ns / 5.85 ns / 8.09 ns / 2.91 ns | 5.32 ns / 7.71 ns / 10.2 ns / 3.00 ns | 5.13 ns / 9.58 ns / 10.0 ns / 3.89 ns | - / - / - / - | - / - / - / - | - / - / - / - | - / - / - / - |
| ChaCha20 header protection | 53.1 ns / 60.9 ns / 84.1 ns / 57.8 ns | 68.5 ns / 81.6 ns / 104 ns / 72.5 ns | 60.4 ns / 74.1 ns / 98.1 ns / 67.4 ns | - / - / - / - | - / - / - / - | - / - / - / - | - / - / - / - |
| X25519 | 18.2 µs / 24.0 µs / 25.0 µs / 16.5 µs | 19.2 µs / 24.6 µs / 30.9 µs / 18.1 µs | 35.6 µs / 46.7 µs / 53.6 µs / 24.9 µs | 18.1 µs / 23.9 µs / 25.0 µs / 16.6 µs | 34.0 µs / 49.7 µs / 55.2 µs / 36.2 µs | 36.1 µs / 52.1 µs / 56.6 µs / 29.7 µs | 42.7 µs / 64.6 µs / 72.3 µs / 37.3 µs |
| P-256 ECDH | 29.5 µs / 42.8 µs / 42.8 µs / 26.4 µs | 31.8 µs / 42.6 µs / 51.7 µs / 28.8 µs | 33.6 µs / 46.0 µs / 47.1 µs / 32.2 µs | 33.7 µs / 47.9 µs / 46.4 µs / 31.2 µs | 52.9 µs / 71.3 µs / 81.7 µs / 50.4 µs | 121 µs / 269 µs / 234 µs / 178 µs | 172 µs / 269 µs / 281 µs / 183 µs |
| Ed25519 sign | 4.11 µs / 5.72 µs / 7.13 µs / 3.93 µs | 4.49 µs / 6.13 µs / 7.81 µs / 4.07 µs | 13.1 µs / 17.2 µs / 22.3 µs / 7.03 µs | 4.40 µs / 5.83 µs / 7.22 µs / 4.21 µs | 13.7 µs / 23.4 µs / 31.0 µs / 16.6 µs | 9.60 µs / 13.3 µs / 17.4 µs / 8.65 µs | 28.5 µs / 41.4 µs / 53.7 µs / 22.0 µs |
| Ed25519 verify | 21.4 µs / 27.1 µs / 31.8 µs / 18.8 µs | 21.4 µs / 27.0 µs / 31.6 µs / 18.5 µs | 25.9 µs / 34.8 µs / 42.5 µs / 20.2 µs | 21.5 µs / 27.1 µs / 32.2 µs / 18.9 µs | 44.8 µs / 79.3 µs / 93.1 µs / 41.3 µs | 21.9 µs / 24.8 µs / 29.9 µs / 18.6 µs | 41.8 µs / 54.5 µs / 69.1 µs / 29.4 µs |
| P-256 ECDSA sign | 6.74 µs / 9.62 µs / 13.1 µs / 7.13 µs | 12.2 µs / 12.7 µs / 19.8 µs / 11.2 µs | 12.2 µs / 13.7 µs / 14.9 µs / 10.9 µs | 8.97 µs / 11.5 µs / 19.9 µs / 8.58 µs | 14.5 µs / 18.0 µs / 20.4 µs / 12.4 µs | 31.3 µs / 70.6 µs / 69.1 µs / 44.0 µs | 61.8 µs / 95.5 µs / 102 µs / 66.9 µs |
| P-256 ECDSA verify | 30.6 µs / 39.2 µs / 41.6 µs / 26.3 µs | 29.8 µs / 40.2 µs / 43.3 µs / 27.4 µs | 33.2 µs / 41.2 µs / 43.9 µs / 29.2 µs | 32.4 µs / 41.9 µs / 44.3 µs / 28.2 µs | 46.5 µs / 60.3 µs / 68.3 µs / 41.9 µs | 103 µs / 226 µs / 197 µs / 152 µs | 147 µs / 216 µs / 237 µs / 156 µs |
| SHA-256, 64 B | 28.2 ns / 68.7 ns / 244 ns / 36.6 ns | 57.0 ns / 95.8 ns / 307 ns / 70.3 ns | 56.3 ns / 78.5 ns / 277 ns / 44.0 ns | 40.1 ns / 67.3 ns / 576 ns / 35.3 ns | 158 ns / 244 ns / 505 ns / 159 ns | 39.1 ns / 58.7 ns / 473 ns / 36.2 ns | 218 ns / 385 ns / 485 ns / 225 ns |
| SHA-256, 1350 B | 269 ns / 620 ns / 2.46 µs / 409 ns | 306 ns / 740 ns / 2.79 µs / 434 ns | 304 ns / 637 ns / 2.82 µs / 410 ns | 292 ns / 637 ns / 6.07 µs / 480 ns | 406 ns / 815 ns / 2.68 µs / 533 ns | 296 ns / 618 ns / 5.11 µs / 473 ns | 2.29 µs / 3.71 µs / 5.23 µs / 2.46 µs |
| SHA-256, 16 KB | 3.19 µs / 6.86 µs / 28.1 µs / 5.01 µs | 3.23 µs / 6.88 µs / 31.5 µs / 4.86 µs | 3.22 µs / 6.97 µs / 32.4 µs / 4.84 µs | 3.22 µs / 7.08 µs / 69.8 µs / 5.79 µs | 3.36 µs / 7.37 µs / 28.8 µs / 4.98 µs | 3.22 µs / 7.22 µs / 58.2 µs / 5.66 µs | 26.9 µs / 43.1 µs / 61.7 µs / 29.0 µs |
| SHA-512, 64 B | 60.1 ns / 138 ns / 198 ns / 75.1 ns | 134 ns / 224 ns / 228 ns / 105 ns | 140 ns / 198 ns / 203 ns / 84.3 ns | 148 ns / 206 ns / 236 ns / 175 ns | 226 ns / 355 ns / 430 ns / 205 ns | 120 ns / 192 ns / 270 ns / 76.9 ns | 166 ns / 246 ns / 289 ns / 142 ns |
| SHA-512, 1350 B | 249 ns / 1.77 µs / 1.65 µs / 730 ns | 1.24 µs / 1.90 µs / 1.97 µs / 768 ns | 1.26 µs / 1.87 µs / 1.93 µs / 743 ns | 1.49 µs / 1.98 µs / 2.35 µs / 1.87 µs | 1.17 µs / 1.76 µs / 1.90 µs / 893 ns | 1.17 µs / 1.92 µs / 2.67 µs / 723 ns | 1.72 µs / 2.94 µs / 3.08 µs / 1.56 µs |
| SHA-512, 16 KB | 2.63 µs / 16.3 µs / 18.5 µs / 8.72 µs | 14.1 µs / 25.2 µs / 21.9 µs / 8.91 µs | 14.1 µs / 25.4 µs / 21.8 µs / 8.70 µs | 17.5 µs / 21.0 µs / 27.2 µs / 21.7 µs | 12.3 µs / 15.1 µs / 19.0 µs / 9.33 µs | 13.5 µs / 24.1 µs / 30.7 µs / 8.59 µs | 20.1 µs / 29.8 µs / 35.4 µs / 18.4 µs |
| HMAC-SHA256, 64 B | 75.1 ns / 147 ns / 628 ns / 110 ns | 180 ns / 264 ns / 811 ns / 245 ns | 145 ns / 214 ns / 707 ns / 134 ns | 110 ns / 172 ns / 1.44 µs / 112 ns | 1.55 µs / 2.57 µs / 3.69 µs / 2.09 µs | 103 ns / 155 ns / 1.19 µs / 99.4 ns | 537 ns / 851 ns / 1.20 µs / 564 ns |
| HMAC-SHA256, 1350 B | 328 ns / 692 ns / 2.79 µs / 487 ns | 435 ns / 853 ns / 3.30 µs / 597 ns | 397 ns / 791 ns / 3.19 µs / 502 ns | 358 ns / 731 ns / 7.05 µs / 562 ns | 1.81 µs / 3.11 µs / 5.94 µs / 2.41 µs | 356 ns / 728 ns / 5.80 µs / 545 ns | 2.68 µs / 4.39 µs / 6.02 µs / 2.80 µs |
| HKDF-SHA256 extract and expand | 158 ns / 257 ns / 1.15 µs / 226 ns | 285 ns / 369 ns / 1.44 µs / 330 ns | 303 ns / 399 ns / 1.31 µs / 273 ns | - / - / - / - | 1.72 µs / 2.72 µs / 4.33 µs / 2.16 µs | 223 ns / 309 ns / 1.98 µs / 212 ns | 965 ns / 1.37 µs / 2.04 µs / 936 ns |
| HKDF-SHA256 expand | 66.1 ns / 88.6 ns / 300 ns / 74.1 ns | 124 ns / 179 ns / 636 ns / 154 ns | 120 ns / 142 ns / 337 ns / 92.6 ns | - / - / - / - | 1.23 µs / 2.00 µs / 2.87 µs / 1.55 µs | 83.8 ns / 103 ns / 512 ns / 68.8 ns | 480 ns / 714 ns / 1.01 µs / 481 ns |
| RSA-2048 PKCS#1 sign | 341 µs / 523 µs / 447 µs / 275 µs | 344 µs / 532 µs / 449 µs / 284 µs | 336 µs / 523 µs / 434 µs / 323 µs | 344 µs / 474 µs / 446 µs / 551 µs | 393 µs / 520 µs / 454 µs / 340 µs | 580 µs / 1.26 ms / 1.14 ms / 588 µs | - / - / - / - |
| RSA-2048 PKCS#1 verify | 8.92 µs / 12.3 µs / 12.9 µs / 8.41 µs | 11.0 µs / 14.6 µs / 15.9 µs / 9.54 µs | 12.8 µs / 16.6 µs / 16.5 µs / 11.0 µs | 14.0 µs / 19.2 µs / 19.1 µs / 18.4 µs | 15.5 µs / 23.6 µs / 24.5 µs / 16.8 µs | 29.0 µs / 54.1 µs / 57.8 µs / 31.0 µs | - / - / - / - |
| RSA-2048 PSS sign | 341 µs / 424 µs / 446 µs / 276 µs | 347 µs / 481 µs / 462 µs / 285 µs | 342 µs / 472 µs / 437 µs / 323 µs | 345 µs / 449 µs / 450 µs / 551 µs | 390 µs / 475 µs / 457 µs / 342 µs | 581 µs / 1.06 ms / 1.15 ms / 590 µs | 3.69 ms / 5.86 ms / 7.22 ms / 4.47 ms |
| RSA-2048 PSS verify | 9.15 µs / 12.7 µs / 14.4 µs / 8.74 µs | 11.2 µs / 15.9 µs / 17.0 µs / 9.89 µs | 13.2 µs / 18.0 µs / 18.2 µs / 11.4 µs | 14.3 µs / 19.6 µs / 21.8 µs / 18.8 µs | 16.2 µs / 23.6 µs / 27.2 µs / 17.5 µs | 35.3 µs / 64.7 µs / 70.0 µs / 38.9 µs | 252 µs / 312 µs / 378 µs / 298 µs |
| ML-KEM-768 key generation | 6.35 µs / 8.43 µs / 10.2 µs / 6.11 µs | 8.13 µs / 9.41 µs / 18.4 µs / 7.31 µs | - / - / - / - | 7.37 µs / 9.28 µs / 10.4 µs / 6.11 µs | 19.8 µs / 26.8 µs / 40.6 µs / 24.9 µs | 39.6 µs / 60.2 µs / 60.0 µs / 26.3 µs | 15.0 µs / 18.1 µs / 20.1 µs / 11.8 µs |
| ML-KEM-768 encapsulate | 6.50 µs / 8.53 µs / 10.6 µs / 6.77 µs | 8.48 µs / 10.4 µs / 18.7 µs / 7.91 µs | - / - / - / - | 9.00 µs / 11.0 µs / 11.1 µs / 6.32 µs | 18.8 µs / 30.0 µs / 41.2 µs / 23.9 µs | 41.4 µs / 72.0 µs / 67.0 µs / 27.9 µs | 12.8 µs / 16.0 µs / 18.1 µs / 9.25 µs |
| ML-KEM-768 decapsulate | 4.64 µs / 6.07 µs / 7.07 µs / 4.12 µs | 8.90 µs / 12.0 µs / 16.0 µs / 7.67 µs | - / - / - / - | 6.54 µs / 7.72 µs / 7.42 µs / 4.02 µs | 15.8 µs / 24.6 µs / 31.7 µs / 24.7 µs | 54.9 µs / 95.2 µs / 84.4 µs / 33.9 µs | 13.4 µs / 16.2 µs / 19.0 µs / 9.48 µs |
| P-384 ECDH | 100 µs / 180 µs / 151 µs / 90.1 µs | 109 µs / 180 µs / 175 µs / 105 µs | 370 µs / 601 µs / 646 µs / 414 µs | 174 µs / 303 µs / 245 µs / 151 µs | 265 µs / 657 µs / 484 µs / 281 µs | 402 µs / 1.04 ms / 770 µs / 488 µs | - / - / - / - |
| P-384 ECDSA sign | 23.5 µs / 53.1 µs / 39.6 µs / 22.3 µs | 46.5 µs / 97.5 µs / 82.7 µs / 49.2 µs | 197 µs / 475 µs / 343 µs / 222 µs | 90.3 µs / 185 µs / 129 µs / 80.2 µs | 89.0 µs / 236 µs / 165 µs / 103 µs | 85.5 µs / 238 µs / 171 µs / 112 µs | - / - / - / - |
| P-384 ECDSA verify | 100 µs / 157 µs / 141 µs / 84.0 µs | 116 µs / 199 µs / 176 µs / 111 µs | 357 µs / 727 µs / 623 µs / 397 µs | 166 µs / 316 µs / 239 µs / 149 µs | 216 µs / 552 µs / 400 µs / 238 µs | 360 µs / 872 µs / 695 µs / 422 µs | - / - / - / - |
| P-521 ECDH | 160 µs / 284 µs / 234 µs / 130 µs | 172 µs / 288 µs / 262 µs / 142 µs | - / - / - / - | - / - / - / - | 277 µs / 506 µs / 497 µs / 247 µs | 428 µs / 868 µs / 841 µs / 396 µs | - / - / - / - |
| P-521 ECDSA sign | 40.1 µs / 58.3 µs / 64.3 µs / 36.3 µs | 88.3 µs / 152 µs / 159 µs / 84.1 µs | - / - / - / - | - / - / - / - | 128 µs / 220 µs / 221 µs / 132 µs | 94.1 µs / 177 µs / 186 µs / 80.1 µs | - / - / - / - |
| P-521 ECDSA verify | 145 µs / 222 µs / 214 µs / 121 µs | 187 µs / 327 µs / 303 µs / 167 µs | - / - / - / - | - / - / - / - | 261 µs / 474 µs / 460 µs / 254 µs | 353 µs / 610 µs / 636 µs / 252 µs | - / - / - / - |
Criterion's estimates from rind's benchmark report at 00610aff, run 2026-11-06 and 2026-11-07, as Arrow Lake / Zen 3 / Coffee Lake / Apple M4. Every library runs its native path on each CPU. On Mac, rind holds DIT across the timed loop, as a caller batching calls does.
AI is really fucking good at benchmarks. It’s terrifying.
Moments like this fill me with dread as a professional software engineer. A decade ago, I wasted weeks speeding up Go’s crypto. I could have just waited a decade for Opus 5.5 instead. idiot
Lean into Lean
I saw an Anthropic blog post about formal verification. Like most programmers, I don’t even test my code, let alone formally verify it. But I figured I’d play along and waste a few more tokens chasing the dragon.
Because like, if you can prove the vibe slop is correct, it’s no longer vibe slop right?
Almost nobody proves
their crypto
Squares: Arrow Lake, Zen 3, Coffee Lake, Apple M4. Blank: not offered.
Data
| Primitive | rind | aws-lc-rs | ring | RustCrypto | graviola | libcrux | OpenSSL |
|---|---|---|---|---|---|---|---|
| AES-GCM | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven end to end, Apple M4: unproven · x86_64: Vale's F* AES-GCM, for every message without VAES but only short ones with it | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: unproven · x86_64: aws-lc-verification's SAW AES-GCM, for AES-256 only | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
| ChaCha20-Poly1305 | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
| XChaCha20-Poly1305 | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: HACL*'s F* ChaCha20-Poly1305, under an unverified HChaCha20 · aarch64: HACL*'s F* ChaCha20-Poly1305, under an unverified HChaCha20 | ||
| AES header protection | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: unproven · x86_64: aws-lc-verification's SAW AES block, for AES-256 only | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | ||||
| ChaCha20 header protection | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | ||||
| SHA-256 | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
| SHA-512 | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven end to end, Zen 3: unproven, Coffee Lake: proven end to end, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
| HMAC-SHA256 | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
| HKDF-SHA256 | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | |
| X25519 | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light X25519 ladder · aarch64: s2n-bignum's HOL Light X25519 ladder | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light X25519 ladder · aarch64: s2n-bignum's HOL Light X25519 ladder | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
| Ed25519 | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light scalar multiplications · aarch64: s2n-bignum's HOL Light scalar multiplications | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light scalar multiplications · aarch64: s2n-bignum's HOL Light scalar multiplications | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light scalar multiplications · aarch64: s2n-bignum's HOL Light scalar multiplications | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
| P-256 | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven end to end · x86_64: s2n-bignum's HOL Light scalar multiplication, for ECDH | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light scalar multiplication, for ECDH · aarch64: s2n-bignum's HOL Light scalar multiplication, for ECDH | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
| P-384 | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light scalar multiplication, for ECDH · aarch64: s2n-bignum's HOL Light scalar multiplication, for ECDH | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light scalar multiplication, for ECDH · aarch64: s2n-bignum's HOL Light scalar multiplication, for ECDH | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | |
| P-521 | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light scalar multiplication, for ECDH · aarch64: s2n-bignum's HOL Light scalar multiplication, for ECDH | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light scalar multiplication, for ECDH · aarch64: s2n-bignum's HOL Light scalar multiplication, for ECDH | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | |||
| RSA-2048 PSS | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven end to end, Zen 3: proven end to end, Coffee Lake: proven end to end, Apple M4: proven end to end | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
| RSA-2048 PKCS#1 | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | |
| ML-KEM-768 | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's and mlkem-native's HOL Light NTT and sampling · aarch64: s2n-bignum's HOL Light NTT and sampling | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: mlkem-native's HOL Light AVX2 NTT and sampling · aarch64: mlkem-native's HOL Light aarch64 NTT and sampling | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: proven core, Zen 3: proven core, Coffee Lake: proven core, Apple M4: proven core · x86_64: s2n-bignum's HOL Light NTT and base multiplication · aarch64: s2n-bignum's HOL Light NTT and base multiplication | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven | Arrow Lake: unproven, Zen 3: unproven, Coffee Lake: unproven, Apple M4: unproven |
End to end: rind's benchmarks/verified.toml at 00610aff, whose ✓ needs a machine-checked proof of all the code, glue included. Proven core: a proven routine does the whole cipher, block function, scalar multiplication, or NTT for at least some of the row's operations; field arithmetic alone doesn't count. Checked against each library's source for each CPU's architecture, at the versions rind's report locks, run 2026-11-06 and 2026-11-07.
Green boxes are formally verified, dashed are mostly formally verified.
libcrux is cool because they try to formally verify everything, but if you saw the last grid, it’s super slow.
rind will only use a formal proof if the performance was within 10%, which has surprisingly decent coverage.
aes-gcm being verified on some architectures is awesome, because it’s the goat of crypto.
I’m sure we could get every algorithm formally proven. If only there was somebody in the audience works for a frontier lab and wants some easy marketting piece instead of stealing all of the glory from the mathematicians.
Please Donate Tokens
If it sounds like I’m desparate and begging for more tokens, ur right.
So I came up with a plan. I decided the best way to get tokens is to rage bait.
Bun is SUPER SLOW.
Holy shit it’s like REALLY SLOW.
Imagine doing common stuff like TLS certificate generation in Bun.
Super SLOW.
faster than
stock Bun
Shorter is faster. Same Bun commit, with rind swapped in.
Data
| Operation | Bun + rind | Node | Bun | Deno |
|---|---|---|---|---|
| WebCrypto Ed25519 generateKey | 6.18 µs | 20.8 µs | 9.71 µs | 15.6 µs |
| WebCrypto Ed25519 importKey pkcs8 | 5.55 µs | 18.6 µs | 16.1 µs | 6.20 µs |
| WebCrypto Ed25519 sign | 7.73 µs | 19.5 µs | 12.1 µs | 13.6 µs |
| WebCrypto Ed25519 verify | 22.9 µs | 50.0 µs | 32.0 µs | 26.4 µs |
| WebCrypto Ed25519 exportKey jwk | 0.510 µs | 1.48 µs | 8.39 µs | 8.30 µs |
| WebCrypto X25519 generateKey | 4.58 µs | 20.1 µs | 9.50 µs | 46.6 µs |
| WebCrypto X25519 deriveBits | 18.1 µs | 28.5 µs | 22.5 µs | 40.9 µs |
| WebCrypto P-256 generateKey | 6.15 µs | 19.3 µs | 6.10 µs | 22.8 µs |
| WebCrypto P-256 importKey pkcs8 | 9.86 µs | 31.2 µs | 9.84 µs | 6.75 µs |
| WebCrypto P-256 importKey spki | 1.05 µs | 21.1 µs | 1.04 µs | 7.11 µs |
| WebCrypto P-256 sign | 17.7 µs | 31.5 µs | 17.8 µs | 356 µs |
| WebCrypto P-256 verify | 33.2 µs | 55.2 µs | 33.2 µs | 453 µs |
| WebCrypto P-256 exportKey pkcs8 | 3.45 µs | 12.0 µs | 3.41 µs | 3.54 µs |
| WebCrypto P-256 ECDH deriveBits | 31.5 µs | 38.6 µs | 31.6 µs | 444 µs |
| WebCrypto P-384 generateKey | 26.4 µs | 76.4 µs | 203 µs | 47.9 µs |
| WebCrypto P-384 importKey pkcs8 | 21.9 µs | 121 µs | 405 µs | 7.37 µs |
| WebCrypto P-384 importKey spki | 1.56 µs | 52.8 µs | 1.21 µs | 7.39 µs |
| WebCrypto P-384 sign | 27.4 µs | 127 µs | 241 µs | 955 µs |
| WebCrypto P-384 verify | 97.0 µs | 237 µs | 246 µs | 1.23 ms |
| WebCrypto P-384 exportKey pkcs8 | 0.480 µs | 21.1 µs | 16.8 µs | 3.36 µs |
| WebCrypto P-384 ECDH deriveBits | 84.7 µs | 164 µs | 221 µs | 1.23 ms |
| WebCrypto P-521 generateKey | 41.5 µs | 81.5 µs | 491 µs | 480 µs |
| WebCrypto P-521 importKey pkcs8 | 36.6 µs | 125 µs | 982 µs | 7.19 µs |
| WebCrypto P-521 importKey spki | 1.56 µs | 53.0 µs | 1.63 µs | 7.63 µs |
| WebCrypto P-521 sign | 44.4 µs | 165 µs | 585 µs | 1.38 ms |
| WebCrypto P-521 verify | 142 µs | 283 µs | 607 µs | 1.72 ms |
| WebCrypto P-521 exportKey pkcs8 | 0.480 µs | 20.0 µs | 44.2 µs | 3.84 µs |
| WebCrypto P-521 ECDH deriveBits | 127 µs | 172 µs | 537 µs | 1.63 ms |
| node:crypto Ed25519 generateKeyPairSync | 5.12 µs | 15.9 µs | 9.39 µs | 12.2 µs |
| node:crypto Ed25519 sign | 4.54 µs | 14.9 µs | 8.63 µs | 9.57 µs |
| node:crypto Ed25519 verify | 19.6 µs | 45.8 µs | 28.4 µs | 22.0 µs |
| node:crypto Ed25519 sign, PEM key each call | 9.21 µs | 36.0 µs | 17.1 µs | 22.6 µs |
| node:crypto X25519 generateKeyPairSync | 5.02 µs | 14.8 µs | 9.24 µs | 11.8 µs |
| node:crypto X25519 diffieHellman | 14.3 µs | 20.9 µs | 19.1 µs | 36.0 µs |
| node:crypto P-256 generateKeyPairSync | 5.83 µs | 13.2 µs | 5.80 µs | 141 µs |
| node:crypto P-256 sign | 14.0 µs | 15.4 µs | 14.0 µs | 305 µs |
| node:crypto P-256 verify | 29.5 µs | 40.0 µs | 29.6 µs | 294 µs |
| node:crypto P-256 createPrivateKey pem | 6.07 µs | 12.1 µs | 5.53 µs | 144 µs |
| node:crypto P-256 export pkcs8 pem | 3.82 µs | 11.0 µs | 3.82 µs | 143 µs |
| node:crypto P-256 diffieHellman | 27.8 µs | 32.6 µs | 27.7 µs | 141 µs |
| node:crypto P-256 ECDH computeSecret | 31.9 µs | 61.0 µs | 31.8 µs | 282 µs |
| node:crypto P-384 generateKeyPairSync | 21.4 µs | 67.8 µs | 203 µs | 355 µs |
| node:crypto P-384 sign | 23.7 µs | 91.9 µs | 235 µs | 742 µs |
| node:crypto P-384 verify | 93.3 µs | 201 µs | 238 µs | 711 µs |
| node:crypto P-384 createPrivateKey pem | 22.0 µs | 14.9 µs | 204 µs | 358 µs |
| node:crypto P-384 export pkcs8 pem | 0.780 µs | 20.2 µs | 17.2 µs | 357 µs |
| node:crypto P-384 diffieHellman | 81.1 µs | 158 µs | 217 µs | 355 µs |
| node:crypto P-384 ECDH computeSecret | 81.0 µs | 284 µs | 418 µs | 708 µs |
| node:crypto P-521 generateKeyPairSync | 36.6 µs | 74.2 µs | 491 µs | 443 µs |
| node:crypto P-521 sign | 40.2 µs | 131 µs | 577 µs | 972 µs |
| node:crypto P-521 verify | 138 µs | 249 µs | 591 µs | 933 µs |
| node:crypto P-521 createPrivateKey pem | 37.6 µs | 15.3 µs | 493 µs | 427 µs |
| node:crypto P-521 export pkcs8 pem | 0.900 µs | 19.3 µs | 44.9 µs | 425 µs |
| node:crypto P-521 diffieHellman | 125 µs | 164 µs | 534 µs | 422 µs |
| node:crypto P-521 ECDH computeSecret | 125 µs | 296 µs | 1.02 ms | - |
Core Ultra 7 270K Plus, one pinned core, the median of 5 alternating rounds, 2026-11-07. Bun + rind: bun 1.4.4 (fb621aaac); Node: node v24.21.0; Bun: bun 1.4.4 (b1007f117); Deno: deno 2.9.8. Bun and Bun + rind are release builds of the same Bun commit, without and with the rind prototype, which moves Ed25519, X25519, P-384, and P-521 onto rind; P-256 still runs BoringSSL in both.
I threw Deprecated Deno in just because it uses RustCrypto. Oh man, I used to hate RustCrypto back when I was a programmer.
calls where Bun + rind is fastest,
or within 10%
Blank: the runtime refuses it. P-256 runs BoringSSL in both Buns.
Data
| Operation | Bun + rind | Node | Bun | Deno |
|---|---|---|---|---|
| WebCrypto Ed25519 generateKey | 6.18 µs | 20.8 µs | 9.71 µs | 15.6 µs |
| WebCrypto Ed25519 importKey pkcs8 | 5.55 µs | 18.6 µs | 16.1 µs | 6.20 µs |
| WebCrypto Ed25519 sign | 7.73 µs | 19.5 µs | 12.1 µs | 13.6 µs |
| WebCrypto Ed25519 verify | 22.9 µs | 50.0 µs | 32.0 µs | 26.4 µs |
| WebCrypto Ed25519 exportKey jwk | 0.510 µs | 1.48 µs | 8.39 µs | 8.30 µs |
| WebCrypto X25519 generateKey | 4.58 µs | 20.1 µs | 9.50 µs | 46.6 µs |
| WebCrypto X25519 deriveBits | 18.1 µs | 28.5 µs | 22.5 µs | 40.9 µs |
| WebCrypto P-256 generateKey | 6.15 µs | 19.3 µs | 6.10 µs | 22.8 µs |
| WebCrypto P-256 importKey pkcs8 | 9.86 µs | 31.2 µs | 9.84 µs | 6.75 µs |
| WebCrypto P-256 importKey spki | 1.05 µs | 21.1 µs | 1.04 µs | 7.11 µs |
| WebCrypto P-256 sign | 17.7 µs | 31.5 µs | 17.8 µs | 356 µs |
| WebCrypto P-256 verify | 33.2 µs | 55.2 µs | 33.2 µs | 453 µs |
| WebCrypto P-256 exportKey pkcs8 | 3.45 µs | 12.0 µs | 3.41 µs | 3.54 µs |
| WebCrypto P-256 ECDH deriveBits | 31.5 µs | 38.6 µs | 31.6 µs | 444 µs |
| WebCrypto P-384 generateKey | 26.4 µs | 76.4 µs | 203 µs | 47.9 µs |
| WebCrypto P-384 importKey pkcs8 | 21.9 µs | 121 µs | 405 µs | 7.37 µs |
| WebCrypto P-384 importKey spki | 1.56 µs | 52.8 µs | 1.21 µs | 7.39 µs |
| WebCrypto P-384 sign | 27.4 µs | 127 µs | 241 µs | 955 µs |
| WebCrypto P-384 verify | 97.0 µs | 237 µs | 246 µs | 1.23 ms |
| WebCrypto P-384 exportKey pkcs8 | 0.480 µs | 21.1 µs | 16.8 µs | 3.36 µs |
| WebCrypto P-384 ECDH deriveBits | 84.7 µs | 164 µs | 221 µs | 1.23 ms |
| WebCrypto P-521 generateKey | 41.5 µs | 81.5 µs | 491 µs | 480 µs |
| WebCrypto P-521 importKey pkcs8 | 36.6 µs | 125 µs | 982 µs | 7.19 µs |
| WebCrypto P-521 importKey spki | 1.56 µs | 53.0 µs | 1.63 µs | 7.63 µs |
| WebCrypto P-521 sign | 44.4 µs | 165 µs | 585 µs | 1.38 ms |
| WebCrypto P-521 verify | 142 µs | 283 µs | 607 µs | 1.72 ms |
| WebCrypto P-521 exportKey pkcs8 | 0.480 µs | 20.0 µs | 44.2 µs | 3.84 µs |
| WebCrypto P-521 ECDH deriveBits | 127 µs | 172 µs | 537 µs | 1.63 ms |
| node:crypto Ed25519 generateKeyPairSync | 5.12 µs | 15.9 µs | 9.39 µs | 12.2 µs |
| node:crypto Ed25519 sign | 4.54 µs | 14.9 µs | 8.63 µs | 9.57 µs |
| node:crypto Ed25519 verify | 19.6 µs | 45.8 µs | 28.4 µs | 22.0 µs |
| node:crypto Ed25519 sign, PEM key each call | 9.21 µs | 36.0 µs | 17.1 µs | 22.6 µs |
| node:crypto X25519 generateKeyPairSync | 5.02 µs | 14.8 µs | 9.24 µs | 11.8 µs |
| node:crypto X25519 diffieHellman | 14.3 µs | 20.9 µs | 19.1 µs | 36.0 µs |
| node:crypto P-256 generateKeyPairSync | 5.83 µs | 13.2 µs | 5.80 µs | 141 µs |
| node:crypto P-256 sign | 14.0 µs | 15.4 µs | 14.0 µs | 305 µs |
| node:crypto P-256 verify | 29.5 µs | 40.0 µs | 29.6 µs | 294 µs |
| node:crypto P-256 createPrivateKey pem | 6.07 µs | 12.1 µs | 5.53 µs | 144 µs |
| node:crypto P-256 export pkcs8 pem | 3.82 µs | 11.0 µs | 3.82 µs | 143 µs |
| node:crypto P-256 diffieHellman | 27.8 µs | 32.6 µs | 27.7 µs | 141 µs |
| node:crypto P-256 ECDH computeSecret | 31.9 µs | 61.0 µs | 31.8 µs | 282 µs |
| node:crypto P-384 generateKeyPairSync | 21.4 µs | 67.8 µs | 203 µs | 355 µs |
| node:crypto P-384 sign | 23.7 µs | 91.9 µs | 235 µs | 742 µs |
| node:crypto P-384 verify | 93.3 µs | 201 µs | 238 µs | 711 µs |
| node:crypto P-384 createPrivateKey pem | 22.0 µs | 14.9 µs | 204 µs | 358 µs |
| node:crypto P-384 export pkcs8 pem | 0.780 µs | 20.2 µs | 17.2 µs | 357 µs |
| node:crypto P-384 diffieHellman | 81.1 µs | 158 µs | 217 µs | 355 µs |
| node:crypto P-384 ECDH computeSecret | 81.0 µs | 284 µs | 418 µs | 708 µs |
| node:crypto P-521 generateKeyPairSync | 36.6 µs | 74.2 µs | 491 µs | 443 µs |
| node:crypto P-521 sign | 40.2 µs | 131 µs | 577 µs | 972 µs |
| node:crypto P-521 verify | 138 µs | 249 µs | 591 µs | 933 µs |
| node:crypto P-521 createPrivateKey pem | 37.6 µs | 15.3 µs | 493 µs | 427 µs |
| node:crypto P-521 export pkcs8 pem | 0.900 µs | 19.3 µs | 44.9 µs | 425 µs |
| node:crypto P-521 diffieHellman | 125 µs | 164 µs | 534 µs | 422 µs |
| node:crypto P-521 ECDH computeSecret | 125 µs | 296 µs | 1.02 ms | - |
Core Ultra 7 270K Plus, one pinned core, the median of 5 alternating rounds, 2026-11-07. Bun + rind: bun 1.4.4 (fb621aaac); Node: node v24.21.0; Bun: bun 1.4.4 (b1007f117); Deno: deno 2.9.8. Bun and Bun + rind are release builds of the same Bun commit, without and with the rind prototype, which moves Ed25519, X25519, P-384, and P-521 onto rind; P-256 still runs BoringSSL in both.
Did the rage bait work?
Login to your work account, choose the secret model nobody else has access to, then /quest-spawn to work on the backlog I planned.
k thx
try it?
Up to you, it depends on your tolerance for vibe slop. This is cryptography so it’s dangerous to get wrong.
But as Sun Tzu once said: yolo
# safer/faster API
rind = "0.3"
# if you use ring
ring = { package = "rind-ring", version = "0.17" }
# if you use rustls
rustls = { version = "0.23", default-features = false, features = ["std", "tls12"] }
rind-rustls = "0.23"
# if you use aws-lc-rs
aws-lc-rs = { package = "rind-aws-lc-rs", version = "1" }
There’s other adapters too if you’re lazy.
Or just tell Mr AI to migrate to the rind API.
The Bill
All of this crypto jizz, it must have been expensive, right?
This took my 9-year old desktop PC… 12 days. And much of that was waiting for a usage reset.
The Opus total was about $4.2k at API pricing. But API pricing is a meme.
12 DAYS · TERMINAL /goal
Data
| Line | Amount |
|---|---|
| Claude Opus 5.5 | $4,158 |
| Tokens | 10.30B |
| Cache reads | 98% |
| Agent hrs | 559.4 |
| Human hrs | 28.3 |
| List price | $4,158.00 |
| Paid | $40.00 |
From the final tally in moq-dev/rind#499, through November 7, 2026, at 14:32 PT. Sessions working in a rind checkout, or whose task names moq-dev/rind, from the graviola bootstrap on 2026-10-27. Opus usage from Claude Code logs, each API message counted once, priced at its standard API rate. Paid: 12 days of a $100 a month plan. Human hours are the maintainer's prompts, with gaps over 15 minutes counted as away. Agent hours cover Claude Code sessions.
That’s right, I made the internet a better place* for $40. It’s ridiculous.
* Assuming anybody actually uses this library.
FIN
Real talk: Programming is dead. We had a good run.
Many of these crypto libraries have been hand optimized by some of the smartest people I know (cryptography nerds). And they get completely blown out of the water by a vibe coder with a credit card.
I know we still need humans to tell the AI to do dumb shit. Like to rewrite libraries in Rust.
But like, for how much longer?