173 lines
4.1 KiB
Go
173 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)
|
||
|
}
|