重大重构:引擎模块拆分 + P0插件实现 + 55个Bug修复

核心变更:
- engine.py(1781行)拆分为8个独立模块: lifecycle/security/deps/
  datastore/pl_injector/watcher/signature/manager
- 新增plugin-bridge: 事件总线 + 服务注册 + RPC通信
- 新增i18n: 国际化/多语言翻译支持
- 新增plugin-storage: 插件键值/文件存储
- 新增ws-api: WebSocket实时通信(pub/sub + 自定义处理器)
- nodejs-adapter统一为Plugin ABC模式

Bug修复:
- 修复load_all()中store_dir未定义崩溃
- 修复DependencyResolver入度计算(拓扑排序)
- 修复PermissionError隐藏内置异常
- 修复CORS中间件头部未附加到响应
- 修复IntegrityChecker跳过__pycache__目录
- 修复版本号不一致(v2.0.0→v1.2.0)
- 修复测试文件的Logger导入/路径/私有方法调用
- 修复context.py缺少typing导入
- 修复config.py STORE_DIR默认路径(./mods→./store)

测试覆盖: 14→91个测试, 全部通过
This commit is contained in:
Falck
2026-05-12 11:40:06 +08:00
parent 3a096f59a9
commit bce27db4ac
57 changed files with 3669 additions and 2367 deletions

View File

@@ -137,6 +137,16 @@ class NBPCrypto:
_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
@@ -225,27 +235,38 @@ class NBPCrypto:
@staticmethod
def derive_hmac_key(key1: bytes, key2: bytes) -> bytes:
"""从两个 AES 密钥派生 HMAC 密钥"""
# 使用 HKDF-like 派生
dig = hashlib.sha256()
dig.update(key1)
dig.update(key2)
dig.update(b"NebulaHMACv1")
return dig.digest()
"""从两个 AES 密钥派生 HMAC 密钥(使用标准 HKDF"""
hkdf_mod = NBPCrypto._imp_hkdf()
hashes_mod = NBPCrypto._imp_hashes()
backends = NBPCrypto._imp_backends()
# 组合两个密钥作为输入密钥材料
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)
# ── AES-256-GCM 加密/解密 ──
@staticmethod
def _aes_encrypt(data: bytes, key: bytes) -> Tuple[bytes, bytes, bytes]:
"""AES-256-GCM 加密,返回 (nonce, ciphertext, tag)"""
"""AES-256-GCM 加密,返回 (nonce, ciphertext, tag)
注意cryptography 库的 AESGCM.encrypt() 返回 ciphertext || tag不含 nonce
nonce 需要由调用方管理并传入 decrypt。
"""
aead_mod = NBPCrypto._imp_crypto()
aesgcm = aead_mod.AESGCM(key)
nonce = os.urandom(NBPCrypto._aes_nonce_len())
ciphertext = aesgcm.encrypt(nonce, data, None)
# AESGCM.encrypt 返回 nonce || ciphertext || tag
# 但我们需要分开,所以手动构造
tag = ciphertext[-NBPCrypto._aes_tag_len():]
ct = ciphertext[:-NBPCrypto._aes_tag_len()]
# AESGCM.encrypt(nonce, data, aad) → ciphertext + tag
combined = aesgcm.encrypt(nonce, data, None)
tag = combined[-NBPCrypto._aes_tag_len():]
ct = combined[:-NBPCrypto._aes_tag_len()]
return nonce, ct, tag
@staticmethod
@@ -514,7 +535,7 @@ class NBPCrypto:
"inner_signature": base64.b64encode(inner_signature).decode(),
"inner_encryption": meta_inf["inner_encryption"],
"module_signatures": module_sigs,
"hmac_key_derivation": "SHA256(key1+key2+NebulaHMACv1)",
"hmac_key_derivation": "HKDF-SHA256(ikm=key1+key2, info=NebulaShell:NBPF:HMAC:v1)",
}
# ── 完整解密流程(加载时使用) ──
@@ -524,8 +545,19 @@ class NBPCrypto:
package_info: dict,
ed25519_public_key: bytes,
rsa_private_key_pem: bytes,
rsa_public_key_pem: bytes = None,
) -> dict[str, bytes]:
"""完整解密流程,返回 NIR 数据字典 {module_name: nir_bytes}"""
"""完整解密流程,返回 NIR 数据字典 {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():
@@ -554,15 +586,15 @@ class NBPCrypto:
meta_inf = json.loads(meta_inf_bytes.decode("utf-8"))
# 4. 中层验签
inner_sig = base64.b64decode(meta_inf["inner_signature"])
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())
# 需要 RSA 公钥来验签,从 meta_inf 中获取
# 实际使用时RSA 公钥应该从信任的密钥目录加载
# 这里假设调用者已经验证过 RSA 公钥
# 4. 中层验签(如果提供了 RSA 公钥)
if rsa_public_key_pem:
inner_sig = base64.b64decode(meta_inf["inner_signature"])
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
key2_encrypted = meta_inf["inner_encryption"]["encrypted_key"]