Files
banban/docs/talkingq_wss.md
T
chenjintao 551a7bb1e6 Prefer X25519 for legacy WSS full handshakes
Prioritize X25519 while retaining enabled curve fallbacks and log the negotiated ECDHE group. Extend ABI checks, crypto benchmarks and backend group probes, and document the updated APP package.
2026-09-16 15:32:22 +08:00

14 KiB
Raw Blame History

旧板 WSS 适配与联调

本次修改位于 legacy-board-wss(基于 legacy-board),默认地址为 wss://banban.api.talkingq.com:443/ws。沿用无 GNSS、默认音量 100 的旧板配置。上板暴露内核缺少 ECDSA 后,已补齐内核 TLS 能力并统一 APP/内核配置;本次必须更新内核和 APP。

后台实测(2026-09-16)

地址 结果
wss://banban.api.talkingq.com:443/ws 11:32 复测:TLS 1.2、证书链、域名、WebSocket Accept 校验通过,返回 HTTP 101;此前 404 已解决
wss://banban.api.talkingq.com:8080/ws TLS 握手失败,端口未提供可用的 TLS 服务
ws://banban.api.talkingq.com:8080/ws 明文 Upgrade 返回 HTTP 101,Accept 值正确

8080 当前是明文 WebSocket 服务,443 的 WSS 入口已可用。测试没有发送设备身份或业务数据,也没有修改服务器。

若 nginx 与 8080 服务部署在同一主机,可在现有 listen 443 ssl 的域名配置中参考以下转发(由后台按真实部署调整):

location /ws {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_read_timeout 300s;
}

可重新运行 powershell.exe -NoProfile -ExecutionPolicy Bypass -File tools\probe_talkingq_wss.ps1。必须得到有效 HTTP 101 和匹配的 Sec-WebSocket-Accept;仅 HTTPS 打得开或证书正常不代表 WebSocket 已可用。

上板 -9774 的修复

11:42 日志中,联网和 MQTT 正常,WSS 在解析根 CA 时返回 -9774(-0x262E:未知签名算法)。ISRG Root X2 使用 ECDSA/SHA-384,原内核 mini TLS 配置没有编译 ECDSA。此前主机 OpenSSL 验证通过,仅能证明证书链有效,不能证明内核支持它。

  • 新增 ql-config/config/talkingq_mbedtls_config.h,由 APP 和内核 mini profile 共用;增加 ECDSA、ECDHE-ECDSA、AES-GCM 和证书日期校验。
  • 内核缺失的 mbedtls/ecdsa.h 从 APP 中同版本 2.28.9 SDK 头文件补入。
  • ARMCC 编译内核头文件、GCC 编译 APP 头文件,对比 10 种 TLS 类型的大小和关键字段偏移,防止调用内核 API 时结构体布局不一致。
  • 内核的 GetCurrentCompTime() 在此产品上是空 shim。X.509 时间检查改用 mbedtls_time() 的真实 RTC 时间和 UTC 转换,保留证书日期验证。
  • 日志增加失败阶段及十六进制错误码;CA 解析成功会输出 WSS root CA parsed successfully。

不能仅刷 APP 修复本次问题,也不能只用未修改配置的旧内核重新打包。

固件行为

