Skip to content

Commit

Permalink
Upgrade to TUF v2 client
Browse files Browse the repository at this point in the history
Swap the use of the go-tuf v0.7.0 client from sigstore/sigstore to the
v2.0.0 client from sigstore/sigstore-go.

This change strictly adds logic to attempt to use the sigstore-go TUF
client if possible, and falls back to the old TUF client. The new client
can only fetch targets by name, not by custom metadata. This means that,
on its own, the new client cannot support renaming/rotating keys, so the
old client must be used to support that case. A future change will add
support for fetching or reading a trusted_root.json file, which has
better support for rotating keys. Once this later change is introduced, using
the old client can be deprecated.

The logic in this change works as follows:

- if a path fo a key is provided by a SIGSTORE_ environment variable,
  read that file and use it (same as previously)
- if new environment variables TUF_MIRROR and TUF_ROOT_JSON are set, use
  those to instantiate a TUF client that fetches keys from the given
  mirror
- else, try reading the mirror URL from remote.json, which set set by
  `cosign initialize`, and try reading the root.json from the mirror's
  cache directory which may have been created by a previous TUF v2 run
- if fetching keys using the new client with the given mirror did not
  work, fallback to the v1 client

Also not that the use of the "status" field in the custom TUF metadata
is removed, as it was only used for human-readable feedback.

TODO:
- e2e tests

Signed-off-by: Colleen Murphy <colleenmurphy@google.com>
  • Loading branch information
cmurphy committed Sep 20, 2024
1 parent 4393313 commit 84e4712
Show file tree
Hide file tree
Showing 15 changed files with 456 additions and 200 deletions.
2 changes: 1 addition & 1 deletion cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func (c *VerifyCommand) loadTSACertificates(ctx context.Context) (*cosign.TSACer
if c.TSACertChainPath == "" && !c.UseSignedTimestamps {
return nil, fmt.Errorf("TSA certificate chain path not provided and use-signed-timestamps not set")
}
tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets)
tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath)
if err != nil {
return nil, fmt.Errorf("unable to load TSA certificates: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/cosign/cli/verify/verify_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func (c *VerifyAttestationCommand) loadTSACertificates(ctx context.Context) (*co
if c.TSACertChainPath == "" && !c.UseSignedTimestamps {
return nil, fmt.Errorf("TSA certificate chain path not provided and use-signed-timestamps not set")
}
tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets)
tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath)
if err != nil {
return nil, fmt.Errorf("unable to load TSA certificates: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/cosign/cli/verify/verify_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (c *VerifyBlobCmd) loadTSACertificates(ctx context.Context) (*cosign.TSACer
if c.TSACertChainPath == "" && !c.UseSignedTimestamps {
return nil, fmt.Errorf("either TSA certificate chain path must be provided or use-signed-timestamps must be set")
}
tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets)
tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath)
if err != nil {
return nil, fmt.Errorf("unable to load TSA certificates: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/cosign/cli/verify/verify_blob_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st
}

if c.TSACertChainPath != "" || c.UseSignedTimestamps {
tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets)
tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath)
if err != nil {
return fmt.Errorf("unable to load or get TSA certificates: %w", err)
}
Expand Down
45 changes: 2 additions & 43 deletions internal/pkg/cosign/fulcio/fulcioroots/fulcioroots.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,10 @@
package fulcioroots

import (
"bytes"
"crypto/x509"
"fmt"
"os"
"sync"

"github.com/sigstore/cosign/v2/pkg/cosign/env"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/fulcioroots"
"github.com/sigstore/cosign/v2/pkg/cosign"
)

