evidence

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Sep 13, 2026 License: Apache-2.0 Imports: 30 Imported by: 0

Documentation

Overview

Package evidence describes where a bundle came from, in a form that can be verified independently of the bundle itself.

Evidence names a subject by digest and is stored beside it, never inside it. That separation is the point of DP-003: adding, renewing, or copying evidence cannot change what it describes, so a signature that expires does not invalidate an artifact, and an artifact rebuilt from the same content keeps its identity while gaining new provenance.

Index

Constants

View Source
const (
	DefaultFulcioURL = "https://fulcio.sigstore.dev"
	DefaultRekorURL  = "https://rekor.sigstore.dev"
)

Public Sigstore service endpoints, used when nothing else is configured.

View Source
const (
	// StatementType is the in-toto Statement v1 type.
	StatementType = "https://in-toto.io/Statement/v1"
	// PredicateTypeSLSAProvenance is SLSA Provenance v1, which a DevProof
	// provenance predicate embeds unchanged.
	PredicateTypeSLSAProvenance = "https://slsa.dev/provenance/v1"
	// PredicateTypeDevProof is the DevProof provenance predicate (DP-024).
	PredicateTypeDevProof = bundle.PredicateTypeProvenanceV1
)

in-toto Attestation Framework constants.

View Source
const MediaTypeBundleV1 = "application/vnd.dev.sigstore.bundle.v1+json"

MediaTypeBundleV1 is the blob media type of an evidence object. It is the Sigstore bundle type so that generic Sigstore tooling can read what DevProof writes.

View Source
const MediaTypeEvidenceV1 = "application/vnd.thingz.devproof.evidence.v1"

MediaTypeEvidenceV1 is the OCI artifact type of a DevProof evidence referrer. It lets discovery filter before fetching anything (DP-027).

View Source
const PayloadType = "application/vnd.in-toto+json"

PayloadType is the DSSE payload type for an in-toto statement.

Variables

This section is empty.

Functions

func KeyID

func KeyID(key crypto.PublicKey) (string, error)

KeyID returns a stable identifier for a public key.

It is the SHA-256 of the key's PKIX encoding, which is the same value whatever container the key arrived in — PEM, DER, or a certificate — so a policy that names a key does not have to name a file format.

func MarshalBundle

func MarshalBundle(b *protobundle.Bundle) ([]byte, error)

MarshalBundle renders a Sigstore bundle as the JSON DevProof stores.

func ParsePrivateKeyPEM

func ParsePrivateKeyPEM(data []byte) (crypto.Signer, error)

ParsePrivateKeyPEM decodes a PEM-encoded private key.

func ParsePublicKeyPEM

func ParsePublicKeyPEM(data []byte) (crypto.PublicKey, error)

ParsePublicKeyPEM decodes a PEM-encoded public key.

func PreAuthEncoding

func PreAuthEncoding(payloadType string, payload []byte) []byte

PreAuthEncoding builds the DSSE pre-authentication encoding.

The format is fixed by the DSSE specification:

"DSSEv1" SP len(payloadType) SP payloadType SP len(payload) SP payload

Length-prefixing every field is what makes the encoding unambiguous: without it, a payload type and a payload could be chosen so that one signature validates two different (type, payload) pairs.

Types

type AttestRequest

type AttestRequest struct {
	// Subject is the OCI manifest digest the statement is about.
	Subject bundle.Digest
	// Statement is the statement to sign. The service builds it; an attester
	// supplies identity and signatures, and must not alter the payload.
	Statement *Statement
	// PayloadType is the DSSE payload type to sign under.
	PayloadType string
	// Payload is the exact serialized statement to sign. Signing these
	// bytes rather than re-serializing the statement is what guarantees the
	// signature covers what the envelope carries.
	Payload []byte
}

AttestRequest is what an attester is asked to sign.

type Attester

type Attester interface {
	// Name identifies the attester in diagnostics and results.
	Name() string
	// Attest returns signatures over the request's payload.
	Attest(ctx context.Context, req AttestRequest) ([]Signature, error)
}

Attester signs a statement.

An attester supplies identity and nothing else. It does not choose the subject, build the statement, or decide what the payload says; those are the evidence service's, so a registered attester cannot attest to something other than what was built.

type BuildDefinition

type BuildDefinition struct {
	BuildType            string               `json:"buildType"`
	ExternalParameters   map[string]any       `json:"externalParameters"`
	InternalParameters   map[string]any       `json:"internalParameters,omitempty"`
	ResolvedDependencies []ResourceDescriptor `json:"resolvedDependencies,omitempty"`
}

