Description
cosign verify-blob --key fails against a bundle cosign sign-blob --key produced moments earlier, when the key is ECDSA P-384. The same sequence with a P-256 key succeeds.
The bundle is internally correct. It records SHA2_384 as the digest algorithm, the recorded digest is the correct SHA-384 of the input, and the signature verifies against that digest with the public key when checked with openssl. Only cosign's own verification of it fails.
Declaring the algorithm at signing with --signing-algorithm ecdsa-sha2-384-nistp384 does not change the outcome. verify-blob exposes no corresponding flag, and the algorithm recorded in the bundle appears not to be honoured on the key based verification path.
Versions affected
v3.1.3 on linux/x86_64
v3.1.3 on darwin/arm64 (GitCommit 11926fa5bbbbde47e88fc006b625a17769b743b2)
v2.6.1 on linux/x86_64
v2.6.1 is after #4050, "Remove SHA256 assumption in sign-blob/verify-blob", which was released in v2.6.0. That change appears not to cover this path.
Reproduction
No registry, no transparency log, no key management service, no credentials.
export COSIGN_PASSWORD=""
# 1. a P-384 key
openssl genpkey -algorithm EC -pkeyopt ec_paramgen_curve:P-384 -out p384.pem
cosign import-key-pair --key p384.pem --output-key-prefix p384
# 2. sign, declaring the algorithm
echo "hello" > blob.txt
cosign sign-blob --key p384.key \
--signing-algorithm ecdsa-sha2-384-nistp384 \
--bundle p384.bundle \
--tlog-upload=false --use-signing-config=false --yes blob.txt
# 3. verify with the same tool and the matching public key
cosign verify-blob --key p384.pub --bundle p384.bundle \
--insecure-ignore-tlog=true blob.txt
Observed
Signing artifact...
Wrote bundle to file p384.bundle
WARNING: Skipping tlog verification is an insecure practice that lacks transparency and auditability verification for the blob.
Error: failed to verify signature: could not verify message: invalid signature when validating ASN.1 encoded signature
error during command execution: failed to verify signature: could not verify message: invalid signature when validating ASN.1 encoded signature
Expected
Verified OK, as the identical sequence gives with a P-256 key.
The bundle is correct, and the signature is valid
The bundle records:
mediaType application/vnd.dev.sigstore.bundle.v0.3+json
messageSignature/messageDigest/algorithm SHA2_384
The recorded digest equals sha384(blob.txt), and the signature verifies against it:
python3 - <<'PY'
import json, base64, hashlib
b = json.load(open('p384.bundle'))
ms = b['messageSignature']
recorded = base64.b64decode(ms['messageDigest']['digest'])
actual = hashlib.sha384(open('blob.txt','rb').read()).digest()
print("recorded digest == sha384(blob):", recorded == actual)
open('sig.der','wb').write(base64.b64decode(ms['signature']))
PY
openssl pkey -in p384.pem -pubout -out p384.pubkey.pem
openssl dgst -sha384 -verify p384.pubkey.pem -signature sig.der blob.txt
gives
recorded digest == sha384(blob): True
Verified OK
So the signature cosign produced is valid over the digest cosign recorded, using the algorithm cosign recorded. The failure is in verification alone.
Control
The same sequence with a P-256 key returns Verified OK from cosign verify-blob in both v2.6.1 and v3.1.3.
Independently reproduced with a hardware backed key on another platform
The reproduction above uses a software key file, which leaves open the possibility that the fault lies in key import or in local key handling. It does not.
The same failure occurs on darwin/arm64 with cosign v3.1.3 against non exportable EC-HSM keys held in Azure Key Vault, addressed through the azurekms:// key management service provider. No private key material exists outside the hardware module, and the signing operation is performed by the module.
| Curve |
Signer |
Verifier |
Result |
| P-256 |
Key Vault EC-HSM, azurekms:// |
public key exported to PEM |
Verified OK |
| P-384 |
Key Vault EC-HSM, azurekms:// |
public key exported to PEM |
invalid signature when validating ASN.1 encoded signature |
Both blobs were the same input file, signed minutes apart with the same cosign binary against the same vault, with the curve the only variable.
The image path fails differently, and it is the same defect
cosign sign and cosign verify on an OCI image wrap the signature in a DSSE envelope, and the failure there surfaces as:
could not verify envelope: accepted signatures do not match threshold, Found: 0, Expected 1
rather than as the ASN.1 message above. Signing the same image with a P-256 key from the same vault and verifying it with the exported public key returns Verified OK. Anyone searching for the threshold message is looking at this defect, and the two messages should be linked.
Why this may have gone unreported
cosign generate-key-pair produces P-256, so the default path is unaffected. The defect is reached only by supplying a P-384 key, which is the common case for organisations whose signing keys are generated in a hardware security module under a FIPS 140-3 posture and cannot be exported.
Impact
An artefact signed with a P-384 key carries a valid signature that cosign cannot check. A verifier following the documented cosign verify path concludes the artefact is unverified, when it is correctly signed. That is a false negative in a verification tool, which is the direction that erodes trust in signing rather than the direction that breaks it, but it makes P-384 unusable for release signing in practice.
Environment
Two platforms, two key custody models, same result.
- Linux x86_64, cosign binaries downloaded from the project's GitHub releases, software key files.
- macOS arm64, cosign v3.1.3, non exportable EC-HSM keys in Azure Key Vault via
azurekms://.
OpenSSL and a short Python script are used only as independent checks and are not part of the reproduction.
Description
cosign verify-blob --keyfails against a bundlecosign sign-blob --keyproduced moments earlier, when the key is ECDSA P-384. The same sequence with a P-256 key succeeds.The bundle is internally correct. It records
SHA2_384as the digest algorithm, the recorded digest is the correct SHA-384 of the input, and the signature verifies against that digest with the public key when checked withopenssl. Only cosign's own verification of it fails.Declaring the algorithm at signing with
--signing-algorithm ecdsa-sha2-384-nistp384does not change the outcome.verify-blobexposes no corresponding flag, and the algorithm recorded in the bundle appears not to be honoured on the key based verification path.Versions affected
v3.1.3on linux/x86_64v3.1.3on darwin/arm64 (GitCommit11926fa5bbbbde47e88fc006b625a17769b743b2)v2.6.1on linux/x86_64v2.6.1 is after #4050, "Remove SHA256 assumption in sign-blob/verify-blob", which was released in v2.6.0. That change appears not to cover this path.
Reproduction
No registry, no transparency log, no key management service, no credentials.
Observed
Expected
Verified OK, as the identical sequence gives with a P-256 key.The bundle is correct, and the signature is valid
The bundle records:
The recorded digest equals
sha384(blob.txt), and the signature verifies against it:gives
So the signature cosign produced is valid over the digest cosign recorded, using the algorithm cosign recorded. The failure is in verification alone.
Control
The same sequence with a P-256 key returns
Verified OKfromcosign verify-blobin both v2.6.1 and v3.1.3.Independently reproduced with a hardware backed key on another platform
The reproduction above uses a software key file, which leaves open the possibility that the fault lies in key import or in local key handling. It does not.
The same failure occurs on darwin/arm64 with cosign v3.1.3 against non exportable EC-HSM keys held in Azure Key Vault, addressed through the
azurekms://key management service provider. No private key material exists outside the hardware module, and the signing operation is performed by the module.azurekms://Verified OKazurekms://invalid signature when validating ASN.1 encoded signatureBoth blobs were the same input file, signed minutes apart with the same cosign binary against the same vault, with the curve the only variable.
The image path fails differently, and it is the same defect
cosign signandcosign verifyon an OCI image wrap the signature in a DSSE envelope, and the failure there surfaces as:rather than as the ASN.1 message above. Signing the same image with a P-256 key from the same vault and verifying it with the exported public key returns
Verified OK. Anyone searching for the threshold message is looking at this defect, and the two messages should be linked.Why this may have gone unreported
cosign generate-key-pairproduces P-256, so the default path is unaffected. The defect is reached only by supplying a P-384 key, which is the common case for organisations whose signing keys are generated in a hardware security module under a FIPS 140-3 posture and cannot be exported.Impact
An artefact signed with a P-384 key carries a valid signature that cosign cannot check. A verifier following the documented
cosign verifypath concludes the artefact is unverified, when it is correctly signed. That is a false negative in a verification tool, which is the direction that erodes trust in signing rather than the direction that breaks it, but it makes P-384 unusable for release signing in practice.Environment
Two platforms, two key custody models, same result.
azurekms://.OpenSSL and a short Python script are used only as independent checks and are not part of the reproduction.