from __future__ import annotations from cryptography.fernet import Fernet, InvalidToken from django.conf import settings class AiKeyEncryptionError(RuntimeError): """Raised when AI provider key encryption/decryption cannot proceed.""" PREFIX = "fernet:" def encrypt_api_key(raw_api_key: str) -> str: api_key = str(raw_api_key or "").strip() if not api_key: raise AiKeyEncryptionError("AI api_key cannot be blank") token = _fernet().encrypt(api_key.encode("utf-8")).decode("ascii") return f"{PREFIX}{token}" def decrypt_api_key(encrypted_api_key: str) -> str: encrypted = str(encrypted_api_key or "") if not encrypted: return "" if not encrypted.startswith(PREFIX): raise AiKeyEncryptionError("AI api_key is not stored in encrypted form") token = encrypted[len(PREFIX) :].encode("ascii") try: return _fernet().decrypt(token).decode("utf-8") except InvalidToken as exc: raise AiKeyEncryptionError("AI api_key cannot be decrypted") from exc def mask_secret(encrypted_api_key: str) -> str: return "********" if encrypted_api_key else "" def _fernet() -> Fernet: key = getattr(settings, "AI_KEY_ENCRYPTION_KEY", "") if not key: raise AiKeyEncryptionError("AI_KEY_ENCRYPTION_KEY is not configured") try: return Fernet(str(key).encode("ascii")) except (TypeError, ValueError) as exc: raise AiKeyEncryptionError("AI_KEY_ENCRYPTION_KEY is invalid") from exc