* fix: handle decryption errors gracefully in GetDecryptedJson function - Replaced os.Exit(1) with an error return to improve error handling during feature flag decryption. * chore: add project name to goreleaser configuration for prime-monitor * chore: update Dockerfile base image and dependencies; enhance .gitignore for Go project - Changed base image in Dockerfile from goreleaser to golang:1.25.1-alpine3.22. - Added necessary packages and updated the installation command for goreleaser. - Updated Go version in go.work from 1.22.4 to 1.24.0. - Expanded .gitignore to include additional Go build artifacts and directories. - Removed unused import in decryption.go. * chore: update Go version and adjust module imports in go.work - Updated Go version from 1.22.4 to 1.24.0. - Added ./lib/api and ./lib/feat_flag to the use block. - Removed ./lib/api and ./lib/feat_flag from the previous imports. * chore: update Go version in Dockerfiles from 1.25.1 to 1.25.4 - Updated the Go base image in both email and monitor Dockerfiles to version 1.25.4-alpine3.22 for improved performance and security. * chore: update go.work.sum with new module versions and modify decryption error logging * Update apps/monitor/.gitignore Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
150 lines
4.2 KiB
Go
150 lines
4.2 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"
|
|
"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 fmt.Errorf("failed to decrypt feature flag: %v", err)
|
|
}
|
|
|
|
// 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
|
|
}
|