""" Encryption service — column-level encryption for sensitive fields. Used by Phase 2+ for MFA secrets, API keys, and other sensitive data. Uses Django's signing framework with Fernet-style encryption. """ import base64 import hashlib import hmac from django.conf import settings from django.utils.crypto import get_random_string def _get_key() -> bytes: """Derive a 32-byte encryption key from Django SECRET_KEY.""" return hashlib.sha256(settings.SECRET_KEY.encode()).digest() def encrypt_field(plaintext: str) -> str: """ Encrypt a string value for safe database storage. Uses HMAC-based encryption with the Django SECRET_KEY. Returns a base64-encoded ciphertext string. """ key = _get_key() nonce = get_random_string(16).encode() mac = hmac.new(key, nonce + plaintext.encode(), hashlib.sha256).digest() payload = nonce + mac + plaintext.encode() return base64.urlsafe_b64encode(payload).decode() def decrypt_field(ciphertext: str) -> str: """ Decrypt a previously encrypted field value. Raises ValueError if the ciphertext is tampered with. """ key = _get_key() payload = base64.urlsafe_b64decode(ciphertext.encode()) nonce = payload[:16] stored_mac = payload[16:48] plaintext_bytes = payload[48:] expected_mac = hmac.new(key, nonce + plaintext_bytes, hashlib.sha256).digest() if not hmac.compare_digest(stored_mac, expected_mac): raise ValueError("Decryption failed: ciphertext has been tampered with.") return plaintext_bytes.decode() def hash_token(token: str) -> str: """One-way hash for tokens (API keys, device IDs, etc.).""" return hashlib.sha256(token.encode()).hexdigest()