Skip to content

fix: use sigstore-go TUF client for verify to match initialize#763

Open
SequeI wants to merge 2 commits intosigstore:mainfrom
SequeI:fixTuf
Open

fix: use sigstore-go TUF client for verify to match initialize#763
SequeI wants to merge 2 commits intosigstore:mainfrom
SequeI:fixTuf

Conversation

@SequeI
Copy link
Copy Markdown

@SequeI SequeI commented Feb 11, 2026

Summary

gitsign initialize writes the TUF cache in sigstore-go format, but verify was reading using the old sigstore/sigstore TUF client which expects a different cache layout. This caused verify to fall back to its expired embedded root. Switch all TUF reads to sigstore-go so initialize and verify use the same cache.

Now, gitsign verify will work with gitsign/cosign initialize as it works off the same sigstore cache format.

Release Note

Documentation

gitsign initialize writes the TUF cache in sigstore-go format, but
verify was reading using the old sigstore/sigstore TUF client which
expects a different cache layout. This caused verify to fall back to
its expired embedded root. Switch all TUF reads to sigstore-go so
initialize and verify use the same cache.

Signed-off-by: SequeI <asiek@redhat.com>
@gittuf-app-beta
Copy link
Copy Markdown

Observed review from tommyd450+59835082 (@tommyd450)

Signed-off-by: SequeI <asiek@redhat.com>
@SequeI
Copy link
Copy Markdown
Author

SequeI commented Feb 18, 2026

I don't think the gosec issues are related to anything I changed

@noopduck
Copy link
Copy Markdown

noopduck commented Apr 30, 2026

I applied this locally and this is the way! I think this needs priority because self hosted sigstore stack's are failing without this

Thereis also this issue, where TSA fails validation because it's validating it incorrectly, let me know if im missing something;

diff --git a/internal/fork/ietf-cms/verify.go b/internal/fork/ietf-cms/verify.go
index fb5fb7e..ec3caf5 100644
--- a/internal/fork/ietf-cms/verify.go
+++ b/internal/fork/ietf-cms/verify.go
@@ -171,7 +171,7 @@ func (sd *SignedData) verify(econtent []byte, opts x509.VerifyOptions, tsOpts x5
                        // This check is slightly redundant, given that the cert validity times
                        // are checked by cert.Verify. We take the timestamp accuracy into account
                        // here though, whereas cert.Verify will not.
-                       if !tsti.Before(cert.NotAfter) || !tsti.After(cert.NotBefore) {
+                       if tsti.After(cert.NotAfter) || tsti.Before(cert.NotBefore) {
                                return nil, x509.CertificateInvalidError{Cert: cert, Reason: x509.Expired, Detail: "timestamp authority verification failed"}
                        }

@SequeI
Copy link
Copy Markdown
Author

SequeI commented Apr 30, 2026

@noopduck I have not looked at this in a while since it got no attention, but I am really happy to hear it could help you out a little bit !

I'll rebase, fix linter, and fix TSA tomorrow; run full tests from a private instance and try to get the maintainers eyes

Copy link
Copy Markdown
Member

@wlynch wlynch left a comment

Choose a reason for hiding this comment

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

Sorry for missing this! Left some comments.

// FromTUF loads certs from the TUF client.
func FromTUF(ctx context.Context) CertificateSource {
// FromTUF loads Fulcio certificates from the sigstore-go TUF cache.
func FromTUF(_ context.Context) CertificateSource {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Plumb through the context to the tuf.Options

// FromTUF loads Fulcio certificates from the sigstore-go TUF cache.
func FromTUF(_ context.Context) CertificateSource {
return func() ([]*x509.Certificate, error) {
tufClient, err := tuf.NewFromEnv(ctx)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think there's a regression here where we'd look for TUF_ROOT env var for a local root, though idk how documented this was in the broader sigstore clients. I don't think sigstore-go handles this natively though.

}
if len(targets) == 0 {
return nil, errors.New("none of the Fulcio roots have been found")
return nil, fmt.Errorf("initializing tuf: %w", err)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
return nil, fmt.Errorf("initializing tuf: %w", err)
return nil, fmt.Errorf("fetching fulcio certs: %w", err)

// limitations under the License.

package version
package version // nolint:revive
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Don't blanket disable linting rules like this - prefer disabling per-line with a description (happy to resolve any of these in another PR).

Comment on lines +35 to +45
opts := tuf.DefaultOptions()
if mirror, err := readRemoteHint(opts.CachePath); err == nil && mirror != "" {
opts.RepositoryBaseURL = mirror
}
if opts.RepositoryBaseURL != tuf.DefaultMirror {
cachedRoot := filepath.Join(opts.CachePath, tuf.URLToPath(opts.RepositoryBaseURL), "root.json")
if rootBytes, err := os.ReadFile(cachedRoot); err == nil {
opts.Root = rootBytes
}
}
return opts
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I assume this is to replicate cosign behavior? I wonder if there's something we can do to standardize that 🤔 (for now link to other similar behavior to explain why we're doing this / why sigstore-go doesn't handle it).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this is more of a regression after 0.13, but it's possible that it makes sense, i haven't done a diff between 0.13 and 0.14... gitsign only started referencing that expired embedded root cert after gitsign version 0.13. So I rolled back and was testing with 0.13 and tufmirror defined and it would work fine until i referenced timestampserverurl (tsa) in the git config. All commits i did in mye testing repo was fine and tsa got correctly set inside the commits, however when i tried to verify them with gitsign verify, they always claimed to be invalid, even though their not before and not after parts where correct. This is where my proposal came in, i think that these fixups are very important for gitsign, since we are experiencing functionality breaking in 0.14 along with this, which was already an issue before 0.13 as well, at least it would seem:

diff --git a/internal/fork/ietf-cms/verify.go b/internal/fork/ietf-cms/verify.go
index fb5fb7e..ec3caf5 100644
--- a/internal/fork/ietf-cms/verify.go
+++ b/internal/fork/ietf-cms/verify.go
@@ -171,7 +171,7 @@ func (sd *SignedData) verify(econtent []byte, opts x509.VerifyOptions, tsOpts x5
                        // This check is slightly redundant, given that the cert validity times
                        // are checked by cert.Verify. We take the timestamp accuracy into account
                        // here though, whereas cert.Verify will not.
-                       if !tsti.Before(cert.NotAfter) || !tsti.After(cert.NotBefore) {
+                       if tsti.After(cert.NotAfter) || tsti.Before(cert.NotBefore) {
                                return nil, x509.CertificateInvalidError{Cert: cert, Reason: x509.Expired, Detail: "timestamp authority verification failed"}
                        }

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.

4 participants