Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade to TUF v2 client #3844

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

Conversation

cmurphy
Copy link
Contributor

@cmurphy cmurphy commented Aug 20, 2024

Use sigstore-go's TUF client to fetch the trusted_root.json from the TUF
mirror, if available. Where possible, use sigstore-go's verifiers which
natively accept the trusted root as its trusted material. Where there is
no trusted root available in TUF or sigstore-go doesn't support a use
case, fall back to the sigstore/sigstore TUF v1 client and the existing
verifiers in cosign.

TODO:

Fixes #3548

Summary

Release Note

Documentation

@cmurphy
Copy link
Contributor Author

cmurphy commented Aug 20, 2024

There is still work to do here but I will be out for a couple of weeks so it might be worth getting some eyes on in the meantime.

Copy link

codecov bot commented Aug 20, 2024

Codecov Report

Attention: Patch coverage is 21.29032% with 244 lines in your changes missing coverage. Please review.

Project coverage is 36.28%. Comparing base (2ef6022) to head (e437b17).
Report is 229 commits behind head on main.

Files with missing lines Patch % Lines
pkg/cosign/tuf.go 0.00% 61 Missing ⚠️
pkg/cosign/verify.go 27.27% 39 Missing and 9 partials ⚠️
cmd/cosign/cli/verify/verify_attestation.go 0.00% 28 Missing ⚠️
cmd/cosign/cli/verify/verify.go 0.00% 27 Missing ⚠️
cmd/cosign/cli/initialize/init.go 0.00% 24 Missing ⚠️
pkg/cosign/tlog.go 12.50% 13 Missing and 1 partial ⚠️
...cosign/cli/fulcio/fulcioverifier/fulcioverifier.go 0.00% 13 Missing ⚠️
cmd/cosign/cli/sign/sign.go 20.00% 8 Missing ⚠️
pkg/cosign/verify_sct.go 66.66% 8 Missing ⚠️
cmd/cosign/cli/verify/verify_blob.go 77.77% 2 Missing and 2 partials ⚠️
... and 3 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3844      +/-   ##
==========================================
- Coverage   40.10%   36.28%   -3.83%     
==========================================
  Files         155      204      +49     
  Lines       10044    13029    +2985     
==========================================
+ Hits         4028     4727     +699     
- Misses       5530     7716    +2186     
- Partials      486      586     +100     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@cmurphy cmurphy force-pushed the upgrade-tuf branch 2 times, most recently from e59faee to 07e1c38 Compare August 21, 2024 00:12
@cmurphy cmurphy force-pushed the upgrade-tuf branch 4 times, most recently from 00b4edb to 820d6a8 Compare September 12, 2024 23:22
@jku
Copy link
Member

jku commented Sep 13, 2024

There is still work to do here but I will be out for a couple of weeks so it might be worth getting some eyes on in the meantime.

sorry, I missed this at the time: I will have a look today or monday

@jku
Copy link
Member

jku commented Sep 13, 2024

Sigstore-go provides a way to check for a trusted root and automatically use it if available, but can also fetch individual targets as needed if the provided TUF mirror does not supply a trusted_root.json.

Can you specify what cosign does with sigstore-go? I can see there are some invidual target fetches in the code but does cosign now use trusted_root.json by default?

@cmurphy
Copy link
Contributor Author

cmurphy commented Sep 13, 2024

Can you specify what cosign does with sigstore-go?

Cosign uses sigstore-go piecemeal for various things, but the goal of this PR is to adopt sigstore-go's TUF client wrapper instead of the client wrapper provided by sigstore/sigstore.

does cosign now use trusted_root.json by default?

It does not, that's part of the purpose of this PR and #3548. There is another PR in progress #3854 that adds a --trusted-root flag that I will have to adjust this PR to conform with.

sorry, I missed this at the time: I will have a look today or monday

I may now have to wait for that other PR to be fleshed out before I can make active progress on this, so don't feel rushed to review this. I can ping you again when it's in a more stable state.

