3. 密钥生成在受限设备上的实现
在低功耗 MCU 上生成密钥,难点并不在“调用一个随机数函数”,而在于:芯片是否真的具备不可预测的熵源、熵源在宽温与低压下是否仍然可用、种子如何被安全保存,以及最终产物能否满足合规与认证要求。本节围绕 TRNG 与 PRNG 的设计约束、熵源质量评估、种子管理和一个基于硬件熵源的密钥生成实战任务展开。
TRNG 与 PRNG 在 MCU 上的分工
真随机数发生器(TRNG)从物理噪声中提取熵,常见熵源包括环形振荡器抖动、RC 充放电噪声、热噪声放大器和亚稳态触发器。它的输出不可复现,但速率低、功耗高、易受温度与电压影响,且往往需要后处理(如 Von Neumann 去偏、SHA-256 条件化)才能得到接近均匀的比特流。伪随机数发生器(PRNG)则是确定性算法,如 CTR_DRBG(AES-128)、HMAC_DRBG 或 ChaCha20,速率高、可复现,但安全性完全依赖种子熵与内部状态保密。
因此工程上的正确做法是:TRNG 只负责产生种子,PRNG 负责扩展密钥流。MCU 上电后先采集 TRNG 熵,条件化后作为 DRBG 的种子,再由 DRBG 输出 AES 密钥、ECC 私钥或会话密钥。这样既避免长期占用高功耗熵源,又保证密钥质量。
熵源质量评估:不能只看“有没有”
评估熵源质量,核心指标是最小熵(min-entropy),而不是香农熵。最小熵按最可能出现的取值计算,直接决定种子强度。工程上常用 NIST SP 800-90B 的评估方法:对原始比特流做 IID 检验,若通过则用频率估计最小熵;若不通过,则用非 IID 估计器(如 Markov、压缩、碰撞、最长重复子串等)取最小值。
一个典型教学示例:某 MCU 的环形振荡器在 25°C、3.3V 下采集 4096 bit 原始输出,经频率测试得到 P(1)=0.51,则单比特最小熵约为 -log2(0.51)≈0.971 bit,4096 bit 总最小熵约 3977 bit。但若在 -40°C 或 1.8V 下 P(1) 漂移到 0.65,单比特最小熵降至约 0.622 bit,总熵仅约 2548 bit。这说明熵源必须做全温区、全电压域评估,并保留足够裕量。若评估不通过,应增加采集量、启用多源 XOR 混合,或改用外部安全芯片。
种子管理与合规要求
种子是 DRBG 的“根”,一旦泄露或可预测,后续所有密钥都不安全。种子管理要满足三点:
- 加密存储:种子写入非易失存储前用设备唯一密钥(如 PUF 派生密钥)做 AES-GCM 封装,防止读出。
- 完整性保护:GCM 标签或 HMAC 校验种子未被篡改;配合单调计数器防止回滚到旧种子。
- 生命周期更新:每次上电重新采集 TRNG 熵并 reseed;长期运行设备应设置最大输出次数或时间间隔强制重播种。
合规方面,NIST SP 800-90A 规定 DRBG 的种子长度、reseed 间隔和预测抵抗要求;SP 800-90B 规定熵源评估与健康测试;FIPS 140-3 则要求 TRNG 具备上电自检、连续健康测试和故障响应。对受限设备而言,还要关注 AIS-31 或国密随机数检测规范中的适用条款。
实战任务:基于硬件熵源生成 AES-128 密钥
下面以一款带硬件 TRNG 的低功耗 MCU(示例型号以“MCU-X”代称)为例,完成一次密钥生成。目标:采集 256 bit 原始熵,条件化后得到 128 bit 种子,经 CTR_DRBG 输出 128 bit AES 密钥,并加密存入 Key Store。
第一步,配置 TRNG。使能环形振荡器熵源,设置采样时钟为 1 MHz,采集 256 bit 原始数据。代码示意如下:
// 示例:MCU-X TRNG 采集 256 bit 原始熵
TRNG->CR = TRNG_CR_EN | TRNG_CR_RNGEN;
TRNG->CFG = TRNG_CFG_SRC_RING | TRNG_CFG_DIV_1M;
uint8_t raw[32];
for (int i = 0; i < 32; i++) {
while (!(TRNG->SR & TRNG_SR_DRDY));
raw[i] = TRNG->DR;
}
// 健康测试:连续 1/0 计数、重复计数
if (!trng_health_check(raw, 32)) {
trng_fault_handler(); // 触发故障响应
}
第二步,条件化。对 256 bit 原始数据做 SHA-256,取前 128 bit 作为种子。SHA-256 的条件化作用是把有偏输入压缩为接近均匀的输出,同时保留最小熵。示例中若原始最小熵为 200 bit,则 128 bit 种子仍有足够裕量。
// 示例:SHA-256 条件化,取前 128 bit 作为种子
uint8_t seed[16];
sha256_context ctx;
sha256_init(&ctx);
sha256_update(&ctx, raw, 32);
sha256_final(&ctx, digest); // digest 为 32 字节
memcpy(seed, digest, 16); // 取前 128 bit
第三步,初始化 CTR_DRBG 并生成密钥。CTR_DRBG 使用 AES-128,种子长度 128 bit,输出 128 bit AES 密钥。生成后立即用设备唯一密钥封装并写入 Key Store。
// 示例:CTR_DRBG 生成 AES-128 密钥
ctr_drbg_ctx drbg;
ctr_drbg_init(&drbg, seed, 16, NULL, 0);
uint8_t aes_key[16];
ctr_drbg_generate(&drbg, aes_key, 16, NULL, 0);
// 加密存储:AES-GCM 封装
key_store_write(KEY_ID_AES, aes_key, 16);
第四步,验证与判断。生成后应检查:TRNG 健康测试是否通过、种子最小熵是否达标、DRBG 输出是否可重复(同一 seed 应得到同一密钥,用于自检)、Key Store 写入后读回校验是否一致。若任一环节失败,应清除中间变量并重新采集熵,而不是继续使用可疑密钥。
| 检查项 | 示例判据 | 不通过时的处理 |
|---|---|---|
| TRNG 健康测试 | 连续 1/0 计数 < 32,重复计数 < 4 | 触发故障响应,重新采集 |
| 种子最小熵 | ≥ 128 bit | 增加采集量或启用多源混合 |
| DRBG 自检 | 同 seed 输出一致 | 检查 DRBG 实现与种子长度 |
| Key Store 回读 | 与写入值一致 | 检查封装密钥与存储完整性 |
综上,受限设备上的密钥生成不是单点操作,而是一条从物理熵源到密钥产物的完整链路。TRNG 提供不可预测性,条件化保证均匀性,DRBG 提供效率,种子管理保证持久安全,合规要求则约束整条链路的可验证性。只有把每一环都做实,密钥才真正可信。