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

adds tsa cert chain check for env var or tuf targets. #3600

Merged
merged 2 commits into from
Jun 19, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/cosign/cli/options/verify.go
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ type CommonVerifyOptions struct {
// it for other verify options.
ExperimentalOCI11 bool
PrivateInfrastructure bool
UseSignedTimestamps bool
}

func (o *CommonVerifyOptions) AddFlags(cmd *cobra.Command) {
@@ -40,6 +41,9 @@ func (o *CommonVerifyOptions) AddFlags(cmd *cobra.Command) {
"path to PEM-encoded certificate chain file for the RFC3161 timestamp authority. Must contain the root CA certificate. "+
"Optionally may contain intermediate CA certificates, and may contain the leaf TSA certificate if not present in the timestamp")

cmd.Flags().BoolVar(&o.UseSignedTimestamps, "use-signed-timestamps", false,
"use signed timestamps if available")

cmd.Flags().BoolVar(&o.IgnoreTlog, "insecure-ignore-tlog", false,
"ignore transparency log verification, to be used when an artifact signature has not been uploaded to the transparency log. Artifacts "+
"cannot be publicly verified when not included in a log")
40 changes: 18 additions & 22 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
@@ -34,7 +34,6 @@ import (
"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/sign"
cosignError "github.com/sigstore/cosign/v2/cmd/cosign/errors"
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa"
"github.com/sigstore/cosign/v2/internal/ui"
"github.com/sigstore/cosign/v2/pkg/blob"
"github.com/sigstore/cosign/v2/pkg/cosign"
@@ -77,11 +76,23 @@ type VerifyCommand struct {
NameOptions []name.Option
Offline bool
TSACertChainPath string
UseSignedTimestamps bool
IgnoreTlog bool
MaxWorkers int
ExperimentalOCI11 bool
}

func (c *VerifyCommand) loadTSACertificates(ctx context.Context) (*cosign.TSACertificates, error) {
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)
if err != nil {
return nil, fmt.Errorf("unable to load TSA certificates: %w", err)
}
return tsaCertificates, nil
}

// Exec runs the verification command
func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
if len(images) == 0 {
@@ -136,29 +147,14 @@ func (c *VerifyCommand) Exec(ctx context.Context, images []string) (err error) {
co.ClaimVerifier = cosign.SimpleClaimVerifier
}

if c.TSACertChainPath != "" {
_, err := os.Stat(c.TSACertChainPath)
if err != nil {
return fmt.Errorf("unable to open timestamp certificate chain file: %w", err)
}
// TODO: Add support for TUF certificates.
pemBytes, err := os.ReadFile(filepath.Clean(c.TSACertChainPath))
if err != nil {
return fmt.Errorf("error reading certification chain path file: %w", err)
}

leaves, intermediates, roots, err := tsa.SplitPEMCertificateChain(pemBytes)
if c.TSACertChainPath != "" || c.UseSignedTimestamps {
tsaCertificates, err := c.loadTSACertificates(ctx)
if err != nil {
return fmt.Errorf("error splitting certificates: %w", err)
}
if len(leaves) > 1 {
return fmt.Errorf("certificate chain must contain at most one TSA certificate")
}
if len(leaves) == 1 {
co.TSACertificate = leaves[0]
return fmt.Errorf("unable to load TSA certificates: %w", err)
}
co.TSAIntermediateCertificates = intermediates
co.TSARootCertificates = roots
co.TSACertificate = tsaCertificates.LeafCert
co.TSARootCertificates = tsaCertificates.RootCert
co.TSAIntermediateCertificates = tsaCertificates.IntermediateCerts
}

if !c.IgnoreTlog {
43 changes: 21 additions & 22 deletions cmd/cosign/cli/verify/verify_attestation.go
Original file line number Diff line number Diff line change
@@ -28,7 +28,6 @@ import (
"github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa"
"github.com/sigstore/cosign/v2/internal/ui"
"github.com/sigstore/cosign/v2/pkg/cosign"
"github.com/sigstore/cosign/v2/pkg/cosign/cue"
@@ -68,6 +67,18 @@ type VerifyAttestationCommand struct {
TSACertChainPath string
IgnoreTlog bool
MaxWorkers int
UseSignedTimestamps bool
}

func (c *VerifyAttestationCommand) loadTSACertificates(ctx context.Context) (*cosign.TSACertificates, error) {
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)
if err != nil {
return nil, fmt.Errorf("unable to load TSA certificates: %w", err)
}
return tsaCertificates, nil
}

// Exec runs the verification command
@@ -118,30 +129,16 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
}
}

if c.TSACertChainPath != "" {
_, err := os.Stat(c.TSACertChainPath)
if err != nil {
return fmt.Errorf("unable to open timestamp certificate chain file '%s: %w", c.TSACertChainPath, err)
}
// TODO: Add support for TUF certificates.
pemBytes, err := os.ReadFile(filepath.Clean(c.TSACertChainPath))
if c.TSACertChainPath != "" || c.UseSignedTimestamps {
tsaCertificates, err := c.loadTSACertificates(ctx)
if err != nil {
return fmt.Errorf("error reading certification chain path file: %w", err)
return fmt.Errorf("unable to load TSA certificates: %w", err)
}

leaves, intermediates, roots, err := tsa.SplitPEMCertificateChain(pemBytes)
if err != nil {
return fmt.Errorf("error splitting certificates: %w", err)
}
if len(leaves) > 1 {
return fmt.Errorf("certificate chain must contain at most one TSA certificate")
}
if len(leaves) == 1 {
co.TSACertificate = leaves[0]
}
co.TSAIntermediateCertificates = intermediates
co.TSARootCertificates = roots
co.TSACertificate = tsaCertificates.LeafCert
co.TSARootCertificates = tsaCertificates.RootCert
co.TSAIntermediateCertificates = tsaCertificates.IntermediateCerts
}

if !c.IgnoreTlog {
if c.RekorURL != "" {
rekorClient, err := rekor.NewClient(c.RekorURL)
@@ -157,6 +154,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
return fmt.Errorf("getting Rekor public keys: %w", err)
}
}

if keylessVerification(c.KeyRef, c.Sk) {
// This performs an online fetch of the Fulcio roots. This is needed
// for verifying keyless certificates (both online and offline).
@@ -169,6 +167,7 @@ func (c *VerifyAttestationCommand) Exec(ctx context.Context, images []string) (e
return fmt.Errorf("getting Fulcio intermediates: %w", err)
}
}

keyRef := c.KeyRef

// Keys are optional!
44 changes: 20 additions & 24 deletions cmd/cosign/cli/verify/verify_blob.go
Original file line number Diff line number Diff line change
@@ -31,7 +31,6 @@ import (
"github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/options"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa"
"github.com/sigstore/cosign/v2/internal/ui"
"github.com/sigstore/cosign/v2/pkg/blob"
"github.com/sigstore/cosign/v2/pkg/cosign"
@@ -64,9 +63,21 @@ type VerifyBlobCmd struct {
IgnoreSCT bool
SCTRef string
Offline bool
UseSignedTimestamps bool
IgnoreTlog bool
}

func (c *VerifyBlobCmd) loadTSACertificates(ctx context.Context) (*cosign.TSACertificates, error) {
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)
if err != nil {
return nil, fmt.Errorf("unable to load TSA certificates: %w", err)
}
return tsaCertificates, nil
}

// nolint
func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error {
var cert *x509.Certificate
@@ -112,32 +123,17 @@ func (c *VerifyBlobCmd) Exec(ctx context.Context, blobRef string) error {
Offline: c.Offline,
IgnoreTlog: c.IgnoreTlog,
}
if c.RFC3161TimestampPath != "" && c.KeyOpts.TSACertChainPath == "" {
return fmt.Errorf("timestamp-certificate-chain is required to validate a RFC3161 timestamp")
if c.RFC3161TimestampPath != "" && !(c.TSACertChainPath != "" || c.UseSignedTimestamps) {
return fmt.Errorf("either TSA certificate chain path must be provided or use-signed-timestamps must be set when using RFC3161 timestamp path")
}
if c.KeyOpts.TSACertChainPath != "" {
_, err := os.Stat(c.KeyOpts.TSACertChainPath)
if err != nil {
return fmt.Errorf("unable to open timestamp certificate chain file '%s: %w", c.KeyOpts.TSACertChainPath, err)
}
// TODO: Add support for TUF certificates.
pemBytes, err := os.ReadFile(filepath.Clean(c.KeyOpts.TSACertChainPath))
if c.TSACertChainPath != "" || c.UseSignedTimestamps {
tsaCertificates, err := c.loadTSACertificates(ctx)
if err != nil {
return fmt.Errorf("error reading certification chain path file: %w", err)
}

leaves, intermediates, roots, err := tsa.SplitPEMCertificateChain(pemBytes)
if err != nil {
return fmt.Errorf("error splitting certificates: %w", err)
}
if len(leaves) > 1 {
return fmt.Errorf("certificate chain must contain at most one TSA certificate")
}
if len(leaves) == 1 {
co.TSACertificate = leaves[0]
return err
}
co.TSAIntermediateCertificates = intermediates
co.TSARootCertificates = roots
co.TSACertificate = tsaCertificates.LeafCert
co.TSARootCertificates = tsaCertificates.RootCert
co.TSAIntermediateCertificates = tsaCertificates.IntermediateCerts
}

if !c.IgnoreTlog {
34 changes: 10 additions & 24 deletions cmd/cosign/cli/verify/verify_blob_attestation.go
Original file line number Diff line number Diff line change
@@ -35,7 +35,6 @@ import (
"github.com/sigstore/cosign/v2/cmd/cosign/cli/rekor"
internal "github.com/sigstore/cosign/v2/internal/pkg/cosign"
payloadsize "github.com/sigstore/cosign/v2/internal/pkg/cosign/payload/size"
"github.com/sigstore/cosign/v2/internal/pkg/cosign/tsa"
"github.com/sigstore/cosign/v2/pkg/blob"
"github.com/sigstore/cosign/v2/pkg/cosign"
"github.com/sigstore/cosign/v2/pkg/cosign/bundle"
@@ -71,7 +70,8 @@ type VerifyBlobAttestationCommand struct {
PredicateType string
// TODO: Add policies

SignaturePath string // Path to the signature
SignaturePath string // Path to the signature
UseSignedTimestamps bool
}

// Exec runs the verification command
@@ -140,32 +140,18 @@ func (c *VerifyBlobAttestationCommand) Exec(ctx context.Context, artifactPath st
}

// Set up TSA, Fulcio roots and tlog public keys and clients.
if c.RFC3161TimestampPath != "" && c.KeyOpts.TSACertChainPath == "" {
return fmt.Errorf("timestamp-cert-chain is required to validate a rfc3161 timestamp bundle")
if c.RFC3161TimestampPath != "" && !(c.TSACertChainPath != "" || c.UseSignedTimestamps) {
return fmt.Errorf("either TSA certificate chain path must be provided or use-signed-timestamps must be set when using RFC3161 timestamp path")
}
if c.KeyOpts.TSACertChainPath != "" {
_, err := os.Stat(c.TSACertChainPath)
if err != nil {
return fmt.Errorf("unable to open timestamp certificate chain file: %w", err)
}
// TODO: Add support for TUF certificates.
pemBytes, err := os.ReadFile(filepath.Clean(c.TSACertChainPath))
if err != nil {
return fmt.Errorf("error reading certification chain path file: %w", err)
}

leaves, intermediates, roots, err := tsa.SplitPEMCertificateChain(pemBytes)
if c.TSACertChainPath != "" || c.UseSignedTimestamps {
tsaCertificates, err := cosign.GetTSACerts(ctx, c.TSACertChainPath, cosign.GetTufTargets)
if err != nil {
return fmt.Errorf("error splitting certificates: %w", err)
}
if len(leaves) > 1 {
return fmt.Errorf("certificate chain must contain at most one TSA certificate")
}
if len(leaves) == 1 {
co.TSACertificate = leaves[0]
return fmt.Errorf("unable to load or get TSA certificates: %w", err)
}
co.TSAIntermediateCertificates = intermediates
co.TSARootCertificates = roots
co.TSACertificate = tsaCertificates.LeafCert
co.TSARootCertificates = tsaCertificates.RootCert
co.TSAIntermediateCertificates = tsaCertificates.IntermediateCerts
}

if !c.IgnoreTlog {
1 change: 1 addition & 0 deletions doc/cosign_dockerfile_verify.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doc/cosign_manifest_verify.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doc/cosign_verify-attestation.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doc/cosign_verify-blob-attestation.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doc/cosign_verify-blob.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions doc/cosign_verify.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 7 additions & 1 deletion pkg/cosign/env/env.go
Original file line number Diff line number Diff line change
@@ -58,6 +58,7 @@ const (
VariableSigstoreRootFile Variable = "SIGSTORE_ROOT_FILE"
VariableSigstoreRekorPublicKey Variable = "SIGSTORE_REKOR_PUBLIC_KEY"
VariableSigstoreIDToken Variable = "SIGSTORE_ID_TOKEN" //nolint:gosec
VariableSigstoreTSACertificateFile Variable = "SIGSTORE_TSA_CERTIFICATE_FILE"

// Other external environment variables
VariableGitHubHost Variable = "GITHUB_HOST"
@@ -138,7 +139,12 @@ var (
Sensitive: false,
External: true,
},

VariableSigstoreTSACertificateFile: {
Description: "path to the concatenated PEM-encoded TSA certificate file (leaf, intermediate(s), root) used by Sigstore",
Expects: "path to the TSA certificate file",
Sensitive: false,
External: true,
},
VariableGitHubHost: {
Description: "is URL of the GitHub Enterprise instance",
Expects: "string with the URL of GitHub Enterprise instance",
Loading