2026-09-16 14:06 板端已通过根 CA 解析、TLS 握手、服务器证书和域名校验。后续 recv data failed / HTTP 0 定位到 APP 接收函数:TLS 返回 WANT_READ/WANT_WRITE 后跳出循环,错误变量仍为初始值 -1,导致误断线。现已在该分支明确清除错误状态,由原握手循环继续等待;真实 EOF、TLS 错误和握手超时仍会结束连接。

  • inc/talkingq_wss.h 集中定义默认地址。解析独立处理域名、端口、路径,默认端口 443、无路径时默认 /ws;拒绝明文 URL、非法端口、过长字段和 CR/LF 注入。现有存储容量为 64 字节;不支持 IPv6 字面量。
  • hal_os_get_talkingq_url() 在没有保存地址、或保存值与已知旧默认地址完全匹配时,返回新默认 WSS 地址。此迁移在读取时生效,不改写 Flash。自定义地址保留;自定义明文地址会明确失败,不自动降级。
  • 启用 librws 的 mbedTLS 实现,统一公共和私有头文件中的宏。TCP 继续使用现有蜂窝 PDP profile 1,避免 SDK 定制 mbedtls_net_connect 的 CID/端口 ABI 差异。
  • 设置 SNI 和预期服务器域名,使用 MBEDTLS_SSL_VERIFY_REQUIRED 和 TLS 1.2 下限。没有客户端证书、客户端私钥或双向 TLS。
  • 使用现有 NITZ/RTC 时间检查证书有效期;时间明显未初始化会拒绝连接并打印原因。不会通过关闭校验来绕过时间问题。上板需要确认运营商时间同步有效。
  • 修复 TLS 短写、WANT_READ/WANT_WRITE、断线处理与资源清理;TLS 握手最多等待 20 秒,HTTP Upgrade 最多 10 秒,应用连接等待 45 秒。DNS/TCP 调用和任务调度还会消耗时间,45 秒不是所有底层调用的硬性总期限。
  • WSS 每次产生随机 WebSocket nonce,校验 HTTP 101、Upgrade、Connection、对应 Accept;支持拆分到多个 TLS 记录的响应头,保留响应头后的 WebSocket 数据。
  • 连接失败唤醒等待方;超时停止待连接 socket,回调只处理对应 socket。工作任务栈从 4 KiB 增至 16 KiB,给 TLS 证书和密码运算留出空间;仍需上板观察实际栈和堆余量。
  • WebSocket 成功后,仍由原 sendAuthentication() 发送 device_id、serial_number。本次不改变 MQTT、HTTP 下载及其他业务连接。

TLS 握手耗时优化(2026-09-16)

14:20 板端日志中,TCP 在 14:20:39.114 已连接,TLS 在 14:20:48.752 左右完成,约 9.6 秒;HTTP 101 在 14:20:48.894 完成,约再耗时 0.1 秒。原 socket break 仅表示退出 TCP 连接重试循环,现改为 TCP connected; leaving connect loop。这不是固定 10 秒延时,也不是 WebSocket HTTP 握手耗时。

共享 TLS 配置启用 MBEDTLS_ECP_NIST_OPTIM,使用 SDK 自带的 NIST 曲线快速模约减,减少证书 ECDSA 校验和相应 ECDHE 运算的开销。不改变证书信任、域名和日期校验规则,不调整服务器地址、设备鉴权、曲线列表或连接生命周期。

APP 通过 SDK 导出的 mbedtls_ssl_handshake_step 驱动同一 TLS 1.2 握手状态机,保留 WANT 重试和 20 秒超时;仅在 WANT 状态等待,正常状态推进不增加睡眠。每阶段打印 WSS TLS phase ... elapsed=... ms, calls=... ms, retries=...,结束打印 WSS TLS handshake total=... ms。elapsed 是该阶段总时间,calls 是 SDK 调用内累计经过时间(含任务调度,并非纯 CPU 时间),retries 是等待 I/O 的返回次数;时间分辨率为 SDK 的 5 ms tick。证书和密钥交换阶段仍可能同时包含网络与运算时间,不能单凭阶段名归因。

同一 Windows 主机上用实际 SDK crypto/X.509 源码验证相同证书链,各运行 20 次:关闭 NIST 优化平均 36.8 ms,开启后 10.2 ms,约 3.6 倍。两种配置均通过证书链、错误域名、不可信根、证书日期与 AES-GCM 检查。这是主机算法对比,不是板端总握手时间预测。可运行 tools/test_mbedtls_cert.py --compare-nist 复测;tools/test_wss_handshake.py 验证计时握手循环的状态推进、WANT、真实错误、超时与 tick 回绕。

