All checks were successful
/ pre-commit (push) Successful in 1m9s
Signed-off-by: Julien Riou <julien@riou.xyz>
77 lines
2.1 KiB
Go
77 lines
2.1 KiB
Go
package internal
|
|
|
|
import (
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"fmt"
|
|
|
|
"golang.org/x/crypto/argon2"
|
|
"golang.org/x/crypto/chacha20poly1305"
|
|
)
|
|
|
|
const (
|
|
SaltSize = 32
|
|
KeySize = uint32(32) // KeySize is 32 bytes (256 bits).
|
|
KeyTime = uint32(5)
|
|
KeyMemory = uint32(1024 * 64) // KeyMemory in KiB. here, 64 MiB.
|
|
KeyThreads = uint8(4)
|
|
)
|
|
|
|
// NewCipher creates a cipher using XChaCha20-Poly1305
|
|
// https://pkg.go.dev/golang.org/x/crypto/chacha20poly1305
|
|
// A salt is required to derive the key from an encryption key using argon
|
|
func NewCipher(encryptionKey string, salt []byte) (cipher.AEAD, error) {
|
|
key := argon2.IDKey([]byte(encryptionKey), salt, KeyTime, KeyMemory, KeyThreads, KeySize)
|
|
return chacha20poly1305.NewX(key)
|
|
}
|
|
|
|
// Encrypt to encrypt a plaintext with an encryption key
|
|
// Returns a byte slice with the generated salt, nonce and the ciphertext
|
|
func Encrypt(plaintext []byte, encryptionKey string) (result []byte, err error) {
|
|
salt := make([]byte, SaltSize)
|
|
if n, err := rand.Read(salt); err != nil || n != SaltSize {
|
|
return nil, err
|
|
}
|
|
|
|
aead, err := NewCipher(encryptionKey, salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result = append(result, salt...)
|
|
|
|
nonce := make([]byte, aead.NonceSize())
|
|
if m, err := rand.Read(nonce); err != nil || m != aead.NonceSize() {
|
|
return nil, err
|
|
}
|
|
|
|
result = append(result, nonce...)
|
|
|
|
ciphertext := aead.Seal(nil, nonce, plaintext, nil)
|
|
result = append(result, ciphertext...)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// Decrypt to decrypt a ciphertext with a encryption key
|
|
// Returns the plaintext
|
|
func Decrypt(ciphertext []byte, encryptionKey string) ([]byte, error) {
|
|
if len(ciphertext) < SaltSize {
|
|
return nil, fmt.Errorf("ciphertext is too short: cannot read salt")
|
|
}
|
|
salt := ciphertext[:SaltSize]
|
|
|
|
aead, err := NewCipher(encryptionKey, salt)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(ciphertext) < SaltSize+aead.NonceSize() {
|
|
return nil, fmt.Errorf("ciphertext is too short: cannot read nonce")
|
|
}
|
|
|
|
nonce := ciphertext[SaltSize : SaltSize+aead.NonceSize()]
|
|
ciphertext = ciphertext[SaltSize+aead.NonceSize():]
|
|
|
|
return aead.Open(nil, nonce, ciphertext, nil)
|
|
}
|