Security threat model & defaults¶
This package exists so that "use TLS" does not become "hand-roll a crypto/tls.Config
and hope." Go's zero-value tls.Config is deliberately permissive — it negotiates
down to TLS 1.0, offers CBC-mode and RSA-key-exchange cipher suites, and leaves curve
selection to the runtime. Those defaults prioritise interoperability with old peers
over security. For first-party services talking to first-party clients, that trade is
backwards. DefaultConfig
inverts it.
Every server and client config this package produces is built from DefaultConfig, so
the guarantees below hold uniformly across both ends of a connection.
What we defend against¶
- Protocol-downgrade attacks. A network attacker who can influence the handshake should not be able to force a connection onto a weak, break-able protocol version.
- Weak cipher negotiation. Cipher suites with known structural weaknesses (CBC padding oracles, non-AEAD constructions, static RSA key exchange with no forward secrecy) should never be on the table, even if a peer offers them.
- Missing forward secrecy. Compromise of a server's long-term private key should not retroactively decrypt previously-captured sessions.
- Fragile trust configuration. A misconfigured certificate path or an empty CA bundle should fail loudly at construction, not silently accept an untrusted peer.
What this package does not do: it is not a PKI, a certificate issuer, or a secret store. It consumes certificate paths you provide and applies a hardened policy. How those files are provisioned, rotated, and protected on disk is the caller's responsibility.
The defaults, and why¶
TLS 1.2 as the floor¶
MinVersion is pinned to VersionTLS12. TLS 1.0 and 1.1 are deprecated
(RFC 8996) and carry downgrade and
cipher-construction weaknesses. Setting an explicit floor removes them from
negotiation entirely, defeating downgrade attempts rather than relying on peers to
prefer better. TLS 1.3 is negotiated automatically when both ends support it; the
floor guarantees the worst case is still 1.2.
AEAD-only cipher suites¶
The suite list is restricted to six ECDHE authenticated-encryption suites:
| Suite | Key exchange | Auth | Cipher |
|---|---|---|---|
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 |
ECDHE | ECDSA | AES-256-GCM |
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 |
ECDHE | RSA | AES-256-GCM |
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 |
ECDHE | ECDSA | AES-128-GCM |
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 |
ECDHE | RSA | AES-128-GCM |
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 |
ECDHE | ECDSA | ChaCha20-Poly1305 |
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 |
ECDHE | RSA | ChaCha20-Poly1305 |
Three properties are guaranteed by construction:
- Forward secrecy — every suite uses ephemeral ECDHE key exchange, so a stolen long-term key cannot decrypt past captured traffic.
- AEAD only — GCM and ChaCha20-Poly1305 authenticate as they encrypt. There are no CBC-mode suites, so the padding-oracle class of attacks (Lucky 13 and relatives) does not apply.
- Both signature families — ECDSA and RSA variants are present so the list works with either an EC or an RSA server certificate; the negotiated suite follows the certificate's key type.
Both AES-GCM and ChaCha20-Poly1305 are included so hardware without AES acceleration still negotiates a fast, constant-time cipher.
Cipher order and TLS 1.3
Go ignores the CipherSuites list for TLS 1.3 connections — the 1.3 suites are
fixed by the protocol and always safe. This list governs the TLS 1.2 floor. Go
also chooses the mutually-preferred suite itself; the list defines which suites
are permitted, not a rigid server-dictated order.
Modern curve preferences¶
CurvePreferences is set to X25519 then CurveP256. X25519 is a fast, misuse-
resistant Montgomery curve and is preferred; P-256 follows for peers or certificates
that require a NIST curve. Slower and less-scrutinised curves are omitted.
Failing closed on trust¶
The trust helpers are written to error rather than silently degrade:
CertPoolreturns an error if a CA file cannot be read, and — importantly — if a file parses to zero certificates. An empty or malformed bundle is a configuration error, not an empty set of trusted roots to shrug at.ClientConfigpropagates those errors, so a client is never constructed with a broken custom trust store. With no CA files it falls back to the system root store (the correct default for public endpoints); it never disables verification.Pair.CertificateandServerConfigsurface load failures at startup, so a bad certificate path stops the process rather than breaking the first client handshake.
There is deliberately no option to skip verification or lower the floor. If you need a
looser policy for a specific integration, build a crypto/tls.Config yourself for that
one call site — the insecure path should be explicit and local, never the default this
package hands out.
Relationship to crypto/tls¶
This package is a thin, opinionated layer over the standard library, not a replacement
for it. DefaultConfig returns a plain *crypto/tls.Config you can inspect or further
constrain. The value it adds is a single, reviewed set of decisions applied
consistently — so a security review happens once, here, instead of at every listener
in every service.