Documentation
¶
Overview ¶
Package trust implements Sigstore/cosign/Rekor signing and verification.
Trust is cryptographic and unconditional. Unsigned artifacts will not mount. There is no flag to skip verification anywhere in this package.
Trust tiers ¶
Tier 0 Strata core layers — signed by Strata maintainer key Tier 1 Community layers — signed by Strata CI key after recipe review Tier 2 Institutional layers — signed by institution key (bring your own) Tier 3 User/local layers — signed by user's cosign key
The verification chain is: profile SHA256 → layer SHA256s + Rekor entries → lockfile Rekor entry. Every element is independently auditable without trusting Strata or its registry.
Index ¶
- Constants
- Variables
- func CosignToolVersion(ctx context.Context) string
- func EmbeddedCosignKey() []byte
- func SignFormation(ctx context.Context, f *spec.Formation, signer Signer) error
- func SignLockFile(ctx context.Context, lf *spec.LockFile, signer Signer) error
- func VerifyFormation(ctx context.Context, f *spec.Formation, verifier Verifier) error
- func VerifyLayer(ctx context.Context, manifest *spec.LayerManifest, squashfsPath string, ...) error
- func VerifyLayers(ctx context.Context, lockfile *spec.LockFile, squashfsDir string, v Verifier) error
- func VerifyLockFile(ctx context.Context, lf *spec.LockFile, verifier Verifier) error
- func WriteEmbeddedKeyFile() (path string, cleanup func(), err error)
- type Bundle
- type CosignSigner
- type CosignVerifier
- type FakeRekorClient
- type FakeSigner
- type FakeVerifier
- type InclusionPromise
- type KindVersion
- type LogID
- type MessageDigest
- type MessageSignature
- type RFC3161Timestamp
- type RawMaterial
- type RekorClient
- type RekorHTTPClient
- type Signer
- type TimestampVerificationData
- type TlogEntry
- type VerificationMaterial
- type Verifier
Constants ¶
const BundleMediaType = "application/vnd.dev.sigstore.bundle.v0.3+json"
BundleMediaType is the media type of the sigstore bundle format used by Strata. Bundles in this format can be independently verified with standard cosign tooling.
Variables ¶
var ( // ErrNoBundle means VerifyEntry was called without the bundle whose // contents the log entry is supposed to attest. Existence of an entry at a // log index says only that somebody logged something there. ErrNoBundle = errors.New("rekor verify: no bundle supplied") // ErrEntryNotFound means the log holds no entry at the requested index. ErrEntryNotFound = errors.New("rekor verify: no entry at log index") // ErrLogIndexMismatch means the bundle names a different log index than the // one being checked, so the two cannot be talking about the same entry. ErrLogIndexMismatch = errors.New("rekor verify: bundle claims a different log index") // ErrMalformedEntry means the entry body could not be decoded. ErrMalformedEntry = errors.New("rekor verify: entry body is not decodable") // ErrUnsupportedEntryKind means the entry is not a hashedrekord, which is // the only kind this client knows how to compare against a bundle. ErrUnsupportedEntryKind = errors.New("rekor verify: unsupported entry kind") // ErrIncompleteBundle means the bundle carries no digest or no signature, // so there is nothing to compare the entry against. Comparing empty to // empty would succeed, which is the failure mode this check exists to stop. ErrIncompleteBundle = errors.New("rekor verify: bundle has no digest or signature to compare") // ErrDigestAlgorithm means the entry or the bundle names a hash algorithm // other than SHA-256. ErrDigestAlgorithm = errors.New("rekor verify: unsupported digest algorithm") // ErrDigestMismatch means the entry attests a different artifact than the // bundle does. This is the case where a real, valid, findable log entry // belongs to somebody else's artifact. ErrDigestMismatch = errors.New("rekor verify: entry attests a different artifact digest") // ErrSignatureMismatch means the entry and the bundle agree on the artifact // but carry different signatures over it. ErrSignatureMismatch = errors.New("rekor verify: entry carries a different signature") // ErrPublicKeyMismatch means the entry and the bundle carry different key // material. Checked only when both sides supply raw key bytes. ErrPublicKeyMismatch = errors.New("rekor verify: entry carries different key material") )
Sentinel errors returned by RekorHTTPClient.VerifyEntry. There is one per distinct reason an entry fails to attest a bundle, because "the call returned an error" is not a measurement: a rejection that fires from the wrong check looks identical in the output. Callers and tests match with errors.Is.
Functions ¶
func CosignToolVersion ¶
CosignToolVersion returns the version string of the cosign binary on PATH (e.g. "v3.0.5"). Returns "unknown" if cosign is not found or the version cannot be parsed. The result is suitable for recording in LayerManifest.
func EmbeddedCosignKey ¶
func EmbeddedCosignKey() []byte
EmbeddedCosignKey returns the pinned public signing key bytes.
func SignFormation ¶ added in v0.29.0
SignFormation signs the formation's canonical content with signer and records the resulting Sigstore bundle on the formation — Bundle holds the bundle JSON, RekorEntry the transparency-log index. In production signer is CosignSigner{KeyRef: <KMS URI>}, which logs the signature to Rekor so the formation carries the same transparency as the layers and lockfiles. The signature is required to carry a transparency-log entry, mirroring SignLockFile: a formation signature that is not logged is refused rather than silently accepted.
It does not set SignedBy — that is the human-readable key ref, provenance the caller records (as the build does for layer manifests); it is excluded from the signed payload for the same reason Bundle/RekorEntry are.
func SignLockFile ¶
SignLockFile signs the lockfile's canonical content with signer and records the resulting Sigstore bundle on the lockfile — Bundle holds the bundle JSON, RekorEntry the transparency-log index. In production signer is CosignSigner{KeyRef: <KMS URI>}, which logs the signature to Rekor so the lockfile has the same transparency as its layers; strata already needs the network to pull layers from S3, so the Rekor round-trip costs nothing extra.
func VerifyFormation ¶ added in v0.29.0
VerifyFormation verifies the formation's own signature with verifier — the agent's embedded key in production. A tampered field, a swapped or reordered layer ref, or a forged set fails here because the signature covers the whole formation, and the verifier also rejects a bundle with no transparency-log entry. It is the formation analog of VerifyLockFile.
func VerifyLayer ¶
func VerifyLayer(ctx context.Context, manifest *spec.LayerManifest, squashfsPath string, v Verifier) error
VerifyLayer verifies a resolved layer before it is mounted. It performs two independent checks:
- SHA256 of squashfsPath matches manifest.SHA256 (content integrity)
- Cosign bundle in manifest.Bundle is valid for squashfsPath (authenticity)
Both checks must pass. A failure in either means the layer is untrusted and must not be mounted. There is no partial success.
func VerifyLayers ¶
func VerifyLayers(ctx context.Context, lockfile *spec.LockFile, squashfsDir string, v Verifier) error
VerifyLayers verifies all layers in a lockfile in parallel. Each layer must pass VerifyLayer. The first failure aborts all remaining verifications and returns an error naming the failing layer. squashfsDir is the directory where layer squashfs files are cached, named as "<layer.ID>.sqfs".
func VerifyLockFile ¶
VerifyLockFile verifies the lockfile's own signature with verifier — the agent's embedded key in production (CosignVerifier{KeyRef: <embedded>}). It is the check #60 found missing: a real lockfile verification that verify, publish, and the agent can all call, so they cannot drift on what "signed" means. A tampered field, a swapped or reordered layer, or a forged set fails here because the signature covers the whole lockfile, and the verifier also rejects a bundle with no transparency-log entry.
func WriteEmbeddedKeyFile ¶
WriteEmbeddedKeyFile materialises the embedded public key to a temp file and returns its path and a cleanup func. cosign verify-blob takes a --key path, not bytes. An empty embed is an error: a binary built without a trust anchor cannot verify, and its callers turn that into a refusal rather than a skip.
Types ¶
type Bundle ¶
type Bundle struct {
MediaType string `json:"mediaType"`
VerificationMaterial VerificationMaterial `json:"verificationMaterial"`
MessageSignature MessageSignature `json:"messageSignature"`
}
Bundle is the attestation record stored alongside every signed artifact in the registry. It conforms to the sigstore bundle format so that any verifier with the cosign tool can independently check signatures without trusting Strata.
func ParseBundle ¶
ParseBundle deserializes a bundle from JSON bytes.
func (*Bundle) HasRekorEntry ¶
HasRekorEntry reports whether the bundle contains at least one Rekor transparency log entry. Bundles without Rekor entries are not accepted by Strata — logging is unconditional.
func (*Bundle) RekorLogIndex ¶
RekorLogIndex returns the integer log index from the first TlogEntry, and reports whether it was present.
type CosignSigner ¶
type CosignSigner struct {
// KeyRef is the signing key reference: a file path (e.g. "cosign.key"),
// a KMS URI (e.g. "gcpkms://..."), or empty for keyless (OIDC) signing.
KeyRef string
}
CosignSigner implements Signer using the cosign CLI. It requires cosign to be installed and available on PATH. The key is a cosign key file path or a KMS URI.
type CosignVerifier ¶
type CosignVerifier struct {
// KeyRef is the verification key reference: a file path, a KMS URI,
// or empty for keyless (OIDC) verification.
KeyRef string
// CertIdentity is required for keyless verification — the expected
// certificate subject (OIDC identity of the signer).
CertIdentity string
// CertOIDCIssuer is required for keyless verification — the expected
// OIDC issuer URL.
CertOIDCIssuer string
}
CosignVerifier implements Verifier using the cosign CLI.
type FakeRekorClient ¶
type FakeRekorClient struct {
// LoggedBundles records every bundle passed to Log.
LoggedBundles []*Bundle
// contains filtered or unexported fields
}
FakeRekorClient is a no-op RekorClient for testing. Log always succeeds and increments a counter. VerifyEntry always succeeds.
func (*FakeRekorClient) VerifyEntry ¶
VerifyEntry always succeeds for testing purposes.
It ignores the bundle, including a nil one, which RekorHTTPClient rejects with ErrNoBundle since #59. So a test that drives a call site through this fake cannot observe whether that site passes a bundle at all — which is how the stage-7 resolver call site stayed unmeasured while VerifyEntry discarded its bundle. A test that means to assert what a call site passes needs a recording client, not this one.
type FakeSigner ¶
type FakeSigner struct {
// NextLogIndex is incremented for each Sign call.
NextLogIndex int64
}
FakeSigner is a deterministic Signer for testing. It produces bundles containing a SHA256-based "signature" with a predictable Rekor log index. FakeSigner is not cryptographically secure and must never be used in production.
type FakeVerifier ¶
type FakeVerifier struct{}
FakeVerifier is a deterministic Verifier for testing. It accepts any bundle produced by FakeSigner (signature = SHA256 of artifact) and rejects all others. FakeVerifier is not cryptographically secure and must never be used in production.
type InclusionPromise ¶
type InclusionPromise struct {
SignedEntryTimestamp []byte `json:"signedEntryTimestamp"`
}
InclusionPromise is the Rekor server's signed timestamp promise.
type KindVersion ¶
KindVersion identifies the Rekor entry type and schema version.
type LogID ¶
type LogID struct {
KeyID string `json:"keyId"`
}
LogID identifies a Rekor instance by the SHA256 of its public key (cosign v3 format).
type MessageDigest ¶
type MessageDigest struct {
// Algorithm is the hash algorithm, e.g. "SHA2_256".
Algorithm string `json:"algorithm"`
// Digest is the raw hash bytes.
Digest []byte `json:"digest"`
}
MessageDigest identifies the hash algorithm and value for the signed content.
type MessageSignature ¶
type MessageSignature struct {
MessageDigest MessageDigest `json:"messageDigest"`
// Signature is the raw signature bytes over MessageDigest.Digest.
Signature []byte `json:"signature"`
}
MessageSignature holds the signature over the artifact's content hash.
type RFC3161Timestamp ¶
type RFC3161Timestamp struct {
SignedTimestamp []byte `json:"signedTimestamp"`
}
RFC3161Timestamp is a signed RFC3161 timestamp token.
type RawMaterial ¶
type RawMaterial struct {
RawBytes []byte `json:"rawBytes,omitempty"`
// Hint is the base64-encoded public key hint used by cosign v3 key-based bundles.
Hint string `json:"hint,omitempty"`
}
RawMaterial holds a DER-encoded certificate or public key. In cosign v3, key-based bundles use Hint instead of RawBytes.
type RekorClient ¶
type RekorClient interface {
// Log submits bundle to the Rekor transparency log and returns the
// log index. The returned index is stored in LayerManifest.RekorEntry.
Log(ctx context.Context, bundle *Bundle) (logIndex int64, err error)
// VerifyEntry confirms that the entry in the Rekor log at logIndex attests
// bundle — the same artifact digest and the same signature, not merely that
// an entry exists there. Returns nil iff the entry matches the bundle.
//
// bundle is required. An implementation given none cannot distinguish this
// artifact's entry from a stranger's and must report that rather than return
// success; RekorHTTPClient returns ErrNoBundle.
VerifyEntry(ctx context.Context, logIndex int64, bundle *Bundle) error
}
RekorClient provides direct access to the Rekor transparency log.
type RekorHTTPClient ¶
type RekorHTTPClient struct {
// BaseURL is the Rekor server base URL. Defaults to https://rekor.sigstore.dev.
BaseURL string
}
RekorHTTPClient implements RekorClient via the Rekor REST API. It uses the public Rekor instance at https://rekor.sigstore.dev by default.
func (*RekorHTTPClient) Log ¶
Log submits a transparency log entry to Rekor for the given bundle. The bundle must contain a MessageSignature and VerificationMaterial.
func (*RekorHTTPClient) VerifyEntry ¶
VerifyEntry confirms that the Rekor log entry at logIndex attests bundle: that an entry exists there, and that its body names the same artifact digest, the same signature, and (where both sides carry raw key bytes) the same key as the bundle presented alongside it.
bundle is required. Existence alone establishes only that something was logged at that index — not that it was this artifact, nor that it was signed by the expected key. An index without a bundle is unverifiable and returns ErrNoBundle rather than success (#59).
The comparison covers the hashedrekord fields this repository itself writes in Log: spec.data.hash (algorithm and hex value) and spec.signature (content and public key). It does not check the log's own signed inclusion promise or the signer's identity — a bundle whose signature is cryptographically invalid but consistently recorded in the log still passes this function. Full signature verification is CosignVerifier.Verify's job; this function's job is to tie the log entry to the bundle.
type Signer ¶
type Signer interface {
// Sign signs artifactPath and returns a Bundle containing the signature,
// key material, and Rekor transparency log entry.
// annotations are additional key/value pairs included in the Rekor entry,
// e.g. {"strata.layer.name": "python", "strata.layer.version": "3.11.9"}.
Sign(ctx context.Context, artifactPath string, annotations map[string]string) (*Bundle, error)
}
Signer creates cosign bundle attestations for artifacts. Implementations must log every signature to the Rekor transparency log — there is no unsigned-but-valid code path.
type TimestampVerificationData ¶
type TimestampVerificationData struct {
RFC3161Timestamps []RFC3161Timestamp `json:"rfc3161Timestamps,omitempty"`
}
TimestampVerificationData holds RFC3161 timestamps included in cosign v3 bundles.
type TlogEntry ¶
type TlogEntry struct {
// LogIndex is the entry's position in the Rekor log. Used as the
// stable reference stored in LayerManifest.RekorEntry.
LogIndex string `json:"logIndex"`
// LogID identifies the Rekor instance. In cosign v3 this is an object
// {"keyId": "<base64>"}, not a plain string.
LogID LogID `json:"logId"`
// KindVersion identifies the Rekor entry type (e.g. hashedrekord 0.0.1).
KindVersion *KindVersion `json:"kindVersion,omitempty"`
// IntegratedTime is the Unix timestamp (seconds) when Rekor accepted
// this entry.
IntegratedTime string `json:"integratedTime"`
// CanonicalizedBody is the base64-encoded canonical form of the entry
// body as stored in Rekor.
CanonicalizedBody string `json:"canonicalizedBody"`
// InclusionPromise is the Rekor server's signed promise that this
// entry will be included in the log.
InclusionPromise *InclusionPromise `json:"inclusionPromise,omitempty"`
// InclusionProof is the Merkle inclusion proof (cosign v3+).
InclusionProof json.RawMessage `json:"inclusionProof,omitempty"`
}
TlogEntry is a single entry in the Rekor transparency log. Field names and types match the cosign v3 bundle format (Sigstore protobuf JSON).
type VerificationMaterial ¶
type VerificationMaterial struct {
// Certificate holds the short-lived certificate for keyless (OIDC) signatures.
// Exactly one of Certificate or PublicKey will be set.
Certificate *RawMaterial `json:"certificate,omitempty"`
// PublicKey holds the raw public key for keyed signatures.
PublicKey *RawMaterial `json:"publicKey,omitempty"`
// TlogEntries are the Rekor transparency log entries for this bundle.
// Strata requires at least one entry (Rekor logging is mandatory).
TlogEntries []TlogEntry `json:"tlogEntries,omitempty"`
// TimestampVerificationData holds RFC3161 timestamps (cosign v3+).
TimestampVerificationData *TimestampVerificationData `json:"timestampVerificationData,omitempty"`
}
VerificationMaterial holds the key material and Rekor transparency log entries.
type Verifier ¶
type Verifier interface {
// Verify checks that artifactPath matches bundle's signature and that
// the bundle's Rekor entry is valid and current.
// Returns nil iff both checks pass. Any failure returns a non-nil error.
Verify(ctx context.Context, artifactPath string, bundle *Bundle) error
}
Verifier checks cosign bundle attestations for artifacts. Implementations must verify both the cryptographic signature and the Rekor entry — there is no partial verification. An error means the artifact is not trusted and must not be used.
func EmbeddedKeyVerifier ¶
EmbeddedKeyVerifier returns a Verifier that checks signatures against the embedded public key, plus a cleanup func for the temp key file it writes. Used by strata verify and the agent to verify a lockfile signature with no AWS access — the transparency proof rides in the bundle.