tlspolicy

package
v0.40.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 14, 2026 License: CC0-1.0 Imports: 16 Imported by: 0

README

tlspolicy

tlspolicy builds explicit, per-context TLS trust policies for Go clients and servers. It is designed for applications that may have several network routes and must not silently fall back to the operating-system trust store or perform certificate-related network lookups outside the selected route.

The module requires Go 1.23 or newer and has no third-party dependencies.

Guarantees of the core package

  • Every server-root and mTLS client-root pool starts with x509.NewCertPool().
  • An empty root selection means “trust nobody”; it never means “use system roots”.
  • The package never calls x509.SystemCertPool or x509.SetFallbackRoots.
  • The package contains no AIA, OCSP, or CRL downloader.
  • Missing intermediates fail verification.
  • Pins are additional to normal PKI and hostname verification.
  • Scoped authority and pin checks run from tls.Config.VerifyConnection, so they also run on resumed sessions.

These guarantees do not cover application-provided verifier callbacks or AuthorityFetcher implementations. A callback or fetcher can perform network I/O if its implementation chooses to do so.

Discovering OS-store candidates

The package contains no platform-specific store code. Implement AuthorityFetcher in build-tagged files in the application or a separate module:

type windowsStoreFetcher struct {
    // Platform-specific options belong here.
}

func (f *windowsStoreFetcher) FetchAuthorities(
    ctx context.Context,
) ([]tlspolicy.AuthorityCandidate, error) {
    // Enumerate local store objects and copy each certificate's complete DER.
    // Do not build paths or invoke online retrieval as part of enumeration.
    panic("platform-specific implementation")
}

Then create a deduplicated catalog for the policy UI:

catalog, err := tlspolicy.FetchAuthorityCatalog(ctx, fetcher)
if err != nil {
    return err
}

for _, record := range catalog.Records() {
    fmt.Printf("%s  %s  %v\n",
        record.Subject,
        record.CertificateFingerprint,
        record.Sources,
    )
}

The fetcher must export complete certificate DER. x509.CertPool.Subjects is not a certificate export API and must not be used for this purpose.

A source's TrustHint is display metadata, not an application trust decision. Importing DER into a Go pool does not copy every platform-specific distrust, usage restriction, enterprise rule, or automatic root-update behavior.

Building a scoped remote-server policy

root, err := tlspolicy.ParseAuthorityDER(selectedRootDER)
if err != nil {
    return err
}

govScope, err := tlspolicy.NewDNSDomainScope("gov.example", true)
if err != nil {
    return err
}

policy, err := tlspolicy.CompileServerPolicy(tlspolicy.ServerPolicySpec{
    TrustAnchors: []tlspolicy.ScopedAuthority{
        {
            Authority: root,
            Scope:     govScope,
        },
    },
})
if err != nil {
    return err
}

The root above can authenticate only gov.example and its subdomains.

Anchor scoping restricts chains that terminate at that exact root. To restrict a CA regardless of which cross-signed path contains it, add an AuthorityConstraint, commonly with MatchSPKI:

constraint := tlspolicy.AuthorityConstraint{
    Authority: governmentCA,
    Match:     tlspolicy.MatchSPKI,
    Scope:     govScope,
}

SPKI matching is intentionally broader than exact-certificate matching. Review all certificates sharing that key before using it.

Pinning a resource

pin, err := tlspolicy.NewSPKIPin(expectedLeafDER)
if err != nil {
    return err
}

resourceScope, err := tlspolicy.NewExactServerScope("api.example.test")
if err != nil {
    return err
}

policy, err := tlspolicy.CompileServerPolicy(tlspolicy.ServerPolicySpec{
    TrustAnchors: selectedAnchors,
    Pins: []tlspolicy.ScopedPin{
        {Pin: pin, Scope: resourceScope},
    },
})

When several pins apply to one server, they are alternatives. Keep an old and a new pin active during key rotation. A certificate pin changes on every reissuance; an SPKI pin survives renewal only when the key is retained.

Fixed-server TLS client

tlsConfig, err := policy.TLSClientConfigForServer(
    "api.example.test",
    tlspolicy.ClientTLSOptions{
        MinVersion:       tls.VersionTLS13,
        SessionCacheSize: 64,
    },
)
if err != nil {
    return err
}

conn, err := tls.DialWithDialer(dialer, "tcp", address, tlsConfig)

Use the fixed-server form with tls.Dial, tls.Client, or another caller that does not automatically set ServerName.

The fixed-server form also supports IP literals. Go uses an IP-valued tls.Config.ServerName for IP SAN verification but does not send it as SNI, so the package carries the fixed IP identity into its policy callback explicitly.

mTLS server

clientRoot, err := tlspolicy.ParseAuthorityDER(clientRootDER)
if err != nil {
    return err
}

clientPolicy, err := tlspolicy.CompileClientPolicy(
    tlspolicy.ClientPolicySpec{
        TrustAnchors: []tlspolicy.Authority{clientRoot},
    },
)
if err != nil {
    return err
}

serverTLS, err := clientPolicy.ServerTLSConfig(
    tlspolicy.ServerTLSOptions{
        Certificates: []tls.Certificate{serverCertificate},
        ClientAuth:   tlspolicy.ClientAuthRequireAndVerify,
    },
)

ClientCAs remains non-nil even if the selected root list is empty. An empty mTLS policy therefore rejects every client chain.

Stapled OCSP and other local checks

The core package deliberately has no OCSP dependency. Add a ServerConnectionVerifier to validate ConnectionState.OCSPResponse with a library and freshness policy selected by the application:

options.Verifiers = []tlspolicy.ServerConnectionVerifier{
    tlspolicy.ServerConnectionVerifierFunc(func(v tlspolicy.ServerVerification) error {
        // Validate v.ConnectionState.OCSPResponse against an issuer from
        // v.AuthorizedChains. Do not contact the network here unless that
        // traffic is explicitly routed and bounded.
        return nil
    }),
}

