Skip to content

fix(x509): support ed25519 private keys in the x509 signer - #1868

Open
pujitha24 wants to merge 2 commits into
tektoncd:mainfrom
pujitha24:auto/issue-1189
Open

fix(x509): support ed25519 private keys in the x509 signer#1868
pujitha24 wants to merge 2 commits into
tektoncd:mainfrom
pujitha24:auto/issue-1189

Conversation

@pujitha24

Copy link
Copy Markdown
Contributor

Changes

The x509 signer's docs (docs/signing.md) state that the private key stored
in the x509.pem secret may be of type ed25519 or ecdsa, but
x509Signer() only ever handled *ecdsa.PrivateKey. Any other PKCS8 key
type — including ed25519 — was rejected with
unsupported private key type ..., only ECDSA keys are supported (this used
to be an unchecked type assertion that panicked the controller; a prior fix
turned that into a graceful error, but ed25519 keys still could not be used
to sign at all, even though the docs say they should work).

This replaces the single pk.(*ecdsa.PrivateKey) assertion with a type
switch that also handles ed25519.PrivateKey via
signature.LoadED25519SignerVerifier (from the already-vendored
github.com/sigstore/sigstore/pkg/signature package used elsewhere in this
file). Any other key type still falls through to the existing descriptive
error, now mentioning both supported types.

The TestSigner_SignED25519 test, previously skipped with
t.Skip("skip test until ed25519 signing is implemented"), is un-skipped
and now passes. TestNewSignerUnsupportedX509KeyType (which exercises the
"unsupported key type" error path) was switched from an ed25519 key to a
freshly generated RSA key, since ed25519 is no longer unsupported.

This is scoped to the x509.pem load path only; the cosign.key path
(cosignSigner) already supports ed25519 via cosign's own key loader and is
unaffected.

Submitter Checklist

As the author of this PR, please check off the items in this checklist:

  • Has Docs included if any changes are user facing
  • Has Tests included if any functionality added or changed
  • Follows the commit message standard
  • Meets the Tekton contributor standards (including
    functionality, content, code)
  • Release notes block below has been updated with any user facing changes (API changes, bug fixes, changes requiring upgrade notices or deprecation warnings)
  • Release notes contains the string "action required" if the change requires additional action from users switching to the new release

Release Notes

The x509 signer now supports ed25519 private keys (in addition to ECDSA), matching the documented behavior. Previously, an ed25519 `x509.pem` signing key was rejected with an "unsupported private key type" error.

AI assistance: this change was drafted with Claude Code.

Fixes #1189

Motivation:
docs/signing.md documents that the x509 signer's `x509.pem` secret may
hold a key "of type `ed25519` or `ecdsa`", but `x509Signer()` only ever
handled `*ecdsa.PrivateKey`. Any other PKCS8 key type, including
ed25519, was rejected with "unsupported private key type ..., only
ECDSA keys are supported". A prior fix already turned the original
unchecked type assertion (which used to panic the controller on an
ed25519 key) into this graceful error, but ed25519 keys still could
not actually be used to sign, even though the docs say they should
work. The test suite already had a `TestSigner_SignED25519` test
skipped with "skip test until ed25519 signing is implemented",
confirming this was the known follow-up.

Approach:
Replace the single `pk.(*ecdsa.PrivateKey)` type assertion in
x509Signer() with a type switch that also handles
`ed25519.PrivateKey` via `signature.LoadED25519SignerVerifier`, from
the sigstore signature package already used elsewhere in this file.
Any other key type still falls through to the existing descriptive
error, updated to mention both supported types. This is scoped to the
x509.pem load path only; the cosign.key path already supports ed25519
via cosign's own key loader.

Un-skipped TestSigner_SignED25519, which signs a payload with an
ed25519 key and verifies it with ed25519.Verify. Switched
TestNewSignerUnsupportedX509KeyType from an ed25519 key to a freshly
generated RSA key, since ed25519 is no longer an unsupported type.

Validation:
- go build ./...
- go test ./pkg/chains/signing/x509/... -v : all tests pass, including
  the newly un-skipped TestSigner_SignED25519.
- go test ./pkg/chains/... : all pass.
- make golangci-lint PKG=./pkg/chains/signing/x509/... : 0 issues.
- Confirmed this is a real regression test: with only x509.go reverted
  (test left un-skipped), TestSigner_SignED25519 fails with
  "unsupported private key type ed25519.PrivateKey, only ECDSA keys
  are supported"; with the fix applied it passes.

