lang/funcs: Add support for OpenSSH RSA key format
Previously this function only supported the x509 RSA private key format. More recent versions of OpenSSH default to generating a new PEM key format, which this commit now supports using the x/crypto/ssh package. Also improve the returned error messages for various invalid ciphertext or invalid private key errors.
This commit is contained in:
parent
6fbd3942ea
commit
a4f5e04066
|
@ -6,12 +6,12 @@ import (
|
|||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"crypto/x509"
|
||||
"encoding/asn1"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"hash"
|
||||
"strings"
|
||||
|
||||
uuidv5 "github.com/google/uuid"
|
||||
uuid "github.com/hashicorp/go-uuid"
|
||||
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/zclconf/go-cty/cty/function"
|
||||
"github.com/zclconf/go-cty/cty/gocty"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
var UUIDFunc = function.New(&function.Spec{
|
||||
|
@ -152,27 +153,30 @@ var RsaDecryptFunc = function.New(&function.Spec{
|
|||
|
||||
b, err := base64.StdEncoding.DecodeString(s)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("failed to decode input %q: cipher text must be base64-encoded", s)
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(0, "failed to decode input %q: cipher text must be base64-encoded", s)
|
||||
}
|
||||
|
||||
block, _ := pem.Decode([]byte(key))
|
||||
if block == nil {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("failed to parse key: no key found")
|
||||
}
|
||||
if block.Headers["Proc-Type"] == "4,ENCRYPTED" {
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf(
|
||||
"failed to parse key: password protected keys are not supported. Please decrypt the key prior to use",
|
||||
)
|
||||
}
|
||||
|
||||
x509Key, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
rawKey, err := ssh.ParseRawPrivateKey([]byte(key))
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
var errStr string
|
||||
switch e := err.(type) {
|
||||
case asn1.SyntaxError:
|
||||
errStr = strings.ReplaceAll(e.Error(), "asn1: syntax error", "invalid ASN1 data in the given private key")
|
||||
case asn1.StructuralError:
|
||||
errStr = strings.ReplaceAll(e.Error(), "asn1: struture error", "invalid ASN1 data in the given private key")
|
||||
default:
|
||||
errStr = fmt.Sprintf("invalid private key: %s", e)
|
||||
}
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(1, errStr)
|
||||
}
|
||||
privateKey, ok := rawKey.(*rsa.PrivateKey)
|
||||
if !ok {
|
||||
return cty.UnknownVal(cty.String), function.NewArgErrorf(1, "invalid private key type %t", rawKey)
|
||||
}
|
||||
|
||||
out, err := rsa.DecryptPKCS1v15(nil, x509Key, b)
|
||||
out, err := rsa.DecryptPKCS1v15(nil, privateKey, b)
|
||||
if err != nil {
|
||||
return cty.UnknownVal(cty.String), err
|
||||
return cty.UnknownVal(cty.String), fmt.Errorf("failed to decrypt: %s", err)
|
||||
}
|
||||
|
||||
return cty.StringVal(string(out)), nil
|
||||
|
|
|
@ -370,58 +370,67 @@ func TestRsaDecrypt(t *testing.T) {
|
|||
Ciphertext cty.Value
|
||||
Privatekey cty.Value
|
||||
Want cty.Value
|
||||
Err bool
|
||||
Err string
|
||||
}{
|
||||
// Base-64 encoded cipher decrypts correctly
|
||||
{
|
||||
cty.StringVal(CipherBase64),
|
||||
cty.StringVal(PrivateKey),
|
||||
cty.StringVal("message"),
|
||||
false,
|
||||
"",
|
||||
},
|
||||
// OpenSSH key format
|
||||
{
|
||||
cty.StringVal(CipherBase64),
|
||||
cty.StringVal(OpenSSHPrivateKey),
|
||||
cty.StringVal("message"),
|
||||
"",
|
||||
},
|
||||
// Wrong key
|
||||
{
|
||||
cty.StringVal(CipherBase64),
|
||||
cty.StringVal(WrongPrivateKey),
|
||||
cty.UnknownVal(cty.String),
|
||||
true,
|
||||
"failed to decrypt: crypto/rsa: decryption error",
|
||||
},
|
||||
// Bad key
|
||||
{
|
||||
cty.StringVal(CipherBase64),
|
||||
cty.StringVal("bad key"),
|
||||
cty.StringVal(BadPrivateKey),
|
||||
cty.UnknownVal(cty.String),
|
||||
true,
|
||||
"invalid ASN1 data in the given private key: data truncated",
|
||||
},
|
||||
// Empty key
|
||||
{
|
||||
cty.StringVal(CipherBase64),
|
||||
cty.StringVal(""),
|
||||
cty.UnknownVal(cty.String),
|
||||
true,
|
||||
"invalid private key: ssh: no key found",
|
||||
},
|
||||
// Bad cipher
|
||||
// Bad ciphertext
|
||||
{
|
||||
cty.StringVal("bad cipher"),
|
||||
cty.StringVal("bad"),
|
||||
cty.StringVal(PrivateKey),
|
||||
cty.UnknownVal(cty.String),
|
||||
true,
|
||||
`failed to decode input "bad": cipher text must be base64-encoded`,
|
||||
},
|
||||
// Empty cipher
|
||||
// Empty ciphertext
|
||||
{
|
||||
cty.StringVal(""),
|
||||
cty.StringVal(PrivateKey),
|
||||
cty.UnknownVal(cty.String),
|
||||
true,
|
||||
"failed to decrypt: crypto/rsa: decryption error",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("RsaDecrypt(%#v, %#v)", test.Ciphertext, test.Privatekey), func(t *testing.T) {
|
||||
got, err := RsaDecrypt(test.Ciphertext, test.Privatekey)
|
||||
|
||||
if test.Err {
|
||||
if test.Err != "" {
|
||||
if err == nil {
|
||||
t.Fatal("succeeded; want error")
|
||||
} else if err.Error() != test.Err {
|
||||
t.Fatalf("wrong error\ngot: %s\nwant: %s", err.Error(), test.Err)
|
||||
}
|
||||
return
|
||||
} else if err != nil {
|
||||
|
@ -699,6 +708,35 @@ OLlRAoGBAIZ5Uv4Z3s8O7WKXXUe/lq6j7vfiVkR1NW/Z/WLKXZpnmvJ7FgxN4e56
|
|||
RXT7GwNQHIY8eDjDnsHxzrxd+raOxOZeKcMHj3XyjCX3NHfTscnsBPAGYpY/Wxzh
|
||||
T8UYnFu6RzkixElTf2rseEav7rkdKkI3LAeIZy7B0HulKKsmqVQ7
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
OpenSSHPrivateKey = `
|
||||
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn
|
||||
NhAAAAAwEAAQAAAQEAgUElV5mwqkloIrM8ZNZ72gSCcnSJt7+/Usa5G+D15YQUAdf9c1zE
|
||||
ekTfHgDP+04nw/uFNFaE5v1RbHaPxhZYVg5ZErNCa/hzn+x10xzcepeS3KPVXcxae4MR0B
|
||||
EegvqZqJzN9loXsNL/c3H/B+2Gle3hTxjlWFb3F5qLgR+4Mf4ruhER1v6eHQa/nchi03MB
|
||||
pT4UeJ7MrL92hTJYLdpSyCqmr8yjxkKJDVC2uRrr+sTSxfh7r6v24u/vp/QTmBIAlNPgad
|
||||
VAZw17iNNb7vjV7Gwl/5gHXonCUKURaV++dBNLrHIZpqcAM8wHRph8mD1EfL9hsz77pHew
|
||||
xolBATV+7QAAA7jbhEFk24RBZAAAAAdzc2gtcnNhAAABAQCBQSVXmbCqSWgiszxk1nvaBI
|
||||
JydIm3v79Sxrkb4PXlhBQB1/1zXMR6RN8eAM/7TifD+4U0VoTm/VFsdo/GFlhWDlkSs0Jr
|
||||
+HOf7HXTHNx6l5Lco9VdzFp7gxHQER6C+pmonM32Whew0v9zcf8H7YaV7eFPGOVYVvcXmo
|
||||
uBH7gx/iu6ERHW/p4dBr+dyGLTcwGlPhR4nsysv3aFMlgt2lLIKqavzKPGQokNULa5Guv6
|
||||
xNLF+Huvq/bi7++n9BOYEgCU0+Bp1UBnDXuI01vu+NXsbCX/mAdeicJQpRFpX750E0usch
|
||||
mmpwAzzAdGmHyYPUR8v2GzPvukd7DGiUEBNX7tAAAAAwEAAQAAAQAtayvpBVt76wGJt/vP
|
||||
30J0EMOZ3nOKOvnK54OiVUFy3h99ql0oTX/JCyxvyY9L2mHEzzw2cPSQipEzENJio/V0f+
|
||||
Qy2wTLFenjV17rySd8eIiluXg/VpCw+BSpTWqwUcju4/LHz06l1u7mrTcVnRR+2LEkbzYf
|
||||
/ackBy1gOTorbonTK2G3NxFMfAdRjzcifVvEPM5zWC38GDo1OFr9UixOqhkEB/UNFswNll
|
||||
H/I5JQmMjGEyMsAIxm/JGwCZSoZo9rdiII5qrcLdT2HKRpam7UAQ1Ill7eUuGF/9ZmiEP+
|
||||
PcnjVGo46WyYh9w24SWx8BU8z96WfT/Rhzs5RpGEfsEhAAAAgQCGeVL+Gd7PDu1il11Hv5
|
||||
auo+734lZEdTVv2f1iyl2aZ5ryexYMTeHuekV0+xsDUByGPHg4w57B8c68Xfq2jsTmXinD
|
||||
B4918owl9zR307HJ7ATwBmKWP1sc4U/FGJxbukc5IsRJU39q7HhGr+65HSpCNywHiGcuwd
|
||||
B7pSirJqlUOwAAAIEAw/Xida8kSv8n86V3qSY/I+fYQ5V+jDtXIE+JhRnS8xzbOzz3v0WS
|
||||
Oo5H+o4nJx5eL3Ghb3Gcm0Jn46dHrxinHbm+3RjXv/X6tlbxIYjRSQfHOTSMCTvdqcliF5
|
||||
vC6RCLXuc7R+IWR1Ky6eDEZGtrvt3DyeYABsp9fRUFR/6NluUAAACBAKjbMNWkpe1iWtux
|
||||
dA6CXZVPS1nq6V3Gs5SXCHTs/0vUA5kit0Q0E3an08UZq8YmCPSxLJpDpL85Z5zgTKZ2d2
|
||||
TlOaiEX6a3nESIt+ygwDh1hp5QtBFhoJeOmC2+/414ln9ABmPg3ySTXfYuk2yA1rvNueP3
|
||||
qwEumyjIVv96u39pAAAAAAEC
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
`
|
||||
WrongPrivateKey = `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
|
@ -728,5 +766,33 @@ CYhwNQKBgGPcLXmjpGtkZvggl0aZr9LsvCTckllSCFSI861kivL/rijdNoCHGxZv
|
|||
dfDkLTLcz9Gk41rD9Gxn/3sqodnTAc3Z2PxFnzg1Q/u3+x6YAgBwI/g/jE2xutGW
|
||||
H7CurtMwALQ/n/6LUKFmjRZjqbKX9SO2QSaC3grd6sY9Tu+bZjLe
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
BadPrivateKey = `
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAgUElV5mwqkloIrM8ZNZ72gSCcnSJt7+/Usa5G+D15YQUAdf9
|
||||
c1zEekTfHgDP+04nw/uFNFaE5v1RbHaPxhZYVg5ZErNCa/hzn+x10xzcepeS3KPV
|
||||
Xcxae4MR0BEegvqZqJzN9loXsNL/c3H/B+2Gle3hTxjlWFb3F5qLgR+4Mf4ruhER
|
||||
1v6eHQa/nchi03MBpT4UeJ7MrL92hTJYLdpSyCqmr8yjxkKJDVC2uRrr+sTSxfh7
|
||||
r6v24u/vp/QTmBIAlNPgadVAZw17iNNb7vjV7Gwl/5gHXonCUKURaV++dBNLrHIZ
|
||||
pqcAM8wHRph8mD1EfL9hsz77pHewxolBATV+7QIDAQABAoIBAC1rK+kFW3vrAYm3
|
||||
+8/fQnQQw5nec4o6+crng6JVQXLeH32qXShNf8kLLG/Jj0vaYcTPPDZw9JCKkTMQ
|
||||
0mKj9XR/5DLbBMsV6eNXXuvJJ3x4iKW5eD9WkLD4FKlNarBRyO7j8sfPTqXW7uat
|
||||
NxWdFH7YsSRvNh/9pyQHLWA5OituidMrYbc3EUx8B1GPNyJ9W8Q8znNYLfwYOjU4
|
||||
Wv1SLE6qGQQH9Q0WzA2WUf8jklCYyMYTIywAjGb8kbAJlKhmj2t2Igjmqtwt1PYc
|
||||
pGlqbtQBDUiWXt5S4YX/1maIQ/49yeNUajjpbJiH3DbhJbHwFTzP3pZ9P9GHOzlG
|
||||
kYR+wSECgYEAw/Xida8kSv8n86V3qSY/I+fYQ5V+jDtXIE+JhRnS8xzbOzz3v0WS
|
||||
Oo5H+o4nJx5eL3Ghb3Gcm0Jn46dHrxinHbm+3RjXv/X6tlbxIYjRSQfHOTSMCTvd
|
||||
qcliF5vC6RCLXuc7R+IWR1Ky6eDEZGtrvt3DyeYABsp9fRUFR/6NluUCgYEAqNsw
|
||||
1aSl7WJa27F0DoJdlU9LWerpXcazlJcIdOz/S9QDmSK3RDQTdqfTxRmrxiYI9LEs
|
||||
mkOkvzlnnOBMpnZ3ZOU5qIRfprecRIi37KDAOHWGnlC0EWGgl46YLb7/jXiWf0AG
|
||||
BhXoKvjI2HjYP21z/EyZ+PFPzur/lNaZhIUlMnUfibbwE9pFggQzzf8scM7c7Sf+
|
||||
mLoVSdoQ/Rujz7CqvQzi2nKSsM7t0curUIb3lJWee5/UeEaxZcmIufoNUrzohAWH
|
||||
BJOIPDM4ssUTLRq7wYM9uQKBgHCBau5OP8gE6mjKuXsZXWUoahpFLKwwwmJUp2vQ
|
||||
pOFPJ/6WZOlqkTVT6QPAcPUbTohKrF80hsZqZyDdSfT3peFx4ZLocBrS56m6NmHR
|
||||
UYHMvJ8rQm76T1fryHVidz85g3zRmfBeWg8yqT5oFg4LYgfLsPm1gRjOhs8LfPvI
|
||||
OLlRAoGBAIZ5Uv4Z3s8O7WKXXUe/lq6j7vfiVkR1NW/Z/WLKXZpnmvJ7FgxN4e56
|
||||
RXT7GwNQHIY8eDjDnsHxzrxd+raOxOZeKcMHj3XyjCX3NHfTscnsBPAGYpY/Wxzh
|
||||
T8UYnFu6RzkixElTf2rseEav7rkdKkI3LAeIZy7B0HulKKsmqVQ7
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue