CPA侧信道分析实战:泄漏模型选择、波形对齐与攻击验证指南

发布时间:2026/10/10 5:19:16
CPA侧信道分析实战:泄漏模型选择、波形对齐与攻击验证指南 先说个有点丢人的事。我头一回在自己搭的功耗采集平台上跑Correlation Power AnalysisCPA用的是 AES-128 第一个 S 盒输出的汉明重量做泄漏模型跑完相关性矩阵后最大相关系数对应的密钥字节居然和真实密钥差了整整 0x11。当时我第一反应是示波器触发没调好、波形没对齐折腾了两天采集参数结果问题根本不在采集端——是我对“泄漏模型”这四个字的理解太浅了。这个经历让我意识到一个关键事实CPA 这个工具虽然叫“相关性能量分析”但它真正的灵魂是后面那个 LeakageModel。你选的模型假设芯片在哪里泄漏、以什么方式泄漏直接决定了相关性计算是在帮你找工作还是在帮你找幻觉。这篇文章我就把自己从原理到实战、从踩坑到收尾的整套经验梳理出来写给正准备在嵌入式安全、硬件安全方向入门侧信道分析的朋友也写给那些已经能跑通脚本、但总觉得攻击结果不够稳定的同行。1. 为什么说CPA的成败一半押在泄漏模型上1.1 CPA不是“测功耗猜密钥”而是“用一个假设去匹配现实”很多人第一次接触 CPA 时容易把它理解成“采集很多功耗曲线然后用统计学方法把密钥挖出来”。这个说法不算错但它少了一个最关键的中间环节——你拿什么去和功耗曲线做匹配CPA 的实际工作方式是这样的你先猜一个密钥字节然后根据密码算法流程算出某个中间值比如 AES 第一轮 S 盒的输出再通过一个泄漏模型把这个中间值映射成“芯片在这个时刻应该消耗多少功耗”的预测值。接下来你把这条预测的功耗值和实测的功耗曲线做相关性计算。如果猜的密钥是对的预测值和实测值在某个时间点上会表现出明显的线性相关如果猜错了相关性就会像噪声一样散开。换句话说CPA 不是“看功耗猜密钥”而是“用一个假设功耗去匹配实测功耗”。这个“假设功耗”就是泄漏模型的输出。它决定了你和实测数据之间建立的是哪一座桥。1.2 模型选错时相关系数照样能出峰值——但是错的峰值这里必须说一个最容易让人困惑的现象即使泄漏模型完全不对相关性计算也能跑出峰值来。我当时第一次跑失败并不是因为相关性矩阵里没有尖峰恰恰相反尖峰非常明显只是它指向的密钥是错的。问题出在模型和真实泄漏模式之间的错位。假设芯片实际泄漏的是寄存器翻转的功耗Hamming Distance你却用了某个中间值的汉明重量Hamming Weight做模型那么在某些密钥候选下模型预测值和实测功耗之间也会产生一定的“伪相关”——这种伪相关有时候甚至比真实相关更强因为它恰好和噪声模式叠在了一起。所以请你务必记住相关系数有峰不等于模型对了。模型与真实泄漏机制之间的匹配程度才是正确密钥能被挑出来的真正原因。1.3 先理解最简单的泄漏模型汉明重量与S盒输出在讨论复杂模型之前先把最简单的跑通。以 AES-128 第一轮为例加密时会把明文分组和轮密钥做异或然后查 S 盒中间值 Sbox[plaintext[i] ^ guess_key]这个中间值是一个字节范围从 0 到 255。一个非常经典的泄漏假设是芯片在计算这个中间值时总线或者寄存器上“1”的个数越多功耗就越高。这就是Hamming Weight 模型它把中间值映射为功耗预测 popcount(中间值)这个模型很简单但它确实在很多实际芯片上有效因为 CMOS 电路在翻转时消耗的动态功耗和输出节点的电容充电次数密切相关而节点上的电平状态往往会和数据的汉明重量挂钩。我第一次跑通 CPA用的就是它。只是后来我才知道这个模型不是万能的具体的适用边界放到第 4 节细讲。2. 相关性的数学直觉怎么从一堆噪声里认出正确密钥2.1 皮尔逊相关系数公式与直觉理解CPA 核心的数学工具是皮尔逊相关系数。对某个猜测密钥 g我们在每个采样时间点 t 上计算预测功耗向量和实测功耗向量之间的相关系数r(g, t) Σ[(h_i - h̄)(p_i,t - p̄_t)] / sqrt(Σ(h_i - h̄)² · Σ(p_i,t - p̄_t)²)其中 h_i 是第 i 条明文对应的模型预测功耗值p_i,t 是第 i 条功耗曲线在时间点 t 的实际采样值h̄ 和 p̄_t 分别是预测值和实测值在 i 维上的平均。直觉理解就是如果猜测密钥正确那么实测功耗里应该有一部分变化趋势和模型预测值同步如果猜测密钥错误模型预测值和实测功耗之间的变化就没有稳定关系。相关系数衡量的正是这种“同向变化的强度”。它的取值在 -1 到 1 之间绝对值越接近 1说明线性关系越强。2.2 为什么用相关系数而不是直接看平均功耗差在 CPA 出现之前更早的 DPA差分功耗分析用的是更简单的办法把功耗曲线按某个中间值的比特位分成两组然后算两组平均功耗的差。如果猜测密钥正确这个差值曲线在泄漏时间点会有一个明显的尖峰。CPA 选择相关系数而不是平均差核心原因是它能归一化掉功耗幅度的绝对大小对结果的影响。不同芯片、不同采集通道的功耗幅度差异可能很大直接看功耗差值阈值很难定但相关系数天然落在 -1 到 1 的范围内不同实验之间、不同密钥候选之间更具可比性。另一个原因是相关系数对线性关系的刻画更精细在噪声较大时能提供更稳健的排序结果。2.3 多重假设检验相关系数矩阵怎么看假设我们要攻击 AES-128 的第一个密钥字节那么需要猜测 256 个候选值。对每个候选值都会得到一条“相关系数随时间变化”的曲线。把这 256 条曲线叠在一起看会得到一个二维结构横轴是时间点纵轴是密钥候选值颜色深浅代表相关系数大小。正确密钥的特征非常独特它会在某个时间点对应泄漏发生的时刻出现一个明显高于其他所有候选的孤立尖峰。而错误密钥的相关性曲线整体应该贴近零偶尔有一些小波动但不会形成稳定的大尖峰。这里有个实用的判据算一下“正确候选的峰值”和“所有候选峰值分布的标准差”之间的比值。我用下来比较舒服的经验是当这个比值超过 5 时基本可以确信密钥恢复成功如果只有 2 到 3那多半是数据集有问题或者模型选得不对。3. 波形采集与对齐模型再好喂进去的数据不对也白搭3.1 采样率、触发信号与示波器设置很多人第一次搭台子时把精力全放在写 Python 脚本上忽略了采集端那些“看起来不重要”的设置结果模型再漂亮也跑不出结果。我建议按下面的基准来起步采样率至少保证每个时钟周期能采到 5 到 10 个点。AES 硬件加速模块的时钟通常在几十到几百 MHz如果示波器采样率只有 100 MS/s那基本是看不出精细泄漏点的。起步时用 500 MS/s 到 1 GS/s 比较稳。带宽示波器带宽建议不低于采样率的四分之一否则高频功耗毛刺会被滤掉。触发最好能触发到加密开始的那个时刻。比如目标板在加密前拉高某根 GPIO示波器就用这根 GPIO 的上升沿做触发。触发不稳定的话波形在时间轴上会错位相关性直接摊平。通道测功耗用的是电流探头或近场探头注意把探头放在芯片电源引脚附近尽量避免探到板子上其他无关电路的活动。3.2 trace对齐触发漂移是最大敌人如果你发现相关系数尖峰不明显、或者同一批数据跑几次结果飘忽不定优先怀疑触发漂移。示波器触发本身有抖动再加上加密算法在开始前可能有一段随机的等待时间就会导致每条功耗曲线在时间轴上出现几纳秒到几十纳秒的偏移。处理方式有几种硬件对齐修改目标板固件在加密开始前输出一个极窄同步脉冲使用这个脉冲做触发。软件重对齐如果触发点不够准确可以先跑一次互相关算法把每条曲线都平移到与某一参考曲线最对齐的位置。重采样插值对整数倍时钟周期的偏移可以用重采样修正但注意不要过度插值引入假的相关性。我最常用的还是第一种硬件上给同步脉冲这是根治问题的方法。软件对齐可以作为辅助但不要指望它把硬件问题完全弥补。3.3 预处理降噪、滤波与降采样预处理这块容易走两个极端什么都不做或者什么都做。什么都不做的风险是噪声太大相关系数的峰值被拉低需要更多曲线才能恢复密钥。什么都做的风险是滤波器把泄漏信号本身也抹掉了峰值变得平坦。我建议的预处理顺序是先做对齐检查只保留偏移量在可接受范围内的曲线。再做均值滤波窗口不超过几个采样点用来去掉高频开关噪声。降采样到每个时钟周期 1 到 2 个点这样计算量大幅下降而对 CPA 来说精度损失很小因为相关性主要靠泄漏时刻的总体趋势不是靠采样点个数。如果曲线有可见的交流电源干扰可以做一次带阻滤波但要先确认这个干扰频率和泄漏信号频段没有重叠。有一点要特别注意不要在预处理阶段对曲线做任何非线性的操作比如硬限幅、削顶、逐点归一化后再归一化。非线性变换可能人为制造相关性或破坏本来存在的相关性结果很容易误导判断。4. 汉明重量还是汉明距离教你选对泄漏模型的判断准则4.1 Hamming Weight模型的适用条件Hamming Weight 模型假设功耗与“当前总线/寄存器上 1 的个数”相关。在什么条件下它最贴近现实一种典型场景是芯片使用预充电总线结构或者内存单元在读取数据时位线上的放电量和数据中“1”的个数成正比。另一个常见场景是某些 RISC 微控制器的 GPIO 翻转行为端口输出驱动能力较强时打开的输出引脚越多、动态功耗越大。我自己的经验是如果你不确定芯片内部结构先用 Hamming Weight 模型做一个快速侦察。实现简单、计算量小如果跑完能看到明显的孤立尖峰那说明泄漏模式确实与数据值相关只是具体机制还需要细化如果完全看不到尖峰那再往 Hamming Distance 方向想。4.2 Hamming Distance模型寄存器跳变才是功耗来源Hamming Distance 模型的假设是功耗与“某个寄存器或总线上的状态从一个时钟周期到下一个时钟周期的翻转位数”成正比。用公式表示就是HD(旧状态, 新状态) popcount(旧状态 XOR 新状态)这个模型比 Hamming Weight 更贴近大多数 CMOS 数字电路的实际物理过程因为动态功耗主要来自节点电容的充放电而充放电是由电平翻转引起的。也就是说一个始终保持 0xFF 的寄存器用 Hamming Weight 模型看功耗可能很大但它实际可能根本不消耗多少动态功耗因为它一直没翻转。所以如果你的目标芯片是典型的同步数字电路Hamming Distance 模型通常比 Hamming Weight 模型更容易成功。代价是需要知道中间值的“旧状态”是什么。在 AES 第一轮场景里旧状态可以是 S 盒输出发生前的寄存器值新状态可以看作 S 盒输出写入寄存器后的值如果中间值在“异或之后、查表之前”的位置旧状态则取决于上一轮或上一条数据的状态。4.3 多模型对比同时跑多个模型看分布既然模型选错的代价这么大一个非常实用的做法是一次攻击同时跑多个候选模型然后比较它们的相关系数分布。我在攻击一个未知固件的某国产 ARM 核 MCU 时遇到过一种情况Hamming Weight 模型跑出的最大相关系数只有 0.23而且排第二的候选密钥和它只差 0.01差一点点就误判而 Hamming Distance 模型跑出的最大相关系数是 0.41正确密钥的峰值明显孤立。那个项目之后我养成了一个不太费事但很有效的习惯第一轮先并行跑 4 到 5 个模型其中包括中间值字节的 Hamming Weight中间值寄存器跳变的 Hamming Distance中间值字节的某一比特位比如最低位中间值字节的倒码反向后 Hamming Weight观察哪个模型的正确候选峰值最孤立、最稳定就说明哪个模型的假设最贴近真实泄漏。这一步不费太多时间但能帮你排除很多“攻击失败到底是芯片不泄漏还是模型不对”的疑惑。4.4 不知道中间值在哪一层时怎么用手动搜索有时候你并不清楚芯片在哪个计算环节发生泄漏。比如一个安全的 AES 加速器可能内部把 AddRoundKey 和 SubBytes 合并成一个周期完成这时你单独用“异或后”的值做中间值就可能对不上。解法是做分层扫描对每个可能的时间点 d扫描多个中间值函数和预测模型计算它们与实测功耗的相关性。把每个候选在“最佳时间点”的相关系数画成一张热力图。这张图能直观看出哪个中间值层级的泄漏最高、发生在哪个时间位置。我在某款智能卡安全芯片上就用这个办法找到了与轮密钥第 9 轮加解密相关的强泄漏点而这在正常加密流程里已经离输入数据很远了。5. 四段翻车实录我踩过的CPA实战大坑5.1 采样率不够导致相关峰被抹平第一次正式做 CPA 时我用的示波器采样率只有 250 MS/s目标板工作时钟约 48 MHz理论上每个时钟周期能采 5 个点看起来够用。但实际跑下来相关系数峰值只有 0.18 左右且位置在时间轴上很模糊像被揉过一样。排查后发现问题在于目标芯片内部有较快的组合逻辑路径泄漏信号在时钟边沿后的几百皮秒内就完成了大部分翻转而 4 ns 的采样间隔把瞬态功耗毛刺平均掉了。后来我把采样率提高到 1 GS/s同样的曲线数量下相关系数峰值直接翻倍到了 0.4 以上。这个经验告诉我“每个时钟周期采几个点”只是底线对于快速逻辑电路采样点越多越能捕获到精细的泄漏窗口。5.2 触发位置漂移导致“伪失败”另一次翻车更有迷惑性模型和数据都没问题正确密钥候选的相关系数曲线确实在所有曲线中排第一但峰值的绝对值只有 0.12和错误密钥候选几乎拉不开差距。我一度怀疑是目标板的功耗输出太弱。后来我在软件里统计了每条曲线的触发点位置偏移发现偏移量的分布宽度竟然接近一个完整时钟周期。原因是硬件同步脉冲引脚旁边有一条数据线在加密启动时数据线上的活动会干扰同步脉冲的边沿时刻判断。解决办法很简单在固件里把同步脉冲宽度从原来的 100 ns 加宽到 500 ns同时把同步引脚的驱动能力配置调强。这样触发抖动从几百皮秒级降到几十皮秒级再次跑 CPA正确峰值一下子冲到了 0.52。别小看这个“波形能不能对齐”的问题它可能是你在实战中遇到的最隐蔽的失败原因。5.3 模型用反把最高相关给了一个错误密钥回到文章开头那个翻车事故当时我用 Hamming Weight 模型中间值选的是 S 盒输出跑出来的 256 条相关系数曲线里有一个错误密钥候选的峰值非常突出。后来我把中间值换成 AddRoundKey 之后的异或值再试了一次 Hamming Weight 模型正确密钥才真正成为最高峰。原因复盘起来很清晰那个芯片的功耗泄漏主要发生在 AddRoundKey 到 S 盒输入的组合逻辑输出上而我对“S 盒输出”建模型时错误密钥在特定明文分布下也能与实测功耗形成虚假相关。错误模型加上有限样本量就可能在错误候选上造出一个足够大的尖峰。从那之后我再也不拿某一次攻击结果当结论至少要用两组不同明文集合交叉验证确认正确密钥在两个集合下的相关峰都出现才算真正成功。5.4 只盯着最大值被偶然伪峰骗了还有一种很难察觉的坑正确密钥候选的相关系数确实排第一但第二名的峰值也非常接近且出现在不同的时间点。如果你只报告“最高相关系数对应哪个密钥”就可能忽略这种不确定性。我的处理方法是报告“Top 5 候选密钥及其峰值位置”然后观察正确密钥的峰值位置是否在所有候选里具有物理一致性。正确密钥的尖峰往往出现在一个稳定、可解释的时间点比如 S 盒输出后的第一个采样窗口错误密钥的伪峰则经常在时间轴上到处飘。这个判断维度比单纯比数值更可靠。6. 攻击收尾如何确认密钥没有白猜以及还能往哪走6.1 用第二密钥字节交叉验证当第一个密钥字节看起来已经被恢复后很多人会立刻把结果写进报告。我劝你先停下来做一次交叉验证。做法很简单用同样一组功耗曲线把目标从第一个 S 盒输出改成第二个 S 盒输出重复一次 CPA。如果第二个字节也能恢复而且它的峰值时间点和第一字节的峰值时间点在算法流程上逻辑自洽那这次攻击结果才经得起复现。如果第二个字节失败一种常见原因是两条 S 盒查表路径的泄漏位置非常接近相关性计算互相干扰。这时可以尝试对时间窗做裁剪只保留第一条路径泄漏附近的时间段去做第二字节攻击。交叉验证的作用不只是确认密钥也是帮你校准采集系统的可靠度。6.2 从一条曲线到一个结论trace数量与成功率曲线另一个值得画出来的东西是成功率曲线横轴是用于攻击的功耗曲线数量纵轴是“正确密钥候选排第一”的比例。做法是把同一批数据分成若干份比如 5 条一组、10 条一组、20 条一组每组独立跑 100 次 CPA统计正确密钥排第一的频率。这条曲线非常有价值。它能告诉你实际攻击时最少需要采集多少条曲线也成为评估泄露强度的量化指标。举个例子如果 50 条曲线就能达到 95% 成功率那说明该设备对 CPA 非常脆弱如果到了 10000 条曲线成功率仍然只有 40%那要么是你选错了泄漏模型要么是采集质量达不到要求。很多论文里用相关系数随 trace 数量的收敛速度来衡量抗侧信道能力原理就在这里。6.3 从CPA出发还能往哪走模板攻击与互信息分析CPA 跑通之后你有两条自然的进阶方向。一条是模板攻击。CPA 用的是统计线性相关但如果芯片的泄漏是非线性的比如某些工艺节点下功耗与数据值呈抛物线关系CPA 的线性假设就不够用了。模板攻击会对每个中间值类别的功耗分布做精细建模可以用更少的曲线达到更高的成功率代价是需要一个可以完全控制密钥的训练阶段。另一条是互信息分析。互信息不假设线性关系能捕捉到更宽泛的统计依赖。在 CPA 相关性很弱但真实泄漏存在的情况下互信息分析往往还能看到一些希望。我个人的经验是先把 CPA 用好、用透再用互信息做交叉验证能大幅降低误判率。最后想分享一个小的实操习惯每次攻击前把采集参数、模型类型、trace 数量、相关系数峰值、候选密钥排序全部存成一个 JSON 元数据文件。别靠脑内记忆人一旦连续采集几组数据很容易弄混哪条是哪个模型跑的。发生过太多次“这个尖峰好像昨天见过但记不清是哪个配置下跑出来的”这种窘境。把这些细节记录下来复盘时会轻松得多——项目的实际推进速度往往就快在这些不起眼的日常规范里。