mirror of
https://github.com/datarhei/core.git
synced 2025-10-05 07:57:13 +08:00

This secret will be used to encrypt automatically obtained secrets at rest, i.e. in a storage. They will be decrypted on demand. If the secret is wrong, stored certificates can't be decrypted. For changing the secret, the stored certificated must be deleted first in order to obtain new ones that will be encrypted with the new secret.
107 lines
2.3 KiB
Go
107 lines
2.3 KiB
Go
package autocert
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"io"
|
|
|
|
"golang.org/x/crypto/scrypt"
|
|
)
|
|
|
|
type Crypto interface {
|
|
// Encrypt encrypts the given data or returns error if encrypting the data is not possible.
|
|
Encrypt(data []byte) ([]byte, error)
|
|
|
|
// Decrypt decrypts the given data or returns error if decrypting the data is not possible.
|
|
Decrypt(data []byte) ([]byte, error)
|
|
}
|
|
|
|
type crypto struct {
|
|
secret []byte
|
|
}
|
|
|
|
// NewCrypto returns a new implementation of the the Crypto interface that encrypts/decrypts
|
|
// the given data with a key and salt derived from the provided secret.
|
|
// Based on https://itnext.io/encrypt-data-with-a-password-in-go-b5366384e291
|
|
func NewCrypto(secret string) Crypto {
|
|
c := &crypto{
|
|
secret: []byte(secret),
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
func (c *crypto) Encrypt(data []byte) ([]byte, error) {
|
|
key, salt, err := c.deriveKey(nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
blockCipher, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
gcm, err := cipher.NewGCM(blockCipher)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nonce := make([]byte, gcm.NonceSize())
|
|
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// The first gcm.NonceSize() are the nonce
|
|
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
|
// The last 32 bytes are the salt
|
|
ciphertext = append(ciphertext, salt...)
|
|
|
|
return ciphertext, nil
|
|
}
|
|
|
|
func (c *crypto) Decrypt(data []byte) ([]byte, error) {
|
|
// The last 32 bytes are the salt
|
|
salt, data := data[len(data)-32:], data[:len(data)-32]
|
|
key, _, err := c.deriveKey(salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
blockCipher, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
gcm, err := cipher.NewGCM(blockCipher)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// The first gcm.NonceSize() are the nonce
|
|
nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():]
|
|
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return plaintext, nil
|
|
}
|
|
|
|
func (c *crypto) deriveKey(salt []byte) ([]byte, []byte, error) {
|
|
if salt == nil {
|
|
salt = make([]byte, 32)
|
|
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
|
return nil, nil, err
|
|
}
|
|
}
|
|
|
|
key, err := scrypt.Key(c.secret, salt, 32768, 8, 1, 32)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return key, salt, nil
|
|
}
|