mirror of
https://github.com/andwati/telecom-platform.git
synced 2026-10-18 02:52:53 +00:00
feat: Add encryption service with AES-GCM, field-level encryption, and PBKDF2 key derivation
- Add EncryptionService struct with masterKey and gcm cipher fields - Add EncryptionConfig with MasterKey and KeyDerivationSalt fields - Add NewEncryptionService constructor with PBKDF2 key derivation, 100000 iterations, and AES-GCM cipher initialization - Add Encrypt and Decrypt methods with nonce generation and validation - Add EncryptString and DecryptString methods with base64 encoding - Add FieldEncryptor struct
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
package security
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
)
|
||||
|
||||
// EncryptionService provides data encryption at rest
|
||||
type EncryptionService struct {
|
||||
masterKey []byte
|
||||
gcm cipher.AEAD
|
||||
}
|
||||
|
||||
// EncryptionConfig configures encryption
|
||||
type EncryptionConfig struct {
|
||||
MasterKey string
|
||||
KeyDerivationSalt string
|
||||
}
|
||||
|
||||
// NewEncryptionService creates a new encryption service
|
||||
func NewEncryptionService(config EncryptionConfig) (*EncryptionService, error) {
|
||||
salt := []byte(config.KeyDerivationSalt)
|
||||
if len(salt) == 0 {
|
||||
salt = []byte("telecom-platform-default-salt")
|
||||
}
|
||||
|
||||
key := pbkdf2.Key([]byte(config.MasterKey), salt, 100000, 32, sha256.New)
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &EncryptionService{
|
||||
masterKey: key,
|
||||
gcm: gcm,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts plaintext data
|
||||
func (e *EncryptionService) Encrypt(plaintext []byte) ([]byte, error) {
|
||||
nonce := make([]byte, e.gcm.NonceSize())
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := e.gcm.Seal(nonce, nonce, plaintext, nil)
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts ciphertext data
|
||||
func (e *EncryptionService) Decrypt(ciphertext []byte) ([]byte, error) {
|
||||
if len(ciphertext) < e.gcm.NonceSize() {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
|
||||
nonce := ciphertext[:e.gcm.NonceSize()]
|
||||
ciphertext = ciphertext[e.gcm.NonceSize():]
|
||||
|
||||
plaintext, err := e.gcm.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// EncryptString encrypts a string and returns base64
|
||||
func (e *EncryptionService) EncryptString(plaintext string) (string, error) {
|
||||
encrypted, err := e.Encrypt([]byte(plaintext))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(encrypted), nil
|
||||
}
|
||||
|
||||
// DecryptString decrypts a base64 string
|
||||
func (e *EncryptionService) DecryptString(ciphertext string) (string, error) {
|
||||
data, err := base64.StdEncoding.DecodeString(ciphertext)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
decrypted, err := e.Decrypt(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(decrypted), nil
|
||||
}
|
||||
|
||||
// FieldEncryptor provides field-level encryption for sensitive data
|
||||
type FieldEncryptor struct {
|
||||
service *EncryptionService
|
||||
fields map[string]bool
|
||||
}
|
||||
|
||||
// NewFieldEncryptor creates a field encryptor
|
||||
func NewFieldEncryptor(service *EncryptionService, sensitiveFields []string) *FieldEncryptor {
|
||||
fields := make(map[string]bool)
|
||||
for _, f := range sensitiveFields {
|
||||
fields[f] = true
|
||||
}
|
||||
return &FieldEncryptor{
|
||||
service: service,
|
||||
fields: fields,
|
||||
}
|
||||
}
|
||||
|
||||
// IsSensitive checks if a field is sensitive
|
||||
func (fe *FieldEncryptor) IsSensitive(field string) bool {
|
||||
return fe.fields[field]
|
||||
}
|
||||
|
||||
// EncryptField encrypts a field value if sensitive
|
||||
func (fe *FieldEncryptor) EncryptField(field, value string) (string, error) {
|
||||
if !fe.IsSensitive(field) {
|
||||
return value, nil
|
||||
}
|
||||
return fe.service.EncryptString(value)
|
||||
}
|
||||
|
||||
// DecryptField decrypts a field value if sensitive
|
||||
func (fe *FieldEncryptor) DecryptField(field, value string) (string, error) {
|
||||
if !fe.IsSensitive(field) {
|
||||
return value, nil
|
||||
}
|
||||
return fe.service.DecryptString(value)
|
||||
}
|
||||
|
||||
// DefaultSensitiveFields returns commonly sensitive fields
|
||||
func DefaultSensitiveFields() []string {
|
||||
return []string{
|
||||
"ssn", "social_security_number",
|
||||
"credit_card", "card_number", "cvv",
|
||||
"password", "secret", "api_key",
|
||||
"phone", "email", "address",
|
||||
"date_of_birth", "dob",
|
||||
"bank_account", "routing_number",
|
||||
"iccid", "imsi", "msisdn",
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user