diff --git a/cmd/cosign/cli/options/signblob.go b/cmd/cosign/cli/options/signblob.go index 8086da15e37..5263e7806df 100644 --- a/cmd/cosign/cli/options/signblob.go +++ b/cmd/cosign/cli/options/signblob.go @@ -22,16 +22,19 @@ import ( ) // SignBlobOptions is the top level wrapper for the sign-blob command. +// The new output-certificate flag is only in use when COSIGN_EXPERIMENTAL is enabled type SignBlobOptions struct { - Key string - Base64Output bool - Output string // TODO: this should be the root output file arg. - SecurityKey SecurityKeyOptions - Fulcio FulcioOptions - Rekor RekorOptions - OIDC OIDCOptions - Registry RegistryOptions - Timeout time.Duration + Key string + Base64Output bool + Output string // deprecated: TODO remove when the output flag is fully deprecated + OutputSignature string // TODO: this should be the root output file arg. + OutputCertificate string // TODO: this should be the root output file arg. + SecurityKey SecurityKeyOptions + Fulcio FulcioOptions + Rekor RekorOptions + OIDC OIDCOptions + Registry RegistryOptions + Timeout time.Duration } var _ Interface = (*SignBlobOptions)(nil) @@ -50,9 +53,15 @@ func (o *SignBlobOptions) AddFlags(cmd *cobra.Command) { cmd.Flags().BoolVar(&o.Base64Output, "b64", true, "whether to base64 encode the output") - cmd.Flags().StringVar(&o.Output, "output", "", + cmd.Flags().StringVar(&o.OutputSignature, "output-signature", "", "write the signature to FILE") + // TODO: remove when output flag is fully deprecated + cmd.Flags().StringVar(&o.Output, "output", "", "write the signature to FILE") + + cmd.Flags().StringVar(&o.OutputCertificate, "output-certificate", "", + "write the certificate to FILE") + cmd.Flags().DurationVar(&o.Timeout, "timeout", time.Second*30, "HTTP Timeout defaults to 30 seconds") } diff --git a/cmd/cosign/cli/sign.go b/cmd/cosign/cli/sign.go index 31bc945962e..f2f733c66aa 100644 --- a/cmd/cosign/cli/sign.go +++ b/cmd/cosign/cli/sign.go @@ -20,6 +20,7 @@ import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/spf13/viper" "github.com/sigstore/cosign/cmd/cosign/cli/generate" "github.com/sigstore/cosign/cmd/cosign/cli/options" @@ -28,6 +29,7 @@ import ( func Sign() *cobra.Command { o := &options.SignOptions{} + viper.RegisterAlias("output", "output-signature") cmd := &cobra.Command{ Use: "sign", @@ -98,7 +100,6 @@ func Sign() *cobra.Command { return nil }, } - o.AddFlags(cmd) return cmd } diff --git a/cmd/cosign/cli/sign/sign_blob.go b/cmd/cosign/cli/sign/sign_blob.go index aa3a1f41a77..5c6474aca73 100644 --- a/cmd/cosign/cli/sign/sign_blob.go +++ b/cmd/cosign/cli/sign/sign_blob.go @@ -52,9 +52,10 @@ type KeyOpts struct { } // nolint -func SignBlobCmd(ctx context.Context, ko KeyOpts, regOpts options.RegistryOptions, payloadPath string, b64 bool, output string, timeout time.Duration) ([]byte, error) { +func SignBlobCmd(ctx context.Context, ko KeyOpts, regOpts options.RegistryOptions, payloadPath string, b64 bool, outputSignature string, outputCertificate string, timeout time.Duration) ([]byte, error) { var payload []byte var err error + var rekorBytes []byte if payloadPath == "-" { payload, err = io.ReadAll(os.Stdin) @@ -79,7 +80,6 @@ func SignBlobCmd(ctx context.Context, ko KeyOpts, regOpts options.RegistryOption if options.EnableExperimental() { // TODO: Refactor with sign.go - var rekorBytes []byte if sv.Cert != nil { fmt.Fprintf(os.Stderr, "signing with ephemeral certificate:\n%s\n", string(sv.Cert)) rekorBytes = sv.Cert @@ -101,8 +101,8 @@ func SignBlobCmd(ctx context.Context, ko KeyOpts, regOpts options.RegistryOption fmt.Fprintln(os.Stderr, "tlog entry created with index:", *entry.LogIndex) } - if output != "" { - f, err := os.Create(output) + if outputSignature != "" { + f, err := os.Create(outputSignature) if err != nil { return nil, err } @@ -131,5 +131,25 @@ func SignBlobCmd(ctx context.Context, ko KeyOpts, regOpts options.RegistryOption } } + if outputCertificate != "" { + f, err := os.Create(outputCertificate) + if err != nil { + return nil, err + } + defer f.Close() + + if b64 { + _, err = f.Write([]byte(base64.StdEncoding.EncodeToString(rekorBytes))) + if err != nil { + return nil, err + } + } else { + _, err = f.Write(rekorBytes) + if err != nil { + return nil, err + } + } + } + return sig, nil } diff --git a/cmd/cosign/cli/signblob.go b/cmd/cosign/cli/signblob.go index c46239d2285..93c9a162486 100644 --- a/cmd/cosign/cli/signblob.go +++ b/cmd/cosign/cli/signblob.go @@ -16,12 +16,14 @@ package cli import ( - "github.com/pkg/errors" - "github.com/spf13/cobra" + "fmt" + "os" + "github.com/pkg/errors" "github.com/sigstore/cosign/cmd/cosign/cli/generate" "github.com/sigstore/cosign/cmd/cosign/cli/options" "github.com/sigstore/cosign/cmd/cosign/cli/sign" + "github.com/spf13/cobra" ) func SignBlob() *cobra.Command { @@ -74,7 +76,12 @@ func SignBlob() *cobra.Command { OIDCClientSecret: o.OIDC.ClientSecret, } for _, blob := range args { - if _, err := sign.SignBlobCmd(cmd.Context(), ko, o.Registry, blob, o.Base64Output, o.Output, o.Timeout); err != nil { + // TODO: remove when the output flag has been deprecated + if o.Output != "" { + fmt.Fprintln(os.Stderr, "WARNING: the '--output' flag is deprecated and will be removed in the future. Use '--output-signature'") + o.OutputSignature = o.Output + } + if _, err := sign.SignBlobCmd(cmd.Context(), ko, o.Registry, blob, o.Base64Output, o.OutputSignature, o.OutputCertificate, o.Timeout); err != nil { return errors.Wrapf(err, "signing %s", blob) } } diff --git a/doc/cosign_sign-blob.md b/doc/cosign_sign-blob.md index d6cf17c7f4e..44d3ba97613 100644 --- a/doc/cosign_sign-blob.md +++ b/doc/cosign_sign-blob.md @@ -46,6 +46,8 @@ cosign sign-blob [flags] --oidc-client-secret string [EXPERIMENTAL] OIDC client secret for application --oidc-issuer string [EXPERIMENTAL] OIDC provider to be used to issue ID token (default "https://oauth2.sigstore.dev/auth") --output string write the signature to FILE + --output-certificate string write the certificate to FILE + --output-signature string write the signature to FILE --rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev") --sk whether to use a hardware security key --slot string security key slot to use for generated key (default: signature) (authentication|signature|card-authentication|key-management) diff --git a/go.mod b/go.mod index 67d6ef870a4..e7738abd34b 100644 --- a/go.mod +++ b/go.mod @@ -49,6 +49,7 @@ require ( github.com/miekg/pkcs11 v1.0.3 github.com/onsi/gomega v1.16.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect + github.com/spf13/viper v1.8.1 github.com/urfave/cli v1.22.5 // indirect go.opentelemetry.io/contrib v1.1.0 // indirect go.opentelemetry.io/proto/otlp v0.10.0 // indirect diff --git a/test/e2e_test.go b/test/e2e_test.go index 38ec1917dc2..254e3a1d41e 100644 --- a/test/e2e_test.go +++ b/test/e2e_test.go @@ -409,7 +409,7 @@ func TestSignBlob(t *testing.T) { KeyRef: privKeyPath1, PassFunc: passFunc, } - sig, err := sign.SignBlobCmd(ctx, ko, options.RegistryOptions{}, bp, true, "", time.Duration(30*time.Second)) + sig, err := sign.SignBlobCmd(ctx, ko, options.RegistryOptions{}, bp, true, "", "", time.Duration(30*time.Second)) if err != nil { t.Fatal(err) } diff --git a/test/piv_test.go b/test/piv_test.go index 5d69999dc10..f0876c4b629 100644 --- a/test/piv_test.go +++ b/test/piv_test.go @@ -12,9 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// +build resetyubikey -// +build e2e -// +build !pivkeydisabled +//go:build resetyubikey && e2e && !pivkeydisabled +// +build resetyubikey,e2e,!pivkeydisabled // DANGER // This test requires a yubikey to be present. It WILL reset the yubikey to exercise functionality.