Files
mtgodot-poc/audit/contracts/network.security-encoding.md
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

16 KiB
Raw Blame History

network.security_encoding

Scope

完整核对 40250 客户端的握手时间基准、后续 time sync、sequence 表和 bSeq 规则、_IMPROVED_PACKET_ENCRYPTION_ 的 DH2/CTR、未启用 improved encryption 时的 TEA 安全分支、HybridCrypt/pack 注入、locale codepage、定长字符串以及连接清理。判断标准是调用链、分支条件、状态转移顺序、时钟/资源来源和失败副作用是否一致;“当前端有同名算法”不等于已对齐。

Reference call chain

  • UserInterface/PythonNetworkStreamPhaseHandShake.cpp
    • CPythonNetworkStream::HandShakePhase
    • CPythonNetworkStream::RecvHandshakePacket
    • CPythonNetworkStream::RecvHandshakeOKPacket
    • CPythonNetworkStream::RecvHybridCryptKeyPacket
    • CPythonNetworkStream::RecvHybridCryptSDBPacket
    • CPythonNetworkStream::RecvKeyAgreementPacket
    • CPythonNetworkStream::RecvKeyAgreementCompletedPacket
  • UserInterface/PythonNetworkStreamPhaseLogin.cpp, PythonNetworkStreamPhaseSelect.cpp, PythonNetworkStreamPhaseGame.cpp
    • 各阶段 HEADER_GC_HANDSHAKEHEADER_GC_HANDSHAKE_OK、HybridCrypt 和 key-agreement allowlist;每个 packet handler 完成后立即从该阶段处理函数返回。
  • UserInterface/AccountConnector.cpp
    • auth connector 的重复 handshake、ping/sequence、legacy security、登录成功后的 Panama key 和 game stream 切换。
  • EterLib/NetStream.cpp
    • CNetworkStream::SetSecurityModeIsSecurityModeSendSequence__SendInternalBufferClearConnectActivateCipher
  • EterBase/cipher.cpp, cipher.h
    • DH2 Prepare/Agree、算法选择、密钥/IV 派生和 CTR stream 状态。
  • EterBase/tea.cpp
    • 未定义 _IMPROVED_PACKET_ENCRYPTION_ 时的收发 TEA 缓冲区及 8-byte 对齐。
  • EterLocale/StringCodec.cppUserInterface/Locale*.h/.cpp
    • Windows WideCharToMultiByte/MultiByteToWideChar、CP1258 特殊处理和 locale codepage 来源。

40250 的具体时钟语义:初始 handshake 设置全局 ELTimer_SetServerMSec(dwTime + lDelta),回显 dwTime + 2*lDeltalDelta=0,之后调用 CTimer::SetBaseTime()Select/Game 的 handshake 保存 m_kServerTimeSync,发送 HEADER_CG_TIME_SYNC 并追加 sequence;收到 HEADER_GC_HANDSHAKE_OK 后用收到 blank 的本地耗时重新设置全局 server msec。这个全局 owner 和二次校准不能用“本地 stream 能计算一个相对时间”替代。

Current call chain

  • extension/src/net/classic/classic_stream.cpp/.h
    • handle_control 处理 handshake、HDR_GC_TIME_SYNC、key agreement/completed、ping、phaseserver_frame_mssteady_clock 与每个 ClassicStream 的两个基准值计算。
    • emit_bytes 在进入发送队列时立即加密;flush_send 只负责写 socket。
  • extension/src/net/classic/classic_cipher.cpp/.h
    • 当前实现包含 40250 DH2/CTR 所需的 RFC 5114 参数、算法 selector、key/IV 派生和双向 CTR 状态。
  • extension/src/net/classic/sequence_table.h, wire_classic.h
    • 内置 32768-byte sequence 表和 is_sequence_cg 规则。
  • extension/src/net/classic/classic_session.cpp, extension/src/net/m2_client.cpp
    • phase/版本发送、time-sync mode 和 Godot 网络生命周期。
  • extension/src/net/secure_cipher.cpp/.h
    • 另一套 libsodium X25519/XChaCha20-Poly1305 backend;它不是 40250 EterBase::Cipher 的替代实现,不能作为 classic improved-encryption 的等价证据。
  • extension/src/net/text_codec.h/.cpp
    • classic fixed text now crosses one explicit UTF-8 -> GB2312/GBK wire boundary; fixed fields are capped after conversion and decoded back to UTF-8.

