tls-1.2: client + server support for 9legacy
TLS 1.2 for 9legacy, client and server: ECDHE key exchange,
AEAD ciphers, SNI, and X.509 chain enforcement. With the
series applied:
- hget(1), webfs(4), and abaco(1) reach RSA- and ECDSA-fronted
servers using ECDHE (X25519, secp256r1, secp384r1), AEAD
ciphers (AES-128-GCM, ChaCha20-Poly1305), and SNI.
- ip/httpd serves HTTPS with per-vhost SNI cert routing
(RFC 6066 Section 3).
- X.509 chain + hostname + validity + RFC 5280 Section 4.2
(basicConstraints, keyUsage, extKeyUsage, nameConstraints)
verification is on by default via /sys/lib/tls/ca.pem.
- Server echoes RFC 5746 renegotiation_info on the initial
handshake.
- Both sides negotiate RFC 7627 extended_master_secret when
the peer offers it.
- NIST P-256 and P-384 client-side ECDHE use a constant-time
Montgomery-ladder scalar multiplier, removing the timing
side-channel that variable-time ecmul exposes on shared
hosts.
- The kernel record layer zeroes cipher state, MAC keys, and
the Secret struct on session close.
Apply
Patches are unified diffs against the iso-extracted 9legacy tree
(Plan 9 + the Stable + 9k patch queues, about 440 patches).
Apply in the order below:
cd /
for p in \
libsec-ecdhe-aead-primitives \
tls-aead-record-layer \
tls-record-zeroize-on-free \
tls-ecdhe-sni \
libsec-ecc-ecdsa-primitives \
libsec-x509-chain-hostname \
tls-ecdhe-ecdsa-and-chain \
tls-nist-ecdhe-curves \
libsec-x509-validity-dates \
libsec-x509-extension-framework \
libsec-x509-rfc5280-constraints \
tls-ca-bundle-default \
tls-aead-cipher-preference \
tls-extension-framework \
tls-server-reneg-info \
tls-extended-master-secret \
ip-httpd-vhosts-sni \
libsec-ecmul-ct-p256 \
libsec-ecmul-ct-p384 \
; do
echo === $p ===
ape/patch -p0 < /n/contrib/tls-1.2/$p.diff
done
Reverse (back out)
To remove the patches, apply in reverse order with -R:
cd /
for p in \
libsec-ecmul-ct-p384 \
libsec-ecmul-ct-p256 \
ip-httpd-vhosts-sni \
tls-extended-master-secret \
tls-server-reneg-info \
tls-extension-framework \
tls-aead-cipher-preference \
tls-ca-bundle-default \
libsec-x509-rfc5280-constraints \
libsec-x509-extension-framework \
libsec-x509-validity-dates \
tls-nist-ecdhe-curves \
tls-ecdhe-ecdsa-and-chain \
libsec-x509-chain-hostname \
libsec-ecc-ecdsa-primitives \
tls-ecdhe-sni \
tls-record-zeroize-on-free \
tls-aead-record-layer \
libsec-ecdhe-aead-primitives \
; do
echo === $p -R ===
ape/patch -p0 -R < /n/contrib/tls-1.2/$p.diff
done
Build
After applying, run the supplied build helper:
rc /n/contrib/tls-1.2/tools/build.rc
This rebuilds libsec, then the userspace consumers (hget, webfs,
tlsclient), then the in-kernel TLS driver (devtls). The kernel
config matching plan9.ini's bootfile is detected automatically
(typically 9pcf for terminal installs, 9pccpuf for CPU server
installs); three patches (tls-aead-record-layer,
tls-record-zeroize-on-free, and tls-ecdhe-ecdsa-and-chain) modify
sys/src/9/port/devtls.c.
To rebuild only userspace (after a patch that does not touch
devtls.c):
rc /n/contrib/tls-1.2/tools/build.rc userspace
mk does not declare that user binaries depend on libsec.a, so
build.rc force-relinks them with `rm -f 8.hget hget.8 6.hget
hget.6` (and similar for tlssrv, webfs) before `mk install`.
build.rc copies the freshly built kernel into the 9fat boot
partition. After it completes:
fshalt -r
Test
The self-test exercises the features the series adds against
real servers. Run from rio after reboot:
rc /n/contrib/tls-1.2/tools/test.rc
Fourteen probes: eleven expect success (one HTTP control,
TLS 1.2 client interop against RSA and ECDSA cert servers
across X25519, P-256, and P-384, plus a TLS 1.2-only boundary
check and a cipher-suite introspection echo), three expect
specific rejections (self-signed cert, wrong hostname, expired
cert). Summary line reads `ok: 14 / 14` when verification
succeeds.
Scope limits
Not in this series:
- server-side ECDSA cert serving. Requires factotum protocol
proto=ecdsa, which 9legacy factotum does not ship.
- RSA-PSS signature verification (RFC 8446). Verify path
accepts RSA-PKCS1-SHA{256,384,512} and ECDSA-SECP{256R1,
384R1}-SHA{256,384}; no PSS.
- server-side NIST P-256 and P-384 ECDHE. Server negotiates
X25519 only; clients that lack X25519 will not handshake.
- session resumption, mTLS, ALPN, encrypt-then-MAC,
fallback-SCSV, HSTS, HTTP-to-HTTPS redirect.
- TLS 1.3. Out of scope.
- IPv6 iPAddress SAN matching. IPv4 SAN matches; IPv6 does
not.
Files
README this file
libsec-ecdhe-aead-primitives.diff X25519 ECDHE, AES-GCM,
ChaCha20-Poly1305 in libsec
tls-aead-record-layer.diff devtls AEAD record layer
tls-record-zeroize-on-free.diff zero cipher state, MAC keys,
and Secret on session close
tls-ecdhe-sni.diff client + server ECDHE-RSA,
SNI, extension framing
libsec-ecc-ecdsa-primitives.diff ECC curve params, ECDSA
verify primitive in libsec
libsec-x509-chain-hostname.diff X.509 chain + hostname
verify (RFC 5280, RFC 6125)
tls-ecdhe-ecdsa-and-chain.diff client-side ECDHE-ECDSA,
ECDSA chain validation
tls-nist-ecdhe-curves.diff NIST P-256 / P-384 ECDHE
(RFC 8422)
libsec-x509-validity-dates.diff cert notBefore / notAfter
(RFC 5280 Section 4.1.2.5)
libsec-x509-extension-framework.diff X.509 v3 extension parser
libsec-x509-rfc5280-constraints.diff basicConstraints, keyUsage,
extKeyUsage, nameConstraints
(RFC 5280 Section 4.2)
tls-ca-bundle-default.diff /sys/lib/tls/ca.pem as
default trust anchor
tls-aead-cipher-preference.diff server cipher selection +
cert-key match (RFC 4492
Section 2.2)
tls-extension-framework.diff table-driven extension
dispatch
tls-server-reneg-info.diff RFC 5746 Section 3.2 server
echo + post-handshake refuse
tls-extended-master-secret.diff RFC 7627 EMS, negotiate
and offer
ip-httpd-vhosts-sni.diff per-vhost cert routing in
ip/httpd (RFC 6066 Section 3)
libsec-ecmul-ct-p256.diff constant-time Montgomery-
ladder ecmul on NIST P-256
libsec-ecmul-ct-p384.diff same for NIST P-384
tools/build.rc rebuild libsec, hget, webfs,
tlsclient, kernel
tools/test.rc 14-probe self-test
Prerequisites
The iso-extracted 9legacy tree (Plan 9 + the Stable and 9k patch
queues already applied) is the assumed baseline. Get the iso
from <https://9legacy.org/download/>.
apply requires `ape/patch`. rebuild requires write access to
/sys and /$cputype; in practice this means running as glenda
(the install user) or another account with the same group
permissions.
Trust anchor bundle
The default /sys/lib/tls/ca.pem ships from a 2021 Mozilla NSS
snapshot. Refresh from upstream when CAs rotate:
hget https://curl.se/ca/cacert.pem > /sys/lib/tls/ca.pem
To trust an internal CA in addition to the default set, append
its PEM-encoded cert:
cat /tmp/internal-ca.pem >> /sys/lib/tls/ca.pem
To bypass chain validation on a per-session basis (thumbprint-
based trust only):
bind /dev/null /sys/lib/tls/ca.pem
hget https://self-signed.example.com/
System-wide opt-out: rename or remove /sys/lib/tls/ca.pem.
|