Skip to content

Commit

Permalink
Document ImportKeyPair and LoadPrivateKey functions in pkg/cosign (#3776
Browse files Browse the repository at this point in the history
)

Document pkg/cosign key utility functions & supported key formats

Signed-off-by: Dmitry S <[email protected]>
  • Loading branch information
dmitris authored Jul 11, 2024
1 parent bdcbf44 commit f7a5725
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 10 deletions.
17 changes: 10 additions & 7 deletions pkg/cosign/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ type Keys struct {
public crypto.PublicKey
}

// TODO(jason): Move this to an internal package.
type KeysBytes struct {
PrivateBytes []byte
PublicBytes []byte
Expand All @@ -69,12 +68,17 @@ func (k *KeysBytes) Password() []byte {
return k.password
}

// TODO(jason): Move this to an internal package.
// GeneratePrivateKey generates an ECDSA private key with the P-256 curve.
func GeneratePrivateKey() (*ecdsa.PrivateKey, error) {
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
}

// TODO(jason): Move this to the only place it's used in cmd/cosign/cli/importkeypair, and unexport it.
// ImportKeyPair imports a key pair from a file containing a PEM-encoded
// private key encoded with a password provided by the 'pf' function.
// The private key can be in one of the following formats:
// - RSA private key (PKCS #1)
// - ECDSA private key
// - PKCS #8 private key (RSA, ECDSA or ED25519).
func ImportKeyPair(keyPath string, pf PassFunc) (*KeysBytes, error) {
kb, err := os.ReadFile(filepath.Clean(keyPath))
if err != nil {
Expand Down Expand Up @@ -180,7 +184,6 @@ func marshalKeyPair(ptype string, keypair Keys, pf PassFunc) (key *KeysBytes, er
}, nil
}

// TODO(jason): Move this to an internal package.
func GenerateKeyPair(pf PassFunc) (*KeysBytes, error) {
priv, err := GeneratePrivateKey()
if err != nil {
Expand All @@ -191,7 +194,7 @@ func GenerateKeyPair(pf PassFunc) (*KeysBytes, error) {
return marshalKeyPair(SigstorePrivateKeyPemType, Keys{priv, priv.Public()}, pf)
}

// TODO(jason): Move this to an internal package.
// PemToECDSAKey marshals and returns the PEM-encoded ECDSA public key.
func PemToECDSAKey(pemBytes []byte) (*ecdsa.PublicKey, error) {
pub, err := cryptoutils.UnmarshalPEMToPublicKey(pemBytes)
if err != nil {
Expand All @@ -204,7 +207,8 @@ func PemToECDSAKey(pemBytes []byte) (*ecdsa.PublicKey, error) {
return ecdsaPub, nil
}

// TODO(jason): Move this to pkg/signature, the only place it's used, and unimport it.
// LoadPrivateKey loads a cosign PEM private key encrypted with the given passphrase,
// and returns a SignerVerifier instance. The private key must be in the PKCS #8 format.
func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) {
// Decrypt first
p, _ := pem.Decode(key)
Expand All @@ -219,7 +223,6 @@ func LoadPrivateKey(key []byte, pass []byte) (signature.SignerVerifier, error) {
if err != nil {
return nil, fmt.Errorf("decrypt: %w", err)
}

pk, err := x509.ParsePKCS8PrivateKey(x509Encoded)
if err != nil {
return nil, fmt.Errorf("parsing private key: %w", err)
Expand Down
63 changes: 60 additions & 3 deletions pkg/cosign/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,35 @@ pGAaLxggvtvuncMuTrG+cdmsR9SafSFKRS92NCxhOUonQ+NP6mLskIGzJZoQ5JvQ
qGzRVIDGbNkrVHM0IsAtHRpC0rYrtZY+9OwiraGcsqUMLwwQdCA=
-----END RSA PRIVATE KEY-----`

// RSA 2048 key encoded with PCKS#1
const validrsapkcs1 = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAqvVkqzBrMzl4TC5VsBgXnQiCo861QcB1TqdwgGzYkrNdF6fr
UisPWgjMJixolmpwHv+088rsbiSlD9hc9DmzgCJPto7wvGeTILa9cNHCGKm12q7K
TFnVUTH9z6D4E3F/IsI22Pg8+cSyeDn+LKxMadTohlcXJ8jqcH75KezoGngMp5OJ
vqs93lkLep+UJMspV029z7PzF9uoT8gI02Adfq5Zkfu8VmIy8gkpYgTBCpNnD01u
vo6HoAYG/mHqgPYivWBwi221GPjJWmCPB1rIJHlpAYUETA5jUY2UwP1oQx54ybcj
eNjMRP5J0cGyOI8MT0j9ul7/DJde3Ds5A7BA9QIDAQABAoIBAQCJ3Q5rhsZMLsI2
HP943FTei+heFOnStlNjNF/jEOOtmfsugnmgb50XrBSFjDZjZj44oVjZaQE06VQ6
7O44/PcmE4VY4Ph91sCtFvC6NE1j+ifuzBnTbHY73iah81tawqIV86yrV7REbzzE
+29fsyqEBe/ltgG0Ua/NPHfOOYALJwZVx8ozkz7xOyU23kNxSzp3T0FBnYYIuzrI
a4h7FVxGLbIJQ3xWBU5xkd4m7EqgFYkWCfSXAVoLT2z7eJSYAmITuiQXl8uDz1XY
lWKgOwkRJrMVVD8hDME7Hoc/RlKmYX64IZ3lv70NuyKDPTuhmoIQRJ49mVaqdPtH
v0Z9L0tBAoGBAMPFcJFaR+VdmsZ2DXQlsPQNAB064SYbIXx/pxNVoDYkHyMfZsf3
vjf4gMKNsHTM4u812UpsE5762OqdVKmWXQc60mkuEk7N55iXuBJiJxSpuj6IbiLw
ogV+B40UC9luOISQpDYdY1Km1ho4HRngNkXMlJ48tFuwIP3lwwz3FtFZAoGBAN+N
wVssBvNhHzGfcUMxxCwJKfHCx1ANWuTe+AsDtpZRTExMcX1PH1euxUV9aII9Klg7
A7FN1It78pDrQBNQJoeMON+5N53//geY6stDfhPkOoT8Zqg2VEz4WRihUgAUHESk
pUVYSvEXG7J7AG5iGgn0B3P9PMvvReIHnTeQ1rz9AoGAWAR31NHrSyMniBzhdZvQ
kBkcOQgU3AYMqyXVXyr7KfxZh3gBxNwMyKtQcKg1cn3/dZ8XP4+RzsNnLSxpOQni
b3Kx0RomnwmSG5fy6Uj52x9oHd9G7SyVG7UK/hHKNgqJHIjPW4kg87MQxZ7+7nhQ
zlbpZq9SQ3rPind3l2er+ZkCgYASFs9ZiEN7uBUlF8i7bjB4e7lYJbGpCZucP2qE
waUpnqR03A6m3BsmJi8yQ0aMm1Rs1UGkPC8BpmLnVRHXPjoP58nGWJ9meotcpAQD
tI9kHqiZkC7iV5sUq1fSRWN0PCxZZZU1+kH+JieIlqlfRTLkMUnVGd2shsz50DHp
iB/IJQKBgAO3kRRVszif2jdo7gzDsiSQ+fSyD6yEE9eP+uNwLZw9bQhyW5wlXF+t
dR5olNrc0bP542MHL5vigRnezq9hT1hbLkQg/MA2k5FrMHIZshfWITnI5B5I2sw6
wu/XEVtNr8RincoHXjov4DiqgbLPWubM7FHLN5CW6nRLXhGkb4+7
-----END RSA PRIVATE KEY-----`

// RSA 2048 key encoded with PKCS#8
const validrsapkcs8 = `-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCwDtRl4McMhk4Q
Expand Down Expand Up @@ -257,7 +286,7 @@ const ed25519key = `-----BEGIN PRIVATE KEY-----
MC4CAQAwBQYDK2VwBCIEIALEbo1EFnWFqBK/wC+hhypG/8hXEerwdNetAoFoFVdv
-----END PRIVATE KEY-----`

// COSIGN labeled key
// COSIGN labeled RSA key
const pemcosignkey = `-----BEGIN ENCRYPTED COSIGN PRIVATE KEY-----
eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6
OCwicCI6MX0sInNhbHQiOiJ4WWdoc09JTUxUWGNOT0RsclNIOUNKc1FlOVFnZmN1
Expand All @@ -270,6 +299,19 @@ Y1pmbEJheXZMV3pXblo4d2NDZ2ZpT1o1VXlRTEFJMHh0dnR6dEh3cTdDV1Vhd3V4
RlhlNDZzck9TUE9SNHN6bytabWErUGovSFE9PSJ9
-----END ENCRYPTED COSIGN PRIVATE KEY-----`

// COSIGN labeled EC key
const pemcosigneckey = `-----BEGIN ENCRYPTED COSIGN PRIVATE KEY-----
eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjo2NTUzNiwiciI6
OCwicCI6MX0sInNhbHQiOiJHK3F5WTYrNzhNS0JzMXNGTGs1ajYwcS9kS3Z1czBW
VkhlSHZybC9POTF3PSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94
Iiwibm9uY2UiOiJRc2JGdG13WDRDK2ttV3ZCcVRaMEFGOUFYdk1jRmg1SCJ9LCJj
aXBoZXJ0ZXh0IjoiREM5T28zeldiYVQzSXYwdFVnWEdycjUxYW1samwwNlQ5MTNP
VkxPbWpuMWhnK2o2WXRUbWg3SGhZSlY1N2J5eGE0Q281bE9YYmRqbTJ3aklubEd1
Um5aZCt5OExnekpSNzFSeEhKVzgrWmRlcFJmYWJMTjdHbDgrSFZEcERVQ3NxQnRh
VngyblpGbFEwWUl1anZwbFphblNGaUVvdERLVGkxZ3VhUXIwUHNzYU01NXZxbTRY
WS9rPSJ9
-----END ENCRYPTED COSIGN PRIVATE KEY-----`

// SIGSTORE labeled key
const pemsigstorekey = `-----BEGIN ENCRYPTED SIGSTORE PRIVATE KEY-----
eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6
Expand Down Expand Up @@ -317,16 +359,22 @@ func TestLoadECDSAPrivateKey(t *testing.T) {
}

func TestReadingPrivatePemTypes(t *testing.T) {
pemECErrMsg := "parsing private key: x509: failed to parse private key (use ParseECPrivateKey instead for this key format)"
testCases := []struct {
pemType string
pemData []byte
expected error
}{
{
pemType: "COSIGN PEM Type",
pemType: "COSIGN PEM RSA Type",
pemData: []byte(pemcosignkey),
expected: nil,
},
{
pemType: "COSIGN PEM EC Type",
pemData: []byte(pemcosigneckey),
expected: errors.New(pemECErrMsg),
},
{
pemType: "SISTORE PEM Type",
pemData: []byte(pemsigstorekey),
Expand All @@ -337,7 +385,11 @@ func TestReadingPrivatePemTypes(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.pemType, func(t *testing.T) {
_, err := LoadPrivateKey(tc.pemData, []byte("hello"))
require.Equal(t, tc.expected, err)
if tc.expected == nil {
require.NoError(t, err)
} else {
require.ErrorContains(t, err, tc.expected.Error())
}
})
}
}
Expand All @@ -363,6 +415,11 @@ func TestImportPrivateKey(t *testing.T) {
pemData: validrsa,
expected: nil,
},
{
fileName: "validrsapkcs1.key",
pemData: validrsapkcs1,
expected: nil,
},
{
fileName: "validrsapkcs8.key",
pemData: validrsapkcs8,
Expand Down

0 comments on commit f7a5725

Please sign in to comment.