Report: tektoncd#1189
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
@tekton-robot
tekton-robot requested review from jkhelil and wlynch August 10, 2026 08:12
@tekton-robot tekton-robot added the size/M Denotes a PR that changes 30-99 lines, ignoring generated files. label Aug 10, 2026
@anithapriyanatarajan

anithapriyanatarajan commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

@pujitha24 Thank you for this. Could you verify with transparency.enabled=true and confirm the rekor uploads and cosign verification work fine E2E for the artifact signature and Provenance attestation?

@pujitha24

Copy link
Copy Markdown
Contributor Author

Fair ask, but I don't have a live cluster with Fulcio/Rekor set up in my environment, so I haven't run this end-to-end with transparency.enabled=true — I don't want to claim that's verified when it isn't.

What I can say from reading the code: this diff only touches key loading in x509Signer. Once the key is loaded, both the ECDSA and the new ed25519 branch return a signature.SignerVerifier wrapped in the same Signer struct, and rekor.go's upload path (pkg/chains/rekor.go) operates on that generic interface with no key-type branching, so it should exercise the identical rekor-upload/cosign-verify path that ECDSA already uses today. That's inference from the code, not something I've tested live.

If you can point me to a cluster/kind-e2e setup to run this against, or would rather I add an ed25519 case to the existing e2e suite, happy to do either — let me know which you'd prefer.

@anithapriyanatarajan

Copy link
Copy Markdown
Contributor

Fair ask, but I don't have a live cluster with Fulcio/Rekor set up in my environment, so I haven't run this end-to-end with transparency.enabled=true — I don't want to claim that's verified when it isn't.

What I can say from reading the code: this diff only touches key loading in x509Signer. Once the key is loaded, both the ECDSA and the new ed25519 branch return a signature.SignerVerifier wrapped in the same Signer struct, and rekor.go's upload path (pkg/chains/rekor.go) operates on that generic interface with no key-type branching, so it should exercise the identical rekor-upload/cosign-verify path that ECDSA already uses today. That's inference from the code, not something I've tested live.

If you can point me to a cluster/kind-e2e setup to run this against, or would rather I add an ed25519 case to the existing e2e suite, happy to do either — let me know which you'd prefer.

Summarizing below possible options:

For key-based signing with transparency, by default Chains uploads to the public Rekor (https://rekor.sigstore.dev, the default). So you could verify this with kind cluster and ko apply of your PR.

1. Create an Ed25519 signing secret

openssl genpkey -algorithm ED25519 -out ed25519.pem
kubectl create secret generic signing-secrets -n tekton-chains \
  --from-file=x509.pem=ed25519.pem --dry-run=client -o yaml | kubectl apply -f -

2. Enable transparency (default URL is public Rekor)

kubectl patch configmap chains-config -n tekton-chains -p '{"data":{
  "transparency.enabled":"true",
  "artifacts.taskrun.format":"slsa/v2alpha4",
  "artifacts.oci.format":"simplesigning"
}}'

3. Run both cases and watch the controller log around the Rekor upload

kubectl logs -n tekton-chains deploy/tekton-chains-controller -f | grep -i "rekor\|tlog\|transparency"

Alternate option: the repo's TestRekor in test/e2e_test.go already sets transparency.enabled: "true" for both formats. You could swap the signing secret to an Ed25519 key and run it.

@pujitha24

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed steps. Unfortunately my environment doesn't have docker/kind/kubectl/ko available, so I can't actually stand up a cluster and run either the manual walkthrough or the e2e suite myself — I'd just be guessing at the outcome, which I don't want to do.

I could add an ed25519 variant to TestRekor in test/e2e_test.go (and the key generation in test/clients.go, which currently hardcodes ECDSA for signing-secrets) so the case exists for whoever has cluster access to run — but I wouldn't be able to run or verify it myself either. Would that be useful as a starting point, or would you rather run the manual verification on your end since you already have a cluster set up?

@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 62.19%. Comparing base (0548d01) to head (1035926).
⚠️ Report is 8 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1868      +/-   ##
==========================================
+ Coverage   61.90%   62.19%   +0.29%     
==========================================
  Files          64       64              
  Lines        4071     4092      +21     
