69 lines
1.9 KiB
Python
69 lines
1.9 KiB
Python
"""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
|