verifier

package
v0.0.43 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: Apache-2.0 Imports: 37 Imported by: 2

Documentation

Overview

Package verifier verifies Sigstore signatures and attestations on OCI artifacts.

Two entry points cover the two trust flows:

  • New builds a Sigstore verifier with a live TUF-refreshed trust root for verifying MCP server images against registry-declared provenance (Sigstore.VerifyServer).
  • RetrieveBundles, VerifyBundle, VerifyBundleWithKey, and VerifyBundleOffline expose the bundle-level building blocks for consumers that manage their own trust decisions — retrieving the bundles attached to an artifact, verifying them against keyless (Fulcio) or key-pair material, binding an expected Identity into the verification policy, and re-verifying stored bundles offline against the embedded trust root.

Verifying a retrieved bundle

bundles, err := verifier.RetrieveBundles(ctx, imageRef, keychain)
// handle err; errors.Is(err, verifier.ErrNoBundles) means unsigned, and
// errors.Is(err, verifier.ErrSignatureArtifactMismatch) means a signature
// was found that does not cover this artifact — a different verdict
tm, _ := verifier.OfflineTrustedMaterial()
opts, _ := verifier.DefaultVerifierOptions()
result, err := verifier.VerifyBundle(bundles[0], tm, nil, opts...)
// errors.Is(err, verifier.ErrVerificationFailed) means signed but invalid
identity, _ := verifier.IdentityFromResult(result)
// store bundles[0].Raw and identity; later:
_, err = verifier.VerifyBundleOffline(storedRaw, "sha256:"+artifactHex, &identity)

Key-signed bundles (Bundle.HasCertificate() == false, the "cosign sign --key" layout) carry no certificate identity; verify them with VerifyBundleWithKey at retrieval time and VerifyBundleOfflineWithKey for stored Raw bytes — both take the signer's PEM public key.

What a bundle is bound to

Every Bundle — retrieved, in memory, or stored — is bound to the ARTIFACT's own manifest digest, and that is the digest the offline entry points take. Callers never handle the digest of the blob a signature covers.

That distinction is not cosmetic. Two layouts are supported and they bind differently:

  • An attestation (OCI 1.1 referrer) bundle carries an in-toto statement whose subject is the artifact digest. It is bound structurally.
  • A cosign signature covers a "simple signing" payload blob, and it is that payload — not the signature — that names the artifact, in critical.image.docker-manifest-digest. Verifying the signature alone proves someone signed something; the payload is what says what. Retrieval, verification, and offline re-verification all check it, and a payload naming a different artifact fails with ErrSignatureArtifactMismatch.

Because that payload is the binding, it has to survive storage: Bundle.Raw for a cosign signature is a small envelope carrying the Sigstore bundle together with the payload (see StoredBundleMediaType). Attestation bundles need no payload and are stored as plain Sigstore bundle JSON. Callers can treat Raw as opaque either way — DecodeStoredBundle reads both.

Stability

This package is Alpha stability. The API may change without notice.

Package verifier provides a client for verifying artifacts using sigstore

Index

Constants

View Source
const (
	MediaTypeOCIEmptyV1JSON            = "application/vnd.oci.empty.v1+json"
	MediaTypeCosignSimpleSigningV1JSON = "application/vnd.dev.cosign.simplesigning.v1+json"
	MediaTypeSigstoreBundleV03JSON     = "application/vnd.dev.sigstore.bundle.v0.3+json"
)

OCI and Sigstore media type constants used when inspecting referrer manifests.

View Source
const (
	// TrustedRootSigstoreGitHub is the GitHub trusted root repository for sigstore (used for private repos, Enterprise)
	TrustedRootSigstoreGitHub = "tuf-repo.github.com"
	// TrustedRootSigstorePublicGoodInstance is the public trusted root repository for sigstore
	TrustedRootSigstorePublicGoodInstance = "tuf-repo-cdn.sigstore.dev"
)
View Source
const CosignSignatureType = "cosign container image signature"

CosignSignatureType is the value cosign writes into a simple-signing payload's "critical.type" field. A payload carrying anything else is not a container image signature and must not be accepted as one.

View Source
const DigestAlgorithmSHA256 = "sha256"

DigestAlgorithmSHA256 is the digest algorithm name used throughout the Sigstore bundle formats this package handles.

View Source
const StoredBundleMediaType = "application/vnd.toolhive.signature-bundle.v1+json"

StoredBundleMediaType identifies the JSON envelope this package persists for a cosign simple-signing signature.

A Sigstore bundle alone cannot express what a simple-signing signature binds to. The signature covers the simple-signing payload blob, and it is the payload — not the bundle — that names the artifact, via critical.image.docker-manifest-digest. The bundle protobuf has nowhere to put those payload bytes: a MessageSignature carries only a digest and a signature. So a bundle stored on its own is verifiable but unbindable — it proves someone signed *something*, with no way left to check what.

