当前位置: 首页 > news >正文

STM32H573 Secure Manager与TLS 1.3集成:HKDF回退方案实战

前阵子我在一块STM32H573-DK上把Secure Manager和TLS 1.3拼在一起跑,结果还没等到服务器回握手消息,客户端就在密钥派生这一步直接停了。控制台干净利落地弹了一句PSA_ERROR_NOT_SUPPORTED,翻译成人话就是:Secure Manager不支持TLS 1.3密钥调度里最核心的HKDF-Extract/Expand。当时第一反应是“我哪个宏没开”,翻了一圈CubeMX和mbedTLS的配置,发现事情没那么简单。这个问题不是单纯少定义一个宏,而是Secure Manager的算法边界和mbedTLS 3.x对TLS 1.3的实现方式之间,存在一个结构性的冲突。

这篇内容就是记录我从复现、分析到最终解决的完整过程。如果你是正在做STM32H5系列TrustZone、Secure Manager、mbedTLS、TLS 1.3集成的开发者,这个坑你大概率躲不掉,看完可以直接照着排查和改配置。

1. 问题现场:TLS 1.3握手卡在密钥派生

1.1 复现环境与报错日志

我的测试环境不算特殊:开发板是STM32H573-DK,工程用STM32CubeMX生成,TrustZone模式打开,安全侧集成了ST的Secure Manager,非安全侧跑应用和mbedTLS。mbedTLS版本是3.5.1,由I-CUBE-TLS中间件带进来,TLS 1.3在mbedtls_config.h里开了MBEDTLS_SSL_PROTO_TLS1_3,并且启用了默认的PSA Crypto支持。

应用代码就是一个标准TLS 1.3客户端,向本地测试服务器发ClientHello。服务器端抓包能看到ClientHello正常发出,但后续握手包一直没出现,再一看调试串口,日志停在这附近:

[mbedtls] ssl_tls13_keys.c: mbedtls_ssl_tls13_key_schedule_stage_on_derive failed [mbedtls] secret derivation failed: psa_status = PSA_ERROR_NOT_SUPPORTED (-164) [app] mbedtls_ssl_handshake returned -0x7280

-0x7280MBEDTLS_ERR_SSL_INTERNAL_ERROR之类的包装错误,真正有价值的是中间那行PSA_ERROR_NOT_SUPPORTED。也就是说,mbedTLS在构造TLS 1.3的早期密钥时,调用PSA层的密钥派生接口,底层直接回复“这个算法我不支持”。

如果你是在调试器里单步跟,会看得更清楚:错误发生在psa_key_derivation_setup()这一步,传入的算法是PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_256)或者PSA_ALG_HKDF_EXPAND(PSA_ALG_SHA_256),返回码是PSA_ERROR_NOT_SUPPORTED,也就是-164。

1.2 TLS 1.3密钥调度为什么必须拆成Extract和Expand

要理解这个报错,得先明白TLS 1.3的密钥调度和TLS 1.2有着本质区别。TLS 1.2是拿握手过程中协商出的pre-master secret直接经过PRF生成主密钥,整个过程是一步步固定的。TLS 1.3则把密钥生成完全建立在HKDF上,并且为了支持0-RTT、会话恢复、流量密钥独立派生等特性,RFC 8446第7.1节把整个密钥调度设计成了一条清晰的多阶段流水线。

HKDF本身分成两步:HKDF-Extract负责“提取”,把输入的熵源(PSK或者ECDHE共享密钥)压缩成固定长度的PRK(pseudorandom key);HKDF-Expand负责“扩展”,用PRK加上info标签,派生出任意长度的输出。在TLS 1.3里,这两步是交替出现的:先用PSK做一次Extract得到Early Secret,再对Early Secret做Derive-Secret,得到Handshake Secret的“盐”,然后用ECDHE共享密钥做第二次Extract得到Handshake Secret,最后再由Handshake Secret派生master secret。

关键点来了:TLS 1.3在这条流水线的不同阶段,需要反复调用Extract和Expand,而且它们的输入和输出是交替衔接的。例如,握手流量密钥不是一次HKDF能算出来的,需要先Extract出Handshake Secret,再Expand出c hs traffics hs traffic等标签对应的密钥。如果协议栈只能用“完整HKDF”一步到位,就无法在中间插入不同标签的Derive-Secret步骤,也不可能拿到中间PRK去喂给下一步。

