一、目标
李老板:什么叫白盒AES算法?
奋飞: 将密钥进行白盒化处理,融入到整个加密过程中,使密钥无法跟踪还原,保障密钥安全。简单的说,就是你可以明明白白的调试整个算法过程,怎么看都像是AES算法,但却是怎么也找不到密钥在哪里?
AES算法的介绍请参照
http://91fans.com.cn/post/ilikeaes/
DFA(Differential Fault Analysis) 的原理和算法推导过程,请参照文末的链接。
我们今天用一个源码实例来操作一下,还原白盒AES算法的密钥
二、步骤
构造缺陷数据
DFA攻击简单来说就是在倒数第一轮列混合和倒数第二轮列混合之间(在AES-128中也就是第8轮和第9轮之间,因为最后第10轮不做列混合),修改此时中间结果的一个字节,会导致最终密文和正确密文有4个字节的不同。通过多次的修改,得到多组错误的密文,然后通过正确密文和这些错误密文能够推算出第10轮的密钥(加密模式下),继而能推算出原始密钥。
所以实际应用中,就需要先找准列混合的函数位置,然后在他之前去插入缺陷数据。
今天我们主要走一遍DFA还原白盒密钥的流程,所以,我们找了一个AES的源码来做演示,这份源码的AES加密流程一目了然,最适合学习AES算法了。
我们先跑一遍源码,输出加密结果是
Output:
cypher: c8 e1 58 1f 08 6c 8b ac 01 b8 37 e1 65 9c 72 46
然后在最后一次做 aes_mix_columns 之前,插入缺陷数据
最后运行一次看结果
cypher: a2 e1 58 1f 08 6c 8b 0a 01 b8 75 e1 65 f6 72 46
和之前的正确结果相比,正好有四个字节不同,说明我们找的位置是对的。
有源码嘛,肯定能找对了。在实际样本分析中 如果 结果全部不同,说明时机太早了; 只有一个不同则说明时机太晚了。
然后依次改动 s的下标 s[0…15]
最后我们得到了一组正确的结果,和16组错误的结果
c8 e1 58 1f 08 6c 8b ac 01 b8 37 e1 65 9c 72 46
a2 e1 58 1f 08 6c 8b 0a 01 b8 75 e1 65 f6 72 46
d9 e1 58 1f 08 6c 8b 57 01 b8 05 e1 65 0d 72 46
9f e1 58 1f 08 6c 8b 96 01 b8 f5 e1 65 aa 72 46
43 e1 58 1f 08 6c 8b 6c 01 b8 e1 e1 65 4d 72 46
c8 09 58 1f 39 6c 8b ac 01 b8 37 ac 65 9c 33 46
c8 c1 58 1f b7 6c 8b ac 01 b8 37 a3 65 9c 39 64
c8 c0 58 1f 8b 6c 8b ac 01 b8 37 57 65 9c 11 46
c8 55 58 1f 76 6c 8b ac 01 b8 37 64 65 9c 68 46
c8 e1 72 1f 08 e7 8b ac 92 b8 37 e1 65 9c 72 c8
c8 e1 2f 1f 08 d1 8b ac e8 b8 37 e1 65 9c 72 01
95 a0 d2 e8 7f 09 85 bd 2e cd a6 b0 0d f7 72 ab
c8 e1 47 1f 08 9b 8b ac 81 b8 37 e1 65 9c 72 58
c8 e1 58 61 08 6c 31 ac 01 e2 37 e1 76 9c 72 46
c8 e1 58 6c 08 6c c0 ac 01 77 37 e1 2c 9c 72 46
c8 e1 58 2f 08 6c f8 ac 01 ee 37 e1 84 9c 72 46
c8 e1 58 97 08 6c 99 ac 01 66 37 e1 68 9c 72 46
phoenixAES 还原轮密钥
有了这17组数据,我们就可以把AES-128的第10轮的轮密钥给还原出来。 (AES-128会把原始密钥扩展成10组密钥)
with open('tracefile', 'wb') as t:
t.write("""
c8e1581f086c8bac01b837e1659c7246
a2e1581f086c8b0a01b875e165f67246
d9e1581f086c8b5701b805e1650d7246
9fe1581f086c8b9601b8f5e165aa7246
43e1581f086c8b6c01b8e1e1654d7246
c809581f396c8bac01b837ac659c3346
c8c1581fb76c8bac01b837a3659c3964
c8c0581f8b6c8bac01b83757659c1146
c855581f766c8bac01b83764659c6846
c8e1721f08e78bac92b837e1659c72c8
c8e12f1f08d18bace8b837e1659c7201
95a0d2e87f0985bd2ecda6b00df772ab
c8e1471f089b8bac81b837e1659c7258
c8e15861086c31ac01e237e1769c7246
c8e1586c086cc0ac017737e12c9c7246
c8e1582f086cf8ac01ee37e1849c7246
c8e15897086c99ac016637e1689c7246
""".encode('utf8'))
phoenixAES.crack_file('tracefile',verbose=0)
把正确密文和错误密文喂进去,第10轮的轮密钥就出来了。 数学就是这么神奇。
Last round key #N found:
13111D7FE3944A17F307A78B4D2B30C5
Stark 从任意一轮的AES-128轮密钥,来还原原始密钥
活还没干完,我们拿到的仅仅是第10轮的轮密钥,但是我们的最终目标是原始密钥。
https://github.com/SideChannelMarvels/Stark
Stark就是干这个的,编译好之后,传入参数, 轮密钥 和 轮数
./main 13111D7FE3944A17F307A78B4D2B30C5 10
K00: 000102030405060708090A0B0C0D0E0F
K01: D6AA74FDD2AF72FADAA678F1D6AB76FE
K02: B692CF0B643DBDF1BE9BC5006830B3FE
K03: B6FF744ED2C2C9BF6C590CBF0469BF41
K04: 47F7F7BC95353E03F96C32BCFD058DFD
K05: 3CAAA3E8A99F9DEB50F3AF57ADF622AA
K06: 5E390F7DF7A69296A7553DC10AA31F6B
K07: 14F9701AE35FE28C440ADF4D4EA9C026
K08: 47438735A41C65B9E016BAF4AEBF7AD2
K09: 549932D1F08557681093ED9CBE2C974E
K10: 13111D7FE3944A17F307A78B4D2B30C5
我爱死数学了,它真的把每一轮密钥都还原出来了,源码里面我们的原始key就是
BYTE bKey16[16] = {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f};
完事,收工,不要走开,仔细看总结。
三、总结
1、DFA的原理和数学推导请参考下列资料,还有白龙写的 白盒 AES 密码学系列 也非常棒。
https://bbs.pediy.com/thread-254042.htm
https://blog.quarkslab.com/differential-fault-analysis-on-white-box-aes-implementations.html
2、真实样本中,寻找插入缺陷数据的位置非常重要,能明显的看到加密经过了10次循环或者15次循环就比较简单。我就遇到一个样本,只要5次循环,他做了一些等价运算来合并了一些操作,这时候就要记口诀了 结果全部不同,说明时机太早了; 只有一个不同则说明时机太晚了
3、AES-128可以从一组轮密钥来还原原始密钥,AES-256就需要两组密钥了,AES-256下如何进行DFA攻击,我还没有验证过。
4、分析加密算法,最好找个清晰的源码实现,然后和样本里的逻辑相互对照。
美妙人生的关键在于你能迷上什么东西。