fix: make Ed25519 keys usable again for sign-blob, attest-blob and verify --key - #5074
Open
hamodywe wants to merge 1 commit into
Open
fix: make Ed25519 keys usable again for sign-blob, attest-blob and verify --key#5074hamodywe wants to merge 1 commit into
hamodywe wants to merge 1 commit into
Conversation
…rify --key
With the v3 defaults, an Ed25519 key that `cosign import-key-pair` accepts
cannot sign, attest, or verify anything:
$ cosign sign-blob --key ed25519.key --bundle blob.sigstore.json blob
error signing bundle: failed to verify signature: could not verify
message: failed to verify signature
$ cosign attest-blob --key ed25519.key --predicate p.json --type custom \
--bundle att.sigstore.json blob
error signing bundle: could not verify envelope: accepted signatures do
not match threshold, Found: 0, Expected 1
Ed25519 has two schemes that share a key and are not interchangeable, and
cosign's own pieces disagreed about which one is in use:
* A key loaded for the transparency log signs as Ed25519ph (the only
scheme a hashedrekord entry takes). The verifier the bundle is checked
against right after signing was loaded with LoadDefaultVerifier, which
is pure Ed25519, so every keyed signing run failed its own post-signing
verification. The verifier is now loaded to match the keypair's
signing algorithm.
* sigstore-go verifies a DSSE envelope with pure Ed25519 and reserves
Ed25519ph for message signatures, but attestations loaded the key with
the default options and signed the envelope as Ed25519ph. Attestations
now load an Ed25519 key as pure Ed25519.
* A verifier loaded from --key was always pure Ed25519, so even a
correctly written bundle could not be verified. It now accepts both
schemes, trying the message-signature one first, the same compatibility
approach sigstore-go takes for certificates.
None of this touches ECDSA or RSA, image signing, or the legacy bundle
format; those paths still load and verify exactly as before. Also replaces
the log.Fatal calls in the bundle signing path with returned errors, so a
library caller gets an error rather than an exit.
Signed-off-by: hamodywe <iosapk.org@gmail.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
hamodywe
force-pushed
the
fix/ed25519-scheme-agreement
branch
from
August 26, 2026 11:31
3145e07 to
900669a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
With the v3 defaults, an Ed25519 key that
cosign import-key-pairaccepts cannot sign, attest, or verify anything. Three places in cosign disagree about which Ed25519 scheme is in use; this makes them agree with sigstore-go's convention and with each other.Reproduction (on
main,58aae9e)Every other key type in the same matrix — RSA PKCS#1 and PKCS#8, EC P-256 SEC1 and PKCS#8, P-384 — signs and verifies. Only Ed25519 fails, and it fails on every run, not on an edge case.
Why
Ed25519 has two schemes that share a key and are not interchangeable: pure Ed25519 and Ed25519ph. sigstore-go's convention (
compatSignatureVerifier) is Ed25519ph for message signatures — the only scheme a hashedrekord entry takes — and pure Ed25519 for DSSE envelopes. cosign'sGetDefaultLoadOptionsloads a key as Ed25519ph, which is right for message signatures, but three other places did not follow:pkg/cosign/bundle/sign.go— the verifier the bundle is checked against right after signingLoadDefaultVerifier(pubKey)→ pure Ed25519, so an Ed25519ph signature failed the bundle's own post-signing verificationkeypair.GetSigningAlgorithm()signcommon.NewAttestationBundle— attestationspkg/cosign/verify.go— a verifier loaded from--keyLoadDefaultVerifier→ pure Ed25519 only, so even a correct bundle could not be verified with the public keyThe first is the one the e2e comment in
TestSignBlobNewBundleNonDefaultAlgorithmalready describes ("By default, we sign using the prehash variant for a ed25519 key. Rekor supports ed25519ph for a hashedrekord"); the verifier just was not loaded the same way.ECDSA and RSA are untouched:
verifierForKeypairadds no option for them,ed25519CompatibleVerifierreturns a non-Ed25519 verifier unchanged, and the attestation change only affects the load options of an Ed25519 key. Image signing and the legacy bundle format go through the sameSignDataand are unaffected. Also replaces thelog.Fatalcalls in that signing path with returned errors, so a library caller gets an error rather than an exit.After
Same key, same commands, default configuration (signatures uploaded to the public log):
The bundle carries a
SHA2_512message digest and a Rekor entry, as expected for Ed25519ph. Controls run in the same session: EC P-256, P-384 and RSA sign-blob/verify-blob, and an ECDSA attest-blob/verify-blob-attestation, allVerified OKbefore and after.Tests
pkg/cosign/bundle:verifierForKeypairaccepts the signature of an ephemeralPKIX_ED25519_PH,PKIX_ED25519andPKIX_ECDSA_P256_SHA_256keypair; a second test pins thatLoadDefaultVerifierrejects an Ed25519ph signature — the mistake the helper exists to avoid.pkg/cosign:ed25519CompatibleVerifieraccepts an Ed25519ph and a pure signature over the same message, rejects a signature over other content, reports the same public key, and returns a non-Ed25519 verifier unchanged.go testforpkg/cosign/...,cmd/cosign/cli/{sign,signcommon,verify,attest}/...andinternal/key/...passes.Found by round-tripping
sign-blob→verify-blobandattest-blob→verify-blob-attestationover every key typeimport-key-pairaccepts.Disclosure: I used Claude (Opus 5) while investigating and writing this. Every command above I ran and checked myself, and review comments will be answered by me.