所以在代码实现层面,TLS 1.3协议栈必须能够分别调用HKDF-Extract和HKDF-Expand。这也正是PSA Crypto API在标准规范里单独定义了PSA_ALG_HKDF_EXTRACTPSA_ALG_HKDF_EXPAND这两个算法标识的原因。它们就是为TLS 1.3这种复杂密钥调度场景专门准备的。

1.3 mbedTLS在密钥调度里实际调了什么

mbedTLS 3.x的TLS 1.3实现在ssl_tls13_keys.c里,整个密钥调度通过PSA的key_derivation接口完成。具体走法是:先调用psa_key_derivation_setup()初始化一个密钥派生操作,算法用PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_256)做提取,再切换成PSA_ALG_HKDF_EXPAND(PSA_ALG_SHA_256)做扩展,中间通过psa_key_derivation_input_bytes()喂入PSK、salt、IKM、info等数据。

这里有一个容易忽略的事实:在mbedTLS 3.x里,TLS 1.3的密钥调度是强制走PSA Crypto接口的,即使你没有显式开MBEDTLS_USE_PSA_CRYPTO,只要启用了TLS 1.3,它就会使用PSA的算法接口来调用哈希和HKDF。这样设计是为了让TLS 1.3可以无缝使用各种硬件加速或安全隔离环境提供的密码服务。但代价就是:底层PSA实现如果不支持单步HKDF算法,TLS 1.3握手就会在这里直接失败。

2. 根因分析:Secure Manager为何会拒绝HKDF单步调用

2.1 Secure Manager的隔离边界和PSA接口

STM32H573内置TrustZone安全扩展,Cortex-M33核心可以划分安全世界和非安全世界。Secure Manager就是ST提供的一个运行在安全世界里的固件组件,它把密钥管理、安全存储、加解密算法、安全启动等服务封装成一个个受保护的调用接口。用户拿到的是一个预编译的固件和安全库,不能修改安全侧实现,只能通过PSA API或FF-M接口发起调用。

这种架构的核心价值是隔离。即使非安全侧的应用被攻破,攻击者也无法直接拿到根密钥,因为真正的加解密运算发生在安全世界里,非安全侧只看到一个句柄或者操作符。密钥永远不落到普通内存中。

但硬币的另一面是:Secure Manager能提供哪些算法,不是由你的应用代码决定的,而是由ST在固件里预先写死的。它暴露的算法集合是一个封闭集合,用户只能在集合内选择,不能往里加东西。当mbedTLS要求HKDF-Extract/Expand这两个单步算法时,如果这个集合里只有完整HKDF组合算法而没有单步算法,那底层就只会老老实实返回PSA_ERROR_NOT_SUPPORTED

2.2 Secure Manager的算法边界和“not supported”到底什么意思

我查了手头Secure Manager版本的用户手册和Release Notes,也对照了它导出的PSA算法支持矩阵。矩阵里明确支持AES-CBC/CTR/GCM/CCM、AES-CMAC、RSA、ECC(P-256/384)、ECDSA、ECDH、SHA-256/384、HMAC、完整HKDF等常规算法。但到了单步HKDF这里,支持情况就变得暧昧了:很多版本只暴露了完整的PSA_ALG_HKDF(PSA_ALG_SHA_256)组合算法,没有把PSA_ALG_HKDF_EXTRACTPSA_ALG_HKDF_EXPAND单独暴露出来。

这不是技术做不到,更像是一种安全策略上的取舍。从固件审计的角度看,单步HKDF-Expand接口允许调用方用任意PRK和任意info标签派生数据,灵活性更高,也意味着一旦非安全侧被攻破,攻击者可以更自由地调用密码学原语去构造各种数据。完整HKDF组合算法则把“输入到输出”封装成一个整体,调用方拿不到中间PRK,审计面更小。对于追求高安全等级认证的Secure Manager固件来说,少暴露一个原语,就少一分被滥用的风险。

所以PSA_ERROR_NOT_SUPPORTED的含义是:Secure Manager固件“故意”没有注册这两个算法。这是Secure Manager的算法边界,不是mbedTLS这边某个开关能直接治愈的。

