滑动攻击

1. 滑动攻击简介

滑动攻击也是一种概率攻击,但它的方法跟差分或线性攻击中的概率偏移性检测是不同的。对差分或线性来说,增加轮数可防止被攻击。但对滑动攻击来说,对于很破的轮函数,仅轮数的增加,是很难抵挡滑动攻击的。

本文对http://www.theamazingking.com/crypto-slide.php中的文章进行了学习,记录笔记如下。

代码中描述了使用30个明、密文对的滑动攻击及与穷举攻击的比较。

2. TOY100密码算法结构

TOY100密码算法100圈,数据块为8位。密钥位为16位。结构图如下:

滑动攻击_笔记 滑动攻击_记录_02

图1 轮函数+整体密码

这里的子密钥实际就K0和K1,太弱了,也是滑动攻击成功的主要原因。

3. 滑动攻击

3.1 滑动攻击成功的前题

密码算法有很多重复的子结构;每个子结构中的密钥最好全相同,或起码要有足够的相关性;每个重复的子结构在已知明文攻击下必须很简单;显然TOY100密码算法满足上述需求。其可看成图2中的结构:

滑动攻击_休闲_03

滑动攻击_记录_04

图2 FEAL轮函数结构

而对一个Doubleround来说,是不难被攻破的,参见图3:

滑动攻击_记录_05

图3 Doubleround的破解

其过程如下:把密文经逆SBOX后,作备用。再把明文经猜测的K0加密再经SBOX处理后与备用数据XOR,得到K1,但此时得到的是256种K0和K1的组合。

3.2 滑动对的寻找

在众多的明、密文对中要找到两对相关的明、密文对。即P0,P1,C0,C1,称为滑动对。

滑动攻击_笔记_06

由上图可看出,P0与P1,C0与C1的关系。这样由C0与C1,可以求出最后一轮的256种K0和K1的组合。实际上C0与C1是随机组对,然后求出256种K0和K1的组合,再通过P0与P1去判断是否真是滑动对。一旦P0经过猜测的K0和K1处理后等于P1,则猜测正确。所得的K即为初始密钥。

3.3 与穷举搜索的对比

穷举搜索穷举2^16个密钥,且每个100轮。而滑动攻击仅C(n,2)*256个密钥,每个密钥跑4轮。这里n是明、密文对数。由生日攻击原理可知,C0与C1发生碰撞的概率是很大的。

4. 试验结果

做1000次的滑动与穷举对比。穷举每次必然成功;滑动未必,看是否能找到滑动对。其成功率跟使用的对数有关。gcc -O3 slide.c -o slide

