Accelerate legacy WSS elliptic-curve operations and time TLS phases
Enable mbedTLS NIST modular reduction and report per-phase handshake timings. Add handshake-loop regression tests and a certificate verification benchmark, and document the full kernel and APP package requirement.
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# 旧板 WSS 联调包
|
||||
|
||||
最新修复包为 `recv-fix/EG800AKCN_11LG-legacy-wss-recv.zip`,供无 GNSS 旧板联调,默认连接 `wss://banban.api.talkingq.com:443/ws`。默认音量仍为 100(已有保存值按原逻辑读取)。这是包含 ECC/RTC 内核和新 APP 的完整多分区包。
|
||||
最新优化包为 `tls-speed/EG800AKCN_11LG-legacy-wss-fast.zip`,供无 GNSS 旧板联调,默认连接 `wss://banban.api.talkingq.com:443/ws`。默认音量仍为 100(已有保存值按原逻辑读取)。这是包含 NIST 运算加速内核和握手计时 APP 的完整多分区包;本次必须更新内核与 APP。
|
||||
|
||||
2026-09-16 14:06 板端日志已确认 ECC/RTC 修复后的根 CA 解析、TLS 握手、证书和域名校验通过。随后发现 HTTP Upgrade 等待时将 TLS WANT_READ/WANT_WRITE 误判为错误;最新 APP 已修正。实际接收/握手函数回归测试、APP 编译和包校验通过,具体 SHA-256 见 `recv-fix/verification.json`;接收修复仍待上板验证。
|
||||
2026-09-16 14:20 板端已通过 TLS 校验与 HTTP 101,用户确认功能正常,但完整 TLS 握手约 9.6 秒。本次启用 mbedTLS NIST 快速模约减,并输出握手各阶段耗时。主机上同一证书链校验平均由 36.8 ms 降至 10.2 ms(各 20 次);不能直接换算板端耗时,优化包仍需上板计时。包校验与 SHA-256 见 `tls-speed/verification.json`。
|
||||
|
||||
2026-09-16 11:32 复测:443 `/ws` 已返回 HTTP 101,TLS 和证书校验通过;8080 仍是明文 WS。本目录原 `EG800AKCN_11LG-legacy-wss.zip` 缺少 ECDSA,`ecc-fix` 包存在接收误判,均保留作对照;请使用 `recv-fix` 下的新包验证。
|
||||
2026-09-16 11:32 复测:443 `/ws` 已返回 HTTP 101,TLS 和证书校验通过;8080 仍是明文 WS。`recv-fix/EG800AKCN_11LG-legacy-wss-recv.zip` 保留为已上板通过的回退版本。本目录原 `EG800AKCN_11LG-legacy-wss.zip` 缺少 ECDSA,`ecc-fix` 包存在接收误判,均仅用于历史对照。
|
||||
|
||||
原旧板包保留在 `../legacy-no-gnss/`。构建日志、探测 JSON、导出的公共证书和包体在此目录本地保存并被 Git 忽略。源码和详细步骤见 `../../docs/talkingq_wss.md`。
|
||||
|
||||
+15
-2
@@ -53,6 +53,18 @@ location /ws {
|
||||
- 连接失败唤醒等待方;超时停止待连接 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。`recv-fix` 包保留为已上板通过的回退版本。优化包仍需采集阶段日志确认实际收益;若主要耗时在网络等待,再评估会话恢复或提前连接,不能用主机基准推断网络影响。
|
||||
|
||||
## 根证书来源与验证
|
||||
|
||||
固件内置的是公共根 CA **ISRG Root X2**,不是短期服务器证书。
|
||||
@@ -83,17 +95,18 @@ build.bat firmware
|
||||
|
||||
导出到下述路径后运行 `ql-cross-tool\win32\owtoolchain\Python\python.exe tools\verify_wss_package.py`,核对 ZIP CRC、APP/内核与当前构建的字节一致性、无 GNSS 入口以及内核 ECDSA/GCM/日期校验符号。SDK 有既有编译警告,不能仅凭外层批处理退出码认定成功。
|
||||
|
||||
最新修复测试包位于 `artifacts/wss/recv-fix/EG800AKCN_11LG-legacy-wss-recv.zip`,标准输出为 `target/EG800AKCN_11LG/EG800AKCN_11LG.zip`,两者字节一致。这是完整多分区烧录包,内核与 ECC/RTC 修复包一致,APP 增加接收重试修复。在保留已构建的 ECC/RTC 内核的当前工作区,本次仅需 `build.bat app` 和 `build.bat firmware`;全新工作区仍需先构建内核。校验命令为 `ql-cross-tool\win32\owtoolchain\Python\python.exe tools\verify_wss_package.py artifacts/wss/recv-fix/EG800AKCN_11LG-legacy-wss-recv.zip`。
|
||||
最新优化包位于 `artifacts/wss/tls-speed/EG800AKCN_11LG-legacy-wss-fast.zip`,标准输出为 `target/EG800AKCN_11LG/EG800AKCN_11LG.zip`,两者字节一致。这是完整多分区烧录包。本次新增内核 NIST 加速,需依次 `build.bat kernel`、`build.bat app`、`build.bat firmware`,不能仅更新 APP。运行 `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 修复已在板端通过 TLS 验证,接收修复包仍需上板检查:
|
||||
443 `/ws` 已通过主机握手测试;ECC/RTC 和接收修复已在板端通过 TLS、HTTP 101,用户确认功能正常。本次 NIST 加速/计时包仍需上板检查:
|
||||
|
||||
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 phase` 和 `WSS TLS handshake total`,同一网络环境多次对比原约 9.6 秒的 TLS 握手耗时。
|
||||
|
||||
TLS 成功、WebSocket 成功、设备鉴权成功是三个不同阶段,不能只看其中一条日志就认定业务已跑通。
|
||||
|
||||
@@ -1350,7 +1350,7 @@ void rws_socket_connect_to_host(_rws_socket *s)
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (void *)&timeout, sizeof(timeout));
|
||||
}
|
||||
#endif
|
||||
LOG_I("[T:%.8d, L:%.5d, %*.*s] socket break\n", ql_rtos_get_systicks(), __LINE__, DBG_FUNCTION_NUM, DBG_FUNCTION_NUM, __FUNCTION__);
|
||||
LOG_I("[T:%.8d, L:%.5d, %*.*s] TCP connected; leaving connect loop\n", ql_rtos_get_systicks(), __LINE__, DBG_FUNCTION_NUM, DBG_FUNCTION_NUM, __FUNCTION__);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -1849,12 +1849,57 @@ void rws_debug(void *ctx, int level, const char *file, int line, const char *str
|
||||
LOG_I("%s", str ? str : NULL);
|
||||
}
|
||||
|
||||
static const char *rws_tls_phase_name(int state)
|
||||
{
|
||||
switch (state) {
|
||||
case MBEDTLS_SSL_CLIENT_HELLO: return "client hello";
|
||||
case MBEDTLS_SSL_SERVER_HELLO: return "server hello";
|
||||
case MBEDTLS_SSL_SERVER_CERTIFICATE: return "server certificate";
|
||||
case MBEDTLS_SSL_SERVER_KEY_EXCHANGE: return "server key exchange";
|
||||
case MBEDTLS_SSL_CLIENT_KEY_EXCHANGE: return "client key exchange";
|
||||
case MBEDTLS_SSL_SERVER_FINISHED: return "server finished";
|
||||
default: return "other";
|
||||
}
|
||||
}
|
||||
|
||||
static int rws_tls_handshake_timed(mbedtls_ssl_context *ctx)
|
||||
{
|
||||
unsigned int started = ql_rtos_get_systicks();
|
||||
unsigned int phase_started = started, call_ticks = 0, retries = 0;
|
||||
int state = ctx->state, ret = 0;
|
||||
while (ctx->state != MBEDTLS_SSL_HANDSHAKE_OVER) {
|
||||
unsigned int before = ql_rtos_get_systicks();
|
||||
ret = mbedtls_ssl_handshake_step(ctx);
|
||||
call_ticks += (unsigned int)(ql_rtos_get_systicks() - before);
|
||||
if (ret != 0 && ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE)
|
||||
break;
|
||||
if (ret != 0) ++retries;
|
||||
if (ctx->state != state) {
|
||||
LOG_I("WSS TLS phase %s (%d): elapsed=%u ms, calls=%u ms, retries=%u\n",
|
||||
rws_tls_phase_name(state), state,
|
||||
(unsigned int)(ql_rtos_get_systicks() - phase_started) * 5,
|
||||
call_ticks * 5, retries);
|
||||
state = ctx->state;
|
||||
phase_started = ql_rtos_get_systicks();
|
||||
call_ticks = retries = 0;
|
||||
}
|
||||
if (ctx->state == MBEDTLS_SSL_HANDSHAKE_OVER) break;
|
||||
if ((unsigned int)(ql_rtos_get_systicks() - started) >= 4000) {
|
||||
ret = MBEDTLS_ERR_SSL_TIMEOUT; /* 20 seconds, 5 ms SDK ticks */
|
||||
break;
|
||||
}
|
||||
if (ret != 0) rws_thread_sleep(10);
|
||||
}
|
||||
LOG_I("WSS TLS handshake total=%u ms, state=%d, ret=%d\n",
|
||||
(unsigned int)(ql_rtos_get_systicks() - started) * 5, ctx->state, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int rws_ssl_conn(_rws_socket *s)
|
||||
{
|
||||
const char *pers = "talkingq-wss";
|
||||
const char *stage = "allocate TLS context";
|
||||
int ret = -1;
|
||||
unsigned int started;
|
||||
uint32_t flags;
|
||||
_rws_ssl *ssl;
|
||||
|
||||
@@ -1918,15 +1963,8 @@ int rws_ssl_conn(_rws_socket *s)
|
||||
if (ret != 0) goto exit;
|
||||
mbedtls_ssl_set_bio(&ssl->ssl_ctx, &ssl->net_ctx, mbedtls_net_send, mbedtls_net_recv, NULL);
|
||||
stage = "TLS handshake / server certificate";
|
||||
started = ql_rtos_get_systicks();
|
||||
while ((ret = mbedtls_ssl_handshake(&ssl->ssl_ctx)) != 0) {
|
||||
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) goto exit;
|
||||
if ((unsigned int)(ql_rtos_get_systicks() - started) >= 4000) {
|
||||
ret = MBEDTLS_ERR_SSL_TIMEOUT; /* 20 seconds, 5 ms SDK ticks */
|
||||
goto exit;
|
||||
}
|
||||
rws_thread_sleep(10);
|
||||
}
|
||||
ret = rws_tls_handshake_timed(&ssl->ssl_ctx);
|
||||
if (ret != 0) goto exit;
|
||||
flags = mbedtls_ssl_get_verify_result(&ssl->ssl_ctx);
|
||||
if (flags != 0) {
|
||||
LOG_E("WSS certificate verification failed: 0x%08lx\n", (unsigned long)flags);
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
#define MBEDTLS_PLATFORM_MEMORY
|
||||
#define MBEDTLS_PLATFORM_C
|
||||
#define MBEDTLS_ECP_C
|
||||
/* Use mbedTLS's fast modular reduction for NIST certificate/ECDHE curves. */
|
||||
#define MBEDTLS_ECP_NIST_OPTIM
|
||||
#define MBEDTLS_ECDH_C
|
||||
#define MBEDTLS_ECDSA_C
|
||||
#define MBEDTLS_CHACHAPOLY_C
|
||||
|
||||
@@ -51,7 +51,7 @@ for target in ['kernel', 'app']:
|
||||
or line.startswith('#define QUECTEL_MBEDTLS_'))
|
||||
assert layouts['kernel'] == layouts['app'], json.dumps(layouts, indent=2)
|
||||
assert macros['kernel'] == macros['app'], json.dumps({k: sorted(set(macros[k]) - set(macros['app' if k == 'kernel' else 'kernel'])) for k in macros})
|
||||
for feature in ['ECDSA_C', 'KEY_EXCHANGE_ECDHE_ECDSA_ENABLED', 'GCM_C', 'HAVE_TIME_DATE', 'PLATFORM_TIME_ALT']:
|
||||
for feature in ['ECDSA_C', 'ECP_NIST_OPTIM', 'KEY_EXCHANGE_ECDHE_ECDSA_ENABLED', 'GCM_C', 'HAVE_TIME_DATE', 'PLATFORM_TIME_ALT']:
|
||||
assert any(line.startswith('#define MBEDTLS_' + feature + ' ') for line in macros['kernel'])
|
||||
report = {'result': 'PASS', 'layout': layouts['kernel'], 'features': macros['kernel']}
|
||||
(output / 'tls-abi.json').write_text(json.dumps(report, indent=2))
|
||||
|
||||
@@ -36,6 +36,8 @@ int main(int argc, char **argv)
|
||||
char pem[16384];
|
||||
FILE *file;
|
||||
size_t length;
|
||||
clock_t verify_started;
|
||||
mbedtls_ecp_group group;
|
||||
unsigned char key[16] = {0}, iv[12] = {0}, plain[16] = {0}, cipher[16], tag[16];
|
||||
const unsigned char expected_cipher[16] = {0x03,0x88,0xda,0xce,0x60,0xb6,0xa3,0x92,0xf3,0x28,0xc2,0xb9,0x71,0xb2,0xfe,0x78};
|
||||
const unsigned char expected_tag[16] = {0xab,0x6e,0x47,0xd4,0x2c,0xec,0x13,0xbd,0xf5,0x3a,0x67,0xb2,0x12,0x57,0xbd,0xdf};
|
||||
@@ -60,6 +62,21 @@ int main(int argc, char **argv)
|
||||
ret = mbedtls_x509_crt_verify(&chain, &root, NULL, "banban.api.talkingq.com", &flags, NULL, NULL, 0);
|
||||
printf("SDK server chain + hostname verify: %d, flags=0x%08x\n", ret, (unsigned int)flags);
|
||||
assert(ret == 0 && flags == 0);
|
||||
verify_started = clock();
|
||||
for (i = 0; i < 20; ++i) {
|
||||
ret = mbedtls_x509_crt_verify(&chain, &root, NULL, "banban.api.talkingq.com", &flags, NULL, NULL, 0);
|
||||
assert(ret == 0 && flags == 0);
|
||||
}
|
||||
printf("Host certificate chain verify average: %.2f ms (20 runs)\n",
|
||||
1000.0 * (clock() - verify_started) / CLOCKS_PER_SEC / 20);
|
||||
mbedtls_ecp_group_init(&group);
|
||||
assert(mbedtls_ecp_group_load(&group, MBEDTLS_ECP_DP_SECP384R1) == 0);
|
||||
#ifdef MBEDTLS_ECP_NIST_OPTIM
|
||||
assert(group.modp != NULL);
|
||||
#else
|
||||
assert(group.modp == NULL);
|
||||
#endif
|
||||
mbedtls_ecp_group_free(&group);
|
||||
ret = mbedtls_x509_crt_verify(&chain, &root, NULL, "wrong.invalid", &flags, NULL, NULL, 0);
|
||||
assert(ret != 0 && (flags & MBEDTLS_X509_BADCERT_CN_MISMATCH));
|
||||
ret = mbedtls_x509_crt_verify(&chain, NULL, NULL, "banban.api.talkingq.com", &flags, NULL, NULL, 0);
|
||||
|
||||
@@ -2,13 +2,17 @@
|
||||
Run from a Visual Studio developer shell. Chain PEMs come from the WSS probe.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
output = root / 'artifacts/wss/ecc-fix/host-cert'
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('chain', nargs='?', default=str(root / 'artifacts/wss/retest-443'))
|
||||
parser.add_argument('--compare-nist', action='store_true')
|
||||
args = parser.parse_args()
|
||||
output = root / 'artifacts/wss/tls-speed/host-cert'
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
chain = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else root / 'artifacts/wss/retest-443'
|
||||
chain = Path(args.chain).resolve()
|
||||
sources = '''asn1parse asn1write bignum base64 pem oid pk pkparse pk_wrap rsa rsa_internal
|
||||
ecp ecp_curves ecdsa md md5 sha1 sha256 sha512 aes des gcm cipher cipher_wrap
|
||||
chacha20 chachapoly poly1305 constant_time ctr_drbg x509 x509_crt platform_util
|
||||
@@ -21,7 +25,9 @@ command = ['cl', '/nologo', '/O2', '/Gy', '/TC', '/D_CRT_SECURE_NO_WARNINGS',
|
||||
str(root / 'tools/test_mbedtls_cert.c'),
|
||||
str(root / 'ql-application/threadx/interface/custom_app/talkingq_root_ca.c')]
|
||||
command += [str(root / ('ql-kernel/threadx/quec_mbedtls/' + name + '.c')) for name in sources]
|
||||
exe = output / 'test_mbedtls_cert.exe'
|
||||
command += ['/Fe' + str(exe), '/link', '/OPT:REF']
|
||||
subprocess.run(command, cwd=output, check=True)
|
||||
subprocess.run([str(exe), str(chain)], check=True)
|
||||
for variant in (['baseline', 'nist'] if args.compare_nist else ['nist']):
|
||||
exe = output / ('test_mbedtls_cert_' + variant + '.exe')
|
||||
flags = ['/DWSS_TEST_DISABLE_NIST_OPTIM'] if variant == 'baseline' else []
|
||||
subprocess.run(command + flags + ['/Fe' + str(exe), '/link', '/OPT:REF'], cwd=output, check=True)
|
||||
print('Certificate test variant:', variant, flush=True)
|
||||
subprocess.run([str(exe), str(chain)], check=True)
|
||||
|
||||
@@ -12,4 +12,6 @@ if errorlevel 1 exit /b 1
|
||||
ql-cross-tool\win32\owtoolchain\Python\python.exe tools\test_wss_io.py
|
||||
if errorlevel 1 exit /b 1
|
||||
ql-cross-tool\win32\owtoolchain\Python\python.exe tools\test_wss_receive.py
|
||||
if errorlevel 1 exit /b 1
|
||||
ql-cross-tool\win32\owtoolchain\Python\python.exe tools\test_wss_handshake.py
|
||||
exit /b %errorlevel%
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
"""Test the actual timed handshake loop with scripted SDK steps and 5 ms ticks."""
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
source = (root / 'ql-application/threadx/interface/custom_app/lib/librws/src/rws_socketpriv.c').read_bytes().decode('latin1')
|
||||
body = source[source.index('static const char *rws_tls_phase_name('):].split('int rws_ssl_conn(', 1)[0]
|
||||
harness = r'''
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
enum { MBEDTLS_SSL_CLIENT_HELLO, MBEDTLS_SSL_SERVER_HELLO,
|
||||
MBEDTLS_SSL_SERVER_CERTIFICATE, MBEDTLS_SSL_SERVER_KEY_EXCHANGE,
|
||||
MBEDTLS_SSL_CLIENT_KEY_EXCHANGE, MBEDTLS_SSL_SERVER_FINISHED,
|
||||
MBEDTLS_SSL_HANDSHAKE_OVER };
|
||||
#define MBEDTLS_ERR_SSL_WANT_READ -2
|
||||
#define MBEDTLS_ERR_SSL_WANT_WRITE -3
|
||||
#define MBEDTLS_ERR_SSL_TIMEOUT -4
|
||||
typedef struct { int state; } mbedtls_ssl_context;
|
||||
typedef struct { int ret, next_state; unsigned int duration; } step;
|
||||
static step steps[16];
|
||||
static int count, calls, sleeps;
|
||||
static unsigned int ticks;
|
||||
static void test_log(const char *fmt, ...) { (void)fmt; }
|
||||
#define LOG_I test_log
|
||||
static unsigned int ql_rtos_get_systicks(void) { return ticks; }
|
||||
static void rws_thread_sleep(unsigned int ms) { ticks += ms / 5; ++sleeps; }
|
||||
static int mbedtls_ssl_handshake_step(mbedtls_ssl_context *ctx) {
|
||||
step s;
|
||||
assert(ctx->state != MBEDTLS_SSL_HANDSHAKE_OVER);
|
||||
if (calls >= count) { ++calls; return MBEDTLS_ERR_SSL_WANT_READ; }
|
||||
s = steps[calls++]; ticks += s.duration; ctx->state = s.next_state; return s.ret;
|
||||
}
|
||||
'''
|
||||
harness += body
|
||||
harness += r'''
|
||||
static void reset(mbedtls_ssl_context *ctx) {
|
||||
ctx->state = MBEDTLS_SSL_CLIENT_HELLO;
|
||||
count = calls = sleeps = 0; ticks = 0; memset(steps, 0, sizeof(steps));
|
||||
}
|
||||
int main(void) {
|
||||
mbedtls_ssl_context ctx;
|
||||
reset(&ctx);
|
||||
steps[0] = (step){0, MBEDTLS_SSL_SERVER_HELLO, 1};
|
||||
steps[1] = (step){MBEDTLS_ERR_SSL_WANT_READ, MBEDTLS_SSL_SERVER_HELLO, 0};
|
||||
steps[2] = (step){0, MBEDTLS_SSL_SERVER_CERTIFICATE, 2};
|
||||
steps[3] = (step){0, MBEDTLS_SSL_CLIENT_KEY_EXCHANGE, 10};
|
||||
steps[4] = (step){MBEDTLS_ERR_SSL_WANT_WRITE, MBEDTLS_SSL_CLIENT_KEY_EXCHANGE, 1};
|
||||
steps[5] = (step){0, MBEDTLS_SSL_SERVER_FINISHED, 5};
|
||||
steps[6] = (step){0, MBEDTLS_SSL_HANDSHAKE_OVER, 1}; count = 7;
|
||||
assert(rws_tls_handshake_timed(&ctx) == 0 && calls == 7 && sleeps == 2 && ticks == 24);
|
||||
reset(&ctx); steps[0] = (step){-99, MBEDTLS_SSL_SERVER_CERTIFICATE, 1}; count = 1;
|
||||
assert(rws_tls_handshake_timed(&ctx) == -99 && calls == 1 && sleeps == 0);
|
||||
reset(&ctx);
|
||||
assert(rws_tls_handshake_timed(&ctx) == MBEDTLS_ERR_SSL_TIMEOUT && ticks == 4000 && calls < 2100);
|
||||
reset(&ctx); ticks = (unsigned int)-1000;
|
||||
assert(rws_tls_handshake_timed(&ctx) == MBEDTLS_ERR_SSL_TIMEOUT && calls < 2100);
|
||||
reset(&ctx); steps[0] = (step){0, MBEDTLS_SSL_SERVER_CERTIFICATE, 4001}; count = 1;
|
||||
assert(rws_tls_handshake_timed(&ctx) == MBEDTLS_ERR_SSL_TIMEOUT && calls == 1 && sleeps == 0);
|
||||
reset(&ctx); ctx.state = MBEDTLS_SSL_HANDSHAKE_OVER;
|
||||
assert(rws_tls_handshake_timed(&ctx) == 0 && calls == 0);
|
||||
puts("PASS: timed SDK handshake loop: progress, WANT_READ/WRITE, no progress sleeps, fatal, deadline, tick wrap, complete state");
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
output = root / 'artifacts/wss'
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
cfile = output / 'test_wss_handshake.c'
|
||||
cfile.write_text(harness, encoding='utf-8')
|
||||
exe = output / 'test_wss_handshake.exe'
|
||||
subprocess.run(['cl', '/nologo', '/W4', '/WX', '/TC', str(cfile),
|
||||
'/Fo' + str(output / 'test_wss_handshake.obj'), '/Fe' + str(exe)], check=True)
|
||||
subprocess.run([str(exe)], check=True)
|
||||
@@ -8,7 +8,7 @@ import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
package = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else root / 'artifacts/wss/recv-fix/EG800AKCN_11LG-legacy-wss-recv.zip'
|
||||
package = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else root / 'artifacts/wss/tls-speed/EG800AKCN_11LG-legacy-wss-fast.zip'
|
||||
output = package.parent
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
objdump = root / 'ql-cross-tool/win32/owtoolchain/gcc-arm-none-eabi/bin/arm-none-eabi-objdump.exe'
|
||||
@@ -16,14 +16,15 @@ app = root / 'ql-application/threadx/build/app.elf'
|
||||
symbols = subprocess.check_output([str(objdump.with_name('arm-none-eabi-nm.exe')), str(app)], text=True)
|
||||
required = ['talkingq_parse_wss_url', 'talkingq_root_ca', 'rws_check_handshake', 'rws_ssl_conn',
|
||||
'mbedtls_ssl_set_hostname', 'mbedtls_ssl_get_verify_result', 'mbedtls_ssl_conf_authmode',
|
||||
'mbedtls_ctr_drbg_random', 'mbedtls_sha1', 'mbedtls_base64_encode']
|
||||
'mbedtls_ctr_drbg_random', 'mbedtls_sha1', 'mbedtls_base64_encode', 'mbedtls_ssl_handshake_step']
|
||||
for symbol in required:
|
||||
assert re.search(r'\b' + symbol + r'$', symbols, re.M), 'Missing linked symbol: ' + symbol
|
||||
kernel_symbols = subprocess.check_output([str(objdump.with_name('arm-none-eabi-nm.exe')),
|
||||
str(root / 'ql-kernel/threadx/build/kernel.axf')], text=True)
|
||||
kernel_required = ['ecdsa_verify_restartable', 'mbedtls_ecdsa_read_signature',
|
||||
'mbedtls_gcm_crypt_and_tag', 'mbedtls_x509_time_is_future',
|
||||
'mbedtls_x509_time_is_past', 'mbedtls_platform_gmtime_r', 'mbedtls_time']
|
||||
'mbedtls_x509_time_is_past', 'mbedtls_platform_gmtime_r', 'mbedtls_time',
|
||||
'ecp_mod_p256', 'ecp_mod_p384', 'ecp_mod_p521']
|
||||
for symbol in kernel_required:
|
||||
assert re.search(r'\b' + symbol + r'$', kernel_symbols, re.M), 'Missing kernel capability: ' + symbol
|
||||
clock_body = subprocess.check_output([str(objdump), '-d', '--disassemble=x509_get_current_time',
|
||||
@@ -32,7 +33,7 @@ assert '<mbedtls_platform_gmtime_r>' in clock_body and '<GetCurrentCompTime>' no
|
||||
(output / 'x509-clock.disasm.txt').write_text(clock_body)
|
||||
report = {'package': package.name, 'sha256': hashlib.sha256(package.read_bytes()).hexdigest(),
|
||||
'linked_symbols': required, 'kernel_symbols': kernel_required,
|
||||
'hardware_test': '2026-09-16 14:06 device log: ECC/RTC TLS verification passed; receive retry fix awaits device test',
|
||||
'hardware_test': '2026-09-16 14:20 device log: TLS and HTTP 101 passed; NIST acceleration and handshake timings await device test',
|
||||
'backend': '2026-09-16 11:32 CST: WSS 443 /ws HTTP 101; 8080 plaintext WS'}
|
||||
with zipfile.ZipFile(package) as archive:
|
||||
assert archive.testzip() is None, 'ZIP CRC failure'
|
||||
@@ -42,6 +43,7 @@ with zipfile.ZipFile(package) as archive:
|
||||
assert data == (root / source).read_bytes(), 'Stale image: ' + name
|
||||
report[name] = {'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}
|
||||
assert b'wss://banban.api.talkingq.com:443/ws' in archive.read('customer_app.bin')
|
||||
assert b'WSS TLS handshake total=' in archive.read('customer_app.bin')
|
||||
report['download_manifest'] = json.loads(archive.read('download.json'))
|
||||
printf_address = int(re.search(r'^([0-9a-fA-F]+) T printf$', symbols, re.M).group(1), 16)
|
||||
for symbol, binary, expected in [
|
||||
@@ -51,7 +53,7 @@ for symbol, binary, expected in [
|
||||
body = subprocess.check_output([str(objdump), '-d', '--disassemble=' + symbol, str(binary)], text=True)
|
||||
assert '<' + symbol + '>:' in body and expected in body and 'task_create' not in body
|
||||
(output / (symbol + '.disasm.txt')).write_text(body)
|
||||
report['verification'] = 'PASS: ZIP CRC, current APP/kernel identity, GNSS bypass, WSS URL, ECDSA/GCM/RTC kernel symbols'
|
||||
report['verification'] = 'PASS: ZIP CRC, current APP/kernel identity, GNSS bypass, WSS URL, ECDSA/GCM/RTC/NIST kernel symbols and handshake timing logs'
|
||||
(output / 'verification.json').write_text(json.dumps(report, indent=2))
|
||||
print(report['verification'])
|
||||
print('SHA256:', report['sha256'])
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/* Host adapter: crypto/X.509 features are the firmware's shared profile.
|
||||
* Only CPU assembly and the OS allocator/clock implementation differ. */
|
||||
#include "../ql-config/config/talkingq_mbedtls_config.h"
|
||||
#ifdef WSS_TEST_DISABLE_NIST_OPTIM
|
||||
#undef MBEDTLS_ECP_NIST_OPTIM
|
||||
#endif
|
||||
#undef MBEDTLS_HAVE_ASM
|
||||
#undef MBEDTLS_FS_IO
|
||||
#define QUECTEL_MBEDTLS_SUPPORT
|
||||
|
||||
Reference in New Issue
Block a user