2.3 几个容易被误判的根因

我这个报错,最开始也怀疑过是不是mbedTLS配置问题,后来发现有些常见误判,值得先说清楚,免得大家绕远路。

第一,不是MBEDTLS_USE_PSA_CRYPTO没开。TLS 1.3的密钥调度在mbedTLS 3.x里本来就强制走PSA,跟这个宏没有直接关系。

第二,不是哈希算法没启用。虽然TLS 1.3用SHA-256或SHA-384做HKDF底层哈希,但Secure Manager通常支持SHA-256,mbedTLS侧也确实编译进去了,只是最终没有走到软件实现的HKDF分支。

第三,不是Secure Manager完全没提供HKDF。它提供了完整HKDF组合算法,只是没有暴露单步版本。所以如果你不跑TLS 1.3,而是自己调psa_key_derivation做完整HKDF,可能是正常的。一进TLS 1.3就挂,因为TLS 1.3需要的是单步原语。

判断根因最直接的办法是写一小段测试代码,在握手前分别调用psa_key_derivation_setup()尝试PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_256)PSA_ALG_HKDF_EXPAND(PSA_ALG_SHA_256),打印返回值。如果这两个调用都返回PSA_ERROR_NOT_SUPPORTED,那就基本实锤是Secure Manager算法支持范围的问题。

3. 有效方案:让HKDF绕开Secure Manager

3.1 方案一:软件回退,修改PSA驱动路由

最稳妥、改动最小、见效最快的办法,是让HKDF单步算法在PSA驱动路由器里回退到mbedTLS自带的软件实现,而不是交给Secure Manager驱动。mbedTLS的PSA驱动机制是支持“多驱动 + fallback”的:每次调用psa_key_derivation_setup()时,它会逐个尝试注册过的驱动,如果某个驱动对该算法返回PSA_ERROR_NOT_SUPPORTED,就继续尝试下一个,最后落到内置软件实现。

在实际工程里,PSA驱动的注册表体现在psa_driver_wrappers.h这个文件里。STM32CubeMX生成工程时,会为Secure Manager的crypto驱动生成对应的包装函数,比如sm_psa_key_derivation_setup()。你可以在包装函数里看到它对HKDF_EXTRACTHKDF_EXPAND直接返回了PSA_ERROR_NOT_SUPPORTED

修改思路是这样:把HKDF相关算法的调用从Secure Manager驱动分支里“摘”出来,或者更简单一点,把Secure Manager驱动在key_derivation_setup这一层直接跳过HKDF算法,让psa_driver_wrappers.h在遍历驱动后进入软件实现分支。

示意逻辑大致如下(实际代码以工程生成的为准):

psa_status_t psa_driver_wrapper_key_derivation_setup( psa_key_derivation_operation_t *operation, psa_algorithm_t alg) { /* 先尝试Secure Manager驱动 */ psa_status_t status = sm_key_derivation_setup(operation, alg); if (status != PSA_ERROR_NOT_SUPPORTED) { return status; } /* 回退到内置软件实现 */ return mbedtls_psa_key_derivation_setup(operation, alg); }

这里有一个前提:软件实现的mbedtls_psa_key_derivation_setup()必须被编译进去,并且psa/crypto_config.h里必须定义了PSA_WANT_ALG_HKDF_EXTRACTPSA_WANT_ALG_HKDF_EXPAND这两个宏。如果你用的还是CubeMX默认配置,一般会默认带软件PSA实现,只需要确认这两个宏存在。

改完之后,Secure Manager的AES-GCM、ECDH等高速算法仍然走硬件安全侧,HKDF这个握手阶段的低频操作走软件实现,既不牺牲安全整体架构,又能让TLS 1.3的密钥调度正常运转。这是我最后采取的方案,也是我觉得最推荐的做法。

3.2 方案二:升级Secure Manager版本

如果你的生产条件允许升级Secure Manager固件,那方案二值得先看一眼。ST在后续版本里确实持续扩展了Secure Manager支持的算法范围,包括对TLS 1.3需要的单步HKDF支持的补强。具体哪些版本开放了,要看你手里的Secure Manager固件包的Release Notes。