5. 附录代码

 

  1. //Slide Attack  
  2.  
  3. //theamazingking.com  
  4.  
  5. #include <stdio.h>  
  6.  
  7. #include <stdlib.h>  
  8.  
  9. #include <time.h>  
  10.  
  11. unsigned int sbox[16] = {2, 6, 1, 13, 0, 5, 8, 7, 14, 10, 15, 11, 4, 9, 3, 12};  
  12.  
  13. unsigned int revSbox[16] = {4, 2, 0, 14, 12, 5, 1, 7, 6, 13, 9, 11, 15, 3, 8, 10};  
  14.  
  15. unsigned int realK0, realK1;  
  16.  
  17. unsigned int plain[200];  
  18.  
  19. unsigned int cipher[200];  
  20.  
  21. unsigned int fullSbox(unsigned int a)  
  22.  
  23. {  
  24.  
  25. unsigned int left = (a &gt;&gt; 4) & 0xf;  
  26.  
  27. unsigned int right = a & 0xf;  
  28.  
  29. left = sbox[left];  
  30.  
  31. right = sbox[right];  
  32.  
  33. return left <&lt; 4 | right;  
  34.  
  35. }  
  36.  
  37. unsigned int fullRevSbox(unsigned int a)  
  38.  
  39. {  
  40.  
  41. unsigned int left = (a >&gt; 4) & 0xf;  
  42.  
  43. unsigned int right = a & 0xf;  
  44.  
  45. left = revSbox[left];  
  46.  
  47. right = revSbox[right];  
  48.  
  49. return left &lt;&lt; 4 | right;  
  50.  
  51. }  
  52.  
  53. unsigned int oneRound(unsigned int a, unsigned int k)  
  54.  
  55. {  
  56.  
  57. return fullSbox(a ^ k);  
  58.  
  59. }  
  60.  
  61. unsigned int encrypt(unsigned int plain, unsigned int k0, unsigned int k1)  
  62.  
  63. {  
  64.  
  65. int c;  
  66.  
  67. for(c = 0; c &lt; 100; c++)  
  68.  
  69. {  
  70.  
  71. if ((c % 2) == 0) //EVEN ROUNDS  
  72.  
  73. plain = oneRound(plain, k0);  
  74.  
  75. else 
  76.  
  77. plain = oneRound(plain, k1);  
  78.  
  79. }  
  80.  
  81. return plain;  
  82.  
  83. }  
  84.  
  85. unsigned int slidePair(unsigned int plain1, unsigned int cipher1, unsigned int plain2, unsigned int cipher2)  
  86.  
  87. {  
  88.  
  89. int k0;  
  90.  
  91. for(k0 = 0; k0 &lt;= 0xff; k0++)  
  92.  
  93. {  
  94.  
  95. unsigned int k1 = fullSbox(cipher1 ^ k0) ^ fullRevSbox(cipher2);  
  96.  
  97. unsigned int testPlain2 = fullSbox(fullSbox(plain1 ^ k0) ^ k1);  
  98.  
  99. if (testPlain2 == plain2)  
  100.  
  101. {  
  102.  
  103. //VERIFY  
  104.  
  105. int crapped = 0;  
  106.  
  107. int c;  
  108.  
  109. for(c = 0; c &lt; 10; c++)  
  110.  
  111. {  
  112.  
  113. if (encrypt(plain[c], k0, k1) != cipher[c])  
  114.  
  115. {  
  116.  
  117. crapped = 1;  
  118.  
  119. break;  
  120.  
  121. }  
  122.  
  123. }  
  124.  
  125. if (crapped) continue;  
  126.  
  127. // printf("REAL K0 = %x K1 = %x\n", realK0, realK1);  
  128.  
  129. // printf(" FOUND K0 = %x K1 = %x\n", k0, k1);  
  130.  
  131. // printf("\n");  
  132.  
  133. return 1;  
  134.  
  135. }  
  136.  
  137. }  
  138.  
  139. return 0;  
  140.  
  141. }  
  142.  
  143. int slideIt()  
  144.  
  145. {  
  146.  
  147. int c, d;  
  148.  
  149. for(c = 0; c &lt; 30; c++)  
  150.  
  151. {  
  152.  
  153. for(d = 0; d &lt; 30; d++)  
  154.  
  155. if (c != d)  
  156.  
  157. if (slidePair(plain[c], cipher[c], plain[d], cipher[d]))  
  158.  
  159. return 1;  
  160.  
  161. }  
  162.  
  163. return 0;  
  164.  
  165. }  
  166.  
  167. int bruteIt()  
  168.  
  169. {  
  170.  
  171. int c, d;  
  172.  
  173. for(c = 0; c &lt;= 0xFF; c++)  
  174.  
  175. for(d = 0; d &lt;= 0xFF; d++)  
  176.  
  177. {  
  178.  
  179. int crapped = 0;  
  180.  
  181. int e;  
  182.  
  183. for(e = 0; e &lt; 10; e++)  
  184.  
  185. {  
  186.  
  187. if (encrypt(plain[e], c, d) != cipher[e])  
  188.  
  189. {  
  190.  
  191. crapped = 1;  
  192.  
  193. break;  
  194.  
  195. }  
  196.  
  197. }  
  198.  
  199. if (crapped) continue;  
  200.  
  201. return 1;  
  202.  
  203. }  
  204.  
  205. return 0;  
  206.  
  207. }  
  208.  
  209. int main()  
  210.  
  211. {  
  212.  
  213. srand(time(NULL));  
  214.  
  215. printf("Simple Slide Attack\n");  
  216.  
  217. int goodOnes = 0;  
  218.  
  219. int f;  
  220.  
  221. //重复1000次  
  222.  
  223. for(f = 0; f &lt; 1000; f++)  
  224.  
  225. {  
  226.  
  227. realK0 = rand() & 0xFF;  
  228.  
  229. realK1 = rand() & 0xFF;  
  230.  
  231. //每次产生200个明、密文对,只用30对  
  232.  
  233. int c;  
  234.  
  235. for(c = 0; c &lt; 200; c++)  
  236.  
  237. {  
  238.  
  239. plain[c] = rand() & 0xFF;  
  240.  
  241. cipher[c] = encrypt(plain[c], realK0, realK1);  
  242.  
  243. }  
  244.  
  245. //不是每次30对都能找到密钥的,即1000次中有几次可能不行  
  246.  
  247. if (slideIt())  
  248.  
  249. goodOnes++;  
  250.  
  251. }  
  252.  
  253. printf("PERCENTAGE FOUND = %i\n", goodOnes * 100 / f);  
  254.  
  255. return 0;  
  256.  
  257. }