对FEAL-4的线性攻击

1. FEAL密码算法简介

FEAL密码算法家族是日本NTT(日本电报电话公司)的清水(Shimizi)和宫口(Miyaguchi)设计的(1986)。作为一种分组密码,与DES相比其主要想法为增加每一圈迭代的算法强度,因此可以通过减少迭代次数而提高运算速度。

FEAL-8即为8圈迭代的FEAL密码算法。FEAL密码算法推出之后,引起有关专家的注意。密码专家比哈姆和沙米尔利用差分密码分析技术发现,可以用比穷举法更快的速度破译FEAL密码。如FEAL-8只需2000个选择明文即可破译,而FEAL-4更只需8个精心选择的明文便可破译。

这个小密码系统用来做破译练手最合适不过了,本文对http://www.computing.dcu.ie/~mike/linear.html中的文章进行了学习,记录笔记如下。

2. FEAL密码算法结构

FEAL密码算法仅4圈,数据块为64位。最核心的便是轮函数f,输入、输出均32位。抗差分或线性分析均取决于此函数的设计。故用红色标出,其实Feistel结构本身的混淆是有限的。Feistel结构的优点是其轮函数可以是单向的,这样密码算法有更好的统计属性。而SPN网络的SBOX虽求逆困难,但不能单向,否则无法解密。

对FEAL-4的线性攻击_职场

图1 FEAL密码结构

这里的密钥K0~K5均是从初始密钥K扩展而来,每个32位。而实际的攻击结果是完全攻破6*32=192位密钥。所以,密钥协商算法是单向的也没关系,可以把子密钥看做随机生成。

2.1 FEAL轮函数结构

对FEAL-4的线性攻击_职场_02

图2 FEAL轮函数结构

轮函数的关键是非线性性,移位和异或都起到了这样的作用。输入为32位字,分为4个8位的小块。

3. FEAL密码算法线性攻击

线性攻击的本质是获得整个密码算法的线性表达式。可先分析轮函数的线性表达式,再分析整个密码算法的线性表达式。该表达式最终能化成只含明、密文及第1轮子密钥的式子。

a = S23,29(L0 Å R0 Å L4) Å S31(L0 Å L4 Å R4)Å S31F(L0 Å R0 Å K0)而a同时又可化为 a = S31(K1ÅK3ÅK4ÅK5) Å S23,29(K4) ,即该式子对任意明、密文应该为常数。于是可穷举K0的值去看a的值,若为常数,则可能获得正确的密钥。

3.1 轮函数中的线性表达式

直接贴来了,参见《Breaking Ciphers in the Real World》。本文省略了很多线性表达式由来的分析。

对FEAL-4的线性攻击_密码_03

图3 轮函数中的线性表达式推导

由于轮函数的输入是32位,所以不可能像小SBOX一样,穷举所有差分。

3.2 整个密码算法的表达式推导

整个密码算法的线性表达式。该表达式最终能化成只含明、密文及第1轮子密钥的式子。a = S23,29(L0 Å R0 Å L4) Å S31(L0 Å L4 Å R4)Å S31F(L0 Å R0 Å K0)。而a同时又可化为  a = S31(K1ÅK3ÅK4ÅK5) Å S23,29(K4),这里一系列的变换参见《Breaking Ciphers in the Real World对FEAL-4的线性攻击_FEAL-4_04

图4 整个密码算法

3.3 初始密钥k0的获取

对FEAL-4的线性攻击_职场_05

穷举所有的K0的值,看a的值是否对n个明、密文对在特定的K0下全为常数,若是,则很有可能是正确的K0.本质上a只与密钥相关,密钥定了,值就定了。a = S23,29(L0 Å R0 Å L4) Å S31(L0 Å L4 Å R4)Å S31F(L0 Å R0 Å K0)

3.4 复杂度的进一步化简

实际在a表达式中真正用到的就12位:10~15,17~23,其它位并不参与a值得运算,所以先穷举出这12位,再同样的技巧分析其它位,最后穷举剩余的位会更为简洁。同样的技巧可用于K1~K5的分析。