Comment on lines 29 to 37
const (
// This is the root in the fulcio project.
fulcioTargetStr = `fulcio.crt.pem`
// This is the v1 migrated root.
fulcioV1TargetStr = `fulcio_v1.crt.pem`
// This is the untrusted v1 intermediate CA certificate, used or chain building.
fulcioV1IntermediateTargetStr = `fulcio_intermediate_v1.crt.pem`
)
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if these filenames should be configurable somewhere for private Sigstore TUF operators. The existing metadata format allows for additional targets to be added and discovered, but TUF v2 does not allow iterating over targets, so that strategy is unsupported, meaning this current diff does not necessarily support all private TUF deployments.

We could also provide a CLI utility to convert an old TUF v1 layout to a trusted_root.json and require private TUF maintainers to use it to generate a trusted root in order to support the next version of cosign. I kind of like this option as it fast tracks the adoption of the trusted root.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wonder if these filenames should be configurable somewhere for private Sigstore TUF operators

These filenames are hardcoded in the TUF v1 code so recreating them here was an attempt to align with the old way of retrieving targets.

The existing metadata format allows for additional targets to be added and discovered, but TUF v2 does not allow iterating over targets, so that strategy is unsupported, meaning this current diff does not necessarily support all private TUF deployments.

You're right, that was an oversight on my part. I had an earlier version of this that could support discovering targets from custom metadata that I will restore.

We could also provide a CLI utility to convert an old TUF v1 layout to a trusted_root.json

We could do that as a last resort, but my hope was to maintain full backwards compatibility and ease users gently toward using trusted_root.json.

Comment on lines 82 to 133
func checkValidityPeriod(start, end time.Time) tufStatus {
now := time.Now()
if now.Before(start) {
return inactive
}
if now.After(end) {
return inactive
}
return active
}
Copy link
Member

Choose a reason for hiding this comment

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

The validity period in the TrustedRoot should be compared to the timestamp provided by the timestamping service or transparency log. I understand this is not something that is known at the time that you are assembling the CheckOpts, and that makes this a hard problem, but I'm not sure we want to enforce this in this version of the code. I believe we still want to be able to verify a signature produced prior to a timestamping service's validity end date, even if the current time is after that date.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My goal here was to have something to substitute for the Active/Expired status from TUF v1 that does not seem to have an equivalent in TUF v2. Open to discarding this entirely or finding an alternative.

Copy link
Contributor

Choose a reason for hiding this comment

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

For what it's worth, I don't think Cosign is effectively using Active/Expired now, as Cosign is only printing a message to stdout if you are verifying an entry with an "expired" key. This isn't really helpful to the user because there's nothing wrong with using an expired key, it's if the key is used to verify something outside of its validity window, which isn't currently implemented in Cosign.

If it'd be easier to just not deal with Active/Expired, I'd be supportive of that. We can either rely on sigstore-go down the line to do that check, or we can file an issue to implement that feature in Cosign's verification API when using the trusted root file. I'd lean towards the latter in line with #3879.

@cmurphy
Copy link
Contributor Author

cmurphy commented Sep 20, 2024

@steiza , @codysoyland , @haydentherapper and I met to discuss a path forward on this and we decided to shrink the scope of this PR by removing the fetch of trusted_root.json, to be addressed separately.

This PR dropped the fetch of trusted_root.json, but it now fails conformance tests because the SCT returned during signing from p-g-i is signed by the ctfe_2022.pub key, which the sigstore-go TUF client has no way to know about. This worked fine with p-g-i when trusted_root.json was considered, but shows that this approach will break any deployment that has rotated keys relying on custom metadata. I'm starting to think that using the new client must also be synchronized with using trusted_root.json and that the whole thing should be guarded by a flag that users can opt into. Thoughts?

@steiza
Copy link
Member

steiza commented Sep 23, 2024

but shows that this approach will break any deployment that has rotated keys relying on custom metadata

Oof, what a mess we've made for ourselves. Yeah, this is what makes the cosign modernization effort we're working on so tricky.