The ConnectionState.VerifiedChains delivered to additional verifiers is filtered to the same authorized chains exposed in AuthorizedChains.

Operational rules

  • Treat compiled policies as immutable.
  • Do not replace the roots, identity, verification callback, proxy, dialer, or TLS dial hooks installed by the package.
  • Keep a separate client session cache per policy; the package does this when SessionCacheSize is positive.
  • Servers must send the leaf followed by all required intermediates and should omit the root.
  • Store selected DER and policy scopes in application configuration. Refreshing an OS-store catalog should not silently expand an existing policy.

Verification

go test ./...
go test -race ./...
go vet ./...

Documentation

Overview

Package tlspolicy builds explicit, application-controlled TLS trust policies.

The package is intended for applications that have several TLS clients or servers operating in different network contexts, such as a direct route, an HTTP CONNECT proxy, a SOCKS tunnel, or an isolated test network. It keeps two concerns separate:

  • a network route decides how sockets and DNS requests leave the process;
  • a compiled policy decides which certificate chains and pins are accepted.

ServerPolicy is used by a TLS client to verify remote servers. It supports an explicit set of trust anchors, DNS- and IP-scoped trust anchors, constraints on intermediate certificate authorities, and leaf certificate or leaf SPKI pins. ClientPolicy is used by a TLS server to verify mTLS clients with an explicit set of client trust anchors and optional leaf pins.

Both policy types compile their trust anchors into an ordinary pool created by x509.NewCertPool. An empty policy therefore means “trust no certificate”, not “fall back to the operating-system trust store”. The package never calls x509.SystemCertPool and never installs process-wide fallback roots.

No implicit certificate-network traffic

The package contains no AIA, OCSP, or CRL downloader. Verification uses the certificates supplied by the peer and the explicitly configured pools. A peer that omits a required intermediate certificate fails verification. Standard crypto/x509 verification does not perform revocation checking.

Additional verifier hooks are available for application-specific checks, including validation of a stapled OCSP response. Those hooks execute inside the TLS handshake. If a hook performs network I/O, the application is solely responsible for binding that I/O to the correct route and for preventing recursion, SSRF, direct-network fallback, and unbounded waits. A verifier that must preserve the package's no-network property should inspect only the supplied ConnectionState and local data.

Operating-system authority discovery

AuthorityFetcher is deliberately only an interface. Platform-specific code may implement it by enumerating local certificate stores and exporting DER certificates. x509.CertPool.Subjects is not a certificate-export API and must not be used to obtain those DER values. FetchAuthorityCatalog validates and deduplicates the candidates for presentation to a user. This package contains no Windows, macOS, Linux, or other platform-specific store-enumeration implementation.

A DER certificate discovered in an OS store is only a candidate. Importing it into an application pool does not reproduce every OS trust decision, distrust list, usage restriction, enterprise policy, automatic root-update rule, or application-specific exception. User interfaces should preserve AuthoritySource information and require an explicit application-policy decision before promoting a discovered certificate to a trust anchor.

Typical client use

A client that talks to a fixed server can build a configuration whose ServerName is fixed by policy:

ca, err := tlspolicy.ParseAuthorityDER(rootDER)
if err != nil {
	return err
}

scope, err := tlspolicy.NewDNSDomainScope("gov.example", true)
if err != nil {
	return err
}

policy, err := tlspolicy.CompileServerPolicy(tlspolicy.ServerPolicySpec{
	TrustAnchors: []tlspolicy.ScopedAuthority{{
		Authority: ca,
		Scope:     scope,
	}},
})
if err != nil {
	return err
}

tlsConfig, err := policy.TLSClientConfigForServer(
	"service.gov.example",
	tlspolicy.ClientTLSOptions{},
)
if err != nil {
	return err
}

For net/http clients that can contact several hosts, use TLSClientConfig. The shared configuration form requires a DNS hostname. For an IP-literal server, use TLSClientConfigForServer because crypto/tls intentionally omits IP literals from SNI and does not expose the expected IP through ConnectionState.ServerName.

Typical server use

CompileClientPolicy builds the trust policy for mTLS client certificates. ServerTLSConfig then combines that policy with the server's certificate and a ClientAuthMode. Even when the policy contains no roots, ClientCAs remains a non-nil empty pool, so verification fails closed instead of using host roots.

Immutability and sharing

Compiled policies are immutable and safe for concurrent use. The returned tls.Config and http.Transport values must be completely configured before first use and must not be mutated afterward. In particular, callers must not replace RootCAs, ClientCAs, ServerName, VerifyConnection, DialContext, Proxy, or the TLS dial hooks installed by this package.

A ClientTLSOptions session cache is created per generated configuration. Do not manually share a tls.ClientSessionCache between trust policies. The package uses VerifyConnection rather than VerifyPeerCertificate, so scoped authority and pin checks also run on resumed TLS sessions.

DNS names