The envelope closes that gap by persisting the payload next to the bundle:

{
  "mediaType": "application/vnd.toolhive.signature-bundle.v1+json",
  "bundle": { ...canonical Sigstore bundle JSON... },
  "simpleSigningPayload": "<base64 of the simple-signing payload>"
}

Bundles that bind to the artifact structurally — attestation (referrer) bundles, whose in-toto subject is the artifact digest — need no payload and are persisted as bare Sigstore bundle JSON, unchanged. DecodeStoredBundle accepts both shapes, so callers hold one opaque blob either way.

Variables

View Source
var (
	// ErrProvenanceNotFoundOrIncomplete is returned when there's no provenance info (missing .sig or attestation) or
	// has incomplete data
	ErrProvenanceNotFoundOrIncomplete = errors.New("provenance not found or incomplete")

	// ErrProvenanceServerInformationNotSet is returned when the provenance information for a server is not set
	ErrProvenanceServerInformationNotSet = errors.New("provenance server information not set")

	// ErrImageNotSigned is returned when no signatures or attestations are found for the image
	ErrImageNotSigned = errors.New("image is not signed")

	// ErrProvenanceMismatch is returned when the image is signed but no bundle matches the expected provenance
	ErrProvenanceMismatch = errors.New("image provenance does not match")

	// MaxAttestationsBytesLimit is the maximum number of bytes we're willing to read from the attestation endpoint
	// We'll limit this to 10mb for now
	MaxAttestationsBytesLimit int64 = 10 * 1024 * 1024
)
View Source
var ErrNoBundles = errors.New("no sigstore bundles found for artifact")

ErrNoBundles is returned by RetrieveBundles when the artifact carries no Sigstore signature or attestation in any supported layout — keyless (certificate-bearing), key-signed ("cosign sign --key"), or attestation — i.e. the artifact is unsigned as far as this package can tell.

View Source
var ErrSignatureArtifactMismatch = errors.New("signature does not bind to this artifact")

ErrSignatureArtifactMismatch is returned when a cosign simple-signing signature was found for an artifact but the signed payload does not bind to that artifact: the payload's critical.type is not a container image signature, or its critical.image.docker-manifest-digest names a different artifact.

This is deliberately distinct from ErrNoBundles / ErrProvenanceNotFoundOrIncomplete. Cosign signatures are discovered at the mutable "sha256-<hex>.sig" tag, so a signature being present says nothing about which artifact it covers — and "a signature that does not cover this artifact" is a materially different verdict from "no signature at all". Collapsing the two would report a rejected signature as merely unsigned, which is the weaker and more easily ignored answer.

View Source
var ErrVerificationFailed = errors.New("sigstore bundle verification failed")

ErrVerificationFailed wraps every cryptographic verification failure returned by the VerifyBundle* functions, so callers can distinguish "signed but failed verification" from malformed input with errors.Is instead of matching sigstore-go's (unstable) error strings.

Functions

func DefaultVerifierOptions added in v0.0.35

func DefaultVerifierOptions() ([]verify.VerifierOption, error)

DefaultVerifierOptions returns the verifier options matching the Sigstore public-good instance trust root (SCT, transparency log, and observer timestamp requirements). Pass these to VerifyBundle together with OfflineTrustedMaterial (or the live public-good root).

func EncodeStoredBundle added in v0.0.43

func EncodeStoredBundle(bundleJSON, simpleSigningPayload []byte) ([]byte, error)

EncodeStoredBundle returns the durable form of a Sigstore bundle: the form VerifyBundleOffline and VerifyBundleOfflineWithKey accept, and the form Bundle.Raw carries.

bundleJSON is the canonical Sigstore bundle JSON. simpleSigningPayload is the cosign simple-signing payload the bundle's signature covers, or nil for a bundle that binds to the artifact without one (an attestation bundle). With no payload the bundle JSON is returned unchanged; with one, it is wrapped in the StoredBundleMediaType envelope.

func OfflineTrustedMaterial added in v0.0.35

func OfflineTrustedMaterial() (root.TrustedMaterial, error)

OfflineTrustedMaterial returns trusted material for the Sigstore public-good instance built entirely from the trusted root embedded in this package — no network access, no TUF refresh. The embedded root is a point-in-time snapshot: key rotations in the public-good instance require a package update to pick up. This cuts both ways — newly rotated-in keys are unknown (verification of fresh signatures fails until the snapshot is updated), and a key rotated out BECAUSE OF COMPROMISE keeps being trusted here until a new release ships and consumers bump. Callers that need live freshness or timely compromise revocation should use New (which performs a TUF fetch) instead; offline verification trades that for hermeticity. See tufroots/README.md for the snapshot's provenance.