I'm starting to think that using the new client must also be synchronized with using trusted_root.json and that the whole thing should be guarded by a flag that users can opt into.

This is what I'm thinking as well. #3854 could be updated to use CheckOpts as @codysoyland described in #3879. Then for those commands that support --trusted-root could also add something like --tuf-url + --tuf-root that would fetch the trusted_root.json for you, instead of having to supply it with --trusted-root.

@cmurphy cmurphy force-pushed the upgrade-tuf branch 2 times, most recently from f5d1221 to b6f5dc4 Compare September 24, 2024 20:32
@cmurphy
Copy link
Contributor Author

cmurphy commented Sep 24, 2024

The latest change adds back the root.NewLiveTrustedRoot TUF fetch, now guarded by environment variables so the user can opt in. Using environment variables seems to be pretty common in cosign and it makes it so all the TUF logic is concentrated in the pkg/cosign package and we don't have to plumb through command line flags. It doesn't add a TrustedMaterial field to CheckOpts because it's not needed with this approach, the TUF functions work mostly the same as they did before. This could still work with #3854 where that PR would allow explicitly providing a trusted_root.json file instead of relying on either TUF client. I'd be happy to chat more about this on a call.

@steiza
Copy link
Member

steiza commented Sep 25, 2024

I think we're still at odds a bit in terms of approach (but it's also possible I'm missing something!)

Ultimately, we have to decide what verification path we use in different scenarios, sigstore-go or pkg/cosign/verify.

Here's my understanding of the current state of things (which we can of course change):