升级这件事有一个约束:Secure Manager通常是和安全启动、密钥注入绑定在一起的,升级前必须确认当前板子的安全状态是否允许替换固件,以及升级后会不会导致已经烧录的密钥失效。开发板上无所谓,但如果已经到了产线阶段,就要严格走固件更新流程,不能直接在应用里刷。

如果你用的Secure Manager版本比较老,我建议先下载最新的STM32CubeFW_H5固件包,对照Release Notes确认算法支持矩阵。如果新版本明确写了支持HKDF-Extract/Expand,那直接在CubeMX里换个配置重新生成工程,再编译烧录,问题可能就消失了。不过这个方法依赖ST的更新节奏,不是你想升级就马上有的,所以不要把它当唯一保底方案。

3.3 方案三:换协议栈或手动实现密钥派生

如果前两个方案都走不通,还有一条路,但工程量和风险都大不少,我一般只建议在特殊场景下考虑。

一条路是换TLS协议栈,比如用wolfSSL。wolfSSL的TLS 1.3实现允许你把密钥派生部分配置成软件实现,也可以自定义回调,绕开Secure Manager的PSA算法限制。代价是你要重新适配接口、移植证书管理、调整非安全侧的RTOS集成,工作量不算小。

另一条路是在应用层手动实现TLS 1.3的密钥调度,完全绕开mbedTLS的默认流程。听起来很酷,但实际非常麻烦。TLS 1.3的密钥调度牵扯到握手状态机、Finished校验、会话恢复等多个环节,你自己实现了HKDF这两个单步函数还不够,还要把ssl_tls13_keys.c里的整体控制流接过来,出错概率极高。除非你是专门研究协议栈的,否则我不推荐走这条路。

3.4 方案对比与选型建议

把三个方案放在一起对比,会更直观:

方案改动量风险适用场景
软件回退,改PSA驱动路由小,改生成代码和配置低,HKDF走软件实现大多数开发项目,需要尽快跑通TLS 1.3
升级Secure Manager版本小,但依赖ST版本中,涉及固件升级和安全状态开发早期或官方已确认支持
换协议栈或手动实现大,移植和调优周期长特殊需求,如必须全硬件加速、不能改固件

对于大多数正在评估STM32H573 Secure Manager + TLS 1.3方案的开发者,我的建议是:第一步直接走方案一,把通信跑通再说,不要被“必须全部走Secure Manager硬件加速”这个念头束缚。TLS 1.3握手过程中的密钥派生只发生一次,计算量远小于后续流量的对称加解密。真正影响性能的AES-GCM流量加密仍然可以继续走Secure Manager,整体性能不会受太大拖累。

4. 实操手记:从复现到握手成功

4.1 确认当前算法路由

在动手改配置之前,先确认当前HKDF到底是不是被Secure Manager挡下来了。我写了一个很小的测试函数,在TLS握手前直接调用PSA接口:

#include "psa/crypto.h" void check_hkdf_support(void) { psa_key_derivation_operation_t op = PSA_KEY_DERIVATION_OPERATION_INIT; psa_algorithm_t algs[] = { PSA_ALG_HKDF_EXTRACT(PSA_ALG_SHA_256), PSA_ALG_HKDF_EXPAND(PSA_ALG_SHA_256), PSA_ALG_HKDF(PSA_ALG_SHA_256) }; for (int i = 0; i < 3; i++) { psa_status_t status = psa_key_derivation_setup(&op, algs[i]); printf("alg %d: status = %d\n", algs[i], (int)status); psa_key_derivation_abort(&op); } }

实测输出是这样的:

alg 2305: status = -164 alg 2306: status = -164 alg 2304: status = 0

这里的-164就是PSA_ERROR_NOT_SUPPORTED,0是PSA_SUCCESS。三种算法的宏值不一定每一位都相同,但行为很清楚:完整HKDF返回成功,单步HKDF全部返回不支持。这就锁定根因了。

4.2 修改mbedTLS/PSA配置

确认根因后,我按方案一操作。

第一步,打开mbedtls_config.h,确保MBEDTLS_PSA_CRYPTO_CONFIG是启用的,这样psa/crypto_config.h里的PSA_WANT_ALG_*宏才会真正影响编译。

第二步,编辑psa/crypto_config.h,确认以下宏被定义:

#define PSA_WANT_ALG_SHA_256 1 #define PSA_WANT_ALG_HMAC 1 #define PSA_WANT_ALG_HKDF 1 #define PSA_WANT_ALG_HKDF_EXTRACT 1 #define PSA_WANT_ALG_HKDF_EXPAND 1

第三步,找到psa_driver_wrappers.hpsa_driver_wrapper_key_derivation_setup()的实现,把HKDF单步算法的调用从Secure Manager驱动里分离出来,或者在Secure Manager驱动函数入口对PSA_ALG_HKDF_EXTRACTPSA_ALG_HKDF_EXPAND直接返回PSA_ERROR_NOT_SUPPORTED。后者更省事,因为driver wrapper本来就有fallback机制,收到NOT_SUPPORTED就会继续走软件实现。

如果你不想手改生成代码,也可以从CubeMX生成的工程里找到Secure Manager的driver配置宏,看看能不能在配置界面里把HKDF算法从Secure Manager的算法集里去掉。但根据我的经验,很多版本的CubeMX配置界面并没有细化到单步HKDF的粒度,所以手改psa_driver_wrappers.h反而是最直接的办法。

改完之后,重新编译。注意工程有非安全和安全两个Project,Secure Manager在安全Project里是预编译的,不需要重新编译固件,只需要把应用侧和PSA driver包装层重新构建即可。

4.3 验证握手成功与性能观察

重新编译烧录后,我把之前那个check_hkdf_support()函数再跑一遍,输出变成了:

alg 2305: status = 0 alg 2306: status = 0 alg 2304: status = 0

三个算法全部返回成功。然后跑完整的TLS 1.3握手,这次ClientHello发出去之后,服务器端开始正常回ServerHello,握手日志一路推进到Application Data阶段,最终mbedtls_ssl_handshake返回0。

为了确认性能影响,我在握手前后各打了一次时间戳,测下来软件HKDF参与的整个密钥调度耗时大约是毫秒级别。在250MHz的Cortex-M33上,一次TLS 1.3握手增加的延迟几乎可以忽略。真正占时间的依旧是第一次连接的ECDHE密钥交换和证书验证,HKDF这几个HMAC操作根本不是瓶颈。

我还顺手测了一下持续流量传输,确认了后续AES-GCM加解密仍然走Secure Manager硬件加速,吞吐没有受到影响。这样整条链路的负担分配是合理的:低频、短数据的密钥派生交给软件,高频、长数据的流量加密交给Secure Manager硬件。

5. 常见问题速查与避坑指南

5.1 错误码速查表

很多问题最终的报错都是PSA_ERROR_NOT_SUPPORTED,但原因五花八门。我整理了一个排查表,方便快速定位:

错误码发生位置常见原因排查方向
-164 NOT_SUPPORTEDpsa_key_derivation_setupHKDF单步算法未注册确认Secure Manager算法矩阵,改驱动路由
-164 NOT_SUPPORTEDpsa_hash_setupSHA算法被裁剪确认crypto_config.h里SHA_WANT宏
-135 INVALID_ARGUMENTpsa_key_derivation_input_bytesHKDF步骤顺序不对检查mbedTLS的PSA调用顺序
-138 BAD_STATEkey_derivation相关操作符状态被意外重置检查是否有中断或IPC并发问题
-147 PROGRAMMER_ERROR任意PSA调用参数或句柄不符合规范检查PSA API调用参数

如果错误码不是-164,那大概率是另一类问题,比如哈希算法没启用、密钥句柄不合法、操作顺序错误等,排查思路不要局限在“Secure Manager不支持”上。

5.2 几个容易踩的坑

第一个坑:只在mbedtls_config.h里加MBEDTLS_USE_PSA_CRYPTO,指望它把所有密码操作都交给PSA,结果TLS 1.3握手还是在HKDF这里挂掉。因为不管有没有这个宏,TLS 1.3密钥调度都走PSA,真正要改的是驱动路由和算法注册。

第二个坑:在psa/crypto_config.h里只定义了PSA_WANT_ALG_HKDF,没有定义PSA_WANT_ALG_HKDF_EXTRACTPSA_WANT_ALG_HKDF_EXPAND。这样编译期可能不会报错,但运行期PSA发现单步算法没有被编译进去,直接返回不支持。改配置时这三个宏要一起检查。