Policy DNS names must be ASCII DNS A-labels, such as xn--bcher-kva.example, not Unicode U-labels. Names are lowercased and a final root dot is removed. Wildcards are not accepted in policy rules. Subdomain matching is expressed explicitly with DNSRule.IncludeSubdomains and always respects label boundaries.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrMissingServerIdentity is returned when a shared TLS client
	// configuration reaches verification without a ServerName. This usually
	// means the caller used tls.Client directly without setting Config.ServerName
	// or used a transport that did not populate it.
	ErrMissingServerIdentity = errors.New(
		"tlspolicy: missing remote server identity",
	)

	// ErrNoVerifiedChains is returned when a policy is asked to inspect a
	// connection for which normal crypto/tls certificate verification did not
	// produce any chain. Policy-generated configurations keep normal
	// verification enabled, so this generally indicates manual misuse.
	ErrNoVerifiedChains = errors.New(
		"tlspolicy: normal TLS verification produced no certificate chain",
	)

	// ErrServerIdentityMismatch is returned when the leaf certificate in an
	// otherwise verified chain is not valid for the ServerIdentity supplied to
	// ServerPolicy.VerifyServer. Policy-generated TLS configurations already ask
	// crypto/tls to perform this check; VerifyServer repeats it so direct callers
	// cannot accidentally apply domain-scoped trust to a chain verified for a
	// different name or IP address.
	ErrServerIdentityMismatch = errors.New(
		"tlspolicy: server certificate does not match the policy identity",
	)

	// ErrUnauthorizedChain is returned when normal PKI verification succeeded,
	// but every verified chain violates a scoped trust-anchor or authority rule.
	ErrUnauthorizedChain = errors.New(
		"tlspolicy: no verified certificate chain is authorized by policy",
	)

	// ErrPinMismatch is returned when at least one pin applies to the peer but
	// none of the applicable pins matches the leaf certificate.
	ErrPinMismatch = errors.New("tlspolicy: peer certificate pin mismatch")
)

Functions

This section is empty.

Types

type Authority

type Authority struct {
	// contains filtered or unexported fields
}

Authority is an immutable, parsed X.509 certificate that can be selected as a trust anchor or used to constrain a certificate authority in a chain.

Authority does not assert that the certificate is trusted, self-signed, or a CA. Trust is granted only by placing it in a policy's trust-anchor list. The zero value is invalid; create values with ParseAuthorityDER, ParseAuthoritiesPEM, or AuthorityFromCertificate.

func AuthorityFromCertificate

func AuthorityFromCertificate(cert *x509.Certificate) (Authority, error)

AuthorityFromCertificate copies cert.Raw and returns it as an immutable Authority value.

The function reparses the DER so that malformed or manually constructed x509.Certificate values are rejected.

func ParseAuthoritiesPEM

func ParseAuthoritiesPEM(pemData []byte) ([]Authority, error)

ParseAuthoritiesPEM parses every CERTIFICATE block in pemData.

Non-certificate PEM blocks are ignored. The function returns an error if a CERTIFICATE block is malformed, if non-PEM non-whitespace data remains, or if no certificate block is present. Each returned Authority owns its DER bytes.

func ParseAuthorityDER

func ParseAuthorityDER(der []byte) (Authority, error)

ParseAuthorityDER parses exactly one DER-encoded X.509 certificate and returns an immutable Authority value.

func (Authority) Certificate

func (a Authority) Certificate() (*x509.Certificate, error)

Certificate returns a newly parsed certificate whose backing byte slices do not alias the Authority value.

func (Authority) DER

func (a Authority) DER() []byte

DER returns a copy of the complete DER-encoded certificate.

func (Authority) Equal

func (a Authority) Equal(other Authority) bool

Equal reports whether a and other contain the exact same DER certificate.

func (Authority) Fingerprint

func (a Authority) Fingerprint() Fingerprint

Fingerprint returns the SHA-256 digest of the complete DER certificate.

func (Authority) MarshalBinary

func (a Authority) MarshalBinary() ([]byte, error)

MarshalBinary implements encoding.BinaryMarshaler by returning a copy of the complete DER certificate.

func (Authority) SPKIFingerprint

func (a Authority) SPKIFingerprint() Fingerprint

SPKIFingerprint returns the SHA-256 digest of the certificate's DER-encoded SubjectPublicKeyInfo value.

func (*Authority) UnmarshalBinary

func (a *Authority) UnmarshalBinary(data []byte) error

UnmarshalBinary implements encoding.BinaryUnmarshaler.

func (Authority) Valid

func (a Authority) Valid() bool

Valid reports whether a contains a parsed certificate.

type AuthorityCandidate

type AuthorityCandidate struct {
	// DER is one complete DER-encoded X.509 certificate.
	DER []byte

	// Source describes where DER was discovered.
	Source AuthoritySource
}

AuthorityCandidate is one DER certificate and the local source that exposed it. AuthorityFetcher implementations may return the same DER certificate more than once with different AuthoritySource values; AuthorityCatalog deduplicates the certificate and preserves all distinct sources.

type AuthorityCatalog

type AuthorityCatalog struct {
	// contains filtered or unexported fields
}

AuthorityCatalog is an immutable, fingerprint-deduplicated snapshot of authority candidates.

Catalog methods return copies. An AuthorityCatalog is safe for concurrent use after construction.

func FetchAuthorityCatalog

func FetchAuthorityCatalog(
	ctx context.Context,
	fetcher AuthorityFetcher,
) (*AuthorityCatalog, error)

FetchAuthorityCatalog invokes fetcher and validates its results with NewAuthorityCatalog.

func NewAuthorityCatalog

func NewAuthorityCatalog(
	candidates []AuthorityCandidate,
) (*AuthorityCatalog, error)

NewAuthorityCatalog validates candidates, deduplicates them by exact certificate fingerprint, and returns a stable, subject-sorted catalog.

The function copies all DER bytes and source metadata. It rejects malformed certificates. Exact duplicate source entries for the same certificate are stored once.

func (*AuthorityCatalog) Len

func (c *AuthorityCatalog) Len() int

Len returns the number of distinct DER certificates in c. A nil catalog has length zero.

func (*AuthorityCatalog) Lookup

Lookup returns a copy of the record with the exact certificate fingerprint id. The boolean result is false when id is absent or c is nil.

func (*AuthorityCatalog) Records

func (c *AuthorityCatalog) Records() []AuthorityRecord

Records returns all catalog entries in stable subject, issuer, and fingerprint order. The returned slice and source metadata are copies.

func (*AuthorityCatalog) Select

