Files
wonderwall/pkg/cryptutil/encrypt.go
2021-08-25 10:15:45 +02:00

91 lines
1.8 KiB
Go

package cryptutil
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"encoding/binary"
"fmt"
"time"
"github.com/nais/liberator/pkg/keygen"
)
type crypter struct {
key []byte
}
type Crypter interface {
Encrypt([]byte) ([]byte, error)
Decrypt([]byte) ([]byte, error)
}
func New(key []byte) Crypter {
return &crypter{
key: key,
}
}
// Generate an initialization vector for encryption.
// It consists of the current UNIX timestamp with nanoseconds, and four bytes of randomness.
func IV() ([]byte, error) {
stor := make([]byte, 0)
buf := bytes.NewBuffer(stor)
err := binary.Write(buf, binary.BigEndian, time.Now().UnixNano())
if err != nil {
return nil, err
}
// Pad nonce with 4 bytes
random, err := keygen.Keygen(4)
if err != nil {
return nil, err
}
err = binary.Write(buf, binary.BigEndian, random)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
// Encrypts a plaintext with AES-256-GCM.
// Returns 12 bytes of IV, and then N bytes of ciphertext.
func (c *crypter) Encrypt(plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(c.key)
if err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
nonce, err := IV()
if err != nil {
return nil, err
}
ciphertext := aesgcm.Seal(nil, nonce, plaintext, nil)
return append(nonce, ciphertext...), nil
}
// Decrypts a ciphertext encrypted with AES-256-GCM.
// The first 12 bytes of the ciphertext is assumed to be the IV.
func (c *crypter) Decrypt(ciphertext []byte) ([]byte, error) {
if len(ciphertext) <= 12 {
return nil, fmt.Errorf("string is too short")
}
block, err := aes.NewCipher(c.key)
if err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
return aesgcm.Open(nil, ciphertext[:12], ciphertext[12:], nil)
}