4. 安全存储与加密引擎:把秘密锁进保险柜

安全存储和加密引擎,说白了就是给芯片里的敏感数据建一个保险柜。我做了这么多年硬件安全,见过太多「裸奔」的设计——密钥明文放在寄存器里,加密引擎跑起来连个侧信道防护都没有。嗯,今天咱们就把这块聊透。

4.1 安全存储架构

安全存储不只是「存数据」,它要解决三个核心问题:机密性(别人读不到)、完整性(别人改不了)、可用性(掉电不丢)。

我个人习惯把安全存储分成三个层级:

层级 存储介质 典型用途 安全要求
L0 — 片上易失 SRAM / 寄存器 会话密钥、临时明文 防探测、掉电自毁
L1 — 片上非易失 eFuse / OTP / Flash 根密钥、芯片身份 防篡改、防逆向
L2 — 片外加密存储 外部Flash / DDR 用户数据、固件镜像 加密+完整性校验

你想想看,根密钥放在eFuse里,但eFuse的读取接口如果直接暴露在总线上,那跟把钥匙挂在门口有什么区别?

⚠️ 我曾经踩过的坑: 某款芯片的OTP读取接口没有做访问权限控制,攻击者通过JTAG直接读出了根密钥。从那以后,我所有设计的OTP接口都加了「一次性熔断」机制——读一次就锁死,或者必须经过安全CPU授权。

4.2 安全存储的关键机制

  • 访问控制:谁可以读?谁可以写?必须用硬件状态机或安全CPU管控。
  • 防侧信道:存储阵列的功耗、电磁辐射要均匀化,别让攻击者通过功耗曲线猜出数据。
  • 错误检测:SRAM/Flash要用ECC,防止单比特翻转导致密钥出错。
  • 安全擦除:密钥用完必须物理清零,不能只清指针。
💡 我的设计习惯: 安全存储区域和普通存储区域物理隔离,走独立的电源域和时钟域。这样即使普通区域被攻破,安全区域还能守住最后一道防线。

4.3 AES加密引擎设计

AES是硬件加密的标配。但「跑得起来」和「跑得安全」是两码事。我见过不少设计,AES引擎跑得飞快,但密钥直接明文加载到Sbox里——这不等于白加密吗?

4.3.1 架构选择

架构 面积 吞吐率 典型场景
迭代架构(1轮/周期) 小 中 IoT、低功耗
流水线架构(10级) 大 高 网络加速、服务器
复合架构(4轮/周期) 中 中高 移动设备

我个人偏好迭代架构,因为它面积小、容易做防护。你想想看,流水线架构里每一级都有中间状态,侧信道攻击面反而更大。

4.3.2 侧信道防护

这是AES设计的重头戏。攻击者不需要知道算法细节,只需要采集功耗曲线就能把密钥猜出来。我常用的防护手段:

  • 掩码(Masking):每个中间值都加随机掩码,让功耗与真实数据无关。
  • 隐藏(Hiding):用恒定功耗逻辑(如双轨预充电)让每条指令的功耗都一样。
  • 随机延时插入:在加密过程中随机插入空操作,打乱时序对齐。
🔑 关键经验: 掩码方案一定要在RTL级就做验证。我曾经遇到一个设计,综合后掩码逻辑被优化掉了——因为综合工具觉得「那个信号没用」。从那以后,我所有安全相关的RTL都加了综合约束,禁止优化。

4.3.3 代码示例:AES-128迭代架构核心状态机

// AES-128 迭代架构状态机(简化版)
typedef enum {
    IDLE,
    LOAD_KEY,
    ADD_ROUND_KEY,
    SUB_BYTES,
    SHIFT_ROWS,
    MIX_COLUMNS,
    FINAL_ROUND,
    DONE
} aes_state_t;

