76 lines
2.7 KiB
Python
76 lines
2.7 KiB
Python
import base64
|
|
import logging
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
from typing import Optional, Tuple
|
|
|
|
from cryptography.hazmat.primitives import serialization
|
|
from cryptography.hazmat.primitives.asymmetric import padding
|
|
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPublicKey
|
|
|
|
from config.settings import Settings
|
|
|
|
CRYPT4_PREFIX = "happ://crypt4/"
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def _load_crypt4_public_key() -> Optional[RSAPublicKey]:
|
|
"""Load and cache the happ crypt4 public key from the project root."""
|
|
pem_path = Path(__file__).resolve().parent.parent.parent / "happ-crypt4.pem"
|
|
try:
|
|
pem_bytes = pem_path.read_bytes()
|
|
key = serialization.load_pem_public_key(pem_bytes)
|
|
return key if isinstance(key, RSAPublicKey) else None
|
|
except FileNotFoundError:
|
|
logging.error("Crypt4 public key file not found at %s", pem_path)
|
|
except Exception as exc:
|
|
logging.error("Failed to load crypt4 public key: %s", exc, exc_info=True)
|
|
return None
|
|
|
|
|
|
def _encrypt_raw_link(raw_link: str) -> Optional[str]:
|
|
"""Encrypt the raw subscription URL with RSA PKCS#1 v1.5 and return base64 payload."""
|
|
public_key = _load_crypt4_public_key()
|
|
if not public_key:
|
|
return None
|
|
|
|
try:
|
|
encrypted = public_key.encrypt(raw_link.encode("utf-8"), padding.PKCS1v15())
|
|
return base64.urlsafe_b64encode(encrypted).decode("utf-8")
|
|
except Exception as exc:
|
|
logging.error("Failed to encrypt config link with crypt4: %s", exc, exc_info=True)
|
|
return None
|
|
|
|
|
|
def prepare_config_links(settings: Settings, raw_link: Optional[str]) -> Tuple[Optional[str], Optional[str]]:
|
|
"""
|
|
Build the user-facing connection key and the URL for the connect button.
|
|
|
|
Returns (display_link, button_link). When CRYPT4 is enabled the display link
|
|
is encrypted and prefixed with happ://crypt4/, and the button link is wrapped
|
|
with CRYPT4_REDIRECT_URL if provided.
|
|
"""
|
|
if not raw_link:
|
|
return None, None
|
|
|
|
cleaned = raw_link.strip()
|
|
if not cleaned:
|
|
return None, None
|
|
|
|
display_link = cleaned
|
|
button_link = cleaned
|
|
|
|
if settings.CRYPT4_ENABLED:
|
|
encrypted_payload = _encrypt_raw_link(cleaned)
|
|
if encrypted_payload:
|
|
display_link = f"{CRYPT4_PREFIX}{encrypted_payload}"
|
|
button_link = display_link
|
|
else:
|
|
logging.error("CRYPT4_ENABLED is set but encryption failed; using raw link as fallback.")
|
|
|
|
redirect_base = (settings.CRYPT4_REDIRECT_URL or "").strip()
|
|
if redirect_base and settings.CRYPT4_ENABLED and display_link:
|
|
button_link = f"{redirect_base}{display_link}"
|
|
|
|
return display_link, button_link
|