updated crypt
This commit is contained in:
@@ -253,7 +253,7 @@ async def process_successful_payment(session: AsyncSession, bot: Bot,
|
||||
traffic_label = (
|
||||
str(int(traffic_amount_gb)) if float(traffic_amount_gb).is_integer() else f"{traffic_amount_gb:g}"
|
||||
)
|
||||
config_link_display, connect_button_url = prepare_config_links(
|
||||
config_link_display, connect_button_url = await prepare_config_links(
|
||||
settings, activation_details.get("subscription_url") if activation_details else None
|
||||
)
|
||||
config_link_text = config_link_display or _("config_link_not_available")
|
||||
|
||||
@@ -88,7 +88,7 @@ async def request_trial_confirmation_handler(
|
||||
pass
|
||||
|
||||
end_date_obj = activation_result.get("end_date")
|
||||
config_link_display_for_trial, connect_button_url_for_trial = prepare_config_links(
|
||||
config_link_display_for_trial, connect_button_url_for_trial = await prepare_config_links(
|
||||
settings, activation_result.get("subscription_url")
|
||||
)
|
||||
config_link_for_trial = config_link_display_for_trial or _("config_link_not_available")
|
||||
@@ -240,7 +240,7 @@ async def confirm_activate_trial_handler(
|
||||
pass
|
||||
|
||||
end_date_obj = activation_result.get("end_date")
|
||||
config_link_display_for_trial, connect_button_url_for_trial = prepare_config_links(
|
||||
config_link_display_for_trial, connect_button_url_for_trial = await prepare_config_links(
|
||||
settings, activation_result.get("subscription_url")
|
||||
)
|
||||
config_link_for_trial = config_link_display_for_trial or _("config_link_not_available")
|
||||
|
||||
@@ -190,7 +190,7 @@ class CryptoPayService:
|
||||
_ = lambda k, **kw: i18n.gettext(lang, k, **kw)
|
||||
|
||||
raw_config_link = activation.get("subscription_url") if activation else None
|
||||
display_link, button_link = prepare_config_links(settings, raw_config_link)
|
||||
display_link, button_link = await prepare_config_links(settings, raw_config_link)
|
||||
config_link_text = display_link or _("config_link_not_available")
|
||||
final_end = activation.get("end_date")
|
||||
applied_days = 0
|
||||
|
||||
@@ -319,7 +319,7 @@ class FreeKassaService:
|
||||
_ = lambda k, **kw: self.i18n.gettext(lang, k, **kw) if self.i18n else k
|
||||
|
||||
raw_config_link = activation.get("subscription_url") if activation else None
|
||||
config_link_display, connect_button_url = prepare_config_links(self.settings, raw_config_link)
|
||||
config_link_display, connect_button_url = await prepare_config_links(self.settings, raw_config_link)
|
||||
config_link_text = config_link_display or _("config_link_not_available")
|
||||
final_end = activation.get("end_date") if activation else None
|
||||
months = payment.subscription_duration_months or 1
|
||||
|
||||
@@ -562,3 +562,20 @@ class PanelApiService:
|
||||
if response_data and not response_data.get("error") and "response" in response_data:
|
||||
return response_data.get("response")
|
||||
return None
|
||||
|
||||
async def encrypt_happ_link(self, link_to_encrypt: str) -> Optional[str]:
|
||||
"""Encrypt a subscription link using the panel's happ crypt4 API.
|
||||
|
||||
Returns the encrypted link string or None if encryption failed.
|
||||
"""
|
||||
payload = {"linkToEncrypt": link_to_encrypt}
|
||||
response_data = await self._request(
|
||||
"POST",
|
||||
"/system/tools/happ/encrypt",
|
||||
json=payload,
|
||||
log_full_response=False
|
||||
)
|
||||
if response_data and not response_data.get("error") and "response" in response_data:
|
||||
return response_data.get("response", {}).get("encryptedLink")
|
||||
logging.error(f"Failed to encrypt happ link. Response: {response_data}")
|
||||
return None
|
||||
|
||||
@@ -215,7 +215,7 @@ class PlategaService:
|
||||
_ = lambda k, **kw: self.i18n.gettext(lang, k, **kw) if self.i18n else k
|
||||
|
||||
raw_config_link = activation.get("subscription_url") if activation else None
|
||||
config_link_display, connect_button_url = prepare_config_links(self.settings, raw_config_link)
|
||||
config_link_display, connect_button_url = await prepare_config_links(self.settings, raw_config_link)
|
||||
config_link_text = config_link_display or _("config_link_not_available")
|
||||
final_end = activation.get("end_date") if activation else None
|
||||
applied_days = 0
|
||||
|
||||
@@ -233,7 +233,7 @@ class SeverPayService:
|
||||
_ = lambda k, **kw: self.i18n.gettext(lang, k, **kw) if self.i18n else k
|
||||
|
||||
raw_config_link = activation.get("subscription_url") if activation else None
|
||||
config_link_display, connect_button_url = prepare_config_links(self.settings, raw_config_link)
|
||||
config_link_display, connect_button_url = await prepare_config_links(self.settings, raw_config_link)
|
||||
config_link_text = config_link_display or _("config_link_not_available")
|
||||
final_end = activation.get("end_date") if activation else None
|
||||
applied_days = 0
|
||||
|
||||
@@ -123,7 +123,7 @@ class StarsService:
|
||||
_ = lambda k, **kw: i18n.gettext(current_lang, k, **kw) if i18n else k
|
||||
|
||||
raw_config_link = activation_details.get("subscription_url") if activation_details else None
|
||||
config_link_display, connect_button_url = prepare_config_links(self.settings, raw_config_link)
|
||||
config_link_display, connect_button_url = await prepare_config_links(self.settings, raw_config_link)
|
||||
config_link_text = config_link_display or _("config_link_not_available")
|
||||
|
||||
if sale_mode == "traffic":
|
||||
|
||||
@@ -902,7 +902,7 @@ class SubscriptionService:
|
||||
else None
|
||||
)
|
||||
config_link_raw = panel_user_data.get("subscriptionUrl")
|
||||
display_link, connect_button_url = prepare_config_links(self.settings, config_link_raw)
|
||||
display_link, connect_button_url = await prepare_config_links(self.settings, config_link_raw)
|
||||
hwid_limit = panel_user_data.get("hwidDeviceLimit")
|
||||
if hwid_limit is None:
|
||||
hwid_limit = self.settings.USER_HWID_DEVICE_LIMIT
|
||||
|
||||
@@ -1,48 +1,22 @@
|
||||
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
|
||||
from bot.services.panel_api_service import PanelApiService
|
||||
|
||||
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)
|
||||
async def _encrypt_raw_link(settings: Settings, raw_link: str) -> Optional[str]:
|
||||
"""Encrypt the raw subscription URL using the panel's happ crypt4 API."""
|
||||
async with PanelApiService(settings) as panel_service:
|
||||
encrypted_link = await panel_service.encrypt_happ_link(raw_link)
|
||||
if encrypted_link:
|
||||
return encrypted_link
|
||||
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.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]]:
|
||||
async 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.
|
||||
|
||||
@@ -61,7 +35,7 @@ def prepare_config_links(settings: Settings, raw_link: Optional[str]) -> Tuple[O
|
||||
button_link = cleaned
|
||||
|
||||
if settings.CRYPT4_ENABLED:
|
||||
encrypted_payload = _encrypt_raw_link(cleaned)
|
||||
encrypted_payload = await _encrypt_raw_link(settings, cleaned)
|
||||
if encrypted_payload:
|
||||
display_link = f"{CRYPT4_PREFIX}{encrypted_payload}"
|
||||
button_link = display_link
|
||||
|
||||
Reference in New Issue
Block a user