"""Utilidades de cifrado para passwords (DPAPI en Windows, Fernet en Linux).""" import base64 import sys from pathlib import Path from typing import Optional from ..constants import DATA_DIR _IS_WINDOWS = sys.platform == "win32" _SECRET_FILE = DATA_DIR / ".secret" def _get_fernet(): from cryptography.fernet import Fernet DATA_DIR.mkdir(parents=True, exist_ok=True) if not _SECRET_FILE.exists(): key = Fernet.generate_key() _SECRET_FILE.write_bytes(key) try: _SECRET_FILE.chmod(0o600) except OSError: pass else: key = _SECRET_FILE.read_bytes() return Fernet(key) def encrypt_password(password: str) -> str: """Cifra una contraseña.""" if not password: return "" try: if _IS_WINDOWS: import win32crypt encrypted_bytes = win32crypt.CryptProtectData( password.encode("utf-8"), None, None, None, None, 0 ) return base64.b64encode(encrypted_bytes).decode("ascii") fernet = _get_fernet() return fernet.encrypt(password.encode("utf-8")).decode("ascii") except Exception as e: raise RuntimeError(f"Error cifrando contraseña: {e}") from e def decrypt_password(encrypted_password: str) -> str: """Descifra una contraseña.""" if not encrypted_password: return "" try: if _IS_WINDOWS: import win32crypt encrypted_bytes = base64.b64decode(encrypted_password) decrypted_bytes = win32crypt.CryptUnprotectData( encrypted_bytes, None, None, None, 0 )[1] return decrypted_bytes.decode("utf-8") fernet = _get_fernet() return fernet.decrypt(encrypted_password.encode("ascii")).decode("utf-8") except Exception as e: raise RuntimeError(f"Error descifrando contraseña: {e}") from e