Skip to content

Limits, defaults and tunables

Every numeric threshold and package-level default the module declares, with its value and — importantly — whether this module actually acts on it.

The key strength floor

A key entering a trust set must be one of:

  • Ed25519, in either OpenPGP packet form (the legacy EdDSA algorithm 22, or the modern Ed25519 algorithm), at any size; or
  • RSA, sign-capable or general, at 3072 bits or more.

Everything else is refused with ErrWeakKey. The rejected list is explicit — DSA, ElGamal, ECDH, ECDSA, X25519, X448, Ed448, and RSA-encrypt-only — and a final catch-all rejects any algorithm a future go-crypto release introduces, so an unknown algorithm fails closed rather than slipping in.

The floor is 3072 bits, not configurable, and not exported. It is applied twice: once when the trust set is constructed, and again at verification time through packet.Config.MinRSABits, so a weak key cannot validate a signature even if it reached the trust set by some path that skipped construction.

Signing-capable subkeys are checked too. When a subkey's binding signature carries valid key flags, the Sign flag decides whether it is checked; when the flags are absent the subkey is treated as signing-capable and checked anyway. A weak subkey under a strong primary is refused.

Size caps on untrusted input

Both are exported vars in verify, so a tool author with an unusual release layout can raise them.

Variable Value Enforced by this module?
verify.MaxWKDResponseSize 65536 (64 KiB) Yes. The WKD fetch reads at most this many bytes and returns ErrWKDResponseTooLarge beyond it.
verify.MaxSignatureSize 8192 (8 KiB) No. Nothing in this module reads it. It documents the cap a downstream release-fetcher should apply to a detached-signature download; a GPG detached signature is typically 400–800 bytes.

openpgpkey.DetachSign has no size cap: it reads its whole io.Reader into memory. Bound the reader yourself when the input is not something you produced.

Compile-time defaults this module does not read

verify declares four Default* variables. They exist so a tool author can set enforcement policy in main() for a downstream self-updater. Nothing in this module reads any of them — in particular, setting DefaultKeySource does not change what BuildKeyResolver does with an empty KeySource (that independently defaults to "both").

Variable Value What it is for
DefaultRequireSignature false Whether a downstream updater refuses an unsigned release. Left off until a project has shipped an embedded key in a prior release, so existing installs are not locked out.
DefaultKeySource "both" The key-source mode a downstream updater starts from.
DefaultRequireExternalCrosscheck false Whether a downstream updater treats a WKD outage as fatal.
DefaultExternalKeyEmail "" The release address a downstream updater derives its WKD URL from.

If you are wiring this module directly, ignore these four and populate KeyResolverConfig instead.

All six variables above are var, not const, so they are mutable at run time and process-global. Nothing synchronises access to them: set them during initialisation, before any goroutine reads them.

Network defaults

Setting Value Notes
Default WKD HTTP timeout 30 seconds Applied only when KeyResolverConfig.HTTPClient is nil. NewWKDResolver requires a client and substitutes nothing.
Redirect policy Go's default The default client sets no CheckRedirect, so up to ten redirects are followed and a redirect from the WKD host to an http:// URL will be followed. The https:// check applies to the initial URL only. Inject a client with a redirect policy if that matters to you.
TLS floor Go's default Nothing here raises the minimum TLS version or pins certificates. Inject a hardened client to do so.
WKD fallback advanced then direct The direct URL is tried only when the advanced URL returns HTTP 404. Any other failure — network, TLS, non-200, oversize, weak key — is returned without trying the direct URL.
Caching none Resolve performs its I/O on every call. Cache above the resolver if you need to.

minisign limits

Constant Value Notes
minisign.KeyIDLen 8 Bytes of key identifier, derived as SHA-256(public_key)[:8].
minisign.MaxUntrustedComment 1024 The reference implementation's COMMENTMAXBYTES.
minisign.MaxTrustedComment 8192 The reference implementation's TRUSTEDCOMMENTMAXBYTES.

Both limits are counted in bytes, not runes, and are enforced here because nothing downstream enforces them: neither the minisign-verify crate that cargo-binstall uses nor jedisct1's Rust minisign crate applies any comment length check. An over-long comment would therefore verify everywhere this module is tested and then fail against upstream minisign -V.

The trusted-comment limit is rarely the binding one in practice. The global signature's message is the artefact signature concatenated with the trusted comment, and an HSM caps its signing message at 4096 bytes, so a KMS-backed signer runs out of room first.

Signing costs exactly two signer calls — one over the 64-byte artefact digest, one over the global signature's message. Budget for both when signing a wide target matrix against a metered KMS.

WKD output file permissions

WriteWKDTree writes files 0644 and directories 0755. That is deliberate: a Web Key Directory exists to be served publicly, so world-readable is the correct mode rather than an oversight.