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
- func KeyID(key crypto.PublicKey) (string, error)
- func MarshalBundle(b *protobundle.Bundle) ([]byte, error)
- func ParsePrivateKeyPEM(data []byte) (crypto.Signer, error)
- func ParsePublicKeyPEM(data []byte) (crypto.PublicKey, error)
- func PreAuthEncoding(payloadType string, payload []byte) []byte
- type AttestRequest
- type Attester
- type BuildDefinition
- type Builder
- type DevProofProvenance
- type Envelope
- type Identity
- type KeyAttester
- type KeyVerifier
- type Predicate
- type ResourceDescriptor
- type RunDetails
- type RunMetadata
- type Signature
- type SigstoreAttester
- type SigstoreOptions
- type SigstoreVerifier
- type SourceProvenance
- type Statement
- type Subject
- type VerificationResult
- type Verifier
- type VerifyRequest
Constants ¶
const ( DefaultFulcioURL = "https://fulcio.sigstore.dev" DefaultRekorURL = "https://rekor.sigstore.dev" )
Public Sigstore service endpoints, used when nothing else is configured.
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.
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.
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).
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 ¶
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 ¶
ParsePrivateKeyPEM decodes a PEM-encoded private key.
func ParsePublicKeyPEM ¶
ParsePublicKeyPEM decodes a PEM-encoded public key.
func PreAuthEncoding ¶
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 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 ¶
DecodePayload returns the envelope's raw payload bytes.
func (*Envelope) Statement ¶
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).
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.
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 ¶
func (v *KeyVerifier) Verify(_ context.Context, req VerifyRequest) (*VerificationResult, error)
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 ¶
func (v *SigstoreVerifier) Verify(_ context.Context, req VerifyRequest) (*VerificationResult, error)
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 ¶
NewStatement builds a statement about a subject digest.
func (*Statement) BindsTo ¶
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 ¶
SubjectDigest returns the digest this statement is about.
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.