Skip to content
Snippets Groups Projects
encryption.go 4.74 KiB
Newer Older
Francé Wilke's avatar
Francé Wilke committed
package encryption

import (
	"crypto/aes"
	"crypto/cipher"
Francé Wilke's avatar
Francé Wilke committed
	"crypto/hmac"
	"crypto/md5"
	"crypto/rand"
Francé Wilke's avatar
Francé Wilke committed
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
Johan de Klerk's avatar
Johan de Klerk committed
	"encoding/json"
Francé Wilke's avatar
Francé Wilke committed
	"fmt"
	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
	"io"
Francé Wilke's avatar
Francé Wilke committed
)

func Hash(input string, key string) string {
	keyBytes := []byte(key)
	h := hmac.New(sha256.New, keyBytes)
	h.Write([]byte(input))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

// GenerateHashFromObject using HMAC with SHA-256
func GenerateHashFromObject(object any, secret string) (string, error) {
	// Base64 Encode body
	var buf bytes.Buffer
	encoder := base64.NewEncoder(base64.StdEncoding, &buf)
	defer encoder.Close()

	err := json.NewEncoder(encoder).Encode(object)
	if err != nil {
		return "", err
	}
	encodedBody := buf.String()

	// Sign encoded body with secret
	hash := hmac.New(sha256.New, []byte(secret))
	hash.Write([]byte(encodedBody))
	hashedBody := hex.EncodeToString(hash.Sum(nil))
	return hashedBody, nil
Francé Wilke's avatar
Francé Wilke committed
func Md5HashString(bytesToHash []byte) string {
	hash := md5.Sum(bytesToHash)
	hashString := fmt.Sprintf("%X", hash)
	return hashString
}
Johan de Klerk's avatar
Johan de Klerk committed
func EncryptStruct(object any, key string) (string, error) {
	if len(key) != 32 {
		return "", errors.Error("key should be 32 bytes")
Johan de Klerk's avatar
Johan de Klerk committed
	}

	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return "", err
	}

	aesGcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}

	nonce := make([]byte, aesGcm.NonceSize())
	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
		return "", err
	}

	jsonValue, err := json.Marshal(object)
	if err != nil {
		return "", err
	}

	encryptedValue := string(aesGcm.Seal(nonce, nonce, jsonValue, nil))
	return base64.StdEncoding.EncodeToString([]byte(encryptedValue)), nil
}

func DecryptStruct(encryptedStruct string, key string, object any) error {
	if len(key) != 32 {
		return errors.Error("key should be 32 bytes")
Johan de Klerk's avatar
Johan de Klerk committed
	}

	decodedStruct, _ := base64.StdEncoding.DecodeString(encryptedStruct)
	decodedStructString := string(decodedStruct)

	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return err
	}

	aesGcm, err := cipher.NewGCM(block)
	if err != nil {
		return err
	}

	nonceSize := aesGcm.NonceSize()
	if len(decodedStructString) < nonceSize {
		return errors.Error("ciphertext too short")
Johan de Klerk's avatar
Johan de Klerk committed
	}

	nonce, ciphertext := decodedStructString[:nonceSize], decodedStructString[nonceSize:]
	value, err := aesGcm.Open(nil, []byte(nonce), []byte(ciphertext), nil)

	err = json.Unmarshal(value, object)
	if err != nil {
		return err
	}
	return nil
}

func EncryptByteArray(byteArray []byte, key string) (string, error) {
	if len(key) != 32 {
		return "", errors.Error("key should be 32 bytes")
	}

	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return "", err
	}

	aesGcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}

	nonce := make([]byte, aesGcm.NonceSize())
	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
		return "", err
	}

	encryptedValue := string(aesGcm.Seal(nonce, nonce, byteArray, nil))
	return base64.StdEncoding.EncodeToString([]byte(encryptedValue)), nil
}

func DecryptByteArray(encryptedByteArray []byte, key string, object any) error {
	if len(key) != 32 {
		return errors.Error("key should be 32 bytes")
	}

	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return err
	}

	aesGcm, err := cipher.NewGCM(block)
	if err != nil {
		return err
	}

	nonceSize := aesGcm.NonceSize()
	if len(encryptedByteArray) < nonceSize {
		return errors.Error("ciphertext too short")
	}

	nonce, ciphertext := encryptedByteArray[:nonceSize], encryptedByteArray[nonceSize:]
	value, err := aesGcm.Open(nil, nonce, ciphertext, nil)

	err = json.Unmarshal(value, object)
	if err != nil {
		return err
	}
	return nil
}

func Encrypt(plaintext string, key string) (string, error) {
	if len(key) != 32 {
		return "", errors.Error("key should be 32 bytes")
	}

	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return "", err
	}

	aesGcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}

	nonce := make([]byte, aesGcm.NonceSize())
	if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
		return "", err
	}

	return string(aesGcm.Seal(nonce, nonce, []byte(plaintext), nil)), nil
func Decrypt(ciphertext string, key string) (string, error) {
	if len(key) != 32 {
		return "", errors.Error("key should be 32 bytes")
	}

	block, err := aes.NewCipher([]byte(key))
	if err != nil {
		return "", err
	aesGcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	nonceSize := aesGcm.NonceSize()
	if len(ciphertext) < nonceSize {
		return "", errors.Error("ciphertext too short")
	}

	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
	value, err := aesGcm.Open(nil, []byte(nonce), []byte(ciphertext), nil)
	return string(value), err