Branch matrix

场景 40250 完整行为 当前端实际行为 结论
初始 handshake 读取固定包,设置全局 ELTimer,回显 time+2*delta/delta=0,调用 CTimer::SetBaseTimeauth connector 也有自己的同类路径 保存 m_server_time_base/m_client_time_base,回显字节基本一致;没有全局 ELTimer/CTimer ownerauth/game 也没有统一时钟服务 PARTIAL:wire 主体接近,时间副作用和 owner 不同
Select/Game 后续 handshake 保存 m_kServerTimeSync,发 0xFC + handshake + sequence;收到 blank HANDSHAKE_OK 后按 changeClientTime 到达时间重新设置全局 server clock ClassicStream 暂存后续 handshake 的 server/client 基准,发 0xFC/sequence;收到 blank 后才提交二次校准 PARTIAL:stream 语义已对齐,仍缺少全局 clock owner/跨 connector fixture
improved DH2/CTR PrepareActivate→发送固定 key-agreement reply→收到 completed 后 ActivateCipher;completed 前后的缓冲和状态顺序由 CNetworkStream 控制 ClassicCipher 的 DH2/selector/派生公式接近 referencestream 也实现 reply/completed/已缓冲密文解密 PARTIAL:算法可做双端 round-trip,但没有 40250 server fixture、固定边界/失败分支和真实分片互操作证据
improved 发送时点 明文先进入 send buffer__SendInternalBuffer 在真正 flush 时按当前 security mode 加密 emit_bytes 在排队时立即加密,随后再 flush PARTIAL:通常字节顺序相同,但队列、重复 flush、切换 security mode 和部分写时点不是同一语义
legacy TEA 未定义 improved 时 SetSecurityMode 保存独立收发 key;接收按 8-byte 缓冲解密,发送按 8-byte 对齐/缓存后发送;auth/game 均可启用 classic 侧没有完整的 SetSecurityMode/TEA 收发缓冲调用链;classic_cipher 中的 TEA 只是可选 DH2 algorithm selector,不是 legacy stream mode PARTIAL:分支没有实现为同一协议路径
HybridCrypt keys/SDB handshake、login、select、game 阶段均允许动态包;消费固定头+精确 stream 长度,并注入 CEterPackManager header 在 wire table 中可识别,但 classic session/parser 没有等价 pack manager 注入,当前会拒绝或忽略该可选资源分支 PARTIAL:动态 framing 不是完整功能等价
Panama pack/login key auth 成功按 loginKey XOR g_adwEncryptKey[0..3] 解密 pack IV,设置 login key,再连接 game stream 当前登录/资源主链没有 40250 Panama pack IV 解密和同一 auth→game 交接证据 PARTIAL:资源/密钥副作用缺失或未证明
anti-cheat/HackShield/XTrap 40250 handshake/game allowlist 有条件编译的 HS/XTrap request/response 分支 header/尺寸部分存在,但没有 Windows anti-cheat provider 或完整 response 处理 PARTIAL:条件分支未等价
sequence table SEQUENCE_TABLE_SIZE=32768SendSequence 在 bSeq 开启后逐包取表项、到末尾回 0bSeq 来自 packet registration 当前内置 32768 表并按 is_sequence_cg 添加 trailer;代码注释声称 verbatim,但还没有和 40250 源数组的 hash/逐项生成校验 PARTIAL:主路径存在,证据和注册矩阵未闭合
版本包 根据 locale/region 选择 CG_CLIENT_VERSION2CG_CLIENT_VERSION,固定字段按 Windows locale 字节复制 当前已按 m_locale_is_europe 选择两个包,字段走 UTF-8 to_wire PARTIALheader/分支存在,非 ASCII 字节语义仍不同
locale 转码 LocaleService_GetCodePage + Windows codepage 转换;中文固定字段使用 GB2312-compatible bytes;固定字段本质是 byte array/strncpy classic 使用 Windows CP936 或 POSIX iconv 的 GB2312/GBK 转码;Godot/Native 内部仍保持 UTF-8;其它 locale codepage 尚未接入 PARTIAL:40250 中文固定字段已对齐,非中文 locale 仍不同
fixed string/NUL reference 对固定 char[] 以字节 cap、NUL 结尾和 struct zero-fill 为准;不可表示字符遵循 Win32 codepage 策略 classic to_wire() 在 GB2312 字节上限后截断且结构体 zero-fill;wire_text_fits() 按编码后字节拒绝超长/不可表示值;无系统 codec 的平台拒绝高位字节而不泄漏 UTF-8 PARTIAL:中文字段已对齐,fallback/其它 locale 仍需真实 fixture
cleanup/failure Clear 清 cipher、socket、TEA buffer、recv/send positions 和 sequencekey agreement/发送失败按阶段返回并由上层断开 disconnect 清 stream/cipher/buffer/sequence;缺少 legacy buffer、global timer、HybridCrypt/pack 和 auth connector 分层清理矩阵 PARTIAL

