Repository navigation
Description
The client sends a server name but never checks that the certificate it gets back is for that name. There is no SAN dNSName comparison and no CN fallback — the leaf's public key is extracted and used, and the name is never looked at.
Confirmed at c836b1e: grep -rn '2.5.29.17\|55 1d 11' src/ returns nothing, and src/der_decode.s exports OID tables for ec_pubkey, prime256v1, secp384r1 and sha256_ecdsa only — there is no SAN OID in the tree.
Where the name already is
The expected name is known and already in RAM, which makes this cheaper than it might look:
- it is a build-time constant —
http_host_targetinHTTPS_TARGET_RODATA, frommake HTTPS_HOST=... src/boot.s:578-590already copies it intotls_hostname/tls_hostname_lenfor SNI (capped at 63 chars)- it is sent again in the
Host:header byhttp_build_get
So the comparison target needs no new plumbing. What is missing is reading the name out of the certificate and comparing.
Where the check would go
src/tls_cert.s's leaf handling, alongside x509_extract_pubkey. The W2 streaming consumer stages the leaf into cert_buf (CERT_BUF_SIZE = 2048 B under UCI, 1536 B under ip65) and cert_buf_len is set, so the whole leaf DER is addressable at that point — the parse does not have to be streaming.
Work required:
- a SAN OID table entry (
2.5.29.17=06 03 55 1D 11) and extension-walk inder_decode.s— it already hasder_read_tag/der_read_length/der_skip_tlv/der_match_oid, so this is a new caller rather than a new parser - iterate
GeneralNames, matchingdNSName(context tag[2], primitive) - case-insensitive ASCII compare against
tls_hostname - left-most wildcard handling (
*.example.org) — RFC 6125 §6.4.3: matches exactly one label, never a bare TLD, no partial-label wildcards - an abort path: mismatch must fail the handshake the way a bad Finished does (
tls_state=$FF+tls_last_state), not warn
Read this before assuming it buys security
Name validation on its own does not stop an active attacker, and shipping it without saying so would create false confidence. There is also no chain validation — no trust store, no root CAs, no issuer check (src/der_decode.s:271 skips the issuer outright). An attacker who can redirect the connection can simply self-sign a certificate carrying the correct name, and a name check passes it.
The two together are what authenticate a server. Name-checking alone upgrades "any certificate is accepted" to "any certificate naming the right host is accepted" — a real but modest improvement, and worth landing only if the limitation is documented alongside it. The README's "What this client does NOT authenticate" section would need editing rather than deleting.
Chain validation deserves its own issue: it is a much larger piece (a trust store costs resident KB, and each additional signature is another 16-30 s ECDSA verify at turbo, minutes at 1 MHz), and it is not obviously affordable on this machine.
Space
Measured at c836b1e on uci-comb, the fullest profile:
LOADER 162 B free
NET_CODE 111 B free
CRYPTO_OVERLAY 714 B free
CRYPTO_HOT 212 B free
A SAN walk plus a wildcard-aware compare should fit, but not carelessly, and TLS_CODE lives in NET_CODE (111 B). Expect to place new code deliberately — see how HTTPS_TARGET_RODATA was routed per-cfg in #126.
Testing
tools/test_x509.pyis the natural home for parser vectors: SAN present / absent, multiple names, wildcard match and non-match, name mismatch, CN-only legacy certs.- A negative end-to-end case matters more than a positive one.
tools/https_e2e/can mint certificates (ensure_certs.pydoes P-256 in pure Python), so a cert naming the wrong host, served to a client built for the right one, should abort — the same shape asrig_https_bad_finished.py, which exists precisely because an untested reject path was found to be a no-op. - Do not accept an all-positive vector set. See the F7 note in
tools/test_ecdsa_kat_oracle.py: an oracle with no negative case passes against a verifier stubbed to succeed.
Roadmap item, not a v0.4.x blocker — the scope is documented in the README as of v0.4.1.
Activity
Merge pull request #136 from JC-000/feat/server-name-validation
tls: enforce the handshake message sequence (#152)
docs(readme): point at v0.4.3, the security release
docs(readme): point at v0.4.3, the security release
docs(readme): point at v0.4.3, the security release