func PublicKeyMaterial added in v0.0.35

func PublicKeyMaterial(pubKeyPEM []byte) (root.TrustedMaterial, error)

PublicKeyMaterial returns trusted material that verifies bundles signed with the private counterpart of the given PEM-encoded public key (the cosign key-pair flow, as opposed to keyless/Fulcio). The key is trusted without validity-period bounds: key-signed bundles carry no certificate whose lifetime could scope it.

func VerifyBundle added in v0.0.35

func VerifyBundle(
	b Bundle,
	tm root.TrustedMaterial,
	expected *Identity,
	verifierOpts ...verify.VerifierOption,
) (*verify.VerificationResult, error)

VerifyBundle verifies a retrieved bundle against the given trusted material, binding it to the artifact digest the bundle carries (Bundle.DigestAlgo/DigestHex). For a cosign simple-signing bundle that means both links of the chain: the signature is checked against the payload blob it covers, and the payload is checked to name this artifact — a payload naming a different artifact fails with ErrSignatureArtifactMismatch, not success.

When expected is non-nil, the identity is bound into the Sigstore verification policy itself (certificate SAN and issuer must match) rather than compared after the fact; a nil expected — the trust-on-first-use case — verifies the chain of trust only, and the caller records the identity from the returned result.

verifierOpts configure the verifier and MUST match the trusted material: pass DefaultVerifierOptions() with public-good material (SCT + transparency log + observer timestamps), and verify.WithNoObserverTimestamps() with PublicKeyMaterial (key-signed bundles carry no certificate transparency or Fulcio timestamps). Requiring the options explicitly prevents public-good defaults being fed to a different root, which surfaces as confusing sigstore-go internals rather than a clear mismatch.

func VerifyBundleOffline added in v0.0.35

func VerifyBundleOffline(
	rawBundle []byte,
	artifactDigest string,
	expected *Identity,
) (*verify.VerificationResult, error)

VerifyBundleOffline re-verifies a stored bundle (the Bundle.Raw form produced by RetrieveBundles or a signing flow) against the ARTIFACT digest, given as "sha256:<hex>", using only the embedded trusted root — no network. See OfflineTrustedMaterial for the freshness trade-off. expected behaves as in VerifyBundle.

artifactDigest is the artifact's own manifest digest: the value a caller already has from a lock file, a registry entry, or a registry resolution. It is deliberately NOT the digest of the blob a cosign signature covers — callers do not need to know that such a blob exists. Where one does, the stored form carries it (see StoredBundleMediaType) and this function checks that it names artifactDigest, failing with ErrSignatureArtifactMismatch if it names something else.

func VerifyBundleOfflineWithKey added in v0.0.35

func VerifyBundleOfflineWithKey(
	rawBundle []byte,
	artifactDigest string,
	pubKeyPEM []byte,
) (*verify.VerificationResult, error)

VerifyBundleOfflineWithKey re-verifies a stored key-signed bundle (the Bundle.Raw form of a bundle whose HasCertificate is false) against the ARTIFACT digest ("sha256:<hex>") and the given PEM public key. The digest contract is the same as VerifyBundleOffline's. Key verification needs no trust root or network in the first place; this entry point only adds the parse step for stored bundles.

func VerifyBundleWithKey added in v0.0.35

func VerifyBundleWithKey(b Bundle, pubKeyPEM []byte) (*verify.VerificationResult, error)

VerifyBundleWithKey verifies a bundle signed with a plain key pair (the cosign --key flow) against the given PEM public key, binding it to the artifact digest the bundle carries exactly as VerifyBundle does. Key-signed bundles carry no certificate, so there is no identity to bind — trust is the key itself — and no transparency-log or timestamp material to require.

Types

type Bundle added in v0.0.35

type Bundle struct {
	// Parsed is the decoded bundle.
	Parsed *bundle.Bundle
	// Raw is the bundle's durable serialization, suitable for storage and
	// later offline re-verification. It is bare Sigstore bundle JSON when
	// SimpleSigningPayload is empty, and the StoredBundleMediaType envelope
	// (bundle plus payload) when it is not — see EncodeStoredBundle.
	Raw []byte
	// DigestAlgo is the algorithm of the artifact digest the bundle binds
	// (e.g. "sha256").
	DigestAlgo string
	// DigestHex is the hex-encoded artifact manifest digest the bundle
	// binds. This is the artifact's digest, NOT the digest of the blob the
	// signature covers; for the cosign simple-signing layout those differ.
	DigestHex string
	// SimpleSigningPayload is the cosign simple-signing payload the
	// signature covers, for bundles reconstructed from a cosign signature
	// manifest (the "sha256-<hex>.sig" tag). It is empty for attestation
	// (referrer) bundles, whose in-toto subject already names the artifact.
	//
	// It is not decoration: the payload is the only thing tying such a
	// signature to an artifact, since the signature itself commits to these
	// bytes and nothing else. Verification checks the signature against
	// their digest AND checks that they name DigestHex.
	SimpleSigningPayload []byte
}