BuildDefinition is the SLSA description of what was built.

type Builder

type Builder struct {
	ID      string            `json:"id"`
	Version map[string]string `json:"version,omitempty"`
}

Builder identifies the build platform.

type DevProofProvenance

type DevProofProvenance struct {
	// FormatVersion is the bundle format the subject was built as.
	FormatVersion string `json:"formatVersion"`
	// ManifestDigest identifies the manifest that was built from.
	ManifestDigest string `json:"manifestDigest"`
	// LockDigest identifies the lock that was enforced or generated.
	LockDigest string `json:"lockDigest"`
	// TreeDigest is the payload identity, independent of encoding.
	TreeDigest string `json:"treeDigest"`
	// Sources describes each source's resolution.
	Sources []SourceProvenance `json:"sources"`
	// ToolVersion is the DevProof version that built the subject. It is
	// diagnostic: it never affects the subject digest (DP-012).
	ToolVersion string `json:"toolVersion,omitempty"`
}

DevProofProvenance is the DevProof-specific half of the predicate.

type Envelope

type Envelope struct {
	PayloadType string      `json:"payloadType"`
	Payload     string      `json:"payload"`
	Signatures  []Signature `json:"signatures"`
}

Envelope is a DSSE envelope.

The payload is base64 of the serialized statement, and signatures are over the pre-authentication encoding rather than over the payload directly. That indirection is what stops a signature made for one payload type being replayed as though it were another.

func (*Envelope) DecodePayload

func (e *Envelope) DecodePayload() ([]byte, error)

DecodePayload returns the envelope's raw payload bytes.

func (*Envelope) Statement

func (e *Envelope) Statement() (*Statement, error)

Statement decodes and validates the envelope's statement.

Callers must not use this before the envelope's signatures have been verified. Parsing a claim is not the same as establishing it, and the verification pipeline is structured so that unverified statements never reach policy (DP-014).

func (*Envelope) Validate

func (e *Envelope) Validate() error

Validate checks an envelope's structure.

type Identity

type Identity struct {
	// KeyID identifies a bare public key.
	KeyID string `json:"keyId,omitempty"`
	// Issuer is the OIDC issuer, for a keyless identity.
	Issuer string `json:"issuer,omitempty"`
	// Subject is the OIDC subject, for a keyless identity.
	Subject string `json:"subject,omitempty"`
}

Identity is who signed, once that has been established cryptographically.

Exactly one of KeyID or the OIDC pair is meaningful, depending on the verifier. Policy matches on this rather than on anything in the envelope, because an envelope's own fields are attacker-controlled until a verification has checked them.

func (Identity) IsZero

func (i Identity) IsZero() bool

IsZero reports whether no identity was established.

func (Identity) String

func (i Identity) String() string

type KeyAttester

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

KeyAttester signs with a local private key.

This is the one attester the module ships. Keyless identity, transparency logs, and TUF trust roots are supplied by an embedding application as registered extensions, so that a caller who signs with a local key or does not sign at all is not made to carry that dependency tree (DP-029).

Supported keys are ECDSA and Ed25519. RSA is omitted deliberately: it has no advantage here, and its parameter space — key size, PKCS#1 against PSS, hash choice — is surface that would have to be pinned and tested for a capability nobody has asked for.

func NewKeyAttester

func NewKeyAttester(key crypto.Signer) (*KeyAttester, error)

NewKeyAttester returns an attester signing with key.

func (*KeyAttester) Attest

func (a *KeyAttester) Attest(_ context.Context, req AttestRequest) ([]Signature, error)

Attest signs the request's payload.

func (*KeyAttester) KeyID

func (a *KeyAttester) KeyID() string

KeyID returns this attester's public key identifier.

func (*KeyAttester) Name

func (a *KeyAttester) Name() string

Name identifies the attester in evidence and in verification reports.

type KeyVerifier

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

KeyVerifier verifies signatures against a set of trusted public keys.

func NewKeyVerifier

func NewKeyVerifier(keys ...crypto.PublicKey) (*KeyVerifier, error)

NewKeyVerifier returns a verifier trusting the supplied public keys.

An empty key set is refused rather than accepted as "trust nothing". A verifier that can never succeed is almost always a configuration mistake, and failing at construction says so at the point where it can be fixed.

func (*KeyVerifier) Name

func (v *KeyVerifier) Name() string

Name identifies the verifier in verification reports. It matches the attester it verifies, so a report says which scheme accepted the evidence.

func (*KeyVerifier) Verify

Verify checks an envelope's signatures against the trusted keys.

