diff --git a/pkg/util/encryption.go b/pkg/util/encryption.go new file mode 100644 index 00000000000..f24a9b29397 --- /dev/null +++ b/pkg/util/encryption.go @@ -0,0 +1,70 @@ +package util + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "io" + + "github.com/grafana/grafana/pkg/log" +) + +func Decrypt(payload []byte, secret string) []byte { + key := encryptionKeyToBytes(secret) + + block, err := aes.NewCipher(key) + if err != nil { + log.Fatal(4, err.Error()) + } + + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + if len(payload) < aes.BlockSize { + log.Fatal(4, "payload too short") + } + iv := payload[:aes.BlockSize] + payload = payload[aes.BlockSize:] + + stream := cipher.NewCFBDecrypter(block, iv) + + // XORKeyStream can work in-place if the two arguments are the same. + stream.XORKeyStream(payload, payload) + return payload +} + +func Encrypt(payload []byte, secret string) []byte { + key := encryptionKeyToBytes(secret) + + block, err := aes.NewCipher(key) + if err != nil { + log.Fatal(4, err.Error()) + } + + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + ciphertext := make([]byte, aes.BlockSize+len(payload)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + log.Fatal(4, err.Error()) + } + + stream := cipher.NewCFBEncrypter(block, iv) + stream.XORKeyStream(ciphertext[aes.BlockSize:], payload) + + return ciphertext +} + +// Key needs to be 32bytes +func encryptionKeyToBytes(secret string) []byte { + key := make([]byte, 32, 32) + keyBytes := []byte(secret) + secretLength := len(keyBytes) + for i := 0; i < 32; i++ { + if secretLength > i { + key[i] = keyBytes[i] + } else { + key[i] = 0 + } + } + return key +} diff --git a/pkg/util/encryption_test.go b/pkg/util/encryption_test.go new file mode 100644 index 00000000000..254f0f178c0 --- /dev/null +++ b/pkg/util/encryption_test.go @@ -0,0 +1,28 @@ +package util + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestEncryption(t *testing.T) { + + Convey("When getting encryption key", t, func() { + + key := encryptionKeyToBytes("secret") + So(len(key), ShouldEqual, 32) + + key = encryptionKeyToBytes("a very long secret key that is larger then 32bytes") + So(len(key), ShouldEqual, 32) + + }) + + Convey("When decrypting basic payload", t, func() { + encrypted := Encrypt([]byte("grafana"), "1234") + decrypted := Decrypt(encrypted, "1234") + + So(string(decrypted), ShouldEqual, "grafana") + }) + +}