From 79fa56f25d056ad12a17bfac514ca989c8e6051b Mon Sep 17 00:00:00 2001 From: nutcas3 Date: Sun, 3 May 2026 10:51:47 +0300 Subject: [PATCH] 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 --- .../internal/security/encryption.go | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 apps/carrier-connector/internal/security/encryption.go diff --git a/apps/carrier-connector/internal/security/encryption.go b/apps/carrier-connector/internal/security/encryption.go new file mode 100644 index 0000000..66c0c7a --- /dev/null +++ b/apps/carrier-connector/internal/security/encryption.go @@ -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", + } +}