package settings import ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/base64" "fmt" "io" ) func seal(plaintext []byte, key []byte) (nonce []byte, ciphertext []byte, err error) { block, err := aes.NewCipher(key) if err != nil { return nil, nil, fmt.Errorf("aes cipher: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { return nil, nil, fmt.Errorf("gcm: %w", err) } nonce = make([]byte, gcm.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return nil, nil, fmt.Errorf("nonce: %w", err) } ciphertext = gcm.Seal(nil, nonce, plaintext, nil) return nonce, ciphertext, nil } func open(nonce []byte, ciphertext []byte, key []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, fmt.Errorf("aes cipher: %w", err) } gcm, err := cipher.NewGCM(block) if err != nil { return nil, fmt.Errorf("gcm: %w", err) } plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { return nil, fmt.Errorf("decrypt: %w", err) } return plaintext, nil } func decodeKey(base64Key string) ([]byte, error) { key, err := base64.StdEncoding.DecodeString(base64Key) if err != nil { return nil, fmt.Errorf("decode key: %w", err) } if length := len(key); length != 16 && length != 24 && length != 32 { return nil, fmt.Errorf("key must be 16, 24, or 32 bytes after base64 decode, got %d", length) } return key, nil }