Serial Studio 中的 Mbed TLS 3.6.7:构建系统、配置方式与 OPC UA 加密栈集成详解
【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio
本文以 Serial Studio 仓库内 vendored 的 lib/mbedtls/README.md 为主线,系统讲解 Mbed TLS 的三大构建系统(GNU Make、CMake、Visual Studio)、配置机制(mbedtls_config.h与scripts/config.py)、PSA 密码学 API 的启用方式与移植平台要求,并结合本仓库 lib/CMakeLists.txt 与 lib/open62541/CMakeLists.txt 的源码,说明这套 C 库是如何以 CMake 子项目形式支撑 Serial Studio 的 OPC UA 安全通道的。读完本文,你既能独立编译 Mbed TLS 及其测试套件,也能理解它在本仓库中的角色边界:只编译库本体、不开启示例程序与测试、静态链接进最终可执行文件。
版本与用途:仓库里这颗加密基石
lib/mbedtls是 Mbed-TLS/mbedtls 上游v3.6.7的完整源码树,这一点由机器可读的溯源清单 lib/VERSIONS.json 明确断言:
version: "3.6.7",ref: "v3.6.7";- 版本标记同时断言于
lib/mbedtls/CMakeLists.txt的project()声明和 lib/mbedtls/ChangeLog; - 注释写明其用途:"Backs the six OPC UA security policies (spec 0067)",即支撑 OPC UA 驱动所需的六个安全策略。
这与 lib/mbedtls/CMakeLists.txt 中的project("Mbed TLS" ... VERSION 3.6.7)完全吻合。Mbed TLS 本身是 Arm 提供的 C 密码库,实现密码学原语(含 PSA Cryptography API)、X.509 证书操作以及 SSL/TLS 与 DTLS 协议,其小代码足迹使其适合嵌入式系统——这正是 Serial Studio 这样一个跨平台遥测应用选择它做静态依赖的理由。
从本仓库的构建结构看,mbedTLS 是 OPC UA 技术栈(mbedTLS + open62541)的底座:lib/open62541/CMakeLists.txt 的注释指出,vendored 的 open62541 单文件发行版在发布时已经预定义了UA_ENABLE_ENCRYPTION_MBEDTLS,因此所有安全策略直接来自lib/mbedtls而不是目标机器上解析到的任何系统库——"OPC UA 驱动的存在性与能力是本构建的属性"(spec 0067)。
三个库、三条目标:依赖关系与链接顺序
README 明确说明,Make 与 CMake 构建系统会生成三个库,且存在严格的依赖链:
libmbedcrypto:密码学原语层(对应 CMake 目标mbedcrypto);libmbedx509:X.509 证书处理(对应目标mbedx509),依赖 mbedcrypto;libmbedtls:TLS/DTLS 协议层(对应目标mbedtls),依赖 mbedx509 与 mbedcrypto。
由于依赖方向固定,部分链接器要求标志按特定顺序出现。例如 GNU 链接器期望-lmbedtls -lmbedx509 -lmbedcrypto(上层库在前)。在 CMake 侧,open62541 目标的写法正是如此:lib/open62541/CMakeLists.txt 中target_link_libraries(open62541 PUBLIC mbedtls mbedx509 mbedcrypto),三个目标按依赖序排列。
配置:mbedtls_config.h 与 config.py
Mbed TLS 在大多数系统上开箱即可构建。平台相关的选项与特性开关都集中在配置文件 lib/mbedtls/include/mbedtls/mbedtls_config.h,该文件带有完整注释,可手工编辑,也可以通过 Python 3 脚本scripts/config.py以编程方式修改(运行--help查看用法)。
两个与 Serial Studio 集成直接相关的配置项值得单独说明(README 的 PSA 章节):
MBEDTLS_USE_PSA_CRYPTO:激活后,X.509 与 TLS 代码将大部分操作经由 PSA 密码学实现;README 特别指出TLS 1.3 无论此选项是否开启都主要使用 PSA 密码学;MBEDTLS_PSA_CRYPTO_CONFIG:允许在不引入对应软件实现代码的前提下启用 PSA 密码机制,通常用于配合硬件加速器/安全元件驱动。README 同时声明驱动接口尚未完全稳定,应用代码(使用 PSA Crypto API)的向后兼容会被保留,但驱动代码可能在后续小版本中变化。
编译器选项则通过常规环境变量(CC、CFLAGS)在 Make 与 CMake 下设置,但注意 CMake 下这类变量只能在首次调用时生效(下文详述)。
工具版本要求
README 给出的构建工具清单(适用于其自带 makefile 的构建方式):
| 工具 | 版本要求 | 用途 |
|---|---|---|
| GNU Make | 3.82+ | Make 构建 |
| C 工具链(编译器/链接器/归档器) | C99;官方活跃测试 GCC 5.4、Clang 3.8、Arm Compiler 6、IAR 8、Visual Studio 2017 | 编译库本体 |
| Python | 3.8 | 生成测试代码、集成 PSA 驱动、构建 development 分支 |
| Perl | 任意 | 运行测试、生成开发分支的部分源文件 |
| CMake | 3.10.2+(CMake 构建时) | CMake 构建 |
| Visual Studio | 2017+(VS 构建时) | Windows 构建 |
| Doxygen | 1.8.11+(生成文档时) | make apidoc本地文档 |
另外,development 分支与mbedtls-3.6长期支持分支使用 Git 子模块(framework 子项目);仅在发布 tag 上编译、或消费 zip/tar 发行包时不需要该子模块。
development 分支的生成源文件
Mbed TLS 源码包含一批由脚本自动生成的文件:其内容只取决于 Mbed TLS 源码本身,与平台和库配置无关,因此不出现在 development 分支中,但包含在正式发行包里。生成这些文件需要:
- Perl:部分库源文件与 Visual Studio 构建文件;
- Python 3.8及若干包(安装命令:
python3 -m pip install --user -r scripts/basic.requirements.txt,如需系统级安装可省略--user); - 宿主机 C 编译器:用于生成部分测试数据。
脚本查找宿主 C 编译器的优先级顺序是:
HOSTCC环境变量(当CC指向交叉编译器时使用);CC环境变量;- 当前路径下的
cc可执行文件。
README 建议安装多套工具链时,在生成文件前显式设置CC或HOSTCC。可用的生成方式包括:非交叉编译时任意make目标会自动生成;非 Windows 非交叉编译时 CMake 自动生成;或显式运行make generated_files、tests/scripts/check-generated-files.sh -u(Unix/POSIX)、scripts\make_generated_files.bat(Windows)。
这一点在 Serial Studio 中有实际体现:lib/CMakeLists.txt 在把 mbedTLS 作为子项目引入时强制设置了GEN_FILES OFF——因为 vendored 的树携带的是发行版形态(生成文件已就位),不需要重复运行生成脚本。
GNU Make 构建
构建库与示例程序只需要 GNU Make 加一个 C 编译器:
make # 构建库与示例程序 make check # 构建并运行测试(需要 Python 构建、Perl 运行) make no_test # 跳过需要 Python/Perl 的测试构建跳过完整测试后仍可运行更小的自检集:programs/test/selftest。
Windows 目标有专门变量:构建环境为类 Unix(如交叉编译或 MSYS shell)时设WINDOWS_BUILD=1;构建环境本身就是 Windows shell(如mingw32-make)时设WINDOWS=1(此时部分目标不可用)。
其他 Make 行为要点:
- 环境里设置
SHARED会在静态库之外再构建共享库;设置DEBUG得到调试构建; CFLAGS与LDFLAGS可通过环境变量或 make 命令行覆盖,警告选项可单独用WARNING_CFLAGS覆盖;目录级选项(如-I)保留不可覆盖;- 注意
CFLAGS的默认值就是-O2,WARNING_CFLAGS默认以-Wall -Wextra开头,因此"只想追加选项"时应写成CFLAGS=-O2 -Werror这类完整赋值,而不是追加; - 针对特定平台的选项可参考
library/、programs/、tests/下的 Makefile。
README 刻意保持 makefile 功能最小化以降低工具链耦合,需要更多功能的用户被建议改用 CMake——这正是 Serial Studio 采用 CMake 路径的原因。
CMake 构建
README 推荐的独立构建目录流程:
mkdir /path/to/build_dir && cd /path/to/build_dir cmake /path/to/mbedtls_source cmake --build . ctest # 运行测试套件若缺少 Python/Perl,可用cmake -DENABLE_TESTING=Off /path/to/mbedtls_source关闭测试套件,此时仍可用programs/test/selftest跑小型自检。构建共享库用-DUSE_SHARED_MBEDTLS_LIBRARY=On。
CMake 下提供了多种构建模式,多数适用于 gcc 与 clang:
| 模式 | 说明 |
|---|---|
Release | 默认,二进制中不含多余信息 |
Debug | 生成调试信息并关闭优化 |
Coverage | 调试信息之外再生成代码覆盖率信息 |
ASan | AddressSanitizer 内存检查(新版 gcc/clang 含 LeakSanitizer;新版 clang 还叠加 UndefinedSanitizer) |
ASanDbg | ASan 加调试信息,更慢、栈回溯更好 |
MemSan | MemorySanitizer 检查未初始化内存读,实验性,需较新 clang 且 Linux/x86_64 |
MemSanDbg | MemSan 加调试信息、栈回溯与 origin tracking |
Check | 激活依赖优化的编译器警告并将所有警告视为错误 |
切换模式很简单:cmake -D CMAKE_BUILD_TYPE=Debug /path/to/mbedtls_source;cmake -LH可列出全部选项。
CMake 的几个易踩坑点(README 专门强调):
- 首次调用后无法再调整编译器或标志,
CC=your_cc make在 CMake 项目里不生效,必须首次就CC=your_cc cmake ...;已配置过则需删除构建目录重来; - 就地构建(
cmake .+make)可行,但会覆盖仓库中的 makefile(可用scripts/tmp_ignore_makefiles.sh让git status不再显示其为修改);改CC/CFLAGS后需清理 CMake 缓存(README 给出了基于find的清理命令); - 设置
CFLAGS不会覆盖 CMake 按构建模式提供的默认内容,只是前置拼接。
作为依赖消费:find_package(MbedTLS)
Mbed TLS 提供 package config 文件供其他 CMake 项目作为依赖使用:
find_package(MbedTLS)若被提示设置MbedTLS_DIR,指向${YOUR_MBEDTLS_INSTALL_DIR}/cmake。这会创建三个目标:MbedTLS::mbedcrypto(加密库)、MbedTLS::mbedtls(TLS 库)、MbedTLS::mbedx509(X.509 库),然后可直接:
add_executable(xyz) target_link_libraries(xyz PUBLIC MbedTLS::mbedtls MbedTLS::mbedcrypto MbedTLS::mbedx509)链接后还会把 Mbed TLS 的包含目录传递给目标(PUBLIC/INTERFACE链接库情形下是传递性的)。
作为 CMake 子项目:Serial Studio 的实际集成方式
README 最后指出 Mbed TLS 支持被父项目用add_subdirectory()纳入构建,且 lib/mbedtls/CMakeLists.txt 中有对应的自检测逻辑:当MBEDTLS_AS_SUBPROJECT未显式定义时,若CMAKE_CURRENT_SOURCE_DIR不等于CMAKE_SOURCE_DIR就判定为子项目,从而自动禁用自身的 install/export 规则。Serial Studio 正是走的这条路。
lib/CMakeLists.txt 中的集成块展示了完整的工程化决策:
if(SS_ENABLE_OPCUA AND (BUILD_COMMERCIAL OR SS_BUILD_TESTS)) set(ENABLE_TESTING OFF CACHE BOOL "" FORCE) set(ENABLE_PROGRAMS OFF CACHE BOOL "" FORCE) set(MBEDTLS_FATAL_WARNINGS OFF CACHE BOOL "" FORCE) set(GEN_FILES OFF CACHE BOOL "" FORCE) set(USE_STATIC_MBEDTLS_LIBRARY ON CACHE BOOL "" FORCE) set(USE_SHARED_MBEDTLS_LIBRARY OFF CACHE BOOL "" FORCE) add_subdirectory(mbedtls) add_subdirectory(open62541) ... endif()逐条对应 README 说明的选项:
- 强制先
add_subdirectory(mbedtls)再add_subdirectory(open62541):注释说明 open62541 会链接 mbedTLS 的目标,顺序不能反; ENABLE_TESTING/ENABLE_PROGRAMS关闭:vendored 树只携带库本体编译所需内容,不含测试与示例程序(README 中make check、programs/那套在本仓库用不到);USE_STATIC_MBEDTLS_LIBRARY ON:与 README 的-DUSE_SHARED_MBEDTLS_LIBRARY选项相反方向取值,把三个库静态链进最终可执行文件——这与 open62541 侧"为静态链进单一可执行文件的客户端构建"的定位一致;GEN_FILES OFF:跳过生成文件步骤,因为消费的是发行版形态的树;MBEDTLS_FATAL_WARNINGS OFF+ 逐目标-w//w:lib/CMakeLists.txt 随后遍历mbedtls、mbedx509、mbedcrypto以及3rdparty下独立的everest、p256m目标,统一压制第三方警告(注释解释这是为了消除-Wundef报告的MBEDTLS_GCC_VERSION未定义这类第三方噪音,保持干净构建)。
还有一处值得注意的构建拓扑:target_link_open62541()在无 OPC UA 栈的配置下被定义为空操作(lib/CMakeLists.txt),即 GPL 配置下即使 SS_BUILD_TESTS 会构建该栈用于单元测试(tst_opcua_marshal 需要真实 open62541 来固定其 C 词表与 Qt 之间的接缝),也不会把库带进应用本体。
本地补丁:mbedtls 证书写入器中的 SAN 缓冲区问题
虽然 mbedtls 树本身保持上游原样,但 vendored 的 open62541 单文件发行版中有一个与 mbedtls API 直接相关的本地补丁,记录在 lib/open62541/PATCHES.md:
- 上游将 subjectAltName 扩展的临时缓冲区按
MBEDTLS_SAN_MAX_LEN * sandeep + sandeep(每名单固定 64 字节)估算,涉及mbedtls_x509write_crt_set_subject_alt_name()(UA_CreateCertificate使用)与mbedtls_x509write_csrSetSubjectAltName()两处; - 当 DNS 名超过约 47 个字符且叠加 URI SAN 后,预算溢出,ASN.1 反向写入器返回
MBEDTLS_ERR_ASN1_BUF_TOO_SMALL,证书生成报 "Setting subject alternative name failed"。该问题在 macOS CI 上以 67 字符主机名(sjc22-bm210-<uuid>-<mac>.local)批量暴露:所有安全通道集成测试失败,而短主机名机器通过; - 修复是在两处分配中加入实际名称长度(
buflen += it->node.hostlen,CSR 变体则遍历it->buf.len),保留原每名单的冗余量以覆盖 ASN.1 tag/length 开销。
这个补丁是理解"为何版本必须钉死"的最佳案例:lib/CMakeLists.txt 的注释解释 mbedTLS 故意固定在 3.6 LTS 线——open62541 的 crypto 插件用MBEDTLS_VERSION_NUMBER检查保护其 API 使用且止步于 3.x,而 4.x 是破坏性的 PSA-only 发布。升级 mbedTLS 时须同步处理 PATCHES.md 中记录的补丁重放。
测试体系
Mbed TLS 自带一套完备的测试体系(README 的 Tests 一节):
tests/下的测试套件先用 Python 从function 文件(如suites/test_suite_mpi.function,包含测试函数)与data 文件(如suites/test_suite_mpi.data,以参数形式给出测试用例)生成test_suite_*.c这类源文件;- 在装有 OpenSSL(可选 GnuTLS)的 Unix 机器上还有互操作脚本:
tests/ssl-opt.sh(TLS 选项集成测试:renegotiation、resumption 等)、tests/compat.sh(每条密码套件与其他实现的互操作)、tests/scripts/test-ref-configs.pl(多种缩减配置下构建)、tests/scripts/depends.py(单曲线/单密钥交换/单哈希/单密码/单 pkalg 的配置矩阵)、tests/scripts/all.sh(组合以上并叠加 ASan、完整mbedtls_config.h等构建选项)。
如前所述,Serial Studio 的构建有意关闭了这套测试(ENABLE_TESTING OFF),mbedtls 自身的正确性由上游 CI 负责;本仓库的验证落在 OPC UA 侧的单元测试(如app/tests/tst_opcua_security.cpp、app/tests/tst_opcua_subscriptions.cpp等)之上。
移植平台要求
README 列出 Mbed TLS 对 C99 之外的平台要求(现代架构基本都满足):
- 字节必须是 8 位;
- 全零位必须是空指针的有效表示;
- 有符号整数必须采用二进制补码;
int与size_t至少 32 位;uint8_t、uint16_t、uint32_t及对应符号类型必须可用;- 不支持混合字节序平台;
SIZE_MAX至少不小于INT_MAX与UINT_MAX。
Serial Studio 的跨平台(Linux/macOS/Windows,x86_64 与 arm64)目标恰好都满足这些约束,这也是"静态 vendored 一棵 C 树"方案可行的前提之一。
PSA 密码学 API 概览
README 用相当篇幅介绍了 PSA(Platform Security Architecture):
API 设计目标包括:区分调用方内存与库内部内存(允许库实现在隔离空间内,调用可以是直接函数调用也可以是 RPC);隐藏内部数据结构(可在构建期或运行期替换实现,例如利用硬件加速器);所有密钥访问都通过密钥标识符(对应用透明地支持外部加密处理器);接口以算法 agility 为导向;易用且难以误用。
在 Mbed TLS 中的实现覆盖了大部分(非全部)算法。X.509 与 TLS 代码可经由MBEDTLS_USE_PSA_CRYPTO切换到 PSA 路径(TLS 1.3 默认如此)。PSA 驱动支持加密加速器、安全元件与随机数生成器,推荐同时启用MBEDTLS_USE_PSA_CRYPTO与MBEDTLS_PSA_CRYPTO_CONFIG,使 X.509/TLS 代码走 PSA 驱动而非内置软件实现。README 明确声明驱动接口仍在完善中,可能随小版本变化。
文档生成与示例程序
生成贴合当前编译配置的本地 HTML 文档(README 的 Documentation 一节):
- 安装 Doxygen(1.8.11+);
- 运行
make apidoc; - 浏览
apidoc/index.html或apidoc/modules.html。
其他文档渠道可参考 lib/mbedtls/SUPPORT.md。
示例程序方面,上游在programs/目录提供了覆盖大量特性的样例(本 vendored 树未携带,README 提醒这些程序的目标是演示特性、代码需自行改造才能用于真实应用)。
许可与第三方代码
README 的 License 一节:除个别文件另有说明外,Mbed TLS 文件以Apache-2.0 OR GPL-2.0-or-later双重许可提供,全文见 lib/mbedtls/LICENSE。3rdparty/目录包含:
3rdparty/everest/:源自 Project Everest,Apache 2.0;3rdparty/p256-m/p256-m/:源自 p256-m 仓库,在上游为 Apache 2.0,在 Mbed TLS 中经作者许可以 Apache-2.0 OR GPL-2.0-or-later 双重许可分发。
这与 Serial Studio 侧的处理相呼应:lib 层的构建胶水(如 lib/CMakeLists.txt)采用 GPL-3.0-or-later OR 商业许可的双许可头,而 mbedTLS 自身作为 Apache-2.0 可选的依赖保持独立的许可边界。
安全响应与贡献
README 的 Contact/Contributing 部分给出两条路径:安全漏洞应通过安全邮件列表报告(详见 lib/mbedtls/SECURITY.md),普通 bug 与特性请求走上游 issue;贡献流程见 lib/mbedtls/CONTRIBUTING.md。分支模型(development 分支与 3.6 LTS 线)参见 lib/mbedtls/BRANCHES.md。
小结
lib/mbedtls/README.md描述的是一套成熟密码库的标准工程实践:单一mbedtls_config.h配置入口、三套等价构建系统、三层库的严格依赖顺序、按构建模式切换的 sanitizer 矩阵、以及find_package与add_subdirectory两种消费姿势。Serial Studio 从中选取了最贴合"静态链进单一桌面可执行文件"的一条路径——CMake 子项目 + 强制静态库 + 关闭测试/示例/生成文件 + 逐目标压制警告——并与预编译出 mbedTLS 加密插件的 open62541 1.5.7 单文件发行版配对,共同构成 OPC UA 驱动的六个安全策略实现。版本钉死在 3.6.7、补丁边界记录在 lib/open62541/PATCHES.md、溯源元数据固化在 lib/VERSIONS.json,使"这棵 vendored 树是否最新、被改过什么"这两个问题始终可以被机械地回答。
【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考