pkg/oci imported pkg/cosign/bundle only for RekorBundle,
RekorPayload, RFC3161Timestamp and BundleV03MediaType. That package
also contains SignData, which imports sigstore-go's sign and root
packages, so every consumer of pkg/oci linked the signing stack.
Move these types and the media type constant into the new leaf
package pkg/cosign/bundle/types and import that from pkg/oci.
pkg/cosign/bundle keeps type aliases, so its API is unchanged.
The base pkg/oci package goes from 86 to 2 transitive modules. For
ko, which uses pkg/oci, pkg/oci/mutate, pkg/oci/walk and
pkg/oci/remote, the binary goes from 161 to 146 modules.
Signed-off-by: Manuel Rüger <manuel@rueg.eu>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Add cosign-specific user-agent to all HTTP requests
This change addresses issue #4692 by ensuring that all HTTP requests
made by cosign identify with a proper user-agent that includes both
the cosign version and sigstore-go version.
Signed-off-by: Carlos Panato <ctadeu@gmail.com>
* Scope sigstore-go user-agent to sigstore-go code paths
Keep the global user-agent as cosign/<version> (os; arch) and only
send cosign/<version> sigstore-go/<version> (os; arch) on requests
made through sigstore-go. Fulcio and TSA clients built in SignData
now use a transport that overrides sigstore-go's default user-agent.
Rekor requests made via sigstore-go still use sigstore-go's
user-agent until sigstore-go exposes a way to set it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Carlos Panato <ctadeu@gmail.com>
* Set cosign user-agent on sigstore-go Fulcio, TSA and Rekor clients
Bump sigstore-go to main to pick up sigstore/sigstore-go#686, which
adds a UserAgent option and always appends sigstore-go/<version>.
Pass cosign's user-agent through it and drop the transport wrapper
and the sigstore-go version lookup.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Carlos Panato <ctadeu@gmail.com>
---------
Signed-off-by: Carlos Panato <ctadeu@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
For #5138
To help people transition old container signatures and attestations to
the protobuf bundle format that supports offline verification, rotating
verification material via trusted roots, etc.
Also do not try to convert container signatures, since the signature
over the container won't match the signature over the new DSSE that
containers use with the protobuf bundle format.
---------
Signed-off-by: Zach Steindler <steiza@github.com>
* fix(verify): don't panic on a bundle entry with no hash
bundleHash dereferenced the algorithm and value of a Rekor entry hash
without checking them. The hash is optional in all five of these schemas,
and unmarshalling does not always populate it: a rekord v0.0.1 entry that
carries data.content but no data.hash, and an intoto v0.0.2 entry whose
envelope payload is empty, both unmarshal cleanly with a nil hash.
The body comes from the bundle attached to the signature, so it is read
from the registry before the certificate has been validated and before
the signature has been checked. A body that omits the hash therefore
crashes the verifier rather than failing verification.
Check each level before dereferencing it and return an error instead.
Signed-off-by: ashvinctrl <sharmaashvin27@gmail.com>
* fix(verify): trim comments in bundleHash
Drop the hashFields doc comment and cut the bundleHash comment down to the
part that is not visible from the code: the Rekor models mark the hash fields
required, but per-type validation does not always enforce it.
Signed-off-by: ashvinctrl <sharmaashvin27@gmail.com>
* test(verify): cover every bundleHash entry type and hashFields
Add success cases for all five entry types, missing-hash cases for each
type (including partial hash objects, which Rekor rejects before the
switch), and a direct test of hashFields for a nil algorithm or value.
Signed-off-by: ashvinctrl <sharmaashvin27@gmail.com>
---------
Signed-off-by: ashvinctrl <sharmaashvin27@gmail.com>
Fixes#5018
Motivation
Signing with a HashiCorp Vault Transit KMS ECDSA P-521 key fails with
"signing digest: signing bundle: error signing bundle: unexpected
length of digest for hash function specified". This is a regression
in the bundle-signing path introduced when SignerVerifierKeypair was
added: it correctly computes the digest using the key's own default
hash algorithm (SHA-512 for P-521), but never told the underlying
signer.SignMessage which hash algorithm that digest was produced with.
KMS-backed SignerVerifiers created via
pkg/signature/keys.go:SignerVerifierFromKeyRef are constructed with a
hardcoded crypto.SHA256 default (unrelated to the actual key's curve),
so sigstore-go's ComputeDigestForSigning fell back to that default,
saw a 64-byte digest against an expected 32-byte SHA-256 digest, and
rejected it with the mismatch error above.
Approach
internal/key/svkeypair.go: SignerVerifierKeypair.SignData now also
passes signatureoptions.WithCryptoSignerOpts(hashType) alongside
WithDigest(digest), so the signer is told exactly which hash algorithm
produced the digest, regardless of what hash it was constructed with
by default. This affects every SignerVerifier-backed signing path (any
KMS provider, not just Vault) whenever the key's natural hash isn't
SHA-256 (e.g. P-384/P-521 ECDSA, larger RSA keys). For keys whose
natural hash is already SHA-256 (e.g. default P-256), this is a no-op:
the newly-passed option matches the signer's existing default.
Validation
go build ./...
go test ./internal/key/... ./pkg/signature/... ./pkg/cosign/bundle/... -count=1
ok github.com/sigstore/cosign/v3/internal/key
ok github.com/sigstore/cosign/v3/pkg/signature
ok github.com/sigstore/cosign/v3/pkg/cosign/bundle
Added TestKMSKeypair_ECDSA_P521_SignData, which reproduces the digest
length/hash mismatch reported in the issue (verified it fails without
this fix with "expected underlying signer to be given crypto.SHA512
opts matching the digest, got <nil>", and passes with it). This was
verified with a unit test against a mock SignerVerifier reproducing
the KMS signer's option-handling behavior; it was not validated
against a live HashiCorp Vault instance.
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Bumps the all group with 1 update: golangci/golangci-lint.
Updates `golangci/golangci-lint` from v2.13.2 to v2.14.0
---
updated-dependencies:
- dependency-name: golangci/golangci-lint
dependency-version: v2.14.0
dependency-type: direct:production
dependency-group: all
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
When cosign save writes an OCI artifact to disk, partial.Descriptor falls
back to config.mediaType as the artifactType in the index.json descriptor
because go-containerregistry's v1.Manifest struct does not expose the
manifest-level artifactType field. The result is that config.mediaType
appears as the artifactType entry in index.json instead of the correct
manifest artifactType, violating the OCI Image Layout spec.
Fix appendImage to parse the raw manifest JSON and override the descriptor's
ArtifactType field when a top-level artifactType is present. The existing
config.mediaType fallback still applies when artifactType is absent.
Fixes#4694
Signed-off-by: Ali <alliasgher123@gmail.com>
cosign load was putting v3 Sigstore bundle manifests before their layer
blobs, causing BLOB_UPLOAD_UNKNOWN on registries that enforce blob-first
ordering (e.g. AWS ECR). Reorder to match WriteReferrer.
Add a unit test for upload ordering and a cross-registry e2e whose
destination rejects manifests that reference missing blobs, so the
regression is not masked by the permissive fake registry.
Fixes#5030
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: Geoffrey Hichborn <geoff@yourparadigm.net>
* fix: strip trailing whitespace from attached signature files/stdin
Motivation:
The linked report below describes a failure after detaching a
signature with `cosign download signature ... | jq -r
'.Base64Signature' > signature.sig` and later re-attaching it on
another registry with `cosign attach signature --signature
signature.sig ...`. The reporter's own --verbose debug output shows
the OCI manifest annotation dev.cosignproject.cosign/signature on the
re-attached image literally contains the base64 signature value with
a trailing newline embedded in it, unlike the original signing
machine's copy. That newline comes from shell redirection after
`jq -r`; cosign's attach signature command stores whatever bytes it
reads from the --signature file or stdin verbatim, with no trimming.
Storing extraneous whitespace inside a signature annotation is a
real, demonstrated defect regardless of whether it fully explains the
"invalid signature when validating ASN.1 encoded signature" error the
reporter hit: Go's own base64 decoder tolerates embedded \r/\n and
decodes to the same bytes, so this alone would not necessarily
reproduce that exact failure, and the report's repro also mixed up
`cosign download signature`'s JSON envelope for --payload, a separate
issue. This change fixes the annotation-pollution defect on its own
merits; it is not a claim that it resolves the full reported scenario
end-to-end.
Approach:
Trim leading and trailing whitespace from bytes read from a signature
file or stdin in signatureBytes() (cmd/cosign/cli/attach/sig.go)
before they are stored as the signature annotation. Base64 has no
whitespace in its alphabet, so trimming cannot remove meaningful
signature bytes. The literal-argument path is untouched.
Validation:
- go test ./cmd/cosign/cli/attach/... — added
TestSignatureBytesTrimsTrailingWhitespace, which fails before this
change (returned bytes include the trailing "\n") and passes after;
verified this directly by stashing the fix and re-running the test.
Also added TestSignatureBytesFileWithoutTrailingWhitespaceUnaffected
confirming clean input is unaffected.
- go build ./... and go vet ./cmd/cosign/cli/attach/... pass.
- go test ./cmd/cosign/... passes (all subpackages).
- golangci-lint run ./cmd/cosign/cli/attach/... (v2.12.2, matching
this repo's Dockerfile.golangci-lint) reports 0 issues.
Report: https://github.com/sigstore/cosign/issues/4207
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
* fix: update copyright year in sig_test.go to 2026
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
---------
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Co-authored-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
COSIGN_REPOSITORY expects a full repository (registry + path), not just
a registry. When a bare registry like "registry.example.com:5000" is
given, name.NewRepository() has no '/' to split on, treats the whole
value as a repository name, and rejects it with a confusing "can only
contain the characters ..." error that looks like a parsing bug rather
than a missing path component.
Detect this case and return an actionable error message pointing out
that COSIGN_REPOSITORY needs a repository path.
Fixes#3029
Signed-off-by: Tommy <tommy@bejara.net>
Motivation:
When signing with `cosign sign --upload=false -a key=value
--output-signature=sig.file`, cosign builds the signed payload from
payload.Cosign{Image, ClaimedIdentity, Annotations} including the
annotations (cmd/cosign/cli/sign/sign.go). When later verifying
offline with `cosign verify -a key=value --signature sig.file` and no
explicit `--payload`, cosign reconstructs an "implied" payload via
ObsoletePayload to compare against the signature, but that
reconstruction dropped annotations entirely. The reconstructed payload
therefore never matched the payload that was actually signed, so
verification always failed with "crypto/rsa: verification error" (or
equivalent) any time annotations were used with offline signing.
Approach:
Thread the caller-supplied annotations through ObsoletePayload so the
implied payload it builds matches what was used at signing time. The
verify call site (pkg/cosign/verify.go) now passes co.Annotations,
which is populated from the `-a` flag. The attach-signature call site
(cmd/cosign/cli/attach/sig.go) has no annotations flag, so it passes
nil, preserving its existing behavior exactly.
This is a pure payload-construction/serialization fix with no
dependency on registry, network, or transparency-log state: the
default (no-annotations) case is byte-for-byte unchanged.
Validation:
- go build ./...
- go test ./pkg/cosign/... ./cmd/cosign/cli/attach/... ./cmd/cosign/cli/verify/...
- go test $(go list ./... | grep -v third_party/)
All pass. Added TestObsoletePayloadWithAnnotations in
pkg/cosign/obsolete_test.go, which fails to compile against the
pre-fix ObsoletePayload signature and asserts the annotation now
appears in the marshaled payload's "optional" field once the fix is
applied; confirmed by temporarily reverting the source changes and
observing the test target fail to build.
Report: https://github.com/sigstore/cosign/issues/4024
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Bundle() decompressed the new-bundle-format attestation layer and read all of
it into memory with no size check, while every other layer read in pkg/oci
calls payloadsize.CheckSize first. A registry the user does not control could
make cosign verify-attestation --new-bundle-format allocate without bound from
a small compressed layer.
The size a registry declares is the compressed size, and this is the only one
of these reads that goes through Uncompressed(), so checking that size is
necessary but not sufficient. This does both: the declared size is checked
first so an oversized layer is rejected before anything is read, and the
decompressed stream is then bounded by the same limit.
GetBundles calls Bundle() for every manifest in a referrers index and swallows
the error with continue, since non-Sigstore referrers are expected there, so
without the second check each oversized layer in an index was read in full
before its error was reached.
MaxSize is exported from the payload size package because a caller reading a
decompressed stream needs the limit itself rather than a yes or no answer
about a size it already knows.
Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
* chore(deps): bump the all group across 1 directory with 2 updates
Bumps the all group with 2 updates in the / directory: golang and golangci/golangci-lint.
Updates `golang` from 1.26.5 to 1.27.0
Updates `golangci/golangci-lint` from v2.12.2 to v2.13.1
---
updated-dependencies:
- dependency-name: golang
dependency-version: 1.27.0
dependency-type: direct:production
update-type: version-update:semver-minor
dependency-group: all
- dependency-name: golangci/golangci-lint
dependency-version: v2.13.1
dependency-type: direct:production
dependency-group: all
...
Signed-off-by: dependabot[bot] <support@github.com>
* address lints
Signed-off-by: Bob Callaway <bcallaway@google.com>
---------
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Bob Callaway <bcallaway@google.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Bob Callaway <bcallaway@google.com>
- chainguard-images/static was folded into the chainguard-images/images
monorepo; point the Dockerfile source comment at the static image's new
home (github.com/chainguard-images/static now 404s)
- notaryproject/nv2 was archived and its issue tracker moved to
notaryproject/notation; the referenced payload-format discussion is the
same issue #40 under the new repo
Signed-off-by: Shurong Cao <CAOShurong@users.noreply.github.com>
Co-authored-by: Shurong Cao <CAOShurong@users.noreply.github.com>
Add the org.opencontainers.image.title annotation to layer descriptors
in attestation manifests to enable tools like 'oras pull' to download
attestation bundles with meaningful, collision-free filenames.
The annotation format is {algorithm}-{hex}.sigstore.json where the
hyphen separator ensures cross-platform filename compatibility,
particularly for Windows which forbids colons in filenames.
Changes:
- Add Annotations field to layer descriptors in WriteReferrer
- Update tests to verify annotation is set correctly
- Document the optional layer annotation in BUNDLE_SPEC.md
Fixes#4497🤖 Generated with [Claude Code](https://claude.com/claude-code)
Signed-off-by: Ralph Bean <rbean@redhat.com>
Co-authored-by: Claude <noreply@anthropic.com>