Skip to content

Conversation

@steiza
Copy link
Member

@steiza steiza commented Jul 29, 2025

Summary

This is an implementation of #3927, but it also finishes the last secondary goal on #3139, which is just over 2 years old 🥳

More explicitly, it allows you to use cosign sign to product Sigstore protobuf signatures that we want everyone to move to, as well as slight updates to cosign verify so it can verify those bundles.

These bundle signatures will show up in cosign tree if you include --experimental-oci11=true, but it will not work with cosign download, which does not look like it has been updated in awhile.

I think this is the last step before we enable --new-bundle-format=true everywhere (and maybe also --experimental-oci11=true in some places).

You can test it with something like:

$ go run cmd/cosign/main.go sign --key cosign.key --new-bundle-format=true --tlog-upload=false ghcr.io/steiza/alpine@sha256:a19367999603840546b8612572e338ec076c6d1f2fec61760a9e11410f546733

And then verify with something like:

$ go run cmd/cosign/main.go verify --key cosign.pub --new-bundle-format=true --insecure-ignore-tlog=true ghcr.io/steiza/alpine@sha256:a19367999603840546b8612572e338ec076c6d1f2fec61760a9e11410f546733

Here's an example of what that bundle looks like:

$ oras blob fetch --output - ghcr.io/steiza/alpine@sha256:fa5d401b87a89e0e1d847862c6552fe48981bcef75cb2b6cdf3c6656aaf64c9c | jq
{
  "mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json",
  "verificationMaterial": {
    "publicKey": {
      "hint": "KphQpmcaLAPdFYX3K1YoGLXC6+jkCGRydpUd45IP1Xo="
    }
  },
  "dsseEnvelope": {
    "payload": "eyJ0eXBlIjoiaHR0cHM6Ly9pbi10b3RvLmlvL1N0YXRlbWVudC92MSIsInN1YmplY3QiOlt7ImRpZ2VzdCI6eyJzaGEyNTYiOiJhMTkzNjc5OTk2MDM4NDA1NDZiODYxMjU3MmUzMzhlYzA3NmM2ZDFmMmZlYzYxNzYwYTllMTE0MTBmNTQ2NzMzIn19XSwicHJlZGljYXRlX3R5cGUiOiJodHRwczovL3NpZ3N0b3JlLmRldi9jb3NpZ24vc2lnbi92MSJ9",
    "payloadType": "application/vnd.in-toto+json",
    "signatures": [
      {
        "sig": "MEUCIQC+txDVbNH9SDGvJHagR98s7ocfBkFAJTCCRQIU/S8YkwIgLquuB9sgLKsL2Z2dUAbrMDDK08R29gXIlW9PKW3gBJY="
      }
    ]
  }
}

And here's what the old signature looked like:

$ cosign download signature ghcr.io/steiza/alpine@sha256:a19367999603840546b8612572e338ec076c6d1f2fec61760a9e11410f546733 | jq
{
  "Base64Signature": "MEQCIGdpIWC5SbvxFdNX4qrjXzA/0unT7NnvZhqUDI3CykSrAiA+hAnjmCLmKU7NMRVNa1jrorP4c9/uOIJK8+F+R670gQ==",
  "Payload": "eyJjcml0aWNhbCI6eyJpZGVudGl0eSI6eyJkb2NrZXItcmVmZXJlbmNlIjoiZ2hjci5pby9zdGVpemEvYWxwaW5lIn0sImltYWdlIjp7ImRvY2tlci1tYW5pZmVzdC1kaWdlc3QiOiJzaGEyNTY6YTE5MzY3OTk5NjAzODQwNTQ2Yjg2MTI1NzJlMzM4ZWMwNzZjNmQxZjJmZWM2MTc2MGE5ZTExNDEwZjU0NjczMyJ9LCJ0eXBlIjoiY29zaWduIGNvbnRhaW5lciBpbWFnZSBzaWduYXR1cmUifSwib3B0aW9uYWwiOm51bGx9",
  "Cert": null,
  "Chain": null,
  "Bundle": null,
  "RFC3161Timestamp": null
}

Release Note

  • Added cosign sign --new-bundle-format=true to store signature in Sigstore protobuf bundles

Documentation

Doc updates are in PR

@steiza steiza requested a review from a team as a code owner July 29, 2025 20:38
@steiza steiza force-pushed the sign-new-bundle branch from 7daea99 to 792d80f Compare July 29, 2025 20:47
@codecov
Copy link

codecov bot commented Jul 29, 2025

Codecov Report

❌ Patch coverage is 1.37615% with 215 lines in your changes missing coverage. Please review.
✅ Project coverage is 34.31%. Comparing base (2ef6022) to head (d204aa0).
⚠️ Report is 464 commits behind head on main.

