Skip to content

Commit

Permalink
Remote KMS support (sigstore#62)
Browse files Browse the repository at this point in the history
* Add support for creating an asymmetric key in GCP KMS

Signed-off-by: Priya Wadhwa <priyawadhwa@google.com>

* Hook up creating KMS key to generate-key-pair command

Signed-off-by: Priya Wadhwa <priyawadhwa@google.com>

* Add permanent link to slack invite. (sigstore#59)

Signed-off-by: Dan Lorenc <dlorenc@google.com>
Signed-off-by: Priya Wadhwa <priyawadhwa@google.com>

* Signing works with kms, desperately need to clean up code

Signed-off-by: Priya Wadhwa <priyawadhwa@google.com>

* Refactored sign.go to remove duplicate code

Signed-off-by: Priya Wadhwa <priyawadhwa@google.com>

* Verify works with a remote public key in a KMS

Signed-off-by: Priya Wadhwa <priyawadhwa@google.com>

* Fix e2e testS

Signed-off-by: Priya Wadhwa <priyawadhwa@google.com>

Co-authored-by: dlorenc <dlorenc@google.com>
  • Loading branch information
priyawadhwa and dlorenc committed Mar 10, 2021
1 parent cc12e52 commit f74fac5
Show file tree
Hide file tree
Showing 9 changed files with 546 additions and 37 deletions.
14 changes: 12 additions & 2 deletions cmd/cli/generate_key_pair.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"os"

"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/kms"

"github.com/peterbourgon/ff/v3/ffcli"
"golang.org/x/term"
Expand All @@ -33,6 +34,7 @@ import (
func GenerateKeyPair() *ffcli.Command {
var (
flagset = flag.NewFlagSet("cosign generate-key-pair", flag.ExitOnError)
kmsVal = flagset.String("kms", "", "create key pair in KMS service to use for signing")
)

return &ffcli.Command{
Expand All @@ -41,12 +43,20 @@ func GenerateKeyPair() *ffcli.Command {
ShortHelp: "generate-key-pair generates a key-pair",
FlagSet: flagset,
Exec: func(ctx context.Context, args []string) error {
return GenerateKeyPairCmd(ctx)
return GenerateKeyPairCmd(ctx, *kmsVal)
},
}
}

func GenerateKeyPairCmd(ctx context.Context) error {
func GenerateKeyPairCmd(ctx context.Context, kmsVal string) error {
if kmsVal != "" {
k, err := kms.Get(ctx, kmsVal)
if err != nil {
return err
}
return k.CreateKey(ctx)
}

keys, err := cosign.GenerateKeyPair(getPass)
if err != nil {
return err
Expand Down
77 changes: 52 additions & 25 deletions cmd/cli/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/peterbourgon/ff/v3/ffcli"
"github.com/pkg/errors"
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/kms"
)

type annotationsMap struct {
Expand Down Expand Up @@ -65,6 +66,7 @@ func Sign() *ffcli.Command {
var (
flagset = flag.NewFlagSet("cosign sign", flag.ExitOnError)
key = flagset.String("key", "", "path to the private key")
kmsVal = flagset.String("kms", "", "sign via a private key stored in a KMS")
upload = flagset.Bool("upload", true, "whether to upload the signature")
payloadPath = flagset.String("payload", "", "path to a payload file to use rather than generating one.")
forceTlog = flagset.Bool("force-tlog", false, "whether to upload to the tlog even when the image is private")
Expand All @@ -77,32 +79,31 @@ func Sign() *ffcli.Command {
ShortHelp: "Sign the supplied container image",
FlagSet: flagset,
Exec: func(ctx context.Context, args []string) error {
if *key == "" {
if *key == "" && *kmsVal == "" {
return flag.ErrHelp
}

if len(args) != 1 {
return flag.ErrHelp
}

return SignCmd(ctx, *key, args[0], *upload, *payloadPath, annotations.annotations, getPass, *forceTlog)
return SignCmd(ctx, *key, args[0], *upload, *payloadPath, annotations.annotations, *kmsVal, getPass, *forceTlog)
},
}
}

func SignCmd(ctx context.Context, keyPath string,
imageRef string, upload bool, payloadPath string,
annotations map[string]string, pf cosign.PassFunc, forceTlog bool) error {
annotations map[string]string, kmsVal string, pf cosign.PassFunc, forceTlog bool) error {

ref, err := name.ParseReference(imageRef)
if err != nil {
return err
return errors.Wrap(err, "parsing reference")
}

get, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
if err != nil {
return err
return errors.Wrap(err, "getting remote image")
}

// The payload can be specified via a flag to skip generation.
var payload []byte
if payloadPath != "" {
Expand All @@ -112,26 +113,29 @@ func SignCmd(ctx context.Context, keyPath string,
payload, err = cosign.Payload(get.Descriptor, annotations)
}
if err != nil {
return err
return errors.Wrap(err, "payload")
}

pass, err := pf(false)
if err != nil {
return err
}
kb, err := ioutil.ReadFile(keyPath)
if err != nil {
return errors.Wrapf(err, "reading %s", keyPath)
}
pk, err := cosign.LoadPrivateKey(kb, pass)
if err != nil {
return errors.Wrap(err, "loading private key")
var signature []byte
var publicKey *ecdsa.PublicKey
if kmsVal != "" {
k, err := kms.Get(ctx, kmsVal)
if err != nil {
return err
}
signature, err = k.Sign(ctx, get, payload)
if err != nil {
return errors.Wrap(err, "signing")
}
publicKey, err = k.PublicKey(ctx)
if err != nil {
return errors.Wrap(err, "getting public key")
}
} else {
signature, publicKey, err = sign(ctx, get, keyPath, payload, pf)
}

h := sha256.Sum256(payload)
signature, err := ecdsa.SignASN1(rand.Reader, pk, h[:])
if err != nil {
return errors.Wrap(err, "signing asn1")
return errors.Wrap(err, "signing")
}

if !upload {
Expand All @@ -152,7 +156,7 @@ func SignCmd(ctx context.Context, keyPath string,
//private image!
if forceTlog {
fmt.Println("force uploading signature of private image to tlog")
return cosign.UploadTLog(signature, payload, &pk.PublicKey)
return cosign.UploadTLog(signature, payload, publicKey)
} else {
fmt.Println("skipping upload of private image, use --force-tlog to upload")
return nil
Expand All @@ -161,5 +165,28 @@ func SignCmd(ctx context.Context, keyPath string,
if os.Getenv(cosign.TLogEnv) != "1" {
return nil
}
return cosign.UploadTLog(signature, payload, &pk.PublicKey)
return cosign.UploadTLog(signature, payload, publicKey)
}

func sign(ctx context.Context, img *remote.Descriptor, keyPath string, payload []byte, pf cosign.PassFunc) (signature []byte, publicKey *ecdsa.PublicKey, err error) {
pass, err := pf(false)
if err != nil {
return
}
kb, err := ioutil.ReadFile(keyPath)
if err != nil {
return
}
pk, err := cosign.LoadPrivateKey(kb, pass)
if err != nil {
return
}

h := sha256.Sum256(payload)
signature, err = ecdsa.SignASN1(rand.Reader, pk, h[:])
if err != nil {
return
}
publicKey = &pk.PublicKey
return
}
12 changes: 9 additions & 3 deletions cmd/cli/verify.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ import (

"github.com/google/go-containerregistry/pkg/name"
"github.com/peterbourgon/ff/v3/ffcli"
"github.com/pkg/errors"
"github.com/sigstore/cosign/pkg/cosign"
)

func Verify() *ffcli.Command {
var (
flagset = flag.NewFlagSet("cosign verify", flag.ExitOnError)
kmsVal = flagset.String("kms", "", "verify via a public key stored in a KMS")
key = flagset.String("key", "", "path to the public key")
checkClaims = flagset.Bool("check-claims", true, "whether to check the claims found")
annotations = annotationsMap{}
Expand All @@ -42,16 +44,20 @@ func Verify() *ffcli.Command {
ShortHelp: "Verify a signature on the supplied container image",
FlagSet: flagset,
Exec: func(ctx context.Context, args []string) error {
if *key == "" {
if *key == "" && *kmsVal == "" {
return flag.ErrHelp
}
if len(args) != 1 {
return flag.ErrHelp
}

pubKey, err := cosign.LoadPublicKey(*key)
pubKeyDescriptor := *key
if *kmsVal != "" {
pubKeyDescriptor = *kmsVal
}
pubKey, err := cosign.LoadPublicKey(pubKeyDescriptor)
if err != nil {
return flag.ErrHelp
return errors.Wrap(err, "loading public key")
}

co := cosign.CheckOpts{
Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/sigstore/cosign
go 1.15

require (
cloud.google.com/go v0.78.0
github.com/go-openapi/strfmt v0.20.0
github.com/go-openapi/swag v0.19.14
github.com/google/go-cmp v0.5.4
Expand All @@ -13,5 +14,8 @@ require (
github.com/pkg/errors v0.9.1
github.com/sigstore/rekor v0.1.1-0.20210228052401-f0b66bf3835c
github.com/theupdateframework/go-tuf v0.0.0-20201230183259-aee6270feb55
gocloud.dev v0.22.0
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb // indirect
google.golang.org/protobuf v1.25.0 // indirect
)

0 comments on commit f74fac5

Please sign in to comment.