Skip to content

signing

OpenPGP/WKD release signing and verification as a light, dependency-inverted Go library.

signing is the same trust model that backs gtb update, extracted so any project can verify signed releases — or sign its own — without pulling in the go-tool-base framework.

import "gitlab.com/phpboyscout/go/signing/verify"

The light-footprint promise

The module graph is deliberately tiny. Outside its tests, go.mod declares three dependencies: go-crypto, cockroachdb/errors, and golang.org/x/crypto for the BLAKE2b hash the minisign package needs. No cloud SDK, no web framework, no go-tool-base — a guard test fails the build if one appears.

A verify-only consumer imports verify, and reaches all three. The direct x/crypto requirement is minisign's BLAKE2b, but go-crypto's own OpenPGP code pulls argon2, cast5, hkdf and sha3 out of the same module whether minisign is imported or not.

Heavy or remote signing backends (AWS KMS, GCP, Azure, an HSM) are defined as an interface and injected by the consumer, so they never enter your dependency graph unless you opt in. See Why backends are injected.

The library leans on the standard library at every seam:

  • crypto.Signer for keys (works with an in-memory RSA key or a remote KMS handle),
  • *slog.Logger for logging (nil disables it),
  • *http.Client for WKD fetches (nil uses a stdlib client with a 30s timeout).

Who it is for

  • Consumers verifying signed releases — e.g. afmpeg verifying the signed wasm release assets published by ffmpeg-wasi.
  • Any non-framework Go project that wants release-asset integrity without adopting a CLI framework.
  • Publishers signing their own release manifests via the reference local backend or a custom KMS/HSM backend.
  • Publishers whose consumers are Rust toolscargo-binstall and rtb-update verify minisign signatures over individual artefacts, which the minisign package produces.

Verify a release in ten lines

priv, _ := rsa.GenerateKey(rand.Reader, 3072)
now := time.Unix(0, 0)
pub, _ := openpgpkey.ArmoredPublicKey(priv, "Release", "release@example.test", now)
manifest := []byte("sha256  ffmpeg.wasm  0xc0ffee\n")
sig, _ := openpgpkey.DetachSign(priv, pub, bytes.NewReader(manifest), now)

trust, _ := verify.LoadTrustSet(pub)
err := trust.VerifyManifestSignature(manifest, sig) // nil == verified

In production you embed the publisher's armoured public key at build time rather than generating one; the verification call is identical.

Where to go next

The documentation follows the Diátaxis framework:

The generated Go API — signatures, doc comments and runnable Example tests — lives on pkg.go.dev. Each backend module has its own there too, for instance signing-aws-kms.

Further reading

The blog carries a curated route through this subject: Signing your releases collects everything written about it, ordered so you can start at the beginning rather than newest-first.

Ask phpbotscout

phpbotscout

He answers questions about the projects over on the Discord, citing the docs where they already cover it, and offering to raise an issue where they don't. Bring a bug, an idea, or a questionable engineering decision.

Join the Discord