Files with missing lines Patch % Lines
cmd/cosign/cli/sign/sign.go 0.00% 86 Missing ⚠️
pkg/cosign/bundle/protobundle.go 0.00% 51 Missing ⚠️
cmd/cosign/cli/verify/verify.go 0.00% 50 Missing ⚠️
pkg/cosign/tsa.go 0.00% 10 Missing ⚠️
cmd/cosign/cli/attest/attest.go 0.00% 7 Missing ⚠️
cmd/cosign/cli/attest/attest_blob.go 0.00% 7 Missing ⚠️
cmd/cosign/cli/options/sign.go 0.00% 2 Missing ⚠️
cmd/cosign/cli/options/verify.go 0.00% 1 Missing ⚠️
cmd/cosign/cli/verify.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4316      +/-   ##
==========================================
- Coverage   40.10%   34.31%   -5.79%     
==========================================
  Files         155      216      +61     
  Lines       10044    15009    +4965     
==========================================
+ Hits         4028     5151    +1123     
- Misses       5530     9194    +3664     
- Partials      486      664     +178     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

@haydentherapper haydentherapper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Amazing work on this! It's clear that this is going to greatly improve maintainability of this codebase with this change as sign and attest start looking quite similar.

}

func signDigestBundle(ctx context.Context, digest name.Digest, ko options.KeyOpts, signOpts options.SignOptions, sv *SignerVerifier) error {
digestParts := strings.Split(digest.DigestStr(), ":")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there currently an expectation that the digest of the image is provided when signing a container? Could we compute this ourselves? Should we compute this regardless to verify the provided digest is correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, before we get to this point cosign sign parses the OCI reference and uses that to compute the digest. Here we're just splitting the algorithm from the digest (that cosign sign has already computed) in order to create the in-toto subject.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect! Just wanted to check that wasn't just a user-provided digest.

if err == nil {
pubKey = &pk
}
bundleBytes, err := cbundle.MakeNewBundle(pubKey, rekorEntry, payload, signedPayload, signerBytes, timestampBytes)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does MakeNewBundle take both a signer and the public key? Can this be simplified?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a holdover from how the private function was before - it first tried to get a certificate from cryptoutils.UnmarshalCertificatesFromPEM(signer) and if it was unable to then it did pubKey, err := sv.PublicKey().

When I made this function public, it was creating an import loop with sign.SignerVerifier, so I opted to replace that option with *crypto.PublicKey. We could try to cram / overload signerBytes, but it's more work for the places we call MakeBundle() 🤷

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh, we can clean this up later with a larger refactor.


must(cmd.Exec(ctx, args), t)
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible, can we have an e2e test that demonstrates interactions with fulcio and rekor as well? There's an example above and in my signingconfig PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to get Rekor working the e2e test, but not Fulcio 😞

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tried cosign sign --new-bundle-format without providing a --key? Just want to make sure it is working with ephemeral keys/Fulcio as expected.

What was the error you got in testing? I can try to get something working later on in the SigningConfig PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry, I should have phrased that better. I was unable to get Fulcio working locally in the e2e test. I was able to get Fulcio verification working generally (see #4316 (comment)).

But I uploaded what I was working on locally, and it seemed to work in the GitHub workflow!

@haydentherapper
Copy link
Contributor

What do we want to do about experimental-oci11? Can we add some documentation to its flag description that it's unrelated to new-bundle-format?

@steiza
Copy link
Member Author

steiza commented Aug 6, 2025

Here's a good command for testing compatibility with GitHub Artifact Attestations:

$ go run cmd/cosign/main.go verify --certificate-oidc-issuer "https://token.actions.githubusercontent.com" --certificate-identity-regexp "https://github.com/github/artifact-attestations-workflows/.github/workflows/ghcr-publish.yml@refs/heads/main" --new-bundle-format=true ghcr.io/github/artifact-attestations-workflows@sha256:06e6818324cb20e1ea6f22da7fb925323fb57251c98768c88b93872b50590f92

haydentherapper
haydentherapper previously approved these changes Aug 7, 2025
Copy link
Contributor

@haydentherapper haydentherapper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work on this!

steiza added 6 commits August 7, 2025 13:32
Signed-off-by: Zach Steindler <[email protected]>
Signed-off-by: Zach Steindler <[email protected]>
Make constant for cosign sign in-toto predicate
Update experimental OCI help string

Signed-off-by: Zach Steindler <[email protected]>
Signed-off-by: Zach Steindler <[email protected]>
Signed-off-by: Zach Steindler <[email protected]>
haydentherapper
haydentherapper previously approved these changes Aug 7, 2025
@steiza steiza merged commit 0ac382a into sigstore:main Aug 7, 2025
29 checks passed
tmeijn pushed a commit to tmeijn/dotfiles that referenced this pull request Sep 16, 2025
This MR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [cosign](https://github.com/sigstore/cosign) | minor | `2.5.3` -> `2.6.0` |

MR created with the help of [el-capitano/tools/renovate-bot](https://gitlab.com/el-capitano/tools/renovate-bot).

**Proposed changes to behavior should be submitted there as MRs.**

---

### Release Notes

<details>
<summary>sigstore/cosign (cosign)</summary>

### [`v2.6.0`](https://github.com/sigstore/cosign/blob/HEAD/CHANGELOG.md#v260)

[Compare Source](sigstore/cosign@v2.5.3...v2.6.0)

v2.6.0 introduces a number of new features, including:

- Signing an in-toto statement rather than Cosign constructing one from a predicate, along with verifying a statement's subject using a digest and digest algorithm rather than providing a file reference ([#&#8203;4306](sigstore/cosign#4306))
- Uploading a signature and its verification material (a ["bundle"](https://github.com/sigstore/protobuf-specs/blob/main/protos/sigstore_bundle.proto)) as an OCI Image 1.1 referring artifact, completing [#&#8203;3927](sigstore/cosign#3927) ([#&#8203;4316](sigstore/cosign#4316))
- Providing service URLs for signing and attesting using a [SigningConfig](https://github.com/sigstore/protobuf-specs/blob/4df5baadcdb582a70c2bc032e042c0a218eb3841/protos/sigstore_trustroot.proto#L185). Note that this is required when using a [Rekor v2](https://github.com/sigstore/rekor-tiles) instance ([#&#8203;4319](sigstore/cosign#4319))

Example generation and verification of a signed in-toto statement:

```
cosign attest-blob --new-bundle-format=true --bundle="digest-key-test.sigstore.json" --key="cosign.key" --statement="../sigstore-go/examples/sigstore-go-signing/intoto.txt"
cosign verify-blob-attestation --bundle="digest-key-test.sigstore.json" --key=cosign.pub --type=unused --digest="b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9" --digestAlg="sha256"
```

Example container signing and verification using the new bundle format and referring artifacts:

```
cosign sign --new-bundle-format=true ghcr.io/user/alpine@sha256:a19367999603840546b8612572e338ec076c6d1f2fec61760a9e11410f546733
cosign verify --new-bundle-format=true ghcr.io/user/alpine@sha256:a19367999603840546b8612572e338ec076c6d1f2fec61760a9e11410f546733
```

Example usage of a signing config provided by the public good instance's TUF repository:

```
cosign sign-blob --use-signing-config --bundle sigstore.json README.md
cosign verify-blob --new-bundle-format --bundle sigstore.json --certificate-identity $EMAIL --certificate-oidc-issuer $ISSUER --use-signed-timestamps README.md
```

v2.6.0 leverages sigstore-go's signing and verification APIs gated behind these new flags. In an upcoming major release, we will be
updating Cosign to default to producing and consuming bundles to align with all other Sigstore SDKs.

#### Features

- Add to `attest-blob` the ability to supply a complete in-toto statement, and add to `verify-blob-attestation` the ability to verify with just a digest ([#&#8203;4306](sigstore/cosign#4306))
- Have cosign sign support bundle format ([#&#8203;4316](sigstore/cosign#4316))
- Add support for SigningConfig for sign-blob/attest-blob, support Rekor v2 ([#&#8203;4319](sigstore/cosign#4319))
- Add support for SigningConfig in sign/attest ([#&#8203;4371](sigstore/cosign#4371))
- Support self-managed keys when signing with sigstore-go ([#&#8203;4368](sigstore/cosign#4368))
- Don't require timestamps when verifying with a key ([#&#8203;4337](sigstore/cosign#4337))
- Don't load content from TUF if trusted root path is specified ([#&#8203;4347](sigstore/cosign#4347))
- Add a terminal spinner while signing with sigstore-go ([#&#8203;4402](sigstore/cosign#4402))
- Require exclusively a SigningConfig or service URLs when signing ([#&#8203;4403](sigstore/cosign#4403))
- Remove SHA256 assumption in sign-blob/verify-blob ([#&#8203;4050](sigstore/cosign#4050))
- Bump sigstore-go, support alternative hash algorithms with keys ([#&#8203;4386](sigstore/cosign#4386))

#### Breaking API Changes

- `sign.SignerFromKeyOpts` no longer generates a key. Instead, it returns whether or not the client needs to generate a key, and if so, clients
  should call `sign.KeylessSigner`. This allows clients to more easily manage key generation.

#### Bug Fixes

- Verify subject with bundle only when checking claims ([#&#8203;4320](sigstore/cosign#4320))
- Fixes to cosign sign / verify for the new bundle format ([#&#8203;4346](sigstore/cosign#4346))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever MR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this MR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this MR, check this box

---

This MR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4xMTMuNSIsInVwZGF0ZWRJblZlciI6IjQxLjExMy41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJSZW5vdmF0ZSBCb3QiXX0=-->
@steiza steiza deleted the sign-new-bundle branch October 6, 2025 14:34
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