==========================================
+ Hits         2520     2545      +25     
+ Misses       1269     1266       -3     
+ Partials      282      281       -1     
Flag Coverage Δ
unit-tests 62.19% <ø> (+0.29%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 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.

@pujitha24

Copy link
Copy Markdown
Contributor Author

/assign @anithapriyanatarajan

@anithapriyanatarajan

Copy link
Copy Markdown
Contributor

Thanks for the detailed steps. Unfortunately my environment doesn't have docker/kind/kubectl/ko available, so I can't actually stand up a cluster and run either the manual walkthrough or the e2e suite myself — I'd just be guessing at the outcome, which I don't want to do.

@pujitha24 We do need E2E evidence. Per the Tekton contribution standards, changes like this need to be verified end-to-end, and that bar matters especially for this PR. Once we merge this we are effectively telling users this mode is supported. If it turns out not to work as expected, for example the user reports "I signed my artifact and now I can't verify it", which is a much worse failure for users than a slower review.

On the tooling: standing up a local environment is simple, and it's a one-time setup that will pay off for future contributions too. DEVELOPMENT.md walks through it, but roughly:

  • Install kind, ko, and kubectl (all single-binary installs, no privileged infra needed)
  • kind create cluster to get a local cluster
  • Install Tekton Pipelines, then deploy your build of Chains with ko apply -f config/
  • Run a TaskRun, then verify the resulting signature/attestation

Happy to help if you are stuck anywhere with these steps

What I am looking for on the PR before approving:

  1. Confirmation the controller comes up cleanly with your change deployed
  2. The manual walkthrough run against a real TaskRun, with the signing and verification output pasted in (this is the part that proves the mode actually works)
  3. The E2E suite passing, or a note on which cases you couldn't cover and why

Once that's on the PR I'm happy to approve. Thank you.

anithapriyanatarajan asked for end-to-end evidence that ed25519
x509.pem keys actually sign, upload to Rekor, and verify against a
real cluster, on top of the unit tests already added in this PR. This
sandbox has no docker/kind/kubectl/ko, so that manual walkthrough
cannot be produced here.

As a partial, honest answer to the "E2E suite passing" ask, add a
taskrun-ed25519 case to TestRekor's table so the e2e job (which does
run against a real cluster) exercises the new code path: generate an
ed25519 key instead of ECDSA for signing-secrets, let the controller
sign with it, upload to the real Rekor, and verify the signature.

To let the test secret hold either key type, change secret.x509priv
from the concrete *signature.ECDSASignerVerifier to the
signature.SignerVerifier interface, which both ECDSASignerVerifier and
ED25519SignerVerifier satisfy. Two call sites used .Public(), a method
promoted from the concrete ECDSA type and not part of the interface;
switched them to .PublicKey(), which is.

This does not touch the already-committed x509.go change and does not
run in this environment; it only adds coverage for whoever/whatever
next runs the e2e suite.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Assisted-by: Claude Sonnet 5 (via Claude Code)
@tekton-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
To complete the pull request process, please ask for approval from anithapriyanatarajan after the PR has been reviewed.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@tekton-robot tekton-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/M Denotes a PR that changes 30-99 lines, ignoring generated files. labels Aug 17, 2026
@pujitha24

Copy link
Copy Markdown
Contributor Author

Since I still don't have kind/kubectl/ko in this sandbox, I can't produce the manual walkthrough or confirm the controller comes up cleanly — I don't want to fake that. What I could do is add a taskrun-ed25519 case to TestRekor in test/e2e_test.go (commit 1035926), which swaps the signing-secrets x509.pem for an ed25519 key via a new useEd25519Signer option in test/clients.go, so the real e2e job exercises the new code path end to end — sign, Rekor upload, verify — the same way the existing ECDSA case does. That required widening secret.x509priv from *signature.ECDSASignerVerifier to the signature.SignerVerifier interface and switching two .Public() call sites to .PublicKey().

This is item 3 from your list (E2E suite coverage), but items 1 and 2 (controller-up confirmation, pasted signing/verification output) still need someone with cluster access — I can't produce those here. If you're able to run the e2e suite (or the manual steps you outlined) against this branch, that would close the gap; otherwise let me know if there's another way you'd like this verified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

panic using ed25519 signing key

3 participants