Encryption: Override Encrypt/Decrypt in util

Breaks every test that uses Encrypt or Decrypt
This commit is contained in:
Emil Tullstedt
2020-10-14 15:53:49 +02:00
parent 1ff6fa9424
commit 84669d2213
11 changed files with 146 additions and 161 deletions
+2 -2
View File
@@ -279,12 +279,12 @@ func tryGetEncryptedCookie(ctx *models.ReqContext, cookieName string) (string, b
return "", false
}
decryptedError, err := util.Decrypt(decoded, setting.SecretKey)
decryptedError, err := util.Decrypt(decoded)
return string(decryptedError), err == nil
}
func (hs *HTTPServer) trySetEncryptedCookie(ctx *models.ReqContext, cookieName string, value string, maxAge int) error {
encryptedError, err := util.Encrypt([]byte(value), setting.SecretKey)
encryptedError, err := util.Encrypt([]byte(value))
if err != nil {
return err
}
+1 -1
View File
@@ -109,7 +109,7 @@ func TestLoginErrorCookieApiEndpoint(t *testing.T) {
setting.OAuthAutoLogin = true
oauthError := errors.New("User not a member of one of the required organizations")
encryptedError, _ := util.Encrypt([]byte(oauthError.Error()), setting.SecretKey)
encryptedError, _ := util.Encrypt([]byte(oauthError.Error()))
expCookiePath := "/"
if len(setting.AppSubUrl) > 0 {
expCookiePath = setting.AppSubUrl
@@ -9,7 +9,6 @@ import (
"github.com/grafana/grafana/pkg/cmd/grafana-cli/utils"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errutil"
)
@@ -109,7 +108,7 @@ func updateRows(session *sqlstore.DBSession, rows []map[string][]byte, passwordF
}
func getUpdatedSecureJSONData(row map[string][]byte, passwordFieldName string) (map[string]interface{}, error) {
encryptedPassword, err := util.Encrypt(row[passwordFieldName], setting.SecretKey)
encryptedPassword, err := util.Encrypt(row[passwordFieldName])
if err != nil {
return nil, err
}
@@ -2,7 +2,6 @@ package securejsondata
import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@@ -14,7 +13,7 @@ type SecureJsonData map[string][]byte
// is true if the key exists and false if not.
func (s SecureJsonData) DecryptedValue(key string) (string, bool) {
if value, ok := s[key]; ok {
decryptedData, err := util.Decrypt(value, setting.SecretKey)
decryptedData, err := util.Decrypt(value)
if err != nil {
log.Fatalf(4, err.Error())
}
@@ -28,7 +27,7 @@ func (s SecureJsonData) DecryptedValue(key string) (string, bool) {
func (s SecureJsonData) Decrypt() map[string]string {
decrypted := make(map[string]string)
for key, data := range s {
decryptedData, err := util.Decrypt(data, setting.SecretKey)
decryptedData, err := util.Decrypt(data)
if err != nil {
log.Fatalf(4, err.Error())
}
@@ -42,7 +41,7 @@ func (s SecureJsonData) Decrypt() map[string]string {
func GetEncryptedJsonData(sjd map[string]string) SecureJsonData {
encrypted := make(SecureJsonData)
for key, data := range sjd {
encryptedData, err := util.Encrypt([]byte(data), setting.SecretKey)
encryptedData, err := util.Encrypt([]byte(data))
if err != nil {
log.Fatalf(4, err.Error())
}
+1
View File
@@ -39,6 +39,7 @@ import (
_ "github.com/grafana/grafana/pkg/services/provisioning"
_ "github.com/grafana/grafana/pkg/services/rendering"
_ "github.com/grafana/grafana/pkg/services/search"
_ "github.com/grafana/grafana/pkg/services/secrets"
_ "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
+80
View File
@@ -0,0 +1,80 @@
package secrets
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"errors"
"io"
"github.com/grafana/grafana/pkg/util"
"golang.org/x/crypto/pbkdf2"
)
const saltLength = 8
// Decrypt decrypts a payload with a given secret.
func decrypt(payload, secret []byte) ([]byte, error) {
salt := payload[:saltLength]
key, err := encryptionKeyToBytes(secret, salt)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 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 {
return nil, errors.New("payload too short")
}
iv := payload[saltLength : saltLength+aes.BlockSize]
payload = payload[saltLength+aes.BlockSize:]
payloadDst := make([]byte, len(payload))
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(payloadDst, payload)
return payloadDst, nil
}
// Encrypt encrypts a payload with a given secret.
func encrypt(payload, secret []byte) ([]byte, error) {
salt, err := util.GetRandomString(saltLength)
if err != nil {
return nil, err
}
key, err := encryptionKeyToBytes(secret, []byte(salt))
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 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, saltLength+aes.BlockSize+len(payload))
copy(ciphertext[:saltLength], salt)
iv := ciphertext[saltLength : saltLength+aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[saltLength+aes.BlockSize:], payload)
return ciphertext, nil
}
// Key needs to be 32bytes
func encryptionKeyToBytes(secret, salt []byte) ([]byte, error) {
return pbkdf2.Key(secret, salt, 10000, 32, sha256.New), nil
}
-80
View File
@@ -1,18 +1,5 @@
package secrets
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"errors"
"io"
"golang.org/x/crypto/pbkdf2"
"github.com/grafana/grafana/pkg/util"
)
type secretKey struct {
key func() []byte
}
@@ -24,70 +11,3 @@ func (s *secretKey) Encrypt(blob []byte) ([]byte, error) {
func (s *secretKey) Decrypt(blob []byte) ([]byte, error) {
return decrypt(blob, s.key())
}
const saltLength = 8
// Decrypt decrypts a payload with a given secret.
func decrypt(payload, secret []byte) ([]byte, error) {
salt := payload[:saltLength]
key, err := encryptionKeyToBytes(secret, salt)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 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 {
return nil, errors.New("payload too short")
}
iv := payload[saltLength : saltLength+aes.BlockSize]
payload = payload[saltLength+aes.BlockSize:]
payloadDst := make([]byte, len(payload))
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(payloadDst, payload)
return payloadDst, nil
}
// Encrypt encrypts a payload with a given secret.
func encrypt(payload, secret []byte) ([]byte, error) {
salt, err := util.GetRandomString(saltLength)
if err != nil {
return nil, err
}
key, err := encryptionKeyToBytes(secret, []byte(salt))
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 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, saltLength+aes.BlockSize+len(payload))
copy(ciphertext[:saltLength], salt)
iv := ciphertext[saltLength : saltLength+aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[saltLength+aes.BlockSize:], payload)
return ciphertext, nil
}
// Key needs to be 32bytes
func encryptionKeyToBytes(secret, salt []byte) ([]byte, error) {
return pbkdf2.Key(secret, salt, 10000, 32, sha256.New), nil
}
+50
View File
@@ -3,10 +3,18 @@ package secrets
import (
"bytes"
"context"
"crypto/rand"
"encoding/base64"
"errors"
"fmt"
"time"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/infra/log"
@@ -17,8 +25,10 @@ var logger = log.New("secrets")
type Secrets struct {
store *sqlstore.SqlStore `inject:""`
bus bus.Bus `inject:""`
defaultEncryptionKey string
defaultProvider string
providers map[string]Provider
dataKeyCache map[string]dataKeyCacheItem
}
@@ -42,6 +52,46 @@ func (s *Secrets) Init() error {
},
}
base_key := "root"
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
_, err := s.store.GetDataKey(ctx, base_key)
if err != nil {
if errors.Is(err, models.ErrDataKeyNotFound) {
err = s.newRandomDataKey(ctx, base_key)
if err != nil {
return err
}
} else {
return err
}
}
util.Encrypt = s.Encrypt
util.Decrypt = s.Decrypt
return nil
}
func (s *Secrets) newRandomDataKey(ctx context.Context, base_key string) error {
b := make([]byte, 16)
_, err := rand.Read(b)
if err != nil {
return err
}
encrypted, err := s.Encrypt(b)
if err != nil {
return err
}
err = s.store.CreateDataKey(ctx, models.DataKey{
Active: true,
Name: base_key,
Provider: s.defaultProvider,
EncryptedData: encrypted,
})
return nil
}
+1 -2
View File
@@ -5,7 +5,6 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@@ -77,7 +76,7 @@ func UpdatePluginSetting(cmd *models.UpdatePluginSettingCmd) error {
return err
}
for key, data := range cmd.SecureJsonData {
encryptedData, err := util.Encrypt([]byte(data), setting.SecretKey)
encryptedData, err := util.Encrypt([]byte(data))
if err != nil {
return err
}
+2 -3
View File
@@ -6,7 +6,6 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
@@ -265,7 +264,7 @@ func decodeAndDecrypt(s string) (string, error) {
if err != nil {
return "", err
}
decrypted, err := util.Decrypt(decoded, setting.SecretKey)
decrypted, err := util.Decrypt(decoded)
if err != nil {
return "", err
}
@@ -275,7 +274,7 @@ func decodeAndDecrypt(s string) (string, error) {
// encryptAndEncode will encrypt a string with grafana's secretKey, and
// then encode it with the standard bas64 encoder
func encryptAndEncode(s string) (string, error) {
encrypted, err := util.Encrypt([]byte(s), setting.SecretKey)
encrypted, err := util.Encrypt([]byte(s))
if err != nil {
return "", err
}
+5 -67
View File
@@ -1,79 +1,17 @@
package util
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"errors"
"io"
"golang.org/x/crypto/pbkdf2"
)
const saltLength = 8
var ErrNotInitialized = errors.New("function is not initialized")
// Decrypt decrypts a payload with a given secret.
func Decrypt(payload []byte, secret string) ([]byte, error) {
salt := payload[:saltLength]
key, err := encryptionKeyToBytes(secret, string(salt))
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 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 {
return nil, errors.New("payload too short")
}
iv := payload[saltLength : saltLength+aes.BlockSize]
payload = payload[saltLength+aes.BlockSize:]
payloadDst := make([]byte, len(payload))
stream := cipher.NewCFBDecrypter(block, iv)
// XORKeyStream can work in-place if the two arguments are the same.
stream.XORKeyStream(payloadDst, payload)
return payloadDst, nil
var Decrypt = func(_ []byte) ([]byte, error) {
return nil, ErrNotInitialized
}
// Encrypt encrypts a payload with a given secret.
func Encrypt(payload []byte, secret string) ([]byte, error) {
salt, err := GetRandomString(saltLength)
if err != nil {
return nil, err
}
key, err := encryptionKeyToBytes(secret, salt)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// 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, saltLength+aes.BlockSize+len(payload))
copy(ciphertext[:saltLength], salt)
iv := ciphertext[saltLength : saltLength+aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(ciphertext[saltLength+aes.BlockSize:], payload)
return ciphertext, nil
}
// Key needs to be 32bytes
func encryptionKeyToBytes(secret, salt string) ([]byte, error) {
return pbkdf2.Key([]byte(secret), []byte(salt), 10000, 32, sha256.New), nil
var Encrypt = func(_ []byte) ([]byte, error) {
return nil, ErrNotInitialized
}