func (c *AuthorityCatalog) Select(ids []Fingerprint) ([]Authority, error)

Select resolves ids to immutable Authority values in the requested order. It returns an error for an unknown fingerprint or a duplicate selection.

type AuthorityConstraint

type AuthorityConstraint struct {
	// Authority supplies the exact certificate or SPKI to recognize.
	Authority Authority

	// Match chooses exact-certificate or SPKI recognition.
	Match AuthorityMatch

	// Scope selects the servers for which the matched CA is permitted.
	Scope Scope
}

AuthorityConstraint restricts where a CA certificate may appear in a verified server chain.

Constraints are evaluated for every non-leaf certificate, including the terminal trust anchor. They do not add Authority to the root pool. A typical use is to restrict a government or enterprise intermediate CA even when it chains to a more generally trusted root.

type AuthorityFetcher

type AuthorityFetcher interface {
	// FetchAuthorities returns a snapshot of locally discoverable certificate
	// candidates. It should honor ctx cancellation, copy DER bytes out of any
	// platform-owned buffers, and describe each source precisely enough for user
	// display and audit logging. Returning a candidate does not select it as a
	// trust anchor.
	FetchAuthorities(ctx context.Context) ([]AuthorityCandidate, error)
}

AuthorityFetcher enumerates certificate-authority candidates from a local source such as an operating-system certificate store.

Implementations belong in platform-specific code. They should enumerate and export local certificate objects only. They should not build certificate paths, validate arbitrary remote certificates, follow AIA URLs, contact OCSP responders, download CRLs, or otherwise perform network I/O as a side effect of discovery.

The configured fetcher decides which user, machine, service, enterprise, or other store views are included. Returning a candidate does not grant trust.

type AuthorityFetcherFunc

type AuthorityFetcherFunc func(context.Context) ([]AuthorityCandidate, error)

AuthorityFetcherFunc adapts a function to AuthorityFetcher.

func (AuthorityFetcherFunc) FetchAuthorities

func (f AuthorityFetcherFunc) FetchAuthorities(
	ctx context.Context,
) ([]AuthorityCandidate, error)

FetchAuthorities calls f(ctx).

type AuthorityMatch

type AuthorityMatch uint8

AuthorityMatch selects how an AuthorityConstraint recognizes a certificate authority in a verified chain.

const (
	// MatchCertificate matches the exact DER certificate. This is the default
	// zero value and distinguishes cross-signed certificate variants.
	MatchCertificate AuthorityMatch = iota

	// MatchSPKI matches SHA-256 over SubjectPublicKeyInfo. Use it when every
	// certificate variant sharing a CA key should receive the same restriction.
	// This is broader than exact-certificate matching and should be selected
	// deliberately.
	MatchSPKI
)

func (AuthorityMatch) String

func (m AuthorityMatch) String() string

String returns a stable human-readable name for m.

type AuthorityRecord

type AuthorityRecord struct {
	// Authority is the immutable certificate value accepted by policy specs.
	Authority Authority

	// CertificateFingerprint is SHA-256 over the complete certificate DER.
	CertificateFingerprint Fingerprint

	// SPKIFingerprint is SHA-256 over DER SubjectPublicKeyInfo.
	SPKIFingerprint Fingerprint

	// Subject is the certificate subject formatted by pkix.Name.String.
	Subject string

	// Issuer is the certificate issuer formatted by pkix.Name.String.
	Issuer string

	// SerialNumber is the certificate serial number in hexadecimal.
	SerialNumber string

	// NotBefore is the beginning of the certificate validity interval.
	NotBefore time.Time

	// NotAfter is the end of the certificate validity interval.
	NotAfter time.Time

	// IsCA reports the parsed BasicConstraints CA value.
	IsCA bool

	// PublicKeyAlgorithm identifies the certificate subject's public-key type.
	PublicKeyAlgorithm x509.PublicKeyAlgorithm

	// SignatureAlgorithm identifies the algorithm that signed the certificate.
	SignatureAlgorithm x509.SignatureAlgorithm

	// Sources lists the local stores or files that reported this certificate.
	Sources []AuthoritySource
}

AuthorityRecord is a validated, display-ready certificate-authority candidate in an AuthorityCatalog.

Authority contains the immutable DER value to use when building a policy. CertificateFingerprint identifies the exact certificate, while SPKIFingerprint can group cross-signed certificate variants that share a public key. Sources contains every distinct discovery source reported for the exact DER certificate.

type AuthoritySource

type AuthoritySource struct {
	// Name identifies the store, file, domain, or other local source.
	Name string

	// Trust is the source's informational trust classification.
	Trust TrustHint

	// Metadata carries optional platform-specific, non-secret attributes.
	Metadata map[string]string
}

AuthoritySource identifies one local source from which an authority candidate was discovered.

Name should be stable and suitable for display or audit logs, for example "windows:CurrentUser\\Root", "macos:system", or a certificate-bundle path. Metadata may contain platform-specific details. FetchAuthorityCatalog copies the map, and callers should treat values returned by Records and Lookup as snapshots.

type ClientAuthMode

type ClientAuthMode uint8

ClientAuthMode selects the mTLS client-certificate requirement installed by ClientPolicy.ServerTLSConfig.

const (
	// ClientAuthNone does not request a client certificate.
	ClientAuthNone ClientAuthMode = iota

	// ClientAuthVerifyIfGiven requests a client certificate and verifies it when
	// the client supplies one, but also permits clients without a certificate.
	ClientAuthVerifyIfGiven

	// ClientAuthRequireAndVerify requires a client certificate and verifies it.
	ClientAuthRequireAndVerify
)

func (ClientAuthMode) String

func (m ClientAuthMode) String() string

String returns a stable human-readable name for m.

type ClientConnectionVerifier