第三个坑:手动改了psa_driver_wrappers.h,但没有重新编译生成driver wrapper依赖的一些自动生成文件,导致改动被覆盖。CubeMX生成工程后会在构建时重新生成部分文件,所以最好在MakefileCMakeLists.txt里确认一下psa_driver_wrappers.h是不是自动生成的。如果是,你需要在CubeMX里找到对应的配置项,或者在生成后统一打补丁,而不是直接改源文件。

第四个坑:升级Secure Manager后,没有重新注入或迁移密钥,导致旧密钥在安全存储里不可读。这个坑在方案二里特别常见。升级前一定要备份并确认密钥迁移路径。

第五个坑:在一开始把问题归因到“Secure Manager不支持HKDF”后,直接决定所有密码运算都走软件,把Secure Manager的AES-GCM也停了,导致流量性能断崖式下跌。其实只需要让HKDF单步算法回退软件,AES-GCM、ECDH这些高频或高计算量算法继续交给Secure Manager即可。

最后再分享一个小心得:遇到这种“底层Secure Manager不支持某个算法”的问题,最忌一上来就怀疑自己的应用代码,也不要急着到处加驱动。先写一个几行代码的PSA算法支持探针,把所有相关算法的返回值打出来,一张表看下来,问题在哪一层就一目了然了。这套排查思路不仅适用HKDF,以后遇到Secure Manager里其他算法not supported,比如CMAC、ECDH某些曲线或SHA算法不支持时,都可以直接复用。

http://www.cnnetsun.cn/news/4314619.html

相关文章:

  • 程序员高考卷:一份覆盖算法、代码评审与隐写的工程实践自测题
  • YOLO OpenVINO 部署实操 | 推理提速3倍,NPU单帧 8.33ms
  • 后端面试实战复盘:技术面、项目深挖与临场策略全解析
  • docling 文档解析如何用 3 行代码跑通:PDF、DOCX 转 Markdown 并直接喂给 RAG
  • XGBoost时间序列预测实战:从特征工程到滚动预测
  • Windows下cuDNN与CUDA版本匹配安装指南
  • Memos 自托管笔记故障排查与部署配置完整指南:8 类常见问题一次讲透
  • Goose 桌面应用完整上手指南:从安装到跑通第一个任务
  • Cherry Studio:如何把多模型 AI 收进一个桌面窗口
  • 如何借助Remotion模板市场从零到出片:新手完整指南
  • 腾讯后端面试复盘:从算法到系统设计的实战经验与避坑指南
  • 字节前端二面实录:从并发控制到Vue3响应式的深度考察
  • PowerShell 安装失败?跨平台安装与验证 5 步避坑完整指南
  • TD-LTE前导检测:Zadoff-Chu序列与匹配滤波实现
  • 2025算法岗面试核心考点与实战攻略:从机器学习到大模型全解析
  • 信息学奥赛C++实战指南:从环境配置到算法精通的系统提升
  • PowerShell 快速入门指南:从启动到跑通第一个脚本
  • Cadence OrCAD CIS元件库深度解析与工程落地指南
  • LX Music 桌面版:免费聚合 6 大音乐源搜索的跨平台播放器完整指南
  • 7款降重会改坏论文吗?实测打分各有侧重(2026)
  • Jellyfin 媒体服务器快速部署指南:免费搭建你的私人影音中心
  • 京东春招技术岗笔试复盘:算法题型、八股范围与时间分配全解析
  • Starship 配色方案完整指南:3 步让凌乱的终端提示符变成清晰的视觉分层
  • Win11Debloat教程:3步给Windows 11瘦身,清理145个预装应用和AI杂项
  • 显卡无故氧化、接触不良?机房高湿腐蚀正在悄悄耗损硬件
  • 如何三步解包、修改并重打包 Android 启动镜像:MagiskBoot 实战指南
  • wav可以转mp3吗?当然可以,分享我这几天亲自用过的转换方法
  • 数据库岗秋招笔试复盘:SQL、索引与事务核心考点解析
  • 小满春招基础架构笔试复盘:分布式、存储与高可用考点解析
  • Anthropic API接入与Claude连接错误排查实践