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
Show file tree
Hide file tree
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
Expand Up @@ -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) {
Expand All @@ -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")
Expand Down
40 changes: 18 additions & 22 deletions cmd/cosign/cli/verify/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
43 changes: 21 additions & 22 deletions cmd/cosign/cli/verify/verify_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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).
Expand All @@ -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!
Expand Down
44 changes: 20 additions & 24 deletions cmd/cosign/cli/verify/verify_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
34 changes: 10 additions & 24 deletions cmd/cosign/cli/verify/verify_blob_attestation.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
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
Expand Up @@ -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"
Expand Down Expand Up @@ -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",
Expand Down
Loading
Loading