blob: f0e5fcc04270b252af0c7d941ec584bf24696dae [file] [log] [blame] [edit]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package configutil
import (
"bytes"
"context"
"encoding/base64"
"errors"
"fmt"
"regexp"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"google.golang.org/protobuf/proto"
)
var (
encryptRegex = regexp.MustCompile(`{{encrypt\(.*\)}}`)
decryptRegex = regexp.MustCompile(`{{decrypt\(.*\)}}`)
)
func EncryptDecrypt(rawStr string, decrypt, strip bool, wrapper wrapping.Wrapper) (string, error) {
var locs [][]int
raw := []byte(rawStr)
searchVal := "{{encrypt("
replaceVal := "{{decrypt("
suffixVal := ")}}"
if decrypt {
searchVal = "{{decrypt("
replaceVal = "{{encrypt("
locs = decryptRegex.FindAllIndex(raw, -1)
} else {
locs = encryptRegex.FindAllIndex(raw, -1)
}
if strip {
replaceVal = ""
suffixVal = ""
}
out := make([]byte, 0, len(rawStr)*2)
var prevMaxLoc int
for _, match := range locs {
if len(match) != 2 {
return "", fmt.Errorf("expected two values for match, got %d", len(match))
}
// Append everything from the end of the last match to the beginning of this one
out = append(out, raw[prevMaxLoc:match[0]]...)
// Transform. First pull off the suffix/prefix
matchBytes := raw[match[0]:match[1]]
matchBytes = bytes.TrimSuffix(bytes.TrimPrefix(matchBytes, []byte(searchVal)), []byte(")}}"))
var finalVal string
// Now encrypt or decrypt
switch decrypt {
case false:
outBlob, err := wrapper.Encrypt(context.Background(), matchBytes, nil)
if err != nil {
return "", fmt.Errorf("error encrypting parameter: %w", err)
}
if outBlob == nil {
return "", errors.New("nil value returned from encrypting parameter")
}
outMsg, err := proto.Marshal(outBlob)
if err != nil {
return "", fmt.Errorf("error marshaling encrypted parameter: %w", err)
}
finalVal = base64.RawURLEncoding.EncodeToString(outMsg)
default:
inMsg, err := base64.RawURLEncoding.DecodeString(string(matchBytes))
if err != nil {
return "", fmt.Errorf("error decoding encrypted parameter: %w", err)
}
inBlob := new(wrapping.BlobInfo)
if err := proto.Unmarshal(inMsg, inBlob); err != nil {
return "", fmt.Errorf("error unmarshaling encrypted parameter: %w", err)
}
dec, err := wrapper.Decrypt(context.Background(), inBlob, nil)
if err != nil {
return "", fmt.Errorf("error decrypting encrypted parameter: %w", err)
}
finalVal = string(dec)
}
// Append new value
out = append(out, []byte(fmt.Sprintf("%s%s%s", replaceVal, finalVal, suffixVal))...)
prevMaxLoc = match[1]
}
// At the end, append the rest
out = append(out, raw[prevMaxLoc:]...)
return string(out), nil
}