从沙子到车辙(6.2):硬件安全——HSM
6.2 硬件安全HSM本文内容摘自本人的开源书《从沙子到车辙 - 一个工程师的理解》 在线阅读/下载from-sand-to-rutsgitclone https://github.com/Lularible/from-sand-to-ruts⭐ 如果对您有帮助欢迎 Star 支持也欢迎通过 GitHub Issues 交流讨论。高速路上的远程劫持2015年7月。圣路易斯高速公路。一辆Jeep Cherokee以110km/h的速度行驶在车流中。驾驶员的手在方向盘上脚在油门上。一切正常。然后——空调风量跳到最大。收音机音量炸裂。雨刷在没有下雨的晴天开始疯狂摆动。接着变速箱被远程切到空档。油门踏板失效。最后方向盘锁死在当前位置。驾驶员在高速公路上对方向盘、油门、刹车——全部失去控制。这不是电影。这是安全研究员Charlie Miller和Chris Valasek对FCA菲亚特克莱斯勒做的一次公开安全演示。他们的攻击不是魔法。每一步都是系统性的。第一步外部入口。Uconnect 信息娱乐系统通过 Sprint 蜂窝网络保持互联网连接。攻击者扫描蜂窝网络的 IP 地址段发现 Uconnect 头部单元开放了端口 6667D-Bus 调试接口。没有防火墙。没有认证。第二步横向移动。信息娱乐系统的 OMAP 处理器通过内部的 SPI 总线与 V850 网关控制器通信。攻击者利用 D-Bus 接口获得了 OMAP 上的 root shell重新刷写了 V850 的固件。V850 现在变成了攻击者的傀儡——它可以向 CAN 总线发送任意报文。第三步CAN 注入。CAN 协议没有任何认证机制。任何能接触 CAN 总线的节点都可以发出任意 CAN ID 的任意数据。攻击者从 V850 向 CAN-C底盘 CAN发送了刹车释放“变速箱切空档”“方向盘角度偏移的 CAN 帧。刹车 ECU 分不清来自制动踏板的减速指令和来自攻击者的减速指令”。方向盘 ECU 分不清来自驾驶员转角传感器的转角信号和攻击者注入的转角信号。FCA 召回 140 万辆车。汽车行业第一次集体意识到车不再是一个封闭的机械系统。它是一个连接到互联网的、多网络接口的、可以被远程攻击的计算节点。为什么必须是硬件一个自然的疑问为什么不靠软件写好代码、做好代码审计、加防火墙——软件安全一样可以防攻击答案残酷但真实代码永远有 bug。Linux 内核截至 2024 年统计的公开缺陷超过三千个。AUTOSAR CP 的完整基础软件栈超过十万行 C 代码。任何非平凡的软件系统都不存在无漏洞的保证。Jeep 那次攻击如果 Uconnect 的信息娱乐系统代码写得完美无缺、防火墙配置毫无疏漏那次攻击就不会发生。但要求任何复杂软件完美无缺是一个无法兑现的承诺。攻击者只需要找到一个漏洞。防御者必须堵上所有漏洞。攻防双方的成本不对称。所以需要另一层保障——不依赖代码质量的保障。这层保障就是物理隔离 密码学。物理隔离意味着即使主 CPU 的全部代码都被攻破、攻击者拿到了主 CPU 的完整执行权限——攻击者也读不到密钥。因为密钥不在主 CPU 的地址空间里。密码学意味着攻击者要伪造一条合法的安全 CAN 报文需要知道密钥。不知道密钥再怎么修改主 CPU 代码也伪造不出合法签名。两者合在一起构成了不依赖代码质量的信任根基。HSM硅晶片内部的独立王国HSMHardware Security Module是一个嵌入在 MCU 芯片 die 内部的独立安全处理器。独立不是修辞。HSM 有自己的 CPU 核通常是 ARM Cortex-M0 或专用安全核有自己独立封装的 Flash 和 RAM。这些存储单元在主 CPU 的地址映射中不存在——物理走线根本没有连接到主 CPU 的交叉总线上。即使攻击者把 JTAG 调试器插在芯片的调试口上、dump 了全部主 Flash 的全部内容——他读不到 HSM 内部的一个 bit。HSM 只做三件事。只做三件但每一件都是信任的基石。第一支柱密钥存储ECU 需要的所有加密密钥——安全通信的 AES 密钥、固件签名的公钥、唯一设备标识的私钥——全部存储在 HSM 内部。主 CPU 不知道密钥的值。主 CPU 只能对 HSM 说请用 slot 3 的密钥加密这段数据或者请用 slot 5 的密钥验证这个 CMAC 值。HSM 内部执行加密运算把结果返回给主 CPU。密钥在运算过程中经过 HSM 内部的加密引擎从未出现在主 CPU 的总线上。这是 CSEcCypress Security Extension / SHE-compliant Crypto Engine的典型 API 用法——主 CPU 通过共享内存和命令寄存器与 HSM 交互/* CSEc_API.c — 主CPU调用HSM加密服务的接口SHE规范简化版 */#defineHSM_CMD_ENC_ECB0x01/* AES-128 ECB 加密 */#defineHSM_CMD_DEC_ECB0x02/* AES-128 ECB 解密 */#defineHSM_CMD_GENERATE_MAC0x03/* AES-CMAC 生成 */#defineHSM_CMD_VERIFY_MAC0x04/* AES-CMAC 验证 */#defineHSM_CMD_LOAD_KEY0x05/* 从HSM内部ROM/OTP加载密钥到slot */#defineHSM_CMD_RND0x06/* 真随机数生成 *//* HSM 命令接口——通过共享内存和状态寄存器通信 */typedefstruct{volatileuint32_tCMD;/* 命令寄存器 */volatileuint32_tSTATUS;/* 状态: 0空闲, 1忙, 2完成, 3错误 */volatileuint32_tKEY_SLOT;/* 密钥槽: 0-9 */uint8_tINPUT[16];/* 输入数据(明文/密文/消息) */uint8_tOUTPUT[16];/* 输出数据(密文/明文/MAC) */}HSM_Interface_t;staticHSM_Interface_t*consthsm(HSM_Interface_t*)HSM_BASE_ADDR;/* 使用HSM slot 3的密钥对16字节明文进行AES-128加密 */intHSM_AES_Encrypt(uint8_tkey_slot,constuint8_t*plaintext,uint8_t*ciphertext){/* 1. 把明文拷贝到HSM输入缓冲区 */memcpy((void*)hsm-INPUT,plaintext,16);/* 2. 选择密钥槽 */hsm-KEY_SLOTkey_slot;/* 3. 发送加密命令 */hsm-CMDHSM_CMD_ENC_ECB;/* 4. 轮询等待——HSM在自己的核上执行AES */while(hsm-STATUSHSM_STATUS_BUSY){/* 在100μs控制周期内这样等待不可接受——实际代码 * 会使用中断回调。这里展示最原始的同步接口。 */}if(hsm-STATUS!HSM_STATUS_DONE){return-1;/* HSM报告错误 */}/* 5. 从HSM输出缓冲区读取密文 */memcpy(ciphertext,(void*)hsm-OUTPUT,16);return0;}/* 使用HSM slot 5的密钥验证AES-CMAC */intHSM_Verify_CMAC(uint8_tkey_slot,constuint8_t*message,uint8_tmsg_len,constuint8_t*expected_mac){/* 把消息拷贝到共享内存 */memcpy((void*)hsm-INPUT,message,msg_len);hsm-KEY_SLOTkey_slot;hsm-CMDHSM_CMD_VERIFY_MAC;while(hsm-STATUSHSM_STATUS_BUSY);if(hsm-STATUS!HSM_STATUS_DONE){return-1;}/* HSM的验证结果在 OUTPUT[0] * 0x00 CMAC不匹配 * 0x01 CMAC匹配 */return(hsm-OUTPUT[0]0x01)?0:-1;}/* 从HSM的真随机数发生器获取随机数用于SecurityAccess seed */intHSM_GetRandom(uint8_t*random_bytes,uint8_tlen){for(uint8_ti0;ilen;i16){hsm-CMDHSM_CMD_RND;while(hsm-STATUSHSM_STATUS_BUSY);memcpy(random_bytesi,(void*)hsm-OUTPUT,(len-i16)?16:len-i);}return0;}关键点plaintext明文在memcpy到hsm-INPUT之后进入了主 CPU 无法访问的总线域——HSM 的 INPUT 寄存器映射在 HSM 专有的地址空间里由 HSM 内部总线连接主 CPU 通过桥接器bridge访问——但桥接器只暴露 INPUT/OUTPUT 和命令寄存器。密钥、中间轮密钥、S-Box 查找表——这些全部在 HSM 内部。主 CPU 总线上的任何逻辑分析仪、任何调试器、任何被攻破的内核代码——都看不到。第二支柱安全启动Secure BootMCU 上电。HSM 先于主 CPU 启动——它有自己独立的时钟和复位逻辑不依赖主 CPU。安全启动的完整流程上电 │ ▼ 电压爬升 → HSM 内部 RC 振荡器起振 → HSM 复位释放 │ ▼ HSM 从内部 ROM 加载 HSM 固件小~4KB │ ▼ HSM 固件自检验证 HSM Flash 中固件签名的完整性 │ (HSM 的 OTP 中烧有 HSM 固件公钥的哈希) │ ├── 签名无效 → HSM 停止主 CPU 永远不启动变砖保护 │ ▼ 签名有效 HSM 从外部 Flash 读取主 CPU 固件映像新分区 │ ▼ HSM 计算固件映像的 SHA-256 哈希 │ ▼ HSM 用公钥验证固件映像末尾的数字签名RSA-2048 或 ECC P256 │ ├── 签名无效 → HSM 拒绝释放复位线 │ 尝试启动旧分区A/B回退逻辑 │ ├── 旧分区签名有效 → 释放复位线跑旧固件 │ └── 旧分区签名无效 → 永远不启动没有可信任的固件 │ ▼ 签名有效 HSM 释放主 CPU 的复位线 │ ▼ 主 CPU 从固件起始地址开始运行 此时主 CPU 已经确信它即将执行的每一行代码都是从 OEM 的签名服务器出来的。 没有被改过一个 bit。在main()执行之前——在主 CPU 的第一个全局变量被初始化之前——信任已经被建立。这个信任不是从代码推导出来的——代码本身是被验证的对象。信任的锚点在更底层。穿透信任的物理锚点——熔丝HSM 内部的 OTP一次性可编程存储是由熔丝fuse阵列组成的。在芯片制造的最后阶段——或在 ECU 产线的安全编程站中——编程器向选定的熔丝施加一个高电压脉冲。这个脉冲的电流密度约 10^6 A/cm²在金属连线中产生焦耳热和电迁移——金属原子被物理地推离原位连线的电阻从接近零跳到接近无穷大。一根熔丝从导通变成了断开。一断永断。热力学第二定律决定这条连线的原子不可能自发地回到原位。你不需要任何软件来保护这个状态——宇宙的物理定律在保护它。安全启动的公钥哈希就烧在这些熔丝里。OTP 上电之后立刻可读——不需要任何软件初始化。上游的固件验证逻辑用这个哈希验校签名块中的公钥用公钥验证固件签名用签名确认固件未被篡改。链条的起点是用来烧断熔丝的那个高压脉冲。信任不是推导出来的——信任是锚定在物理现实中的。在芯片的 die 上在二氧化硅和铝互连层下面在一组被电迁移永远切断的金属连线阵列里——信任就在这里。这就是**信任根Root of Trust**的含义信任链的最终锚点不在软件中——在物理中。第三支柱加密加速HSM 内置了硬件加密引擎——AES-128/256、SHA-256、RSA-2048、ECC P256/P384、TRNG 真随机数发生器。一套 AES-128 加密在 HSM 的硬件引擎中只需要 11 个时钟周期pipelined AES core。如果让主 CPU 的软件库算——即使高度优化的 T-table 查表实现也要约 200 周期。在 100μs 控制周期的实时系统里省下的 189 个周期可能是能在截止时间前发完那帧安全 CAN 报文和来不及之间的差别。对于非对称加密RSA-2048 签名验证硬件加速的差距是碾压级的。软件 RSA-2048 验证一次签名约需 50-100ms。HSM 的硬件加速器 5ms。在安全启动中——你只有几十毫秒的启动时间预算——软件 RSA 根本不可行。SecOC给 CAN 报文穿上签名安全启动保启动。但主 CPU 在运行时发出的每一帧 CAN 报文——它们在总线上裸奔攻击者能不能伪造Jeep 被黑的核心漏洞之一正是 CAN 没有认证。任何节点都可以发送任意 CAN ID接收方无法分辨真假。SecOCSecure Onboard Communication是 AUTOSAR 定义的车内安全通信机制。ECU A 和 ECU B 共享一个 AES 对称密钥存储在各自 HSM 的 slot 中。发送方ECU A构造 CAN 帧的数据部分8 字节——比如方向盘转角信号。从 HSM 获取当前的 Freshness ValueFV 单调递增计数器。把 FV4 字节 CAN 数据8 字节拼接成 12 字节的消息。发送 HSM 命令“用 slot 3 的密钥对这个 12 字节消息计算 AES-CMAC”。HSM 返回 16 字节的 CMAC——截取前 4-8 字节作为Authenticator。把 Authenticator 附加在 CAN 帧的数据域尾部发送到总线上。FV写入 NVM 以防止断电后重放。接收方ECU B收到 CAN 帧提取数据部分和 Authenticator。从 HSM 获取自己记录的 FV应该 ≥ 发送方的 FV。检查接收的 FV 是否 上次接收的 FV。如果 ≤ 上次值——这是重放攻击丢弃。用 FV CAN 数据重新计算 CMAC与收到的 Authenticator 比对。匹配——数据未被篡改处理。不匹配——发送方的密钥不对报文是伪造的丢弃。Freshness Value 是 SecOC 最巧妙的设计。它是一个单调递增的计数器每发一帧 1。攻击者在总线上录下了一帧合法报文想稍后重放——接收方检查 FV发现这个值 ≤ 上次收到的值——这是旧消息你在重放。丢弃。不需要 GPS 同步时钟。不需要毫秒级时间精度。只需要一个单调递增的数字。重放攻击被彻底防死。攻击者不知道密钥无法伪造 CMAC不能重用旧报文FV 过时无法篡改内容CMAC 不匹配。三重防护全部来自一个存在于 HSM 内部的、主 CPU 永远看不到的对称密钥。穿透从方向盘到硅晶片我们做一次安全穿透。你向左打方向盘。EPS 控制器收到指令驱动转向电机。如果存在攻击者攻击者通过 OBD 口向 CAN 总线注入方向盘右转的报文。EPS 控制器分不清真假——CAN 协议没有认证。如果启用了 SecOCEPS 控制器收到方向盘右转的报文后HSM 验证附加的 CMAC。不匹配——发送方的密钥不对。丢弃。转向电机不动。攻击失败。往下穿透一层这个 CMAC 验证在 HSM 的硬件加密引擎中完成——AES-128 运算11 个时钟周期。再往下验证用的 AES 密钥存储在 HSM 的 key slot 3 中——主 CPU 读不到。HSM 内部总线和主 CPU AHB 总线之间的桥接器不暴露任何密钥寄存器。再往下HSM 自身在上电时经过安全启动自检——HSM 固件未被篡改。这个自检使用 HSM OTP 中烧入的公钥哈希从 OTP 到签名验证——整条信任链横跨了熔丝阵列、HSM 内部 ROM、外部 Flash。再往下熔丝阵列。高压脉冲。金属原子电迁移。0 变 11 变 0。断电后不丢失。20 年数据保持。存储在宇宙的物理定律中。从方向盘的物理转动到硅晶片 OTP 中一段被电迁移永久切断的金属连线。安全的链条贯穿了这整条路径。链条的任何一环断裂整条信任崩塌。安全是社会契约你坐进一辆车系上安全带点火上高速。你对这辆车有一个基本假设方向盘会响应你的手刹车会响应你的脚。没有人能远程抢走这个控制权。这个假设如此基本以至于你从不有意识地怀疑它。但它不是天然成立的——它是被工程保障的。汽车制造商对每一个驾驶员有一个承诺“我们交付给你的车控制权永远属于你。” 这个承诺的法律形式是产品责任。技术形式是 HSM、安全启动、SecOC、安全编程流程——整套硬件安全架构。每当有人质疑在 ECU 里加 HSM 芯片多花两美元值不值得——回答是这不是成本核算。这是信任的物理基础。驾驶员把命交给你。你用什么保证但安全启动验证的是固件的签名是否合法。一个更根本的问题是固件本身怎么到达车里新版本的固件怎么安全、完整、不出错地写入每一片 Flash100 万辆分布全球的 ECU。200MB 的新固件。不可靠的蜂窝网络。零容错——一次失败也不能有。本篇小结今天我们做了一件事理解安全的根基不在任何一行代码中——而在硅晶片内一段被高压脉冲物理烧断的金属连线里。关键结论安全的信任根不在软件中——在硅的物理定律中HSM的OTP熔丝阵列中高压脉冲通过电迁移物理切断金属连线。这是热力学保证——不是数学保证。断电不丢20年数据保持。安全启动验证的不是代码写得对不对——而是代码有没有被篡改启动时HSM先于主CPU验签固件。信任链从OTP→HSM内部ROM→bootloader→app——环环相扣任何一环断裂则整条信任崩塌。安全是对驾驶员的社会契约你坐进车里默认方向盘会响应你的手、刹车会响应你的脚。这个基本假设不是天然成立的——它是被HSM、安全启动、SecOC这套硬件安全架构保障的。下一节固件本身怎么安全到达车里Part 6最后一节——软件升级与存储。OTA是你对一百万永远不会被物理接触的ECU的最终承诺。【下集预告】你写的安全启动代码保护了 Flash 里的每一行固件。但固件会老——有 bug 需要修有功能需要升级。你需要在不拆车、不召回、不中断行驶的前提下把 200MB 的新固件推送到全球 100 万辆车上。OTA 是汽车电子工程的终极考验。它调用了你学过的所有知识通信、存储、安全、时序、状态机。所有知识在一个不可逆转地修改物理上永远无法接触的设备的场景下汇聚。下载固件的主体是一个 A/B 分区的状态机——A 区运行当前固件B 区接收新固件。如果 B 区升级失败A 区完好如初。但 A/B 分区的本质不只是一个软件设计模式——它的根源是 Flash 的物理铁律擦除必须整个扇区进行写入只能把 1 翻成 0擦除比编程慢一千倍。下一节——Part 6 最后一节。软件升级与存储。这是你对一百万永远不会被物理接触的 ECU 的最终责任。