var (
Expand Down Expand Up @@ -64,41 +59,5 @@ func ReInit() error {
}

func initRoots() (*x509.CertPool, *x509.CertPool, error) {
rootPool := x509.NewCertPool()
// intermediatePool should be nil if no intermediates are found
var intermediatePool *x509.CertPool

rootEnv := env.Getenv(env.VariableSigstoreRootFile)
if rootEnv != "" {
raw, err := os.ReadFile(rootEnv)
if err != nil {
return nil, nil, fmt.Errorf("error reading root PEM file: %w", err)
}
certs, err := cryptoutils.UnmarshalCertificatesFromPEM(raw)
if err != nil {
return nil, nil, fmt.Errorf("error unmarshalling certificates: %w", err)
}
for _, cert := range certs {
// root certificates are self-signed
if bytes.Equal(cert.RawSubject, cert.RawIssuer) {
rootPool.AddCert(cert)
} else {
if intermediatePool == nil {
intermediatePool = x509.NewCertPool()
}
intermediatePool.AddCert(cert)
}
}
} else {
var err error
rootPool, err = fulcioroots.Get()
if err != nil {
return nil, nil, err
}
intermediatePool, err = fulcioroots.GetIntermediates()
if err != nil {
return nil, nil, err
}
}
return rootPool, intermediatePool, nil
return cosign.GetFulcioCerts()
}
46 changes: 21 additions & 25 deletions pkg/cosign/ctlog.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@ package cosign

import (
"context"
"errors"
"fmt"
"os"

"github.com/sigstore/cosign/v2/pkg/cosign/env"
"github.com/sigstore/sigstore/pkg/tuf"
tufv1 "github.com/sigstore/sigstore/pkg/tuf"
)

// This is the CT log public key target name
var ctPublicKeyStr = `ctfe.pub`
var (
ctPublicKeyStr = `ctfe.pub`
ctPublicKeyDesc = `CT log public key`
)

// GetCTLogPubs retrieves trusted CTLog public keys from the embedded or cached
// TUF root. If expired, makes a network call to retrieve the updated targets.
Expand All @@ -37,32 +38,27 @@ func GetCTLogPubs(ctx context.Context) (*TrustedTransparencyLogPubKeys, error) {
altCTLogPub := env.Getenv(env.VariableSigstoreCTLogPublicKeyFile)

if altCTLogPub != "" {
raw, err := os.ReadFile(altCTLogPub)
if err != nil {
return nil, fmt.Errorf("error reading alternate CTLog public key file: %w", err)
}
if err := publicKeys.AddTransparencyLogPubKey(raw, tuf.Active); err != nil {
return nil, fmt.Errorf("AddCTLogPubKey: %w", err)
}
} else {
tufClient, err := tuf.NewFromEnv(ctx)
err := addKeyFromFile(&publicKeys, altCTLogPub, ctPublicKeyDesc)
if err != nil {
return nil, err
}
targets, err := tufClient.GetTargetsByMeta(tuf.CTFE, []string{ctPublicKeyStr})
if err != nil {
return nil, err
}
for _, t := range targets {
if err := publicKeys.AddTransparencyLogPubKey(t.Target, t.Status); err != nil {
return nil, fmt.Errorf("AddCTLogPubKey: %w", err)
}
return nil, fmt.Errorf("error adding key from environment variable: %w", err)
}
return &publicKeys, nil
}

if len(publicKeys.Keys) == 0 {
return nil, errors.New("none of the CTLog public keys have been found")
opts, err := setTUFOpts()
if err != nil {
return nil, err
}

_ = addKeyFromTUF(&publicKeys, opts, ctPublicKeyStr, ctPublicKeyDesc)
if len(publicKeys.Keys) == 0 {
err = legacyAddKeyFromTUF(ctx, &publicKeys, tufv1.CTFE, []string{ctPublicKeyStr}, ctPublicKeyDesc)
if err != nil {
return nil, fmt.Errorf("error adding ct log public key: %w", err)
}
if len(publicKeys.Keys) == 0 {
return nil, fmt.Errorf("none of the CTLog public keys have been found")
}
}
return &publicKeys, nil
}
137 changes: 137 additions & 0 deletions pkg/cosign/fulcio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2024 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cosign

import (
"bytes"
"crypto/x509"
"fmt"
"os"

"github.com/sigstore/cosign/v2/pkg/cosign/env"
"github.com/sigstore/sigstore-go/pkg/tuf"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/fulcioroots"
)

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`
)

func GetFulcioCerts() (*x509.CertPool, *x509.CertPool, error) {
rootEnv := env.Getenv(env.VariableSigstoreRootFile)
if rootEnv != "" {
return getFulcioCertsFromFile(rootEnv)
}

opts, err := setTUFOpts()
if err != nil {
return nil, nil, err
}

roots, intermediates, _ := getFulcioCertsFromTUF(opts)
if roots == nil {
roots, intermediates, err = legacyGetFulcioCertsFromTUF()
if err != nil {
return nil, nil, fmt.Errorf("error getting fulcio certs: %w", err)
}
}
return roots, intermediates, nil
}

func getFulcioCertsFromFile(path string) (*x509.CertPool, *x509.CertPool, error) {
rootPool := x509.NewCertPool()
// intermediatePool should be nil if no intermediates are found
var intermediatePool *x509.CertPool
raw, err := os.ReadFile(path)
if err != nil {
return nil, nil, fmt.Errorf("error reading root PEM file: %w", err)
}
certs, err := cryptoutils.UnmarshalCertificatesFromPEM(raw)
if err != nil {
return nil, nil, fmt.Errorf("error unmarshalling certificates: %w", err)
}
for _, cert := range certs {
// root certificates are self-signed
if bytes.Equal(cert.RawSubject, cert.RawIssuer) {
rootPool.AddCert(cert)
} else {
if intermediatePool == nil {
intermediatePool = x509.NewCertPool()
}
intermediatePool.AddCert(cert)
}
}
return rootPool, intermediatePool, nil
}

func getFulcioCertsFromTUF(opts *tuf.Options) (*x509.CertPool, *x509.CertPool, error) {
tufClient, err := tuf.New(opts)
if err != nil {
return nil, nil, fmt.Errorf("error creating TUF client: %w", err)
}
rootPool := x509.NewCertPool()
fulcioCertBytes, _ := tufClient.GetTarget(fulcioTargetStr)
fulcioV1CertBytes, _ := tufClient.GetTarget(fulcioV1TargetStr)
if len(fulcioCertBytes) > 0 {
fulcioCert, err := cryptoutils.UnmarshalCertificatesFromPEM(fulcioCertBytes)
if err != nil {
return nil, nil, fmt.Errorf("error unmarshalling fulcio cert: %w", err)
}
for _, c := range fulcioCert {
rootPool.AddCert(c)
}
}
if len(fulcioV1CertBytes) > 0 {
fulcioV1Cert, err := cryptoutils.UnmarshalCertificatesFromPEM(fulcioV1CertBytes)
if err != nil {
return nil, nil, fmt.Errorf("error unmarshalling fulcio v1 cert: %w", err)
}
for _, c := range fulcioV1Cert {
rootPool.AddCert(c)
}
}

var intermediatePool *x509.CertPool
fulcioIntermediateBytes, _ := tufClient.GetTarget(fulcioV1IntermediateTargetStr)
if len(fulcioIntermediateBytes) == 0 {
fulcioIntermediate, err := cryptoutils.UnmarshalCertificatesFromPEM(fulcioIntermediateBytes)
if err != nil {
return nil, nil, fmt.Errorf("error unmarshalling fulcio intermediate cert: %w", err)
}
intermediatePool = x509.NewCertPool()
for _, c := range fulcioIntermediate {
intermediatePool.AddCert(c)
}
}
return rootPool, intermediatePool, nil
}

func legacyGetFulcioCertsFromTUF() (*x509.CertPool, *x509.CertPool, error) {
roots, err := fulcioroots.Get()
if err != nil {
return nil, nil, fmt.Errorf("error getting fulcio roots: %w", err)
}
intermediates, err := fulcioroots.GetIntermediates()
if err != nil {
return nil, nil, fmt.Errorf("error getting fulcio intermediates: %w", err)
}
return roots, intermediates, err
}
Loading

0 comments on commit 84e4712

Please sign in to comment.