add service
This commit is contained in:
125
pkg/crypto/crypto.go
Normal file
125
pkg/crypto/crypto.go
Normal file
@@ -0,0 +1,125 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user