Skip to content

Emit certificate annotations and fix dedupe for keyless signatures - #234

Merged
rdimitrov merged 2 commits into
mainfrom
skills-keyless/c1-attach-cert-annotations
Aug 13, 2026
Merged

rdimitrov merged 2 commits into
mainfrom
skills-keyless/c1-attach-cert-annotations

Conversation

@samuv

@samuv samuv commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

container/signer's attach logic (added in #230) only wrote the plain
dev.cosignproject.cosign/signature annotation, regardless of what kind
of Sigstore bundle was being attached. container/verifier/sigstore.go
classifies a signature layer as key-signed whenever the certificate
annotation is absent — so a certificate-bearing (keyless/Fulcio) bundle
attached through the existing code path would silently be misclassified,
and keyless verification would fail. Dedupe was also broken for that case:
signedByKey verifies against the public key currently being used to
sign, but keyless signing mints a fresh ephemeral keypair (and
certificate) on every run, so it would never dedupe and would append an
unbounded number of duplicate layers on repeated pushes.

This PR fixes both, ahead of a follow-up PR that adds the actual
keyless/Fulcio signing flow on top:

  • attachCosignSignature now takes the full Sigstore bundle (*protobundle.Bundle)
    instead of a raw signature slice. When the bundle's verification material
    carries a certificate, it also writes the dev.sigstore.cosign/certificate
    (PEM leaf cert) and dev.sigstore.cosign/bundle (Rekor transparency-log
    entry) annotations, matching exactly what container/verifier/sigstore.go
    reads back on retrieval.
  • Dedupe for a certificate-bearing signature now compares signer identity
    (certificate SAN + OIDC issuer) against already-attached layers, instead
    of key material. The key-signed path (signedByKey) is unchanged.
  • Append-never-replace is preserved for both paths — this only changes what
    counts as "already present," not append-vs-replace.

No changes to actual Fulcio/Rekor signing — this PR is attach/dedupe logic
only, working with whatever bundle shape is handed to it.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Test plan

  • Added/updated unit tests
  • go test -race ./container/signer/... ./container/verifier/... passes
  • task lint and task test pass for the full repo
  • task license-check passes

New tests cover:

  • A certificate-bearing bundle attached through attachCosignSignature
    round-trips through verifier.RetrieveBundles and is classified as
    keyless (HasCertificate() == true), not key-signed — the regression
    test for the misclassification bug.
  • The certificate annotation is written with or without a transparency-log
    entry; the dev.sigstore.cosign/bundle annotation is written only when
    a tlog entry is present, and its JSON shape matches what
    container/verifier/sigstore.go reads back.
  • Dedupe matrix: re-signing with the same keyless identity (same SAN +
    issuer) across a fresh ephemeral certificate each "run" collapses to one
    layer; a different SAN, or the same SAN under a different OIDC issuer,
    appends a new layer instead; the key-signed path's dedupe behavior is
    unchanged.

Does this introduce a user-facing change?

No — container/signer's public API (Signer, Options, Result,
SignOCI) is unchanged. This only affects internal attach/dedupe logic,
in preparation for a future keyless-signing feature that isn't wired up
yet.

Generated with Claude Code

container/signer only wrote the plain signature annotation when
attaching a cosign signature manifest, so a certificate-bearing
(keyless) bundle would silently be misclassified as key-signed by
container/verifier, which treats a missing certificate annotation as
the key-pair layout. Dedupe also verified against the currently-used
public key, which never matches for keyless signing, since each run
mints a fresh ephemeral keypair and certificate — so re-signing would
append an unbounded number of duplicate layers.

attachCosignSignature now takes the full Sigstore bundle and, when its
verification material carries a certificate, writes the certificate
and transparency-log annotations container/verifier's read side
expects, and dedupes by signer identity (certificate SAN + OIDC
issuer) instead of key material. The key-signed path is unchanged.

This is groundwork for keyless (Fulcio) signing; that flow itself is
not implemented here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@samuv samuv self-assigned this Aug 13, 2026
A certificate-bearing signature with no transparency-log entry (a
Fulcio deployment run without Rekor) previously failed to round-trip
through RetrieveBundles: the read side unconditionally parsed the
absent bundle annotation and discarded the layer. rekorBundleAnnotation
also silently produced a broken annotation for a proof-only (Rekor v2)
tlog entry, since the classic cosign bundle layout only carries a v1
inclusion promise. And identity-based dedupe trusted an existing
layer's certificate match without verifying its signature, letting a
corrupted or mismatched layer permanently block a valid re-sign.
@rdimitrov
rdimitrov merged commit c4234ea into main Aug 13, 2026
5 checks passed
@rdimitrov
rdimitrov deleted the skills-keyless/c1-attach-cert-annotations branch August 13, 2026 16:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants