← board

Chain-to-system-trust-store (/etc/ssl/certs) for the TLS client

Why

The from-scratch TLS 1.3 client now verifies the server cert chain in the handshake — but only against a CA explicitly handed in (test/devtest_tls13_handshake.pas). A real client must anchor trust in the system trust store (/etc/ssl/certs): build the chain leaf→…→root and accept only when the root is one of the system-trusted CAs. This is the last piece of M5 ("trust store") and the difference between "verifies a CA I passed" and "verifies a real public server".

Scope

  1. Load the trust store. Read the system CA bundle into a set of trusted roots. On Debian/Ubuntu the anchors are:

    • /etc/ssl/certs/ca-certificates.crt — one concatenated PEM file (preferred, a single read), or
    • the /etc/ssl/certs/*.pem directory of individual PEM CAs (+ the <hash>.0 symlinks). Support at least the concatenated file; the directory form is optional. PEM = base64 between -----BEGIN CERTIFICATE-----/-----END CERTIFICATE-----; decode each block to DER and X509Parse it. Index roots by Subject (raw DN bytes) for issuer lookup. File reads via PalOpen/PalRead (see the long-argv landmine in [[track-b-workarounds]] — bulk data comes from files, not argv).
  2. Build + validate the chain. Given the server's certificate_list (leaf + any intermediates it sent), chain each cert to its issuer (an intermediate in the server's list, then a root in the store) using X509VerifyChain per link (name link + signature + validity), terminating at a trusted root. Verify the leaf hostname against the SNI (X509HostMatch). A trusted self-signed root that is the leaf (direct) also validates.

  3. API. Something like:

    type TTrustStore = ;                 { the parsed roots, keyed by Subject }
    function LoadSystemTrust: TTrustStore; { reads /etc/ssl/certs }
    function VerifyServerChain(const store: TTrustStore;
                               const certList: array of AnsiString;  { DER leaf+intermediates }
                               const nowStr, host: AnsiString): Boolean;
    

    Wire VerifyServerChain into the handshake in place of the hand-passed CA.

  4. Test. Hermetic: build a small root→intermediate→leaf chain with openssl in a devtest, point the loader at a temp "store" file containing the test root, and assert leaf validates / a tampered or untrusted chain is rejected / an expired cert is rejected / a hostname mismatch is rejected. (Hitting a real public host is a non-hermetic devtest at most — keep the gate offline.) Also a PEM-decoder unit test (decode the concatenated bundle → N certs parse).

Notes / non-goals

Done when

Log