硬件信任根:HSM与TPM的区别

说到硬件信任根,很多刚接触安全启动的朋友都会问:HSM和TPM到底有啥区别?我当年也困惑过。说白了,它们都是硬件安全模块,但定位完全不同。

HSM是什么?

HSM,全称是硬件安全模块。你可以把它想象成一个保险箱。这个保险箱专门用来存放密钥,执行加密运算。它有自己的处理器,自己的内存,甚至有自己的操作系统。

我在项目中遇到过一种情况:客户要求密钥必须物理隔离,不能给CPU任何接触的机会。这时候HSM就是唯一的选择。它独立于主系统运行,就算主芯片被攻破了,密钥依然安全。

HSM的核心特点:

  • 物理隔离:独立芯片,独立总线
  • 高性能:专用硬件加速加密运算
  • 高安全性:通过FIPS 140-2 Level 3以上认证
  • 价格昂贵:一个工业级HSM可能要几千美元

TPM又是什么?

TPM是可信平台模块。它更像一个安全协处理器,集成在主板上。TPM的定位是「够用就好」——提供基本的密钥存储、平台完整性度量、远程证明等功能。

你想想看,一台笔记本电脑需要HSM吗?没必要。TPM就足够了。它成本低,体积小,功耗低。我建议做消费级产品时优先考虑TPM。

对比项 HSM TPM
成本 高(几百到几万美元) 低(几美元到几十美元)
性能 高(专用硬件加速) 中等(通用协处理器)
密钥存储量 大(可扩展) 有限(通常几十个密钥槽)
典型应用 服务器、金融、工业控制 PC、物联网设备、嵌入式系统
认证标准 FIPS 140-2/3, Common Criteria TCG TPM 2.0 规范

我的经验之谈:如果产品量产后需要远程更新固件,TPM是性价比最高的选择。但如果产品涉及金融交易或关键基础设施,别省那点钱,上HSM。

Zephyr支持的硬件安全模块

Zephyr RTOS对硬件安全模块的支持相当全面。我整理了一下,主要分三类:

内置安全单元

很多MCU厂商在芯片内部集成了安全硬件。比如NXP的LPC系列有安全子系统的,ST的STM32系列有CRYP和HASH外设。Zephyr通过驱动层直接调用这些硬件。

// Zephyr中配置硬件加密的示例
// prj.conf
CONFIG_CRYPTO=y
CONFIG_CRYPTO_STM32=y
CONFIG_CRYPTO_INIT_PRIORITY=50

// 代码中调用
#include <crypto/crypto.h>

struct crypto_dtls_ctx ctx;
crypto_dtls_init(&ctx, DEVICE_DT_GET(DT_NODELABEL(crypto)));

外部安全芯片

这类芯片通过SPI或I2C与主MCU通信。常见的包括:

  • ATECC608A:Microchip的加密协处理器,支持ECDH、SHA-256
  • SLB9670:Infineon的TPM 2.0芯片
  • OPTIGA Trust M:英飞凌的安全芯片系列

我记得有一次调试ATECC608A,发现SPI时钟频率太高会导致通信不稳定。后来把时钟降到1MHz以下就正常了。嗯,这里要注意:安全芯片对时序要求比较严格。

软件模拟的安全模块

Zephyr也支持纯软件实现的安全功能。比如mbedTLS库,可以在没有硬件加速的情况下完成加密运算。当然,性能会差一些。

警告:软件实现的密钥存储是不安全的。密钥会暴露在内存中,容易被调试工具读取。生产环境中务必使用硬件安全模块。

MCUboot的硬件抽象层

MCUboot是Zephyr默认的启动加载器。它设计了一个精巧的硬件抽象层,让开发者可以轻松对接不同的安全硬件。

HAL的结构

MCUboot的HAL位于 boot/bootutil/src/hw_abstract.c。它定义了一组标准接口:

// MCUboot硬件抽象层核心接口
int boot_hal_init(void);
int boot_hal_verify_signature(const uint8_t *hash, 
                              uint32_t hash_len,
                              const uint8_t *signature,
                              uint32_t sig_len,
                              const uint8_t *pub_key);
int boot_hal_generate_random(uint8_t *buf, uint32_t len);
int boot_hal_secure_store(uint32_t key_id, 
                          const uint8_t *data, 
                          uint32_t len);
int boot_hal_secure_load(uint32_t key_id, 
                         uint8_t *data, 
                         uint32_t len);

说白了,你只需要实现这几个函数,MCUboot就能用你的安全硬件了。我建议先实现 boot_hal_verify_signature,这是最核心的——固件签名验证全靠它。

实际对接案例

以对接ATECC608A为例,看看具体怎么做:

// 自定义硬件抽象实现
#include "bootutil/hw_abstract.h"
#include "atecc608a.h"

int boot_hal_init(void) {
    // 初始化ATECC608A
    return atecc608a_init();
}

int boot_hal_verify_signature(const uint8_t *hash, 
                              uint32_t hash_len,
                              const uint8_t *signature,
                              uint32_t sig_len,
                              const uint8_t *pub_key) {
    // 使用ATECC608A验证ECDSA签名
    // 注意:ATECC608A内部存储公钥,不需要传入
    return atecc608a_verify(hash, hash_len, signature, sig_len);
}

避坑指南:我曾经在对接TPM时犯过一个错误——没有处理TPM的NV索引冲突。TPM的NV存储空间有限,如果多个应用共用同一个索引,会导致数据覆盖。建议为每个密钥分配独立的NV索引,并在文档中记录清楚。

性能考量

不同硬件模块的签名验证速度差异很大。我测试过几款:

  • ATECC608A:ECDSA验证约50ms
  • SLB9670 TPM:RSA 2048验证约200ms
  • 软件mbedTLS:ECDSA验证约500ms(Cortex-M4 @ 168MHz)

你想想看,如果每次启动都要等500ms,用户体验会很差。所以我建议优先使用硬件加速的方案。特别是对于需要快速启动的场景,比如汽车电子、工业控制器。

最后说一句:MCUboot的HAL设计得很灵活。你可以根据产品需求,在编译时选择不同的后端。比如开发阶段用软件模拟,量产时切换到硬件安全模块。这种设计思路值得学习。