Skip to content

Commit

Permalink
Add KMS support to sign-blob and verify-blob (sigstore#104)
Browse files Browse the repository at this point in the history
* Add kms to sign-blob

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

* Add documentation for KMS to README (sigstore#101)

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

* Add the public key from KMS (sigstore#100)

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

* remove unnecessary image input to sign function

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

* Add kms support to verify-blob as well

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

* Fix lint

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

* fix test

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

Co-authored-by: dlorenc <dlorenc@google.com>
  • Loading branch information
priyawadhwa and dlorenc committed Mar 19, 2021
1 parent ddf6533 commit 93bad79
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 41 deletions.
6 changes: 3 additions & 3 deletions cmd/cosign/cli/sign.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func SignCmd(ctx context.Context, keyPath string,
if err != nil {
return err
}
signature, err = k.Sign(ctx, get, payload)
signature, err = k.Sign(ctx, payload)
if err != nil {
return errors.Wrap(err, "signing")
}
Expand All @@ -151,7 +151,7 @@ func SignCmd(ctx context.Context, keyPath string,
return errors.Wrap(err, "getting public key")
}
case keyPath != "":
signature, publicKey, err = sign(ctx, get, keyPath, payload, pf)
signature, publicKey, err = sign(ctx, keyPath, payload, pf)
if err != nil {
return errors.Wrap(err, "signing payload")
}
Expand Down Expand Up @@ -214,7 +214,7 @@ func SignCmd(ctx context.Context, keyPath string,
return nil
}

func sign(ctx context.Context, img *remote.Descriptor, keyPath string, payload []byte, pf cosign.PassFunc) (signature []byte, publicKey *ecdsa.PublicKey, err error) {
func sign(ctx context.Context, keyPath string, payload []byte, pf cosign.PassFunc) (signature []byte, publicKey *ecdsa.PublicKey, err error) {
kb, err := ioutil.ReadFile(filepath.Clean(keyPath))
if err != nil {
return
Expand Down
49 changes: 25 additions & 24 deletions cmd/cosign/cli/sign_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ package cli
import (
"context"
"crypto/ecdsa"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"flag"
"fmt"
Expand All @@ -32,23 +30,25 @@ import (
"github.com/pkg/errors"
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/fulcio"
"github.com/sigstore/cosign/pkg/cosign/kms"
)

func SignBlob() *ffcli.Command {
var (
flagset = flag.NewFlagSet("cosign sign-blob", flag.ExitOnError)
key = flagset.String("key", "", "path to the private key")
kmsVal = flagset.String("kms", "", "sign via a private key stored in a KMS")
b64 = flagset.Bool("b64", true, "whether to base64 encode the output")
)
return &ffcli.Command{
Name: "sign-blob",
ShortUsage: "cosign sign-blob -key <key> [-sig <sig path>] <blob>",
ShortUsage: "cosign sign-blob -key <key>|-kms <kms> [-sig <sig path>] <blob>",
ShortHelp: "Sign the supplied blob, outputting the base64-nocded signature to stdout",
FlagSet: flagset,
Exec: func(ctx context.Context, args []string) error {
// A key file is required unless we're in experimental mode!
if !cosign.Experimental() {
if *key == "" {
if *key == "" && *kmsVal == "" {
return flag.ErrHelp
}
}
Expand All @@ -57,13 +57,13 @@ func SignBlob() *ffcli.Command {
return flag.ErrHelp
}

_, err := SignBlobCmd(ctx, *key, args[0], *b64, GetPass)
_, err := SignBlobCmd(ctx, *key, *kmsVal, args[0], *b64, GetPass)
return err
},
}
}

func SignBlobCmd(ctx context.Context, keyPath, payloadPath string, b64 bool, pf cosign.PassFunc) ([]byte, error) {
func SignBlobCmd(ctx context.Context, keyPath, kmsVal, payloadPath string, b64 bool, pf cosign.PassFunc) ([]byte, error) {
var payload []byte
var err error
if payloadPath == "-" {
Expand All @@ -76,42 +76,44 @@ func SignBlobCmd(ctx context.Context, keyPath, payloadPath string, b64 bool, pf
return nil, err
}

var priv *ecdsa.PrivateKey
var cert string
if keyPath != "" {
kb, err := ioutil.ReadFile(filepath.Clean(keyPath))
var signature []byte
var publicKey *ecdsa.PublicKey

switch {
case keyPath != "":
signature, publicKey, err = sign(ctx, keyPath, payload, pf)
if err != nil {
return nil, errors.Wrapf(err, "reading %s", keyPath)
return nil, errors.Wrap(err, "signing blob")
}
pass, err := pf(false)
case kmsVal != "":
k, err := kms.Get(ctx, kmsVal)
if err != nil {
return nil, err
}
priv, err = cosign.LoadPrivateKey(kb, pass)
signature, err = k.Sign(ctx, payload)
if err != nil {
return nil, errors.Wrap(err, "signing")
}
publicKey, err = k.PublicKey(ctx)
if err != nil {
return nil, errors.Wrap(err, "loading private key")
return nil, errors.Wrap(err, "getting public key")
}
} else { // Keyless!
default: // Keyless!
fmt.Fprintln(os.Stderr, "Generating ephemeral keys...")
priv, err = cosign.GeneratePrivateKey()
priv, err := cosign.GeneratePrivateKey()
if err != nil {
return nil, errors.Wrap(err, "generating cert")
}
fmt.Fprintln(os.Stderr, "Retrieving signed certificate...")
cert, err = fulcio.GetCert(ctx, priv)
cert, err := fulcio.GetCert(ctx, priv)
if err != nil {
return nil, errors.Wrap(err, "retrieving cert")
}
fmt.Fprintf(os.Stderr, "Signing with certificate:\n%s\n", cert)
}
h := sha256.Sum256(payload)
signature, err := ecdsa.SignASN1(rand.Reader, priv, h[:])
if err != nil {
return nil, err
}

if cosign.Experimental() {
index, err := cosign.UploadTLog(signature, payload, &priv.PublicKey)
index, err := cosign.UploadTLog(signature, payload, publicKey)
if err != nil {
return nil, err
}
Expand All @@ -129,6 +131,5 @@ func SignBlobCmd(ctx context.Context, keyPath, payloadPath string, b64 bool, pf
return nil, err
}
}

return signature, nil
}
19 changes: 15 additions & 4 deletions cmd/cosign/cli/verify_blob.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,36 +21,38 @@ import (
"crypto/ecdsa"
"crypto/x509"
"encoding/base64"
"errors"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"

"github.com/peterbourgon/ff/v3/ffcli"
"github.com/pkg/errors"
"github.com/sigstore/cosign/pkg/cosign"
"github.com/sigstore/cosign/pkg/cosign/fulcio"
"github.com/sigstore/cosign/pkg/cosign/kms"
"github.com/sigstore/rekor/cmd/cli/app"
)

func VerifyBlob() *ffcli.Command {
var (
flagset = flag.NewFlagSet("cosign verify-blob", flag.ExitOnError)
key = flagset.String("key", "", "path to the public key")
kmsVal = flagset.String("kms", "", "verify via a public key stored in a KMS")
cert = flagset.String("cert", "", "path to the public certificate")
signature = flagset.String("signature", "", "path to the signature")
)
return &ffcli.Command{
Name: "verify-blob",
ShortUsage: "cosign verify-blob -key <key>|-cert <cert> -signature <sig> <blob>",
ShortUsage: "cosign verify-blob -key <key>|-cert <cert>|-kms <kms> -signature <sig> <blob>",
ShortHelp: "Verify a signature on the supplied blob",
FlagSet: flagset,
Exec: func(ctx context.Context, args []string) error {
if len(args) != 1 {
return flag.ErrHelp
}
return VerifyBlobCmd(ctx, *key, *cert, *signature, args[0])
return VerifyBlobCmd(ctx, *key, *kmsVal, *cert, *signature, args[0])
},
}
}
Expand All @@ -60,7 +62,7 @@ func isb64(data []byte) bool {
return err == nil
}

func VerifyBlobCmd(_ context.Context, keyRef string, certRef string, sigRef string, blobRef string) error {
func VerifyBlobCmd(ctx context.Context, keyRef, kmsVal, certRef, sigRef, blobRef string) error {

var pubKey *ecdsa.PublicKey
var err error
Expand All @@ -71,6 +73,15 @@ func VerifyBlobCmd(_ context.Context, keyRef string, certRef string, sigRef stri
if err != nil {
return err
}
case kmsVal != "":
k, err := kms.Get(ctx, kmsVal)
if err != nil {
return errors.Wrap(err, "getting kms")
}
pubKey, err = k.PublicKey(ctx)
if err != nil {
return errors.Wrap(err, "kms public key")
}
case certRef != "": // KEYLESS MODE!
pems, err := ioutil.ReadFile(certRef)
if err != nil {
Expand Down
3 changes: 1 addition & 2 deletions pkg/cosign/kms/gcp/gcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"regexp"

kms "cloud.google.com/go/kms/apiv1"
"github.com/google/go-containerregistry/pkg/v1/remote"
"github.com/pkg/errors"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
"google.golang.org/protobuf/types/known/wrapperspb"
Expand Down Expand Up @@ -88,7 +87,7 @@ func NewGCP(ctx context.Context, keyResourceID string) (*KMS, error) {
}, nil
}

func (g *KMS) Sign(ctx context.Context, img *remote.Descriptor, payload []byte) (signature []byte, err error) {
func (g *KMS) Sign(ctx context.Context, payload []byte) (signature []byte, err error) {
// Calculate the digest of the message.
digest := sha256.New()
_, err = digest.Write(payload)
Expand Down
4 changes: 1 addition & 3 deletions pkg/cosign/kms/kms.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import (
"crypto/ecdsa"
"fmt"

"github.com/google/go-containerregistry/pkg/v1/remote"

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

Expand All @@ -33,7 +31,7 @@ type KMS interface {

// Sign is responsible for signing an image via the keys
// stored in KMS
Sign(ctx context.Context, img *remote.Descriptor, payload []byte) (signature []byte, err error)
Sign(ctx context.Context, payload []byte) (signature []byte, err error)

// PublicKey returns the public key stored in the KMS
PublicKey(ctx context.Context) (*ecdsa.PublicKey, error)
Expand Down
10 changes: 5 additions & 5 deletions test/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,17 +142,17 @@ func TestSignBlob(t *testing.T) {
ctx := context.Background()

// Verify should fail on a bad input
mustErr(cli.VerifyBlobCmd(ctx, pubKeyPath1, "", "badsig", blob), t)
mustErr(cli.VerifyBlobCmd(ctx, pubKeyPath2, "", "badsig", blob), t)
mustErr(cli.VerifyBlobCmd(ctx, pubKeyPath1, "", "", "badsig", blob), t)
mustErr(cli.VerifyBlobCmd(ctx, pubKeyPath2, "", "", "badsig", blob), t)

// Now sign the blob with one key
sig, err := cli.SignBlobCmd(ctx, privKeyPath1, bp, true, passFunc)
sig, err := cli.SignBlobCmd(ctx, privKeyPath1, "", bp, true, passFunc)
if err != nil {
t.Fatal(err)
}
// Now verify should work with that one, but not the other
must(cli.VerifyBlobCmd(ctx, pubKeyPath1, "", string(sig), bp), t)
mustErr(cli.VerifyBlobCmd(ctx, pubKeyPath2, "", string(sig), bp), t)
must(cli.VerifyBlobCmd(ctx, pubKeyPath1, "", "", string(sig), bp), t)
mustErr(cli.VerifyBlobCmd(ctx, pubKeyPath2, "", "", string(sig), bp), t)
}

func TestGenerate(t *testing.T) {
Expand Down

0 comments on commit 93bad79

Please sign in to comment.