Skip to content

Commit

Permalink
Add ssh support to all keys
Browse files Browse the repository at this point in the history
  • Loading branch information
davidlesfrog committed Aug 27, 2024
1 parent a14290f commit 3097402
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 20 deletions.
2 changes: 1 addition & 1 deletion evidence/cryptox/readkey_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
func TestReadKey(t *testing.T) {
files, err := os.ReadDir("testdata")
assert.NoError(t, err)
assert.Equal(t, 14, len(files))
assert.Equal(t, 20, len(files))
var keyFiles []os.DirEntry
keysToValidate := []string{"ecdsa-test-key-pem", "ed25519-test-key-pem", "rsa-test-key"}
for _, file := range files {
Expand Down
33 changes: 24 additions & 9 deletions evidence/cryptox/signerverifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ var (
)

const (
PublicKeyPEM = "PUBLIC KEY"
PrivateKeyPEM = "PRIVATE KEY"
PublicKeyPEM = "PUBLIC KEY"
)

type SSLibKey struct {
Expand Down Expand Up @@ -83,28 +82,44 @@ func LoadKey(keyBytes []byte) (*SSLibKey, error) {
Scheme: RSAKeyScheme,
}

case ed25519.PublicKey:
case ed25519.PublicKey, *ed25519.PublicKey:
var pubKey ed25519.PublicKey
if pk, ok := k.(ed25519.PublicKey); ok {
pubKey = pk
} else if pk, ok := k.(*ed25519.PublicKey); ok {
pubKey = *pk
} else {
return nil, errorutils.CheckError(fmt.Errorf("couldn't convert to ed25519 public key"))
}
key = &SSLibKey{
KeyIDHashAlgorithms: KeyIDHashAlgorithms,
KeyType: ED25519KeyType,
KeyVal: KeyVal{
Public: strings.TrimSpace(hex.EncodeToString(k)),
Public: strings.TrimSpace(hex.EncodeToString(pubKey)),
},
Scheme: ED25519KeyType,
}

case ed25519.PrivateKey:
pubKeyBytes := k.Public()
case ed25519.PrivateKey, *ed25519.PrivateKey:
var privateKey ed25519.PrivateKey
if pk, ok := k.(ed25519.PrivateKey); ok {
privateKey = pk
} else if pk, ok := k.(*ed25519.PrivateKey); ok {
privateKey = *pk
} else {
return nil, errorutils.CheckError(fmt.Errorf("couldn't convert to ed25519 private key"))
}
pubKeyBytes := privateKey.Public()
pukBytes, ok := pubKeyBytes.(ed25519.PublicKey)
if !ok {
return nil, errorutils.CheckError(fmt.Errorf("couldnt convert to ecdsa public key bytes"))
return nil, errorutils.CheckError(fmt.Errorf("couldn't convert to ed25519 public key bytes"))
}
key = &SSLibKey{
KeyIDHashAlgorithms: KeyIDHashAlgorithms,
KeyType: ED25519KeyType,
KeyVal: KeyVal{
Public: strings.TrimSpace(hex.EncodeToString(pukBytes)),
Private: strings.TrimSpace(hex.EncodeToString(k)),
Private: strings.TrimSpace(hex.EncodeToString(privateKey)),
},
Scheme: ED25519KeyType,
}
Expand Down Expand Up @@ -133,7 +148,7 @@ func LoadKey(keyBytes []byte) (*SSLibKey, error) {
KeyType: ECDSAKeyType,
KeyVal: KeyVal{
Public: strings.TrimSpace(string(generatePEMBlock(pubKeyBytes, PublicKeyPEM))),
Private: strings.TrimSpace(string(generatePEMBlock(pemBlock.Bytes, PrivateKeyPEM))),
Private: strings.TrimSpace(string(generatePEMBlock(pemBlock.Bytes, pemBlock.Type))),
},
Scheme: ECDSAKeyScheme,
}
Expand Down
48 changes: 46 additions & 2 deletions evidence/cryptox/signerverifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ var ecdsaPrivateKey []byte
//go:embed testdata/ecdsa-test-key-pem.pub
var ecdsaPublicKey []byte

//go:embed testdata/ssh-rsa-2048
var sshRSAPrivateKey []byte

//go:embed testdata/ssh-ecdsa-256
var sshEcdsaPrivateKey []byte

//go:embed testdata/ssh-ed25519-256
var sshEd25519PrivateKey []byte

func TestLoadKey(t *testing.T) {
// RSA expected values
expectedRSAPrivateKey := strings.TrimSpace(strings.ReplaceAll(string(rsaPrivateKey), "\r\n", "\n"))
Expand All @@ -46,12 +55,18 @@ func TestLoadKey(t *testing.T) {
expectedECDSAPrivateKey := strings.TrimSpace(strings.ReplaceAll(string(ecdsaPrivateKey), "\r\n", "\n"))
expectedECDSAPublicKey := strings.TrimSpace(strings.ReplaceAll(string(ecdsaPublicKey), "\r\n", "\n"))

// SSH private key expected values
expectedSSHECDSAPrivateKey := strings.TrimSpace(strings.ReplaceAll(strings.ReplaceAll(string(sshEcdsaPrivateKey), "\r\n", "\n"), "\n", ""))
expectedSSHED25519PrivateKey := "14d62acc5eabc0a430bb5eedfef691e0a8f57e03a7e618c7c980f89452ea231535f2ba08016fc052241cdf87ea0d632f777e99cb562bdf199317d06eb98781f2"
expectedSSHRSAPrivateKey := strings.TrimSpace(strings.ReplaceAll(strings.ReplaceAll(string(sshRSAPrivateKey), "\r\n", "\n"), "\n", ""))

tests := map[string]struct {
keyBytes []byte
expectedPrivateKey string
expectedPublicKey string
expectedKeyType string
expectedScheme string
removeNewLines bool
}{
"RSA private key": {
keyBytes: rsaPrivateKey,
Expand Down Expand Up @@ -102,14 +117,43 @@ func TestLoadKey(t *testing.T) {
expectedKeyType: ECDSAKeyType,
expectedScheme: ECDSAKeyScheme,
},
"SSH ECDSA private key": {
keyBytes: sshEcdsaPrivateKey,
expectedPrivateKey: expectedSSHECDSAPrivateKey,
expectedPublicKey: "",
expectedKeyType: ECDSAKeyType,
expectedScheme: ECDSAKeyScheme,
removeNewLines: true,
},
"SSH ED25519 private key": {
keyBytes: sshEd25519PrivateKey,
expectedPrivateKey: expectedSSHED25519PrivateKey,
expectedPublicKey: "",
expectedKeyType: ED25519KeyType,
expectedScheme: ED25519KeyType,
},
"SSH RSA private key": {
keyBytes: sshRSAPrivateKey,
expectedPrivateKey: expectedSSHRSAPrivateKey,
expectedPublicKey: "",
expectedKeyType: RSAKeyType,
expectedScheme: RSAKeyScheme,
removeNewLines: true,
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
key, err := LoadKey(test.keyBytes)
assert.Nil(t, err, fmt.Sprintf("unexpected error in test '%s'", name))
assert.Equal(t, test.expectedPublicKey, key.KeyVal.Public)
assert.Equal(t, test.expectedPrivateKey, key.KeyVal.Private)
if test.expectedPublicKey != "" {
assert.Equal(t, test.expectedPublicKey, key.KeyVal.Public)
}
if test.removeNewLines {
assert.Equal(t, test.expectedPrivateKey, strings.ReplaceAll(key.KeyVal.Private, "\n", ""))
} else {
assert.Equal(t, test.expectedPrivateKey, key.KeyVal.Private)
}
assert.Equal(t, test.expectedScheme, key.Scheme)
assert.Equal(t, test.expectedKeyType, key.KeyType)
})
Expand Down
8 changes: 8 additions & 0 deletions evidence/cryptox/testdata/ssh-ecdsa-256
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTwoRBQKS7njRr13ZdVZ/dR8cqOI/4P
sxNBx9iWwLSF5O7BRmdp5WJxMnChj/OSWG2WHrygboWH2mwrQ9EfpoUpAAAAoJpJS22aSU
ttAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPChEFApLueNGvXd
l1Vn91Hxyo4j/g+zE0HH2JbAtIXk7sFGZ2nlYnEycKGP85JYbZYevKBuhYfabCtD0R+mhS
kAAAAhANdwC5Wgr7gg2B8pVudQ8cpncq6/OayiWRMptHZzpHdtAAAABm5vbmFtZQE=
-----END OPENSSH PRIVATE KEY-----
1 change: 1 addition & 0 deletions evidence/cryptox/testdata/ssh-ecdsa-256.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPChEFApLueNGvXdl1Vn91Hxyo4j/g+zE0HH2JbAtIXk7sFGZ2nlYnEycKGP85JYbZYevKBuhYfabCtD0R+mhSk= noname
7 changes: 7 additions & 0 deletions evidence/cryptox/testdata/ssh-ed25519-256
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACA18roIAW/AUiQc34fqDWMvd36Zy1Yr3xmTF9BuuYeB8gAAAJBQsmCkULJg
pAAAAAtzc2gtZWQyNTUxOQAAACA18roIAW/AUiQc34fqDWMvd36Zy1Yr3xmTF9BuuYeB8g
AAAEAU1irMXqvApDC7Xu3+9pHgqPV+A6fmGMfJgPiUUuojFTXyuggBb8BSJBzfh+oNYy93
fpnLVivfGZMX0G65h4HyAAAABm5vbmFtZQECAwQFBgc=
-----END OPENSSH PRIVATE KEY-----
1 change: 1 addition & 0 deletions evidence/cryptox/testdata/ssh-ed25519-256.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDXyuggBb8BSJBzfh+oNYy93fpnLVivfGZMX0G65h4Hy noname
27 changes: 27 additions & 0 deletions evidence/cryptox/testdata/ssh-rsa-2048
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
NhAAAAAwEAAQAAAQEA6DxJk3YWBsZ3ZRBSKKBQqE4ZziHuTOqZe8T4eL+EBAS2XBZkg85h
Tqph/7+G9l4l41kzT9SyYu7atNRwa6aIP2PPy0bKUAmv4A9682pmCAvZspx45gJx9r8rs1
XTBwEXyKOJ891lk8zXiD6yYAFlXgvLfz+q9SjSoTfOvlraRFuEflnM3KTQnBB2FtlC3TUR
+SdC3gWA4RFSG17++j0BGNp2WsplWYv9uPCdDTn/3ntC2AjvUPg1j9QNIdzdQO/dg2QnOn
qakaZ0P02kShDaBDKWhSrAMHN1b/LYkzojVNn0LFTfl+75ScpzvBqgcRKs/j8Lmz6tAIWd
s4Cnl7i0kQAAA8ARXgTcEV4E3AAAAAdzc2gtcnNhAAABAQDoPEmTdhYGxndlEFIooFCoTh
nOIe5M6pl7xPh4v4QEBLZcFmSDzmFOqmH/v4b2XiXjWTNP1LJi7tq01HBrpog/Y8/LRspQ
Ca/gD3rzamYIC9mynHjmAnH2vyuzVdMHARfIo4nz3WWTzNeIPrJgAWVeC8t/P6r1KNKhN8
6+WtpEW4R+WczcpNCcEHYW2ULdNRH5J0LeBYDhEVIbXv76PQEY2nZaymVZi/248J0NOf/e
e0LYCO9Q+DWP1A0h3N1A792DZCc6epqRpnQ/TaRKENoEMpaFKsAwc3Vv8tiTOiNU2fQsVN
+X7vlJynO8GqBxEqz+PwubPq0AhZ2zgKeXuLSRAAAAAwEAAQAAAQA9T51FFwndpAvlXJe0
Lg7dQDFD4GVfXRhEOs42YqlhvjS+vxKuUZATCpxwvonNBTIPgX8wdkDaKaEH2IYNhZu20I
0NZ5UF6GjMSIn3NYHWYfqJUxH/92lK8VGCOQhLcxhqWfKTtUGZ8Zx9VcAV5Ih0ebyxzYc+
zhY9hdRJIQc6wEXNd51e3wOq8eFuTkrDaFWg7Ya72IBpEfRwtx/0XBURPJBFDMs78k4p7w
xhpb8adH/171YrcCqVjE2pKOaHJv00y0SLRPEM7sVTU/NTuplV5Wd4yBxsFtC2XnsiHKVk
nt3MstoUInmaTTn3VgLJfSjFnhI6AzgrA8Zz4g9finOhAAAAgQDMNlf3AIAj+xYi9z5HAt
A9tWLA5PtKvJggGRknQzvz9BvK70LfyUEZl9Z7w5Zn4/PMzju6NM143Xruu0NisQfmpI6/
1IA6eLnvuZpKYwttSwTphrzghn6zki4Q0tejoaXRhEVxnIZg371Jl2TwW5xNO14Kfu5Vrd
cNiN2txmj+FwAAAIEA9LhrWvzPtkPrA4igtAYWyqicoaSHilVTUQb6/c1RpmmQdTIufUqK
I1IL4lntkQvB4HD80buu1kXk/vJFhWwqlu7JEoCKfAsAU/8p/jaRUCCZyQow8u7KL6l/Zi
+l7aW1YacwFQjYlitjdivPiF7ntmg4gEMwHMcYopigTKWVav0AAACBAPLwjW4UUqdSLOYr
JrK9YvnLa3l2WCucTuxa+VDQpq4Glz8wZ9IIADHA+6iM21JDkGxHoY2qf8MMKYpzUaEKdS
KouKcy1CZty1lNaDuypRM3zf0e5EzaoiEIuCMadeKhYPabRNPszAbHxj42uAUrSszhJaXT
2SWwl+Wn53BRMJYlAAAABm5vbmFtZQECAwQ=
-----END OPENSSH PRIVATE KEY-----
1 change: 1 addition & 0 deletions evidence/cryptox/testdata/ssh-rsa-2048.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDoPEmTdhYGxndlEFIooFCoThnOIe5M6pl7xPh4v4QEBLZcFmSDzmFOqmH/v4b2XiXjWTNP1LJi7tq01HBrpog/Y8/LRspQCa/gD3rzamYIC9mynHjmAnH2vyuzVdMHARfIo4nz3WWTzNeIPrJgAWVeC8t/P6r1KNKhN86+WtpEW4R+WczcpNCcEHYW2ULdNRH5J0LeBYDhEVIbXv76PQEY2nZaymVZi/248J0NOf/ee0LYCO9Q+DWP1A0h3N1A792DZCc6epqRpnQ/TaRKENoEMpaFKsAwc3Vv8tiTOiNU2fQsVN+X7vlJynO8GqBxEqz+PwubPq0AhZ2zgKeXuLSR noname
40 changes: 34 additions & 6 deletions evidence/cryptox/utils.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package cryptox

import (
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"errors"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"golang.org/x/crypto/ssh"
"hash"
"testing"

Expand Down Expand Up @@ -67,30 +71,48 @@ because LoadKey relies on decoded pemData for operating system
interoperability.
*/
func decodeAndParsePEM(pemBytes []byte) (*pem.Block, any, error) {
// pem.Decode returns the parsed pem block and a rest.
// The rest is everything, that could not be parsed as PEM block.
// Therefore we can drop this via using the blank identifier "_"
// Attempt to decode PEM block
data, _ := pem.Decode(pemBytes)
if data == nil {
return nil, nil, errorutils.CheckError(ErrNoPEMBlock)
}

// Try to load private key, if this fails try to load
// key as public key
if data.Type == "OPENSSH PRIVATE KEY" {
key, err := parseSSHKey(pemBytes)
if err != nil {
return nil, nil, errorutils.CheckError(ErrNoPEMBlock)
}
return data, key, nil
}
// Try to load private key, if this fails try to load key as public key
key, err := parsePEMKey(data.Bytes)
if err != nil {
return nil, nil, err
return nil, nil, errorutils.CheckError(err)
}
return data, key, nil
}

func parseSSHKey(keyBytes []byte) (any, error) {
key, err := ssh.ParseRawPrivateKey(keyBytes)
if err != nil {
return nil, errorutils.CheckError(ErrNoPEMBlock)
}
switch k := key.(type) {
case *ecdsa.PrivateKey, *ed25519.PrivateKey, *rsa.PrivateKey:
return k, nil
}
return nil, errorutils.CheckErrorf("PEM parsing failed %w", ErrFailedPEMParsing)
}

/*
parseKey tries to parse a PEM []byte slice. Using the following standards
in the given order:
- PKCS8
- PKCS1
- PKIX
- ECDSA
- SSH
On success it returns the parsed key and nil.
On failure it returns nil and the error ErrFailedPEMParsing
Expand All @@ -112,6 +134,12 @@ func parsePEMKey(data []byte) (any, error) {
if err == nil {
return key, nil
}

key, err = parseSSHKey(data)
if err == nil {
return key, nil
}

return nil, errorutils.CheckErrorf("PEM parsing failed %w", ErrFailedPEMParsing)
}

Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ require (
github.com/stretchr/testify v1.9.0
github.com/urfave/cli v1.22.15
go.uber.org/mock v0.4.0
golang.org/x/crypto v0.26.0
)

require (
Expand Down Expand Up @@ -81,7 +82,6 @@ require (
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.26.0 // indirect
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.28.0 // indirect
Expand All @@ -97,4 +97,4 @@ require (

replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240811150357-12a9330a2d67

replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240811142930-ab9715567376
replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240811142930-ab9715567376

0 comments on commit 3097402

Please sign in to comment.