-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
wolfNanoTLS does not copy or fork wolfSSL source. It pins wolfSSL as a git submodule, compiles selected files unchanged, and builds a thin TLS shell on top. This keeps the crypto bytes identical to upstream without forking.
The shell calls crypto only through a wc_* facade
(include/wolfnano/wolfnano_crypto.h, added with the shell). The concrete
provider is chosen at compile time by WOLFNANO_CRYPTO:
-
src(default, GPLv3): a hand-picked list ofwolfcrypt/src/*.cfrom the submodule. Memory model is selectable (see below); the default is plain wolfSSL heap.
The seam is what lets the shell objects link against the crypto backend with
zero shell source changes. Protect that invariant: the shell never calls a
wolfSSL TLS/SSL API or reaches into internal.c / ssl.c structures.
The cert path has two interchangeable, compile-time-selected parsers, so the handshake pays only for what a deployment needs:
-
wolfSSL
asn.c(default): the fullwc_ParseCert/DecodedCertdecoder, kept verbatim. It is the complete, battle-tested spec parser (full DN, certificate policies, name constraints, CRL/OCSP distribution points, ML-DSA leaf keys). It is the default because the cert parser is the handshake's trust boundary, and the proven decoder is the safe default. Note this selects the decoder, not a stronger validation path: the handshake enforces only the checks inwn_VerifyChain(signature linkage, CA flag, leaf keyUsage/serverAuth, hostname/pin, validity). Name constraints, policies, and revocation are decoded but not enforced on either backend. -
native
wn_x509(WOLFNANO_X509_LITE,make ... X509_LITE=1):src/wn_x509.c, a hand-written lightweight DER + RFC 5280 subset (wn_X509_Parse/wn_X509_VerifySignedBy/wn_X509_TimeValid). The TLV layer is lifted from wolfTPMtpm2_asn.c; the field walk follows wolfSSLexamples/asn1/asn1.cand the RFC. All signature math stays inwc_*via the seam; zero allocation, in-place references. It parses exactly the fields a pinned-anchor TLS client consumes (tbs range, SPKI/raw key, sig, algorithm ids, validity, subject CN, and the basicConstraints/keyUsage/extKeyUsage/SAN extensions), fails closed on an unrecognized critical extension, and dropsasn.c's ~14-16 KB cert parser for ~15% less.text(see Footprint). It is stricter thanasn.con some DER encodings and trims scope (no name constraints/policies/CRL/OCSP, capped SAN pool) - an opt-in size tier, not a drop-in equal.
Intended scope of the native backend (the pinned-anchor model; use the default
asn.c backend for full RFC 5280 path validation):
- Chain linkage is presentation order + per-hop signature + a pinned trust anchor; issuer/subject DN name-chaining is not compared (redundant once each signature verifies against the next/pinned key).
-
basicConstraintspathLenConstraintis parsed for structure but not enforced. - A critical
subjectAltNameis honored for its dNSNames; other GeneralName forms (iPAddress, URI, ...) are not processed but not rejected, so IP-SAN certs still connect. In a pin-only build (WOLFNANO_X509_HOSTNAME 0) SAN is not parsed at all - the key pin is the identity. - Duplicate instances of a recognized extension (BC/KU/EKU/SAN) are rejected; duplicate unrecognized non-critical extensions are ignored.
wn_connect.c selects the backend with a localized #ifdef; the differential
test (make x509diff) pins native output field-for-field to wolfSSL, and CI
runs the cert suite on both.
There is no WOLF_CRYPTO_CB on the default path; it is a fallback adder only.
The default follows wolfSSL's normal behavior (heap). The model is selected with wolfSSL's own macros (no wolfNano wrapper):
- default: plain wolfSSL, malloc on. Supports the full
asn.cX.509 path. -
WOLFSSL_SMALL_STACK: big working buffers move to the heap, keeping stack frames small (embedded). The nativewn_x509parser is small-stack aware. -
WOLFSSL_NO_MALLOC: true zero dynamic allocation - all state in caller-provided or static buffers, no heap. Proven two ways: the PSK, hybrid, and native-ECDSA-cert paths build and run underWOLFSSL_NO_MALLOC(allocation is disabled, so any attempt fails), and a runtime--wrapallocation probe additionally counts zero allocations over the crypto path and the full PSK handshake. The nativewn_x509ECDSA cert path is fully no-malloc; theasn.cbackend requires heap.
The TLS 1.3 server is a compile-time adder, off by default, built the same way as
the client: a slim wn_ shell (wn_Accept_Psk / wn_Accept_Cert) that lifts the
server-side handshake from wolfSSL tls13.c, reaches crypto only through the
wc_* seam, and reuses the client's role-agnostic modules (key share, key
schedule, transcript, record, session). The direction-specific pieces are a
ClientHello decoder, a ServerHello/Certificate encoder, a server-role key-share
(ECDHE generate-and-combine, ML-KEM encapsulate), and a CertificateVerify signer
(the mirror of the client's verify helpers: ECDSA, Ed25519, RSA-PSS, ML-DSA). It
supports every group and cipher the client does and interops both ways with
OpenSSL, wolfSSL, and mbedTLS. PSK + ECDSA/Ed25519 stay on the zero-allocation
tier; RSA and ML-DSA signing use the heap tier, exactly as on the client.
The server reassembles a ClientHello that spans multiple records (RFC 8446 5.1); the client Finished is a single 36-byte message read as one record. When a peer leads with a key_share for a group the server was not built with but offers the server's group in supported_groups, the server sends a HelloRetryRequest (RFC 8446 4.1.4) and completes on ClientHello2, feeding the synthetic message_hash(ClientHello1) + HRR into the transcript (4.4.1). The server is still single-group per build; HRR is the negotiation path when a peer's lead group differs.
wolfNanoTLS is "wolfSSL with features turned off." Interop stays identical to wolfSSL. The cipher-suite and supported-groups lists are derived from the active backend, never a fixed array, so the offered lists always match what the backend supports.
"Clean-room" here is a provenance rule: copy wolfSSL-family code verbatim (it is our own code), and write everything that is not wolfSSL-family strictly from the RFC, never from a third-party source.