Fix legacy WSS certificate support and receive retries
Share the APP/kernel TLS profile, enable ECDSA and AES-GCM, and use RTC time for certificate validation. Fix WANT_READ/WANT_WRITE handling during WebSocket upgrade and add ABI, certificate, and receive regression checks.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
"""Check actual ARMCC kernel / GCC APP TLS layouts and enabled features."""
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import struct
|
||||
import subprocess
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
output = root / 'artifacts/wss/ecc-fix'
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
toolchain = root / 'ql-cross-tool/win32/owtoolchain'
|
||||
gcc = toolchain / 'gcc-arm-none-eabi/bin/arm-none-eabi-gcc.exe'
|
||||
objcopy = gcc.with_name('arm-none-eabi-objcopy.exe')
|
||||
armcc = toolchain / 'ARM_Compiler_5/bin/armcc.exe'
|
||||
types = ['ssl_context', 'ssl_config', 'net_context', 'x509_crt', 'pk_context',
|
||||
'entropy_context', 'ctr_drbg_context', 'ecp_group', 'ecp_point', 'mpi']
|
||||
fields = {'ssl_context': ['conf', 'state', 'in_buf', 'out_buf', 'session', 'handshake', 'hostname'],
|
||||
'ssl_config': ['ca_chain', 'cert_profile', 'f_rng', 'p_rng'],
|
||||
'net_context': ['fd', 'cid'], 'x509_crt': ['pk', 'sig', 'next']}
|
||||
labels = ['sizeof(mbedtls_' + t + ')' for t in types]
|
||||
labels += ['offsetof(mbedtls_' + t + ', ' + f + ')' for t, fs in fields.items() for f in fs]
|
||||
source = output / 'tls_layout.c'
|
||||
source.write_text('#include <stddef.h>\n#include "mbedtls/ssl.h"\n'
|
||||
'#include "mbedtls/net_sockets.h"\n#include "mbedtls/entropy.h"\n'
|
||||
'#include "mbedtls/ctr_drbg.h"\n'
|
||||
'const unsigned int talkingq_tls_layout[] = {\n' + ',\n'.join(labels) + '\n};\n')
|
||||
env = os.environ.copy()
|
||||
env['ARMLMD_LICENSE_FILE'] = str(toolchain / 'license.dat')
|
||||
layouts, macros = {}, {}
|
||||
for target in ['kernel', 'app']:
|
||||
include = root / ('ql-kernel' if target == 'kernel' else 'ql-application') / 'threadx/common/include'
|
||||
obj = output / (target + '-tls-layout.o')
|
||||
flags = ['-I' + str(include), '-DQUECTEL_OPEN_CPU']
|
||||
if target == 'kernel':
|
||||
command = [str(armcc), '--cpu', 'Cortex-R5F', '--fpu=softvfp+vfpv3_d16',
|
||||
'--gnu', '--thumb', '-c', str(source), '-o', str(obj)] + flags
|
||||
section = '.constdata'
|
||||
else:
|
||||
command = [str(gcc), '-mcpu=cortex-r5', '-mthumb', '-mfloat-abi=softfp', '-mfpu=vfpv3-d16',
|
||||
'-c', str(source), '-o', str(obj)] + flags
|
||||
section = '.rodata'
|
||||
subprocess.run(command, env=env, check=True)
|
||||
binary = obj.with_suffix('.bin')
|
||||
subprocess.run([str(objcopy), '-O', 'binary', '-j', section, str(obj), str(binary)], check=True)
|
||||
data = binary.read_bytes()
|
||||
assert len(data) == len(labels) * 4, (target, len(data))
|
||||
layouts[target] = dict(zip(labels, struct.unpack('<' + 'I' * len(labels), data)))
|
||||
defines = subprocess.check_output([str(gcc), '-E', '-dM', '-x', 'c', '-include', 'mbedtls/config.h',
|
||||
'NUL'] + flags, text=True)
|
||||
macros[target] = sorted(line for line in defines.splitlines() if line.startswith('#define MBEDTLS_')
|
||||
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']:
|
||||
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))
|
||||
print('PASS: ARMCC kernel / GCC APP TLS sizes, field offsets and feature macros agree')
|
||||
print(json.dumps(layouts['kernel'], indent=2))
|
||||
@@ -0,0 +1,84 @@
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <stdarg.h>
|
||||
#include "mbedtls/x509_crt.h"
|
||||
#include "mbedtls/platform_time.h"
|
||||
#include "mbedtls/gcm.h"
|
||||
#include "mbedtls/ssl_ciphersuites.h"
|
||||
#include "talkingq_wss.h"
|
||||
|
||||
void *mbedtls_calloc(size_t n, size_t size) { return calloc(n, size); }
|
||||
void mbedtls_free(void *p) { free(p); }
|
||||
static time_t test_time;
|
||||
static time_t read_test_time(time_t *p) { if (p) *p = test_time; return test_time; }
|
||||
mbedtls_time_t (*mbedtls_time)(mbedtls_time_t *) = read_test_time;
|
||||
void CPUartLogPrintf(const char *format, ...) { (void)format; }
|
||||
int mbedtls_snprintf(char *buffer, size_t size, const char *format, ...)
|
||||
{
|
||||
int result;
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
result = vsnprintf(buffer, size, format, args);
|
||||
va_end(args);
|
||||
return result;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
mbedtls_x509_crt root, chain;
|
||||
mbedtls_gcm_context gcm;
|
||||
uint32_t flags;
|
||||
int ret, i;
|
||||
char path[1024];
|
||||
char pem[16384];
|
||||
FILE *file;
|
||||
size_t length;
|
||||
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};
|
||||
assert(argc == 2);
|
||||
test_time = time(NULL);
|
||||
mbedtls_x509_crt_init(&root);
|
||||
mbedtls_x509_crt_init(&chain);
|
||||
ret = mbedtls_x509_crt_parse(&root, (const unsigned char *)talkingq_root_ca, talkingq_root_ca_size);
|
||||
printf("SDK root CA parse: %d (previous device failure: -9774)\n", ret);
|
||||
assert(ret == 0);
|
||||
for (i = 0; i < 3; ++i) {
|
||||
snprintf(path, sizeof(path), "%s/chain-%d.pem", argv[1], i);
|
||||
file = fopen(path, "rb");
|
||||
assert(file != NULL);
|
||||
length = fread(pem, 1, sizeof(pem)-1, file);
|
||||
assert(feof(file) && !ferror(file));
|
||||
fclose(file);
|
||||
pem[length] = 0;
|
||||
ret = mbedtls_x509_crt_parse(&chain, (const unsigned char *)pem, length + 1);
|
||||
assert(ret == 0);
|
||||
}
|
||||
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);
|
||||
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);
|
||||
assert(ret != 0 && (flags & MBEDTLS_X509_BADCERT_NOT_TRUSTED));
|
||||
test_time = 1900000000; /* 2030: this server certificate is expired. */
|
||||
ret = mbedtls_x509_crt_verify(&chain, &root, NULL, "banban.api.talkingq.com", &flags, NULL, NULL, 0);
|
||||
assert(ret != 0 && (flags & MBEDTLS_X509_BADCERT_EXPIRED));
|
||||
test_time = 1700000000; /* 2023: this server certificate is not yet valid. */
|
||||
ret = mbedtls_x509_crt_verify(&chain, &root, NULL, "banban.api.talkingq.com", &flags, NULL, NULL, 0);
|
||||
assert(ret != 0 && (flags & MBEDTLS_X509_BADCERT_FUTURE));
|
||||
assert(mbedtls_ssl_ciphersuite_from_id(MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384) != NULL);
|
||||
mbedtls_gcm_init(&gcm);
|
||||
assert(mbedtls_gcm_setkey(&gcm, MBEDTLS_CIPHER_ID_AES, key, 128) == 0);
|
||||
assert(mbedtls_gcm_crypt_and_tag(&gcm, MBEDTLS_GCM_ENCRYPT, 16, iv, 12, NULL, 0,
|
||||
plain, cipher, 16, tag) == 0);
|
||||
assert(!memcmp(cipher, expected_cipher, 16) && !memcmp(tag, expected_tag, 16));
|
||||
mbedtls_gcm_free(&gcm);
|
||||
mbedtls_x509_crt_free(&chain);
|
||||
mbedtls_x509_crt_free(&root);
|
||||
puts("PASS: SDK ECDSA certificate chain, hostname, trust, dates, TLS suite and AES-GCM vector");
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
"""Build the SDK's real crypto/X.509 sources with MSVC and test the server chain.
|
||||
Run from a Visual Studio developer shell. Chain PEMs come from the WSS probe.
|
||||
"""
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
output = root / 'artifacts/wss/ecc-fix/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'
|
||||
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
|
||||
ecdh ssl_ciphersuites'''.split()
|
||||
command = ['cl', '/nologo', '/O2', '/Gy', '/TC', '/D_CRT_SECURE_NO_WARNINGS',
|
||||
'/DMBEDTLS_CONFIG_FILE="wss_tls_host_config.h"',
|
||||
'/I' + str(root / 'tools'), '/I' + str(root / 'tools/wss_host_stubs'),
|
||||
'/I' + str(root / 'ql-kernel/threadx/common/include'),
|
||||
'/I' + str(root / 'ql-application/threadx/interface/custom_app/inc'),
|
||||
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)
|
||||
@@ -10,4 +10,6 @@ if errorlevel 1 exit /b 1
|
||||
artifacts\wss\test_wss.exe
|
||||
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
|
||||
exit /b %errorlevel%
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
"""Exercise the real receive/upgrade loop with delayed TLS records."""
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
lib = root / 'ql-application/threadx/interface/custom_app/lib/librws'
|
||||
source = (lib / 'src/rws_socketpriv.c').read_bytes().decode('latin1')
|
||||
receive = source[source.index('rws_bool rws_socket_recv('):].split('/* RT-Thread Team add */', 1)[0]
|
||||
upgrade = source[source.index('void rws_socket_wait_handshake_responce('):].split('void rws_socket_send_disconnect(', 1)[0]
|
||||
harness = r'''
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "rws_handshake.h"
|
||||
#define LIBRWS_USING_MBED_TLS
|
||||
#define MBEDTLS_ERR_SSL_WANT_READ -2
|
||||
#define MBEDTLS_ERR_SSL_WANT_WRITE -3
|
||||
#define MBEDTLS_ERR_NET_RECV_FAILED -4
|
||||
#define WSAEWOULDBLOCK 11
|
||||
#define WSAEINPROGRESS 115
|
||||
#define RWS_RECEIVE_BUFF_SIZE 1024
|
||||
#define COMMAND_WAIT_HANDSHAKE_RESPONCE 3
|
||||
#define COMMAND_INFORM_CONNECTED 4
|
||||
#define COMMAND_INFORM_DISCONNECTED 5
|
||||
#define rws_error_code_read_write_socket 4
|
||||
#define rws_error_code_parse_handshake 5
|
||||
#define LOG_E(...) ((void)0)
|
||||
#define LOG_D(...) ((void)0)
|
||||
#define LOG_I(...) ((void)0)
|
||||
typedef int rws_bool;
|
||||
enum { rws_false, rws_true };
|
||||
typedef struct { int ssl_ctx; } tls;
|
||||
typedef struct {
|
||||
const char *scheme, *expected_accept;
|
||||
tls *ssl;
|
||||
char *payload_buff, *received;
|
||||
size_t received_size, received_len;
|
||||
int socket, command, is_connected;
|
||||
const char *error;
|
||||
} _rws_socket;
|
||||
static int replies[16], reply_count, calls, closes;
|
||||
static size_t offset;
|
||||
static unsigned int ticks;
|
||||
static const char response[] =
|
||||
"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\n"
|
||||
"Connection: Upgrade\r\nSec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n\r\nDATA";
|
||||
static unsigned int ql_rtos_get_systicks(void) { return ticks; }
|
||||
static void rws_thread_sleep(int ms) { ticks += (unsigned int)ms; }
|
||||
static void rws_error_delete_clean(const char **e) { *e = NULL; }
|
||||
static const char *rws_error_new_code_descr(int code, const char *text) { (void)code; return text; }
|
||||
static void rws_socket_close(_rws_socket *s) { (void)s; ++closes; }
|
||||
static void rws_socket_resize_received(_rws_socket *s, size_t size) {
|
||||
s->received = (char *)realloc(s->received, size);
|
||||
assert(s->received); s->received_size = size;
|
||||
}
|
||||
static int recv(int fd, char *buf, int size, int flags) {
|
||||
(void)fd; (void)buf; (void)size; (void)flags;
|
||||
assert(0 && "WSS must use TLS receive"); return -1;
|
||||
}
|
||||
static int mbedtls_ssl_read(int *ctx, unsigned char *buf, size_t size) {
|
||||
int ret = calls < reply_count ? replies[calls] : MBEDTLS_ERR_SSL_WANT_READ;
|
||||
(void)ctx; ++calls;
|
||||
if (ret > 0) {
|
||||
assert((size_t)ret <= size && offset + (size_t)ret <= sizeof(response) - 1);
|
||||
memcpy(buf, response + offset, (size_t)ret); offset += (size_t)ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
'''
|
||||
harness += receive + upgrade
|
||||
harness += r'''
|
||||
static void reset(_rws_socket *s, tls *ctx) {
|
||||
free(s->received); memset(s, 0, sizeof(*s));
|
||||
s->scheme = "wss"; s->ssl = ctx;
|
||||
s->expected_accept = "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=";
|
||||
s->payload_buff = (char *)malloc(RWS_RECEIVE_BUFF_SIZE); assert(s->payload_buff);
|
||||
s->command = COMMAND_WAIT_HANDSHAKE_RESPONCE;
|
||||
calls = closes = reply_count = 0; offset = 0; ticks = 0;
|
||||
}
|
||||
int main(void) {
|
||||
tls ctx = {0}; _rws_socket s = {0}; int want;
|
||||
for (want = -2; want >= -3; --want) {
|
||||
reset(&s, &ctx); replies[0] = want; reply_count = 1;
|
||||
assert(!rws_socket_recv(&s));
|
||||
assert(!s.error && s.command == COMMAND_WAIT_HANDSHAKE_RESPONCE && closes == 0);
|
||||
replies[1] = 17; replies[2] = want; replies[3] = (int)sizeof(response) - 1 - 17;
|
||||
reply_count = 4;
|
||||
rws_socket_wait_handshake_responce(&s);
|
||||
assert(s.is_connected && !s.error && s.command == COMMAND_INFORM_CONNECTED);
|
||||
assert(calls == 4 && closes == 0 && s.received_len == 4);
|
||||
assert(!memcmp(s.received, "DATA", 4)); free(s.payload_buff);
|
||||
}
|
||||
reset(&s, &ctx);
|
||||
rws_socket_wait_handshake_responce(&s);
|
||||
assert(!s.is_connected && s.error && closes == 1 && ticks >= 2000 && calls < 300);
|
||||
free(s.payload_buff);
|
||||
reset(&s, &ctx); ticks = (unsigned int)-1000;
|
||||
rws_socket_wait_handshake_responce(&s);
|
||||
assert(!s.is_connected && s.error && closes == 1 && calls < 300); free(s.payload_buff);
|
||||
for (want = 0; want >= -99; want -= 99) {
|
||||
reset(&s, &ctx); replies[0] = want; reply_count = 1;
|
||||
rws_socket_wait_handshake_responce(&s);
|
||||
assert(!s.is_connected && s.error && closes == 1 && calls == 1);
|
||||
assert(s.command == COMMAND_INFORM_DISCONNECTED); free(s.payload_buff);
|
||||
}
|
||||
reset(&s, NULL); rws_socket_wait_handshake_responce(&s);
|
||||
assert(s.error && closes == 1 && calls == 0); free(s.payload_buff);
|
||||
reset(&s, &ctx); s.expected_accept = "wrong";
|
||||
replies[0] = (int)sizeof(response) - 1; reply_count = 1;
|
||||
rws_socket_wait_handshake_responce(&s);
|
||||
assert(!s.is_connected && s.error && closes == 1); free(s.payload_buff);
|
||||
free(s.received);
|
||||
puts("PASS: actual TLS receive/upgrade loop: WANT_READ/WRITE, split HTTP 101, trailing frame bytes, timeout/wrap, EOF/fatal, null TLS, invalid Accept");
|
||||
return 0;
|
||||
}
|
||||
'''
|
||||
output = root / 'artifacts/wss'
|
||||
output.mkdir(parents=True, exist_ok=True)
|
||||
cfile = output / 'test_wss_receive.c'
|
||||
cfile.write_text(harness, encoding='utf-8')
|
||||
exe = output / 'test_wss_receive.exe'
|
||||
subprocess.run(['cl', '/nologo', '/W4', '/WX', '/wd4018', '/TC',
|
||||
'/I' + str(lib / 'inc'), str(cfile), str(lib / 'src/rws_handshake.c'),
|
||||
'/Fo' + str(output) + '/', '/Fe' + str(exe)], check=True)
|
||||
subprocess.run([str(exe)], check=True)
|
||||
@@ -3,12 +3,14 @@ import hashlib
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
root = Path(__file__).resolve().parents[1]
|
||||
output = root / 'artifacts/wss'
|
||||
package = output / 'EG800AKCN_11LG-legacy-wss.zip'
|
||||
package = Path(sys.argv[1]).resolve() if len(sys.argv) > 1 else root / 'artifacts/wss/recv-fix/EG800AKCN_11LG-legacy-wss-recv.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'
|
||||
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)
|
||||
@@ -17,9 +19,21 @@ required = ['talkingq_parse_wss_url', 'talkingq_root_ca', 'rws_check_handshake',
|
||||
'mbedtls_ctr_drbg_random', 'mbedtls_sha1', 'mbedtls_base64_encode']
|
||||
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']
|
||||
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',
|
||||
str(root / 'ql-kernel/threadx/build/kernel.axf')], text=True)
|
||||
assert '<mbedtls_platform_gmtime_r>' in clock_body and '<GetCurrentCompTime>' not in clock_body
|
||||
(output / 'x509-clock.disasm.txt').write_text(clock_body)
|
||||
report = {'package': package.name, 'sha256': hashlib.sha256(package.read_bytes()).hexdigest(),
|
||||
'linked_symbols': required, 'hardware_test': 'Not performed',
|
||||
'backend': '443 /ws returns HTTP 404; 8080 is plaintext WS'}
|
||||
'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',
|
||||
'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'
|
||||
for name, source in {'cp.bin': 'ql-kernel/threadx/build/release_kernel.bin',
|
||||
@@ -29,9 +43,6 @@ with zipfile.ZipFile(package) as archive:
|
||||
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')
|
||||
report['download_manifest'] = json.loads(archive.read('download.json'))
|
||||
with zipfile.ZipFile(root / 'artifacts/legacy-no-gnss/EG800AKCN_11LG-legacy-no-gnss.zip') as old:
|
||||
with zipfile.ZipFile(package) as new:
|
||||
assert old.read('cp.bin') == new.read('cp.bin'), 'Legacy kernel changed unexpectedly'
|
||||
printf_address = int(re.search(r'^([0-9a-fA-F]+) T printf$', symbols, re.M).group(1), 16)
|
||||
for symbol, binary, expected in [
|
||||
('quec_get_gnssid_task', root / 'ql-kernel/threadx/build/kernel.axf', '<RTI_LOG>'),
|
||||
@@ -40,7 +51,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, unchanged legacy kernel, GNSS bypass, WSS URL and TLS symbols'
|
||||
report['verification'] = 'PASS: ZIP CRC, current APP/kernel identity, GNSS bypass, WSS URL, ECDSA/GCM/RTC kernel symbols'
|
||||
(output / 'verification.json').write_text(json.dumps(report, indent=2))
|
||||
print(report['verification'])
|
||||
print('SHA256:', report['sha256'])
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
void CPUartLogPrintf(const char *format, ...);
|
||||
@@ -0,0 +1,7 @@
|
||||
/* 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"
|
||||
#undef MBEDTLS_HAVE_ASM
|
||||
#undef MBEDTLS_FS_IO
|
||||
#define QUECTEL_MBEDTLS_SUPPORT
|
||||
#include "mbedtls/check_config.h"
|
||||
Reference in New Issue
Block a user