Skip to content

Commit

Permalink
feat: add crypto utils, template functions and ibm vpc certificate
Browse files Browse the repository at this point in the history
Signed-off-by: Mehant Kammakomati <kmehant@gmail.com>
  • Loading branch information
kmehant committed Sep 16, 2022
1 parent 5064d5f commit 7a58987
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 1 deletion.
4 changes: 4 additions & 0 deletions assets/assets.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ var AssetsDir embed.FS
//AssetFilePermissions is embedded file having permissions information for the assets
//go:embed filepermissions.yaml
var AssetFilePermissions string

//IbmHyperProtectCert is embedded file having IBM Hyper Protect encryption certificate
//go:embed ibm-hyper-protect-container-runtime-1-0-s390x-4-encrypt.crt
var IbmHyperProtectCert string
34 changes: 34 additions & 0 deletions assets/ibm-hyper-protect-container-runtime-1-0-s390x-4-encrypt.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-----BEGIN CERTIFICATE-----
MIIF8TCCA9mgAwIBAgIQLKNAizePV1jGkvBknjjfOzANBgkqhkiG9w0BAQ0FADCB
0TELMAkGA1UEBhMCREUxGzAZBgNVBAgMEkJhZGVuLVfDvHJ0dGVtYmVyZzETMBEG
A1UEBwwKQsO2YmxpbmdlbjE0MDIGA1UECgwrSUJNIERldXRzY2hsYW5kIFJlc2Vh
cmNoICYgRGV2ZWxvcG1lbnQgR21iSDEkMCIGA1UECxMbSUJNIFogSHlicmlkIENs
b3VkIFBsYXRmb3JtMTQwMgYDVQQDDCtJQk0gRGV1dHNjaGxhbmQgUmVzZWFyY2gg
JiBEZXZlbG9wbWVudCBHbWJIMB4XDTIyMDkwMjE2NTc0N1oXDTQyMDkwMjE2NTc1
N1owgZYxCzAJBgNVBAYTAkRFMQswCQYDVQQIEwJCVzETMBEGA1UEBxMKQm9lYmxp
bmdlbjEhMB8GA1UECgwYSUJNIERldXRzY2hsYW5kIFImRCBHbWJIMSQwIgYDVQQL
ExtJQk0gWiBIeWJyaWQgQ2xvdWQgUGxhdGZvcm0xHDAaBgNVBAMTE2NvbnRyYWN0
LWRlY3J5cHRpb24wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDI9Jx9
NXPsbONFVqIsXfzB/4WI4Kj070AxveF8QHTMb8mQ8KOD5ZDs6Ug1fli2JbxFPfvK
oFD0v1FNsxBhjWHAkq8LpeIzrG0YVLmDcjQqEaJQd58YK8GygOLy7qoRMedsVr2X
+MIqxJda06tc/O3GrM4swZRQVh7I0BHB9cJ3mLbh7St3vmhBpNZt9EKIgTJUGFUH
gTpeZuh2AjOcKsdrbzfGcs+4q1CstVNZ9eECVc27JPAzzrfzS8ZRlLJPOVEVDj1Z
gs3rA36eTxRMC0XuJC+mgKASJsFKygYQmfbs1mzIN0oIzsewjHM6AywuJ21Srjaq
gMSaRKzfpnMELJqWpIKFDGjj+p6anp8zJPYQy9IrOG8ifgCg+LhVGQ6mx3xMgY3m
H9Mwcto/ox6mkLf/7JYWK2RoAZEJRuojuMpOfeOLEkkzkBgzgD2JLh2ps+Zc7YxE
I9O02vMHUHhamqLyjD1OOBUBbYQ+W+28svbMgr3m5F8ILzXVWTnT6+h6WStXhLbk
zUIsAWconRt6g3A6Y9UCeK252j3ITjKPlcduICZkkcnaj73VDACRmoOVBPrnb2Ex
YfXhibBlwPcGyUV+GwlZgs5IN+X8GIU0I6QFFUUh3+BhgbVu8Rei0CKl52aRyFTe
w9wo0abntwYLQlovZLNsPtMeZIGO/P37IMelGwIDAQABMA0GCSqGSIb3DQEBDQUA
A4ICAQAgBhbamlqQlOYNgyOOPnuDNRe/LEshv+yeHS5Yqjgb/o5WzhHQNla6kQpD
TgbYvF70Qkj3agSH6+M6C+mmdgzGNQOWhnPBPtDiySOn8BvlhIvcsOz/OQyIi0Se
4vqiKPQmGUJ9aZCmzmkKbzUIpWJZy8XOcG15a5lW1OIDIVl7qRehZDQ0MqhYk5yQ
hXG/0o50APhSJ3fN6ulcdP/BfMGQmHs3fRHiaOMxJvJC/obUSDCgDIrBodAk2GvW
8aKEu2yRS1RoespumrkB621eULWhTQ//M31JlvBSo5daulOcjfBeCmGcQGQFJs45
hsTkLfltYf6nkFxzrjPvaRMT9xGmXFUkMrr163P2f0ngDp2BopqAGaVT/yD4llOs
Li5o5ZEcSOhILypa141pGwDBK/7IGv35zicO39VlpKsF/sRej4xPMkZOSlBSAgQf
oDJ6NLx69TtmcDpz0nU9y4yjZQDWj2CiG8yK5Lr9ayq8ayOneJr3Krh0bJ43izD2
19UeNHaQrN94ylMNAyNB+2QrOtkAYuu0XKYuEDYaKx5V9w0Oodc2RJVZVt4PeHyY
BxB0v4gNdfr/ESjrmwHfQJh1wQYMG6mUUHseIGKwb7qLaHIp7Nxxc1bydlxEHqqB
bF0c1daNoz1JrAL6rrhMRMT8TQZTw+n/+R3HDbdIWG9alxtNbg==
-----END CERTIFICATE-----
139 changes: 139 additions & 0 deletions common/cryptoutils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright IBM Corporation 2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package common

