Files
smart-search-back/pkg/crypto/crypto.go
vallyenfail d959dcca96 add service
2026-01-17 17:39:33 +03:00

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
}