Repository navigation
simplesigning + sigstore-bundle + transparency creates unverifiable signatures #2036
Description
Expected Behavior
If I configure Chains with simplesigning format + sigstore-bundle encoding + transparency enabled, either:
- The config should be rejected with an error telling me this combo doesn't work, OR
- Signatures should actually verify with cosign verify
Actual Behavior
Everything looks like it works - signing succeeds, Rekor upload succeeds, bundle gets pushed to the registry. But when you try to verify, it blows up:
Error: no matching attestations: failed to verify log inclusion:
transparency log rekor v1 entry is not a dsse_v001 or intoto_v002 entry
The problem is that Chains creates a hashedrekord entry in Rekor (because simplesigning isn't in the IntotoAttestationSet), but then wraps it in a DsseEnvelope bundle. When sigstore-go tries to verify, it sees the DSSE envelope and expects a dsse_v001 or intoto_v002 Rekor entry, but finds hashedrekord instead. Boom.
This creates signatures that literally cannot be verified by anything.
Steps to Reproduce the Problem
- Set up Chains config:
apiVersion: v1
kind: ConfigMap
metadata:
name: chains-config
namespace: tekton-chains
data:
artifacts.oci.format: "simplesigning"
artifacts.oci.storage: "oci"
storage.oci.encoding-format: "sigstore-bundle"
transparency.enabled: "true"- Run a TaskRun that outputs IMAGE_URL and IMAGE_DIGEST:
apiVersion: tekton.dev/v1
kind: TaskRun
metadata:
name: test-signing
spec:
taskSpec:
results:
- name: IMAGE_URL
- name: IMAGE_DIGEST
steps:
- name: build
image: bash:latest
script: |
echo -n "quay.io/myuser/myimage" | tee $(results.IMAGE_URL.path)
echo -n "sha256:abcd..." | tee $(results.IMAGE_DIGEST.path)- Check the Chains logs - you'll see:
"Created payload of type simplesigning"
"Uploaded entry to https://rekor.sigstore.dev with index XXXXX"
"Using sigstore bundle format for signature storage" - Verify the Rekor entry type:
curl -s "https://rekor.sigstore.dev/api/v1/log/entries?logIndex=XXXXX" | jq -r '.[] | .body' | base64 -d | jq -r '.kind'
- Try to verify (will fail):
cosign verify --key <pubkey> quay.io/myuser/myimage@sha256:abcd...
Additional Info
-
Kubernetes version:
Output of
kubectl version:
Client Version: v1.32.2
Kustomize Version: v5.5.0
Server Version: v1.32.2
-
Tekton Pipeline version: v0.17.0
Output of
tkn versionorkubectl get pods -n tekton-pipelines -l app=tekton-pipelines-controller -o=jsonpath='{.items[0].metadata.labels.version}'
Client version: 0.42.0
Chains version: v0.29.7
Pipeline version: v1.17.0
Activity
anithapriyanatarajan commentedon Oct 8, 2026
Root cause
Confirmed against a live
sigstore-bundleimage signature. For the image signature, Chains builds a bundle whosecontenttype disagrees with what's inside it:contentoneofdsseEnvelopemessageSignatureapplication/vnd.dev.cosign.simplesigning.v1+jsonhashedrekordhashedrekordA
simplesigningpayload gets wrapped in adsseEnvelopecontent field instead of amessageSignaturefield. cosign/sigstore-go dispatch on thecontenttype: seeingdsseEnvelope, they expect DSSE — but the payload is simplesigning and (when transparency is on) the Rekor entry ishashedrekord, so verify fails:This is a labeling bug, not a crypto bug.
Transparency on/off makes no difference
The broken part is the
contenttype, which Chains sets the same way regardless oftransparency.enabled. Transparency only changes which error surfaces:hashedrekord-vs-dssetlog mismatch above.dsseEnvelopewrapping a simplesigning payload, which has no valid DSSE interpretation, so verify still fails.Either way the bundle is unverifiable. Enabling/disabling transparency does not fix or cause the root issue.
Note on scope: encoding is one global knob
storage.oci.encoding-formatapplies to the whole OCI backend — image sig and attestation, same run. There's no per-type split. Undersigstore-bundle, the attestation verifies (genuine DSSE + matchingdsse/intotoentry) while the image sig fails (simplesigning mislabeled as DSSE). Same encoding, differentcontenttype.Why "just make it a DSSE bundle" isn't the fix
A simplesigning signature is computed over the simplesigning payload, not over DSSE content — there's no valid DSSE form of it. cosign PR #5144 confirms: container signatures stay simplesigning; only attestations get DSSE bundles. The only correct bundle shape for an image sig is
messageSignature+hashedrekord.Proposed approach
artifacts.oci.format: simplesigning+storage.oci.encoding-format: sigstore-bundleat config validation, pointing todsse. - But this is not what we want.simplesigning, build withBundle_MessageSignature, notDsseEnvelope, so content type andhashedrekordagree. Attestations keep the DSSE path.Interim workaround: set
storage.oci.encoding-format: dsse. Since the knob is global, both artifacts move to legacy tags — image sig on.sig(cosign verify), attestation on.att(cosign verify-attestation). Both verify.fix(storage): use MessageSignature for OCI signature bundles
fix(storage): use MessageSignature for OCI signature bundles
fix(storage): use MessageSignature for OCI signature bundles
fix(storage): use MessageSignature for OCI signature bundles
fix(storage): use MessageSignature for OCI signature bundles