diff --git a/signerverifier/ecdsa.go b/signerverifier/ecdsa.go index f3e6c20..691091a 100644 --- a/signerverifier/ecdsa.go +++ b/signerverifier/ecdsa.go @@ -11,7 +11,10 @@ import ( "os" ) -const ECDSAKeyType = "ecdsa" +const ( + ECDSAKeyType = "ecdsa" + ECDSAKeyScheme = "ecdsa-sha2-nistp256" +) // ECDSASignerVerifier is a dsse.SignerVerifier compliant interface to sign and // verify signatures using ECDSA keys. @@ -89,6 +92,11 @@ func (sv *ECDSASignerVerifier) Public() crypto.PublicKey { // LoadECDSAKeyFromFile returns an SSLibKey instance for an ECDSA key stored in // a file in the custom securesystemslib format. +// +// Deprecated: use LoadKey(). The custom serialization format has been +// deprecated. Use +// https://github.com/secure-systems-lab/securesystemslib/blob/main/docs/migrate_key.py +// to convert your key. func LoadECDSAKeyFromFile(path string) (*SSLibKey, error) { contents, err := os.ReadFile(path) if err != nil { diff --git a/signerverifier/ecdsa_test.go b/signerverifier/ecdsa_test.go index 71163dc..d695412 100644 --- a/signerverifier/ecdsa_test.go +++ b/signerverifier/ecdsa_test.go @@ -148,6 +148,55 @@ func TestECDSASignerVerifierWithDSSEEnvelope(t *testing.T) { assert.Equal(t, "98adf38602c48c5479e9a991ee3f8cbf541ee4f985e00f7a5fc4148d9a45b704", acceptedKeys[0].KeyID) } +func TestECDSASignerVerifierWithDSSEEnvelopeAndPEMKey(t *testing.T) { + key, err := LoadKey(ecdsaPrivateKey) + if err != nil { + t.Fatal(err) + } + + sv, err := NewECDSASignerVerifierFromSSLibKey(key) + if err != nil { + t.Fatal(err) + } + + payloadType := "application/vnd.dsse+json" + payload := []byte("test message") + + es, err := dsse.NewEnvelopeSigner(sv) + if err != nil { + t.Error(err) + } + + env, err := es.SignPayload(context.Background(), payloadType, payload) + if err != nil { + t.Error(err) + } + + assert.Equal(t, "98adf38602c48c5479e9a991ee3f8cbf541ee4f985e00f7a5fc4148d9a45b704", env.Signatures[0].KeyID) + envPayload, err := env.DecodeB64Payload() + assert.Equal(t, payload, envPayload) + assert.Nil(t, err) + + key, err = LoadKey(ecdsaPublicKey) + if err != nil { + t.Fatal(err) + } + + sv, err = NewECDSASignerVerifierFromSSLibKey(key) + if err != nil { + t.Fatal(err) + } + + ev, err := dsse.NewEnvelopeVerifier(sv) + if err != nil { + t.Error(err) + } + + acceptedKeys, err := ev.Verify(context.Background(), env) + assert.Nil(t, err) + assert.Equal(t, "98adf38602c48c5479e9a991ee3f8cbf541ee4f985e00f7a5fc4148d9a45b704", acceptedKeys[0].KeyID) +} + func TestECDSASignerVerifierWithMetablockFile(t *testing.T) { key, err := LoadECDSAKeyFromFile(filepath.Join("test-data", "ecdsa-test-key.pub")) if err != nil { @@ -189,3 +238,45 @@ func TestECDSASignerVerifierWithMetablockFile(t *testing.T) { err = sv.Verify(context.Background(), encodedBytes, decodedSig) assert.Nil(t, err) } + +func TestECDSASignerVerifierWithMetablockFileAndPEMKey(t *testing.T) { + key, err := LoadKey(ecdsaPublicKey) + if err != nil { + t.Fatal(err) + } + + sv, err := NewECDSASignerVerifierFromSSLibKey(key) + if err != nil { + t.Fatal(err) + } + + metadataBytes, err := os.ReadFile(filepath.Join("test-data", "test-ecdsa.98adf386.link")) + if err != nil { + t.Fatal(err) + } + + mb := struct { + Signatures []struct { + KeyID string `json:"keyid"` + Sig string `json:"sig"` + } `json:"signatures"` + Signed any `json:"signed"` + }{} + + if err := json.Unmarshal(metadataBytes, &mb); err != nil { + t.Fatal(err) + } + + assert.Equal(t, "304502201fbb03c0937504182a48c66f9218bdcb2e99a07ada273e92e5e543867f98c8d7022100dbfa7bbf74fd76d76c1d08676419cba85bbd81dfb000f3ac6a786693ddc508f5", mb.Signatures[0].Sig) + assert.Equal(t, sv.keyID, mb.Signatures[0].KeyID) + + encodedBytes, err := cjson.EncodeCanonical(mb.Signed) + if err != nil { + t.Fatal(err) + } + + decodedSig := hexDecode(t, mb.Signatures[0].Sig) + + err = sv.Verify(context.Background(), encodedBytes, decodedSig) + assert.Nil(t, err) +} diff --git a/signerverifier/ed25519.go b/signerverifier/ed25519.go index 0a2210c..d954e14 100644 --- a/signerverifier/ed25519.go +++ b/signerverifier/ed25519.go @@ -88,6 +88,11 @@ func (sv *ED25519SignerVerifier) Public() crypto.PublicKey { // LoadED25519KeyFromFile returns an SSLibKey instance for an ED25519 key stored // in a file in the custom securesystemslib format. +// +// Deprecated: use LoadKey(). The custom serialization format has been +// deprecated. Use +// https://github.com/secure-systems-lab/securesystemslib/blob/main/docs/migrate_key.py +// to convert your key. func LoadED25519KeyFromFile(path string) (*SSLibKey, error) { contents, err := os.ReadFile(path) if err != nil { diff --git a/signerverifier/ed25519_test.go b/signerverifier/ed25519_test.go index 19cf91e..5b1973f 100644 --- a/signerverifier/ed25519_test.go +++ b/signerverifier/ed25519_test.go @@ -164,6 +164,55 @@ func TestED25519SignerVerifierWithDSSEEnvelope(t *testing.T) { assert.Equal(t, "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", acceptedKeys[0].KeyID) } +func TestED25519SignerVerifierWithDSSEEnvelopeAndPEMKey(t *testing.T) { + key, err := LoadKey(ed25519PrivateKey) + if err != nil { + t.Fatal(err) + } + + sv, err := NewED25519SignerVerifierFromSSLibKey(key) + if err != nil { + t.Fatal(err) + } + + payloadType := "application/vnd.dsse+json" + payload := []byte("test message") + + es, err := dsse.NewEnvelopeSigner(sv) + if err != nil { + t.Error(err) + } + + env, err := es.SignPayload(context.Background(), payloadType, payload) + if err != nil { + t.Error(err) + } + + assert.Equal(t, "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", env.Signatures[0].KeyID) + envPayload, err := env.DecodeB64Payload() + assert.Equal(t, payload, envPayload) + assert.Nil(t, err) + + key, err = LoadKey(ed25519PublicKey) + if err != nil { + t.Fatal(err) + } + + sv, err = NewED25519SignerVerifierFromSSLibKey(key) + if err != nil { + t.Fatal(err) + } + + ev, err := dsse.NewEnvelopeVerifier(sv) + if err != nil { + t.Error(err) + } + + acceptedKeys, err := ev.Verify(context.Background(), env) + assert.Nil(t, err) + assert.Equal(t, "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997", acceptedKeys[0].KeyID) +} + func TestED25519SignerVerifierWithMetablockFile(t *testing.T) { key, err := LoadED25519KeyFromFile(filepath.Join("test-data", "ed25519-test-key.pub")) if err != nil { @@ -205,3 +254,45 @@ func TestED25519SignerVerifierWithMetablockFile(t *testing.T) { err = sv.Verify(context.Background(), encodedBytes, decodedSig) assert.Nil(t, err) } + +func TestED25519SignerVerifierWithMetablockFileAndPEMKey(t *testing.T) { + key, err := LoadKey(ed25519PublicKey) + if err != nil { + t.Fatal(err) + } + + sv, err := NewED25519SignerVerifierFromSSLibKey(key) + if err != nil { + t.Fatal(err) + } + + metadataBytes, err := os.ReadFile(filepath.Join("test-data", "test-ed25519.52e3b8e7.link")) + if err != nil { + t.Fatal(err) + } + + mb := struct { + Signatures []struct { + KeyID string `json:"keyid"` + Sig string `json:"sig"` + } `json:"signatures"` + Signed any `json:"signed"` + }{} + + if err := json.Unmarshal(metadataBytes, &mb); err != nil { + t.Fatal(err) + } + + assert.Equal(t, "4c8b7605a9195d4ddba54493bbb5257a9836c1d16056a027fd77e97b95a4f3e36f8bc3c9c9960387d68187760b3072a30c44f992c5bf8f7497c303a3b0a32403", mb.Signatures[0].Sig) + assert.Equal(t, sv.keyID, mb.Signatures[0].KeyID) + + encodedBytes, err := cjson.EncodeCanonical(mb.Signed) + if err != nil { + t.Fatal(err) + } + + decodedSig := hexDecode(t, mb.Signatures[0].Sig) + + err = sv.Verify(context.Background(), encodedBytes, decodedSig) + assert.Nil(t, err) +} diff --git a/signerverifier/rsa.go b/signerverifier/rsa.go index b039659..2abfcb2 100644 --- a/signerverifier/rsa.go +++ b/signerverifier/rsa.go @@ -94,6 +94,11 @@ func (sv *RSAPSSSignerVerifier) Public() crypto.PublicKey { // LoadRSAPSSKeyFromFile returns an SSLibKey instance for an RSA key stored in a // file. +// +// Deprecated: use LoadKey(). The custom serialization format has been +// deprecated. Use +// https://github.com/secure-systems-lab/securesystemslib/blob/main/docs/migrate_key.py +// to convert your key. func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) { contents, err := os.ReadFile(path) if err != nil { @@ -103,9 +108,13 @@ func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) { return LoadRSAPSSKeyFromBytes(contents) } -// LoadRSAPSSKeyFromBytes is a function that takes a byte array as input. This byte array should represent a PEM encoded RSA key, as PEM encoding is required. -// The function returns an SSLibKey instance, which is a struct that holds the key data. - +// LoadRSAPSSKeyFromBytes is a function that takes a byte array as input. This +// byte array should represent a PEM encoded RSA key, as PEM encoding is +// required. The function returns an SSLibKey instance, which is a struct that +// holds the key data. +// +// Deprecated: use LoadKey() for all key types, RSA is no longer the only key +// that uses PEM serialization. func LoadRSAPSSKeyFromBytes(contents []byte) (*SSLibKey, error) { pemData, keyObj, err := decodeAndParsePEM(contents) if err != nil { diff --git a/signerverifier/rsa_test.go b/signerverifier/rsa_test.go index b8711d8..1ca3412 100644 --- a/signerverifier/rsa_test.go +++ b/signerverifier/rsa_test.go @@ -102,7 +102,7 @@ func TestRSAPSSSignerVerifierSignAndVerify(t *testing.T) { } func TestRSAPSSSignerVerifierWithDSSEEnvelope(t *testing.T) { - key, err := LoadRSAPSSKeyFromFile(filepath.Join("test-data", "rsa-test-key")) + key, err := LoadKey(rsaPrivateKey) if err != nil { t.Fatal(err) } @@ -130,7 +130,7 @@ func TestRSAPSSSignerVerifierWithDSSEEnvelope(t *testing.T) { assert.Equal(t, payload, envPayload) assert.Nil(t, err) - key, err = LoadRSAPSSKeyFromFile(filepath.Join("test-data", "rsa-test-key.pub")) + key, err = LoadKey(rsaPublicKey) if err != nil { t.Fatal(err) } @@ -151,7 +151,7 @@ func TestRSAPSSSignerVerifierWithDSSEEnvelope(t *testing.T) { } func TestRSAPSSSignerVerifierWithMetablockFile(t *testing.T) { - key, err := LoadRSAPSSKeyFromFile(filepath.Join("test-data", "rsa-test-key.pub")) + key, err := LoadKey(rsaPublicKey) if err != nil { t.Fatal(err) } diff --git a/signerverifier/signerverifier.go b/signerverifier/signerverifier.go index 85cae65..67d1722 100644 --- a/signerverifier/signerverifier.go +++ b/signerverifier/signerverifier.go @@ -1,7 +1,13 @@ package signerverifier import ( + "crypto/ecdsa" + "crypto/ed25519" + "crypto/rsa" + "crypto/x509" + "encoding/hex" "errors" + "strings" ) var KeyIDHashAlgorithms = []string{"sha256", "sha512"} @@ -12,6 +18,7 @@ var ( ErrUnknownKeyType = errors.New("unknown key type") ErrInvalidThreshold = errors.New("threshold is either less than 1 or greater than number of provided public keys") ErrInvalidKey = errors.New("key object has no value") + ErrInvalidPEM = errors.New("unable to parse PEM block") ) const ( @@ -34,3 +41,106 @@ type KeyVal struct { Identity string `json:"identity,omitempty"` Issuer string `json:"issuer,omitempty"` } + +// LoadKey returns an SSLibKey object when provided a PEM encoded key. +// Currently, RSA, ED25519, and ECDSA keys are supported. +func LoadKey(keyBytes []byte) (*SSLibKey, error) { + pemBlock, rawKey, err := decodeAndParsePEM(keyBytes) + if err != nil { + return nil, err + } + + var key *SSLibKey + switch k := rawKey.(type) { + case *rsa.PublicKey: + pubKeyBytes, err := x509.MarshalPKIXPublicKey(k) + if err != nil { + return nil, err + } + key = &SSLibKey{ + KeyIDHashAlgorithms: KeyIDHashAlgorithms, + KeyType: RSAKeyType, + KeyVal: KeyVal{ + Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))), + }, + Scheme: RSAKeyScheme, + } + + case *rsa.PrivateKey: + pubKeyBytes, err := x509.MarshalPKIXPublicKey(k.Public()) + if err != nil { + return nil, err + } + key = &SSLibKey{ + KeyIDHashAlgorithms: KeyIDHashAlgorithms, + KeyType: RSAKeyType, + KeyVal: KeyVal{ + Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))), + Private: strings.TrimSpace(string(generatePEMBlock(pemBlock.Bytes, RSAPrivateKeyPEM))), + }, + Scheme: RSAKeyScheme, + } + + case ed25519.PublicKey: + key = &SSLibKey{ + KeyIDHashAlgorithms: KeyIDHashAlgorithms, + KeyType: ED25519KeyType, + KeyVal: KeyVal{ + Public: strings.TrimSpace(hex.EncodeToString(k)), + }, + Scheme: ED25519KeyType, + } + + case ed25519.PrivateKey: + pubKeyBytes := k.Public() + key = &SSLibKey{ + KeyIDHashAlgorithms: KeyIDHashAlgorithms, + KeyType: ED25519KeyType, + KeyVal: KeyVal{ + Public: strings.TrimSpace(hex.EncodeToString(pubKeyBytes.(ed25519.PublicKey))), + Private: strings.TrimSpace(hex.EncodeToString(k)), + }, + Scheme: ED25519KeyType, + } + + case *ecdsa.PublicKey: + pubKeyBytes, err := x509.MarshalPKIXPublicKey(k) + if err != nil { + return nil, err + } + key = &SSLibKey{ + KeyIDHashAlgorithms: KeyIDHashAlgorithms, + KeyType: ECDSAKeyType, + KeyVal: KeyVal{ + Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))), + }, + Scheme: ECDSAKeyScheme, + } + + case *ecdsa.PrivateKey: + pubKeyBytes, err := x509.MarshalPKIXPublicKey(k.Public()) + if err != nil { + return nil, err + } + key = &SSLibKey{ + KeyIDHashAlgorithms: KeyIDHashAlgorithms, + KeyType: ECDSAKeyType, + KeyVal: KeyVal{ + Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))), + Private: strings.TrimSpace(string(generatePEMBlock(pemBlock.Bytes, PrivateKeyPEM))), + }, + Scheme: ECDSAKeyScheme, + } + + default: + return nil, ErrUnknownKeyType + } + + keyID, err := calculateKeyID(key) + if err != nil { + return nil, err + } + key.KeyID = keyID + + return key, nil +} diff --git a/signerverifier/signerverifier_test.go b/signerverifier/signerverifier_test.go new file mode 100644 index 0000000..71b1175 --- /dev/null +++ b/signerverifier/signerverifier_test.go @@ -0,0 +1,103 @@ +package signerverifier + +import ( + _ "embed" + "testing" + + "github.com/stretchr/testify/assert" +) + +//go:embed test-data/rsa-test-key +var rsaPrivateKey []byte + +//go:embed test-data/rsa-test-key.pub +var rsaPublicKey []byte + +//go:embed test-data/ed25519-test-key-pem +var ed25519PrivateKey []byte + +//go:embed test-data/ed25519-test-key-pem.pub +var ed25519PublicKey []byte + +//go:embed test-data/ecdsa-test-key-pem +var ecdsaPrivateKey []byte + +//go:embed test-data/ecdsa-test-key-pem.pub +var ecdsaPublicKey []byte + +func TestLoadKey(t *testing.T) { + // RSA expected values + expectedRSAPrivateKey := "-----BEGIN RSA PRIVATE KEY-----\nMIIG5AIBAAKCAYEA04egZRic+dZMVtiQc56DejU4FF1q3aOkUKnD+Q4lTbj1zp6O\nDKJTcktupmrad68jqtMiSGG8he6ELFs377q8bbgEUMWgAf+06Q8oFvUSfOXzZNFI\n7H5SMPOJY5aDWIMIEZ8DlcO7TfkA7D3iAEJXxxTOVS3UAIk5umO7Y7t7yXr8O/C4\nu78krGazCnoblcekMLJZV4O/5BloWNAe/B1cvZdaZUf3brD4ZZrxEtXw/tefhn1a\nHsSUajVW2wwjSpKhqj7Z0XS3bDS3T95/3xsN6+hlS6A7rJfiWpKIRHj0vh2SXLDm\nmhQl1In8TD/aiycTUyWcBRHVPlYFgYPt6SaTVQSgMzSxC43/2fINb2fyt8SbUHJ3\nCt+mzRzd/1AQikWhBdstJLxInewzjYE/sb+c2CmCxMPQG2BwmAWXaaumeJcXVPBl\nMgAcjMatM8bPByTbXpKDnQslOE7g/gswDIwnEm53T13mZzYUvbLJ0q3aljZVLIC3\nIZn3ZwA2yCWchBkVAgMBAAECggGAKswAeCPMMsIYTOPhCftyt2mIEJq78d7Xclh+\npWemxXxcAzNSIx0+i9vWJcZtsBRXv4qbH5DiryhMRpsoDJE36Wz3No5darodFKAz\n6L0pwepWXbn4Kpz+LRhA3kzIA0LzgXkuJQFmZoawGJwGmy3RC57ahiJRB9C7xMnD\n0pBOobuHx+rSvW2VUmou5DpDVYEAZ7fV2p511wUK9xkYg8K/Dj7Ok7pFRfh5MTlx\nd/GgIjdm97Np5dq4+moTShtBEqfqviv1OfDa32DISAOcEKiC2jg0O96khDz2YjK4\n0HAbWrGjVB1v+/kWKTWJ6/ddLb+Dk77KKeZ4pSPKYeUM7jXlyVikntmFTw4CXFvk\n2QqOfJyBxAxcx4eB/n6j1mqIvqL6TjloXn/Bhc/65Fr5een3hLbRnhtNxXBURwVo\nYYJwLw7tZOMKqt51qbKU2XqaII7iVHGPaeDUYs4PaBSSW/E1FFAZbId1GSe4+mDi\nJipxs4M6S9N9FPgTmZlgQ/0j6VMhAoHBANrygq2IsgRjczVO+FhOAmmP6xjbcoII\n582JTunwb8Yf4KJR8DM295LRcafk9Ns4l3QF/rESK8mZAbMUsjKlD4WcE2QTOEoQ\nQBV+lJLDyYeAhmq2684dqaIGA5jEW0GcfDpj42Hhy/qiy1PWTe/O1aFaLaYV0bXL\nPN1CTGpc+DdRh5lX7ftoTS/Do0U9Of30s00Bm9AV0LLoyH5WmXpGWatOYBHHwomi\n08vMsbJelgFzDQPRjHfpj7+EZh1wdqe8cQKBwQD3U8QP7ZatB5ymMLsefm/I6Uor\nwz5SqMyiz+u/Fc+4Ii8SwLsVQw+IoZyxofkKTbMESrgQhLbzC59eRbUcF7GZ+lZQ\nw6gG/+YLvx9MYcEVGeruyPmlYFp6g+vN/qEiPs1oZej8r1XjNj228XdTMAJ2qTbZ\nGVyhEMMbBgd5FFxEqueD5/EILT6xj9BxvQ1m2IFbVIkXfOrhdwEk+RcbXDA0n+rS\nkhBajWQ3eVQGY2hWnYB+1fmumYFs8hAaMAJlCOUCgcBCvi6Ly+HIaLCUDZCzCoS9\nvTuDhlHvxdsz0qmVss+/67PEh4nbcuQhg2tMLQVfVm8E1VcAj3N9rwDPoH155stG\nhX97wEgme7GtW7rayohCoDFZko1rdatiUscB6MmQxK0x94U3L2fI7Zth4TA87CY/\nW4gS2w/khSH2qOE2g0S/SEE3w5AuVWtCJjc9Qh7NhayqytS+qAfIoiGMMcXzekKX\nb/rlMKni3xoFRE7e+uprYrES+uwBGdfSIAAo9UGWfGECgcEA8pCJ4qE+vJaRkQCM\nFD0mvyHl54PGFOWORUOsTy1CGrIT/s1c7l5l1rfB6QkVKYDIyLXLThALKdVFSP0O\nwe2O9pfpna42lh7VbMHWHWBmMJ7JpcUf6ozUUAIf+1j2iZKUfAYu+duwXXWuE0VA\npSqZz+znaQaRrTm2UEOagqpwT7xZ8SlCYKWXLigA4/vpL+u4+myvQ4T1C4leaveN\nLP0+He6VLE2qklTHbAynVtiZ1REFm9+Z0B6nK8U/+58ISjTtAoHBALgqMopFIOMw\nAhhasnrL3Pzxf0WKzKmj/y2yEP0Vctm0muqxFnFwPwyOAd6HODJOSiFPD5VN4jvC\n+Yw96Qn29kHGXTKgL1J9cSL8z6Qzlc+UYCdSwmaZK5r36+NBTJgvKY9KrpkXCkSa\nc5YgIYtXMitmq9NmNvcSJWmuuiept3HFlwkU3pfmwzKNEeqi2jmuIOqI2zCOqX67\nI+YQsJgrHE0TmYxxRkgeYUy7s5DoHE25rfvdy5Lx+xAOH8ZgD1SGOw==\n-----END RSA PRIVATE KEY-----" + expectedRSAPublicKey := "-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA04egZRic+dZMVtiQc56D\nejU4FF1q3aOkUKnD+Q4lTbj1zp6ODKJTcktupmrad68jqtMiSGG8he6ELFs377q8\nbbgEUMWgAf+06Q8oFvUSfOXzZNFI7H5SMPOJY5aDWIMIEZ8DlcO7TfkA7D3iAEJX\nxxTOVS3UAIk5umO7Y7t7yXr8O/C4u78krGazCnoblcekMLJZV4O/5BloWNAe/B1c\nvZdaZUf3brD4ZZrxEtXw/tefhn1aHsSUajVW2wwjSpKhqj7Z0XS3bDS3T95/3xsN\n6+hlS6A7rJfiWpKIRHj0vh2SXLDmmhQl1In8TD/aiycTUyWcBRHVPlYFgYPt6SaT\nVQSgMzSxC43/2fINb2fyt8SbUHJ3Ct+mzRzd/1AQikWhBdstJLxInewzjYE/sb+c\n2CmCxMPQG2BwmAWXaaumeJcXVPBlMgAcjMatM8bPByTbXpKDnQslOE7g/gswDIwn\nEm53T13mZzYUvbLJ0q3aljZVLIC3IZn3ZwA2yCWchBkVAgMBAAE=\n-----END PUBLIC KEY-----" + expectedRSAKeyID := "4e8d20af09fcaed6c388a186427f94a5f7ff5591ec295f4aab2cff49ffe39e9b" + + // ED25519 expected values + expectedED25519PrivateKey := "66f6ebad4aeb949b91c84c9cfd6ee351fc4fd544744bab6e30fb400ba13c6e9a3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f" + expectedED25519PublicKey := "3f586ce67329419fb0081bd995914e866a7205da463d593b3b490eab2b27fd3f" + expectedED25519KeyID := "52e3b8e73279d6ebdd62a5016e2725ff284f569665eb92ccb145d83817a02997" + + // ECDSA expected values + expectedECDSAPrivateKey := "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgCjoPFeWCpjL5OS+h\nwg7JaWoDcpW2np2VJjuVLeSR4QyhRANCAAS74cSqqlctrjyVcf2uRHKCx+wIqrVc\nzfqhfKYn3DGkzrycKqStkJWdn2WQR4LAPypZhM3EPnJ9ZfAmMWH4ruot\n-----END PRIVATE KEY-----" + expectedECDSAPublicKey := "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEu+HEqqpXLa48lXH9rkRygsfsCKq1\nXM36oXymJ9wxpM68nCqkrZCVnZ9lkEeCwD8qWYTNxD5yfWXwJjFh+K7qLQ==\n-----END PUBLIC KEY-----" + expectedECDSAKeyID := "98adf38602c48c5479e9a991ee3f8cbf541ee4f985e00f7a5fc4148d9a45b704" + + t.Run("RSA private key", func(t *testing.T) { + key, err := LoadKey(rsaPrivateKey) + assert.Nil(t, err) + assert.Equal(t, expectedRSAKeyID, key.KeyID) + assert.Equal(t, expectedRSAPublicKey, key.KeyVal.Public) + assert.Equal(t, expectedRSAPrivateKey, key.KeyVal.Private) + assert.Equal(t, RSAKeyScheme, key.Scheme) + assert.Equal(t, RSAKeyType, key.KeyType) + }) + + t.Run("RSA public key", func(t *testing.T) { + key, err := LoadKey(rsaPublicKey) + assert.Nil(t, err) + assert.Equal(t, expectedRSAKeyID, key.KeyID) + assert.Equal(t, expectedRSAPublicKey, key.KeyVal.Public) + assert.Equal(t, "", key.KeyVal.Private) + assert.Equal(t, RSAKeyScheme, key.Scheme) + assert.Equal(t, RSAKeyType, key.KeyType) + }) + + t.Run("ED25519 private key", func(t *testing.T) { + key, err := LoadKey(ed25519PrivateKey) + assert.Nil(t, err) + assert.Equal(t, expectedED25519KeyID, key.KeyID) + assert.Equal(t, expectedED25519PublicKey, key.KeyVal.Public) + assert.Equal(t, expectedED25519PrivateKey, key.KeyVal.Private) + assert.Equal(t, ED25519KeyType, key.Scheme) + assert.Equal(t, ED25519KeyType, key.KeyType) + }) + + t.Run("ED25519 public key", func(t *testing.T) { + key, err := LoadKey(ed25519PublicKey) + assert.Nil(t, err) + assert.Equal(t, expectedED25519KeyID, key.KeyID) + assert.Equal(t, expectedED25519PublicKey, key.KeyVal.Public) + assert.Equal(t, "", key.KeyVal.Private) + assert.Equal(t, ED25519KeyType, key.Scheme) + assert.Equal(t, ED25519KeyType, key.KeyType) + }) + + t.Run("ECDSA private key", func(t *testing.T) { + key, err := LoadKey(ecdsaPrivateKey) + assert.Nil(t, err) + assert.Equal(t, expectedECDSAKeyID, key.KeyID) + assert.Equal(t, expectedECDSAPublicKey, key.KeyVal.Public) + assert.Equal(t, expectedECDSAPrivateKey, key.KeyVal.Private) + assert.Equal(t, ECDSAKeyScheme, key.Scheme) + assert.Equal(t, ECDSAKeyType, key.KeyType) + }) + + t.Run("ECDSA public key", func(t *testing.T) { + key, err := LoadKey(ecdsaPublicKey) + assert.Nil(t, err) + assert.Equal(t, expectedECDSAKeyID, key.KeyID) + assert.Equal(t, expectedECDSAPublicKey, key.KeyVal.Public) + assert.Equal(t, "", key.KeyVal.Private) + assert.Equal(t, ECDSAKeyScheme, key.Scheme) + assert.Equal(t, ECDSAKeyType, key.KeyType) + }) +} diff --git a/signerverifier/test-data/ecdsa-test-key-pem b/signerverifier/test-data/ecdsa-test-key-pem new file mode 100644 index 0000000..9a9dc36 --- /dev/null +++ b/signerverifier/test-data/ecdsa-test-key-pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgCjoPFeWCpjL5OS+h +wg7JaWoDcpW2np2VJjuVLeSR4QyhRANCAAS74cSqqlctrjyVcf2uRHKCx+wIqrVc +zfqhfKYn3DGkzrycKqStkJWdn2WQR4LAPypZhM3EPnJ9ZfAmMWH4ruot +-----END PRIVATE KEY----- diff --git a/signerverifier/test-data/ecdsa-test-key-pem.pub b/signerverifier/test-data/ecdsa-test-key-pem.pub new file mode 100644 index 0000000..fa97dc5 --- /dev/null +++ b/signerverifier/test-data/ecdsa-test-key-pem.pub @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEu+HEqqpXLa48lXH9rkRygsfsCKq1 +XM36oXymJ9wxpM68nCqkrZCVnZ9lkEeCwD8qWYTNxD5yfWXwJjFh+K7qLQ== +-----END PUBLIC KEY----- diff --git a/signerverifier/test-data/ed25519-test-key-pem b/signerverifier/test-data/ed25519-test-key-pem new file mode 100644 index 0000000..019197d --- /dev/null +++ b/signerverifier/test-data/ed25519-test-key-pem @@ -0,0 +1,3 @@ +-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIGb2661K65SbkchMnP1u41H8T9VEdEurbjD7QAuhPG6a +-----END PRIVATE KEY----- diff --git a/signerverifier/test-data/ed25519-test-key-pem.pub b/signerverifier/test-data/ed25519-test-key-pem.pub new file mode 100644 index 0000000..fc74cbc --- /dev/null +++ b/signerverifier/test-data/ed25519-test-key-pem.pub @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEAP1hs5nMpQZ+wCBvZlZFOhmpyBdpGPVk7O0kOqysn/T8= +-----END PUBLIC KEY----- diff --git a/signerverifier/utils.go b/signerverifier/utils.go index e77e07f..e8a30b5 100644 --- a/signerverifier/utils.go +++ b/signerverifier/utils.go @@ -27,6 +27,9 @@ var ( // LoadKeyFromSSLibBytes returns a pointer to a Key instance created from the // contents of the bytes. The key contents are expected to be in the custom // securesystemslib format. +// +// Deprecated: use LoadKey() for all key types, RSA is no longer the only key +// that uses PEM serialization. func LoadKeyFromSSLibBytes(contents []byte) (*SSLibKey, error) { var key *SSLibKey if err := json.Unmarshal(contents, &key); err != nil {