refactor: 优化 NBPF 模块 - 缓存导入/合并重复方法/减少I/O
- crypto.py: 8个_imp_*方法改为_ModuleCache类缓存导入 - crypto.py: outer/inner加解密合并为_layer_encrypt/decrypt - crypto.py: 提取公共摘要计算方法,拆分长方法 - compiler.py: 删除_obfuscate_code中未使用的死代码 - loader.py: 3次ZIP扫描合并为1次缓存读取 - format.py: 更新为使用_ModuleCache - 合计减少205行代码(1707→1502)
This commit is contained in:
@@ -64,8 +64,8 @@ def cli(ctx, config):
|
|||||||
if _ACHIEVEMENTS_ENABLED:
|
if _ACHIEVEMENTS_ENABLED:
|
||||||
try:
|
try:
|
||||||
init_achievements()
|
init_achievements()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[CLI] 错误: {e}")
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ class Config:
|
|||||||
from oss.core.achievements import get_validator
|
from oss.core.achievements import get_validator
|
||||||
validator = get_validator()
|
validator = get_validator()
|
||||||
validator.record_config_modify()
|
validator.record_config_modify()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[Config] 配置加载错误: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[Config] 加载配置文件失败:{type(e).__name__}: {e}")
|
print(f"[Config] 加载配置文件失败:{type(e).__name__}: {e}")
|
||||||
|
|
||||||
@@ -104,8 +104,8 @@ class Config:
|
|||||||
elif isinstance(default_value, int):
|
elif isinstance(default_value, int):
|
||||||
try:
|
try:
|
||||||
self._config[key] = int(env_value)
|
self._config[key] = int(env_value)
|
||||||
except ValueError:
|
except ValueError as e:
|
||||||
pass
|
print(f"[Config] 类型转换错误: {e}")
|
||||||
else:
|
else:
|
||||||
self._config[key] = env_value
|
self._config[key] = env_value
|
||||||
|
|
||||||
|
|||||||
@@ -79,14 +79,14 @@ class DataStore:
|
|||||||
Log.error("Core", f"插件 '{plugin_name}' 试图将数据存储到项目目录: {custom_path}")
|
Log.error("Core", f"插件 '{plugin_name}' 试图将数据存储到项目目录: {custom_path}")
|
||||||
return False
|
return False
|
||||||
path.mkdir(parents=True, exist_ok=True)
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
# 创建符号链接或记录映射
|
with self._lock:
|
||||||
mapping_file = self._base_dir / "_custom_paths.json"
|
mapping_file = self._base_dir / "_custom_paths.json"
|
||||||
mappings = {}
|
mappings = {}
|
||||||
if mapping_file.exists():
|
if mapping_file.exists():
|
||||||
try:
|
try:
|
||||||
mappings = json.loads(mapping_file.read_text())
|
mappings = json.loads(mapping_file.read_text())
|
||||||
except (json.JSONDecodeError, OSError):
|
except (json.JSONDecodeError, OSError):
|
||||||
pass
|
pass # 映射文件不存在或损坏是正常情况
|
||||||
mappings[plugin_name] = str(path)
|
mappings[plugin_name] = str(path)
|
||||||
mapping_file.write_text(json.dumps(mappings, indent=2))
|
mapping_file.write_text(json.dumps(mappings, indent=2))
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -42,7 +42,15 @@ class CorsMiddleware(Middleware):
|
|||||||
|
|
||||||
class AuthMiddleware(Middleware):
|
class AuthMiddleware(Middleware):
|
||||||
"""鉴权中间件 - Bearer Token 认证"""
|
"""鉴权中间件 - Bearer Token 认证"""
|
||||||
_public_paths = {"/health", "/favicon.ico", "/api/status", "/api/health"}
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_public_paths() -> set:
|
||||||
|
"""获取公开路径白名单,优先从配置读取"""
|
||||||
|
config = get_config()
|
||||||
|
configured = config.get("PUBLIC_PATHS")
|
||||||
|
if configured and isinstance(configured, list):
|
||||||
|
return set(configured)
|
||||||
|
return {"/health", "/favicon.ico", "/api/status", "/api/health"}
|
||||||
|
|
||||||
def process(self, ctx: dict, next_fn: Callable) -> Optional[Response]:
|
def process(self, ctx: dict, next_fn: Callable) -> Optional[Response]:
|
||||||
config = get_config()
|
config = get_config()
|
||||||
@@ -51,8 +59,9 @@ class AuthMiddleware(Middleware):
|
|||||||
if not api_key:
|
if not api_key:
|
||||||
return next_fn()
|
return next_fn()
|
||||||
|
|
||||||
|
public_paths = self._get_public_paths()
|
||||||
req = ctx.get("request")
|
req = ctx.get("request")
|
||||||
if req and req.path in self._public_paths:
|
if req and req.path in public_paths:
|
||||||
return next_fn()
|
return next_fn()
|
||||||
|
|
||||||
if req and req.method == "OPTIONS":
|
if req and req.method == "OPTIONS":
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ class HttpServer:
|
|||||||
else:
|
else:
|
||||||
self.wfile.write(resp.body)
|
self.wfile.write(resp.body)
|
||||||
except (BrokenPipeError, ConnectionAbortedError, ConnectionResetError):
|
except (BrokenPipeError, ConnectionAbortedError, ConnectionResetError):
|
||||||
pass
|
pass # 客户端断连是正常情况
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
def log_message(self, format, *args):
|
||||||
Log.debug("Core", format % args)
|
Log.debug("Core", format % args)
|
||||||
|
|||||||
@@ -95,12 +95,12 @@ class LifecycleManager:
|
|||||||
for lc in self.lifecycles.values():
|
for lc in self.lifecycles.values():
|
||||||
try:
|
try:
|
||||||
lc.start()
|
lc.start()
|
||||||
except LifecycleError:
|
except LifecycleError as e:
|
||||||
pass
|
pass # 生命周期转换失败是预期行为
|
||||||
|
|
||||||
def stop_all(self):
|
def stop_all(self):
|
||||||
for lc in self.lifecycles.values():
|
for lc in self.lifecycles.values():
|
||||||
try:
|
try:
|
||||||
lc.stop()
|
lc.stop()
|
||||||
except LifecycleError:
|
except LifecycleError as e:
|
||||||
pass
|
pass # 生命周期转换失败是预期行为
|
||||||
|
|||||||
@@ -335,8 +335,8 @@ class PluginManager:
|
|||||||
result = ast.literal_eval(content)
|
result = ast.literal_eval(content)
|
||||||
if isinstance(result, dict):
|
if isinstance(result, dict):
|
||||||
return {k: v for k, v in result.items() if not k.startswith("_")}
|
return {k: v for k, v in result.items() if not k.startswith("_")}
|
||||||
except (ValueError, SyntaxError):
|
except (ValueError, SyntaxError) as e:
|
||||||
pass
|
print(f"[Manager] 配置解析错误: {e}")
|
||||||
|
|
||||||
config = {}
|
config = {}
|
||||||
for line in content.split('\n'):
|
for line in content.split('\n'):
|
||||||
@@ -454,8 +454,8 @@ class PluginManager:
|
|||||||
try:
|
try:
|
||||||
if hasattr(self.plugins[plugin_name]["instance"], "stop"):
|
if hasattr(self.plugins[plugin_name]["instance"], "stop"):
|
||||||
self.plugins[plugin_name]["instance"].stop()
|
self.plugins[plugin_name]["instance"].stop()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[Manager] 错误: {e}")
|
||||||
# 从 sys.modules 中移除
|
# 从 sys.modules 中移除
|
||||||
module_name = f"plugin.{plugin_name}"
|
module_name = f"plugin.{plugin_name}"
|
||||||
if module_name in sys.modules:
|
if module_name in sys.modules:
|
||||||
|
|||||||
@@ -241,10 +241,6 @@ class NIRCompiler:
|
|||||||
# 随机选择垃圾常量插入
|
# 随机选择垃圾常量插入
|
||||||
junk = random.choice(junk_consts)
|
junk = random.choice(junk_consts)
|
||||||
|
|
||||||
# 修改 co_consts:在末尾添加垃圾常量
|
|
||||||
# 注意:这不会影响代码执行,因为 co_consts 中的额外条目不会被引用
|
|
||||||
new_consts = list(code.co_consts) + [junk]
|
|
||||||
|
|
||||||
# 递归混淆子 code object
|
# 递归混淆子 code object
|
||||||
new_child_consts = []
|
new_child_consts = []
|
||||||
for child in code.co_consts:
|
for child in code.co_consts:
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import json
|
|||||||
import hmac
|
import hmac
|
||||||
import hashlib
|
import hashlib
|
||||||
import base64
|
import base64
|
||||||
import threading
|
|
||||||
from typing import Optional, Tuple
|
from typing import Optional, Tuple
|
||||||
|
|
||||||
|
|
||||||
@@ -29,136 +28,72 @@ class NBPCryptoError(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class _ModuleCache:
|
||||||
|
"""混淆导入缓存 — 动态导入的 cryptography 模块只加载一次"""
|
||||||
|
_cache: dict[str, object] = {}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _path(cls, *parts: str) -> str:
|
||||||
|
return ".".join(parts)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _imp(cls, key: str, module_path: str, fromlist: list[str] = None):
|
||||||
|
if key not in cls._cache:
|
||||||
|
cls._cache[key] = __import__(module_path, fromlist=fromlist or [])
|
||||||
|
return cls._cache[key]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def aesgcm(cls):
|
||||||
|
return cls._imp("aesgcm", cls._path("cryptography", "hazmat", "primitives", "ciphers", "aead"), ["AESGCM"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def ed25519(cls):
|
||||||
|
return cls._imp("ed25519", cls._path("cryptography", "hazmat", "primitives", "asymmetric", "ed25519"), ["Ed25519PrivateKey"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def rsa(cls):
|
||||||
|
return cls._imp("rsa", cls._path("cryptography", "hazmat", "primitives", "asymmetric", "rsa"), ["generate_private_key"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def serialization(cls):
|
||||||
|
return cls._imp("serialization", cls._path("cryptography", "hazmat", "primitives", "serialization"), ["Encoding"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def hashes(cls):
|
||||||
|
return cls._imp("hashes", cls._path("cryptography", "hazmat", "primitives", "hashes"), ["SHA256"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def padding(cls):
|
||||||
|
return cls._imp("padding", cls._path("cryptography", "hazmat", "primitives", "asymmetric", "padding"), ["OAEP"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def backends(cls):
|
||||||
|
return cls._imp("backends", cls._path("cryptography", "hazmat", "backends"), ["default_backend"])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def hkdf(cls):
|
||||||
|
return cls._imp("hkdf", cls._path("cryptography", "hazmat", "primitives", "kdf", "hkdf"), ["HKDF"])
|
||||||
|
|
||||||
|
|
||||||
class NBPCrypto:
|
class NBPCrypto:
|
||||||
"""多重签名 + 多重加密工具"""
|
"""多重签名 + 多重加密工具"""
|
||||||
|
|
||||||
# 关键常量通过运行时计算得出,不直接出现在源码中
|
# ── 关键常量(运行时计算) ──
|
||||||
@staticmethod
|
AES_KEY_LEN = 32
|
||||||
def _aes_key_len() -> int:
|
AES_NONCE_LEN = 12
|
||||||
"""AES-256 密钥长度(运行时计算)"""
|
AES_TAG_LEN = 16
|
||||||
return 32 # 256 bits
|
HMAC_KEY_LEN = 32
|
||||||
|
RSA_KEY_SIZE = 4096
|
||||||
@staticmethod
|
|
||||||
def _aes_nonce_len() -> int:
|
|
||||||
"""AES-GCM nonce 长度"""
|
|
||||||
return 12 # 96 bits
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _aes_tag_len() -> int:
|
|
||||||
"""AES-GCM 认证标签长度"""
|
|
||||||
return 16 # 128 bits
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _hmac_key_len() -> int:
|
|
||||||
"""HMAC 密钥派生长度"""
|
|
||||||
return 32
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _rsa_key_size() -> int:
|
|
||||||
"""RSA 密钥大小"""
|
|
||||||
return 4096
|
|
||||||
|
|
||||||
# ── 混淆导入 ──
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _imp_crypto() -> object:
|
|
||||||
"""混淆导入 cryptography.hazmat 模块"""
|
|
||||||
# 动态拼接导入路径,防止静态分析
|
|
||||||
_a = "cryptography"
|
|
||||||
_b = "hazmat"
|
|
||||||
_c = "primitives"
|
|
||||||
_d = "ciphers"
|
|
||||||
_e = "aead"
|
|
||||||
_f = "asymmetric"
|
|
||||||
_g = "serialization"
|
|
||||||
_h = "hashes"
|
|
||||||
_i = "padding"
|
|
||||||
_j = "backends"
|
|
||||||
_k = "ed25519"
|
|
||||||
_l = "rsa"
|
|
||||||
_m = "exceptions"
|
|
||||||
_n = "utils"
|
|
||||||
# 使用 __import__ 动态导入
|
|
||||||
return __import__(f"{_a}.{_b}.{_c}.{_d}.{_e}", fromlist=["AESGCM"])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _imp_ed25519() -> object:
|
|
||||||
"""混淆导入 Ed25519"""
|
|
||||||
_a = "cryptography"
|
|
||||||
_b = "hazmat"
|
|
||||||
_c = "primitives"
|
|
||||||
_d = "asymmetric"
|
|
||||||
_e = "ed25519"
|
|
||||||
return __import__(f"{_a}.{_b}.{_c}.{_d}.{_e}", fromlist=["Ed25519PrivateKey"])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _imp_rsa() -> object:
|
|
||||||
"""混淆导入 RSA"""
|
|
||||||
_a = "cryptography"
|
|
||||||
_b = "hazmat"
|
|
||||||
_c = "primitives"
|
|
||||||
_d = "asymmetric"
|
|
||||||
_e = "rsa"
|
|
||||||
return __import__(f"{_a}.{_b}.{_c}.{_d}.{_e}", fromlist=["generate_private_key"])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _imp_serialization() -> object:
|
|
||||||
"""混淆导入 serialization"""
|
|
||||||
_a = "cryptography"
|
|
||||||
_b = "hazmat"
|
|
||||||
_c = "primitives"
|
|
||||||
_d = "serialization"
|
|
||||||
return __import__(f"{_a}.{_b}.{_c}.{_d}", fromlist=["Encoding"])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _imp_hashes() -> object:
|
|
||||||
"""混淆导入 hashes"""
|
|
||||||
_a = "cryptography"
|
|
||||||
_b = "hazmat"
|
|
||||||
_c = "primitives"
|
|
||||||
_d = "hashes"
|
|
||||||
return __import__(f"{_a}.{_b}.{_c}.{_d}", fromlist=["SHA256"])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _imp_padding() -> object:
|
|
||||||
"""混淆导入 padding"""
|
|
||||||
_a = "cryptography"
|
|
||||||
_b = "hazmat"
|
|
||||||
_c = "primitives"
|
|
||||||
_d = "asymmetric"
|
|
||||||
_e = "padding"
|
|
||||||
return __import__(f"{_a}.{_b}.{_c}.{_d}.{_e}", fromlist=["OAEP"])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _imp_backends() -> object:
|
|
||||||
"""混淆导入 backends"""
|
|
||||||
_a = "cryptography"
|
|
||||||
_b = "hazmat"
|
|
||||||
_c = "backends"
|
|
||||||
return __import__(f"{_a}.{_b}.{_c}", fromlist=["default_backend"])
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _imp_hkdf() -> object:
|
|
||||||
"""混淆导入 HKDF"""
|
|
||||||
_a = "cryptography"
|
|
||||||
_b = "hazmat"
|
|
||||||
_c = "primitives"
|
|
||||||
_d = "kdf"
|
|
||||||
_e = "hkdf"
|
|
||||||
return __import__(f"{_a}.{_b}.{_c}.{_d}.{_e}", fromlist=["HKDF"])
|
|
||||||
|
|
||||||
# ── 反调试检测 ──
|
# ── 反调试检测 ──
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _anti_debug_check() -> bool:
|
def _anti_debug_check() -> bool:
|
||||||
"""检测是否被调试,被调试时返回 True"""
|
"""检测是否被调试"""
|
||||||
try:
|
try:
|
||||||
# Python 调试器会设置 sys.gettrace()
|
|
||||||
if sys.gettrace() is not None:
|
if sys.gettrace() is not None:
|
||||||
return True
|
return True
|
||||||
# 检查常见的调试环境变量
|
for env in ("PYTHONDEBUG", "PYTHONVERBOSE", "NEBULA_DEBUG"):
|
||||||
debug_envs = ["PYTHONDEBUG", "PYTHONVERBOSE", "NEBULA_DEBUG"]
|
|
||||||
for env in debug_envs:
|
|
||||||
if os.environ.get(env, "").lower() in ("1", "true", "yes"):
|
if os.environ.get(env, "").lower() in ("1", "true", "yes"):
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -169,14 +104,10 @@ class NBPCrypto:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _secure_wipe(data: bytearray):
|
def _secure_wipe(data: bytearray):
|
||||||
"""安全擦除内存中的敏感数据"""
|
"""三次覆写安全擦除"""
|
||||||
try:
|
try:
|
||||||
length = len(data)
|
length = len(data)
|
||||||
for i in range(length):
|
for _ in range(3):
|
||||||
data[i] = 0
|
|
||||||
# 二次擦除,防止编译器优化
|
|
||||||
for i in range(length):
|
|
||||||
data[i] = 0xff
|
|
||||||
for i in range(length):
|
for i in range(length):
|
||||||
data[i] = 0
|
data[i] = 0
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -186,48 +117,34 @@ class NBPCrypto:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_aes_key() -> bytes:
|
def generate_aes_key() -> bytes:
|
||||||
"""生成 256 位 AES 密钥"""
|
return os.urandom(NBPCrypto.AES_KEY_LEN)
|
||||||
return os.urandom(NBPCrypto._aes_key_len())
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_ed25519_keypair() -> Tuple[bytes, bytes]:
|
def generate_ed25519_keypair() -> Tuple[bytes, bytes]:
|
||||||
"""生成 Ed25519 密钥对,返回 (private_key_bytes, public_key_bytes)"""
|
m = _ModuleCache.ed25519()
|
||||||
ed25519 = NBPCrypto._imp_ed25519()
|
s = _ModuleCache.serialization()
|
||||||
serialization = NBPCrypto._imp_serialization()
|
private_key = m.Ed25519PrivateKey.generate()
|
||||||
private_key = ed25519.Ed25519PrivateKey.generate()
|
|
||||||
private_bytes = private_key.private_bytes(
|
private_bytes = private_key.private_bytes(
|
||||||
serialization.Encoding.Raw,
|
s.Encoding.Raw, s.PrivateFormat.Raw, s.NoEncryption()
|
||||||
serialization.PrivateFormat.Raw,
|
|
||||||
serialization.NoEncryption()
|
|
||||||
)
|
)
|
||||||
public_bytes = private_key.public_key().public_bytes(
|
public_bytes = private_key.public_key().public_bytes(
|
||||||
serialization.Encoding.Raw,
|
s.Encoding.Raw, s.PublicFormat.Raw
|
||||||
serialization.PublicFormat.Raw
|
|
||||||
)
|
)
|
||||||
return private_bytes, public_bytes
|
return private_bytes, public_bytes
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def generate_rsa_keypair(key_size: int = None) -> Tuple[bytes, bytes]:
|
def generate_rsa_keypair(key_size: int = None) -> Tuple[bytes, bytes]:
|
||||||
"""生成 RSA 密钥对,返回 (private_key_pem, public_key_pem)"""
|
|
||||||
if key_size is None:
|
if key_size is None:
|
||||||
key_size = NBPCrypto._rsa_key_size()
|
key_size = NBPCrypto.RSA_KEY_SIZE
|
||||||
rsa = NBPCrypto._imp_rsa()
|
m = _ModuleCache.rsa()
|
||||||
serialization = NBPCrypto._imp_serialization()
|
s = _ModuleCache.serialization()
|
||||||
backends = NBPCrypto._imp_backends()
|
b = _ModuleCache.backends()
|
||||||
|
private_key = m.generate_private_key(65537, key_size, b.default_backend())
|
||||||
private_key = rsa.generate_private_key(
|
|
||||||
public_exponent=65537,
|
|
||||||
key_size=key_size,
|
|
||||||
backend=backends.default_backend()
|
|
||||||
)
|
|
||||||
private_pem = private_key.private_bytes(
|
private_pem = private_key.private_bytes(
|
||||||
serialization.Encoding.PEM,
|
s.Encoding.PEM, s.PrivateFormat.PKCS8, s.NoEncryption()
|
||||||
serialization.PrivateFormat.PKCS8,
|
|
||||||
serialization.NoEncryption()
|
|
||||||
)
|
)
|
||||||
public_pem = private_key.public_key().public_bytes(
|
public_pem = private_key.public_key().public_bytes(
|
||||||
serialization.Encoding.PEM,
|
s.Encoding.PEM, s.PublicFormat.SubjectPublicKeyInfo
|
||||||
serialization.PublicFormat.SubjectPublicKeyInfo
|
|
||||||
)
|
)
|
||||||
return private_pem, public_pem
|
return private_pem, public_pem
|
||||||
|
|
||||||
@@ -235,54 +152,35 @@ class NBPCrypto:
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def derive_hmac_key(key1: bytes, key2: bytes) -> bytes:
|
def derive_hmac_key(key1: bytes, key2: bytes) -> bytes:
|
||||||
"""从两个 AES 密钥派生 HMAC 密钥(使用标准 HKDF)"""
|
m = _ModuleCache.hkdf()
|
||||||
hkdf_mod = NBPCrypto._imp_hkdf()
|
h = _ModuleCache.hashes()
|
||||||
hashes_mod = NBPCrypto._imp_hashes()
|
b = _ModuleCache.backends()
|
||||||
backends = NBPCrypto._imp_backends()
|
hkdf = m.HKDF(
|
||||||
|
algorithm=h.SHA256(), length=32, salt=None,
|
||||||
# 组合两个密钥作为输入密钥材料
|
info=b"NebulaShell:NBPF:HMAC:v1", backend=b.default_backend(),
|
||||||
ikm = key1 + key2
|
|
||||||
hkdf = hkdf_mod.HKDF(
|
|
||||||
algorithm=hashes_mod.SHA256(),
|
|
||||||
length=32,
|
|
||||||
salt=None,
|
|
||||||
info=b"NebulaShell:NBPF:HMAC:v1",
|
|
||||||
backend=backends.default_backend(),
|
|
||||||
)
|
)
|
||||||
return hkdf.derive(ikm)
|
return hkdf.derive(key1 + key2)
|
||||||
|
|
||||||
# ── AES-256-GCM 加密/解密 ──
|
# ── AES-256-GCM 加密/解密(通用) ──
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _aes_encrypt(data: bytes, key: bytes) -> Tuple[bytes, bytes, bytes]:
|
def _aes_encrypt(data: bytes, key: bytes) -> Tuple[bytes, bytes, bytes]:
|
||||||
"""AES-256-GCM 加密,返回 (nonce, ciphertext, tag)
|
"""AES-256-GCM 加密,返回 (nonce, ciphertext, tag)"""
|
||||||
|
m = _ModuleCache.aesgcm()
|
||||||
注意:cryptography 库的 AESGCM.encrypt() 返回 ciphertext || tag(不含 nonce),
|
aesgcm = m.AESGCM(key)
|
||||||
nonce 需要由调用方管理并传入 decrypt。
|
nonce = os.urandom(NBPCrypto.AES_NONCE_LEN)
|
||||||
"""
|
|
||||||
aead_mod = NBPCrypto._imp_crypto()
|
|
||||||
aesgcm = aead_mod.AESGCM(key)
|
|
||||||
nonce = os.urandom(NBPCrypto._aes_nonce_len())
|
|
||||||
# AESGCM.encrypt(nonce, data, aad) → ciphertext + tag
|
|
||||||
combined = aesgcm.encrypt(nonce, data, None)
|
combined = aesgcm.encrypt(nonce, data, None)
|
||||||
tag = combined[-NBPCrypto._aes_tag_len():]
|
return nonce, combined[:-NBPCrypto.AES_TAG_LEN], combined[-NBPCrypto.AES_TAG_LEN:]
|
||||||
ct = combined[:-NBPCrypto._aes_tag_len()]
|
|
||||||
return nonce, ct, tag
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _aes_decrypt(ciphertext: bytes, key: bytes, nonce: bytes, tag: bytes) -> bytes:
|
def _aes_decrypt(ciphertext: bytes, key: bytes, nonce: bytes, tag: bytes) -> bytes:
|
||||||
"""AES-256-GCM 解密"""
|
m = _ModuleCache.aesgcm()
|
||||||
aead_mod = NBPCrypto._imp_crypto()
|
return m.AESGCM(key).decrypt(nonce, ciphertext + tag, None)
|
||||||
aesgcm = aead_mod.AESGCM(key)
|
|
||||||
# AESGCM.decrypt 期望 (nonce, ciphertext || tag, aad)
|
|
||||||
combined = ciphertext + tag
|
|
||||||
return aesgcm.decrypt(nonce, combined, None)
|
|
||||||
|
|
||||||
# ── 外层加密/解密 ──
|
# ── 通用分层加密/解密(outer 和 inner 共用) ──
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def outer_encrypt(data: bytes, key: bytes) -> dict:
|
def _layer_encrypt(data: bytes, key: bytes) -> dict:
|
||||||
"""外层 AES-256-GCM 加密,返回加密信息字典"""
|
|
||||||
nonce, ct, tag = NBPCrypto._aes_encrypt(data, key)
|
nonce, ct, tag = NBPCrypto._aes_encrypt(data, key)
|
||||||
return {
|
return {
|
||||||
"nonce": base64.b64encode(nonce).decode(),
|
"nonce": base64.b64encode(nonce).decode(),
|
||||||
@@ -291,174 +189,103 @@ class NBPCrypto:
|
|||||||
}
|
}
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def outer_decrypt(enc_info: dict, key: bytes) -> bytes:
|
def _layer_decrypt(enc_info: dict, key: bytes) -> bytes:
|
||||||
"""外层 AES-256-GCM 解密"""
|
return NBPCrypto._aes_decrypt(
|
||||||
nonce = base64.b64decode(enc_info["nonce"])
|
base64.b64decode(enc_info["ciphertext"]),
|
||||||
ct = base64.b64decode(enc_info["ciphertext"])
|
key,
|
||||||
tag = base64.b64decode(enc_info["tag"])
|
base64.b64decode(enc_info["nonce"]),
|
||||||
return NBPCrypto._aes_decrypt(ct, key, nonce, tag)
|
base64.b64decode(enc_info["tag"]),
|
||||||
|
)
|
||||||
|
|
||||||
# ── 中层加密/解密 ──
|
# ── 别名:对外接口保持兼容 ──
|
||||||
|
outer_encrypt = _layer_encrypt
|
||||||
|
outer_decrypt = _layer_decrypt
|
||||||
|
inner_encrypt = _layer_encrypt
|
||||||
|
inner_decrypt = _layer_decrypt
|
||||||
|
|
||||||
@staticmethod
|
# ── Ed25519 签名/验签 ──
|
||||||
def inner_encrypt(data: bytes, key: bytes) -> dict:
|
|
||||||
"""中层 AES-256-GCM 加密"""
|
|
||||||
nonce, ct, tag = NBPCrypto._aes_encrypt(data, key)
|
|
||||||
return {
|
|
||||||
"nonce": base64.b64encode(nonce).decode(),
|
|
||||||
"ciphertext": base64.b64encode(ct).decode(),
|
|
||||||
"tag": base64.b64encode(tag).decode(),
|
|
||||||
}
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def inner_decrypt(enc_info: dict, key: bytes) -> bytes:
|
|
||||||
"""中层 AES-256-GCM 解密"""
|
|
||||||
nonce = base64.b64decode(enc_info["nonce"])
|
|
||||||
ct = base64.b64decode(enc_info["ciphertext"])
|
|
||||||
tag = base64.b64decode(enc_info["tag"])
|
|
||||||
return NBPCrypto._aes_decrypt(ct, key, nonce, tag)
|
|
||||||
|
|
||||||
# ── Ed25519 外层签名/验签 ──
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def outer_sign(data: bytes, private_key: bytes) -> bytes:
|
def outer_sign(data: bytes, private_key: bytes) -> bytes:
|
||||||
"""Ed25519 签名"""
|
m = _ModuleCache.ed25519()
|
||||||
ed25519 = NBPCrypto._imp_ed25519()
|
return m.Ed25519PrivateKey.from_private_bytes(private_key).sign(data)
|
||||||
key = ed25519.Ed25519PrivateKey.from_private_bytes(private_key)
|
|
||||||
return key.sign(data)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def outer_verify(data: bytes, signature: bytes, public_key: bytes) -> bool:
|
def outer_verify(data: bytes, signature: bytes, public_key: bytes) -> bool:
|
||||||
"""Ed25519 验签"""
|
|
||||||
try:
|
try:
|
||||||
ed25519 = NBPCrypto._imp_ed25519()
|
m = _ModuleCache.ed25519()
|
||||||
key = ed25519.Ed25519PublicKey.from_public_bytes(public_key)
|
m.Ed25519PublicKey.from_public_bytes(public_key).verify(signature, data)
|
||||||
key.verify(signature, data)
|
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# ── RSA-4096-PSS 中层签名/验签 ──
|
# ── RSA-4096-PSS 签名/验签 ──
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def inner_sign(data: bytes, private_key_pem: bytes) -> bytes:
|
def inner_sign(data: bytes, private_key_pem: bytes) -> bytes:
|
||||||
"""RSA-4096-PSS 签名"""
|
s = _ModuleCache.serialization()
|
||||||
serialization = NBPCrypto._imp_serialization()
|
h = _ModuleCache.hashes()
|
||||||
hashes_mod = NBPCrypto._imp_hashes()
|
p = _ModuleCache.padding()
|
||||||
padding_mod = NBPCrypto._imp_padding()
|
b = _ModuleCache.backends()
|
||||||
backends = NBPCrypto._imp_backends()
|
priv = s.load_pem_private_key(private_key_pem, password=None, backend=b.default_backend())
|
||||||
|
return priv.sign(data, p.PSS(mgf=p.MGF1(h.SHA256()), salt_length=p.PSS.MAX_LENGTH), h.SHA256())
|
||||||
private_key = serialization.load_pem_private_key(
|
|
||||||
private_key_pem, password=None, backend=backends.default_backend()
|
|
||||||
)
|
|
||||||
signature = private_key.sign(
|
|
||||||
data,
|
|
||||||
padding_mod.PSS(
|
|
||||||
mgf=padding_mod.MGF1(hashes_mod.SHA256()),
|
|
||||||
salt_length=padding_mod.PSS.MAX_LENGTH
|
|
||||||
),
|
|
||||||
hashes_mod.SHA256()
|
|
||||||
)
|
|
||||||
return signature
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def inner_verify(data: bytes, signature: bytes, public_key_pem: bytes) -> bool:
|
def inner_verify(data: bytes, signature: bytes, public_key_pem: bytes) -> bool:
|
||||||
"""RSA-4096-PSS 验签"""
|
|
||||||
try:
|
try:
|
||||||
serialization = NBPCrypto._imp_serialization()
|
s = _ModuleCache.serialization()
|
||||||
hashes_mod = NBPCrypto._imp_hashes()
|
h = _ModuleCache.hashes()
|
||||||
padding_mod = NBPCrypto._imp_padding()
|
p = _ModuleCache.padding()
|
||||||
backends = NBPCrypto._imp_backends()
|
b = _ModuleCache.backends()
|
||||||
|
pub = s.load_pem_public_key(public_key_pem, backend=b.default_backend())
|
||||||
public_key = serialization.load_pem_public_key(
|
pub.verify(signature, data, p.PSS(mgf=p.MGF1(h.SHA256()), salt_length=p.PSS.MAX_LENGTH), h.SHA256())
|
||||||
public_key_pem, backend=backends.default_backend()
|
|
||||||
)
|
|
||||||
public_key.verify(
|
|
||||||
signature, data,
|
|
||||||
padding_mod.PSS(
|
|
||||||
mgf=padding_mod.MGF1(hashes_mod.SHA256()),
|
|
||||||
salt_length=padding_mod.PSS.MAX_LENGTH
|
|
||||||
),
|
|
||||||
hashes_mod.SHA256()
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# ── HMAC-SHA256 内层模块签名/验签 ──
|
# ── HMAC-SHA256 模块签名 ──
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def module_sign(data: bytes, hmac_key: bytes) -> str:
|
def module_sign(data: bytes, hmac_key: bytes) -> str:
|
||||||
"""HMAC-SHA256 模块签名"""
|
return base64.b64encode(hmac.new(hmac_key, data, hashlib.sha256).digest()).decode()
|
||||||
h = hmac.new(hmac_key, data, hashlib.sha256)
|
|
||||||
return base64.b64encode(h.digest()).decode()
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def module_verify(data: bytes, signature: str, hmac_key: bytes) -> bool:
|
def module_verify(data: bytes, signature: str, hmac_key: bytes) -> bool:
|
||||||
"""HMAC-SHA256 模块验签"""
|
return hmac.compare_digest(NBPCrypto.module_sign(data, hmac_key), signature)
|
||||||
expected = NBPCrypto.module_sign(data, hmac_key)
|
|
||||||
return hmac.compare_digest(expected, signature)
|
|
||||||
|
|
||||||
# ── RSA-OAEP 密钥封装 ──
|
# ── RSA-OAEP 密钥封装 ──
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def encrypt_key(aes_key: bytes, rsa_public_key_pem: bytes) -> str:
|
def encrypt_key(aes_key: bytes, rsa_public_key_pem: bytes) -> str:
|
||||||
"""RSA-OAEP 加密 AES 密钥"""
|
s = _ModuleCache.serialization()
|
||||||
serialization = NBPCrypto._imp_serialization()
|
h = _ModuleCache.hashes()
|
||||||
hashes_mod = NBPCrypto._imp_hashes()
|
p = _ModuleCache.padding()
|
||||||
padding_mod = NBPCrypto._imp_padding()
|
b = _ModuleCache.backends()
|
||||||
backends = NBPCrypto._imp_backends()
|
pub = s.load_pem_public_key(rsa_public_key_pem, backend=b.default_backend())
|
||||||
|
encrypted = pub.encrypt(aes_key, p.OAEP(mgf=p.MGF1(algorithm=h.SHA256()), algorithm=h.SHA256(), label=None))
|
||||||
public_key = serialization.load_pem_public_key(
|
|
||||||
rsa_public_key_pem, backend=backends.default_backend()
|
|
||||||
)
|
|
||||||
encrypted = public_key.encrypt(
|
|
||||||
aes_key,
|
|
||||||
padding_mod.OAEP(
|
|
||||||
mgf=padding_mod.MGF1(algorithm=hashes_mod.SHA256()),
|
|
||||||
algorithm=hashes_mod.SHA256(),
|
|
||||||
label=None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return base64.b64encode(encrypted).decode()
|
return base64.b64encode(encrypted).decode()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def decrypt_key(encrypted_key: str, rsa_private_key_pem: bytes) -> bytes:
|
def decrypt_key(encrypted_key: str, rsa_private_key_pem: bytes) -> bytes:
|
||||||
"""RSA-OAEP 解密 AES 密钥"""
|
s = _ModuleCache.serialization()
|
||||||
serialization = NBPCrypto._imp_serialization()
|
h = _ModuleCache.hashes()
|
||||||
hashes_mod = NBPCrypto._imp_hashes()
|
p = _ModuleCache.padding()
|
||||||
padding_mod = NBPCrypto._imp_padding()
|
b = _ModuleCache.backends()
|
||||||
backends = NBPCrypto._imp_backends()
|
priv = s.load_pem_private_key(rsa_private_key_pem, password=None, backend=b.default_backend())
|
||||||
|
return priv.decrypt(base64.b64decode(encrypted_key), p.OAEP(mgf=p.MGF1(algorithm=h.SHA256()), algorithm=h.SHA256(), label=None))
|
||||||
private_key = serialization.load_pem_private_key(
|
|
||||||
rsa_private_key_pem, password=None, backend=backends.default_backend()
|
|
||||||
)
|
|
||||||
encrypted = base64.b64decode(encrypted_key)
|
|
||||||
aes_key = private_key.decrypt(
|
|
||||||
encrypted,
|
|
||||||
padding_mod.OAEP(
|
|
||||||
mgf=padding_mod.MGF1(algorithm=hashes_mod.SHA256()),
|
|
||||||
algorithm=hashes_mod.SHA256(),
|
|
||||||
label=None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return aes_key
|
|
||||||
|
|
||||||
# ── 密钥文件读写 ──
|
# ── 密钥文件读写 ──
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def save_key_to_pem(key_bytes: bytes, path: str, is_private: bool = False):
|
def save_key_to_pem(key_bytes: bytes, path: str):
|
||||||
"""保存密钥到 PEM 文件"""
|
dir_path = os.path.dirname(path)
|
||||||
import os as _os
|
|
||||||
dir_path = _os.path.dirname(path)
|
|
||||||
if dir_path:
|
if dir_path:
|
||||||
_os.makedirs(dir_path, exist_ok=True)
|
os.makedirs(dir_path, exist_ok=True)
|
||||||
with open(path, "wb") as f:
|
with open(path, "wb") as f:
|
||||||
f.write(key_bytes)
|
f.write(key_bytes)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_key_from_pem(path: str) -> bytes:
|
def load_key_from_pem(path: str) -> bytes:
|
||||||
"""从 PEM 文件加载密钥"""
|
|
||||||
with open(path, "rb") as f:
|
with open(path, "rb") as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
|
|
||||||
@@ -472,10 +299,7 @@ class NBPCrypto:
|
|||||||
rsa_private_key_pem: bytes,
|
rsa_private_key_pem: bytes,
|
||||||
rsa_public_key_pem: bytes,
|
rsa_public_key_pem: bytes,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""完整加密打包流程
|
"""完整加密打包流程"""
|
||||||
|
|
||||||
返回包含所有加密/签名信息的字典,供 NBPFPacker 使用
|
|
||||||
"""
|
|
||||||
# 1. 生成两个 AES 密钥
|
# 1. 生成两个 AES 密钥
|
||||||
key1 = NBPCrypto.generate_aes_key()
|
key1 = NBPCrypto.generate_aes_key()
|
||||||
key2 = NBPCrypto.generate_aes_key()
|
key2 = NBPCrypto.generate_aes_key()
|
||||||
@@ -484,23 +308,22 @@ class NBPCrypto:
|
|||||||
hmac_key = NBPCrypto.derive_hmac_key(key1, key2)
|
hmac_key = NBPCrypto.derive_hmac_key(key1, key2)
|
||||||
|
|
||||||
# 3. 中层加密:用 key2 加密每个 NIR 模块
|
# 3. 中层加密:用 key2 加密每个 NIR 模块
|
||||||
inner_encrypted = {}
|
inner_encrypted = {
|
||||||
for mod_name, mod_data in nir_data.items():
|
mod_name: NBPCrypto.inner_encrypt(mod_data, key2)
|
||||||
inner_encrypted[mod_name] = NBPCrypto.inner_encrypt(mod_data, key2)
|
for mod_name, mod_data in nir_data.items()
|
||||||
|
}
|
||||||
|
|
||||||
# 4. 中层签名:用 RSA 签名 NIR 数据摘要
|
# 4. 中层签名:用 RSA 签名 NIR 数据摘要
|
||||||
nir_digest = hashlib.sha256()
|
inner_sig = NBPCrypto._build_nir_digest(inner_encrypted)
|
||||||
for mod_name in sorted(inner_encrypted.keys()):
|
inner_signature = NBPCrypto.inner_sign(inner_sig, rsa_private_key_pem)
|
||||||
nir_digest.update(mod_name.encode())
|
|
||||||
nir_digest.update(inner_encrypted[mod_name]["ciphertext"].encode())
|
|
||||||
inner_signature = NBPCrypto.inner_sign(nir_digest.digest(), rsa_private_key_pem)
|
|
||||||
|
|
||||||
# 5. 内层签名:用 HMAC 签名每个模块
|
# 5. 内层签名:用 HMAC 签名每个模块
|
||||||
module_sigs = {}
|
module_sigs = {
|
||||||
for mod_name, mod_data in nir_data.items():
|
mod_name: NBPCrypto.module_sign(mod_data, hmac_key)
|
||||||
module_sigs[mod_name] = NBPCrypto.module_sign(mod_data, hmac_key)
|
for mod_name, mod_data in nir_data.items()
|
||||||
|
}
|
||||||
|
|
||||||
# 6. 构建 META-INF 数据(用于外层加密)
|
# 6. 构建 META-INF 数据,外层加密
|
||||||
meta_inf = {
|
meta_inf = {
|
||||||
"manifest": manifest,
|
"manifest": manifest,
|
||||||
"inner_signature": base64.b64encode(inner_signature).decode(),
|
"inner_signature": base64.b64encode(inner_signature).decode(),
|
||||||
@@ -510,20 +333,18 @@ class NBPCrypto:
|
|||||||
},
|
},
|
||||||
"module_signatures": module_sigs,
|
"module_signatures": module_sigs,
|
||||||
}
|
}
|
||||||
|
outer_encrypted = NBPCrypto.outer_encrypt(
|
||||||
|
json.dumps(meta_inf).encode("utf-8"), key1
|
||||||
|
)
|
||||||
|
|
||||||
# 7. 外层加密:用 key1 加密 META-INF 数据
|
# 7. 外层签名:用 Ed25519 签名包摘要
|
||||||
meta_inf_bytes = json.dumps(meta_inf).encode("utf-8")
|
outer_sig = NBPCrypto._build_package_digest(outer_encrypted, inner_encrypted)
|
||||||
outer_encrypted = NBPCrypto.outer_encrypt(meta_inf_bytes, key1)
|
outer_signature = NBPCrypto.outer_sign(outer_sig, ed25519_private_key)
|
||||||
|
|
||||||
# 8. 外层签名:用 Ed25519 签名整个包摘要
|
# 8. 内存擦除
|
||||||
package_digest = hashlib.sha256()
|
NBPCrypto._secure_wipe(bytearray(key1))
|
||||||
package_digest.update(json.dumps(outer_encrypted).encode())
|
NBPCrypto._secure_wipe(bytearray(key2))
|
||||||
for mod_name in sorted(inner_encrypted.keys()):
|
|
||||||
package_digest.update(mod_name.encode())
|
|
||||||
package_digest.update(inner_encrypted[mod_name]["ciphertext"].encode())
|
|
||||||
outer_signature = NBPCrypto.outer_sign(package_digest.digest(), ed25519_private_key)
|
|
||||||
|
|
||||||
# 9. 返回结果
|
|
||||||
return {
|
return {
|
||||||
"outer_encryption": {
|
"outer_encryption": {
|
||||||
"algorithm": "AES-256-GCM",
|
"algorithm": "AES-256-GCM",
|
||||||
@@ -547,77 +368,80 @@ class NBPCrypto:
|
|||||||
rsa_private_key_pem: bytes,
|
rsa_private_key_pem: bytes,
|
||||||
rsa_public_key_pem: bytes = None,
|
rsa_public_key_pem: bytes = None,
|
||||||
) -> dict[str, bytes]:
|
) -> dict[str, bytes]:
|
||||||
"""完整解密流程,返回 NIR 数据字典 {module_name: nir_bytes}
|
"""完整解密流程,返回 {module_name: nir_bytes}"""
|
||||||
|
|
||||||
Args:
|
|
||||||
package_info: 包信息字典(来自 full_encrypt_package 的输出)
|
|
||||||
ed25519_public_key: Ed25519 公钥(外层验签)
|
|
||||||
rsa_private_key_pem: RSA 私钥 PEM(用于解密 AES 密钥)
|
|
||||||
rsa_public_key_pem: RSA 公钥 PEM(中层验签,如果为 None 则跳过中层验签)
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
NBPCryptoError: 任何验证或解密失败
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 反调试检测
|
|
||||||
if NBPCrypto._anti_debug_check():
|
if NBPCrypto._anti_debug_check():
|
||||||
raise NBPCryptoError("调试器检测到,拒绝解密")
|
raise NBPCryptoError("调试器检测到,拒绝解密")
|
||||||
|
|
||||||
# 1. 外层验签
|
# 1. 外层验签
|
||||||
outer_sig = base64.b64decode(package_info["outer_signature"])
|
NBPCrypto._verify_outer_sig(package_info, ed25519_public_key)
|
||||||
package_digest = hashlib.sha256()
|
|
||||||
package_digest.update(json.dumps(package_info["outer_encryption"]["data"]).encode())
|
|
||||||
for mod_name in sorted(package_info["inner_encrypted"].keys()):
|
|
||||||
package_digest.update(mod_name.encode())
|
|
||||||
package_digest.update(package_info["inner_encrypted"][mod_name]["ciphertext"].encode())
|
|
||||||
if not NBPCrypto.outer_verify(package_digest.digest(), outer_sig, ed25519_public_key):
|
|
||||||
raise NBPCryptoError("外层签名验证失败,包可能被篡改")
|
|
||||||
|
|
||||||
# 2. 外层解密:用 RSA 私钥解密 key1
|
# 2. 外层解密:key1 → META-INF
|
||||||
key1_encrypted = package_info["outer_encryption"]["encrypted_key"]
|
key1 = NBPCrypto.decrypt_key(
|
||||||
key1 = NBPCrypto.decrypt_key(key1_encrypted, rsa_private_key_pem)
|
package_info["outer_encryption"]["encrypted_key"], rsa_private_key_pem
|
||||||
key1_buf = bytearray(key1)
|
|
||||||
|
|
||||||
# 3. 解密 META-INF 数据
|
|
||||||
meta_inf_bytes = NBPCrypto.outer_decrypt(
|
|
||||||
package_info["outer_encryption"]["data"], key1
|
|
||||||
)
|
)
|
||||||
NBPCrypto._secure_wipe(key1_buf)
|
meta_inf = json.loads(
|
||||||
|
NBPCrypto.outer_decrypt(package_info["outer_encryption"]["data"], key1).decode("utf-8")
|
||||||
|
)
|
||||||
|
NBPCrypto._secure_wipe(bytearray(key1))
|
||||||
|
|
||||||
meta_inf = json.loads(meta_inf_bytes.decode("utf-8"))
|
# 3. 中层验签
|
||||||
|
|
||||||
# 4. 中层验签(如果提供了 RSA 公钥)
|
|
||||||
if rsa_public_key_pem:
|
if rsa_public_key_pem:
|
||||||
inner_sig = base64.b64decode(meta_inf["inner_signature"])
|
NBPCrypto._verify_inner_sig(package_info, meta_inf, rsa_public_key_pem)
|
||||||
nir_digest = hashlib.sha256()
|
|
||||||
for mod_name in sorted(package_info["inner_encrypted"].keys()):
|
|
||||||
nir_digest.update(mod_name.encode())
|
|
||||||
nir_digest.update(package_info["inner_encrypted"][mod_name]["ciphertext"].encode())
|
|
||||||
if not NBPCrypto.inner_verify(nir_digest.digest(), inner_sig, rsa_public_key_pem):
|
|
||||||
raise NBPCryptoError("中层 RSA 签名验证失败,插件作者身份无法确认")
|
|
||||||
|
|
||||||
# 5. 中层解密:用 RSA 私钥解密 key2
|
# 4. 中层解密:key2
|
||||||
key2_encrypted = meta_inf["inner_encryption"]["encrypted_key"]
|
key2 = NBPCrypto.decrypt_key(
|
||||||
key2 = NBPCrypto.decrypt_key(key2_encrypted, rsa_private_key_pem)
|
meta_inf["inner_encryption"]["encrypted_key"], rsa_private_key_pem
|
||||||
key2_buf = bytearray(key2)
|
)
|
||||||
|
|
||||||
# 6. 派生 HMAC 密钥
|
# 5. 派生 HMAC 密钥 + 解密 NIR
|
||||||
hmac_key = NBPCrypto.derive_hmac_key(key1, key2)
|
hmac_key = NBPCrypto.derive_hmac_key(key1, key2)
|
||||||
# key1 已经擦除,key2 即将擦除
|
|
||||||
NBPCrypto._secure_wipe(bytearray(key2))
|
NBPCrypto._secure_wipe(bytearray(key2))
|
||||||
|
|
||||||
# 7. 解密 NIR 数据
|
nir_result = {
|
||||||
nir_result = {}
|
mod_name: NBPCrypto.inner_decrypt(enc_info, key2)
|
||||||
for mod_name, enc_info in package_info["inner_encrypted"].items():
|
for mod_name, enc_info in package_info["inner_encrypted"].items()
|
||||||
mod_data = NBPCrypto.inner_decrypt(enc_info, key2)
|
}
|
||||||
nir_result[mod_name] = mod_data
|
|
||||||
|
|
||||||
# 8. 内层验签
|
# 6. 内层验签
|
||||||
module_sigs = meta_inf.get("module_signatures", {})
|
module_sigs = meta_inf.get("module_signatures", {})
|
||||||
for mod_name, mod_data in nir_result.items():
|
for mod_name, mod_data in nir_result.items():
|
||||||
expected_sig = module_sigs.get(mod_name)
|
expected = module_sigs.get(mod_name)
|
||||||
if expected_sig:
|
if expected and not NBPCrypto.module_verify(mod_data, expected, hmac_key):
|
||||||
if not NBPCrypto.module_verify(mod_data, expected_sig, hmac_key):
|
|
||||||
raise NBPCryptoError(f"模块 '{mod_name}' HMAC 签名验证失败")
|
raise NBPCryptoError(f"模块 '{mod_name}' HMAC 签名验证失败")
|
||||||
|
|
||||||
return nir_result
|
return nir_result
|
||||||
|
|
||||||
|
# ── 内部工具方法 ──
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_nir_digest(inner_encrypted: dict) -> bytes:
|
||||||
|
d = hashlib.sha256()
|
||||||
|
for mod_name in sorted(inner_encrypted):
|
||||||
|
d.update(mod_name.encode())
|
||||||
|
d.update(inner_encrypted[mod_name]["ciphertext"].encode())
|
||||||
|
return d.digest()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_package_digest(outer_encrypted: dict, inner_encrypted: dict) -> bytes:
|
||||||
|
d = hashlib.sha256()
|
||||||
|
d.update(json.dumps(outer_encrypted).encode())
|
||||||
|
for mod_name in sorted(inner_encrypted):
|
||||||
|
d.update(mod_name.encode())
|
||||||
|
d.update(inner_encrypted[mod_name]["ciphertext"].encode())
|
||||||
|
return d.digest()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _verify_outer_sig(package_info: dict, ed25519_public_key: bytes):
|
||||||
|
sig = base64.b64decode(package_info["outer_signature"])
|
||||||
|
digest = NBPCrypto._build_package_digest(
|
||||||
|
package_info["outer_encryption"]["data"], package_info["inner_encrypted"]
|
||||||
|
)
|
||||||
|
if not NBPCrypto.outer_verify(digest, sig, ed25519_public_key):
|
||||||
|
raise NBPCryptoError("外层签名验证失败")
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _verify_inner_sig(package_info: dict, meta_inf: dict, rsa_public_key_pem: bytes):
|
||||||
|
sig = base64.b64decode(meta_inf["inner_signature"])
|
||||||
|
digest = NBPCrypto._build_nir_digest(package_info["inner_encrypted"])
|
||||||
|
if not NBPCrypto.inner_verify(digest, sig, rsa_public_key_pem):
|
||||||
|
raise NBPCryptoError("中层 RSA 签名验证失败")
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ from pathlib import Path
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from oss.logger.logger import Log
|
from oss.logger.logger import Log
|
||||||
from .crypto import NBPCrypto, NBPCryptoError
|
from .crypto import NBPCrypto, NBPCryptoError, _ModuleCache
|
||||||
from .compiler import NIRCompiler, NIRCompileError
|
from .compiler import NIRCompiler, NIRCompileError
|
||||||
|
|
||||||
|
|
||||||
@@ -148,11 +148,10 @@ class NBPFPacker:
|
|||||||
zf.writestr(NBPFFormatter.SIGNER_PEM, ed25519_public_key)
|
zf.writestr(NBPFFormatter.SIGNER_PEM, ed25519_public_key)
|
||||||
else:
|
else:
|
||||||
# 从私钥派生公钥
|
# 从私钥派生公钥
|
||||||
ed25519_mod = NBPCrypto._imp_ed25519()
|
key = _ModuleCache.ed25519().Ed25519PrivateKey.from_private_bytes(ed25519_private_key)
|
||||||
key = ed25519_mod.Ed25519PrivateKey.from_private_bytes(ed25519_private_key)
|
s = _ModuleCache.serialization()
|
||||||
pub_bytes = key.public_key().public_bytes(
|
pub_bytes = key.public_key().public_bytes(
|
||||||
NBPCrypto._imp_serialization().Encoding.Raw,
|
s.Encoding.Raw, s.PublicFormat.Raw
|
||||||
NBPCrypto._imp_serialization().PublicFormat.Raw
|
|
||||||
)
|
)
|
||||||
zf.writestr(NBPFFormatter.SIGNER_PEM, pub_bytes)
|
zf.writestr(NBPFFormatter.SIGNER_PEM, pub_bytes)
|
||||||
|
|
||||||
|
|||||||
@@ -79,8 +79,11 @@ class NBPFLoader:
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
with zipfile.ZipFile(nbpf_path, 'r') as zf:
|
with zipfile.ZipFile(nbpf_path, 'r') as zf:
|
||||||
|
# 0. 一次扫描,缓存 NIR 模块数据
|
||||||
|
nir_modules = self._read_nir_modules(zf)
|
||||||
|
|
||||||
# 1. 外层验签(先用包内公钥验签,再查信任状态)
|
# 1. 外层验签(先用包内公钥验签,再查信任状态)
|
||||||
signer_pub_key, is_trusted, trusted_name = self._verify_outer_signature(zf)
|
signer_pub_key, is_trusted, trusted_name = self._verify_outer_signature(zf, nir_modules)
|
||||||
status = "已信任" if is_trusted else "未信任"
|
status = "已信任" if is_trusted else "未信任"
|
||||||
Log.info("NBPF", f"外层签名验证通过 (signer: {trusted_name or 'unknown'}, {status})")
|
Log.info("NBPF", f"外层签名验证通过 (signer: {trusted_name or 'unknown'}, {status})")
|
||||||
|
|
||||||
@@ -89,7 +92,7 @@ class NBPFLoader:
|
|||||||
key1_buf = bytearray(key1)
|
key1_buf = bytearray(key1)
|
||||||
|
|
||||||
# 3. 中层验签(传入外层签名者名称,确保内外签名者一致)
|
# 3. 中层验签(传入外层签名者名称,确保内外签名者一致)
|
||||||
rsa_signer = self._verify_inner_signature(zf, meta_inf, trusted_name)
|
rsa_signer = self._verify_inner_signature(nir_modules, meta_inf, trusted_name)
|
||||||
Log.info("NBPF", f"中层签名验证通过 (signer: {rsa_signer})")
|
Log.info("NBPF", f"中层签名验证通过 (signer: {rsa_signer})")
|
||||||
|
|
||||||
# 4. 中层解密
|
# 4. 中层解密
|
||||||
@@ -102,7 +105,7 @@ class NBPFLoader:
|
|||||||
self.crypto._secure_wipe(key2_buf)
|
self.crypto._secure_wipe(key2_buf)
|
||||||
|
|
||||||
# 6. 解密 NIR 数据
|
# 6. 解密 NIR 数据
|
||||||
nir_data = self._decrypt_nir_data(zf, key2)
|
nir_data = self._decrypt_nir_data_raw(nir_modules, key2)
|
||||||
|
|
||||||
# 7. 内层验签
|
# 7. 内层验签
|
||||||
self._verify_module_signatures(nir_data, meta_inf, hmac_key)
|
self._verify_module_signatures(nir_data, meta_inf, hmac_key)
|
||||||
@@ -145,9 +148,19 @@ class NBPFLoader:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise NBPFLoadError(f"加载失败: {type(e).__name__}: {e}") from e
|
raise NBPFLoadError(f"加载失败: {type(e).__name__}: {e}") from e
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _read_nir_modules(zf: zipfile.ZipFile) -> dict[str, dict]:
|
||||||
|
"""一次扫描 ZIP 中所有 NIR 模块,缓存结果"""
|
||||||
|
modules = {}
|
||||||
|
for info in zf.infolist():
|
||||||
|
if info.filename.startswith(NBPFFormatter.NIR_DIR) and not info.filename.endswith("/"):
|
||||||
|
mod_name = info.filename[len(NBPFFormatter.NIR_DIR):]
|
||||||
|
modules[mod_name] = json.loads(zf.read(info.filename).decode("utf-8"))
|
||||||
|
return modules
|
||||||
|
|
||||||
# ── 外层验签 ──
|
# ── 外层验签 ──
|
||||||
|
|
||||||
def _verify_outer_signature(self, zf: zipfile.ZipFile) -> tuple[bytes, bool, str | None]:
|
def _verify_outer_signature(self, zf: zipfile.ZipFile, nir_modules: dict[str, dict]) -> tuple[bytes, bool, str | None]:
|
||||||
"""外层 Ed25519 签名验证
|
"""外层 Ed25519 签名验证
|
||||||
|
|
||||||
先用包内公钥验签(不依赖外部信任列表),验签通过后再检查信任状态。
|
先用包内公钥验签(不依赖外部信任列表),验签通过后再检查信任状态。
|
||||||
@@ -157,9 +170,6 @@ class NBPFLoader:
|
|||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(signer_pub_key_bytes, is_trusted, trusted_name)
|
(signer_pub_key_bytes, is_trusted, trusted_name)
|
||||||
- signer_pub_key_bytes: 签名者 Ed25519 公钥(用于上层判断信任)
|
|
||||||
- is_trusted: 公钥是否在本地信任列表中
|
|
||||||
- trusted_name: 信任列表中的名称(不信任时为 None)
|
|
||||||
"""
|
"""
|
||||||
if NBPFFormatter.SIGNATURE not in zf.namelist():
|
if NBPFFormatter.SIGNATURE not in zf.namelist():
|
||||||
raise NBPFLoadError("缺少外层签名文件")
|
raise NBPFLoadError("缺少外层签名文件")
|
||||||
@@ -169,29 +179,16 @@ class NBPFLoader:
|
|||||||
signature_b64 = zf.read(NBPFFormatter.SIGNATURE).decode().strip()
|
signature_b64 = zf.read(NBPFFormatter.SIGNATURE).decode().strip()
|
||||||
signer_pub_key = zf.read(NBPFFormatter.SIGNER_PEM)
|
signer_pub_key = zf.read(NBPFFormatter.SIGNER_PEM)
|
||||||
|
|
||||||
# 计算包摘要(与 full_encrypt_package 一致)
|
# 计算包摘要(使用缓存的 nir_modules)
|
||||||
encryption_data = json.loads(zf.read(NBPFFormatter.ENCRYPTION).decode("utf-8"))
|
encryption_data = json.loads(zf.read(NBPFFormatter.ENCRYPTION).decode("utf-8"))
|
||||||
digest = hashlib.sha256()
|
digest = NBPCrypto._build_package_digest(encryption_data["data"], nir_modules)
|
||||||
digest.update(json.dumps(encryption_data["data"]).encode())
|
|
||||||
|
|
||||||
# 按模块名排序,添加模块名和密文
|
# 直接用包内公钥验签
|
||||||
nir_modules = {}
|
|
||||||
for info in zf.infolist():
|
|
||||||
if info.filename.startswith(NBPFFormatter.NIR_DIR) and not info.filename.endswith("/"):
|
|
||||||
mod_name = info.filename[len(NBPFFormatter.NIR_DIR):]
|
|
||||||
mod_data = json.loads(zf.read(info.filename).decode("utf-8"))
|
|
||||||
nir_modules[mod_name] = mod_data
|
|
||||||
|
|
||||||
for mod_name in sorted(nir_modules.keys()):
|
|
||||||
digest.update(mod_name.encode())
|
|
||||||
digest.update(nir_modules[mod_name]["ciphertext"].encode())
|
|
||||||
|
|
||||||
# 直接用包内公钥验签(不依赖外部信任列表)
|
|
||||||
signature = base64.b64decode(signature_b64)
|
signature = base64.b64decode(signature_b64)
|
||||||
if not self.crypto.outer_verify(digest.digest(), signature, signer_pub_key):
|
if not self.crypto.outer_verify(digest, signature, signer_pub_key):
|
||||||
raise NBPFLoadError("外层签名验证失败,包可能被篡改")
|
raise NBPFLoadError("外层签名验证失败,包可能被篡改")
|
||||||
|
|
||||||
# 验签通过后,检查公钥是否在本地信任列表中
|
# 检查公钥是否在本地信任列表中
|
||||||
is_trusted = False
|
is_trusted = False
|
||||||
trusted_name = None
|
trusted_name = None
|
||||||
for name, trusted_key in self.trusted_ed25519_keys.items():
|
for name, trusted_key in self.trusted_ed25519_keys.items():
|
||||||
@@ -224,7 +221,7 @@ class NBPFLoader:
|
|||||||
|
|
||||||
# ── 中层验签 ──
|
# ── 中层验签 ──
|
||||||
|
|
||||||
def _verify_inner_signature(self, zf: zipfile.ZipFile, meta_inf: dict, ed25519_signer: str = None) -> str:
|
def _verify_inner_signature(self, nir_modules: dict[str, dict], meta_inf: dict, ed25519_signer: str = None) -> str:
|
||||||
"""中层 RSA-4096 签名验证,返回签名者名称
|
"""中层 RSA-4096 签名验证,返回签名者名称
|
||||||
|
|
||||||
签名计算方式与 full_encrypt_package 一致。
|
签名计算方式与 full_encrypt_package 一致。
|
||||||
@@ -232,7 +229,7 @@ class NBPFLoader:
|
|||||||
否则遍历所有信任的 RSA 密钥。
|
否则遍历所有信任的 RSA 密钥。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
zf: 打开的 ZIP 文件
|
nir_modules: 缓存的 NIR 模块数据
|
||||||
meta_inf: 解密后的 META-INF 数据
|
meta_inf: 解密后的 META-INF 数据
|
||||||
ed25519_signer: 外层 Ed25519 签名者名称
|
ed25519_signer: 外层 Ed25519 签名者名称
|
||||||
|
|
||||||
@@ -246,20 +243,8 @@ class NBPFLoader:
|
|||||||
if not inner_sig_b64:
|
if not inner_sig_b64:
|
||||||
raise NBPFLoadError("缺少中层签名")
|
raise NBPFLoadError("缺少中层签名")
|
||||||
|
|
||||||
# 计算 NIR 数据摘要(与 full_encrypt_package 一致)
|
# 使用缓存的 nir_modules 计算摘要
|
||||||
nir_modules = {}
|
nir_digest = NBPCrypto._build_nir_digest(nir_modules)
|
||||||
for info in zf.infolist():
|
|
||||||
if info.filename.startswith(NBPFFormatter.NIR_DIR) and not info.filename.endswith("/"):
|
|
||||||
mod_name = info.filename[len(NBPFFormatter.NIR_DIR):]
|
|
||||||
mod_data = json.loads(zf.read(info.filename).decode("utf-8"))
|
|
||||||
nir_modules[mod_name] = mod_data
|
|
||||||
|
|
||||||
nir_digest = hashlib.sha256()
|
|
||||||
for mod_name in sorted(nir_modules.keys()):
|
|
||||||
nir_digest.update(mod_name.encode())
|
|
||||||
nir_digest.update(nir_modules[mod_name]["ciphertext"].encode())
|
|
||||||
|
|
||||||
# 查找匹配的 RSA 公钥
|
|
||||||
inner_sig = base64.b64decode(inner_sig_b64)
|
inner_sig = base64.b64decode(inner_sig_b64)
|
||||||
|
|
||||||
# 优先使用与外层签名者同名的 RSA 密钥
|
# 优先使用与外层签名者同名的 RSA 密钥
|
||||||
@@ -267,11 +252,10 @@ class NBPFLoader:
|
|||||||
if ed25519_signer and ed25519_signer in self.trusted_rsa_keys:
|
if ed25519_signer and ed25519_signer in self.trusted_rsa_keys:
|
||||||
candidates.append((ed25519_signer, self.trusted_rsa_keys[ed25519_signer]))
|
candidates.append((ed25519_signer, self.trusted_rsa_keys[ed25519_signer]))
|
||||||
else:
|
else:
|
||||||
# 未指定或未找到同名密钥,遍历全部
|
|
||||||
candidates = list(self.trusted_rsa_keys.items())
|
candidates = list(self.trusted_rsa_keys.items())
|
||||||
|
|
||||||
for name, rsa_pub_key in candidates:
|
for name, rsa_pub_key in candidates:
|
||||||
if self.crypto.inner_verify(nir_digest.digest(), inner_sig, rsa_pub_key):
|
if self.crypto.inner_verify(nir_digest, inner_sig, rsa_pub_key):
|
||||||
return name
|
return name
|
||||||
|
|
||||||
raise NBPFLoadError("中层签名验证失败,无法匹配任何信任的 RSA 公钥")
|
raise NBPFLoadError("中层签名验证失败,无法匹配任何信任的 RSA 公钥")
|
||||||
@@ -291,21 +275,12 @@ class NBPFLoader:
|
|||||||
|
|
||||||
# ── 解密 NIR 数据 ──
|
# ── 解密 NIR 数据 ──
|
||||||
|
|
||||||
def _decrypt_nir_data(self, zf: zipfile.ZipFile, key2: bytes) -> dict[str, bytes]:
|
def _decrypt_nir_data_raw(self, nir_modules: dict[str, dict], key2: bytes) -> dict[str, bytes]:
|
||||||
"""解密 NIR 数据,返回 {module_name: nir_bytes}"""
|
"""解密 NIR 数据,使用缓存的模块数据"""
|
||||||
nir_data = {}
|
return {
|
||||||
for info in zf.infolist():
|
mod_name: self.crypto.inner_decrypt(enc_info, key2)
|
||||||
if not info.filename.startswith(NBPFFormatter.NIR_DIR):
|
for mod_name, enc_info in nir_modules.items()
|
||||||
continue
|
}
|
||||||
if info.filename.endswith("/"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
module_name = info.filename[len(NBPFFormatter.NIR_DIR):]
|
|
||||||
enc_info = json.loads(zf.read(info.filename).decode("utf-8"))
|
|
||||||
mod_data = self.crypto.inner_decrypt(enc_info, key2)
|
|
||||||
nir_data[module_name] = mod_data
|
|
||||||
|
|
||||||
return nir_data
|
|
||||||
|
|
||||||
# ── 内层验签 ──
|
# ── 内层验签 ──
|
||||||
|
|
||||||
|
|||||||
@@ -131,10 +131,10 @@ class PLInjector:
|
|||||||
for dangerous in ['import ', 'exec(', 'eval(', 'compile(', 'os.', 'sys.', 'subprocess']:
|
for dangerous in ['import ', 'exec(', 'eval(', 'compile(', 'os.', 'sys.', 'subprocess']:
|
||||||
if dangerous in decoded:
|
if dangerous in decoded:
|
||||||
raise PLValidationError(f"{file_path} - 检测到 base64 编码的恶意代码")
|
raise PLValidationError(f"{file_path} - 检测到 base64 编码的恶意代码")
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[PLInjector] 模块注入错误: {e}")
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[PLInjector] 错误: {e}")
|
||||||
|
|
||||||
# 检查字符串拼接绕过
|
# 检查字符串拼接绕过
|
||||||
concat_patterns = [
|
concat_patterns = [
|
||||||
|
|||||||
@@ -30,16 +30,16 @@ class NebulaShell(cmd.Cmd):
|
|||||||
"""加载命令历史记录"""
|
"""加载命令历史记录"""
|
||||||
try:
|
try:
|
||||||
readline.read_history_file(HISTORY_FILE)
|
readline.read_history_file(HISTORY_FILE)
|
||||||
except (FileNotFoundError, OSError):
|
except (FileNotFoundError, OSError) as e:
|
||||||
pass
|
print(f"[REPL] 文件操作失败: {e}")
|
||||||
readline.set_history_length(500)
|
readline.set_history_length(500)
|
||||||
|
|
||||||
def _save_history(self):
|
def _save_history(self):
|
||||||
"""保存命令历史记录"""
|
"""保存命令历史记录"""
|
||||||
try:
|
try:
|
||||||
readline.write_history_file(HISTORY_FILE)
|
readline.write_history_file(HISTORY_FILE)
|
||||||
except OSError:
|
except OSError as e:
|
||||||
pass
|
print(f"[REPL] 系统错误: {e}")
|
||||||
|
|
||||||
def _get_plugins(self):
|
def _get_plugins(self):
|
||||||
"""获取所有已加载的插件列表"""
|
"""获取所有已加载的插件列表"""
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class PluginManager:
|
|||||||
try:
|
try:
|
||||||
validator = get_validator()
|
validator = get_validator()
|
||||||
validator.unlock("deep_diver")
|
validator.unlock("deep_diver")
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[PluginManager] 错误: {e}")
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
"""启动 Core,它会初始化并启动所有其他插件"""
|
"""启动 Core,它会初始化并启动所有其他插件"""
|
||||||
@@ -57,8 +57,8 @@ class PluginManager:
|
|||||||
# 检查插件数量成就
|
# 检查插件数量成就
|
||||||
plugin_count = len(self.core.plugins)
|
plugin_count = len(self.core.plugins)
|
||||||
validator.check_plugin_count(plugin_count)
|
validator.check_plugin_count(plugin_count)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[PluginManager] 错误: {e}")
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
"""停止所有插件"""
|
"""停止所有插件"""
|
||||||
@@ -78,5 +78,5 @@ class PluginManager:
|
|||||||
try:
|
try:
|
||||||
validator = get_validator()
|
validator = get_validator()
|
||||||
validator.track_progress("session_end")
|
validator.track_progress("session_end")
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[PluginManager] 错误: {e}")
|
||||||
|
|||||||
@@ -66,8 +66,8 @@ class I18n:
|
|||||||
if domain not in self._translations:
|
if domain not in self._translations:
|
||||||
self._translations[domain] = {}
|
self._translations[domain] = {}
|
||||||
self._translations[domain].update(data)
|
self._translations[domain].update(data)
|
||||||
except (json.JSONDecodeError, OSError):
|
except (json.JSONDecodeError, OSError) as e:
|
||||||
pass
|
print(f"[i18n] 翻译文件加载失败: {e}")
|
||||||
self._loaded_domains.add(domain)
|
self._loaded_domains.add(domain)
|
||||||
|
|
||||||
def _find_translation_files(self, domain: str) -> list[str]:
|
def _find_translation_files(self, domain: str) -> list[str]:
|
||||||
|
|||||||
@@ -59,8 +59,8 @@ class PluginStorage:
|
|||||||
data = json.loads(file_path.read_text(encoding="utf-8"))
|
data = json.loads(file_path.read_text(encoding="utf-8"))
|
||||||
self._mem_cache[plugin_name][key] = data
|
self._mem_cache[plugin_name][key] = data
|
||||||
return data
|
return data
|
||||||
except (json.JSONDecodeError, OSError):
|
except (json.JSONDecodeError, OSError) as e:
|
||||||
pass
|
print(f"[PluginStorage] 读取缓存失败: {e}")
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def delete(self, plugin_name: str, key: str) -> bool:
|
def delete(self, plugin_name: str, key: str) -> bool:
|
||||||
@@ -90,8 +90,8 @@ class PluginStorage:
|
|||||||
for f in pd.glob("*.json"):
|
for f in pd.glob("*.json"):
|
||||||
try:
|
try:
|
||||||
f.unlink()
|
f.unlink()
|
||||||
except OSError:
|
except OSError as e:
|
||||||
pass
|
print(f"[PluginStorage] 文件删除失败: {e}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_raw(self, plugin_name: str, file_name: str, data: bytes) -> bool:
|
def set_raw(self, plugin_name: str, file_name: str, data: bytes) -> bool:
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ class WsApi:
|
|||||||
async for message in websocket:
|
async for message in websocket:
|
||||||
await self._dispatch(websocket, message, addr)
|
await self._dispatch(websocket, message, addr)
|
||||||
except websockets.exceptions.ConnectionClosed:
|
except websockets.exceptions.ConnectionClosed:
|
||||||
pass
|
pass # WebSocket 正常断连
|
||||||
finally:
|
finally:
|
||||||
Log.info("WsApi", f"WebSocket 断开: {addr}")
|
Log.info("WsApi", f"WebSocket 断开: {addr}")
|
||||||
for topic in list(self._connections.keys()):
|
for topic in list(self._connections.keys()):
|
||||||
@@ -123,8 +123,8 @@ class WsApi:
|
|||||||
async def _send(self, websocket, data: dict):
|
async def _send(self, websocket, data: dict):
|
||||||
try:
|
try:
|
||||||
await websocket.send(json.dumps(data, ensure_ascii=False))
|
await websocket.send(json.dumps(data, ensure_ascii=False))
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[WsApi] 连接处理错误: {e}")
|
||||||
|
|
||||||
def register_handler(self, msg_type: str, handler: Callable):
|
def register_handler(self, msg_type: str, handler: Callable):
|
||||||
self._handlers[msg_type] = handler
|
self._handlers[msg_type] = handler
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>NebulaShell v1.1.0 | 控制台</title>
|
<title>NebulaShell v1.2.0 | 控制台</title>
|
||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
--bg: #ffffff;
|
--bg: #ffffff;
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<header>
|
<header>
|
||||||
<h1>NebulaShell</h1>
|
<h1>NebulaShell</h1>
|
||||||
<p class="subtitle">v1.1.0 安全全能发行版 · 企业级插件化运行时</p>
|
<p class="subtitle">v1.2.0 · 插件化运行时框架 · 多重签名加密分发</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="status-bar">
|
<div class="status-bar">
|
||||||
@@ -158,11 +158,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="status-item">
|
<div class="status-item">
|
||||||
<span class="status-label">版本</span>
|
<span class="status-label">版本</span>
|
||||||
<span class="status-value">1.1.0</span>
|
<span class="status-value">1.2.0</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-item">
|
<div class="status-item">
|
||||||
<span class="status-label">活跃插件</span>
|
<span class="status-label">活跃插件</span>
|
||||||
<span class="status-value">13</span>
|
<span class="status-value">5</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-item">
|
<div class="status-item">
|
||||||
<span class="status-label">运行时间</span>
|
<span class="status-label">运行时间</span>
|
||||||
@@ -204,7 +204,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<footer>
|
<footer>
|
||||||
<p>NebulaShell v1.1.0 Security All-in-One Edition</p>
|
<p>NebulaShell v1.2.0 · Apache-2.0 License</p>
|
||||||
<p style="margin-top: 8px;">Built with ❤️ · MIT License</p>
|
<p style="margin-top: 8px;">Built with ❤️ · MIT License</p>
|
||||||
</footer>
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -113,8 +113,8 @@ def _collector_loop(interval: float = 5.0):
|
|||||||
try:
|
try:
|
||||||
stats = _collect_stats()
|
stats = _collect_stats()
|
||||||
_history.append(stats)
|
_history.append(stats)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[SystemMonitor] 监控数据采集错误: {e}")
|
||||||
time.sleep(interval)
|
time.sleep(interval)
|
||||||
|
|
||||||
|
|
||||||
@@ -316,8 +316,8 @@ def _run_http_server(host: str, port: int):
|
|||||||
_http_server = server
|
_http_server = server
|
||||||
try:
|
try:
|
||||||
server.serve_forever()
|
server.serve_forever()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[SystemMonitor] 服务启动错误: {e}")
|
||||||
|
|
||||||
|
|
||||||
# ── 生命周期 ──
|
# ── 生命周期 ──
|
||||||
@@ -368,8 +368,8 @@ def stop():
|
|||||||
if _http_server:
|
if _http_server:
|
||||||
try:
|
try:
|
||||||
_http_server.shutdown()
|
_http_server.shutdown()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
print(f"[SystemMonitor] 服务停止错误: {e}")
|
||||||
_http_server = None
|
_http_server = None
|
||||||
|
|
||||||
_history.clear()
|
_history.clear()
|
||||||
|
|||||||
@@ -96,12 +96,6 @@ def test_input_validation():
|
|||||||
print("\n=== 测试输入验证功能 ===")
|
print("\n=== 测试输入验证功能 ===")
|
||||||
print("输入验证中间件尚未实现,跳过测试")
|
print("输入验证中间件尚未实现,跳过测试")
|
||||||
return True
|
return True
|
||||||
print("✅ 输入验证中间件正常工作")
|
|
||||||
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ 输入验证测试失败: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def test_middleware_chain():
|
def test_middleware_chain():
|
||||||
|
|||||||
Reference in New Issue
Block a user