4. 物理不可克隆函数(PUF):芯片的“指纹”技术

各位工程师,咱们今天聊一个很有意思的话题——物理不可克隆函数,简称PUF。说白了,它就是给每颗芯片一个独一无二的“指纹”。你想想看,人的指纹没法复制,芯片的PUF也一样。我在做安全芯片项目时,第一次接触这个概念就觉得:嗯,这才是密钥存储该有的样子。

4.1 PUF的基本原理

PUF的核心思想很简单:利用芯片制造过程中不可避免的物理差异。每颗芯片在光刻、掺杂、金属布线时,都会产生微小的随机偏差。这些偏差虽然不影响功能,但足以产生独特的电学特性。

为什么会这样?因为半导体制造不是完美的。晶圆上的掺杂浓度、氧化层厚度、线宽,都有纳米级的波动。这些波动在宏观上可以忽略,但在微观层面,它们就是芯片的“基因”。

PUF的本质: 将物理实体的微观随机性,转化为可重复的、不可克隆的数字输出。

我个人习惯把PUF比作“芯片的DNA”。DNA由碱基对序列决定,PUF由物理结构决定。两者都满足三个关键特性:

  • 唯一性: 不同芯片的PUF响应不同
  • 不可克隆: 即使拿到设计图纸,也无法制造出相同的PUF
  • 不可预测: 无法通过数学模型预测PUF的输出

我在项目中遇到过一种情况:客户想用PUF做设备身份认证。他们担心PUF响应会随温度变化。确实,PUF有噪声,但通过纠错码和模糊提取器,可以稳定输出密钥。这个后面会细说。

4.2 基于SRAM的PUF

在所有PUF实现中,SRAM PUF是最常见的。为什么?因为几乎所有芯片都有SRAM,不需要额外硬件。你想想看,这多省钱。

SRAM PUF的原理基于上电时的初始状态。每个SRAM单元由两个交叉耦合的反相器组成。上电时,两个反相器会竞争,最终稳定在0或1。理论上,这个状态是随机的。但实际上,由于制造偏差,每个单元都有偏好——有的偏向0,有的偏向1。

经验之谈: 我建议在芯片上电后立即读取SRAM内容,不要等系统初始化。因为后续的读写操作会覆盖初始状态。我曾经踩过这个坑,后来在BootROM里加了专门的PUF采样流程。

SRAM PUF的工作流程大致如下:

  1. 上电: 给SRAM供电,等待稳定(通常几微秒)
  2. 采样: 读取整个SRAM阵列的初始值
  3. 提取: 从原始数据中提取稳定的PUF响应
  4. 纠错: 使用纠错码处理噪声位
  5. 生成: 通过模糊提取器生成固定长度的密钥

这里有个关键点:不是所有SRAM单元都稳定。有些单元的偏好很弱,会随环境变化。所以我们需要筛选出“强偏好”单元。我一般会做多次采样,只保留那些90%以上时间输出相同值的单元。

特性 SRAM PUF 其他PUF(如环形振荡器PUF)
硬件开销 极低(复用现有SRAM) 中等(需要专用电路)
稳定性 中等(受温度影响) 较高
唯一性 好 好
实现难度 低 中
适用场景 低成本设备、IoT 高安全要求场景

注意: SRAM PUF的响应会随温度变化。我曾经在-40°C到85°C的测试中发现,大约5-10%的位会翻转。所以必须使用纠错码,否则密钥会出错。

4.3 PUF在密钥生成中的应用

好了,现在我们知道PUF能生成唯一且不可克隆的响应。那怎么用它来生成密钥呢?

直接拿PUF响应当密钥?不行。因为PUF响应有噪声,今天和明天可能不一样。我们需要一个“模糊提取器”(Fuzzy Extractor)来稳定输出。

模糊提取器的工作原理分两步:

  • 生成阶段: 从PUF响应中提取密钥,同时生成辅助数据(Helper Data)
  • 重构阶段: 用新的PUF响应和辅助数据,重构出相同的密钥

辅助数据可以公开存储,因为它不泄露密钥信息。但要注意,辅助数据不能篡改,否则重构会失败。我一般把辅助数据存在芯片的OTP(一次性可编程)存储器里,或者用数字签名保护。

下面是一个简化的密钥生成流程:

// 伪代码:PUF密钥生成
// 生成阶段(在安全环境中执行)
puf_response = read_sram_puf()          // 读取PUF响应
key, helper_data = fuzzy_extractor.generate(puf_response)
store_helper_data(helper_data)          // 存储辅助数据
secure_store(key)                       // 安全存储密钥

// 重构阶段(每次上电时执行)
puf_response_new = read_sram_puf()      // 重新读取PUF响应
helper_data = load_helper_data()        // 加载辅助数据
key = fuzzy_extractor.reconstruct(puf_response_new, helper_data)
// 现在key和生成阶段相同,可以用于加解密

我在实际项目中用过这个方案。当时做一款安全MCU,需要存储设备私钥。传统做法是把私钥烧录到OTP里,但OTP容易被物理攻击读取。改用PUF后,私钥只在运行时存在,掉电即消失。攻击者即使拿到芯片,也读不到密钥。

核心优势: PUF生成的密钥不需要存储在非易失存储器中。密钥“活”在芯片的物理特性里,每次上电重新生成。这从根本上杜绝了密钥提取攻击。

当然,PUF不是万能的。它也有局限性:

  • 环境敏感性: 温度、电压变化会影响PUF响应
  • 老化效应: 芯片使用久了,PUF特性会缓慢变化
  • 纠错开销: 需要额外的纠错码和模糊提取器逻辑
  • 测试难度: 无法像传统存储器那样直接测试

但瑕不掩瑜。在IoT设备、安全芯片、FPGA等场景,PUF已经成为密钥存储的主流方案。我建议你在设计新项目时,优先考虑PUF。特别是那些需要防物理攻击、防克隆的场景。

最后,咱们用一张图来总结PUF的核心逻辑:

PUF密钥生成与重构流程 生成阶段(安全环境) 1. 读取SRAM PUF响应 2. 模糊提取器生成密钥 3. 存储辅助数据(Helper Data) 4. 密钥仅运行时存在 重构阶段(每次上电) 1. 重新读取PUF响应 2. 加载辅助数据 3. 模糊提取器重构密钥 4. 密钥与生成阶段一致 辅助数据传递 密钥不存储在非易失存储器中,每次上电从物理特性重新生成 攻击者无法通过读取存储器获取密钥,从根本上防物理攻击

这张图展示了PUF密钥的完整生命周期。生成阶段在安全环境中完成,重构阶段每次上电执行。辅助数据可以公开,但密钥只在运行时存在。这就是PUF最迷人的地方——密钥“活”在芯片里,而不是“存”在芯片里。