Verify a release¶
Verify a detached OpenPGP signature over a checksums manifest against a public
key you embed at build time. This is the surface a consumer such as afmpeg
uses.
For a step-by-step introduction, see Sign and verify a release; this guide is the task-focused recipe.
Embed the publisher's key¶
Bake the publisher's armoured public key into your binary so verification needs no network access and no external trust store:
Verify the manifest¶
Load the key into a TrustSet and verify. A nil error means the manifest was
signed by a key in the set and has not been altered.
trust, err := verify.LoadTrustSet(releaseKey)
if err != nil {
return err // e.g. verify.ErrWeakKey — see below
}
if err := trust.VerifyManifestSignature(manifest, signature); err != nil {
return err // verification failed — do not trust the assets
}
// verified: now check each asset's checksum against the manifest
manifest and signature are the bytes you downloaded alongside the release
(signature is the ASCII-armoured detached signature produced by
openpgpkey.DetachSign).
Verifying the signature establishes that the manifest is authentic. You still hash each downloaded asset and compare it to the manifest line to confirm the asset itself is intact.
Get the signer fingerprint¶
When you need to know which key signed — for logging, audit, or pinning to a
specific rotation — use the Signer variant. It returns the signer's
fingerprint on success:
fingerprint, err := trust.VerifyManifestSignatureSigner(manifest, signature)
if err != nil {
return err
}
log.Printf("release signed by %s", fingerprint)
On failure the returned fingerprint is empty. To enumerate every key the trust set will accept (for example, to display pinned anchors at startup), call:
Fingerprints are returned as sorted upper-case hex, 40 characters each.
The fingerprint you get back is always the primary key's, even when a signing subkey produced the signature. Pinning it therefore pins the identity, not the particular subkey that signed — which is usually what you want, since subkeys rotate under a stable primary.
Handle a weak key¶
LoadTrustSet enforces the minimum-strength policy at construction time. An RSA
key below 3072 bits fails fast with verify.ErrWeakKey:
trust, err := verify.LoadTrustSet(releaseKey)
if errors.Is(err, verify.ErrWeakKey) {
return fmt.Errorf("embedded release key is too weak to trust: %w", err)
}
This is a build-time fact about your embedded key, so it should surface during development, not in the field. See the strength policy for the rationale.
Handle verification failure¶
VerifyManifestSignature returns a non-nil error whenever trust cannot be
established — a tampered manifest, a signature from a key outside the set, or a
malformed/empty signature. Treat any error as "do not trust" and refuse the
release:
if err := trust.VerifyManifestSignature(manifest, signature); err != nil {
return fmt.Errorf("refusing release, signature did not verify: %w", err)
}
Never proceed to install or use the assets when verification fails — fail closed.
What a single error value is hiding¶
ErrSignatureInvalid is deliberately broad. It covers a forged signature, a
malformed or empty one, a signature from a key outside the set — and also
everything go-crypto decides about the key material itself: a revoked primary
key, a revoked identity, a revoked or expired signing subkey, an expired key,
an expired signature, and an RSA key below the strength floor. Expiry is judged
against the current clock at the moment you verify.
The underlying reason is folded into the message text, not the error chain, so
errors.Is against a go-crypto sentinel will not match. If you need to tell
"your key expired last week" from "someone forged this", read the message —
and refuse the release either way.
Nothing here names which key rejected the signature. That is deliberate: a caller logging only the sentinel leaks nothing about the trust set.
See also¶
- Configure trust — add an independent WKD anchor so a single embedded key is not your only source of truth.
- Errors — every sentinel and what raises it.
- The trust model — what verification does and does not guarantee.
- What this module does not do — in particular, that checking asset hashes against the manifest is your job.
- The full API and runnable
Exampletests: pkg.go.dev/gitlab.com/phpboyscout/go/signing/verify.