第一轮加速包是 artifacts/wss/tls-speed/EG800AKCN_11LG-legacy-wss-fast.zip,相对早期版本需更新内核和 APP。14:38 上板已确认 TLS 握手 1820 ms,其中证书校验 965 ms、服务器密钥交换 160 ms、客户端密钥交换 445 ms,HTTP 101 正常。

完整握手的曲线协商优化

用户要求每次完整握手且不提前建连。WSS 的 ssl_conf.curve_list 改为 X25519、P-256、P-384、P-521 优先,其他原已启用曲线继续作为回退;不启用会话恢复、不改变按键/休眠/提示音逻辑。SDK 没有向 APP 导出 mbedtls_ssl_conf_curves,因此采用其实现同样的配置字段赋值。ABI 检查扩展到该字段、握手参数及 ECDH 上下文,验证 ARMCC 内核与 GCC APP 的实际布局一致。协商日志直接读取已匹配的上下文,WSS TLS negotiated ECDHE group: 9 表示 X25519(P-256=3、P-384=4、P-521=5)。

tools/probe_wss_groups.py 用同一固件根 CA、TLS 1.2 和证书/域名校验进行独立完整握手,读取固件的曲线数组进行验证:实际后台在 SDK 默认顺序下选择 P-521,在新固件顺序下选择 X25519;密码套件均为 ECDHE-ECDSA-AES256-GCM-SHA384。证据见 artifacts/wss/full-handshake/group-probe.json。X25519 是标准的约 128 位安全强度密钥交换;P-521 的参数更大,单纯参数更大不等于更适合本设备。当前服务端叶证书是 P-256 ECDSA,证书链、有效期和域名校验保持必需。

主机实际 SDK 新建 ECDH(每组 20 次,重新生成临时密钥并计算共享密钥)平均:X25519 1.55 ms、P-256 1.90 ms、P-384 3.40 ms、P-521 5.15 ms;X25519 测试峰值额外堆 1144 字节,P-521 6376 字节(包含测试对端共享密钥校验的内存)。每次校验双方计算结果相同并检查内存释放。板端仍需实测,主要目标是降低客户端密钥交换开销,不能把基准比例套用到整个握手。

另对比 ECP window=4/5/6:证书校验约 10.35/10.20/10.45 ms,峰值分配约 11913/16009/18697 字节,收益不稳定,因此固件保留 window=4。可在 MSVC 开发者终端运行 tools/test_mbedtls_cert.py --compare-windows 复测。

最新包 artifacts/wss/full-handshake/EG800AKCN_11LG-legacy-wss-x25519.zip 沿用已验证的 NIST 内核,只更新 APP。当前工作区可 build.bat app、build.bat firmware;全新检出仍需先构建匹配的内核。旧 tls-speed 包保留,作为板端 1820 ms 的对照。

根证书来源与验证

固件内置的是公共根 CA ISRG Root X2,不是短期服务器证书。

  • 来源:对目标域名进行 Windows 系统信任和域名校验后导出的证书链根。
  • 根证书 DER SHA-256:69729b8e15a86efc177a57afb7171dfc64add28c2fca8cf1507e34453ccb1470。
  • 根证书有效期到 2040-09-17。
  • 实测链:banban.api.talkingq.com → Let's Encrypt YE1 → Root YE(由 X2 交叉签名)→ ISRG Root X2。
  • 当前服务器证书有效期:2026-08-28 至 2026-11-26。后台正常续期且仍能链到内置根时,不需要随服务器证书续期更新固件。
  • Python/OpenSSL 仅加载从固件源码提取的这一张根 CA,没有加载系统证书库或自动下载中间证书,TLS 1.2 验证成功;错误域名和空信任库均验证失败。
  • 固件进行证书链、有效期和域名验证;未增加 OCSP/CRL 吊销查询。后端更换到其他根 CA 时,需要评估并更新信任根。

