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

Add --signing-algorithm flag #3497

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
14 changes: 12 additions & 2 deletions cmd/cosign/cli/generate/generate_key_pair.go
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/sigstore/cosign/v2/pkg/cosign"
"github.com/sigstore/cosign/v2/pkg/cosign/kubernetes"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/sigstore/sigstore/pkg/signature/kms"
)

Expand All @@ -43,7 +44,7 @@ var (
)

// nolint
func GenerateKeyPairCmd(ctx context.Context, kmsVal string, outputKeyPrefixVal string, args []string) error {
func GenerateKeyPairCmd(ctx context.Context, kmsVal string, outputKeyPrefixVal string, signatureAlgorithmName string, args []string) error {
privateKeyFileName := outputKeyPrefixVal + ".key"
publicKeyFileName := outputKeyPrefixVal + ".pub"

Expand Down Expand Up @@ -86,7 +87,16 @@ func GenerateKeyPairCmd(ctx context.Context, kmsVal string, outputKeyPrefixVal s
return fmt.Errorf("undefined provider: %s", provider)
}

keys, err := cosign.GenerateKeyPair(GetPass)
signatureAlgorithm, err := signature.ParseSignatureAlgorithmFlag(signatureAlgorithmName)
if err != nil {
return err
}
algorithmDetails, err := signature.GetAlgorithmDetails(signatureAlgorithm)
if err != nil {
return err
}

keys, err := cosign.GenerateKeyPairWithAlgo(GetPass, algorithmDetails)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/cosign/cli/generate/generate_key_pair_test.go
Expand Up @@ -57,7 +57,7 @@ func TestGenerationOfKeys(t *testing.T) {
// be default it's set to `cosign`, but this is done by the CLI flag
// framework if there is no value set by the user when running the
// command.
GenerateKeyPairCmd(context.Background(), "", "my-test", nil)
GenerateKeyPairCmd(context.Background(), "", "my-test", "ecdsa-sha2-256-nistp256", nil)

checkIfFileExistsThenDelete(privateKeyName, t)
checkIfFileExistsThenDelete(publicKeyName, t)
Expand Down
5 changes: 4 additions & 1 deletion cmd/cosign/cli/generate_key_pair.go
Expand Up @@ -34,6 +34,9 @@ func GenerateKeyPair() *cobra.Command {
# generate key-pair and write to cosign.key and cosign.pub files
cosign generate-key-pair

# generate ED25519 key-pair and write to cosign.key and cosign.pub files
cosign generate-key-pair --signing-algorithm=ed25519-ph

# generate key-pair and write to custom named my-name.key and my-name.pub files
cosign generate-key-pair --output-key-prefix my-name

Expand Down Expand Up @@ -67,7 +70,7 @@ CAVEATS:

PersistentPreRun: options.BindViper,
RunE: func(cmd *cobra.Command, args []string) error {
return generate.GenerateKeyPairCmd(cmd.Context(), o.KMS, o.OutputKeyPrefix, args)
return generate.GenerateKeyPairCmd(cmd.Context(), o.KMS, o.OutputKeyPrefix, o.SigningAlgorithm, args)
},
}

Expand Down
16 changes: 14 additions & 2 deletions cmd/cosign/cli/options/generate_key_pair.go
Expand Up @@ -16,14 +16,21 @@
package options

import (
"fmt"
"strings"

"github.com/sigstore/cosign/v2/pkg/cosign"
v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/spf13/cobra"
)

// GenerateKeyPairOptions is the top level wrapper for the generate-key-pair command.
type GenerateKeyPairOptions struct {
// KMS Key Management Service
KMS string
OutputKeyPrefix string
KMS string
OutputKeyPrefix string
SigningAlgorithm string
}

var _ Interface = (*GenerateKeyPairOptions)(nil)
Expand All @@ -34,4 +41,9 @@ func (o *GenerateKeyPairOptions) AddFlags(cmd *cobra.Command) {
"create key pair in KMS service to use for signing")
cmd.Flags().StringVar(&o.OutputKeyPrefix, "output-key-prefix", "cosign",
"name used for generated .pub and .key files (defaults to `cosign`)")

keyAlgorithmTypes := cosign.GetSupportedAlgorithms()
keyAlgorithmHelp := fmt.Sprintf("signing algorithm to use for signing/hashing (allowed %s)", strings.Join(keyAlgorithmTypes, ", "))
defaultKeyFlag, _ := signature.FormatSignatureAlgorithmFlag(v1.KnownSignatureAlgorithm_ECDSA_SHA2_256_NISTP256)
cmd.Flags().StringVar(&o.SigningAlgorithm, "signing-algorithm", defaultKeyFlag, keyAlgorithmHelp)
}
5 changes: 4 additions & 1 deletion cmd/cosign/cli/options/key.go
Expand Up @@ -15,12 +15,15 @@

package options

import "github.com/sigstore/cosign/v2/pkg/cosign"
import (
"github.com/sigstore/cosign/v2/pkg/cosign"
)

type KeyOpts struct {
Sk bool
Slot string
KeyRef string
SigningAlgorithm string
FulcioURL string
RekorURL string
IDToken string
Expand Down
12 changes: 12 additions & 0 deletions cmd/cosign/cli/options/sign.go
Expand Up @@ -16,12 +16,19 @@
package options

import (
"fmt"
"strings"

"github.com/sigstore/cosign/v2/pkg/cosign"
v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/spf13/cobra"
)

// SignOptions is the top level wrapper for the sign command.
type SignOptions struct {
Key string
SigningAlgorithm string
Cert string
CertChain string
Upload bool
Expand Down Expand Up @@ -67,6 +74,11 @@ func (o *SignOptions) AddFlags(cmd *cobra.Command) {
"path to the private key file, KMS URI or Kubernetes Secret")
_ = cmd.Flags().SetAnnotation("key", cobra.BashCompFilenameExt, []string{})

keyAlgorithmTypes := cosign.GetSupportedAlgorithms()
keyAlgorithmHelp := fmt.Sprintf("signing algorithm to use for signing/hashing (allowed %s)", strings.Join(keyAlgorithmTypes, ", "))
defaultKeyFlag, _ := signature.FormatSignatureAlgorithmFlag(v1.KnownSignatureAlgorithm_ECDSA_SHA2_256_NISTP256)
cmd.Flags().StringVar(&o.SigningAlgorithm, "signing-algorithm", defaultKeyFlag, keyAlgorithmHelp)

cmd.Flags().StringVar(&o.Cert, "certificate", "",
"path to the X.509 certificate in PEM format to include in the OCI Signature")
_ = cmd.Flags().SetAnnotation("certificate", cobra.BashCompFilenameExt, []string{"cert"})
Expand Down
12 changes: 12 additions & 0 deletions cmd/cosign/cli/options/signblob.go
Expand Up @@ -16,13 +16,20 @@
package options

import (
"fmt"
"strings"

"github.com/sigstore/cosign/v2/pkg/cosign"
v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
"github.com/sigstore/sigstore/pkg/signature"
"github.com/spf13/cobra"
)

// 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
SigningAlgorithm 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.
Expand Down Expand Up @@ -57,6 +64,11 @@ func (o *SignBlobOptions) AddFlags(cmd *cobra.Command) {
"path to the private key file, KMS URI or Kubernetes Secret")
_ = cmd.Flags().SetAnnotation("key", cobra.BashCompFilenameExt, []string{})

keyAlgorithmTypes := cosign.GetSupportedAlgorithms()
keyAlgorithmHelp := fmt.Sprintf("signing algorithm to use for signing/hashing (allowed %s)", strings.Join(keyAlgorithmTypes, ", "))
defaultKeyFlag, _ := signature.FormatSignatureAlgorithmFlag(v1.KnownSignatureAlgorithm_ECDSA_SHA2_256_NISTP256)
cmd.Flags().StringVar(&o.SigningAlgorithm, "signing-algorithm", defaultKeyFlag, keyAlgorithmHelp)

cmd.Flags().BoolVar(&o.Base64Output, "b64", true,
"whether to base64 encode the output")

Expand Down
38 changes: 28 additions & 10 deletions cmd/cosign/cli/options/verify.go
Expand Up @@ -16,9 +16,15 @@
package options

import (
"fmt"
"strings"

"github.com/spf13/cobra"

"github.com/sigstore/cosign/v2/internal/pkg/cosign"
cosign_v2 "github.com/sigstore/cosign/v2/pkg/cosign"
v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"
"github.com/sigstore/sigstore/pkg/signature"
)

type CommonVerifyOptions struct {
Expand Down Expand Up @@ -56,13 +62,14 @@ func (o *CommonVerifyOptions) AddFlags(cmd *cobra.Command) {

// VerifyOptions is the top level wrapper for the `verify` command.
type VerifyOptions struct {
Key string
CheckClaims bool
Attachment string
Output string
SignatureRef string
PayloadRef string
LocalImage bool
Key string
SigningAlgorithm string
CheckClaims bool
Attachment string
Output string
SignatureRef string
PayloadRef string
LocalImage bool

CommonVerifyOptions CommonVerifyOptions
SecurityKey SecurityKeyOptions
Expand Down Expand Up @@ -90,6 +97,11 @@ func (o *VerifyOptions) AddFlags(cmd *cobra.Command) {
"path to the public key file, KMS URI or Kubernetes Secret")
_ = cmd.Flags().SetAnnotation("key", cobra.BashCompFilenameExt, []string{})

keyAlgorithmTypes := cosign_v2.GetSupportedAlgorithms()
keyAlgorithmHelp := fmt.Sprintf("accepted signing algorithm to use for verifying the signature (allowed %s)", strings.Join(keyAlgorithmTypes, ", "))
defaultKeyFlag, _ := signature.FormatSignatureAlgorithmFlag(v1.KnownSignatureAlgorithm_ECDSA_SHA2_256_NISTP256)
cmd.Flags().StringVar(&o.SigningAlgorithm, "signing-algorithm", defaultKeyFlag, keyAlgorithmHelp)

cmd.Flags().BoolVar(&o.CheckClaims, "check-claims", true,
"whether to check the claims found")

Expand Down Expand Up @@ -154,9 +166,10 @@ func (o *VerifyAttestationOptions) AddFlags(cmd *cobra.Command) {

// VerifyBlobOptions is the top level wrapper for the `verify blob` command.
type VerifyBlobOptions struct {
Key string
Signature string
BundlePath string
Key string
SigningAlgorithm string
Signature string
BundlePath string

SecurityKey SecurityKeyOptions
CertVerify CertVerifyOptions
Expand All @@ -178,6 +191,11 @@ func (o *VerifyBlobOptions) AddFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&o.Key, "key", "",
"path to the public key file, KMS URI or Kubernetes Secret")

keyAlgorithmTypes := cosign_v2.GetSupportedAlgorithms()
keyAlgorithmHelp := fmt.Sprintf("accepted signing algorithm to use for verifying the signature (allowed %s)", strings.Join(keyAlgorithmTypes, ", "))
defaultKeyFlag, _ := signature.FormatSignatureAlgorithmFlag(v1.KnownSignatureAlgorithm_ECDSA_SHA2_256_NISTP256)
cmd.Flags().StringVar(&o.SigningAlgorithm, "signing-algorithm", defaultKeyFlag, keyAlgorithmHelp)

cmd.Flags().StringVar(&o.Signature, "signature", "",
"signature content or path or remote URL")

Expand Down
1 change: 1 addition & 0 deletions cmd/cosign/cli/sign.go
Expand Up @@ -102,6 +102,7 @@ race conditions or (worse) malicious tampering.
}
ko := options.KeyOpts{
KeyRef: o.Key,
SigningAlgorithm: o.SigningAlgorithm,
PassFunc: generate.GetPass,
Sk: o.SecurityKey.Use,
Slot: o.SecurityKey.Slot,
Expand Down
43 changes: 33 additions & 10 deletions cmd/cosign/cli/sign/sign.go
Expand Up @@ -18,7 +18,6 @@ package sign
import (
"bytes"
"context"
"crypto"
"crypto/x509"
"encoding/base64"
"encoding/json"
Expand All @@ -32,6 +31,7 @@ import (
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
pb_go_v1 "github.com/sigstore/protobuf-specs/gen/pb-go/common/v1"

"github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio"
"github.com/sigstore/cosign/v2/cmd/cosign/cli/fulcio/fulcioverifier"
Expand Down Expand Up @@ -261,7 +261,12 @@ func signDigest(ctx context.Context, digest name.Digest, payload []byte, ko opti
if err != nil {
return err
}
s = irekor.NewSigner(s, rClient)

hashAlgorithm, err := getHashAlgorithmFromSignerVerifier(sv)
if err != nil {
return err
}
s = irekor.NewSigner(s, rClient, hashAlgorithm)
}

ociSig, _, err := s.Sign(ctx, bytes.NewReader(payload))
Expand Down Expand Up @@ -391,8 +396,8 @@ func signerFromSecurityKey(ctx context.Context, keySlot string) (*SignerVerifier
}, nil
}

func signerFromKeyRef(ctx context.Context, certPath, certChainPath, keyRef string, passFunc cosign.PassFunc) (*SignerVerifier, error) {
k, err := sigs.SignerVerifierFromKeyRef(ctx, keyRef, passFunc)
func signerFromKeyRef(ctx context.Context, certPath, certChainPath, keyRef string, passFunc cosign.PassFunc, opts ...signature.LoadOption) (*SignerVerifier, error) {
k, err := sigs.SignerVerifierFromKeyRefWithOpts(ctx, keyRef, passFunc, opts...)
if err != nil {
return nil, fmt.Errorf("reading key: %w", err)
}
Expand Down Expand Up @@ -521,12 +526,12 @@ func signerFromKeyRef(ctx context.Context, certPath, certChainPath, keyRef strin
return certSigner, nil
}

func signerFromNewKey() (*SignerVerifier, error) {
privKey, err := cosign.GeneratePrivateKey()
func signerFromNewKey(algorithmDetails signature.AlgorithmDetails, svOpts ...signature.LoadOption) (*SignerVerifier, error) {
privKey, err := cosign.GeneratePrivateKeyWithAlgo(algorithmDetails)
if err != nil {
return nil, fmt.Errorf("generating cert: %w", err)
}
sv, err := signature.LoadECDSASignerVerifier(privKey, crypto.SHA256)
sv, err := signature.LoadSignerVerifierWithOpts(privKey, svOpts...)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -560,18 +565,36 @@ func keylessSigner(ctx context.Context, ko options.KeyOpts, sv *SignerVerifier)
}

func SignerFromKeyOpts(ctx context.Context, certPath string, certChainPath string, ko options.KeyOpts) (*SignerVerifier, error) {
var svOpts []signature.LoadOption
signingAlgorithm, err := signature.ParseSignatureAlgorithmFlag(ko.SigningAlgorithm)
if err != nil {
// Default to ECDSA_SHA2_256_NISTP256 if no algorithm is specified
signingAlgorithm = pb_go_v1.KnownSignatureAlgorithm_ECDSA_SHA2_256_NISTP256
}

algorithmDetails, err := signature.GetAlgorithmDetails(signingAlgorithm)
if err != nil {
return nil, err
}
hashAlgorithm := algorithmDetails.GetHashType()
svOpts = []signature.LoadOption{
signatureoptions.WithHash(hashAlgorithm),
}
if algorithmDetails.GetSignatureAlgorithm() == pb_go_v1.KnownSignatureAlgorithm_ED25519_PH {
svOpts = append(svOpts, signatureoptions.WithED25519ph())
}

var sv *SignerVerifier
var err error
genKey := false
switch {
case ko.Sk:
sv, err = signerFromSecurityKey(ctx, ko.Slot)
case ko.KeyRef != "":
sv, err = signerFromKeyRef(ctx, certPath, certChainPath, ko.KeyRef, ko.PassFunc)
sv, err = signerFromKeyRef(ctx, certPath, certChainPath, ko.KeyRef, ko.PassFunc, svOpts...)
default:
genKey = true
ui.Infof(ctx, "Generating ephemeral keys...")
sv, err = signerFromNewKey()
sv, err = signerFromNewKey(algorithmDetails, svOpts...)
}
if err != nil {
return nil, err
Expand Down