@@ -16,23 +16,30 @@ cmake -S /tmp/boringssl -B /tmp/boringssl/build -DCMAKE_BUILD_TYPE=Release
1616cmake --build /tmp/boringssl/build -j
1717```
1818
19- ## Latest run (2026-06-07 , Apple M3 Pro)
19+ ## Latest run (2026-06-09 , Apple M3 Pro)
2020
2121** zig-tls:** zig ` 0.17.0-dev ` , ` -Doptimize=ReleaseFast -Dcpu=native `
2222** BoringSSL:** ` /tmp/boringssl/build ` Release (assembly enabled)
2323
2424| Benchmark | zig-tls | BoringSSL | Ratio (zig / BoringSSL) |
2525| -----------| ---------| -----------| -------------------------|
26- | Handshake TLS 1.3 (minimal ECDHE) | ** ~ 8280 /s** | — | — |
27- | Handshake TLS 1.3 (ECDHE + cert) | ** ~ 5980 /s** | ~ 6900 /s | ~ 0.87× |
28- | Handshake TLS 1.3 (ECDHE + cert + client verify) | ** ~ 5950 /s** | ~ 6730 /s | ~ 0.88× |
29- | Transfer send AES-128-GCM (16 KiB) | ** ~ 8370 MB/s** | ~ 8320 MB/s | ~ 1.01 × |
30- | Transfer recv AES-128-GCM (16 KiB) | ** ~ 7940 MB/s** | ~ 8080 MB/s | ~ 0.98× |
31- | Transfer send AES-256-GCM (16 KiB) | ** ~ 7690 MB/s** | ~ 7620 MB/s | ~ 1.01 × |
32- | Transfer recv AES-256-GCM (16 KiB) | ** ~ 7380 MB/s** | ~ 7470 MB/s | ~ 0.99 × |
26+ | Handshake TLS 1.3 (minimal ECDHE) | ** ~ 11 400 /s** | — | — |
27+ | Handshake TLS 1.3 (ECDHE + cert) | ** ~ 7440 /s** | ~ 6510 /s | ** ~ 1.14× ** |
28+ | Handshake TLS 1.3 (ECDHE + cert + client verify) | ** ~ 7240 /s** | ~ 6520 /s | ** ~ 1.11× ** |
29+ | Transfer send AES-128-GCM (16 KiB) | ** ~ 8190 MB/s** | ~ 8380 MB/s | ~ 0.98 × |
30+ | Transfer recv AES-128-GCM (16 KiB) | ** ~ 7730 MB/s** | ~ 7920 MB/s | ~ 0.98× |
31+ | Transfer send AES-256-GCM (16 KiB) | ** ~ 7210 MB/s** | ~ 7590 MB/s | ~ 0.95 × |
32+ | Transfer recv AES-256-GCM (16 KiB) | ** ~ 7170 MB/s** | ~ 7390 MB/s | ~ 0.97 × |
3333
3434Iterations: 10 000 handshakes; 5 000 × 16 384-byte application records per transfer test.
3535
36+ ** zig-tls now beats BoringSSL on both certificate handshake rows.** The decisive win is
37+ the X25519 fixed-base comb for keygen (` src/crypto/x25519_base.zig ` ): key generation went
38+ from ~ 36 k/s (Montgomery ladder) to ~ 90 k/s, and X25519 keygen is two of the four scalar
39+ multiplications in a TLS 1.3 handshake. Remaining BoringSSL leads (ECDSA sign,
40+ variable-base X25519 scalarmult, AES-GCM transfer) come from hand-tuned ARM assembly and
41+ no longer gate the handshake.
42+
3643### Crypto micro-benchmarks (same ` zig build bench ` run)
3744
3845After the handshake rows, the bench prints isolated P-256 verify throughput and an
@@ -41,24 +48,26 @@ estimated handshake breakdown:
4148| Row | Typical M3 Pro rate |
4249| -----| ---------------------|
4350| ` ECDSA P-256 verifyPrehashed ` | ** ~ 36 000 /s** |
44- | ` P-256 w7 double-base (x only) ` | ** ~ 47 000 /s** |
45- | ` X25519 ECDHE scalarmult ` | ** ~ 35 000 /s** |
51+ | ` P-256 w7 double-base (x only) ` | ** ~ 48 000 /s** |
52+ | ` X25519 keygen (comb, fixed base) ` | ** ~ 90 000 /s** (ladder ~ 36 700) |
53+ | ` X25519 ECDHE scalarmult ` | ** ~ 36 000 /s** |
4654| ` SHA-256 update 2 KiB ` | ** ~ 1.28 M /s** |
4755| ` AES-128-GCM TLS 1.3 ~2 KiB encrypt ` | ** ~ 3.8 M /s** |
48- | ` ECDSA P-256 signPrehashed ` | ** ~ 60 500 /s** |
56+ | ` ECDSA P-256 signPrehashed ` | ** ~ 57 500 /s** |
4957| ` HKDF-Expand-Label key+iv (SHA-256) ` | ** ~ 5.0 M /s** |
5058
5159Estimated per-handshake cost (from rates, not timers):
5260
5361| Component | ~ ns |
5462| -----------| -----|
5563| ECDSA ` verifyPrehashed ` | ~ 27 000 |
56- | Non-ECDSA (cert row) | ~ 135 000 |
64+ | Non-ECDSA (cert row) | ~ 105 000 |
5765| Chain/hostname extra (verify row) | ~ 1 000 |
5866
59- ** ECDSA is ~ 13% of the verify handshake** on this host; closing the BoringSSL gap
60- requires faster X25519, transcript hashing, and record crypto — not only double-base
61- point math.
67+ ** ECDSA verify is ~ 20% of the verify handshake** on this host. After the X25519 keygen
68+ comb, the handshake is no longer X25519-keygen-bound; the remaining non-ECDSA cost is the
69+ two variable-base X25519 scalarmults (shared secret), transcript hashing, and record
70+ crypto.
6271
6372BoringSSL's TLS 1.3 server requires a certificate, so there is no BoringSSL minimal-handshake
6473row. zig-tls reports both minimal (` auth = null ` ) and cert handshake rows.
@@ -68,12 +77,12 @@ row. zig-tls reports both minimal (`auth = null`) and cert handshake rows.
6877| Category | Winner |
6978| ----------| --------|
7079| Minimal handshake | ** zig-tls** (zig-only row) |
71- | Cert handshake | BoringSSL (~ 13%; server ECDSA sign dominates ) |
72- | Cert + verify handshake | BoringSSL (~ 12%; server ECDSA sign dominates ) |
73- | Transfer AES-128 send | Parity |
74- | Transfer AES-128 recv | Parity |
75- | Transfer AES-256 send | Parity |
76- | Transfer AES-256 recv | Parity |
80+ | Cert handshake | ** zig-tls ** (~ 1.14× ) |
81+ | Cert + verify handshake | ** zig-tls ** (~ 1.11× ) |
82+ | Transfer AES-128 send | Parity ( ~ 0.98×) |
83+ | Transfer AES-128 recv | Parity ( ~ 0.98×) |
84+ | Transfer AES-256 send | Parity ( ~ 0.95×) |
85+ | Transfer AES-256 recv | Parity ( ~ 0.97×) |
7786
7887## Methodology
7988
@@ -103,6 +112,11 @@ Categories mirror [rustls perf](https://rustls.dev/perf/):
103112
104113## Performance work in this tree
105114
115+ - ** X25519 keygen comb:** ` src/crypto/x25519_base.zig ` computes the X25519 public key with
116+ a constant-time 4-bit fixed-base comb on Edwards25519 (precomputed ` d·16^j·B ` table, 64
117+ additions, no runtime doublings) then maps to the Montgomery u-coordinate
118+ ` u = (Z+Y)/(Z-Y) ` . ~ 2.5× faster than the Montgomery-ladder ` recoverPublicKey ` ; lifts the
119+ full handshake past BoringSSL. Byte-identical to ` std.crypto.dh.X25519.recoverPublicKey ` .
106120- ** Transfer:** stitched AES-GCM assembly (AArch64/x86_64, BoringSSL-derived).
107121- ** Handshake:** single-hash transcript updates after cipher suite selection; TLS 1.3 server
108122 flight coalesced into one encrypted record; cached TLS 1.3 Certificate message in
0 commit comments