可重跑:ql-cross-tool\win32\owtoolchain\Python\python.exe tools\probe_wss_root.py。该测试验证服务器链完整性和内置信任根,不能代替板端 mbedTLS 实测。

构建与验证

tools\test_wss.cmd
ql-cross-tool\win32\owtoolchain\Python\python.exe tools\check_mbedtls_abi.py
build.bat kernel
build.bat app
build.bat firmware

主机测试使用 MSVC 编译实际 URL 和握手解析源码;另从真实实现提取 TLS 写入和取消连接函数,注入桩测试短写、重试、超时、时钟回绕和资源释放。tools/test_wss_receive.py 提取实际接收和握手等待函数,结合实际 HTTP 解析器验证 WANT_READ/WANT_WRITE、分片 101、响应后帧数据保留、超时和时钟回绕、EOF/真实错误、空 TLS 上下文及错误 Accept;修复前可复现误断线,修复后通过。解析测试覆盖合法地址、非法端口、明文拒绝、缓冲区边界、旧地址识别、每个响应头分片边界、非 101、错误/重复 Accept、缺失 Upgrade/Connection 和响应大小上限。

在 Visual Studio 开发者终端运行 ql-cross-tool\win32\owtoolchain\Python\python.exe tools\test_mbedtls_cert.py,使用 SDK 实际 crypto/X.509 源码和共享配置验证内置根 CA、当前服务器证书链、错误域名、空信任库、过期/尚未生效日期,以及 AES-GCM 标准测试向量。测试中的分配器、UTC 时钟和日志适配到 Windows,文件读取由测试程序完成;没有替换证书算法实现。默认使用 artifacts/wss/retest-443/chain-0.pem 至 chain-2.pem,可把探测输出目录作为脚本参数。

导出到下述路径后运行 ql-cross-tool\win32\owtoolchain\Python\python.exe tools\verify_wss_package.py,核对 ZIP CRC、APP/内核与当前构建的字节一致性、无 GNSS 入口以及内核 ECDSA/GCM/日期校验符号。SDK 有既有编译警告,不能仅凭外层批处理退出码认定成功。

最新优化包位于 artifacts/wss/full-handshake/EG800AKCN_11LG-legacy-wss-x25519.zip,标准输出为 target/EG800AKCN_11LG/EG800AKCN_11LG.zip,两者字节一致。这是完整多分区烧录包,内核与上一版 NIST 加速包字节一致。运行 ql-cross-tool\win32\owtoolchain\Python\python.exe tools\verify_wss_package.py 校验默认最新包;也可显式传入该 ZIP 路径。

ecc-fix/EG800AKCN_11LG-legacy-wss-ecc.zip 保留用于对照接收错误。原 artifacts/wss/EG800AKCN_11LG-legacy-wss.zip 存在缺失 ECDSA 的问题,请勿用于验证本次修复。旧板明文 WS 包仍保留在 artifacts/legacy-no-gnss/。

上板验收

443 /ws 已通过主机握手测试;ECC/RTC、接收修复和 NIST 加速已在板端通过 TLS、HTTP 101。本次 X25519 优先配置仍需上板检查:

  1. 无 GNSS 旧板正常启动、蜂窝网络就绪、RTC 时间正确。
  2. 先出现 WSS root CA parsed successfully,再出现 WSS TLS handshake and certificate/hostname verification passed。
  3. 出现 WebSocket upgrade verified: HTTP 101 和 ws connect okay。
  4. 服务器确认原设备鉴权成功,再测试收发音频、断网重连和休眠唤醒。
  5. 确认 WSS TLS negotiated ECDHE group: 9;记录 WSS TLS phase 和 WSS TLS handshake total,同一网络环境多次对比上一版 1820 ms 的 TLS 握手耗时。

TLS 成功、WebSocket 成功、设备鉴权成功是三个不同阶段,不能只看其中一条日志就认定业务已跑通。