func (c *crypter) EncryptString(plaintext string) (string, error) {
	plainbytes := []byte(plaintext)
	cipherbytes, err := c.encrypt(plainbytes)
	if err != nil {
		return "", err
	}
	hmacbytes := c.hmac(cipherbytes)
	messagebytes := make([]byte, HmacOutputLength+len(cipherbytes))
	copy(messagebytes[:len(hmacbytes)], hmacbytes)
	copy(messagebytes[len(hmacbytes):], cipherbytes)
	return base64.StdEncoding.EncodeToString(messagebytes), nil
}
EncryptString converts plaintext to signed, base 64 encoded ciphertext by
encrypting the plaintext using AES-256 and prepending a Hmac SHA-512
signature.
package crypto
import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/rand"
	"crypto/sha512"
	"encoding/base64"
	"fmt"
	"io"
	"os"
)
const (
	SymetricKeyLength = 32
	HmacKeyLength = 128
	HmacOutputLength = 64
)
type Crypter interface {
	EncryptString(plaintext string) (string, error)
	DecryptString(message string) (string, error)
}
type crypter struct {
	hmacKey, symetricKey []byte
	block                cipher.Block
}
func NewCrypter(key io.Reader) (*crypter, error) {
	crypter := &crypter{}
	crypter.hmacKey = make([]byte, HmacKeyLength)
	if _, err := io.ReadFull(key, crypter.hmacKey); err != nil {
		return crypter, err
	}
	crypter.symetricKey = make([]byte, SymetricKeyLength)
	if _, err := io.ReadFull(key, crypter.symetricKey); err != nil {
		return crypter, err
	}
	block, err := aes.NewCipher(crypter.symetricKey)
	if err != nil {
		return crypter, err
	}
	crypter.block = block
	return crypter, nil
}
func NewRandomCrypter() (*crypter, error) {
	return NewCrypter(rand.Reader)
}
func NewCrypterFromFile(filepath string) (*crypter, error) {
	stat, err := os.Stat(filepath)
	if err != nil {
		return nil, err
	}
	if mode := stat.Mode(); mode != 0600 && mode != 0400 {
		return nil, CrypterError{"incorrect file mode for key"}
	}
	file, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer file.Close()
	decoder := base64.NewDecoder(base64.StdEncoding, file)
	return NewCrypter(decoder)
}
func (c *crypter) hmac(message []byte) []byte {
	signer := hmac.New(sha512.New, c.hmacKey)
	signer.Write(message)
	return signer.Sum(nil)
}
func (c *crypter) encrypt(plainbytes []byte) ([]byte, error) {
	size := aes.BlockSize + len(plainbytes)
	if extra := len(plainbytes) % aes.BlockSize; extra != 0 {
		size += aes.BlockSize - extra
	}
	cipherbytes := make([]byte, size)
	copy(cipherbytes[aes.BlockSize:], plainbytes)
	iv := cipherbytes[:aes.BlockSize]
	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
		return []byte{}, err
	}
	mode := cipher.NewCBCEncrypter(c.block, iv)
	mode.CryptBlocks(cipherbytes[aes.BlockSize:], cipherbytes[aes.BlockSize:])
	return cipherbytes, nil
}
func (c *crypter) decrypt(cipherbytes []byte) ([]byte, error) {
	if len(cipherbytes) < aes.BlockSize*2 {
		return []byte{}, CrypterError{"cipherbytes is too short"}
	}
	if len(cipherbytes)%aes.BlockSize != 0 {
		return []byte{}, CrypterError{"cipherbytes is not a multiple of the block size"}
	}
	iv := cipherbytes[:aes.BlockSize]
	plainbytes := make([]byte, len(cipherbytes)-aes.BlockSize)
	mode := cipher.NewCBCDecrypter(c.block, iv)
	mode.CryptBlocks(plainbytes, cipherbytes[aes.BlockSize:])
	plainbytes = bytes.TrimRight(plainbytes, "\x00")
	return plainbytes, nil
}