目录
概念
问题分析
差分密码分析
总结
参考文献
一.概念
差分密码分析研究首先被Eli Biham和Adi Shamir在1990年发表。其更早是由IBM在1974年提出,不过IBM将差分密码分析设为机密。差分分析方法是一种选择明文攻击,其基本思想时通过分析特定明文差分对结果密文差分的影响来获得可能性最大的密钥。它主要使用于攻击迭代密码体制。1980 年代后期,诶利·比哈姆和阿迪·萨莫尔发表了一系列针对多个块加密和哈希函数的攻击,包括对资料加密标准(DES)理论弱点的运用。因此,二者通常被认为是发现差分密码分析的元勋。比哈姆和萨莫尔表示,DES 在抗差分密码分析方面表现意外的好,不过只要对加密算法稍加修改就能大幅减弱其抗攻击能力。
1994 年,IBM DES 初始团队的成员唐·库帕史密斯发表论文称,IBM 早在 1974 年就发现了差分密码分析法,并早已将抗差分分析纳入算法的设计目标中。作家史蒂芬·列维表示,IBM 的确独立发现了差分密码分析方法,显然 NSA 也知道这项技术。对于 IBM 选择保密的原因,库帕史密斯解释道:“IBM 与 NSA 商讨后,认为若公布加密算法中抗差分密码分析的设计,那么差分密码分析这种能攻击多种加密算法的强力技术就会被暴露,这将削弱美国在密码学领域的领先优势。IBM 内部把差分分析称为“T-attack”或“Tickle attack”。
与内建抗差分密码分析的 DES 相比,同期其它加密算法在这方面被证实是脆弱的。FEAL 是本分析方法的早期攻击目标。原始的 4 轮版本(FEAL-4)可以在仅利用八个选择明文的情况下被破解,且 31 轮版本的 FEAL 的抗攻击性也不尽人意。相比之下,差分分析在使用 247 数量级的选择明文后才能破解 DES 算法。
攻击原理 差分密码分析通常是选择明文攻击,意思是攻击者可以自行选取一部分明文并获取相应密文。不过,一些扩展也能让此方法用在已知明文攻击,甚至是唯密文攻击上。差分分析的基本方法,是运用若干对有着常量差异的明文;差异可以用数种方法定义,最常见的是逻辑异或(XOR)。然后,攻击者计算相应密文的差异,尝试找出差异分布的统计特征。明文差异和密文差异所组成的差异对被称为差分,其统计性质由加密所使用的 S 盒决定。也就是说,对于 S 盒子 S,攻击者分析差分(ΔX, ΔY),其中ΔY = S(X ⊕ ΔX) ⊕ S(X)(⊕表示异或)。在初等攻击中,攻击者希望某个密文差异出现的频率非常高,这样就能将加密和随机过程区分开来。更复杂的变体攻击能做到比穷举更快地破解出密钥。
最基本的差分密码分析密钥破解形式中,攻击者首先获取大量明文对的密文,并假设差分在至少 r − 1 轮后有效,r 即加密算法的总轮数。攻击者在倒数第二轮与最后一轮之间差异固定的假设下,去推测可能的轮密钥。如果轮密钥比较短,那么只需举可能的轮密钥,直到最后一轮运算结果和差异的密文对一致即可。当一个轮密钥看起来明显比其他密钥常出现时,其会被假设是正确的轮密钥。
针对特定的加密算法,输入差异要经过精心挑选才能使攻击成功。这需要研究算法的内部过程;标准的方法是在加密的不同阶段,跟踪一个高频差异经过的路径,术语上将这点称为差分特征。
自从差分密码分析进入公众视野,其就成了加密设计者的基本考量。新的加密设计通常需要举证其算法可以抗此类攻击。包括 AES 在内的许多算法都被证明在差分分析攻击下是安全的。
攻击细则:攻击主要依赖于一点:给定输入/输出,差异特征仅在特定输入下出现。这种方法通常用于线性结构组成的加密方式,如差表结构或 S 盒。给定两个已知密文或明文后,观察其输出差异可猜测密钥的可能值。
二.问题分析
差分密码分析的基本思想是:根据比较明文与密文之间的差异,从而基于这些差异,对密钥进行合理的猜测。下面先学习一个简单的流密码加密过程。差分密码分析是一种密码分析的方法,主要用于破解分组加密,但也适用于流加密和加密哈希函数。广义上讲,其研究的是信息输入上的差别对输出结果变化的影响。对于分组密码,其指的是利用差异来获取密钥的技术,包括跟踪变换网络中的差异,以及寻找加密中的非随机行为等。
其加密运算如下: Ci = Pi xor K
<center>密钥:0010010</center>
<center>明文:0110100</center>
<center>密文:0100110</center>
假设现在存在两个明文分组为P1,P2,使用同一个密钥K加密后的密文分组为C1,C2,则存在如下关系表达式:C1 xor C2 = P1 xor K xor P2 xor K
化简得C1 xor C2 = P1 xor P2
我们通过举一个实例来理解上述关系表达式,例如我们有:
<center>明文P1:0110100 明文P2:1110011
<center> 密钥K :0010010 密钥K :0010010
<center>密文C1:0100110 密文C2:1100001</center>
则根据XOR运算表达关系式有:
<center>C1 XOR C2 = 1000111
<center>P1 XOR P2 = 1000111
显然,两者产生相同的结果。那么根据表达式,我们很容易得出这样的结论:如果已知两个密文流和其中一个明文流,那么就可以确定另一个明文流。例如,已知C1,C2,P1的情况下,那么,P2 = C1 xor C2 xor P1
在实例中我们详细介绍了流密码的线性特性,即明文对与密文对的差异是相同的,使用密钥进行加密,并不会改变这个特性。但是分组密码却不是线性的,因为分组密码中的S盒是进行非线性变换,该操作可以使得明文对的差异(差分)与密文对的差异变得不同。如图一所示,这是差分分析一个有趣的性质:差分不会被异或运算所影响,但是S盒会改变差分。
三.差分密码分析
分组密码的非线性特性是由S盒的结构导致的,因此差分分析法就从考查S盒的特性入手。图二描述了玩具密码的过程:明文先与K0异或,然后输入到S盒中,其输出值与K1异或,然后输出密文。密码分析者如果单纯地使用穷举的方式去破解密钥,显然需要花费很多的时间(如果密钥是8bits,则需要尝试2^8次)。然而,如果猜测一个合理的输入S盒的值和输出S盒的值,那么破解K0,K1所花费的时间将大大减少。
幸运的是,S盒并不是那么完美。通过统计分析的方式可以建立输入差分与输出差分的关系。根据所找出的关系,我们可以建立差分分布表
横坐标是X\Delta(输入差分),纵坐标是Y\Delta(输出差分),单元格里的值代表出现的次数。这是一个16进制的分布表
当输入差分为4,输出差分为7时,单元格的值是6,代表(4,7)出现了6次(总共为16次)。差分分布表可以帮助我们更快地破解密钥K。
例如,密码分析者使用选择明文攻击的方式,选择明文对(1,5),其差分是4,由于异或不改变差分,所以S盒的输入差分也是4。密文对(0,7)的差分是7,由于异或不改变差分,所以S盒的输出差分也为7。输入差分为4,根据差分分布表,我们可以得到输入S盒的明文对存在以下几种可能:(0,4)、(1,5)、(4,0)、(5,1)、(9,13)、(13,9)。同理,我们可以得到输出S盒的密文对存在以下几种可能:(3,4)、(14,9)、(4,3)、(9,14)、(11,12)、(12、11)。根据差分分布表,运用统计的规律,针对S盒的输入输出进行合理的猜测,就可以破解密钥K。
1:S盒差分分布表
% MATLAB
S = [2 6 8 7 1 4 9 5 3 0]; %S盒
for i = 1:10 %行
for j = 1:10 %列
n = 0; %前面公式中的#表示符合条件的x的个数
for x = 1:10 %遍历x
% S(x+a)-S(x) = a'
if mod(S(mod(x+j-2,10)+1)-S(x),10) == i-1
n = n + 1;
end
end
N(i,j) = n;
end
end
2:S盒的差分分析
知道了S盒的一组输入差分和输出差分( α , β ),我们就可以知道哪些具体的输入值x xx会导致输入差分α 传播至输出差分β
用差分密码破解分组密码的过程:
C语言实现:
#include <stdio.h>
int sbox[16] = {3, 14, 1, 10, 4, 9, 5, 6, 8, 11, 15, 2, 13, 12, 0, 7};
int sboxRev[16] = {14, 2, 11, 0, 4, 6, 7, 15, 8, 5, 3, 9, 13, 12, 1, 10};
int chars[16][16];
int roundFunc(int input, int key)
{
return sbox[key ^ input];
}
int encrypt(int input, int k0, int k1)
{
int x0 = roundFunc(input, k0);
return x0 ^ k1;
}
void findDiffs() //通过一个二重循环遍历的表,用统计的方式固定一个X遍历所有的Y,得到输入差分以及输出差分,把他们出现的次数打印出来
{
printf("\nCreating XOR differential table:\n");
int c, d, e, f;
for(c = 0; c < 16; c++)
for(d = 0; d < 16; d++)
chars[c ^ d][sbox[c] ^ sbox[d]]++;
for(c = 0; c < 16; c++)
{
for(d = 0; d < 16; d++)
printf(" %x ", chars[c][d]);
printf("\n");
}
printf("\nDisplaying most probable differentials:\n");
for(c = 0; c < 16; c++)
for(d = 0; d < 16; d++)
if (chars[c][d] == 6)
printf(" 6/16: %i --> %i\n", c, d);
}
int knownP0[10000];
int knownP1[10000];
int knownC0[10000];
int knownC1[10000];
int goodP0, goodP1, goodC0, goodC1;
int numPairs;
//int realk0, realk1;
int chardat0[16];
int chardatmax = 0;
void genCharData(int indiff, int outdiff) //4是输入差分7是输出差分 得到s盒上半部分的明文
{
printf("\nGenerating possible intermediate values based on differential(%i --> %i):\n", indiff, outdiff);
chardatmax = 0;
int f;
for(f = 0; f < 16; f++)
{
int myComp = f ^ indiff;
if ((sbox[f] ^ sbox[myComp]) == outdiff)
{
printf(" Possibles: %i + %i --> %i + %i\n", f, myComp, sbox[f], sbox[myComp]);
chardat0[chardatmax] = f;
chardatmax++;
}
}
}
void genPairs(int indiff) //输入差分
{
printf("\nGenerating %i known pairs with input differential of %i.\n", numPairs, indiff);
//随机两个值k0k1生成已知明文然后用明文和差分异或得到另一部分明文
int realk0 = rand() % 16;
int realk1 = rand() % 16;
printf(" Real K0 = %i\n", realk0);
printf(" Real K1 = %i\n", realk1);
int c;
for(c = 0; c < numPairs; c++)
{
knownP0[c] = rand() % 16;
knownP1[c] = knownP0[c] ^ indiff;
knownC0[c] = encrypt(knownP0[c], realk0, realk1);
knownC1[c] = encrypt(knownP1[c], realk0, realk1);
}
}
void findGoodPair(int outdiff) //输出差分 可以通过判断C0得到最大概率的outdiff=密文分组里面满足差分是7的值的密文对应的明文,找到goodpairs这样的值为7的明密文队并打印
{
printf("\nSearching for good pair:\n");
int c;
for(c = 0; c < numPairs; c++)
if ((knownC0[c] ^ knownC1[c]) == outdiff)
{
goodC0 = knownC0[c];
goodC1 = knownC1[c];
goodP0 = knownP0[c];
goodP1 = knownP1[c];
printf(" FOUND GOOD PAIR: (P0 = %i, P1 = %i) --> (C0 = %i, C1 = %i)\n", goodP0, goodP1, goodC0, goodC1);
return;
}
printf("NO GOOD PAIR FOUND!\n");
}
//用k0k1加密过来看是否等于密文这样可以测试出我没猜测的K0K1是否为正确的密钥
int testKey(int testK0, int testK1)
{
int c;
int crap = 0;
for(c = 0; c < numPairs; c++)
{
if ((encrypt(knownP0[c], testK0, testK1) != knownC0[c]) || (encrypt(knownP1[c], testK0, testK1) != knownC1[c]))
{
crap = 1;
break;
}
}
if (crap == 0)
return 1;
else
return 0;
}
//这样我们得到了一个输入差分是4的明文对,输出差分是7的密文对
void crack() //这样就可以进行暴力破解,我们得到了s盒上k0右边部分的值用最大概率猜左边部分的值,同理得到S盒下方K1左边部分的值,明文与用最大概率猜出的值异或可以得到一个合理的K0,同理得到K1
{
printf("\nBrute forcing reduced keyspace:\n");
int f;
for(f = 0; f < chardatmax; f++)
{
int testK0 = chardat0[f] ^ goodP0;
int testK1 = sbox[chardat0[f]] ^ goodC0;
if (testKey(testK0, testK1) == 1)
printf(" KEY! (%i, %i)\n", testK0, testK1);
else
printf(" (%i, %i)\n", testK0, testK1);
}
}
int main()
{
srand(time(NULL)); //里面包含随机数的值
findDiffs(); //构造生产打印差分密码的分布表
numPairs = 8; //定义了8个分组
genCharData(4, 7);
genPairs(4);
findGoodPair(7);
crack();
while(1){}
return 0;
}
四.总结
分组密码的非线性特性是由S盒的结构导致的,因此差分分析法就从考查S盒的特性入手。差分密码分析是方法是攻击迭代型分组密码的最有效的方式之一,也是最基础的一种密码分析手段,同时也是衡量一个分组密码安全性的重要指标之一。差分密码分析的原理比较简单,发展至今,该方法出现了很多的变种,但万变不离其宗,本质上就是研究差分在加解密过程中的概率传播特性。
五.参考文献:
《经典密码学与现代密码学》分组密码的非线性特性是由S盒的结构导致的,因此差分分析法就从考查S盒的特性入手。图二描述了玩具密码的过程:明文先与K0异或,然后输入到S盒中,其输出值与K1异或,然后输出密文。密码分析者如果单纯地使用穷举的方式去破解密钥,显然需要花费很多的时间(如果密钥是8bits,则需要尝试2^8次)。然而,如果猜测一个合理的输入S盒的值和输出S盒的值,那么破解K0,K1所花费的时间将大大减少。