Documentation
¶
Overview ¶
Package verify implements the §6 consumer verification contract for grc.store-sourced plugins: keyless signature verification over a pinned public-good Sigstore trusted root, an identity policy (camp (b) TOFU), and the full digest-chain walk index → child → config/layer → bytes. Everything fails closed — a verification or digest failure ABORTS the install; the path never degrades to an unverified copy.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrUnsigned: the index carries no signature referrer. ErrUnsigned = errors.New("plugin index is not signed") // ErrSignatureInvalid: a present signature failed cryptographic verification // (bad chain, missing SCT, no Rekor inclusion, digest mismatch, etc.). ErrSignatureInvalid = errors.New("plugin signature verification failed") // ErrIdentityMismatch: the signature is valid but the signer identity does // not satisfy the policy (TOFU pin mismatch, or an unexpected SAN). ErrIdentityMismatch = errors.New("plugin signer identity does not match the pinned identity") // ErrDigestMismatch: a fetched artifact's bytes do not hash to the digest the // verified index committed to (any arrow of the walk). ErrDigestMismatch = errors.New("plugin digest-chain verification failed") // os/arch (a first-class checked condition, not a nil-deref). ErrPlatformUnavailable = errors.New("no plugin build for this platform") // ErrMalformedIndex: the verified index/child/config bytes violate the // grc.store shape contract. ErrMalformedIndex = errors.New("plugin index is malformed") // ErrTrustRoot: the pinned trusted root is missing, unparseable, or expired. ErrTrustRoot = errors.New("sigstore trusted root unavailable") )
Named fail-closed errors. Every one ABORTS the install and is surfaced with the signer identity (when known) and coordinate by the caller.
Functions ¶
This section is empty.
Types ¶
type IdentityPolicy ¶
type IdentityPolicy struct {
// PinnedIdentity is the previously-recorded canonical identity, or "" on
// first install.
PinnedIdentity string
}
IdentityPolicy decides whether a verified signer identity is acceptable. Camp (b) TOFU: on first install the pin is empty → accept and the caller records the returned identity; on update the pin is the previously-recorded identity → require equality.
type VerifiedPlugin ¶
type VerifiedPlugin struct {
Coordinate string
Version string
IndexDigest string // sha256:... — what was signed; record for drift detection
SignerIdentity string // canonical keyless identity; pin on first install
Entrypoint string // go-plugin discovery name (binary filename) — from the signed config
Protocol string
OS string
Arch string
Binary []byte // the verified binary bytes to write +x under Entrypoint
}
VerifiedPlugin is the trusted result of a successful §6 verification: the bytes to install, the name to install them under, and the provenance to record. Nothing here is derived from an unsigned source — entrypoint/protocol come from the digest-checked config blob, the binary from the digest-checked layer, the identity from the verified cert.
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier performs keyless signature verification against the pinned trusted root. It is constructed once (parsing the root is non-trivial) and reused.
func NewVerifier ¶
NewVerifier builds a Verifier over the embedded public-good trusted root with the production keyless posture: Fulcio cert chain + an SCT (CT-log proof) + Rekor transparency-log inclusion + an observed timestamp. This matches the hub's NewSigstoreVerifier (sctThreshold=1) byte-for-byte so the two agree on what a valid signature is.
func (*Verifier) Index ¶
func (v *Verifier) Index(ctx context.Context, fetched *oci.FetchedIndex, policy IdentityPolicy) (*VerifiedPlugin, error)
Index runs the full §6 contract over a fetched (untrusted) index and returns the VerifiedPlugin for the host platform, or a named fail-closed error. The order is load-bearing: verify the signature and identity BEFORE trusting any index bytes, then walk index → child → config/layer → bytes, checking every digest. Any failure aborts; nothing degrades to an unverified copy.
Multiple signature bundles are tried in order (they accumulate because AttachSignature never removes prior referrers and re-signing is content-addressed/idempotent). The first bundle that passes BOTH signature verification and the identity policy proceeds to the walk. If no bundle passes, the errors are collected and the first error's sentinel is preserved so errors.Is(err, ErrSignatureInvalid) / ErrIdentityMismatch still work.