Skip to content

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 Getting started; 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:

//go:embed keys/release.asc
var releaseKey []byte

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:

for _, fp := range trust.Fingerprints() {
    log.Printf("trusted signer: %s", fp)
}

Fingerprints are returned as sorted upper-case hex.

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.

See also