type ClientConnectionVerifier interface {
	// VerifyClient inspects an mTLS client connection already accepted by the
	// compiled client policy. Returning a non-nil error aborts the TLS handshake.
	// The supplied state and certificates are read-only.
	VerifyClient(connection ClientVerification) error
}

ClientConnectionVerifier performs an additional local check after normal mTLS verification and client pins have succeeded.

Implementations can enforce application-specific certificate identities or extensions. The method may be called concurrently and on resumed sessions. Network I/O has the same routing and leakage caveats described for ServerConnectionVerifier.

type ClientConnectionVerifierFunc

type ClientConnectionVerifierFunc func(ClientVerification) error

ClientConnectionVerifierFunc adapts a function to ClientConnectionVerifier.

func (ClientConnectionVerifierFunc) VerifyClient

func (f ClientConnectionVerifierFunc) VerifyClient(
	connection ClientVerification,
) error

VerifyClient calls f(connection).

type ClientPolicy

type ClientPolicy struct {
	// contains filtered or unexported fields
}

ClientPolicy is an immutable policy used by TLS servers to authenticate mTLS clients.

A ClientPolicy is safe for concurrent use. Create one with CompileClientPolicy and attach it with ServerTLSConfig.

func CompileClientPolicy

func CompileClientPolicy(spec ClientPolicySpec) (*ClientPolicy, error)

CompileClientPolicy validates spec and returns an immutable policy.

Every trust anchor is added to an x509.NewCertPool pool. An empty TrustAnchors slice is valid and creates a fail-closed policy. Duplicate trust anchors and duplicate pins are rejected.

func (*ClientPolicy) PinCount

func (p *ClientPolicy) PinCount() int

PinCount returns the number of client pins in p. A nil policy has count zero.

func (*ClientPolicy) ServerTLSConfig

func (p *ClientPolicy) ServerTLSConfig(
	options ServerTLSOptions,
) (*tls.Config, error)

ServerTLSConfig combines p with options and returns a TLS server configuration.

The configuration always has a non-nil ClientCAs pool. For verification modes, an empty ClientPolicy therefore rejects every client chain rather than falling back to operating-system roots. At least one server certificate or a GetCertificate callback must be configured.

func (*ClientPolicy) TrustAnchorCount

func (p *ClientPolicy) TrustAnchorCount() int

TrustAnchorCount returns the number of exact client trust anchors in p. A nil policy has count zero.

func (*ClientPolicy) VerifyClient

func (p *ClientPolicy) VerifyClient(
	state tls.ConnectionState,
) (ClientVerification, error)

VerifyClient applies the explicit-root and pin checks to a normally verified mTLS client state.

Normal crypto/tls client-certificate verification must already have succeeded and populated state.VerifiedChains. The method does not build chains or perform network access.

type ClientPolicySpec

type ClientPolicySpec struct {
	// TrustAnchors is the explicit set of accepted client-certificate roots.
	TrustAnchors []Authority

	// Pins contains optional global leaf certificate or leaf SPKI pins.
	Pins []Pin
}

ClientPolicySpec describes how a TLS server authenticates mTLS client certificates.

TrustAnchors is the complete client-root set; there is no implicit system fallback. If Pins is non-empty, every presented and otherwise valid client certificate must match at least one pin. Multiple pins are alternatives, allowing overlap during certificate or key rotation.

type ClientTLSOptions

type ClientTLSOptions struct {
	// Certificates contains optional client certificates for mTLS. The slice and
	// certificate byte slices are copied. PrivateKey objects are shared and must
	// be safe for concurrent use as required by crypto/tls.
	Certificates []tls.Certificate

	// GetClientCertificate, when non-nil, selects an mTLS client certificate.
	// Returned chains should contain the leaf followed by all required
	// intermediates. The callback may be called concurrently and must not mutate
	// policy data.
	GetClientCertificate func(*tls.CertificateRequestInfo) (*tls.Certificate, error)

	// MinVersion is the minimum permitted TLS version. Zero means TLS 1.2, which
	// is the package default rather than a request to inherit a future library
	// default.
	MinVersion uint16

	// MaxVersion is the maximum permitted TLS version. Zero lets crypto/tls use
	// its current maximum.
	MaxVersion uint16

	// CipherSuites optionally limits TLS 1.0 through TLS 1.2 cipher suites. TLS
	// 1.3 cipher suites are selected by crypto/tls and are not configurable.
	CipherSuites []uint16

	// CurvePreferences optionally limits key-exchange groups.
	CurvePreferences []tls.CurveID

	// NextProtos lists application protocols for ALPN negotiation.
	NextProtos []string

	// SessionCacheSize enables a new, policy-local LRU TLS client session cache
	// with this capacity. Zero disables client session resumption. Negative
	// values are invalid. The cache is never shared with another generated
	// configuration.
	SessionCacheSize int

	// Time optionally supplies the current time to crypto/tls. It must be safe
	// for concurrent use. Nil uses time.Now.
	Time func() time.Time

	// Verifiers contains additional checks run after normal verification,
	// authority scoping, and pins. They run in order and the first error aborts
	// the handshake.
	Verifiers []ServerConnectionVerifier
}

ClientTLSOptions contains non-trust settings for a TLS client configuration.

RootCAs, ServerName, InsecureSkipVerify, and VerifyConnection are deliberately absent because the policy owns them. The returned tls.Config must not be modified after first use.

type ClientVerification

type ClientVerification struct {
	// ConnectionState is the TLS state supplied to VerifyConnection.
	ConnectionState tls.ConnectionState

	// AuthorizedChains is the non-empty subset of normally verified client
	// chains accepted by the explicit root set.
	AuthorizedChains [][]*x509.Certificate
}

ClientVerification is the result of applying a ClientPolicy to a normally verified mTLS client connection.