import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/pem"
"fmt"

"github.com/konveyor/move2kube/assets"

"github.com/sirupsen/logrus"
"golang.org/x/crypto/pbkdf2"
)

// RsaCertEncryptWrapper can be used to encrypt the data using RSA PKCS1v15 algorithm with certificate as key
func RsaCertEncryptWrapper(certificate string, data string) string {
if certificate == "" {
logrus.Info("using certificate from IBM Hyper Protect Container Runtime")
certificate = assets.IbmHyperProtectCert
}

rsaPublicKey, err := getRSAPublicKeyFromCertificate([]byte(certificate))
if err != nil {
logrus.Errorf("failed to parse the certificate and get the RSA public key from it. Error: %q", err)
}

out, err := rsaEncrypt(rsaPublicKey, []byte(data))
if err != nil {
logrus.Errorf("failed to encrypt the data with RSA PKCS1v15 algorithm with certificate as key : %q", err)
}

return string(out)
}

// AesCbcEncryptWithPbkdfWrapper can be used to encrypt the data using AES 256 CBC mode with Pbkdf key derivation
func AesCbcEncryptWithPbkdfWrapper(key string, data string) string {
salt := make([]byte, 16)
_, err := rand.Read(salt)
if err != nil {
logrus.Errorf("failed to prepare the salt : %s", err)
}
out, err := aesCbcEncryptWithPbkdf([]byte(key), salt, []byte(data))
if err != nil {
logrus.Errorf("failed to encrypt the data using AES 256 CBC mode with Pbkdf key derivation : %q", err)
}
opensslOut := toOpenSSLFormat(salt, out)
return string(opensslOut)
}

// getRSAPublicKeyFromCertificate gets a RSA public key from a PEM-encoded certificate.crt file.
func getRSAPublicKeyFromCertificate(certificateInPemFormat []byte) (*rsa.PublicKey, error) {
block, _ := pem.Decode(certificateInPemFormat)
if block == nil {
return nil, fmt.Errorf("invalid certificate. Expected a PEM encoded certificate")
}
cert, err := x509.ParseCertificate(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse the x509 certificate. Error: %q", err)
}
pubKey, ok := cert.PublicKey.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("failed to type cast the public key from the x509 certificate. Actual type: %T and value: %+v", cert.PublicKey, cert.PublicKey)
}
return pubKey, nil
}