A signature is tried against every trusted key rather than only the one its keyid names, because the keyid is attacker-controlled: an envelope could name a key that is not trusted and the signature still be valid under one that is. What matters is that some trusted key verifies it.

type Predicate

type Predicate struct {
	// BuildDefinition and RunDetails are SLSA Provenance v1, verbatim.
	BuildDefinition BuildDefinition `json:"buildDefinition"`
	RunDetails      RunDetails      `json:"runDetails"`

	// DevProof carries what SLSA cannot express.
	DevProof DevProofProvenance `json:"devproof"`
}

Predicate is DevProof provenance.

It embeds SLSA Provenance v1 unchanged and adds the facts SLSA has no field for. Neither alone worked: SLSA cannot express "the lock digest" or "this source's canonical tree digest", and discarding SLSA would give up every consumer that already reads it (DP-024).

type ResourceDescriptor

type ResourceDescriptor struct {
	Name        string            `json:"name,omitempty"`
	URI         string            `json:"uri,omitempty"`
	Digest      map[string]string `json:"digest,omitempty"`
	Annotations map[string]any    `json:"annotations,omitempty"`
}

ResourceDescriptor is the SLSA reference to an input.

type RunDetails

type RunDetails struct {
	Builder  Builder     `json:"builder"`
	Metadata RunMetadata `json:"metadata,omitzero"`
}

RunDetails is the SLSA description of who built it and when.

type RunMetadata

type RunMetadata struct {
	InvocationID string     `json:"invocationId,omitempty"`
	StartedOn    *time.Time `json:"startedOn,omitempty"`
	FinishedOn   *time.Time `json:"finishedOn,omitempty"`
}

RunMetadata describes one invocation.

type Signature

type Signature struct {
	// KeyID identifies the signing key or identity. Its meaning is the
	// attester's; policy matches on the verified identity, not on this.
	KeyID string `json:"keyid,omitempty"`
	// Sig is the base64 signature.
	Sig string `json:"sig"`
	// Certificate is the PEM signing certificate, when the attester uses
	// one. Present for keyless identities, absent for bare keys.
	Certificate string `json:"cert,omitempty"`
}

Signature is one signature over an envelope's payload.

type SigstoreAttester

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

SigstoreAttester signs keylessly against Fulcio and Rekor.

An ephemeral key is generated per signature, certified by Fulcio against an OIDC identity, used once, and discarded. Nothing durable is held, which is the point: there is no signing key to protect, rotate, or leak, and the identity in the certificate is the thing policy matches on.

func NewSigstoreAttester

func NewSigstoreAttester(opts SigstoreOptions) *SigstoreAttester

NewSigstoreAttester returns a keyless attester.

func (*SigstoreAttester) Attest

func (a *SigstoreAttester) Attest(ctx context.Context, req AttestRequest) ([]Signature, error)

Attest signs the payload and returns the signature plus its certificate.

The returned signature carries the Fulcio certificate, so a verifier can establish the OIDC identity without another round trip. The full Sigstore bundle, including the transparency-log entry, is what gets stored; see SigstoreAttester.AttestBundle.

func (*SigstoreAttester) AttestBundle

func (a *SigstoreAttester) AttestBundle(ctx context.Context, req AttestRequest) (*protobundle.Bundle, error)

AttestBundle signs and returns the complete Sigstore bundle.

This is what DevProof stores as evidence: it carries the certificate, the transparency-log entry, and the signed timestamp, none of which fit in a bare DSSE signature.

func (*SigstoreAttester) Name

func (a *SigstoreAttester) Name() string

Name identifies the attester in evidence and in verification reports.

type SigstoreOptions

type SigstoreOptions struct {
	// FulcioURL issues the short-lived signing certificate. Empty uses the
	// public instance.
	FulcioURL string
	// RekorURL records the signature in a transparency log. Empty uses the
	// public instance.
	RekorURL string
	// IDToken is an OIDC identity token. Empty uses ambient detection.
	IDToken string
	// IDTokenProvider supplies a token on demand. Takes precedence over
	// IDToken, so a long-running process can refresh rather than hold one.
	IDTokenProvider func(ctx context.Context) (string, error)
	// TrustedRootJSON is a caller-supplied Sigstore trusted root. Empty
	// fetches and caches the public root over TUF.
	//
	// Supplying one is what makes offline verification possible: no network,
	// and trust material that came from somewhere the caller chose.
	TrustedRootJSON []byte
	// Timeout bounds a single network call to Fulcio or Rekor.
	Timeout time.Duration
}

SigstoreOptions configures keyless signing and verification.

type SigstoreVerifier

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

SigstoreVerifier verifies Sigstore bundles.

func NewSigstoreVerifier

func NewSigstoreVerifier(opts SigstoreOptions) *SigstoreVerifier

NewSigstoreVerifier returns a keyless verifier.

func (*SigstoreVerifier) Name

func (v *SigstoreVerifier) Name() string

Name identifies the verifier in verification reports. It matches the attester it verifies, so a report says which scheme accepted the evidence.

func (*SigstoreVerifier) Verify

Verify checks a Sigstore bundle and reports the identity it establishes.

type SourceProvenance

type SourceProvenance struct {
	// Name is the logical source name from the manifest.
	Name string `json:"name"`
	// Type is the source type, such as "git" or "path".
	Type string `json:"type"`
	// Resolver identifies the implementation and version that resolved it,
	// so a resolver whose behavior changed is visible to policy.
	Resolver string `json:"resolver"`
	// Requested is what the manifest asked for, which may be mutable.
	Requested map[string]any `json:"requested,omitempty"`
	// Resolved is what it resolved to, which is not.
	Resolved map[string]any `json:"resolved,omitempty"`
	// TreeDigest is this source's filtered contribution, before mounting.
	TreeDigest string `json:"treeDigest"`
	// MountPath is where the contribution was placed.
	MountPath string `json:"mountPath,omitempty"`
}

SourceProvenance describes one source's contribution.

type Statement

type Statement struct {
	Type          string    `json:"_type"`
	Subject       []Subject `json:"subject"`
	PredicateType string    `json:"predicateType"`
	Predicate     Predicate `json:"predicate"`
}

Statement is an in-toto Statement v1.

The subject is always a bundle's OCI manifest digest. A statement that named content by anything else — a tag, a repository, a build number — would describe something that can change after the statement was signed.

func NewStatement

func NewStatement(subjectDigest bundle.Digest, name string, predicate Predicate) *Statement

NewStatement builds a statement about a subject digest.

func (*Statement) BindsTo

func (s *Statement) BindsTo(subject bundle.Digest) bool

BindsTo reports whether the statement is about a particular subject.

This is the check that stops evidence for one artifact being presented as evidence for another. A signature proves who wrote a statement; only this proves what the statement is about.

func (*Statement) SubjectDigest

func (s *Statement) SubjectDigest() (bundle.Digest, error)

SubjectDigest returns the digest this statement is about.

func (*Statement) Validate

func (s *Statement) Validate() error

Validate checks a statement's structure.

type Subject

type Subject struct {
	// Name is a label. It is not identity and is not matched against
	// anything; the digest is.
	Name string `json:"name,omitempty"`
	// Digest maps an algorithm to a hex value, per the in-toto spec.
	Digest map[string]string `json:"digest"`
}

Subject names what a statement is about.

type VerificationResult

type VerificationResult struct {
	// Identities are the signers whose signatures verified. An empty slice
	// with a nil error is a contradiction a verifier must not produce.
	Identities []Identity
	// TransparencyLogVerified reports whether inclusion in a transparency
	// log was proven. Policy may require it; absence is not an error here.
	TransparencyLogVerified bool
	// IntegratedTime is an authenticated signing time, when one was
	// established. A verifier must not populate it from the envelope or from
	// the local clock — an unauthenticated time cannot satisfy a rule that
	// requires one.
	IntegratedTime *int64
}

VerificationResult is what a verifier establishes.

type Verifier

type Verifier interface {
	// Name identifies the verifier in diagnostics and results.
	Name() string
	// Verify checks an envelope's signatures.
	Verify(ctx context.Context, req VerifyRequest) (*VerificationResult, error)
}

Verifier establishes who signed an envelope.

A verifier returns identities or an error, and never both an empty identity list and success: "verified, by nobody" is the shape of a bug that lets unauthenticated claims reach policy.

type VerifyRequest

type VerifyRequest struct {
	// Envelope is the DSSE envelope to verify.
	Envelope *Envelope
	// Payload is the decoded payload the signatures must cover.
	Payload []byte
	// BundleJSON is the stored evidence blob, when the object is a Sigstore
	// bundle. A keyless verifier needs the whole bundle — certificate,
	// transparency-log entry, timestamps — not just the envelope.
	BundleJSON []byte
	// TrustRoots is caller-supplied trust material. Its format is the
	// verifier's; the SDK never interprets it.
	TrustRoots [][]byte
}

VerifyRequest is what a verifier is asked to check.

Jump to

Keyboard shortcuts

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