277 lines
15 KiB
Python
277 lines
15 KiB
Python
# Source: https://github.com/3ixi/CodeScripts/blob/main/cloud_auth.py
|
||
# Raw: https://raw.githubusercontent.com/3ixi/CodeScripts/main/cloud_auth.py
|
||
# Repo: 3ixi/CodeScripts
|
||
# Path: cloud_auth.py
|
||
# UploadedAt: 2025-09-23T14:07:43+08:00
|
||
# SHA256: eb2f681e407a991c4a909c9f58a74cd58726b3204bbaa0f721a9fc015d53551c
|
||
# Category: APP版/抓包
|
||
# Evidence: cookie/token/authorization/header
|
||
|
||
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
3iXi云函数认证模块
|
||
用于与3iXi认证服务器进行安全通信,云端加密敏感信息
|
||
创建日期:2025-09-08
|
||
模块作者:3iXi
|
||
项目主页:https://github.com/3ixi/CloudScripts
|
||
获取授权码:https://3ixi.top
|
||
|
||
安全说明:
|
||
- 本模块仅用于与云函数认证服务器进行安全通信
|
||
- 不会收集或上传用户的个人账号数据
|
||
- 所有敏感信息均在云端加密处理
|
||
- 仅传输必要的认证信息,保护用户隐私
|
||
"""
|
||
|
||
import os
|
||
import json
|
||
import time
|
||
import uuid
|
||
import base64
|
||
import sys
|
||
from datetime import datetime, timezone, timedelta
|
||
|
||
def check_required_packages():
|
||
missing_packages = []
|
||
|
||
try:
|
||
import requests
|
||
except ImportError:
|
||
missing_packages.append('requests')
|
||
|
||
try:
|
||
from Crypto.Cipher import AES
|
||
from Crypto.Util.Padding import pad, unpad
|
||
from Crypto.PublicKey import RSA
|
||
from Crypto.Cipher import PKCS1_v1_5
|
||
except ImportError:
|
||
missing_packages.append('pycryptodome')
|
||
|
||
if missing_packages:
|
||
print("❌ 缺少必需的依赖库,请安装以下库:")
|
||
print()
|
||
for package in missing_packages:
|
||
print(f" pip install {package}")
|
||
print()
|
||
print("安装完成后请重新运行脚本。")
|
||
sys.exit(1)
|
||
|
||
check_required_packages()
|
||
|
||
import requests
|
||
from Crypto.Cipher import AES
|
||
from Crypto.Util.Padding import pad, unpad
|
||
|
||
PRIMARY_BASE_URL = "https://3ixi.top"
|
||
BACKUP_BASE_URL = "https://cloud.3ixi.top"
|
||
|
||
class CloudAuth:
|
||
def __init__(self):
|
||
self.base_urls = [PRIMARY_BASE_URL, BACKUP_BASE_URL]
|
||
self.current_url_index = 0
|
||
self.auth_code = None
|
||
self.session = requests.Session()
|
||
self._load_auth_code()
|
||
|
||
if self.auth_code:
|
||
self._verify_auth_code()
|
||
|
||
def _set_connection(self):
|
||
key_fragments = {
|
||
'prefix': 'QGp3','middle1': 'PFUk','middle2': 'MERJYmQp','suffix': 'ZW4pPw==',}
|
||
|
||
sequence = ['prefix', 'middle1', 'middle2', 'suffix']
|
||
encoded_key = ''.join([key_fragments[part] for part in sequence])
|
||
|
||
try:
|
||
decoded_key = base64.b64decode(encoded_key).decode('utf-8')
|
||
if len(decoded_key) == 16:
|
||
return decoded_key
|
||
except:
|
||
pass
|
||
|
||
backup_data = [64, 106, 119, 60, 85, 36, 48, 68, 73, 98, 100, 41, 101, 110, 41, 63]
|
||
return ''.join([chr(x) for x in backup_data])
|
||
|
||
def _switch_to_next_url(self):
|
||
self.current_url_index = (self.current_url_index + 1) % len(self.base_urls)
|
||
return self.base_urls[self.current_url_index]
|
||
|
||
def _get_current_url(self):
|
||
return self.base_urls[self.current_url_index].rstrip('/')
|
||
|
||
def _load_auth_code(self):
|
||
self.auth_code = os.getenv('CloudAuth')
|
||
if not self.auth_code:
|
||
raise ValueError("未找到环境变量'CloudAuth',请访问https://3ixi.top获取授权码")
|
||
|
||
try:
|
||
uuid.UUID(self.auth_code)
|
||
except ValueError:
|
||
raise ValueError("授权码格式无效,请访问https://3ixi.top重新获取")
|
||
|
||
def _get_timestamp(self):
|
||
timestamp_ms = int(time.time() * 1000)
|
||
timestamp_bytes = timestamp_ms.to_bytes(8, byteorder='big') + b'\x00' * 8
|
||
return timestamp_bytes, timestamp_ms
|
||
|
||
def _aes_encrypt(self, data):
|
||
timestamp_data, timestamp_ms = self._get_timestamp()
|
||
|
||
cipher = AES.new(self._set_connection().encode('utf-8'), AES.MODE_CBC, timestamp_data)
|
||
|
||
padded_data = pad(data.encode('utf-8'), AES.block_size)
|
||
encrypted_data = cipher.encrypt(padded_data)
|
||
|
||
encrypted_b64 = base64.b64encode(encrypted_data).decode('utf-8')
|
||
random_hex = hex(timestamp_ms)[2:]
|
||
|
||
return encrypted_b64, random_hex
|
||
|
||
def _aes_decrypt(self, encrypted_data, random_hex):
|
||
timestamp_ms = int(random_hex, 16)
|
||
timestamp_bytes = timestamp_ms.to_bytes(8, byteorder='big') + b'\x00' * 8
|
||
|
||
cipher = AES.new(self._set_connection().encode('utf-8'), AES.MODE_CBC, timestamp_bytes)
|
||
|
||
encrypted_bytes = base64.b64decode(encrypted_data)
|
||
decrypted_padded = cipher.decrypt(encrypted_bytes)
|
||
decrypted_data = unpad(decrypted_padded, AES.block_size)
|
||
|
||
return decrypted_data.decode('utf-8')
|
||
|
||
def _make_request(self, endpoint, data=None, method='POST'):
|
||
if data is None:
|
||
data = {}
|
||
|
||
data['auth_code'] = self.auth_code
|
||
json_data = json.dumps(data, ensure_ascii=False)
|
||
encrypted_data, random_hex = self._aes_encrypt(json_data)
|
||
|
||
headers = {
|
||
'Content-Type': 'application/json',
|
||
'random': random_hex
|
||
}
|
||
|
||
for _ in range(len(self.base_urls)):
|
||
current_url = self._get_current_url()
|
||
url = f"{current_url}{endpoint}"
|
||
|
||
try:
|
||
if method.upper() == 'GET':
|
||
response = self.session.get(url, headers=headers, timeout=30)
|
||
else:
|
||
response = self.session.post(url, json={'data': encrypted_data}, headers=headers, timeout=30)
|
||
|
||
response.raise_for_status()
|
||
|
||
response_data = response.json()
|
||
if 'data' in response_data:
|
||
decrypted_response = self._aes_decrypt(response_data['data'], random_hex)
|
||
return json.loads(decrypted_response)
|
||
else:
|
||
return response_data
|
||
|
||
except requests.RequestException as e:
|
||
print(f"请求失败: {e}")
|
||
self._switch_to_next_url()
|
||
print(f"尝试切换到备用地址")
|
||
except json.JSONDecodeError as e:
|
||
print(f"响应解析失败: {e}")
|
||
self._switch_to_next_url()
|
||
print(f"尝试切换到备用地址")
|
||
except Exception as e:
|
||
print(f"解密失败: {e}")
|
||
self._switch_to_next_url()
|
||
print(f"尝试切换到备用地址")
|
||
|
||
raise Exception("所有服务器地址均请求失败,请检查网络连接或服务器状态")
|
||
|
||
def _verify_auth_code(self):
|
||
try:
|
||
response = self._make_request('/api/verify')
|
||
|
||
if response.get('success'):
|
||
expire_date = response.get('expire_date')
|
||
if expire_date:
|
||
china_tz = timezone(timedelta(hours=8))
|
||
expire_dt = datetime.fromisoformat(expire_date.replace('Z', '+00:00'))
|
||
china_time = expire_dt.astimezone(china_tz)
|
||
|
||
print(f"📅 授权码截止日期: {china_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||
else:
|
||
print("✅ 授权码验证成功,但未获取到截止日期")
|
||
|
||
notifications = response.get('notifications', [])
|
||
if notifications:
|
||
print("\n" + "="*30)
|
||
print("📢 系统通知:")
|
||
for i, notification in enumerate(notifications, 1):
|
||
title = notification.get('title', '无标题')
|
||
content = notification.get('content', '无内容')
|
||
print(f"\n{i}. {title}")
|
||
print(f" {content}")
|
||
print("="*30)
|
||
|
||
else:
|
||
error_msg = response.get('error', '未知错误')
|
||
|
||
if '禁用' in error_msg:
|
||
disable_reason = response.get('disable_reason', '未知原因')
|
||
disabled_at = response.get('disabled_at', '')
|
||
|
||
print(f"❌ 授权码已被禁用")
|
||
print(f"📝 禁用原因: {disable_reason}")
|
||
|
||
if disabled_at:
|
||
try:
|
||
china_tz = timezone(timedelta(hours=8))
|
||
disabled_dt = datetime.fromisoformat(disabled_at.replace('Z', '+00:00'))
|
||
china_time = disabled_dt.astimezone(china_tz)
|
||
print(f"⏰ 禁用时间: {china_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||
except:
|
||
print(f"⏰ 禁用时间: {disabled_at}")
|
||
else:
|
||
print(f"❌ 授权码验证失败: {error_msg}")
|
||
|
||
raise Exception(f"授权码验证失败: {error_msg}")
|
||
|
||
except Exception as e:
|
||
if "授权码验证失败:" not in str(e):
|
||
print(f"❌ 授权码验证失败: {e}")
|
||
raise
|
||
|
||
def call_service(self, service_name, **kwargs):
|
||
data = {
|
||
'mod': service_name,
|
||
**kwargs
|
||
}
|
||
|
||
try:
|
||
response = self._make_request('/api/service', data)
|
||
|
||
if response.get('success'):
|
||
return response
|
||
else:
|
||
error_msg = response.get('error', '未知错误')
|
||
raise Exception(f"{service_name}服务调用失败: {error_msg}")
|
||
|
||
except Exception as e:
|
||
print(f"❌ {service_name}服务调用失败: {e}")
|
||
raise
|
||
|
||
|
||
def get_auth_client():
|
||
return CloudAuth()
|
||
|
||
|
||
def call_service(service_name, **kwargs):
|
||
client = get_auth_client()
|
||
return client.call_service(service_name, **kwargs)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
print("3iXi认证模块")
|
||
print("=" * 30)
|
||
print("访问https://3ixi.top获取授权码") |