126 lines
2.7 KiB
Go
126 lines
2.7 KiB
Go
package crypto
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/hmac"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"crypto/sha512"
|
|
"encoding/hex"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
type Crypto struct {
|
|
secret string
|
|
}
|
|
|
|
func NewCrypto(secret string) *Crypto {
|
|
return &Crypto{secret: secret}
|
|
}
|
|
|
|
func (c *Crypto) EmailHash(email string) string {
|
|
if email == "" {
|
|
return email
|
|
}
|
|
normalized := strings.TrimSpace(strings.ToLower(email))
|
|
h := hmac.New(sha256.New, []byte(c.secret))
|
|
h.Write([]byte(normalized))
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|
|
|
|
func PasswordHash(password string) string {
|
|
h := sha512.New()
|
|
h.Write([]byte(password))
|
|
return hex.EncodeToString(h.Sum(nil))
|
|
}
|
|
|
|
func (c *Crypto) getKey() []byte {
|
|
h := sha256.New()
|
|
h.Write([]byte(c.secret))
|
|
return h.Sum(nil)
|
|
}
|
|
|
|
func (c *Crypto) Encrypt(plaintext string) (string, error) {
|
|
if plaintext == "" {
|
|
return plaintext, nil
|
|
}
|
|
|
|
key := c.getKey()
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create cipher: %w", err)
|
|
}
|
|
|
|
aesGCM, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create GCM: %w", err)
|
|
}
|
|
|
|
iv := make([]byte, aesGCM.NonceSize())
|
|
if _, err := rand.Read(iv); err != nil {
|
|
return "", fmt.Errorf("failed to generate IV: %w", err)
|
|
}
|
|
|
|
ciphertext := aesGCM.Seal(nil, iv, []byte(plaintext), nil)
|
|
|
|
tag := ciphertext[len(ciphertext)-aesGCM.Overhead():]
|
|
cipherOnly := ciphertext[:len(ciphertext)-aesGCM.Overhead()]
|
|
|
|
return fmt.Sprintf("%s:%s:%s",
|
|
hex.EncodeToString(iv),
|
|
hex.EncodeToString(tag),
|
|
hex.EncodeToString(cipherOnly),
|
|
), nil
|
|
}
|
|
|
|
func (c *Crypto) Decrypt(ciphertext string) (string, error) {
|
|
if ciphertext == "" {
|
|
return ciphertext, nil
|
|
}
|
|
|
|
parts := strings.Split(ciphertext, ":")
|
|
if len(parts) != 3 {
|
|
return "", errors.New("invalid encrypted value format")
|
|
}
|
|
|
|
ivHex, tagHex, cipherHex := parts[0], parts[1], parts[2]
|
|
|
|
iv, err := hex.DecodeString(ivHex)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to decode IV: %w", err)
|
|
}
|
|
|
|
tag, err := hex.DecodeString(tagHex)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to decode tag: %w", err)
|
|
}
|
|
|
|
cipherOnly, err := hex.DecodeString(cipherHex)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to decode ciphertext: %w", err)
|
|
}
|
|
|
|
key := c.getKey()
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create cipher: %w", err)
|
|
}
|
|
|
|
aesGCM, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to create GCM: %w", err)
|
|
}
|
|
|
|
ciphertextWithTag := append(cipherOnly, tag...)
|
|
|
|
plaintext, err := aesGCM.Open(nil, iv, ciphertextWithTag, nil)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to decrypt: %w", err)
|
|
}
|
|
|
|
return string(plaintext), nil
|
|
}
|