Encryption: Override Encrypt/Decrypt in util
Breaks every test that uses Encrypt or Decrypt
This commit is contained in:
+2
-2
@@ -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
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user