from cryptography.fernet import Fernet, InvalidToken def get_fernet() -> Fernet: key = _resolve_crypto_key() if not key: raise ValueError("CRYPTO_KEY not configured. Set INFRASYNTH_SECURITY['CRYPTO_KEY'] or CRYPTO_KEY env var.") return Fernet(key.encode() if isinstance(key, str) else key) def _resolve_crypto_key() -> str | None: from django.conf import settings key = getattr(settings, "INFRASYNTH_SECURITY", {}).get("CRYPTO_KEY") if not key: import os key = os.environ.get("CRYPTO_KEY") return key def encrypt(value: str) -> str: f = get_fernet() return f.encrypt(value.encode()).decode() def decrypt(token: str) -> str: f = get_fernet() try: return f.decrypt(token.encode()).decode() except InvalidToken: raise ValueError("Invalid Fernet token or corrupted data.") def generate_key() -> str: return Fernet.generate_key().decode() def rotate_keys(old_key: str, new_key: str, tokens: list[str]) -> list[str]: old_fernet = Fernet(old_key.encode() if isinstance(old_key, str) else old_key) new_fernet = Fernet(new_key.encode() if isinstance(new_key, str) else new_key) re_encrypted = [] for token in tokens: plaintext = old_fernet.decrypt(token.encode()).decode() re_encrypted.append(new_fernet.encrypt(plaintext.encode()).decode()) return re_encrypted