AuthorizedChains contains only chains whose terminal certificate was an explicit ClientPolicy trust anchor. ConnectionState.VerifiedChains is replaced with the same filtered chain set. ConnectionState and certificates must be treated as read-only.

type DNSRule

type DNSRule struct {
	// Domain is the DNS domain to match.
	Domain string

	// IncludeSubdomains extends the match to names below Domain.
	IncludeSubdomains bool
}

DNSRule matches one DNS domain in a Scope.

Domain must be an ASCII DNS A-label name and must not contain a wildcard. If IncludeSubdomains is false, only the exact domain matches. If true, both the domain and names below it match. For example, a rule for example.test with IncludeSubdomains set matches example.test and a.b.example.test, but not badexample.test.

type Fingerprint

type Fingerprint [sha256.Size]byte //nolint:recvcheck // Unmarshal mutates.

Fingerprint is a SHA-256 digest used to identify a complete certificate or a certificate's SubjectPublicKeyInfo value.

Fingerprints compare by value and are safe to use as map keys. String returns 64 lowercase hexadecimal characters without separators. ParseFingerprint also accepts colon-separated hexadecimal input commonly shown by certificate inspection tools.

func ParseFingerprint

func ParseFingerprint(value string) (Fingerprint, error)

ParseFingerprint parses a SHA-256 fingerprint.

The accepted representation is hexadecimal, with optional colon separators. Leading and trailing whitespace is ignored. Other separators and algorithms are rejected.

func (Fingerprint) MarshalText

func (f Fingerprint) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (Fingerprint) String

func (f Fingerprint) String() string

String returns f as 64 lowercase hexadecimal characters without separators.

func (*Fingerprint) UnmarshalText

func (f *Fingerprint) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

type IdentityKind

type IdentityKind uint8

IdentityKind identifies the representation used by a ServerIdentity.

const (
	// IdentityInvalid is the kind of the zero ServerIdentity value.
	IdentityInvalid IdentityKind = iota

	// IdentityDNS identifies a canonical ASCII DNS name.
	IdentityDNS

	// IdentityIP identifies an IPv4 or IPv6 address.
	IdentityIP
)

func (IdentityKind) String

func (k IdentityKind) String() string

String returns a stable human-readable name for k.

type Pin

type Pin struct {
	// contains filtered or unexported fields
}

Pin is an immutable SHA-256 leaf-certificate or leaf-SPKI pin.

A Pin is an additional condition after normal certificate-chain and hostname verification. It never replaces PKI validation. The zero value is invalid.

func NewCertificatePin

func NewCertificatePin(certificateDER []byte) (Pin, error)

NewCertificatePin parses certificateDER and returns a pin for the complete leaf certificate DER.

func NewPin

func NewPin(kind PinKind, digest Fingerprint) (Pin, error)

NewPin constructs a Pin from kind and digest.

func NewSPKIPin

func NewSPKIPin(certificateDER []byte) (Pin, error)

NewSPKIPin parses certificateDER and returns a pin for the leaf certificate's DER SubjectPublicKeyInfo value.

func (Pin) Digest

func (p Pin) Digest() Fingerprint

Digest returns p's SHA-256 digest.

func (Pin) Kind

func (p Pin) Kind() PinKind

Kind returns the part of a leaf certificate matched by p.

func (Pin) MarshalText

func (p Pin) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (Pin) Matches

func (p Pin) Matches(cert *x509.Certificate) bool

Matches reports whether p matches cert. A nil certificate or invalid pin returns false.

func (Pin) String

func (p Pin) String() string

String returns p in the form "certificate:<hex>" or "spki:<hex>". An invalid pin is rendered as "invalid:<hex>".

func (*Pin) UnmarshalText

func (p *Pin) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler. It accepts the format produced by String.

func (Pin) Valid

func (p Pin) Valid() bool

Valid reports whether p has a supported kind.

type PinKind

type PinKind uint8

PinKind selects which part of a leaf certificate a Pin authenticates.

const (
	// PinInvalid is the kind of the zero Pin value.
	PinInvalid PinKind = iota

	// PinLeafCertificate matches SHA-256 over the complete DER leaf certificate.
	// It changes whenever the certificate is renewed or reissued.
	PinLeafCertificate

	// PinLeafSPKI matches SHA-256 over the leaf certificate's DER-encoded
	// SubjectPublicKeyInfo. It can survive certificate renewal when the same key
	// is retained.
	PinLeafSPKI
)

func (PinKind) String

func (k PinKind) String() string

String returns a stable human-readable name for k.

type Scope

type Scope struct {
	// Any matches every valid server identity.
	Any bool

	// DNS contains exact-domain or domain-subtree rules.
	DNS []DNSRule

	// IPPrefixes contains IPv4 or IPv6 network prefixes. An exact address uses a
	// /32 IPv4 prefix or a /128 IPv6 prefix.
	IPPrefixes []netip.Prefix
}

Scope is a set of remote-server identities for which a rule is active.

The entries in DNS and IPPrefixes are alternatives. Any makes the scope unrestricted and cannot be combined with other entries. The zero value matches nothing and is rejected when used in a compiled policy rule.

func AnyServerScope

func AnyServerScope() Scope

AnyServerScope returns a scope that matches every valid server identity.

func NewDNSDomainScope

func NewDNSDomainScope(domain string, includeSubdomains bool) (Scope, error)

NewDNSDomainScope returns a scope for domain. When includeSubdomains is true, the scope also matches every DNS name below domain.

func NewExactServerScope

func NewExactServerScope(identity string) (Scope, error)

NewExactServerScope returns a scope that matches exactly identity.

func NewIPPrefixScope

func NewIPPrefixScope(prefix netip.Prefix) (Scope, error)

NewIPPrefixScope returns a scope containing prefix.

