mirror of
https://github.com/nais/wonderwall.git
synced 2026-05-07 00:46:56 +00:00
97 lines
2.5 KiB
Go
97 lines
2.5 KiB
Go
package crypto
|
|
|
|
import (
|
|
cryptorand "crypto/rand"
|
|
"encoding/base64"
|
|
"fmt"
|
|
|
|
"github.com/nais/liberator/pkg/keygen"
|
|
"golang.org/x/crypto/chacha20poly1305"
|
|
|
|
"github.com/nais/wonderwall/pkg/config"
|
|
)
|
|
|
|
const (
|
|
KeySize = chacha20poly1305.KeySize
|
|
|
|
// MaxPlaintextSize is set to 64 MB, which is a fairly generous limit. The implementation in x/crypto/xchacha20poly1305 has a plaintext limit to 256 GB.
|
|
// We generally only handle data that is stored within a cookie or a session store, i.e. it should be reasonably small.
|
|
// In most cases the data is around 4 KB or less, mostly depending on the length of the tokens returned from the identity provider.
|
|
MaxPlaintextSize = 64 * 1024 * 1024
|
|
)
|
|
|
|
type crypter struct {
|
|
key []byte
|
|
}
|
|
|
|
type Crypter interface {
|
|
Encrypt([]byte) ([]byte, error)
|
|
Decrypt([]byte) ([]byte, error)
|
|
}
|
|
|
|
func NewCrypter(key []byte) Crypter {
|
|
return &crypter{
|
|
key: key,
|
|
}
|
|
}
|
|
|
|
func EncryptionKeyOrGenerate(cfg *config.Config) ([]byte, error) {
|
|
key, err := base64.StdEncoding.DecodeString(cfg.EncryptionKey)
|
|
if err != nil {
|
|
if len(cfg.EncryptionKey) > 0 {
|
|
return nil, fmt.Errorf("decode encryption key: %w", err)
|
|
}
|
|
}
|
|
|
|
if len(key) == 0 {
|
|
key, err = keygen.Keygen(KeySize)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("generate random encryption key: %w", err)
|
|
}
|
|
}
|
|
|
|
if len(key) != chacha20poly1305.KeySize {
|
|
return nil, fmt.Errorf("bad key length (expected %d, got %d)", chacha20poly1305.KeySize, len(key))
|
|
}
|
|
|
|
return key, nil
|
|
}
|
|
|
|
// Encrypt encrypts a plaintext with XChaCha20-Poly1305.
|
|
func (c *crypter) Encrypt(plaintext []byte) ([]byte, error) {
|
|
aead, err := chacha20poly1305.NewX(c.key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
plaintextSize := len(plaintext)
|
|
if plaintextSize > MaxPlaintextSize {
|
|
return nil, fmt.Errorf("crypter: plaintext too large (%d > %d)", plaintextSize, MaxPlaintextSize)
|
|
}
|
|
|
|
// Select a random nonce, and leave capacity for the ciphertext.
|
|
nonce := make([]byte, aead.NonceSize(), aead.NonceSize()+plaintextSize+aead.Overhead())
|
|
_, err = cryptorand.Read(nonce)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return aead.Seal(nonce, nonce, plaintext, nil), nil
|
|
}
|
|
|
|
// Decrypt decrypts a ciphertext encrypted with XChaCha20-Poly1305.
|
|
func (c *crypter) Decrypt(ciphertext []byte) ([]byte, error) {
|
|
aead, err := chacha20poly1305.NewX(c.key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(ciphertext) < aead.NonceSize() {
|
|
return nil, fmt.Errorf("ciphertext is too short")
|
|
}
|
|
|
|
// Split nonce and ciphertext.
|
|
nonce, encrypted := ciphertext[:aead.NonceSize()], ciphertext[aead.NonceSize():]
|
|
return aead.Open(nil, nonce, encrypted, nil)
|
|
}
|