4d8678c7f2
Co-authored-by: Michael Mandrus <michael.mandrus@grafana.com> Co-authored-by: Matheus Macabu <macabu@users.noreply.github.com>
122 lines
2.7 KiB
Go
122 lines
2.7 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
"crypto/aes"
|
|
cpr "crypto/cipher"
|
|
"crypto/rand"
|
|
"io"
|
|
|
|
"github.com/grafana/grafana/pkg/registry/apis/secret/encryption/cipher"
|
|
)
|
|
|
|
const (
|
|
gcmSaltLength = 8
|
|
AesGcm = "aes-gcm"
|
|
)
|
|
|
|
var (
|
|
_ cipher.Encrypter = (*aesGcmCipher)(nil)
|
|
_ cipher.Decrypter = (*aesGcmCipher)(nil)
|
|
)
|
|
|
|
type aesGcmCipher struct {
|
|
// randReader is used to generate random bytes for the nonce.
|
|
// This allows us to change out the entropy source for testing.
|
|
randReader io.Reader
|
|
}
|
|
|
|
func NewAesGcmCipher() aesGcmCipher {
|
|
return aesGcmCipher{
|
|
randReader: rand.Reader,
|
|
}
|
|
}
|
|
|
|
func (c aesGcmCipher) Encrypt(_ context.Context, payload []byte, secret string) ([]byte, error) {
|
|
salt, err := c.readEntropy(gcmSaltLength)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
key, err := aes256CipherKey(secret, salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
gcm, err := cpr.NewGCM(block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nonce, err := c.readEntropy(gcm.NonceSize())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ciphertext := gcm.Seal(nil, nonce, payload, nil)
|
|
|
|
// Salt Nonce Encrypted
|
|
// | | Payload
|
|
// | | |
|
|
// | +---------v-------------+ |
|
|
// +-->SSSSSSSNNNNNNNEEEEEEEEE<--+
|
|
// +-----------------------+
|
|
prefix := append(salt, nonce...)
|
|
ciphertext = append(prefix, ciphertext...)
|
|
|
|
return ciphertext, nil
|
|
}
|
|
|
|
func (c aesGcmCipher) Decrypt(_ context.Context, payload []byte, secret string) ([]byte, error) {
|
|
// The input payload looks like:
|
|
// Salt Nonce Encrypted
|
|
// | | Payload
|
|
// | | |
|
|
// | +---------v-------------+ |
|
|
// +-->SSSSSSSNNNNNNNEEEEEEEEE<--+
|
|
// +-----------------------+
|
|
|
|
if len(payload) < gcmSaltLength {
|
|
// If we don't return here, we'd panic.
|
|
return nil, ErrPayloadTooShort
|
|
}
|
|
salt, payload := payload[:gcmSaltLength], payload[gcmSaltLength:]
|
|
// Can't get nonce until we get a size from the AEAD interface.
|
|
|
|
key, err := aes256CipherKey(secret, salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
gcm, err := cpr.NewGCM(block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(payload) < gcm.NonceSize() {
|
|
// If we don't return here, we'd panic.
|
|
return nil, ErrPayloadTooShort
|
|
}
|
|
nonce, payload := payload[:gcm.NonceSize()], payload[gcm.NonceSize():]
|
|
|
|
return gcm.Open(nil, nonce, payload, nil)
|
|
}
|
|
|
|
func (c aesGcmCipher) readEntropy(n int) ([]byte, error) {
|
|
entropy := make([]byte, n)
|
|
if _, err := io.ReadFull(c.randReader, entropy); err != nil {
|
|
return nil, err
|
|
}
|
|
return entropy, nil
|
|
}
|