Files
plane/monitor/lib/feat_flag/decryption.go
2024-10-23 19:55:01 +05:30

150 lines
4.1 KiB
Go

package feat_flag
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/rsa"
"crypto/sha1"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"fmt"
"os"
"strings"
"github.com/youmark/pkcs8"
)
// File exports utility functions for decrypting feature flags, requiring the
// private key to be passed in as a parameter.
/* ------------------------ Controller Methods ------------------------------- */
// Takes in the private key and returns the decrypted feature flags
func GetDecryptedJson(base64EncodedKey string, encryptedFeatureFlag EncryptedData, out interface{}) error {
// Parse the private key
rsaPrivateKey, err := ParsePrivateKey(base64EncodedKey)
if err != nil {
return fmt.Errorf("failed to parse private key: %v", err)
}
// decrypt with the pem key
// Decrypt the feature flag
decryptedData, err := decryptWithPrivateKey(encryptedFeatureFlag, rsaPrivateKey)
if err != nil {
fmt.Printf("failed to decrypt feature flag: %v", err)
os.Exit(1)
}
// Return the error from the unmarshal function
return json.Unmarshal(decryptedData, out)
}
func decodeBase64Key(base64EncodedKey string) ([]byte, error) {
// Remove any whitespace from the encoded key
base64EncodedKey = strings.TrimSpace(base64EncodedKey)
// Remove any line breaks or spaces within the string
base64EncodedKey = strings.ReplaceAll(base64EncodedKey, "\n", "")
base64EncodedKey = strings.ReplaceAll(base64EncodedKey, "\r", "")
base64EncodedKey = strings.ReplaceAll(base64EncodedKey, " ", "")
// Check if the length of the string is a multiple of 4, if not, pad with '='
if len(base64EncodedKey)%4 != 0 {
padding := 4 - (len(base64EncodedKey) % 4)
base64EncodedKey += strings.Repeat("=", padding)
}
// Try standard base64 decoding
decodedKey, err := base64.StdEncoding.DecodeString(base64EncodedKey)
if err == nil {
return decodedKey, nil
}
// If both methods fail, return an error
return nil, fmt.Errorf("failed to decode base64 key: %v", err)
}
/* ---------------------- Helper Functions ---------------------------- */
// Parses the private key given to the rsa.PrivateKey type
func ParsePrivateKey(base64EncodedKey string) (*rsa.PrivateKey, error) {
decodedKey, err := decodeBase64Key(base64EncodedKey)
if err != nil {
return nil, fmt.Errorf("failed to decode base64 key: %v", err)
}
block, _ := pem.Decode(decodedKey)
if block == nil {
return nil, fmt.Errorf("failed to parse PEM block containing the key")
}
privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
privateKey, err := pkcs8.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse RSA private key: %v", err)
}
key := privateKey.(*rsa.PrivateKey)
return key, nil
}
return privateKey, nil
}
func decryptWithPrivateKey(data EncryptedData, privateKey *rsa.PrivateKey) ([]byte, error) {
// Decode the base64 encoded AES key
aesKey, err := base64.StdEncoding.DecodeString(data.AesKey)
if err != nil {
fmt.Printf("Error decoding AES key: %v\n", err)
return nil, err
}
nonce, err := base64.StdEncoding.DecodeString(data.Nonce)
if err != nil {
fmt.Printf("Error decoding nonce: %v\n", err)
return nil, err
}
ciphertext, err := base64.StdEncoding.DecodeString(data.CipherText)
if err != nil {
fmt.Printf("Error decoding ciphertext: %v\n", err)
return nil, err
}
tag, err := base64.StdEncoding.DecodeString(data.Tag)
if err != nil {
fmt.Printf("Error decoding tag: %v\n", err)
return nil, err
}
decryptedAESKey, err := rsa.DecryptOAEP(sha1.New(), rand.Reader, privateKey, aesKey, nil)
if err != nil {
fmt.Printf("Error decrypting AES key: %v\n", err)
return nil, err
}
blockCipher, err := aes.NewCipher(decryptedAESKey)
if err != nil {
fmt.Printf("Error creating AES cipher: %v\n", err)
return nil, err
}
aesGCM, err := cipher.NewGCMWithNonceSize(blockCipher, 16)
if err != nil {
fmt.Printf("Error creating GCM: %v\n", err)
return nil, err
}
plaintext, err := aesGCM.Open(nil, nonce, append(ciphertext, tag...), nil)
if err != nil {
fmt.Printf("Error decrypting ciphertext: %v\n", err)
return nil, err
}
return plaintext, nil
}