-
Notifications
You must be signed in to change notification settings - Fork 0
Footprint
wolfNanoTLS's whole TLS 1.3 .text - as a client, as a server
(WOLFNANO_SERVER), and as a combined client+server device - measured on
Cortex-M33 (Thumb2, arm-none-eabi-gcc -Os -flto -ffunction-sections -fdata-sections -Wl,--gc-sections + nano specs, ArmGNU 14.2). Same method and
toolchain as the head-to-head in Comparison. Each row builds
from a public configs/ starter template, so the numbers reproduce from the
same user_settings.h a deployment ships.
The cert rows show the native wn_x509 parser (WOLFNANO_X509_LITE); the
default backend is wolfSSL asn.c (see the X.509 backend section below).
| Client profile | config | .text |
|---|---|---|
| PSK + ECDHE, X25519 | user_settings_minimal.h |
18680 (18.2 KB) |
| PSK + ECDHE, P-256 | user_settings_psk_p256.h |
26604 (26.0 KB) |
| PSK + X25519MLKEM768 (PQC KEX) | user_settings_pqc.h |
34436 (33.6 KB) |
| cert / X.509, P-256 min | user_settings_cert_p256min.h |
36440 (35.6 KB) |
cert / X.509, P-256 (X509_LITE) |
user_settings_cert.h |
54280 (53.0 KB) |
cert / X.509 + ML-DSA-44 (PQC auth, X509_LITE) |
user_settings_cert_mldsa.h |
69071 (67.5 KB) |
cert / X.509 + X25519MLKEM768 (PQC KEX, X509_LITE) |
user_settings_cert_pqc.h |
76052 (74.3 KB) |
Reproduce: sh bench/footprint-clients.sh (wolfNanoTLS column).
These client rows are the pre-server-code production numbers actually measured on Cortex-M33, kept as the reference client footprint. A fresh build at the current pin lands ~0.6 KB higher on the PSK rows (shared shell files grew with the server adder); the server and device tables below are measured on that current codebase.
The WOLFNANO_SERVER adder (off by default): the same configs/ template built
with -DWOLFNANO_SERVER, linking only wn_Accept_* (no wn_Connect_*). cert
rows use the native wn_x509 X509_LITE backend.
| Server profile | config (+ WOLFNANO_SERVER) |
.text |
|---|---|---|
| PSK + ECDHE, X25519 | user_settings_minimal.h |
20084 (19.6 KB) |
| PSK + ECDHE, P-256 | user_settings_psk_p256.h |
28008 (27.4 KB) |
| PSK + X25519MLKEM768 (PQC KEX) | user_settings_pqc.h |
33128 (32.4 KB) |
cert / X.509, P-256 (X509_LITE) |
user_settings_cert.h |
46768 (45.7 KB) |
cert / X.509 + ML-DSA-44 (PQC auth, X509_LITE) |
user_settings_cert_mldsa.h |
57449 (56.1 KB) |
Reproduce: sh bench/footprint-servers.sh (wolfNanoTLS column).
The cert server (45.7 KB) is 14% under its own cert client (53.0 KB) because
wn_accept (sign) and wn_connect (verify a full chain) are genuinely separate
units - unlike mbedTLS, whose server ≈ its client (see Comparison).
One binary that is both roles (wn_Connect_* + wn_Accept_*), the realistic
device case:
| Client+server device | config | .text |
|---|---|---|
| PSK + ECDHE, X25519 | user_settings_minimal.h |
22612 (22.1 KB) |
| PSK + ECDHE, P-256 | user_settings_psk_p256.h |
30392 (29.7 KB) |
| PSK + X25519MLKEM768 (PQC KEX) | user_settings_pqc.h |
39236 (38.3 KB) |
cert / X.509, P-256 (X509_LITE) |
user_settings_cert.h |
61424 (60.0 KB) |
cert / X.509 + ML-DSA-44 (PQC auth, X509_LITE) |
user_settings_cert_mldsa.h |
77518 (75.7 KB) |
Reproduce: sh bench/footprint-serverclient.sh (wolfNanoTLS column).
Public HTTPS is TLS 1.3 with X.509 server-cert validation, so only the cert rows can do it; the PSK rows cannot. The distinction, per profile:
-
PSK (
minimal,psk_p256,pqc): a pre-shared key, no certificates - for private/embedded peers that already share a key. Cannot authenticate a public web server. The PQC PSK row uses X25519MLKEM768 for the key exchange, but auth is still PSK. -
cert, P-256 min (
cert_p256min): private PKI only - see the two cert tiers below. -
cert, P-256 / ML-DSA / X25519MLKEM768 (
cert,cert_mldsa,cert_pqc): full public web PKI - validates real public HTTPS chains to a public root.
The two cert tiers differ by which chains they can validate:
-
35.6 KB, P-256 min (
user_settings_cert_p256min.h): ECDSA P-256 / SHA-256 only (no SHA-384/512, P-384, RSA;rsa.c/sha512.cdropped). Validates a chain that is P-256/ECDSA-SHA256 from the pinned anchor down - a private PKI you issue, or a public site pinned at a P-256 intermediate. It cannot validate a full public chain to the root (public roots/intermediates are ECDSA-SHA384 or RSA), so it is not a general-web client. -
53.0 KB, P-256 (full algorithms): adds RSA + SHA-384, so it validates real
public HTTPS chains to a public root - verified live against Let's Encrypt (ISRG
Root X1) each build, on both the
asn.cand nativewn_x509backends.
The PQC PSK client adds ~15 KB over the classical X25519 client for the
ML-KEM-768 lattice math plus SHA-3/SHAKE (the same wc_mlkem code wolfSSL
ships). The ML-DSA-44 cert client (ECDHE P-256 KEX + ML-DSA-44 cert verify, no
RSA) is the one row built without -flto: wc_mldsa.c trips an LTO +
--gc-sections live-code-removal bug in ArmGNU 14.2, so it slightly over-states
versus the -flto rows. That row is PQC authentication (ML-DSA cert) with a
classical ECDHE P-256 key exchange.
The X25519MLKEM768 cert row (user_settings_cert_pqc.h) is the complementary
axis: PQC key exchange - a hybrid ML-KEM-768/X25519 handshake that resists
harvest-now-decrypt-later - with a classical X.509 cert. It is
WOLFNANO_HAVE_MLKEM_HYBRID layered on the cert config, so WN_DEFAULT_GROUP
becomes X25519MLKEM768 and wn_keyshare routes through wn_hybrid; no cert-path
code changes. The two PQC rows are independent and can be combined for a
fully-PQC (KEX + auth) client.
The cert path defaults to wolfSSL's full asn.c/DecodedCert (complete,
proven; decodes a wider field set - full DN, certificate policies, name
constraints, CRL/OCSP, ML-DSA leaf keys - decoded, not enforced by the
handshake). WOLFNANO_X509_LITE (make ... X509_LITE=1) swaps in the smaller
native wn_x509 parser (hand-written lightweight DER + RFC 5280 subset). Same
client, same crypto floor; only the X.509 backend differs:
| cert client | wolfSSL asn.c (default) |
native wn_x509 (X509_LITE) |
reduction |
|---|---|---|---|
| X.509, P-256 | 63877 (62.4 KB) | 54280 (53.0 KB) | -9597 B (15.0%) |
| X.509 + ML-DSA-44 | 81722 (79.8 KB) | 69071 (67.5 KB) | -12651 B (15.5%) |
Native removes asn.c's reachable cert parser (DecodeCert / GetName /
DecodeCertExtensions / GetDateInfo / wc_*PublicKeyDecode, ~14-16 KB after
--gc-sections) and replaces it with the ~4-5 KB wn_x509 walker. wolfSSL's own
asn.c size options (WOLFSSL_X509_TINY, WOLFSSL_X509_VERIFY_ONLY,
IGNORE_NAME_CONSTRAINTS, WOLFSSL_NO_ASN_STRICT) trim only ~3.4% here, because
--gc-sections already drops most of what they remove; they keep the ASN
template engine, which is what wn_x509 replaces wholesale.
Reproduce: sh bench/footprint-clients.sh (the "X.509 backend" block).
Code size is static, so the on-target crypto floor can be measured on the host
by cross-compiling for Thumb2 and running size (no device needed):
make floor-thumb2 # cross-compile the floor for Cortex-M33
arm-none-eabi-size -t build/thumb2/*.oThe default floor (all curves + ASN + both SP backends, pre---gc-sections)
sums to ~250 KB .text as an upper bound. The shippable number is much
smaller: roughly 40% is optional adders (X.509/ASN ~50 KB, Ed25519 group ops
~49 KB) a minimal PSK/ECDHE-P256 build does not link, and --gc-sections drops
the unreferenced remainder. A minimal TLS 1.3 floor (ECDHE-P256 + AES-GCM +
SHA-256 + HKDF + DRBG) lands far lower.
| Build | flash + RAM budget | device classes |
|---|---|---|
| PSK (17 KB) | ~17 KB flash, ~8-16 KB RAM | Cortex-M0+/M3/M4 from ~32 KB flash: LoRaWAN/NB-IoT/Matter sensors, wearables (STM32L0/L4, nRF52) |
| cert (52 KB) | ~52 KB flash, ~24-40 KB RAM | Cortex-M4/M33 from ~128 KB flash: cloud-IoT endpoints, gateways (STM32L4/U5/H5, nRF53, ESP32) |
The key-exchange floor knob is X25519 (smallest); set
WOLFNANO_HAVE_ECDHE_P256 for the P-256 build. PQC and asm add flash on top of
the classical floor.
A size comparison against stock/hard-minimized mbedTLS and full wolfSSL is in Comparison.