sigstore / cosign

Code signing and transparency for containers and binaries
Apache License 2.0
4.49k stars 547 forks source link

Cosign timestamp verification does not work with protobuf bundle format #3875

Closed cmurphy closed 2 months ago

cmurphy commented 2 months ago

Description

Signing and verifying an artifact with a legacy-format bundle with a disconnected timestamp file works:

$ go run cmd/cosign/main.go sign-blob /tmp/blob -y --bundle /tmp/blob.bundle --timestamp-server-url=https://freetsa.org/tsr --rekor-url http://localhost:3000 --fulcio-url http://localhost:5555 --rfc3161-timestamp=/tmp/timestamp.txtUsing payload from: /tmp/blob
Generating ephemeral keys...
Retrieving signed certificate...
[...snipped...]
Successfully verified SCT...
Timestamp fetched with time:  2024-09-10 18:58:41 +0000 UTC
RFC3161 timestamp written to file /tmp/timestamp.txt

using ephemeral certificate:
[...snipped...]
$ go run cmd/cosign/main.go verify-blob /tmp/blob --bundle /tmp/blob.bundle --rekor-url http://localhost:3000 --rfc3161-timestamp=/tmp/timestamp.txt --use-signed-timestamps --certificate-identity-regexp='.*' --certificate-oidc-issuer-regexp='.*'
Verified OK

Signing an artifact with a protobuf bundle with an embedded timestamp works, but verification does not:

$ go run cmd/cosign/main.go sign-blob /tmp/blob -y --bundle /tmp/blob.bundle --timestamp-server-url=https://freetsa.org/tsr --rekor-url http://localhost:3000 --fulcio-url http://localhost:5555 --new-bundle-format
Using payload from: /tmp/blob
Generating ephemeral keys...
Retrieving signed certificate...
[...snipped...]
Successfully verified SCT...
Timestamp fetched with time:  2024-09-10 19:01:23 +0000 UTC
using ephemeral certificate:
[...snipped...]
$ go run cmd/cosign/main.go verify-blob /tmp/blob --bundle /tmp/blob.bundle --rekor-url http://localhost:3000 --use-signed-timestamps --certificate-identity-regexp='.*' --certificate-oidc-issuer-regexp='.*' --new-bundle-format --trusted-root /home/colleenmurphy/.sigstore/root/targets/trusted_root.json
Error: failed to verify timestamps: threshold not met for verified signed timestamps: 0 < 1
main.go:74: error during command execution: failed to verify timestamps: threshold not met for verified signed timestamps: 0 < 1
exit status 1

The trusted root contains the certificate chain for the given timestamp authority: https://gist.github.com/cmurphy/cf1d42615eae8bbcbe332d4b91cc4baf

If this is user error, then it should be addressed with documentation.

Version

$ git rev-parse --short HEAD
dee0b23f
cmurphy commented 2 months ago

It was indeed user error, but still something documentation could help with.

It seems like this comes down to the different TUF clients in use between cosign and sigstore-go, and the fact that the TSA I've chosen (which is recommended in the cosign help) does not have intermediate certificates, only a root and a leaf cert: https://freetsa.org/index_en.php

My TUF mirror was set up to include both a trusted_root.json and the individual TSA targets tsa_leaf.crt.pem and tsa_root.crt.pem. The Legacy TUF client used in cosign sees that there are no intermediates and so the timestamp authority verification skips the check for the timestamping extended key usage. When I created my trusted_root.json, I erroneously included both the leaf and the root. sigstore-go interpreted the root as an intermediate, and since it did not have the timestamping extension (which it shouldn't, as a CA), it failed to verify. Updating the trusted_root.json to only include the TSA's CA and not the leaf cert fixed the issue.

haydentherapper commented 2 months ago

When I created my trusted_root.json, I erroneously included both the leaf and the root. sigstore-go interpreted the root as an intermediate, and since it did not have the timestamping extension (which it shouldn't, as a CA), it failed to verify. Updating the trusted_root.json to only include the TSA's CA and not the leaf cert fixed the issue.

Hm, you should be able to include both the CA certificate and leaf certificate for the TSA. For timestamp verification, a leaf is optional, but if present, it will be used in verification. If the leaf is not present, then it is expected to be embedded in the signed timestamp, which is a parameter of the timestamp request.

I think the issue is the order of the certificates in the trust root, see the protobuf file which specifies the last cert in the chain should be the trust anchor, eg leaf is index 0, intermediates are 1 to n-1, and index n is the root, which is how sigstore-go parses the cert chain. Looking at the gist, I think the order is flipped.