尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STM32H573 Secure Manager下PSA密钥生成返回-129的排查与解决

STM32H573 Secure Manager下PSA密钥生成返回-129的排查与解决 开篇先交代一下背景。最近一直在折腾STM32H573的Secure Manager方案手上项目需要同时生成ECDSA P-256签名密钥和AES-128对称密钥逻辑上很常规初始化PSA Crypto构造key attributes调用psa_generate_key()生成临时密钥对用完即毁。结果代码烧进去之后返回的错误码直接把我看懵了psa_generate_key()返回PSA_ERROR_NOT_PERMITTED (-129)而且是在生成volatile密钥时报的。换成persistent密钥就正常一改回volatile就报错异常稳定地复现。这篇文章我就把这个问题的完整排查过程、底层原因和最终解决办法整理出来。如果你也正在用STM32H573的Secure Manager做密钥管理或者遇到类似的PSA API调用被拒的场景这篇内容应该能帮你省下不少排查时间也顺便把Secure Manager的权限模型和密钥生命周期这几块关键知识理一理。1. 问题现象与复现路径先从最直观的入手1.1 复现时的软硬件环境和调用栈我手上的硬件平台是NUCLEO-H573ZI开发板板载STM32H573RIT6Cortex-M33内核带TrustZone。软件侧用的是STM32CubeFW_H5_V1.1.0工程结构是基于STM32CubeMX生成的开启了Secure Manager支持也就是说非安全侧Non-Secure简称NS侧的应用程序通过PSA API直接调用Secure侧预置的安全服务。整个工程在NS侧跑RTOS用的ThreadX但问题跟RTOS无关裸机也一样复现。核心代码长这样psa_key_attributes_t attrs PSA_KEY_ATTRIBUTES_INIT; psa_set_key_type(attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_usage_flags(attrs, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE); psa_set_key_algorithm(attrs, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); psa_set_key_lifetime(attrs, PSA_KEY_LIFETIME_VOLATILE); // 关键区别在这 psa_key_id_t key_id 0; psa_status_t status psa_generate_key(attrs, key_id); // status PSA_ERROR_NOT_PERMITTED (-129)这个调用是紧接着psa_crypto_init()返回PSA_SUCCESS之后执行的排除初始化失败的因素。我一开始以为是不是密钥属性设置有问题试过只保留PSA_KEY_USAGE_SIGN_MESSAGE一个用途位算法换成PSA_ALG_ECDSA_ANY甚至不设置算法全部一样返回-129。更诡异的是把lifetime切成PSA_KEY_LIFETIME_PERSISTENT并配合psa_set_key_id()指定一个persistent key ID后同样的属性能正常生成密钥。1.2 错误码逐一排查-129到底在表达什么要解决问题先把PSA_ERROR_NOT_PERMITTED在PSA Crypto API里的语义吃透。这个错误码表示“调用者请求的操作被实现策略拒绝”不是参数错误不是资源不足更不是底层硬件故障——是安全策略层面的拒绝。跟几个相邻错误码做个对比就清楚了错误码值含义典型触发场景PSA_SUCCESS0操作成功属性和上下文都满足要求PSA_ERROR_NOT_PERMITTED-129策略拒绝调用者被禁止执行该操作lifetime/usage/上下文触发安全策略限制PSA_ERROR_NOT_SUPPORTED-134功能存在但当前配置不支持底层算法/密钥类型未编译进固件PSA_ERROR_INVALID_ARGUMENT-135参数错误key type/algorithm不匹配、属性字段非法PSA_ERROR_COMMUNICATION_FAILURE-137Secure侧通信失败IPC mailbox不可用、RSS/Secure Manager未启动从错误码的特性来看-129大概率不是key attributes本身写错了的问题——如果是属性不合法PSA规范要求返回PSA_ERROR_INVALID_ARGUMENT。因此问题出在Secure Manager对“NS侧生成volatile密钥”这一行为做了策略拦截。1.3 volatile密钥和persistent密钥背后的安全语义差异为什么Secure Manager会放着persistent密钥不管单拦volatile密钥我从PSA Certified规范和TF-M的隔离模型里找到了一些线索。在PSA安全模型中volatile密钥只存在于RAM中的临时密钥槽位生命周期到调用psa_destroy_key()或系统重启即结束。persistent密钥则不同它会通过Secure Manager的Secure Storage服务经过加密、完整性校验后落盘到安全存储区域每把密钥都带有对应的元数据和访问策略。问题恰恰出在这里。Secure Manager作为Secure侧的控制中心对persistent密钥可以做到全生命周期监管创建时记录归属、使用时校验权限、销毁时清除存储记录。而volatile密钥的创建和销毁都发生在内存里几乎不留审计痕迹Secure Manager对它的监管能力天然弱一截。因此ST在Secure Manager的安全策略里针对NS侧调用psa_generate_key()生成volatile密钥做了限制。这在设计上可以理解——既然无法对volatile密钥做有效的持久化审计那就从源头收紧权限宁可误杀也不放过。2. 根因分析Secure Manager的权限模型究竟卡在哪一环2.1 STM32H573的安全架构与Secure Manager的分工要把这个错误彻底说明白必须得把H573的Secure Manager架构梳理一遍。H573跟之前的STM32L5/U5系列一样Cortex-M33自带TrustZone可以简单理解成一颗芯片里住了两个世界Secure世界安全侧和Non-Secure世界非安全侧。Secure侧运行的是ST出厂预置的Secure Manager组件它分为两层ST-iROT不可变根信任和SFI安全固件安装负责系统初始化、安全启动、密钥管理、安全存储、密码学计算等敏感操作NS侧跑的是用户自己的应用不能直接访问安全资源只能通过PSA API的IPC机制向Secure侧发请求。这个架构参考了很多基于TF-M的MCU安全方案但又不太一样。TF-M通常允许用户自定义Secure侧服务H573的Secure Manager是ST出厂预置的固件用户无法修改Secure侧代码只能通过Secure Manager暴露出来的PSA接口调用固定服务。好处是开箱即用不用自己维护Secure侧坏处是Secure侧策略是黑盒出问题后很难像TF-M那样直接翻源码定位。2.2 SPE与NSPE边界上的调用上下文核心问题在于psa_generate_key()调用发生在NSPE非安全处理环境请求要穿越SPE安全处理环境边界才能抵达Secure Manager。在穿越过程中Secure Manager需要判断调用者是谁、调用的操作允许不允许、以什么生命周期去执行。Secure Manager对调用者的身份判断靠的是Client ID。当NS侧通过IPC向Secure侧发送PSA请求时会携带一个调用上下文Secure Manager通过这个上下文识别调用方。如果你的工程里存在多个NS侧客户端比如主应用和一个独立的配置工具每个客户端的Client ID不同权限也就不同。但如果Client ID没有正确配置Secure Manager只能把它当作匿名调用处理权限自然最严格。我开始怀疑是不是Client ID的问题于是检查了工程中ns_client_id相关的配置。在STM32CubeMX里如果启用了Secure Manager通常会生成一个默认的客户端配置。如果配置成了0IPC层会当成无效客户端处理。我又试过通过psa_set_key_id()给密钥指定ID这也没用-129依旧。真正有意思的是我对比了从Secure侧调用同一个PSA API通过Secure侧测试代码和NS侧调用同一个PSA API前者生成volatile ECC密钥是完全正常的。这说明Secure Manager的能力本身具备volatile密钥生成能力限制只针对NSPE调用方。2.3 SPM策略中volatile key generation的真实限制逻辑继续往下挖实际上Secure Manager内部的SPMSecure Partition Manager对密钥生成操作有一套完整的策略矩阵。这个矩阵里至少包含三个维度调用者所在的处理环境SPE内部调用 vs NSPE跨边界调用权限差异巨大要执行的PSA API类别签名/验签、密钥导入、密钥生成、哈希计算每个操作单独判定密钥生命周期类型volatile还是persistent管理强度不同在这套矩阵下psa_generate_key()在NSPE侧生成persistent密钥是放行的因为密钥最终落到Secure StorageSecure Manager拥有完整控制权但NSPE侧生成volatile密钥会被拦截因为volatile密钥从创建到最后销毁都完全处在调用者的控制范围内Secure Manager无法介入。换句话说一个不受Secure Manager监管的密钥对象不应该由它在不可信的NSPE侧创建。这个推断跟我实际测试的结果完全吻合。顺带说明一下这并非H573独有的设计部分基于TF-M的芯片也有类似策略只是在H573上策略更加严格直接以错误码形式暴露出来了。3. 从问题到解决完整排查链路与可行方案3.1 Step 1 确认密钥生命周期与属性设置遇到任何PSA API返回异常先检查密钥属性这是成本最低的一步。我这个例子最终确认属性没有问题但属性检查本身是一个必要的过程至少能排除一半以上的低级错误。属性检查建议走一遍顺查流程psa_key_attributes_t attrs PSA_KEY_ATTRIBUTES_INIT; // 1. 先初始化属性对象确保没有残留数据 psa_set_key_type(attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_bits(attrs, 256); psa_set_key_usage_flags(attrs, PSA_KEY_USAGE_SIGN_MESSAGE); psa_set_key_algorithm(attrs, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); psa_set_key_lifetime(attrs, PSA_KEY_LIFETIME_VOLATILE); // 2. 打印当前属性确认与期望值一致 psa_key_lifetime_t lifetime psa_get_key_lifetime(attrs); psa_key_type_t type psa_get_key_type(attrs); size_t bits psa_get_key_bits(attrs); // 核对这三个值这里最容易忽略的是密钥类型和算法之间的匹配规则。比如PSA_KEY_TYPE_AES配PSA_ALG_ECDSA或者PSA_KEY_TYPE_ECC_KEY_PAIR没指定曲线family这些组合虽然不会导致-129但会在后续使用时暴露问题。检查时优先确认三个字段type、usage_flags、lifetime。lifetime字段如果默认初始化得到的值是PSA_KEY_LIFETIME_VOLATILE也就是0这跟你显式设置volatile没有区别所以不设置lifetime但报-129也一样适用本文的场景。3.2 Step 2 校验调用上下文与安全边界排除属性问题后第二步确认调用上下文。先确认工程确实跑在NS侧并且通过psa_crypto_init()完成了PSA客户端初始化。在H573上NS侧调用PSA API通常需要SDK中间层支持。如果使用的是STM32CubeH5的Secure Manager中间件psa_crypto_init()内部会初始化mailbox等通信资源。如果这些资源没初始化或者初始化时序有误psa_generate_key()可能直接返回PSA_ERROR_COMMUNICATION_FAILURE或者PSA_ERROR_GENERIC_ERROR而不是-129。既然我们能稳定拿到-129基本说明NS侧到Secure Manager的IPC通道是通的问题定位在策略层。还要检查一个容易踩的坑工程中是否混用了PSA API的不同实现。H573上如果同时引用了STM32 HAL自带的软件PSA实现和Secure Manager托管实现编译器链接到了错误的实现会出现各种匪夷所思的返回码。我当时查了map文件确认psa_generate_key符号是从libs_smanager_ns.a这类Secure Manager库中解析出来的这个方向可以放心。3.3 Step 3 调整Secure Manager配置与策略走到这一步基本可以确认-129是Secure Manager策略层面的拒绝。那就需要看Secure Manager的配置是否允许某些操作。H573的Secure Manager策略可以通过ST提供的工具和配置项来调整需要在CubeMX里开启相关配置。具体的配置入口STM32CubeMX中Categories - Middleware - STM32 Secure Manager配置页面检查Secure Manager services启用情况确认RSS boot、SFI等选项已经正确配置在Project Manager - Advanced settings里确认生成的代码包含了SN Manager的IPC启动流程说实话Secure Manager的官方配置项里并没有一个“允许volatile key generation”的开关至少目前我手上的CubeMX 6.11.0里没有看到。所以这一层我们能做的只是确认配置无误、固件版本匹配、生命周期状态正常为后续更深的排查打底。3.4 Step 4 绕过限制改用persistent密钥实现同等能力策略无法直接修改时最终务实的解法是对应用层逻辑做调整。既然Secure Manager不信任NSPE生成volatile密钥那就让它生成persistent密钥用完之后主动销毁。改动后的代码如下psa_key_attributes_t attrs PSA_KEY_ATTRIBUTES_INIT; psa_set_key_type(attrs, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); psa_set_key_usage_flags(attrs, PSA_KEY_USAGE_SIGN_MESSAGE | PSA_KEY_USAGE_VERIFY_MESSAGE); psa_set_key_algorithm(attrs, PSA_ALG_ECDSA(PSA_ALG_SHA_256)); psa_set_key_lifetime(attrs, PSA_KEY_LIFETIME_PERSISTENT); // 指定一个唯一的持久化密钥ID应用自定义建议放在固定地址段 psa_set_key_id(attrs, 0x00010001); psa_key_id_t key_id 0; psa_status_t status psa_generate_key(attrs, key_id); if (status PSA_SUCCESS) { // 使用密钥执行签名、验签等操作 // 用完后主动销毁避免密钥残留在安全存储中 status psa_destroy_key(key_id); }实测这个方案是稳定可行的key ID可以从0x00010000这一段开始分配避开系统保留区域和Secure Manager自身使用的ID范围。配合psa_destroy_key()在每次使用后清理volatile密钥能实现的效果基本都能覆盖。不过要提醒的是persistent密钥的生成会在Flash里留下痕迹。如果需求里明确要求“密钥不得落盘”那你需要重新评估方案——比如考虑在Secure侧定制服务或者切换到支持volatile密钥的PSA实现例如完全基于本地TF-M的方案。H573上这是我目前能测试出的最合适路径。3.5 实操心得当Secure Manager成为黑盒时怎么调试H573的Secure Manager不像TF-M那样可以单步调试Secure侧排错手段有限。我将调试过程中的几个关键经验列一下用不同密钥类型交叉验证。如果ECC密钥报-129试试AES密钥。如果全类型都报-129更倾向策略拦截如果只有某些类型报锁定单点问题。用persistent/volatile切换来缩小排查范围。两种lifetime各跑一遍结果不同基本就是lifetime策略问题结果相同就从头检查属性和IPC。留意调试器连接带来的策略变化。Secure Manager在检测到调试器连接时某些安全策略会被自动收紧。调试完后用独立供电运行测试有时问题会自动消失。保留一个最小可复现工程。固定在一个最小工程里做验证避免业务代码干扰定位问题和回归都方便。4. 常见问题与排查技巧实录这类报错还能怎么快速收口4.1 几个容易混淆的坑不是所有-129都一个原因排查过程中我顺手记录了一下H573 Secure Manager下PSA API常见的几个坑尤其是跟-129容易混淆的场景放在一起对分析很有帮助。第一类是调用时机不对。部分Secure Manager服务只在系统生命周期特定阶段开放比如在安全启动流程尚未完全结束时某些PSA操作会被临时禁止。我实际遇到过一次系统上电后立即调用psa_generate_key()返回-129但在系统运行几百毫秒后再次调用就成功了跟踪下来是Secure Manager仍在执行启动后初始化相关服务还没就绪。这种问题在打印日志时容易跟策略问题混淆。第二类是boot锁和调试锁状态影响。H573支持将设备生命周期状态调整为不同级别某些状态下Secure Manager会放行更宽松的操作某些状态则收紧。如果你在开发阶段用STM32CubeProgrammer改过选项字节后来忘了恢复可能影响Secure Manager的策略判定。排查时建议先读取当前安全状态确认与预期一致。第三类是OTA和固件升级引入的策略变更。Secure Manager固件如果升级过策略矩阵可能跟着变化。比如旧固件允许NS侧执行的操作新固件可能默认禁止。这类问题在量产设备上尤其容易暴露做方案时最好把Secure Manager固件版本纳入版本管理范围。4.2 排查过程中的工具与日志技巧实录H573 Secure Manager的调试手段确实少但也不是完全没有。给你一套我实测可用的日志排查方法Secure Manager日志STM32CubeH5中间件里内置了Secure Manager的错误日志读取函数可以通过PSA API返回的额外信息定位具体被拒的服务ID和调用上下文。我在CubeMX里勾选了“Secure Manager Runtime Logs”相关选项后通过串口打印能看到Secure侧返回的JSON格式日志里面记录了被拒绝的操作类型和调用来源。状态寄存器检查通过读RSS相关的状态寄存器可以拿到Secure Manager当前运行状态和生命周期状态。使用STM32CubeProgrammer连接后在OTP和Option Bytes页确认实际状态。IPC调用跟踪在NS侧对psa_generate_key()做一层封装在调用前和后打印时间戳和错误码看看是否存在时序敏感问题。psa_status_t tracked_generate_key(psa_key_attributes_t *attrs, psa_key_id_t *key_id) { printf([PSA] psa_generate_key() entry, type%lu, lifetime%lu\r\n, (unsigned long)psa_get_key_type(attrs), (unsigned long)psa_get_key_lifetime(attrs)); psa_status_t status psa_generate_key(attrs, key_id); printf([PSA] psa_generate_key() exit, status%ld\r\n, (long)status); return status; }这套日志对定位“什么时候开始报-129”特别关键。我就是通过这个方式确认了错误与系统运行时间的相关性。4.3 常见误操作总结新手最容易翻车的4个操作虽然这次问题的根因是Secure Manager策略限制但我排查过程中尝试过不少错误方向也顺便把它们整理出来省得你再踩一遍。第一盲目修改key attributes属性来规避错误。调整usage flags、algorithm、类型结果全试了一遍-129依旧。这是因为问题出在lifetime所在的策略维度跟这些字段无关。属于典型的“改错方向”。第二怀疑HAL库版本问题来回更换中间件版本。从V1.0切到V1.1问题没有消失也没有变化。方向不指向中间件。第三试图在NS侧代码里手动访问Secure Manager内部寄存器。H573的Secure Manager占用Secure区地址NS侧直接访问会触发总线错误或返回随机数据完全走不通。更离谱的是我还试过通过Memory Protection Unit放行对Secure内存的访问后来想通了TrustZone的SAU权限控制优先于MPU配置这条路就不该碰。第四在Secure Manager不支持的操作上死磕。比如试图修改Secure Manager的密钥策略或者用调试接口直接往Secure侧注入代码。Secure Manager是ST出厂预置的不开放这部分能力死磕徒劳无功。4.4 如果遇到类似的-129但场景不同如何举一反三这次的经历比较有代表性总结出一套可复用的判断逻辑观察到的现象优先排查方向可能原因示例生成persistent成功volatile失败策略对volatile的限制Secure Manager限制NS侧生成volatile key指定某种类型/算法失败其他成功属性与算法匹配算法/类型组合不被Secure Manager支持冷启动立即调用失败延时后成功服务初始化时序Secure Manager启动流程未完成调试器连接时失败断开后成功调试状态策略安全策略检测到调试连接并收紧权限所有PSA操作都失败IPC链路问题psa_crypto_init()未调用或mailbox异常这套判断思路本质上是一个简化的二分排查先区分是全局故障还是局部故障再区分是策略问题还是实现问题逐步缩小范围。你后面再遇到PSA相关的错误码可以先套一套这个架子再深入具体细节往往能少走一半弯路。最后分享一点实战心得这次排查-129的过程与其说是在解决一个API调用问题不如说是在补一堂架构课。H573的Secure Manager是一个安全性优先的封闭组件它对NS侧的各个PSA操作有一套自己的信任模型。volatile密钥短暂存在于内存、不受Secure Storage监管在Secure Manager看来是风险较大的一类请求直接拒绝是合理设计。至少在NUCLEO-H573ZI、STM32CubeH5 V1.1.0这套环境下我的结论是稳定的。如果是生产项目建议把生成persistent密钥后立即用、用完即毁的逻辑固化到自己的key management模块里这样既能规避这个问题也能让调用逻辑更统一。最后建议大家升级到最新的STM32CubeH5中间件版本再看看Secure Manager Release Notes里是否有策略相关的更新。照着我上面这套流程走一遍你的-129大概率也很快可以清掉。
返回列表