always_ff @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        state <= IDLE;
        round <= 4'b0;
    end else begin
        case (state)
            IDLE: if (start) state <= LOAD_KEY;
            LOAD_KEY: state <= ADD_ROUND_KEY;
            ADD_ROUND_KEY: begin
                if (round == 4'd10) state <= DONE;
                else if (round == 4'd0) state <= SUB_BYTES;
                else state <= SUB_BYTES;
            end
            SUB_BYTES: state <= SHIFT_ROWS;
            SHIFT_ROWS: begin
                if (round == 4'd10) state <= ADD_ROUND_KEY;
                else state <= MIX_COLUMNS;
            end
            MIX_COLUMNS: begin
                round <= round + 1;
                state <= ADD_ROUND_KEY;
            end
            DONE: state <= IDLE;
        endcase
    end
end
💡 注意: 实际设计中,密钥扩展(Key Expansion)需要单独的状态机,而且密钥加载路径必须用掩码保护。上面的代码只是核心轮函数的状态控制。

4.4 密钥管理策略

密钥管理是整个安全体系的「七寸」。算法再强,密钥泄露了全白搭。我总结了一套「三不原则」:

  • 不存明文:密钥在非易失存储里必须加密存储。
  • 不传明文:密钥在总线上传输必须加密或加扰。
  • 不重用:每个设备、每次会话用不同的密钥。

4.4.1 密钥层级

我习惯用三层密钥体系:

层级 名称 存储位置 生命周期
L0 根密钥(Root Key) eFuse / OTP 芯片全生命周期
L1 设备密钥(Device Key) 片上Flash(加密存储) 每次启动派生
L2 会话密钥(Session Key) 片上SRAM 单次会话

根密钥由芯片厂商在产线写入,设备密钥由根密钥派生,会话密钥每次通信重新协商。这样即使会话密钥泄露,也影响不了根密钥。

4.4.2 密钥派生流程

  1. 芯片上电,安全CPU从eFuse读取根密钥(只读一次,读后熔断)。
  2. 用根密钥解密Flash中的设备密钥密文,得到设备密钥明文。
  3. 设备密钥与随机数(从TRNG获取)做KDF,生成会话密钥。
  4. 会话密钥加载到AES引擎的密钥寄存器,使用完毕后立即清零。
⚠️ 我曾经踩过的坑: 某次设计里,密钥派生用的随机数来自伪随机数生成器(PRNG),种子竟然写死在代码里。结果所有设备的会话密钥都一样。从那以后,我坚持所有随机数必须来自真随机数生成器(TRNG),而且每次上电都要重新校准。

4.5 安全存储与加密引擎的集成架构

下面这张图是我常用的集成架构,你可以看到各个模块怎么配合:

安全存储与加密引擎集成架构 安全存储与加密引擎集成架构 安全CPU 安全总线(加密+完整性保护) eFuse / OTP 根密钥存储 AES加密引擎 掩码+隐藏防护 安全SRAM 会话密钥+临时数据 TRNG 说明: 1. 所有模块通过安全总线通信,数据加密传输。 2. 根密钥只在安全CPU内部使用,不暴露到总线上。

这个架构的核心思想是:所有敏感数据都在安全区域内流转。安全CPU是唯一能访问根密钥的模块,AES引擎只处理加密后的数据,安全SRAM里的会话密钥用完即焚。

4.6 失效率与可靠性考虑

安全存储和加密引擎的失效率,直接影响整个芯片的安全等级。我常用的评估方法:

  • FIT(Failures In Time):每10^9小时失效次数,安全存储要求<10 FIT。
  • DPA(Differential Power Analysis):侧信道攻击的成功率,要求<10^-6。
  • MTBF(Mean Time Between Failures):安全功能平均无故障时间,要求>10年。
📊 我的经验数据: 采用掩码+隐藏双重防护的AES引擎,DPA攻击成功率可以从10^-3降到10^-8。代价是面积增加约30%,功耗增加20%。这个trade-off在大多数场景下是值得的。

好了,安全存储和加密引擎的核心内容就这些。记住:安全不是加个锁,而是整个体系的设计。从存储介质的选择,到密钥的派生,再到加密引擎的防护,每一环都不能掉链子。