encryption.go
package helper
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"fmt"
"io"
)
// hashTo32Bytes will compute a cryptographically useful hash of the input string.
func hashTo32Bytes(input string) []byte {
data := sha256.Sum256([]byte(input))
return data[0:]
}
// DecryptString takes two strings, cryptoText and keyString.
// cryptoText is the text to be decrypted and the keyString is the key to use for the decryption.
// The function will output the resulting plain text string with an error variable.
func DecryptString(cryptoText string, keyString string) (plainTextString string, err error) {
encrypted, err := base64.URLEncoding.DecodeString(cryptoText)
if err != nil {
return "", err
}
if len(encrypted) < aes.BlockSize {
return "", fmt.Errorf("cipherText too short. It decodes to %v bytes but the minimum length is 16", len(encrypted))
}
decrypted, err := DecryptAES(hashTo32Bytes(keyString), encrypted)
if err != nil {
return "", err
}
return string(decrypted), nil
}
// DecryptAES ...
func DecryptAES(key, data []byte) ([]byte, error) {
// split the input up in to the IV seed and then the actual encrypted data.
iv := data[:aes.BlockSize]
data = data[aes.BlockSize:]
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(data, data)
return data, nil
}
// EncryptString takes two string, plainText and keyString.
// plainText is the text that needs to be encrypted by keyString.
// The function will output the resulting crypto text and an error variable.
func EncryptString(plainText string, keyString string) (cipherTextString string, err error) {
key := hashTo32Bytes(keyString)
encrypted, err := encryptAES(key, []byte(plainText))
if err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(encrypted), nil
}
func encryptAES(key, data []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
// create two 'windows' in to the output slice.
output := make([]byte, aes.BlockSize+len(data))
iv := output[:aes.BlockSize]
encrypted := output[aes.BlockSize:]
// populate the IV slice with random data.
if _, err = io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
stream := cipher.NewCFBEncrypter(block, iv)
// note that encrypted is still a window in to the output slice
stream.XORKeyStream(encrypted, data)
return output, nil
}