1
0
Fork 0
telegraf/plugins/secretstores/http/aes.go
Daniel Baumann 4978089aab
Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-24 07:26:29 +02:00

172 lines
4.1 KiB
Go

package http
import (
"crypto/aes"
"crypto/cipher"
"encoding/hex"
"errors"
"fmt"
"strings"
"github.com/awnumar/memguard"
"github.com/influxdata/telegraf/config"
)
type AesEncryptor struct {
Variant []string `toml:"-"`
Key config.Secret `toml:"key"`
Vec config.Secret `toml:"init_vector"`
KDFConfig
mode string
trim func([]byte) ([]byte, error)
}
func (a *AesEncryptor) Init() error {
var cipherName, mode, padding string
switch len(a.Variant) {
case 3:
padding = strings.ToLower(a.Variant[2])
fallthrough
case 2:
mode = strings.ToLower(a.Variant[1])
cipherName = strings.ToLower(a.Variant[0])
if !strings.HasPrefix(cipherName, "aes") {
return fmt.Errorf("requested AES but specified %q", cipherName)
}
case 1:
return errors.New("please specify cipher mode")
case 0:
return errors.New("please specify cipher")
default:
return errors.New("too many variant elements")
}
var keylen int
switch cipherName {
case "aes128":
keylen = 16
case "aes192":
keylen = 24
case "aes256":
keylen = 32
default:
return fmt.Errorf("unsupported AES cipher %q", cipherName)
}
if mode != "cbc" {
return fmt.Errorf("unsupported cipher mode %q", a.Variant[1])
}
a.mode = mode
// Setup the trimming function to revert padding
switch padding {
case "", "none":
// identity, no padding
a.trim = func(in []byte) ([]byte, error) { return in, nil }
case "pkcs#5", "pkcs#7":
a.trim = PKCS5or7Trimming
default:
return fmt.Errorf("unsupported padding %q", padding)
}
// Generate the key using password-based-keys
if a.Key.Empty() {
if a.Passwd.Empty() {
return errors.New("either key or password has to be specified")
}
if a.Salt.Empty() || a.Iterations == 0 {
return errors.New("salt and iterations required for password-based-keys")
}
key, iv, err := a.KDFConfig.NewKey(keylen)
if err != nil {
return fmt.Errorf("generating key failed: %w", err)
}
a.Key.Destroy()
a.Key = key
if a.Vec.Empty() && !iv.Empty() {
a.Vec.Destroy()
a.Vec = iv
}
} else {
encodedKey, err := a.Key.Get()
if err != nil {
return fmt.Errorf("getting key failed: %w", err)
}
key := make([]byte, hex.DecodedLen(len(encodedKey.Bytes())))
_, err = hex.Decode(key, encodedKey.Bytes())
encodedKey.Destroy()
if err != nil {
return fmt.Errorf("decoding key failed: %w", err)
}
actuallen := len(key)
memguard.WipeBytes(key)
if actuallen != keylen {
return fmt.Errorf("key length (%d bit) does not match cipher (%d bit)", actuallen*8, keylen*8)
}
}
if a.Vec.Empty() {
return errors.New("'init_vector' has to be specified or derived from password")
}
encodedIV, err := a.Vec.Get()
if err != nil {
return fmt.Errorf("getting IV failed: %w", err)
}
ivlen := len(encodedIV.Bytes())
encodedIV.Destroy()
if ivlen != 2*aes.BlockSize {
return errors.New("init vector size must match block size")
}
return nil
}
func (a *AesEncryptor) Decrypt(data []byte) ([]byte, error) {
if len(data)%aes.BlockSize != 0 {
return nil, fmt.Errorf("invalid data size %d", len(data))
}
if a.mode != "cbc" {
return nil, fmt.Errorf("unsupported cipher mode %q", a.mode)
}
// Setup the cipher and return the decoded data
encodedKey, err := a.Key.Get()
if err != nil {
return nil, fmt.Errorf("getting key failed: %w", err)
}
key := make([]byte, hex.DecodedLen(len(encodedKey.Bytes())))
_, err = hex.Decode(key, encodedKey.Bytes())
encodedKey.Destroy()
if err != nil {
return nil, fmt.Errorf("decoding key failed: %w", err)
}
// Setup AES
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("creating AES cipher failed: %w", err)
}
// Setup the block/stream cipher and decode the data
encodedIV, err := a.Vec.Get()
if err != nil {
return nil, fmt.Errorf("getting initialization-vector failed: %w", err)
}
iv := make([]byte, hex.DecodedLen(len(encodedIV.Bytes())))
_, err = hex.Decode(iv, encodedIV.Bytes())
encodedIV.Destroy()
if err != nil {
memguard.WipeBytes(iv)
return nil, fmt.Errorf("decoding init vector failed: %w", err)
}
cipher.NewCBCDecrypter(block, iv).CryptBlocks(data, data)
return a.trim(data)
}