iris/crypto/gcm/gcm.go

135 lines
2.8 KiB
Go
Raw Normal View History

// Package gcm implements encryption/decription using the AES algorithm and the Galois/Counter Mode.
package gcm
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha512"
"encoding/hex"
)
// MustGenerateKey generates an aes key.
// It panics if any error occurred.
func MustGenerateKey() []byte {
aesKey, err := GenerateKey()
if err != nil {
panic(err)
}
return aesKey
}
// GenerateKey returns a random aes key.
func GenerateKey() ([]byte, error) {
key := make([]byte, 64)
n, err := rand.Read(key)
if err != nil {
return nil, err
}
return encode(key[:n]), nil
}
// Encrypt encrypts and authenticates the plain data and additional data
// and returns the ciphertext of it.
// It uses the AEAD cipher mode providing authenticated encryption with associated
// data.
// The same additional data must be kept the same for `Decrypt`.
func Encrypt(aesKey, data, additionalData []byte) ([]byte, error) {
key, err := decode(aesKey)
if err != nil {
return nil, err
}
h := sha512.New()
h.Write(key)
digest := encode(h.Sum(nil))
// key based on the hash itself, we have space because of sha512.
newKey, err := decode(digest[:64])
if err != nil {
return nil, err
}
// nonce based on the hash itself.
nonce, err := decode(digest[64:(64 + 24)])
if err != nil {
return nil, err
}
aData, err := decode(additionalData)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(newKey)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
ciphertext := encode(gcm.Seal(nil, nonce, data, aData))
return ciphertext, nil
}
// Decrypt decrypts and authenticates ciphertext, authenticates the
// additional data and, if successful, returns the resulting plain data.
// The additional data must match the value passed to `Encrypt`.
func Decrypt(aesKey, ciphertext, additionalData []byte) ([]byte, error) {
key, err := decode(aesKey)
if err != nil {
return nil, err
}
h := sha512.New()
h.Write(key)
digest := encode(h.Sum(nil))
newKey, err := decode(digest[:64])
if err != nil {
return nil, err
}
nonce, err := decode(digest[64:(64 + 24)])
if err != nil {
return nil, err
}
additionalData, err = decode(additionalData)
if err != nil {
return nil, err
}
block, err := aes.NewCipher(newKey)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
ciphertext, err = decode(ciphertext)
return gcm.Open(nil, nonce, ciphertext, additionalData)
}
func decode(src []byte) ([]byte, error) {
buf := make([]byte, hex.DecodedLen(len(src)))
n, err := hex.Decode(buf, src)
if err != nil {
return nil, err
}
return buf[:n], nil
}
func encode(src []byte) []byte {
buf := make([]byte, hex.EncodedLen(len(src)))
hex.Encode(buf, src)
return buf
}