diff --git a/README.md b/README.md index 7a04d40b7ff..21e7662f2c5 100644 --- a/README.md +++ b/README.md @@ -459,12 +459,12 @@ Note: different registries might expect different formats for the "repository." Generated private keys are stored in PEM format. The keys encrypted under a password using scrypt as a KDF and nacl/secretbox for encryption. -They have a PEM header of `ENCRYPTED COSIGN PRIVATE KEY`: +They have a PEM header of `ENCRYPTED SIGSTORE PRIVATE KEY`: ```shell ------BEGIN ENCRYPTED COSIGN PRIVATE KEY----- +-----BEGIN ENCRYPTED SIGSTORE PRIVATE KEY----- ... ------END ENCRYPTED COSIGN PRIVATE KEY----- +-----END ENCRYPTED SIGSTORE PRIVATE KEY----- ``` Public keys are stored on disk in PEM-encoded standard PKIX format with a header of `PUBLIC KEY`. diff --git a/pkg/cosign/keys.go b/pkg/cosign/keys.go index 9a51e0d8c07..3ab43cae479 100644 --- a/pkg/cosign/keys.go +++ b/pkg/cosign/keys.go @@ -38,7 +38,8 @@ import ( ) const ( - CosignPrivateKeyPemType = "ENCRYPTED COSIGN PRIVATE KEY" + CosignPrivateKeyPemType = "ENCRYPTED COSIGN PRIVATE KEY" + SigstorePrivateKeyPemType = "ENCRYPTED SIGSTORE PRIVATE KEY" // PEM-encoded PKCS #1 RSA private key RSAPrivateKeyPemType = "RSA PRIVATE KEY" // PEM-encoded ECDSA private key @@ -134,10 +135,10 @@ func ImportKeyPair(keyPath string, pf PassFunc) (*KeysBytes, error) { default: return nil, fmt.Errorf("unsupported private key") } - return marshalKeyPair(Keys{pk, pk.Public()}, pf) + return marshalKeyPair(p.Type, Keys{pk, pk.Public()}, pf) } -func marshalKeyPair(keypair Keys, pf PassFunc) (key *KeysBytes, err error) { +func marshalKeyPair(ptype string, keypair Keys, pf PassFunc) (key *KeysBytes, err error) { x509Encoded, err := x509.MarshalPKCS8PrivateKey(keypair.private) if err != nil { return nil, fmt.Errorf("x509 encoding private key: %w", err) @@ -156,10 +157,15 @@ func marshalKeyPair(keypair Keys, pf PassFunc) (key *KeysBytes, err error) { return nil, err } + // default to SIGSTORE, but keep support of COSIGN + if ptype != CosignPrivateKeyPemType { + ptype = SigstorePrivateKeyPemType + } + // store in PEM format privBytes := pem.EncodeToMemory(&pem.Block{ Bytes: encBytes, - Type: CosignPrivateKeyPemType, + Type: ptype, }) // Now do the public key @@ -182,7 +188,8 @@ func GenerateKeyPair(pf PassFunc) (*KeysBytes, error) { return nil, err } - return marshalKeyPair(Keys{priv, priv.Public()}, pf) + // Emit SIGSTORE keys by default + return marshalKeyPair(SigstorePrivateKeyPemType, Keys{priv, priv.Public()}, pf) } // TODO(jason): Move this to an internal package. @@ -205,7 +212,7 @@ func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) { if p == nil { return nil, errors.New("invalid pem block") } - if p.Type != CosignPrivateKeyPemType { + if p.Type != CosignPrivateKeyPemType && p.Type != SigstorePrivateKeyPemType { return nil, fmt.Errorf("unsupported pem type: %s", p.Type) } diff --git a/pkg/cosign/keys_test.go b/pkg/cosign/keys_test.go index e8586cbf0b3..ab031d238c3 100644 --- a/pkg/cosign/keys_test.go +++ b/pkg/cosign/keys_test.go @@ -257,6 +257,32 @@ const ed25519key = `-----BEGIN PRIVATE KEY----- MC4CAQAwBQYDK2VwBCIEIALEbo1EFnWFqBK/wC+hhypG/8hXEerwdNetAoFoFVdv -----END PRIVATE KEY-----` +// COSIGN labeled key +const pemcosignkey = `-----BEGIN ENCRYPTED COSIGN PRIVATE KEY----- +eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 +OCwicCI6MX0sInNhbHQiOiJ4WWdoc09JTUxUWGNOT0RsclNIOUNKc1FlOVFnZmN1 +cmUrMXlLdHh1TlkwPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 +Iiwibm9uY2UiOiI0cS9PSlVmaXJkSUkrUjZ0ajZBMmcyQ0JqL25xdFNicCJ9LCJj +aXBoZXJ0ZXh0IjoiKzB4Q3NzcFN0WStBczdKanJpOWtsbHBWd2JhcUI4ZWJNdWto +eS9aVE1MSXRsL3B1YS9jWVJvbytLRGxMWWdmOW1kSjk4K1FnQW9oTktoYnJPMTcw +MHdBY1JTMjFDOE4zQUNJRUVZaWpOMllBNnMraGJSbkhjUnd4eGhDMDFtb2FvL0dO +Y1pmbEJheXZMV3pXblo4d2NDZ2ZpT1o1VXlRTEFJMHh0dnR6dEh3cTdDV1Vhd3V4 +RlhlNDZzck9TUE9SNHN6bytabWErUGovSFE9PSJ9 +-----END ENCRYPTED COSIGN PRIVATE KEY-----` + +// SIGSTORE labeled key +const pemsigstorekey = `-----BEGIN ENCRYPTED SIGSTORE PRIVATE KEY----- +eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6 +OCwicCI6MX0sInNhbHQiOiI3T3VGd2VsbWZZNXVId2NoaURSc210anNwZ2ZlZjFG +Mk5lOGFDTjVLYVpZPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94 +Iiwibm9uY2UiOiJQNHk4OGhCb3ZTa09MbXN0bFVBaGJwdDJ0K2xTNUxQSCJ9LCJj +aXBoZXJ0ZXh0IjoiMnB1QzdyZldJOWh3bnJlQ2s4aUZDRlVwQlRrSzRJNlIvbFBF +cnBDekpXUGpJWXl4eGVIL1A2VW52cFJHdVhla1NNb3JMdGhLamdoQ1JlNy82NDVH +QWtoVm1LRC92eEF0S2EvbE1abENSQ3FlekJGUFd1dzNpeFRtZ2xhb2J1ZFVSbUVs +bmNGOGlZbzBTMVl6Y1ZOMVFwY2J2c0dNcUlYRzVlbmdteGp5dCtBcXlyZTF0Q0Y0 +V01tU1BlaEljNlBqd2h1Q2xHaVpJUWRvTGc9PSJ9 +-----END ENCRYPTED SIGSTORE PRIVATE KEY-----` + func pass(s string) PassFunc { return func(_ bool) ([]byte, error) { return []byte(s), nil @@ -290,6 +316,41 @@ func TestLoadECDSAPrivateKey(t *testing.T) { } } +func TestReadingPrivatePemTypes(t *testing.T) { + testCases := []struct { + pemType string + pemData []byte + expected error + }{ + { + pemType: "COSIGN PEM Type", + pemData: []byte(pemcosignkey), + expected: nil, + }, + { + pemType: "SISTORE PEM Type", + pemData: []byte(pemsigstorekey), + expected: nil, + }, + } + + for _, tc := range testCases { + t.Run(tc.pemType, func(t *testing.T) { + _, err := LoadPrivateKey(tc.pemData, []byte("hello")) + require.Equal(t, tc.expected, err) + }) + } +} + +func TestWritingPrivatePemTypes(t *testing.T) { + keys, err := GenerateKeyPair(pass("hello")) + if err != nil { + t.Fatal(err) + } + + require.Contains(t, string(keys.PrivateBytes), SigstorePrivateKeyPemType) +} + func TestImportPrivateKey(t *testing.T) { testCases := []struct { fileName string