Equivalence matrix

项目 状态 证据
Preconditions PARTIAL classic 对固定长度、DH2 key-ready、completed 顺序有保护;legacy/hybrid/anti-cheat 和阶段 allowlist 不完整
Branch structure PARTIAL initial/time-sync/improved 主分支存在;TEA、HybridCrypt、Panama、anti-cheat 分支没有完整等价实现
Algorithms/formulas PARTIAL ClassicCipher 的 DH2/CTR 公式与参数接近 reference;缺少 40250 server 密文 fixture,且 legacy TEA stream 没有主链
State transition order PARTIAL completed 后激活和已有缓冲解密存在;global clock 二次校准、auth/game 双 connector、flush 时加密时点不同
Constants/units PARTIAL handshake/header/32768 sequence 常量已定位;locale byte cap、TEA block/buffer 和 optional packet size 规则未闭合
Timing/event sources PARTIAL 当前每个 stream 使用 steady_clock,并在 HANDSHAKE_OK 提交暂存基准;40250 使用全局 ELTimerauth/game 共用时钟 owner 仍不同
Resource/data sources PARTIAL key/sequence/version 来源已定位;当前没有真实 locale codepage 和 EterPack HybridCrypt/Panama 注入来源
Protocol side effects PARTIAL handshake/key reply/ping/version 有路径;clock、pack IV、HybridCrypt 和 anti-cheat side effect 缺失或未证明
Interruption/failure/cleanup PARTIAL classic disconnect 会清基本状态;TEA 对齐缓存、auth connector 和可选资源的失败/重入/断开组合未覆盖

Evidence already available

  • extension/tests/net_classic_cipher_test.cppclient/server polarity 的 DH2 round-trip、CTR 连续性和新会话密钥差异。
  • extension/tests/net_classic_encstream_test.cpphandshake、key agreement、completed、已缓冲密文和 sequence 的 stream happy path。
  • extension/tests/net_text_codec_test.cpp:12 个中文字符在 24 个 GB2312 字节内 round-trip、13 个字符被拒绝、真实中文名称和 32 字节商店招牌边界。
  • extension/tests/net_classic_session_test.cpp:角色创建/改名包验证实际写入 GB2312 字节、NUL 结尾和 24 字节边界。
  • extension/src/net/classic/sequence_table.h:32768 项静态表和编译期长度断言;audit_sequence_table.py 已逐字节比对 40250 数组,SHA-256 为 3f6f31964896e712f1f54cb813c7b488b656f700f6ca948ece3313419f876279

Deep audit round 2026-09-20

本轮重新检查 ClassicStream::handle_control/emit_bytesClassicCipher、sequence 表和文本边界,并重跑当前加密/编码回归:

  • net_classic_cipher_testnet_classic_encstream_testnet_text_codec_test 均退出码 0;它们证明当前 DH2/CTR round-trip、连续 CTR、key agreement/已缓存密文、sequence trailer 和 UTF-8 codepoint-safe 截断没有回归。
  • 当前 HANDSHAKE_OK 已提交后续 handshake 的暂存 server clockserver_frame_ms() 仍是每个 ClassicStream 的 steady-clock 相对计算,尚未接入 40250 CTimer/ELTimer 全局 owner。net_classic_stream_test 已覆盖 blank 前不跳时钟、blank 后完成校准。
  • 当前 emit_bytes() 在进入发送队列时加密,参考端在真正 flush internal buffer 时按当前 security mode 加密;在正常单次发送中可能得到相同字节,但切换 security mode、部分写、重复 flush 和失败清理的状态边界不同。
  • classic_cipher 中的 TEA 仅作为可选算法 selector,当前 classic stream 没有 40250 legacy SetSecurityMode 的独立收发 TEA block buffer 主链;HybridCrypt/SDB、Panama pack IV、HS/XTrap 和其它 locale codepage 也没有完整注入/消费路径。中文 40250 fixed text 已由 text_codec.cpp 在网络边界转为 GB2312/GBK-compatible bytes。
  • 32768 项 sequence 表已通过 reference 数组逐字节校验;主要 bSeq 分支仍依赖当前 wire 注册表,未完成所有版本/locale/可选包的生成式矩阵。

