From fe60d1b8c9784a8767a237998eb87984c7b01a09 Mon Sep 17 00:00:00 2001 From: neilnaveen <42328488+neilnaveen@users.noreply.github.com> Date: Tue, 7 Nov 2023 13:02:41 -0600 Subject: [PATCH 1/5] Changed LoadKeyFromSSLibBytes to Support RSA Keys - Changed LoadKeyFromSSLibBytes to support RSA keys - Seperated LoadRSAPSSKeyFromFile into helper function LoadRSAPSSKeyFromBytes so that data can be passed as a byte array or as a file name. - Made LoadRSAPSSKeyFromBytes into an exported function to use in https://github.com/gittuf/gittuf/pull/174 - Added test for RSA key to SSLibKey for LoadKeyFromSSLibbytes Signed-off-by: neilnaveen <42328488+neilnaveen@users.noreply.github.com> --- signerverifier/ecdsa.go | 2 +- signerverifier/ed25519.go | 2 +- signerverifier/rsa.go | 50 ++++++++++++++++++++++++++---------- signerverifier/utils.go | 9 +++---- signerverifier/utils_test.go | 39 ++++++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 21 deletions(-) create mode 100644 signerverifier/utils_test.go diff --git a/signerverifier/ecdsa.go b/signerverifier/ecdsa.go index 578d6a5..f3e6c20 100644 --- a/signerverifier/ecdsa.go +++ b/signerverifier/ecdsa.go @@ -95,7 +95,7 @@ func LoadECDSAKeyFromFile(path string) (*SSLibKey, error) { return nil, fmt.Errorf("unable to load ECDSA key from file: %w", err) } - return loadKeyFromSSLibBytes(contents) + return LoadKeyFromSSLibBytes(contents) } func getECDSAHashedData(data []byte, curveSize int) []byte { diff --git a/signerverifier/ed25519.go b/signerverifier/ed25519.go index c71d313..0a2210c 100644 --- a/signerverifier/ed25519.go +++ b/signerverifier/ed25519.go @@ -94,5 +94,5 @@ func LoadED25519KeyFromFile(path string) (*SSLibKey, error) { return nil, fmt.Errorf("unable to load ED25519 key from file: %w", err) } - return loadKeyFromSSLibBytes(contents) + return LoadKeyFromSSLibBytes(contents) } diff --git a/signerverifier/rsa.go b/signerverifier/rsa.go index 3612f28..7da11d1 100644 --- a/signerverifier/rsa.go +++ b/signerverifier/rsa.go @@ -99,7 +99,12 @@ func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) { if err != nil { return nil, fmt.Errorf("unable to load RSA key from file: %w", err) } + return LoadRSAPSSKeyFromBytes(contents) +} +// LoadRSAPSSKeyFromFile returns an SSLibKey instance for an RSA key stored in a +// byte array. +func LoadRSAPSSKeyFromBytes(contents []byte) (*SSLibKey, error) { pemData, keyObj, err := decodeAndParsePEM(contents) if err != nil { return nil, fmt.Errorf("unable to load RSA key from file: %w", err) @@ -113,21 +118,20 @@ func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) { } switch k := keyObj.(type) { - case *rsa.PublicKey: - pubKeyBytes, err := x509.MarshalPKIXPublicKey(k) - if err != nil { - return nil, fmt.Errorf("unable to load RSA key from file: %w", err) - } - key.KeyVal.Public = strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))) - - case *rsa.PrivateKey: - pubKeyBytes, err := x509.MarshalPKIXPublicKey(k.Public()) - if err != nil { - return nil, fmt.Errorf("unable to load RSA key from file: %w", err) - } - key.KeyVal.Public = strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))) - key.KeyVal.Private = strings.TrimSpace(string(generatePEMBlock(pemData.Bytes, RSAPrivateKeyPEM))) + case *rsa.PublicKey, *rsa.PrivateKey: + pubKeyBytes, err := marshalAndGeneratePEM(k) + if err != nil { + return nil, fmt.Errorf("unable to load RSA key from file: %w", err) + } + key.KeyVal.Public = strings.TrimSpace(string(pubKeyBytes)) + + if _, ok := k.(*rsa.PrivateKey); ok { + key.KeyVal.Private = strings.TrimSpace(string(generatePEMBlock(pemData.Bytes, RSAPrivateKeyPEM))) + } + default : + return nil, fmt.Errorf("unexpected key type: %T", k) } + if len(key.KeyID) == 0 { keyID, err := calculateKeyID(key) @@ -139,3 +143,21 @@ func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) { return key, nil } + +func marshalAndGeneratePEM(key interface{}) ([]byte, error) { + var pubKeyBytes []byte + var err error + + switch k := key.(type) { + case *rsa.PublicKey: + pubKeyBytes, err = x509.MarshalPKIXPublicKey(k) + case *rsa.PrivateKey: + pubKeyBytes, err = x509.MarshalPKIXPublicKey(k.Public()) + } + + if err != nil { + return nil, err + } + + return generatePEMBlock(pubKeyBytes, PublicKeyPEM), nil +} diff --git a/signerverifier/utils.go b/signerverifier/utils.go index 73aaa77..ed1a204 100644 --- a/signerverifier/utils.go +++ b/signerverifier/utils.go @@ -24,15 +24,14 @@ var ( ErrFailedPEMParsing = errors.New("failed parsing the PEM block: unsupported PEM type") ) -// loadKeyFromSSLibBytes returns a pointer to a Key instance created from the +// 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. -func loadKeyFromSSLibBytes(contents []byte) (*SSLibKey, error) { +func LoadKeyFromSSLibBytes(contents []byte) (*SSLibKey, error) { var key *SSLibKey if err := json.Unmarshal(contents, &key); err != nil { - return nil, err - } - + return LoadRSAPSSKeyFromBytes(contents) + } if len(key.KeyID) == 0 { keyID, err := calculateKeyID(key) if err != nil { diff --git a/signerverifier/utils_test.go b/signerverifier/utils_test.go new file mode 100644 index 0000000..60bc045 --- /dev/null +++ b/signerverifier/utils_test.go @@ -0,0 +1,39 @@ +package signerverifier + +import ( + "path/filepath" + "testing" + "os" + "github.com/stretchr/testify/assert" +) +func TestLoadKeyFromSSLibBytes(t *testing.T) { + t.Run("RSA public key", func(t *testing.T) { + contents, err := os.ReadFile(filepath.Join("test-data", "rsa-test-key.pub")) + if err != nil { + t.Fatalf("bad test") + } + key, err := LoadKeyFromSSLibBytes(contents) + assert.Nil(t, err) + + assert.Equal(t, "4e8d20af09fcaed6c388a186427f94a5f7ff5591ec295f4aab2cff49ffe39e9b", key.KeyID) + assert.Equal(t, "-----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-----", key.KeyVal.Public) + assert.Equal(t, RSAKeyScheme, key.Scheme) + assert.Equal(t, RSAKeyType, key.KeyType) + }) + + t.Run("RSA private key", func(t *testing.T) { + contents, err := os.ReadFile(filepath.Join("test-data", "rsa-test-key")) + if err != nil { + t.Fatalf("bad test") + } + key, err := LoadKeyFromSSLibBytes(contents) + assert.Nil(t, err) + + assert.Equal(t, "4e8d20af09fcaed6c388a186427f94a5f7ff5591ec295f4aab2cff49ffe39e9b", key.KeyID) + assert.Equal(t, "-----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-----", key.KeyVal.Public) + expectedPrivateKey := "-----BEGIN RSA PRIVATE KEY-----\n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\n-----END RSA PRIVATE KEY-----" + assert.Equal(t, expectedPrivateKey, key.KeyVal.Private) + assert.Equal(t, RSAKeyScheme, key.Scheme) + assert.Equal(t, RSAKeyType, key.KeyType) + }) +} \ No newline at end of file From 49cfbdebb75f702acbf8b412f6f75b867db6e733 Mon Sep 17 00:00:00 2001 From: neilnaveen <42328488+neilnaveen@users.noreply.github.com> Date: Thu, 9 Nov 2023 10:31:02 -0600 Subject: [PATCH 2/5] Pulled switch case to helper function Signed-off-by: neilnaveen <42328488+neilnaveen@users.noreply.github.com> --- signerverifier/rsa.go | 24 ++++++++++-------------- signerverifier/utils_test.go | 10 ++++++---- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/signerverifier/rsa.go b/signerverifier/rsa.go index 7da11d1..2df9fda 100644 --- a/signerverifier/rsa.go +++ b/signerverifier/rsa.go @@ -117,21 +117,15 @@ func LoadRSAPSSKeyFromBytes(contents []byte) (*SSLibKey, error) { KeyVal: KeyVal{}, } - switch k := keyObj.(type) { - case *rsa.PublicKey, *rsa.PrivateKey: - pubKeyBytes, err := marshalAndGeneratePEM(k) - if err != nil { - return nil, fmt.Errorf("unable to load RSA key from file: %w", err) - } - key.KeyVal.Public = strings.TrimSpace(string(pubKeyBytes)) - - if _, ok := k.(*rsa.PrivateKey); ok { - key.KeyVal.Private = strings.TrimSpace(string(generatePEMBlock(pemData.Bytes, RSAPrivateKeyPEM))) - } - default : - return nil, fmt.Errorf("unexpected key type: %T", k) + pubKeyBytes, err := marshalAndGeneratePEM(keyObj) + if err != nil { + return nil, fmt.Errorf("unable to load RSA key from file: %w", err) + } + key.KeyVal.Public = strings.TrimSpace(string(pubKeyBytes)) + + if _, ok := keyObj.(*rsa.PrivateKey); ok { + key.KeyVal.Private = strings.TrimSpace(string(generatePEMBlock(pemData.Bytes, RSAPrivateKeyPEM))) } - if len(key.KeyID) == 0 { keyID, err := calculateKeyID(key) @@ -153,6 +147,8 @@ func marshalAndGeneratePEM(key interface{}) ([]byte, error) { pubKeyBytes, err = x509.MarshalPKIXPublicKey(k) case *rsa.PrivateKey: pubKeyBytes, err = x509.MarshalPKIXPublicKey(k.Public()) + default: + return nil, fmt.Errorf("unexpected key type: %T", k) } if err != nil { diff --git a/signerverifier/utils_test.go b/signerverifier/utils_test.go index 60bc045..ccb22c2 100644 --- a/signerverifier/utils_test.go +++ b/signerverifier/utils_test.go @@ -1,16 +1,18 @@ package signerverifier import ( + "os" "path/filepath" "testing" - "os" + "github.com/stretchr/testify/assert" ) + func TestLoadKeyFromSSLibBytes(t *testing.T) { t.Run("RSA public key", func(t *testing.T) { contents, err := os.ReadFile(filepath.Join("test-data", "rsa-test-key.pub")) if err != nil { - t.Fatalf("bad test") + t.Fatal(err) } key, err := LoadKeyFromSSLibBytes(contents) assert.Nil(t, err) @@ -24,7 +26,7 @@ func TestLoadKeyFromSSLibBytes(t *testing.T) { t.Run("RSA private key", func(t *testing.T) { contents, err := os.ReadFile(filepath.Join("test-data", "rsa-test-key")) if err != nil { - t.Fatalf("bad test") + t.Fatal(err) } key, err := LoadKeyFromSSLibBytes(contents) assert.Nil(t, err) @@ -36,4 +38,4 @@ func TestLoadKeyFromSSLibBytes(t *testing.T) { assert.Equal(t, RSAKeyScheme, key.Scheme) assert.Equal(t, RSAKeyType, key.KeyType) }) -} \ No newline at end of file +} From d3e2d4165d9e6ad095cc81b38216015ae47113f8 Mon Sep 17 00:00:00 2001 From: neilnaveen <42328488+neilnaveen@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:25:49 -0500 Subject: [PATCH 3/5] Expanded on LoadRSAPSSKeyFromBytes definition Signed-off-by: neilnaveen <42328488+neilnaveen@users.noreply.github.com> --- signerverifier/rsa.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/signerverifier/rsa.go b/signerverifier/rsa.go index 2df9fda..09d454b 100644 --- a/signerverifier/rsa.go +++ b/signerverifier/rsa.go @@ -99,11 +99,13 @@ func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) { if err != nil { return nil, fmt.Errorf("unable to load RSA key from file: %w", err) } + return LoadRSAPSSKeyFromBytes(contents) } -// LoadRSAPSSKeyFromFile returns an SSLibKey instance for an RSA key stored in a -// byte array. +// LoadRSAPSSKeyFromBytes is a function that takes a byte array as input, which represents an RSA key. +// It returns an SSLibKey instance, which is a struct that holds the key data. + func LoadRSAPSSKeyFromBytes(contents []byte) (*SSLibKey, error) { pemData, keyObj, err := decodeAndParsePEM(contents) if err != nil { From b2171bfc024b6a78a89afe0ab6bebc15bf7dbdeb Mon Sep 17 00:00:00 2001 From: neilnaveen <42328488+neilnaveen@users.noreply.github.com> Date: Sun, 19 Nov 2023 20:07:39 -0500 Subject: [PATCH 4/5] added more to the defenition of load rsa key from bytes Signed-off-by: neilnaveen <42328488+neilnaveen@users.noreply.github.com> --- signerverifier/rsa.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/signerverifier/rsa.go b/signerverifier/rsa.go index 09d454b..b039659 100644 --- a/signerverifier/rsa.go +++ b/signerverifier/rsa.go @@ -103,8 +103,8 @@ func LoadRSAPSSKeyFromFile(path string) (*SSLibKey, error) { return LoadRSAPSSKeyFromBytes(contents) } -// LoadRSAPSSKeyFromBytes is a function that takes a byte array as input, which represents an RSA key. -// It 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. func LoadRSAPSSKeyFromBytes(contents []byte) (*SSLibKey, error) { pemData, keyObj, err := decodeAndParsePEM(contents) From 41c3b9bb58a3ca9418f7ec6b7659ffac4ece6275 Mon Sep 17 00:00:00 2001 From: neilnaveen <42328488+neilnaveen@users.noreply.github.com> Date: Mon, 20 Nov 2023 10:33:03 -0500 Subject: [PATCH 5/5] fixed fmt error Signed-off-by: neilnaveen <42328488+neilnaveen@users.noreply.github.com> --- signerverifier/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/signerverifier/utils.go b/signerverifier/utils.go index ed1a204..e77e07f 100644 --- a/signerverifier/utils.go +++ b/signerverifier/utils.go @@ -31,7 +31,7 @@ func LoadKeyFromSSLibBytes(contents []byte) (*SSLibKey, error) { var key *SSLibKey if err := json.Unmarshal(contents, &key); err != nil { return LoadRSAPSSKeyFromBytes(contents) - } + } if len(key.KeyID) == 0 { keyID, err := calculateKeyID(key) if err != nil {