Plan 9 from Bell Labs’s /usr/web/sources/contrib/mospak/tls-1.2/README

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


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.

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.