diff --git a/.gitignore b/.gitignore index 7370cfb6396..e4da2287fce 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,4 @@ # cosign stuff cosign.key cosign.pub -cosign +/cosign diff --git a/cmd/generate_key_pair.go b/cmd/generate_key_pair.go index 30b50d3e69b..69b1b2f1462 100644 --- a/cmd/generate_key_pair.go +++ b/cmd/generate_key_pair.go @@ -18,8 +18,6 @@ package main import ( "context" - "crypto/ed25519" - "crypto/rand" "encoding/pem" "errors" "flag" @@ -27,7 +25,7 @@ import ( "io/ioutil" "os" - "github.com/theupdateframework/go-tuf/encrypted" + "github.com/projectcosign/cosign/pkg/cosign" "github.com/peterbourgon/ff/v3/ffcli" "golang.org/x/term" @@ -50,28 +48,12 @@ func GenerateKeyPair() *ffcli.Command { } func generateKeyPair(ctx context.Context) error { - pub, priv, err := ed25519.GenerateKey(rand.Reader) + keys, err := cosign.GenerateKeyPair(getPass) if err != nil { return err } - - // Encrypt the private key and store it. - password, err := getPass(true) - if err != nil { - return err - } - - encBytes, err := encrypted.Encrypt(priv, password) - if err != nil { - return err - } - - privBytes := pem.EncodeToMemory(&pem.Block{ - Bytes: encBytes, - Type: "ENCRYPTED COSIGN PRIVATE KEY", - }) // TODO: make sure the perms are locked down first. - if err := ioutil.WriteFile("cosign.key", privBytes, 0600); err != nil { + if err := ioutil.WriteFile("cosign.key", keys.PrivateBytes, 0600); err != nil { return err } fmt.Fprintln(os.Stderr, "Private key written to cosign.key") @@ -79,7 +61,7 @@ func generateKeyPair(ctx context.Context) error { // Now do the public key pubBytes := pem.EncodeToMemory(&pem.Block{ Type: "COSIGN PUBLIC KEY", - Bytes: pub, + Bytes: keys.PublicBytes, }) if err := ioutil.WriteFile("cosign.pub", pubBytes, 0600); err != nil { return err diff --git a/cmd/sign.go b/cmd/sign.go index dcd21973466..7bc4c064de6 100644 --- a/cmd/sign.go +++ b/cmd/sign.go @@ -113,7 +113,11 @@ func sign(ctx context.Context, keyPath string, if err != nil { return err } - pk, err := cosign.LoadPrivateKey(keyPath, pass) + kb, err := ioutil.ReadFile(keyPath) + if err != nil { + return err + } + pk, err := cosign.LoadPrivateKey(kb, pass) if err != nil { return err } diff --git a/pkg/cosign/keys.go b/pkg/cosign/keys.go new file mode 100644 index 00000000000..475b8100abe --- /dev/null +++ b/pkg/cosign/keys.go @@ -0,0 +1,50 @@ +package cosign + +import ( + "crypto/ed25519" + "crypto/rand" + "encoding/pem" + + "github.com/theupdateframework/go-tuf/encrypted" +) + +type PassFunc func(bool) ([]byte, error) + +type Keys struct { + PrivateBytes []byte + PublicBytes []byte +} + +func GenerateKeyPair(pf PassFunc) (*Keys, error) { + pub, priv, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, err + } + + // Encrypt the private key and store it. + password, err := pf(true) + if err != nil { + return nil, err + } + + encBytes, err := encrypted.Encrypt(priv, password) + if err != nil { + return nil, err + } + + privBytes := pem.EncodeToMemory(&pem.Block{ + Bytes: encBytes, + Type: "ENCRYPTED COSIGN PRIVATE KEY", + }) + + // Now do the public key + pubBytes := pem.EncodeToMemory(&pem.Block{ + Type: "COSIGN PUBLIC KEY", + Bytes: pub, + }) + + return &Keys{ + PrivateBytes: privBytes, + PublicBytes: pubBytes, + }, nil +} diff --git a/pkg/cosign/payload.go b/pkg/cosign/payload.go index ecb20c779f3..05917d1d2a7 100644 --- a/pkg/cosign/payload.go +++ b/pkg/cosign/payload.go @@ -23,7 +23,6 @@ import ( ) func Payload(img v1.Descriptor, a map[string]string) ([]byte, error) { - simpleSigning := SimpleSigning{ Critical: Critical{ Image: Image{ diff --git a/pkg/cosign/sign.go b/pkg/cosign/sign.go index 4af101819ee..0cf83b2ff4e 100644 --- a/pkg/cosign/sign.go +++ b/pkg/cosign/sign.go @@ -19,8 +19,8 @@ package cosign import ( "crypto/ed25519" "encoding/pem" + "errors" "fmt" - "io/ioutil" "github.com/theupdateframework/go-tuf/encrypted" ) @@ -30,14 +30,12 @@ const ( sigkey = "dev.cosignproject.cosign/signature" ) -func LoadPrivateKey(keyPath string, pass []byte) (ed25519.PrivateKey, error) { - b, err := ioutil.ReadFile(keyPath) - if err != nil { - return nil, err - } - +func LoadPrivateKey(key []byte, pass []byte) (ed25519.PrivateKey, error) { // Decrypt first - p, _ := pem.Decode(b) + p, _ := pem.Decode(key) + if p == nil { + return nil, errors.New("invalid pem block") + } if p.Type != pemType { return nil, fmt.Errorf("unsupported pem type: %s", p.Type) } diff --git a/pkg/cosign/sign_test.go b/pkg/cosign/sign_test.go new file mode 100644 index 00000000000..08ad1b7063a --- /dev/null +++ b/pkg/cosign/sign_test.go @@ -0,0 +1,56 @@ +/* +Copyright The Cosign Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cosign + +import ( + "crypto/rand" + "testing" +) + +func pass(s string) PassFunc { + return func(_ bool) ([]byte, error) { + return []byte(s), nil + } +} + +func TestLoadPrivateKey(t *testing.T) { + // Generate a valid keypair + keys, err := GenerateKeyPair(pass("hello")) + if err != nil { + t.Fatal(err) + } + + // Load the private key with the right password + if _, err := LoadPrivateKey(keys.PrivateBytes, []byte("hello")); err != nil { + t.Errorf("unexpected error decrypting key: %s", err) + } + + // Try it with the wrong one + if _, err := LoadPrivateKey(keys.PrivateBytes, []byte("wrong")); err == nil { + t.Error("expected error decrypting key!") + } + + // Try to decrypt garbage + buf := [100]byte{} + if _, err := rand.Read(buf[:]); err != nil { + t.Fatal(err) + } + if _, err := LoadPrivateKey(buf[:], []byte("wrong")); err == nil { + t.Error("expected error decrypting key!") + } + +} diff --git a/pkg/cosign/verify.go b/pkg/cosign/verify.go index 75ef862d87c..618e740a2e0 100644 --- a/pkg/cosign/verify.go +++ b/pkg/cosign/verify.go @@ -29,7 +29,6 @@ import ( const pubKeyPemType = "COSIGN PUBLIC KEY" func LoadPublicKey(keyRef string) (ed25519.PublicKey, error) { - // The key could be plaintext or in a file. // First check if the file exists. if _, err := os.Stat(keyRef); os.IsNotExist(err) {