结论:改进加密的局部算法和当前 wire framing 可用,但全局时钟、加密时点、legacy/optional security 分支、codepage 和 sequence 参考数据仍未闭合,合同保持 PARTIAL

Required tests before claiming parity

  • 使用 40250 wire fixture 验证初始 handshake、Select/Game handshake、HANDSHAKE_OK 二次校准和实际 server-frame 时钟;当前已有 socket-free 二次校准回归。
  • 用 40250 server polarity fixture 验证 DH2/CTRsplit recv/send、flush 前后加密、重复 key agreement、completed 前后明文、非法长度、失败清理和 reconnect。
  • 保持 40250 sequence 数组逐字节校验,并补齐从 PacketInfo/CMainPacketHeaderMap 生成的完整 bSeq 矩阵。
  • 为 CP949/CP1252/CP1258 等非中文部署建立 reference byte fixtures;中文 GB2312 已覆盖不可表示字符、NUL、byte cap、固定字段 zero-fill 和反向 decode。
  • 对 HybridCrypt keys/SDB、Panama pack、legacy TEA、HS/XTrap 明确支持/拒绝策略,并分别验证不乱 consume、不把包流错位、不产生错误的成功状态。

Status

PARTIAL

本轮已修复后续 handshake 的二次时钟校准,并加入 40250 sequence table 逐字节审计;DH2/CTR 的局部算法相似性仍不能覆盖全链路差异,在补齐全局 clock、locale、legacy/optional 分支和真实 wire fixture 前,合同保持 PARTIAL

Implementation fix round 2026-09-21 — Chinese fixed-field wire encoding

按 40250 中文客户端的 LocaleService_GetCodePage/固定 char[] 语义补齐文本边界:Godot 和 native 业务层继续以 UTF-8 保存文本,text_codec.cpp 在 classic 发送前按 Windows CP936 或 POSIX GB2312/GBK 转码;wire_text_fits() 在转码后按字节容量校验,to_wire() 只写完整的 GB2312 字符并依靠 zero-initialized packet 保留 NUL。接收路径用同一 codepage 解回 UTF-8。没有系统 codepage codec 的平台不会把高位 UTF-8 字节直接发送到 40250 字段,而是将该值标记为不可表示。

角色创建、改名以及聊天、密语、公会、商店等 classic 固定/动态文本发送入口均使用这个边界;地图名、任务名和系统提示仍按各自动态包长度处理,不套用 24 字节角色名限制。

回归:net_text_codec_testnet_classic_session_testnet_classic_stream_testnet_loopback_test、CTest 23/23、Godot 生命周期/角色选择/移动回归和 git diff --check 通过。中文角色名实际创建/改名包已证明 12 个汉字占 24 个 GB2312 字节,13 个汉字被拒绝。合同仍保持 PARTIAL,因为 legacy TEA、可选安全分支、其它 locale codepage 和真实服务端中文建号包序仍未完全闭合。

Implementation fix round 2026-09-21

按 40250 RecvHandshakePacket/RecvHandshakeOKPacket 修复 ClassicStreamSelect/Game 后续 handshake 只保存 pending server/client 基准,发送 CG_TIME_SYNC 后等待一字节 HANDSHAKE_OK,再按到达耗时提交 server clock;断开时清理 pending 状态,避免下次连接复用旧校准。新增 audit_sequence_table.py,对 40250 EterLib/NetStream.cpp 的 32768 项 sequence table 与当前静态表逐字节比较,当前两端 SHA-256 完全一致。

回归:net_classic_stream_testnet_classic_session_test、sequence table audit 和 git diff --check 通过。全局 ELTimer/CTimer owner、legacy TEA、HybridCrypt/Panama/HS-XTrap、Windows codepage、flush 时加密时点和真实 server fixture 仍保持 PARTIAL