IPv4-mapped IPv6 prefixes and prefixes with a zone identifier are rejected; callers should normalize them to ordinary IPv4 or unzoned IPv6 values.

func (Scope) Allows

func (s Scope) Allows(identity ServerIdentity) (bool, error)

Allows reports whether s contains identity.

An invalid scope or invalid identity produces an error. A valid zero scope returns false.

func (Scope) Validate

func (s Scope) Validate() error

Validate checks that s is well formed. Unlike policy compilation, Validate permits the zero scope, which is a useful representation of “matches nothing”.

type ScopedAuthority

type ScopedAuthority struct {
	// Authority is the exact certificate to add as a trust anchor.
	Authority Authority

	// Scope selects the DNS names or IP addresses for which the anchor is valid.
	Scope Scope
}

ScopedAuthority is a certificate selected as a trust anchor only for remote servers in Scope.

The certificate is placed in an explicit application-owned x509.CertPool. Scope is enforced after normal chain and hostname verification. A zero Scope is rejected because it would make the anchor unusable.

type ScopedPin

type ScopedPin struct {
	// Pin is the certificate or SPKI digest to require.
	Pin Pin

	// Scope selects the servers for which Pin is active.
	Scope Scope
}

ScopedPin applies Pin only when Scope contains the remote server identity. If several pins apply to one server, they are alternatives: at least one matching pin is sufficient. This supports overlap during key rotation.

type ServerConnectionVerifier

type ServerConnectionVerifier interface {
	// VerifyServer inspects a connection already accepted by the compiled server
	// policy. Returning a non-nil error aborts the TLS handshake. The supplied
	// state and certificates are read-only.
	VerifyServer(connection ServerVerification) error
}

ServerConnectionVerifier performs an additional local check after normal TLS verification, scoped-authority checks, and pins have succeeded.

Implementations may validate a stapled OCSP response, enforce application certificate extensions, or consult local policy data. Implementations should not perform network I/O unless the application explicitly routes and bounds that traffic. The method may be called concurrently and on resumed sessions.

type ServerConnectionVerifierFunc

type ServerConnectionVerifierFunc func(ServerVerification) error

ServerConnectionVerifierFunc adapts a function to ServerConnectionVerifier.

func (ServerConnectionVerifierFunc) VerifyServer

func (f ServerConnectionVerifierFunc) VerifyServer(
	connection ServerVerification,
) error

VerifyServer calls f(connection).

type ServerIdentity

type ServerIdentity struct {
	// contains filtered or unexported fields
}

ServerIdentity is the expected DNS name or IP address of a remote TLS server. It is used for scoped trust-anchor, authority-constraint, and pin matching.

Values are immutable and comparable. The zero value is invalid. Use ParseServerIdentity to construct one.

func ParseServerIdentity

func ParseServerIdentity(value string) (ServerIdentity, error)

ParseServerIdentity parses a DNS name or IP address without a port.

DNS names must be ASCII A-labels. They are lowercased and one final root dot is removed. IPv6 addresses may be bracketed. IPv6 zone identifiers are rejected because they are routing details, not certificate identities.

func (ServerIdentity) DNSName

func (id ServerIdentity) DNSName() (string, bool)

DNSName returns the canonical DNS name and true when id is a DNS identity.

func (ServerIdentity) IP

func (id ServerIdentity) IP() (netip.Addr, bool)

IP returns the address and true when id is an IP identity.

func (ServerIdentity) Kind

func (id ServerIdentity) Kind() IdentityKind

Kind returns whether id contains a DNS name or an IP address.

func (ServerIdentity) MarshalText

func (id ServerIdentity) MarshalText() ([]byte, error)

MarshalText implements encoding.TextMarshaler.

func (ServerIdentity) String

func (id ServerIdentity) String() string

String returns the canonical DNS name or IP address. It returns an empty string for the zero value.

func (*ServerIdentity) UnmarshalText

func (id *ServerIdentity) UnmarshalText(text []byte) error

UnmarshalText implements encoding.TextUnmarshaler.

func (ServerIdentity) Valid

func (id ServerIdentity) Valid() bool

Valid reports whether id contains a parsed DNS name or IP address.

type ServerPolicy

type ServerPolicy struct {
	// contains filtered or unexported fields
}

ServerPolicy is an immutable policy used by TLS clients to authenticate remote servers.

A ServerPolicy is safe for concurrent use. Create one with CompileServerPolicy. Use TLSClientConfig, TLSClientConfigForServer, or NewHTTPTransport to attach it to TLS connections.

func CompileServerPolicy

func CompileServerPolicy(spec ServerPolicySpec) (*ServerPolicy, error)

CompileServerPolicy validates spec and returns an immutable policy.

Every trust anchor is added to a pool created with x509.NewCertPool. An empty TrustAnchors slice is valid and creates a fail-closed policy that trusts no server chain. Duplicate exact trust anchors and duplicate constraint keys are rejected to make policy mistakes visible.

func (*ServerPolicy) PinCount

func (p *ServerPolicy) PinCount() int

PinCount returns the number of scoped pins in p. A nil policy has count zero.

func (*ServerPolicy) TLSClientConfig

func (p *ServerPolicy) TLSClientConfig(
	options ClientTLSOptions,
) (*tls.Config, error)

TLSClientConfig returns a TLS client configuration for a transport that sets tls.Config.ServerName separately for every connection.

This form is appropriate for net/http.Transport and similar multiplexing transports that clone the configuration and set ServerName for each connection. For direct tls.Client or tls.Dial use, call TLSClientConfigForServer instead. Verification fails with ErrMissingServerIdentity when no name is available.

The dynamic form supports DNS hostnames, but not IP-literal destinations. crypto/tls uses an IP-valued Config.ServerName for SAN verification while intentionally omitting it from SNI; ConnectionState.ServerName is therefore empty and does not provide this callback with the expected IP. Use TLSClientConfigForServer for an IP-literal resource.