Bundle is a Sigstore bundle retrieved for an artifact, in both parsed and serialized form.

What a Bundle is bound to

A Bundle is bound to the ARTIFACT digest, in every layout and both online and offline: DigestAlgo/DigestHex are the artifact's own manifest digest, and Raw round-trips through VerifyBundleOffline (or VerifyBundleOfflineWithKey for the key-signed layout — HasCertificate tells the two apart) against that same artifact digest. Callers never need to know that a cosign signature actually covers a payload blob rather than the artifact; SimpleSigningPayload carries whatever is needed to resolve that internally.

func DecodeStoredBundle added in v0.0.43

func DecodeStoredBundle(raw []byte, artifactDigest string) (Bundle, error)

DecodeStoredBundle parses the durable form produced by EncodeStoredBundle (equivalently, Bundle.Raw) and binds it to artifactDigest, given as "<algorithm>:<hex>".

artifactDigest is the ARTIFACT's own manifest digest — the value a caller naturally has from a lock file, a registry entry, or a `remote.Get`. It is never the digest of a simple-signing payload; the returned Bundle carries the payload itself when one is needed, so callers never handle payload digests.

Both persisted shapes are accepted: the StoredBundleMediaType envelope, and bare Sigstore bundle JSON.

func RetrieveBundles added in v0.0.35

func RetrieveBundles(ctx context.Context, imageRef string, keychain authn.Keychain) ([]Bundle, error)

RetrieveBundles fetches the Sigstore bundles attached to imageRef, trying both layouts this package understands: attestation manifests (the OCI 1.1 referrers API) and a cosign-style signature manifest (the "sha256-<hex>.sig" tag). Every returned Bundle is bound to imageRef's own manifest digest — see Bundle.

It returns ErrNoBundles when the artifact has no discoverable signature material — the caller's signal that the artifact is unsigned — and ErrSignatureArtifactMismatch when signature material WAS found but does not cover this artifact. Those are different answers and callers should treat them differently; a mismatch in particular must not be softened into "unsigned".

func (Bundle) HasCertificate added in v0.0.35

func (b Bundle) HasCertificate() bool

HasCertificate reports whether the bundle carries a signing certificate — i.e. it came from a keyless (Fulcio) flow and verifies with VerifyBundle; a false result is the key-signed layout, verifying with VerifyBundleWithKey / VerifyBundleOfflineWithKey.

type Identity added in v0.0.35

type Identity struct {
	// SignerIdentity is the certificate's subject identity. For
	// certificates issued through GitHub Actions tokens this is the
	// workflow path relative to the repository (see
	// signerIdentityFromCertificate); otherwise it is the certificate SAN
	// verbatim (a URI, email, or SPIFFE ID).
	SignerIdentity string
	// CertIssuer is the OIDC issuer that authenticated the signer.
	CertIssuer string
	// SourceRepositoryURI is the source repository recorded in the Fulcio
	// certificate extensions, when present.
	SourceRepositoryURI string
}

Identity is the signer identity extracted from a verified Sigstore bundle.

func IdentityFromResult added in v0.0.35

func IdentityFromResult(r *verify.VerificationResult) (Identity, error)

IdentityFromResult extracts the signer Identity from a verification result.

type Result

type Result struct {
	IsSigned   bool `json:"is_signed"`
	IsVerified bool `json:"is_verified"`
	verify.VerificationResult
}

Result is the result of the verification

type Sigstore

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

Sigstore is the sigstore verifier

func New

func New(provenance *registry.Provenance, keychain authn.Keychain) (*Sigstore, error)

New creates a new Sigstore verifier

func (*Sigstore) GetVerificationResults

func (s *Sigstore) GetVerificationResults(
	imageRef string,
) ([]*verify.VerificationResult, error)

GetVerificationResults returns the verification results for the given image reference

func (*Sigstore) VerifyServer

func (s *Sigstore) VerifyServer(imageRef string, provenance *registry.Provenance) error

VerifyServer verifies the server information for the given image reference

func (*Sigstore) WithKeychain

func (s *Sigstore) WithKeychain(keychain authn.Keychain) *Sigstore

WithKeychain sets the keychain for authentication

Jump to

Keyboard shortcuts

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