// rsaEncrypt encrypts the plain text using the RSA public key.
func rsaEncrypt(publicKey *rsa.PublicKey, plainText []byte) ([]byte, error) {
cipherText, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText)
if err != nil {
return cipherText, fmt.Errorf("failed to RSA encrypt the plain text. Error: %q", err)
}
return cipherText, nil
}

// deriveAesKeyAndIv returns an AES key and IV derived from the password and salt using PBKDF2 function.
func deriveAesKeyAndIv(password, salt []byte) ([]byte, []byte) {
aesKeyAndIv := pbkdf2.Key(password, salt, 10000, 32+16, sha256.New)
return aesKeyAndIv[:32], aesKeyAndIv[32:]
}

// aesCbcEncryptWithPbkdf derives an AES key and IV using the given password and salt and then encrypts the plain text.
func aesCbcEncryptWithPbkdf(password, salt, plainText []byte) ([]byte, error) {

// derive an AES key and IV using the password and the salt

aesKey, iv := deriveAesKeyAndIv(password, salt)

// encrypt the workload using the AES key

aesCipher, err := aes.NewCipher(aesKey)
if err != nil {
return nil, fmt.Errorf("failed to create a new AES cipher using the key. Error: %q", err)
}

// pad the plain text as per PKCS#5 https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS#5_and_PKCS#7

paddingRequired := aes.BlockSize - len(plainText)%aes.BlockSize
if paddingRequired == 0 {
paddingRequired = aes.BlockSize
}
padding := make([]byte, paddingRequired)
for i := 0; i < paddingRequired; i++ {
padding[i] = byte(paddingRequired)
}

paddedPlainText := append(plainText, padding...)
cipherText := make([]byte, len(paddedPlainText))

aesCbcEncrypter := cipher.NewCBCEncrypter(aesCipher, iv)
aesCbcEncrypter.CryptBlocks(cipherText, paddedPlainText)
return cipherText, nil
}

// toOpenSSLFormat converts the cipher text to OpenSSL encrypted with password and salt format.
// http://justsolve.archiveteam.org/wiki/OpenSSL_salted_format
func toOpenSSLFormat(salt, cipherText []byte) []byte {
return append(append([]byte("Salted__"), salt...), cipherText...)
}
19 changes: 18 additions & 1 deletion filesystem/templatecopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"text/template"

"github.com/Masterminds/sprig"
Expand Down Expand Up @@ -145,6 +146,15 @@ func templateCopyDeletionCallBack(source, destination string, addOnConfigAsIface
return nil
}

// execTpl executes the template and returns the filled template
func execTpl(t *template.Template) func(string, interface{}) (string, error) {
return func(name string, v interface{}) (string, error) {
var buf strings.Builder
err := t.ExecuteTemplate(&buf, name, v)
return buf.String(), err
}
}

// writeTemplateToFile writes a templated string to a file
func writeTemplateToFile(tpl string, config interface{}, writepath string,
filemode os.FileMode, openingDelimiter string, closingDelimiter string) error {
Expand All @@ -153,7 +163,14 @@ func writeTemplateToFile(tpl string, config interface{}, writepath string,
openingDelimiter = "{{"
closingDelimiter = "}}"
}
packageTemplate, err := template.New("").Delims(openingDelimiter, closingDelimiter).Funcs(sprig.TxtFuncMap()).Parse(tpl)
packageTemplate := template.New("")
var err error
methodMap := template.FuncMap{
"execTpl": execTpl(packageTemplate),
"aesCbcPbkdfEnc": common.AesCbcEncryptWithPbkdfWrapper,
"rsaCertEnc": common.RsaCertEncryptWrapper,
}
template.Must(packageTemplate.Delims(openingDelimiter, closingDelimiter).Funcs(sprig.TxtFuncMap()).Funcs(methodMap).Parse(tpl))
if err != nil {
logrus.Errorf("Unable to parse the template : %s", err)
return err
Expand Down

0 comments on commit 7a58987

Please sign in to comment.