4. 试验结果

使用了60个已知明文、密文。在穷举10~15,17~23下,共4096种可能。看哪一种会使60个明文、密文的式子a全为常数0或1,则视为正确密钥10~15,17~23。可能的密钥有几个,但仅一个是正确的。

5. 附录代码

注:代码中的a表达式用的是下面的,推导见http://www.computing.dcu.ie/~mike/linear.html

a=Ph[10,16,18,26] ÅPl[10,18,26] Å Ch[10,16,18,26] Å Cl[16] Å F(PhÅPl,K1)[16]  = K4[16,24] Å K6[10,18,26] Å K2[16,24]

 

  1. /*  
  2.  
  3. * The FEAL cipher - Linear Cryptanalysis  
  4.  
  5. */ 
  6.  
  7. #include <stdio.h>  
  8.  
  9. #include <stdlib.h>  
  10.  
  11. /* uncomment this next statement to activate linear cryptanalysis code... */ 
  12.  
  13. #define DEBUG   
  14.  
  15. #define WORD32 unsigned int  
  16.  
  17. #define BYTE unsigned char  
  18.  
  19. //循环左移技巧  
  20.  
  21. #define ROT2(x) (((x)<&lt;2) | ((x)>&gt;6))  
  22.  
  23. #define S0(a,b) (ROT2((BYTE)((a)+(b))))  
  24.  
  25. #define S1(a,b) (ROT2((BYTE)((a)+(b)+1)))  
  26.  
  27. //字节数组化字技巧  
  28.  
  29. static WORD32 pack32(BYTE *b)  
  30.  
  31. /* pack 4 bytes into a 32-bit Word */ 
  32.  
  33. return ((WORD32)b[3]<&lt;24)|((WORD32)b[2]&lt;&lt;16)|((WORD32)b[1]&lt;&lt;8)|(WORD32)b[0];  
  34.  
  35. }  
  36.  
  37. //字化字节数组技巧  
  38.  
  39. static void unpack32(WORD32 a,BYTE *b)  
  40.  
  41. /* unpack bytes from a 32-bit word */ 
  42.  
  43. b[0]=(BYTE)a;  
  44.  
  45. b[1]=(BYTE)(a>&gt;8);  
  46.  
  47. b[2]=(BYTE)(a&gt;&gt;16);  
  48.  
  49. b[3]=(BYTE)(a&gt;&gt;24);  
  50.  
  51. }  
  52.  
  53. WORD32 f(WORD32 x,WORD32 key)  
  54.  
  55. {  
  56.  
  57. BYTE f[4];  
  58.  
  59. BYTE k[4];  
  60.  
  61. unpack32(x,f);  
  62.  
  63. unpack32(key,k);  
  64.  
  65. f[2]^=(f[3]^k[2]);  
  66.  
  67. f[1]^=(f[0]^k[1]);  
  68.  
  69. f[2]=S1(f[2],f[1]);  
  70.  
  71. f[1]=S0(f[1],f[2]);  
  72.  
  73. f[3]=S0(f[3]^k[3],f[2]);  
  74.  
  75. f[0]=S1(f[0]^k[0],f[1]);   
  76.  
  77. /* printf("%02x%02x%02x%02x\n",f[3],f[2],f[1],f[0]); */ 
  78.  
  79. return pack32(f);  
  80.  
  81. }  
  82.  
  83. /* H for High, L for Low */ 
  84.  
  85. void feal_encrypt(BYTE data[8],WORD32 *key)  
  86.  
  87. {  
  88.  
  89. int i;  
  90.  
  91. WORD32 H,L,T;  
  92.  
  93. H=pack32(&data[4]);  
  94.  
  95. L=H^pack32(&data[0]);  
  96.  
  97. T=L;  
  98.  
  99. L=H^f(L,key[0]);  
  100.  
  101. H=T;  
  102.  
  103. T=L;  
  104.  
  105. L=H^f(L,key[1]);  
  106.  
  107. H=T;  
  108.  
  109. L^=key[4];  
  110.  
  111. H^=key[5];   
  112.  
  113. T=L;  
  114.  
  115. L=H^f(L,key[2]);  
  116.  
  117. H=T;  
  118.  
  119. T=L;  
  120.  
  121. L=H^f(L,key[3]);  
  122.  
  123. H=T;  
  124.  
  125. H^=L;  
  126.  
  127. unpack32(L,&data[4]);  
  128.  
  129. unpack32(H,&data[0]);  
  130.  
  131. }  
  132.  
  133. void feal_decrypt(BYTE data[8],WORD32 *key)  
  134.  
  135. {  
  136.  
  137. int i;  
  138.  
  139. WORD32 H,L,T;  
  140.  
  141. L=pack32(&data[4]);  
  142.  
  143. H=L^pack32(&data[0]);  
  144.  
  145. T=H;  
  146.  
  147. H=L^f(H,key[3]);  
  148.  
  149. L=T;  
  150.  
  151. T=H;  
  152.  
  153. H=L^f(H,key[2]);  
  154.  
  155. L=T;  
  156.  
  157. L^=key[4];  
  158.  
  159. H^=key[5];   
  160.  
  161. T=H;  
  162.  
  163. H=L^f(H,key[1]);  
  164.  
  165. L=T;  
  166.  
  167. T=H;  
  168.  
  169. H=L^f(H,key[0]);  
  170.  
  171. L=T;  
  172.  
  173. L^=H;  
  174.  
  175. unpack32(H,&data[4]);  
  176.  
  177. unpack32(L,&data[0]);  
  178.  
  179. }  
  180.  
  181. int main(int argc,char **argv)  
  182.  
  183. {  
  184.  
  185. int i,j,lhs,rhs;  
  186.  
  187. int Ph10161826,Pl101826,Ch10161826,Cl16,Ch16;  
  188.  
  189. BYTE input[10];  
  190.  
  191. BYTE data[8],k[4];  
  192.  
  193. WORD32 key[6],ph,pl,ch,cl,k1;  
  194.  
  195. BYTE bits[4096];  
  196.  
  197. int first_time_in=1;  
  198.  
  199. /* DELETED - Secret key generator. Sets keys key[0] ... key[5] */ 
  200.  
  201. key[1]=key[2]=key[3]=key[4]=key[5]=0; /* not the real key ! */ 
  202.  
  203. key[0]=0x2E3500; /* !! This is the first sub-key we find! */ 
  204.  
  205. #ifndef DEBUG  
  206.  
  207. argc--; argv++;  
  208.  
  209. if (argc!=8)  
  210.  
  211. {  
  212.  
  213. printf("command line error - input 8 bytes of plaintext in hex\n");  
  214.  
  215. printf("For example:-\n");  
  216.  
  217. printf("findkey 0 1 2 3 4e 5a f6 37\n");  
  218.  
  219. return 0;  
  220.  
  221. }  
  222.  
  223. for (i=0;i<8;i++)  
  224.  
  225. sscanf(argv[i],"%x",&input[i]);  
  226.  
  227. for (i=0;i&lt;8;i++) data[i]=input[7-i];  
  228.  
  229. printf("Plaintext= ");  
  230.  
  231. for (i=7;i>=0;i--) printf("%02x ",data[i]);  
  232.  
  233. printf("\n");  
  234.  
  235. feal_encrypt(data,key);  
  236.  
  237. printf("Ciphertext= ");  
  238.  
  239. for (i=7;i&gt;=0;i--) printf("%02x ",data[i]);  
  240.  
  241. printf("\n");  
  242.  
  243. feal_decrypt(data,key);  
  244.  
  245. printf("Plaintext= ");  
  246.  
  247. for (i=7;i&gt;=0;i--) printf("%02x ",data[i]);  
  248.  
  249. printf("\n");  
  250.  
  251. #else  
  252.  
  253. for (i=0;i<8;i++) data[i]=(BYTE)rand();  
  254.  
  255. /*  
  256.  
  257. the debug code below searches through all k1[8~13] and k1[16~21],  
  258.  
  259. as described by Matsui. That's 4096 possibilities. It outputs a   
  260.  
  261. 0 or a 1 for each possible key.  
  262.  
  263. For all known plaintexts/ciphertexts the right key will always give the  
  264.  
  265. same fixed 0 or 1  
  266.  
  267. For each plaintext/ciphertext pair newly discovered wrong keys   
  268.  
  269. are replaced with an X  
  270.  
  271. */ 
  272.  
  273. /* Now try for 60 random plaintexts... */ 
  274.  
  275. for (j=0;j&lt;60;j++)  
  276.  
  277. {  
  278.  
  279. for (i=0;i&lt;8;i++) data[i]=(BYTE)rand(); /* random plaintexts */ 
  280.  
  281. printf("\nPlaintext= ");  
  282.  
  283. for (i=7;i>=0;i--) printf("%02x ",data[i]);  
  284.  
  285. printf("\n");  
  286.  
  287. pl=pack32(&data[0]);  
  288.  
  289. ph=pack32(&data[4]);  
  290.  
  291. Ph10161826=((ph&gt;&gt;10)^(ph&gt;&gt;16)^(ph&gt;&gt;18)^(ph&gt;&gt;26))&1;  
  292.  
  293. Pl101826 =((pl&gt;&gt;10)^(pl&gt;&gt;18)^(pl&gt;&gt;26))&1;  
  294.  
  295. feal_encrypt(data,key);  
  296.  
  297. printf("Ciphertext= ");  
  298.  
  299. for (i=7;i&gt;=0;i--) printf("%02x ",data[i]);  
  300.  
  301. printf("\n");  
  302.  
  303. cl=pack32(&data[0]);  
  304.  
  305. ch=pack32(&data[4]);  
  306.  
  307. Ch10161826=((ch&gt;&gt;10)^(ch&gt;&gt;16)^(ch&gt;&gt;18)^(ch&gt;&gt;26))&1;  
  308.  
  309. Cl16=((cl&gt;&gt;16)&1);  
  310.  
  311. Ch16=((ch&gt;&gt;16)&1);  
  312.  
  313. for (i=0;i<4096;i++)  
  314.  
  315. {  
  316.  
  317. //无关的位置0,用到的&出来  
  318.  
  319. k[0]=0; k[1]=i&0x3F; k[2]=(i>&gt;6)&0x3F; k[3]=0;  
  320.  
  321. k1=pack32(k);  
  322.  
  323. lhs=(Ph10161826^Pl101826^Ch10161826^Cl16^(f(ph^pl,k1)&gt;&gt;16))&1;  
  324.  
  325. if (first_time_in) bits[i]=lhs;  
  326.  
  327. else if (lhs!=bits[i]) bits[i]=0xFF;  
  328.  
  329. }  
  330.  
  331. /* print out the 4096 bit array */ 
  332.  
  333. for (i=0;i<4096;i++)  
  334.  
  335. {  
  336.  
  337. if (bits[i]==0xFF)   
  338.  
  339. {  
  340.  
  341. printf("X");  
  342.  
  343. continue;  
  344.  
  345. }  
  346.  
  347. printf("%d",bits[i]);  
  348.  
  349. }  
  350.  
  351. first_time_in=0;  
  352.  
  353. }  
  354.  
  355. printf("\nCandidates for bits 8-13, and 16-21 of key[0] are\n");  
  356.  
  357. for (i=0;i&lt;4096;i++)  
  358.  
  359. {  
  360.  
  361. if (bits[i]==0xFF) continue;  
  362.  
  363. k[0]=0; k[1]=i&0x3F; k[2]=(i>&gt;6)&0x3F; k[3]=0;  
  364.  
  365. k1=pack32(k);  
  366.  
  367. printf("%x\n",k1);  
  368.  
  369. }  
  370.  
  371. printf("Actual key[0] we were looking for = %x\n",key[0]);   
  372.  
  373. #endif  
  374.  
  375. return 0;  
  376.  
  377. }