Scenario Verification Path
Verifying with a new protocol buffer bundle (which requires a trusted root) sigstore-go (landed in #3796)
Verifying disparate signed materials or an old bundle with a trusted root sigstore-go (proposed in #3854)
Verifying anything with TUF v2 pkg/cosign/verify (proposed here in #3844)
Verifying anything with TUF v2 sigstore-go (suggested by @steiza - fetch the trusted root and then use proposed code in #3854)

It is certainly possible to use pkg/cosign/verify here with TUF v2, but I think our current tests are missing the case where there are several sets of verification material and today pkg/cosign/verify picks one to use instead of checking all of them, including at least SCT verification, VerifySet, and TSA verification that I listed out on #3854 (comment).

We could make this pull request address those cases. But ultimately, we need #3844 and #3854 to agree on what the verification path should be when you're using TUF v2 - sigstore-go or pkg/cosign/verify.

@steiza
Copy link
Member

steiza commented Sep 26, 2024

I was confused again this morning, and worried I lumped together too many use-cases, so I made a more detailed table:

Signed materials Verification materials Verification materials provided by Verification Path
Protocol buffer bundle requires trusted root trusted root file on disk sigstore-go (landed in #3796)
Protocol buffer bundle requires trusted root TUFv1 N/A - we require a trusted root, which TUFv1 does not provide
Protocol buffer bundle requires trusted root TUFv2 sigstore-go (suggested by @steiza - fetch the trusted root and then use code landed in #3796)
Not protocol buffer bundle trusted root trusted root file on disk sigstore-go (proposed in #3854)
Not protocol buffer bundle trusted root TUFv1 N/A - TUFv1 does not supply a trusted root, see "not trusted root" below
Not protocol buffer bundle trusted root TUFv2 pkg/cosign/verify (proposed here in #3844) or row below
Not protocol buffer bundle trusted root TUFv2 sigstore-go (suggested by @steiza - fetch the trusted root and then use proposed code in #3854) or row above
Not protocol buffer bundle not trusted root (e.g. disparate material via flags or ENV vars) TUFv1 pkg/cosign/verify (already implemented)
Not protocol buffer bundle not trusted root (e.g. disparate material via flags or ENV vars) TUFv2 This doesn't make sense - if you're using TUFv2 you're going to have a trusted root

I think I ended up in the same place though.

In the case where we don't have a new protocol buffer bundle, and we have a trusted root that we fetched with TUFv2, we need to decide if the verification path is going to be:

  • pkg/cosign/verify (in which case we need to audit and improve that verification path to handle the lists of possible verification material the trusted root provides) or
  • sigstore-go where we've already implemented that functionality (which means modifying commands individually)

@jku
Copy link
Member

jku commented Sep 26, 2024

thanks for writing that up zach, I'm trying to catch up and this is useful

@cmurphy
Copy link
Contributor Author

cmurphy commented Sep 26, 2024

@steiza thanks for the summary, I have a couple of minor clarifying questions:

This doesn't make sense - if you're using TUFv2 you're going to have a trusted root

Do you mean ideally, or currently? Using the TUF v2 client doesn't guarantee you've created a trusted_root.json and added it to your TUF repository.

pkg/cosign/verify (proposed here in #3844) or row below

Technically this PR does very little in pkg/cosign/verify.go. The scope of the TUF change also needs to include getting keys for the CT server which are needed during signing.

@jku
Copy link
Member

jku commented Sep 27, 2024

if new environment variables TUF_MIRROR, TUF_ROOT_JSON or
TUF_USE_TRUSTED_ROOT are set, use those to instantiate a TUF v2 client.

How do we expect these to get set? By end users?

It feels wrong if users need to set an environment variable to make the software use the recommended trust root mechanism.

@steiza
Copy link
Member

steiza commented Sep 27, 2024

Do you mean ideally, or currently? Using the TUF v2 client doesn't guarantee you've created a trusted_root.json and added it to your TUF repository.

Aha, I think I finally understand our different perspectives! I thought that when we use a TUF v2 client we were only going to fetch the trusted_root.json and not fetch individual files over TUF like ctfe.pub - but maybe that's that not the case? The way #3548 is worded made me think that we're doing the TUF v2 client upgrade and moving to using trusted_root.json at the same time.

If we are allowing fetching files like ctfe.pub over TUF v2, then I agree that we should use the contents of those files with the existing pkg/cosign/verify verification path (or signing, like you linked).

But if we're fetching trusted_root.json over TUF v2, that's when we need to decide if we're going to use sigstore-go or if we're going to make additional changes to pkg/cosign/verify in this pull request. The trusted root provides lists of possible verification material, which the pkg/cosign/verify was not originally written to handle.

For example, we verify the certificate with intermediateCerts *x509.CertPool that includes all the intermediate certs listed in the trusted root (great), but we always pick the first chain to verify the SCT (not great). I listed out two other similar cases on #3854 (comment), but I haven't done a close audit of pkg/cosign/verify either.

@haydentherapper
Copy link
Contributor

I've caught up on all the discussion, thanks Zach for the thorough chart!

#3548 was not clear on what was in scope for the TUF v2 upgrade. When I wrote it, my goal was to move to TUF v2 independent of the trusted root changes. This was when the client was quite new, so I wanted to be able to test out TUF v2 without making any other changes, leveraging the client to fetch root material by name. As noted in the issue, we'd still need to support TUF v1 to fallback to the custom metadata and for private deployments that are using custom metadata.

There is less motivation for doing this change by itself as we have more confidence in the TUF client now, though I had hoped we could make the change by itself still because it will keep these PRs smaller. The original version of this PR - where you initialize metadata with the new TUF client and fallback to the old TUF client - was what I had in my head when I wrote the issue.

My concern with using env vars isn't necessarily that users have to set it - functionally, TUF v1 and v2 should be equivalent, so setting these are more for our testing during development than the end users. The concern is that we have to rely on the hardcoded target names without custom metadata. If we have to rotate the roots in the next N months, then the TUF v2 client won't pick up these changes unless we hardcode more filenames. This is why I had proposed to concurrently support TUF v2 and v1 clients - leverage TUF v2 for harcoded targets, TUF v1 for custom metadata, and merge.

I also recognize that this is functionally a no-op, that we get none of the benefits of the trusted root (validity windows) and still require provided TUF root to use custom metadata (though a separate topic, we will have to for quite awhile or else we'll break old Cosign clients).

At this point, I think we need to make a call on next steps. Are we going to interlink TUF v2 and the trusted root work, and say that a) TUF v1 is only for custom metadata and only support "legacy" verification with detached material/the old bundle format and b) if you provide either a trust root or verification bundle, the client will use TUF v2?

My only hesitance is around the sequencing of the refactoring. I want to make sure we keep PRs small so we can iterate faster. Having some intermediate PRs like this lets us test out well-scoped changes.


Tangentially, addressing:

pkg/cosign/verify (in which case we need to audit and improve that verification path to handle the lists of possible verification material the trusted root provides) or

I think it would be worthwhile to do this. This is the only way we can minimize the likelihood of introducing unexpected breaking changes. I'll respond on #3854 about this.

@cmurphy
Copy link
Contributor Author

cmurphy commented Oct 2, 2024

I see now how trying to switch the internals of cosign's individual key material fetching functions isn't going to result in a seamless outcome. I'm afraid, however, that conditioning the which-TUF-client decision on what kind of bundle is in use is also going to miss several important pathways, namely signing (there is no bundle yet) and image verification (also no protobuf bundle). I've attempted to take a step back and enumerate everywhere we are obtaining and using trusted keys in some form:

Key type: CT Log

Action TUF fetch happens Used
creating a non-ephemeral-key signer
pubKeys, err := cosign.GetCTLogPubs(ctx)
if err := cosign.VerifyEmbeddedSCT(context.Background(), chain, pubKeys); err != nil {
creating a fulcio signer
pubKeys, err := cosign.GetCTLogPubs(ctx)
if err := cosign.VerifySCT(ctx, fs.Cert, fs.Chain, fs.SCT, pubKeys); err != nil {
verifying an SCT in an image
co.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx)
if err := VerifyEmbeddedSCT(context.Background(), chains[0], co.CTLogPubKeys); err != nil {
if err := VerifySCT(context.Background(), certPEM, chainPEM, co.SCT, co.CTLogPubKeys); err != nil {
verifying an SCT in an image attestation
co.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx)
same as above
verifying an SCT for a blob
co.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx)
same as above
verifying an SCT for a blob attestation
co.CTLogPubKeys, err = cosign.GetCTLogPubs(ctx)
same as above

Proposal

Where cosign.GetCTLogPubs() is called, use root.NewLiveTrustedRoot(). If CheckOpts is available (verifier case), store the trusted root as a field on CheckOpts. Otherwise (signer case), use the trusted root directly or store on KeyOpts. If the returned trusted root, wherever stored, is non-nil, pass it to sigstore-go's verify.VerifySCT(). Otherwise, use cosign's cosign.VerifySCT() or cosign.VerifyEmbeddedSCT().

Caveat: cosign's version of VerifySCT has options for embedded and non-embedded SCTs in certificates, so will have to make some changes to make it compatible with sigstore-go's version (embedded, non-embedded).

Key type: Fulcio CA

Action TUF fetch happens Used
verifying certificate in an image
co.RootCerts, err = fulcio.GetRoots()
if err != nil {
return fmt.Errorf("getting Fulcio roots: %w", err)
}
co.IntermediateCerts, err = fulcio.GetIntermediates()
if err != nil {
return fmt.Errorf("getting Fulcio intermediates: %w", err)
}
or
co.RootCerts, err = fulcio.GetRoots()
if err != nil {
return fmt.Errorf("getting Fulcio roots: %w", err)
}
co.IntermediateCerts, err = fulcio.GetIntermediates()
if err != nil {
return fmt.Errorf("getting Fulcio intermediates: %w", err)
}
chains, err := TrustedCert(cert, co.RootCerts, intermediateCerts)
verifying certificate in an image attestation
co.RootCerts, err = fulcio.GetRoots()
if err != nil {
return fmt.Errorf("getting Fulcio roots: %w", err)
}
co.IntermediateCerts, err = fulcio.GetIntermediates()
if err != nil {
return fmt.Errorf("getting Fulcio intermediates: %w", err)
}
same as above

The Fulcio CA is NOT retrieved from TUF for verifying the certificate for blobs or blob attestations - the Fulcio certificate must be passed in by a flag or extracted from the bundle.

Proposal

Where fulcio.GetRoots() and fulcio.GetIntermediates() are called, use root.NewLiveTrustedRoot() and store the result on CheckOpts. If the trusted root is non-nil, pass it to sigstore-go's verify.VerifyLeafCertificate(). Otherwise, use cosign's cosign.TrustedCert().

Key type: Rekor pub

Action TUF fetch happens Used
verifying an SET in an image
co.RekorPubKeys, err = cosign.GetRekorPubs(ctx)
err = VerifySET(bundle.Payload, bundle.SignedEntryTimestamp, pubKey.PubKey.(*ecdsa.PublicKey))
and again
err = VerifySET(payload, []byte(e.Verification.SignedEntryTimestamp), pubKey.PubKey.(*ecdsa.PublicKey))
(duplicate?)
verifying an SET in an image attestation
co.RekorPubKeys, err = cosign.GetRekorPubs(ctx)
same as above
verifying an SET for a blob
co.RekorPubKeys, err = cosign.GetRekorPubs(ctx)
same as above
verifying an SET for a blob attestation
co.RekorPubKeys, err = cosign.GetRekorPubs(ctx)
same as above

Proposal

Where cosign.GetRekorPubs() is called, use root.NewLiveTrustedRoot() and store the result on CheckOpts. If the trusted root is non-nil, pass it to sigstore-go's tlog.VerifySET(). Otherwise, use cosign's cosign.VerifySET(). Also, see if the calls to VerifySET can be de-duplicated.


@steiza

I thought that when we use a TUF v2 client we were only going to fetch the trusted_root.json and not fetch individual files over TUF like ctfe.pub

@haydentherapper

When I wrote it, my goal was to move to TUF v2 independent of the trusted root changes.
...
I also recognize that this is functionally a no-op

I'm in agreement with @steiza now, I think separating TUF v2 from trusted root is a non-starter at this point. TUF v2 does not support custom metadata, so it's not just a no-op, it's really a big step backwards and makes users less secure. Really the only reason to use the TUF v2 client is to enable support for fetching the trusted root.

@jku

It feels wrong if users need to set an environment variable to make the software use the recommended trust root mechanism.

@haydentherapper

My concern with using env vars isn't necessarily that users have to set it - functionally, TUF v1 and v2 should be equivalent, so setting these are more for our testing during development than the end users

In this PR's current design (which we do not want to follow anymore), users would have had to set those env vars in order to opt into trusted root and TUF v2. In the proposal I've outlined above, I think we can make them optional, since we will always fall back to TUF v1 if we can't safely initialize a TUF v2 client or the TUF mirror does not have a trusted root.

@cmurphy
Copy link
Contributor Author

cmurphy commented Oct 22, 2024

I've updated this PR to approximately implement the plan I laid out in my comment. I've done some light manual testing but it will need significant e2e and unit testing. I wanted to make sure there is buy-in on this approach before setting to work on that. I think we should also add support for setting --trusted-root like @steiza started in #3854 but I think that can be done in a separate PR.

@steiza
Copy link
Member

steiza commented Oct 24, 2024

I think this approach is great! But I'll admit I don't fully understand the constraints of OCI verification, so we should probably get feedback from @codysoyland as well.

I think these changes around getting the trusted root could be used to improve the implementation in #3796 or (like you say) bring back some elements of #3854, but I agree that that can wait for a later pull request.

Nice work!

Use sigstore-go's TUF client to fetch the trusted_root.json from the TUF
mirror, if available. Where possible, use sigstore-go's verifiers which
natively accept the trusted root as its trusted material. Where there is
no trusted root available in TUF or sigstore-go doesn't support a use
case, fall back to the sigstore/sigstore TUF v1 client and the existing
verifiers in cosign.

Signed-off-by: Colleen Murphy <[email protected]>
@haydentherapper
Copy link
Contributor

@cmurphy Fantastic summary of the work necessary to implement this! I'm supportive of all of the above.

The Fulcio CA is NOT retrieved from TUF for verifying the certificate for blobs or blob attestations - the Fulcio certificate must be passed in by a flag or extracted from the bundle.

For verification, root certificates should be pulled through TUF through an API in sigstore/sigstore. We had started a refactor a long time ago to move TUF logic into this shared repo but never finished. Or is this referring to bring-your-own PKI where roots are provided via flag?

I'm in agreement with @steiza now, I think separating TUF v2 from trusted root is a non-starter at this point. TUF v2 does not support custom metadata, so it's not just a no-op, it's really a big step backwards and makes users less secure. Really the only reason to use the TUF v2 client is to enable support for fetching the trusted root.

That makes sense, and if it's less work to not try and separate these two goals, let's not try to. Our goal is to adopt TUF v2 wholly, so no point in trying to have an intermediate no-op state.

@cmurphy
Copy link
Contributor Author

cmurphy commented Oct 28, 2024

@haydentherapper

For verification, root certificates should be pulled through TUF through an API in sigstore/sigstore. We had started a refactor a long time ago to move TUF logic into this shared repo but never finished. Or is this referring to bring-your-own PKI where roots are provided via flag?

This was referring to the slightly different logic between cosign verify-blob/cosign verify-blob-attestation and cosign verify/cosign verify-attestation currently, where in the OCI case the fulcio roots are fetched from TUF if not provided via flag, while in the blob case they aren't necessarily, though I think I missed that they are fetched from TUF in the keyless case on my first pass.

Copy link
Member

@loosebazooka loosebazooka left a comment

Choose a reason for hiding this comment

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

I read over the history, and (perhaps I missed it) has this option been considered:

Is there a way to just build a trusted_root.json from the provided materials. We provide sensible defaults for config (like if provided on the command line -- they are just valid forever). Then only use the tursted_root code path for everything?

If that sounds dumb then I'll just finish up a regular code review here.

// Detached SCTs cannot be verified with this function.
certs, err := cryptoutils.UnmarshalCertificatesFromPEM(fs.Cert)
if err != nil || len(certs) < 1 {
return nil, fmt.Errorf("unmarshalling SCT from PEM: %w", err)
Copy link
Member

Choose a reason for hiding this comment

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

Would "unmarshalling certificate from PEM for SCT verification" make more sense? or am I misreading this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, will fix

@steiza
Copy link
Member

steiza commented Oct 31, 2024

Is there a way to just build a trusted_root.json from the provided materials.

Yeah, so there are several things happening in parallel, if I understand your question correctly.

This pull request is "just" about upgrading cosign's TUF client from the existing version which fetches verification material as individual files to TUFv2 which fetches all the verification material in one trusted_root.json file. I put "just" in quotes, because that's actually quite a bit of work: we also have to adapt the various parts of the cosign verification path to make use of a trusted_root.json file where there might be multiple valid verification material options for a given time.

I think your question is "in the future, once cosign verification paths support making use of a trusted_root.json file, can we change the old verification paths that make use of individual files and instead assemble them into a trusted_root.json file and use the new verification path?"

My answer is that we could, although we might also delete those old verification paths and ask people to generate their own trusted root with something like #3876. But I think the work laid out in this pull request is a prerequisite, either way.

@steiza
Copy link
Member

steiza commented Nov 7, 2024

@codysoyland and I talked about this pull request yesterday at the sigstore-go meeting. We think this is compatible with the work he's doing on OCI and is good to proceed!

@codysoyland
Copy link
Member

As @steiza mentioned, I've had a bit of time to look at the latest changes here, and I'm pleased with the recent changes! It will complement my work on container image bundle verification quite well (we even both added TrustedMaterial to CheckOpts!). Apologies for not following this PR very closely recently! I'm close to getting that PR into review, perhaps next week. Looking forward to seeing you both at SigstoreCon in a few days!

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.

Upgrade to latest Sigstore TUF client
6 participants