Saas-Base / core /services /encryption.py
rsnarsna
first commit
667aacd
Raw
History Blame Contribute Delete
1.71 kB
"""
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()