func (*ServerPolicy) TLSClientConfigForServer

func (p *ServerPolicy) TLSClientConfigForServer(
	serverName string,
	options ClientTLSOptions,
) (*tls.Config, error)

TLSClientConfigForServer returns a TLS client configuration fixed to serverName.

serverName may be an ASCII DNS A-label or an IP address without a port. The returned config sets tls.Config.ServerName, so crypto/tls performs normal DNS or IP SAN verification before the policy callback runs.

func (*ServerPolicy) TrustAnchorCount

func (p *ServerPolicy) TrustAnchorCount() int

TrustAnchorCount returns the number of exact trust-anchor certificates in p. A nil policy has count zero.

func (*ServerPolicy) VerifyServer

func (p *ServerPolicy) VerifyServer(
	identity ServerIdentity,
	state tls.ConnectionState,
) (ServerVerification, error)

VerifyServer applies scoped-authority and pin checks to state for identity.

Normal crypto/tls verification must already have succeeded and populated state.VerifiedChains. The method does not build chains or perform any network access. It does repeat leaf hostname or IP verification against identity so a direct caller cannot accidentally apply a scoped policy to a chain verified for another resource. Policy-generated TLS configurations call this method from tls.Config.VerifyConnection.

type ServerPolicySpec

type ServerPolicySpec struct {
	// TrustAnchors is the explicit, server-scoped root CA set.
	TrustAnchors []ScopedAuthority

	// Constraints restrict recognized CA certificates wherever they occur above
	// the leaf in an otherwise verified chain.
	Constraints []AuthorityConstraint

	// Pins contains resource-scoped leaf certificate or SPKI pins.
	Pins []ScopedPin
}

ServerPolicySpec describes how TLS clients authenticate remote servers.

TrustAnchors is the complete root set; there is never an implicit system-root fallback. Constraints can further restrict roots and intermediates. Pins are additional leaf requirements after PKI and hostname verification.

type ServerTLSOptions

type ServerTLSOptions struct {
	// Certificates contains the server certificate chains. The first certificate
	// in each chain must be the leaf, followed by all required intermediates; the
	// root should normally be omitted. Certificate byte slices are copied.
	Certificates []tls.Certificate

	// GetCertificate optionally selects a server certificate from ClientHello.
	// Returned chains should contain the leaf followed by all required
	// intermediates and normally omit the root.
	GetCertificate func(*tls.ClientHelloInfo) (*tls.Certificate, error)

	// ClientAuth selects whether an mTLS client certificate is optional,
	// required, or not requested.
	ClientAuth ClientAuthMode

	// MinVersion is the minimum permitted TLS version. Zero means TLS 1.2.
	MinVersion uint16

	// MaxVersion is the maximum permitted TLS version. Zero lets crypto/tls use
	// its current maximum.
	MaxVersion uint16

	// CipherSuites optionally limits TLS 1.0 through TLS 1.2 cipher suites.
	CipherSuites []uint16

	// CurvePreferences optionally limits key-exchange groups.
	CurvePreferences []tls.CurveID

	// NextProtos lists application protocols for ALPN negotiation.
	NextProtos []string

	// SessionTicketsDisabled disables server-side TLS session tickets and PSK
	// resumption. VerifyConnection runs on resumptions, so disabling tickets is
	// not required for policy enforcement, but may be appropriate operationally.
	SessionTicketsDisabled bool

	// Time optionally supplies the current time to crypto/tls. Nil uses time.Now.
	Time func() time.Time

	// Verifiers contains additional checks for a presented and normally verified
	// client certificate. Verifiers are not called when ClientAuthVerifyIfGiven
	// permits a connection without a certificate.
	Verifiers []ClientConnectionVerifier
}

ServerTLSOptions contains non-trust settings for a TLS server configuration.

ClientCAs, ClientAuth, and VerifyConnection are owned by ClientPolicy and ClientAuthMode. The returned tls.Config must not be modified after first use.

type ServerVerification

type ServerVerification struct {
	// Identity is the canonical remote-server identity used for policy matching.
	Identity ServerIdentity

	// ConnectionState is the TLS state supplied to VerifyConnection.
	ConnectionState tls.ConnectionState

	// AuthorizedChains is the non-empty subset of normally verified chains
	// permitted by the policy.
	AuthorizedChains [][]*x509.Certificate
}

ServerVerification is the result of applying a ServerPolicy to a normally verified TLS connection.

AuthorizedChains contains only the chains that satisfy all scoped authority rules. ConnectionState.VerifiedChains is replaced with the same filtered chain set. ConnectionState and the certificates in AuthorizedChains must be treated as read-only. The outer and inner chain slices are copies, but the x509.Certificate objects are owned by crypto/tls.

type TrustHint

type TrustHint uint8

TrustHint describes how the source store characterized a discovered certificate.

A TrustHint is informational. CompileServerPolicy and CompileClientPolicy do not consult it. Selecting a discovered certificate as an application trust anchor must always be an explicit policy decision.

const (
	// TrustHintUnknown means the fetcher could not express the source store's
	// trust semantics as one of the other hints.
	TrustHintUnknown TrustHint = iota

	// TrustHintAnchor means the source presented the certificate as a candidate
	// trust anchor. Platform-specific restrictions may still apply and are not
	// represented by this value alone.
	TrustHintAnchor

	// TrustHintDistrusted means the source explicitly distrusted the certificate.
	// User interfaces should not preselect such a certificate as an anchor.
	TrustHintDistrusted

	// TrustHintRestricted means trust depends on usage, policy, hostname,
	// application, key usage, or other source-specific constraints.
	TrustHintRestricted
)

func (TrustHint) String

func (h TrustHint) String() string

String returns a stable human-readable name for h.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL