DES算法实现+差分分析和线性分析代码实现

DES算法差分和线性分析

目录

摘要

数据秘钥
差分第一组数据0x1a624d88530cec40
差分第二组数据0xf438e76b8f971eba
差分第三组数据0xa24cb10dfa8d1322
线性 N S 5 ( α = 16 , β = 15 ) = − 20 NS_5(α=16,β=15)=-20 NS5(α=16,β=15)=20 P L [ 15 ] ⊕ C L [ 15 ] ⊕ P H [ 24 , 18 , 7 , 29 ] ⊕ C H [ 24 , 18 , 7 , 29 ] = K 1 [ 22 ] ⊕ K 3 [ 22 ] P_L[15]\oplus C_L[15]\oplus P_H[24, 18, 7, 29]\oplus C_H[24, 18, 7, 29]=K_1[22]\oplus K_3[22] PL[15]CL[15]PH[24,18,7,29]CH[24,18,7,29]=K1[22]K3[22]

DES攻击方法

​ 我们知道密码算法的安全性是基于密钥空间的安全性来讨论的,而考虑到敌手的计算资源,密码算法实际上拥有计算安全性。对于DES来说,如果敌手可以进行选择明文攻击,就有多种方法可以对秘钥进行破解:穷举攻击,查表攻击,时间存储权衡攻击等。

类型DMT(离线)T(在线)
穷举攻击已知明文攻击O(1)O(1)0 O ( 2 k ) O(2^k) O(2k)
查表攻击选择明文O(1) O ( 2 k ) O(2^k) O(2k) O ( 2 k ) O(2^k) O(2k)1
时间存储权衡攻击选择明文O(1) O ( 2 2 k / 3 ) O(2^{2k/3}) O(22k/3) O ( 2 k ) O(2^k) O(2k) O ( 2 2 k / 3 ) O(2^{2k/3}) O(22k/3)

时间存储权衡攻击:

关键:
        建立密文密钥之间的链接关系

函数定义:

● 约化函数(reduction function)
         R : n − b i t → k − b i t R:n-bit \to k-bit R:nbitkbit,其中,n为分组长度,k为密钥长度。
        可为杂凑函数或简单截取。
● 链接函数
         g ( K ) = R ( E K ( P ∗ ) ) g(K) = R(E_{K}(P^*)) g(K)=R(EK(P)),先加密再约化,
        其中,P*为敌手选定的明文。

预计算

    ● 随机选择m个长度为56-bit的数据 K 1 , 0 , K 2 , 0 , K 3 , 0 , K 4 , 0 . . . K m , 0 K_{1,0},K_{2,0},K_{3,0},K_{4,0}...K_{m,0} K1,0,K2,0,K3,0,K4,0...Km,0一些密钥
    ● 根据所选择的明文P*,对 1 ≤ i ≤ m , 1 ≤ j ≤ t 1\le i\le m,1\le j\le t 1im,1jt
  ● 计算 K i , j = g ( K i , j − 1 ) = R ( E K i , j − 1 ( P ∗ ) ) K_{i,j}=g(K_{i,j-1})=R(E_{K_{i,j-1}}(P^*)) Ki,j=g(Ki,j1)=R(EKi,j1(P))存储起点和终点。

          K 1 , 0 → g K 1 , 1 → g K 1 , 2 . . . . → g K 1 , t K_{1,0}\stackrel{g}{\to}K_{1,1}\stackrel{g}{\to}K_{1,2}....\stackrel{g}{\to}K_{1,t} K1,0gK1,1gK1,2....gK1,t
          K 2 , 0 → g K 2 , 1 → g K 2 , 2 . . . . → g K 2 , t K_{2,0}\stackrel{g}{\to}K_{2,1}\stackrel{g}{\to}K_{2,2}....\stackrel{g}{\to}K_{2,t} K2,0gK2,1gK2,2....gK2,t
         。。。。。。
          K m , 0 → g K m , 1 → g K m , 2 . . . . → g K m , t K_{m,0}\stackrel{g}{\to}K_{m,1}\stackrel{g}{\to}K_{m,2}....\stackrel{g}{\to}K_{m,t} Km,0gKm,1gKm,2....gKm,t

    ● 更换R函数,如上构造s个预计算表。只需要储存开头和结尾。
    ● T = O ( m t s ) , M = O ( m s ) T=O(mts),M=O(ms) T=O(mts)M=O(ms)

在线阶段

● 获得 ( P ∗ , C ∗ ) (P^*,C^*) (P,C)
通过g函数,有 P ∗ ⟶ E K ∗ C ∗ ⟶ R C 1 ⟶ g C 2 ⟶ g C 3 . . . ⟶ g C t P^*\stackrel{E_{K^*}}{\longrightarrow}C^*\stackrel{R}{\longrightarrow}C_1\stackrel{g}{\longrightarrow}C_2\stackrel{g}{\longrightarrow}C_3...\stackrel{g}{\longrightarrow}C_t PEKCRC1gC2gC3...gCt
由g函数的定义,有 P ∗ ⟶ E K ∗ C ∗ ⟶ R C 1 P^*\stackrel{E_{K^*}}{\longrightarrow}C^*\stackrel{R}{\longrightarrow}C_1 PEKCRC1 K ∗ ⟶ R C 1 K^*\stackrel{R}{\longrightarrow}C_1 KRC1是等价的。所以正确的 K ∗ K^* K可以得到同一个 C 1 C_1 C1

● 先用 C 1 C_1 C1去找储存表中找,如果找不到,就在用g函数计算 C 2 C_2 C2…直到找到一个 K i , t K_{i,t} Ki,t C i C_i Ci相等。
● 假设找到 K i , t K_{i,t} Ki,t C j C_j Cj相等,就可以通过计算,把 K i , t K_{i,t} Ki,t回溯 j j j个位置找到 K i , t − j K_{i,t-j} Ki,tj大概率就是正确的K。

DES差分分析

差分分析原理

​ 差分攻击通过分析特定明文差分对相对应密文差分的影响来提取密钥。差分攻击往往是一种选择明文攻击,也就是说,我们可以得到想要的任意明密文对。

一轮加密

加密示意图(c、m、k都是一位16进制数):

​ 对明密文对(m1,c1),(m2,c2),定义明文差分 Δ m = m 1 ⊕ m 2 , Δ c = c 1 ⊕ c 2 \Delta m=m_1\oplus m_2,\Delta c=c_1\oplus c_2 Δm=m1m2,Δc=c1c2

c 1 ⊕ c 2 = S [ m 1 ⊕ k 0 ] ⊕ k 1 ⊕ S [ m 1 ⊕ k 0 ] ⊕ k 1 = S [ m 1 ⊕ k 0 ] ⊕ S [ m 2 ⊕ k 0 ] c_1\oplus c_2=S[m_1\oplus k_0]\oplus k_1\oplus S[m_1\oplus k_0]\oplus k_1=S[m_1\oplus k_0]\oplus S[m_2\oplus k_0] c1c2=S[m1k0]k1S[m1k0]k1=S[m1k0]S[m2k0],所以我们现在考虑S盒的输入输出。

​ 我们定义输出异或: I N x o r = u 1 ⊕ u 2 = m 1 ⊕ m 2 IN_{xor}=u_1\oplus u_2=m_1\oplus m_2 INxor=u1u2=m1m2

​ 输出异或: O U T x o r = S [ m 1 ⊕ k 0 ] ⊕ S [ m 2 ⊕ k 0 ] = c 1 ⊕ c 2 OUT_{xor}=S[m_1\oplus k_0]\oplus S[m_2\oplus k_0]=c_1\oplus c_2 OUTxor=S[m1k0]S[m2k0]=c1c2

​ 因为S盒是已知的,所以存在S盒的逆运算,可以通过穷举明文对,计算明文对过S盒时候的输入异或和输出异或,并储存。当我们需要解密时,就可以通过获取的多组明密文对,分别计算输入输出异或,然后根据输入输出异或查询S盒的异或表,找到对应的输入对,然后通过线性运算或者穷举等方法,解出秘钥可能,最后通过概率的方法,得到最大可能的秘钥。

​ S盒的差分分布表,假设横坐标表示INxor,列坐标表示OUTxor,每一项就表示满足条件的明文对(m1,m2) = { m 1 ⊕ m 2 = I N x o r , S [ m 1 ] ⊕ S [ m 2 ] = O U T x o r m_1\oplus m_2=IN_{xor},S[m_1]\oplus S[m_2]=OUT_{xor} m1m2=INxor,S[m1]S[m2]=OUTxor} 的个数:

​ 因为是选择明文攻击,由上表,当输入异或为f时,有10种秘钥可以使输出异或是d,我们选择两个输入分别是 i , j , i ⊕ j = f i,j,i\oplus j = f i,j,ij=f,此时的S盒异或表:

​ 计算S盒的所有输入异或和输出异或,对应的明文输入对,即计算

S [ I N x o r ] [ O U T x o r ] = { ( i , j ) ∣ i ⊕ j = I N x o r , S [ i ] ⊕ S [ j ] = O U T x o r } S[IN_{xor}][OUT_{xor}] = \{(i,j)|i\oplus j=IN_{xor},S[i]\oplus S[j]=OUT_{xor} \} S[INxor][OUTxor]={(i,j)ij=INxor,S[i]S[j]=OUTxor}

​ 然后我们通过已知的明密文,计算INxor和OUTxor,去查表,得到 { ( i 1 , j 1 ) , ( i 2 , j 2 ) . . . } \{(i_1,j_1),(i_2,j_2)...\} {(i1,j1),(i2,j2)...},而实际上 ( i , j ) = ( c 1 ⊕ k 0 , c 2 ⊕ k 0 ) (i,j) = (c_1\oplus k_0,c_2\oplus k_0) (i,j)=(c1k0,c2k0),我们再用 c 1 c_1 c1 c 2 c_2 c2去异或,就得到了秘钥的备选,通过计算多组明密文的备选秘钥,取它们的交集就是最可能的秘钥。

​ 这是一种中间相遇的攻击方法,优点是在线时间短,缺点也很明显,就是当S盒的输入比特位很多时,会有不小的空间复杂度。

二轮加密

加密示意图(c和m都是一位16进制数):

​ 二轮的加密完整的表达式 c 1 ⊕ c 2 = S [ S [ m 1 ⊕ k 0 ] ⊕ k 1 ] ⊕ S [ S [ m 2 ⊕ k 0 ] ⊕ k 1 ] c_1\oplus c_2=S[S[m_1\oplus k_0]\oplus k_1]\oplus S[S[m_2\oplus k_0]\oplus k_1] c1c2=S[S[m1k0]k1]S[S[m2k0]k1],对于输出异或 c 1 ⊕ c 2 c_1\oplus c_2 c1c2我们没有办法直接得到他的输入异或为 S [ m 1 ⊕ k 0 ] ⊕ S [ m 2 ⊕ k 0 ] S[m_1\oplus k_0]\oplus S[m_2\oplus k_0] S[m1k0]S[m2k0],而这个值我们是得不到的,只能用过猜测秘钥k0,求得。同样我们选择明文的差分为f的多组输入,如果猜测的秘钥正确,那么就有 10 16 \frac{10}{16} 1610 的概率 v 1 ⊕ v 2 v_1\oplus v_2 v1v2的值是d,而 v 1 ⊕ v 2 = w 1 ⊕ w 2 v_1\oplus v_2=w_1\oplus w_2 v1v2=w1w2,而我们知道 x 1 ⊕ x 2 x_1\oplus x_2 x1x2的值,我们就可以用查询输入输入差分的方法,再次解出秘钥。这样做,是有一定概率成功的,所以我们在第一次差分的地方,就要选择高概率的差分路径,在这里是 P ( f → S d ) = 10 16 P(f\stackrel{S}{\to}d)=\frac{10}{16} P(fSd)=1610

DES差分分析具体实现

题目

第一组数据如下:

明文:0x5E870BA0B559A8CF 密文:0X71BF939C0CEEE3B1
明文:0xE7C1F970B559A8CF 密文:0XEAA6CE7BC9DB808B
明文:0x5D6F0803ED9FAC45 密文:0XD99FDDD5A3016E53
明文:0x1EB2B007ED9FAC45 密文:0XB49E2F61B4172078
明文:0x7ECF80BD2FE0EA99 密文:0XC9BE22F6DA261B9A
明文:0x8B2CBE002FE0EA99 密文:0X2360C6F9ACD3982D
明文:0X97D2078984F010B4 密文:0X719849F28E5313BF
明文:0X4A5C783384F010B4 密文:0XE4DDEEDB66776D42
明文:0X641E10E96186B8A0 密文:0X7918C1C6400F4AA2
明文:0XCA4E94596186B8A0 密文:0XB8D0DC72CD2F6579

题解

先写出表达式,利用已有的数据,尽可能的化简表达式,

迭代公式
第一轮 L 1 = R 0 L_1=R_0 L1=R0 R 1 = L 0 ⊕ F ( K 1 , R 0 ) R_1=L_0\oplus F(K_1,R_0) R1=L0F(K1,R0)
第二轮 L 2 = R 1 L_2=R_1 L2=R1 R 2 = L 1 ⊕ F ( K 2 , R 1 ) R_2 =L_1\oplus F(K_2,R_1) R2=L1F(K2,R1)
第三轮 L 3 = R 2 L_3=R_2 L3=R2 R 3 = L 2 ⊕ F ( K 3 , R 2 ) R_3 =L_2\oplus F(K_3,R_2) R3=L2F(K3,R2)

​ 我们对一组数据 ( P 1 , C 1 ) , ( P 1 ∗ , C 1 ∗ ) (P_1,C_1),(P_1^*,C_1^*) (P1,C1),(P1,C1),有如下关系式

R 3 = L 2 ⊕ F ( K 3 , R 2 ) = R 1 ⊕ F ( K 3 , R 2 ) = L 0 ⊕ F ( K 1 , R 0 ) ⊕ F ( K 3 , R 2 ) R_3 = L_2\oplus F(K_3,R_2)=R_1\oplus F(K_3,R_2)=L_0\oplus F(K_1,R_0)\oplus F(K_3,R_2) R3=L2F(K3,R2)=R1F(K3,R2)=L0F(K1,R0)F(K3,R2)

R 3 ∗ = L 2 ∗ ⊕ F ( K 3 , R 2 ∗ ) = R 1 ⊕ F ( K 3 , R 2 ∗ ) = L 0 ⊕ F ( K 1 , R 0 ∗ ) ⊕ F ( K 3 , R 2 ∗ ) R_3^* = L_2^*\oplus F(K_3,R_2^*)=R_1\oplus F(K_3,R_2^*)=L_0\oplus F(K_1,R_0^*)\oplus F(K_3,R_2^*) R3=L2F(K3,R2)=R1F(K3,R2)=L0F(K1,R0)F(K3,R2)

​ 我们可以得到差分 Δ R 3 = R 3 ⊕ R 3 ∗ \Delta R_3 = R_3\oplus R_3^* ΔR3=R3R3,化简可以得到如下表达式:
Δ R 3 = Δ L 0 ⊕ F ( K 1 , R 0 ) ⊕ F ( K 1 , R 0 ∗ ) ⊕ F ( K 3 , R 2 ) ⊕ F ( K 3 , R 2 ∗ ) \Delta R_3 = \Delta L_0\oplus F(K_1,R_0)\oplus F(K_1,R_0^*)\oplus F(K_3,R_2)\oplus F(K_3,R_2^*) ΔR3=ΔL0F(K1,R0)F(K1,R0)F(K3,R2)F(K3,R2)

​ 通过观察数据,我们看到 R 0 = R 0 ∗ R_0=R_0^* R0=R0,所以 F ( K 1 , R 0 ) ⊕ F ( K 1 , R 0 ∗ ) = 0 F(K_1,R_0)\oplus F(K_1,R_0^*)=0 F(K1,R0)F(K1,R0)=0,且 R 2 = L 3 R_2=L_3 R2=L3,

​ 所以有 Δ R 3 = Δ L 0 ⊕ F ( K 3 , L 3 ) ⊕ F ( K 3 , L 3 ∗ ) \Delta R_3 = \Delta L_0\oplus F(K_3,L_3)\oplus F(K_3,L_3^*) ΔR3=ΔL0F(K3,L3)F(K3,L3)

​ 我们知道 F ( K , R ) = P ( S ( E ( R ) ⊕ K ) ) F(K,R)=P(S(E(R)\oplus K)) F(K,R)=P(S(E(R)K)),且P盒是线性的,所以可以化简得到
S ( E ( L 3 ) ⊕ K 3 ) ⊕ S ( E ( L 3 ∗ ) ⊕ K 3 ) = P − 1 ( Δ R 3 ⊕ Δ L 0 ) S(E(L_3)\oplus K_3)\oplus S(E(L_3^*)\oplus K_3) = P^{-1}(\Delta R_3 \oplus \Delta L_0) S(E(L3)K3)S(E(L3)K3)=P1(ΔR3ΔL0)

​ 就出现了类似差分原理中讨论过的,S盒的差分输入输出的情况。注意,此时的 S ( ) S() S()并不是指6bit过S盒的过程,是指48bit的 E ( R ) ⊕ K E(R)\oplus K E(R)K整体过S盒后得到32bit的异或输出的函数方法。

​ 记 B = E ( L 3 ) ⊕ K 3 , B ∗ = E ( L 3 ∗ ) ⊕ K 3 , B ⊕ B ∗ = I N x o r , P − 1 ( Δ R 3 ⊕ Δ L 0 ) = O U T x o r B =E(L_3)\oplus K_3,B^* =E(L_3^*)\oplus K_3 ,B\oplus B^* = IN_{xor},P^{-1}(\Delta R_3 \oplus \Delta L_0)=OUT_{xor} B=E(L3)K3,B=E(L3)K3,BB=INxor,P1(ΔR3ΔL0)=OUTxor ,有 S ( B ) ⊕ S ( B ∗ ) = O U T x o r S(B)\oplus S(B^*)=OUT_{xor} S(B)S(B)=OUTxor

​ 而秘钥,就藏在 B B B B ∗ B^* B中。此时我们先转头去讨论一下DES的S盒的差分表。

​ DES的S盒共有8个,这里讨论第一个S盒。第一个S盒的输入是6bit的二进制数,所有输入共有64种情况,输出是4bit,共有16种情况。假设存在两组S盒的输入: B → S C , B ∗ → S C ∗ B\stackrel{S}{\to}C,B^*\stackrel{S}{\to}C^* BSC,BSC,记 I N x o r = B ⊕ B ∗ , O U T x o r = C ⊕ C ∗ IN_{xor}=B\oplus B^*,OUT_{xor}=C\oplus C^* INxor=BB,OUTxor=CC,我们计算此时S盒的差分分布表:

S x o r [ I N x o r ] [ O U T x o r ] = { ( B , B ∗ ) ∣ B ⊕ B ∗ = I N x o r , S [ B ] ⊕ S [ B ∗ ] = O U T x o r } S_{xor}[IN_{xor}][OUT_{xor}]=\{(B,B^*)|B\oplus B^*=IN_{xor},S[B]\oplus S[B^*]=OUT_{xor}\} Sxor[INxor][OUTxor]={(B,B)BB=INxor,S[B]S[B]=OUTxor}

​ 同理计算其他7个S盒,此时我们得到 S x o r [ 8 ] [ 64 ] [ 16 ] S_{xor}[8][64][16] Sxor[8][64][16] 的分布表,有些项可能是空项,但是为了使用下标检索方便,我们还是选择置空。

python实现求S盒异或分布表函数
#S盒的输入输出异或分布
def Sxor():
    # 每个S盒的输入输出异或分布,共八项,每项都是一个
    global Sxor_i_in_out 
    for i in range(8):
        for B in range(64):
            for B_ in range(64):
                inxor=B^B_
                outxor=S(i,B)^S(i,B_)
                Sxor_i_in_out[i][inxor][outxor].append(B)
                
(完整代码见附录)

结果示例:


print(Sxor_i_in_out[0][int('110011',2)][int('0110',2)])
#即打印出第一个S盒,所有B和B*,满足B⊕B* = 110011,S(B)⊕S(B*)=0110
#打印结果:[12, 30, 45, 63]

print(Sxor_i_in_out[1][int('000111',2)][int('0011',2)])
#即打印出第二个S盒,所有的B和B*,满足B⊕B* = 000111,S(B)⊕S(B*)=0011
#打印结果:[10, 13, 16, 23]

​ 得到了S盒的差分分布表后,我们既可以通过计算上述红色字体标注语句中的 I N x o r IN_{xor} INxor O U T x o r OUT_{xor} OUTxor,然后去 S x o r _ i _ i n _ o u t Sxor\_i\_in\_out Sxor_i_in_out 中检索所有可能的 ( B , B ∗ ) (B,B^*) (B,B),我们又知道 B = E ( L 3 ) ⊕ K 3 B=E(L_3)\oplus K_3 B=E(L3)K3,然后就用 E ( L 3 ) E(L_3) E(L3)去异或,所有找到的 { B , B ∗ . . . } \{B,B^*...\} {B,B...},的每一项,这样里面就有正确的秘钥了。

​ 不过我们知道对于一项 S x o r [ I N x o r ] [ O U T x o r ] S_{xor}[IN_{xor}][OUT_{xor}] Sxor[INxor][OUTxor],里面可能有不只一对的 ( B , B ∗ ) (B,B^*) (B,B),但是 K 3 K_3 K3只有一个,如何找到正确的 K 3 K_3 K3呢?此时就要用到其他的数据,把各个组的数据全都进行如上过程,每个S盒都可以得到很多的 K 3 K_3 K3比特的备选( K 3 K_3 K3在F函数中,也是分为6bit每组的,不过前文的讨论都是在第一个 S S S盒的基础上讨论的),不同的组得到的 K 2 K_2 K2比特会出现重复,如果有一项 K 3 K_3 K3比特,在每组数据中都出现了,在本题中就是出现了5次,就确定为秘钥。把8个 S S S盒的 K 3 K_3 K3比特拼接起来,就是我们要的48位的 K 3 K_3 K3了。

秘钥出现次数统计(加粗字体标注了出现了5次的秘钥可能):

S盒可能秘钥出现次数统计
S1{14: 1, 4: 1, 28: 1, 25: 1, 47: 5, 42: 1, 61: 1, 55: 2, 34: 1, 24: 1, 18: 1, 15: 1, 50: 1, 37: 1, 11: 1, 29: 1, 17: 1, 35: 1, 57: 1}
S2{38: 1, 51: 1, 5: 5, 16: 2, 48: 1, 53: 1, 42: 1, 47: 2, 31: 1, 20: 1, 1: 2, 56: 1, 45: 2, 43: 1, 54: 1, 28: 1, 46: 1, 6: 1}
S3{48: 1, 57: 1, 19: 5, 26: 2, 51: 2, 53: 1, 32: 1, 42: 2, 21: 1, 0: 1, 10: 1, 50: 2, 41: 1, 40: 1, 23: 2, 12: 1, 8: 1, 59: 1, 63: 1, 62: 1, 22: 1, 34: 1, 61: 1, 60: 1, 5: 1, 4: 1, 27: 1}
S4{50: 1, 0: 5, 47: 1, 46: 2, 45: 1, 44: 2, 3: 1, 2: 1, 1: 2, 60: 1, 61: 1, 52: 1, 53: 1, 8: 1, 9: 1, 48: 1, 51: 1, 56: 1, 59: 2, 33: 1, 39: 1, 42: 1, 17: 1, 26: 1, 11: 1, 62: 1, 43: 1, 16: 1, 21: 1, 5: 1}
S5{51: 1, 52: 1, 57: 2, 18: 1, 24: 5, 31: 1, 43: 1, 38: 2, 21: 1, 4: 1, 58: 2, 11: 1, 25: 1, 20: 1, 45: 1, 44: 1, 32: 1, 63: 1, 27: 1, 12: 1, 15: 1, 33: 1, 34: 1}
S6{36: 1, 54: 1, 50: 1, 62: 3, 5: 5, 13: 2, 9: 1, 31: 1, 57: 2, 53: 1, 52: 1, 32: 1, 27: 1, 28: 2, 17: 1, 16: 1, 11: 2, 51: 1, 61: 1, 2: 1, 1: 1, 7: 1, 10: 1, 39: 1, 49: 1, 48: 1, 58: 1, 60: 1}
S7{58: 2, 35: 2, 34: 3, 32: 1, 38: 2, 37: 2, 26: 2, 25: 1, 31: 1, 30: 1, 28: 1, 6: 5, 16: 3, 17: 1, 7: 2, 0: 2, 1: 1, 62: 1, 63: 1, 52: 2, 53: 1, 40: 1, 41: 1, 36: 1, 47: 1, 10: 2, 60: 1, 48: 1, 54: 1, 44: 1, 20: 1, 12: 1, 2: 1}
S8{62: 1, 63: 2, 48: 1, 49: 5, 46: 2, 47: 2, 32: 1, 33: 1, 28: 1, 31: 2, 16: 2, 19: 1, 14: 1, 1: 1, 45: 1, 61: 1, 55: 1, 7: 1, 5: 1, 25: 1, 23: 1, 21: 1, 34: 1, 0: 1, 3: 1, 24: 1, 26: 1, 41: 1, 42: 1, 57: 1, 51: 1}

​ 上表中每个S盒都有一个可能秘钥出现了5次,我们取这几个int值,转成6位二进制拼接后就得到了48位的轮秘钥 K 3 K_3 K3

python实现求每个S盒的可能秘钥:

def DES_Diff(PC1,PC2): # PC1=(P1,C1)
    #hexstr to binstr
    P1 = hex2bin(PC1[0].lower())        
    C1 = hex2bin(PC1[-1].lower())
    P2 = hex2bin(PC2[0].lower())
    C2 = hex2bin(PC2[-1].lower())

    L0 = P1[:32];     L0_ = P2[:32]
    L3 =  C1[:32];    R3 = C1[32:]
    L3_ = C2[:32];    R3_ = C2[32:]  

    #过E拓展,得到48bit的S盒预备输入
    E = translation(L3,E_box) #bin_str
    E_ = translation(L3_,E_box)

    OUTxor = translation(xor_bstr(xor_bstr(L0,L0_),xor_bstr(R3,R3_)),P_1) 
    INxor = xor_bstr(E,E_)
    
    #分组得到S盒的异或输入和输出
    En = [E[i:i+6] for i in range(0,48,6)]
    INxor_n = [INxor[i:i+6] for i in range(0,48,6)]
    OUTxor_n = [OUTxor[i:i+4] for i in range(0,32,4)]

    global diff_Key #共8项,每项中存的对应S盒的 E(L3) xor S_xor[INxor][OUTxor]
    for i in range(8):
        E_ten = int(En[i],2)
        for B in Sxor_i_in_out[i][int(INxor_n[i],2)][int(OUTxor_n[i],2)]:
            diff_Key[i].append(B^E_ten) 
            
#此处并没有对diff_key 做统计,diff_key 中只有不同的各项,还没有统计哪些是出现了5次的项,在另一个函数中才得到了48位的K3

(完整代码见附录)

​ 得到的48位的秘钥,是第三轮的轮秘钥,我们需要得到最终的64位秘钥还要经过穷举和检验等步骤。

48 b i t → 56 b i t 48bit\to56bit 48bit56bit

​ 1. 根据秘钥生成方案,我们需要先把48位的轮秘钥过 P C 2 − 1 PC2^{-1} PC21,得到一个临时的56位秘钥,未知的位可以用标志位填充一下;

​ 2. 然后分成左右两部分,分别逆左移回去,得到最初的56位秘钥。此时的8个未知位一共有 2 8 2^8 28种组合,穷举这64种可能,每次穷举得到56位秘钥值;

3. 用这56位秘钥,重新生成3轮的加密用秘钥,随便找一组明密文,把明文加密一遍,如果密文正确,说明此时检验的穷举密钥是正确的。

56 b i t → 64 b i t 56bit\to64bit 56bit64bit

​ 得到正确的56位秘钥后,只需要过一遍 P C 1 − 1 PC1^{-1} PC11矩阵,然后每8位补上奇或偶校验位就得到了64位的初始秘钥。

python得到64位秘钥函数

def get_key():
    #先确定秘钥的48位,
    key_choice = [{} for i in range(8)]
    for i in range(8):
        for key in diff_Key[i]:
            count = 0
            for other in diff_Key[i]:
                if other == key:
                    count = count + 1
            key_choice[i][key] = count
    round3_Key = ''
    for i in range(8):
        for k in key_choice[i].keys():
            if key_choice[i][k] == 5:
                round3_Key += int2bit6(k)
    #现在我们得到了48位的第三轮秘钥,通过秘钥生成方案,反向计算回去看看原始秘钥是什么
    print(round3_Key)
    print(hex(int(round3_Key,2)))
    
    temp_Key = ['2']*56
    #先过PC2的逆
    for i in range(48):
        temp_Key[K56_48[i]-1] = round3_Key[i]

    temp_Key = ''.join(temp_Key)

    temp_Key_l = temp_Key[:28]
    temp_Key_r = temp_Key[28:]

    shift = Kleft_shift[0] +  Kleft_shift[1] + Kleft_shift[2]
    Key56_l = temp_Key_l[-shift:] + temp_Key_l[:-shift]
    Key56_r = temp_Key_r[-shift:] + temp_Key_r[:-shift]
    temp_Key56 = Key56_l + Key56_r

    #穷举Key56的剩余位置,然后经过一次三轮加密,
    # 先找到需要猜测的秘钥位置,顺便初始化一下, 注意字符串是不可变的(不可以用下标索引来修改,但是可以下标访问)
    guess_Key = ['0']*56
    guess_loca = []
    for i in range(56):
        if temp_Key56[i] == '2':
            guess_loca.append(i)  #注意此处下标从0开始算的
        else:
            guess_Key[i] = temp_Key56[i]
    right_Key56 = ''
    for i in range(2**8):
        guess = '{:0>8}'.format(bin(i)[2:])
        for j in range(8):
            guess_Key[guess_loca[j]] = guess[j]
        test_Key = ''.join(guess_Key)
        # 接下来要用我们的test_Key去计算三轮秘钥,然后跑一次DES,如果可以把明文顺利加密为密文,就说明这个是对的秘钥
        if (round3_DES(P_C[0][0].lower(),test_Key)) == hex2bin(P_C[0][1].lower()):
            right_Key56 = test_Key
            break
    print('right_Key56',right_Key56)

    # 过PC1逆置换得到Key64的56个位置,剩余的位置是奇偶校验位,此处采取奇校验,就是数据每8位的最后一位作为校验位,用来保证这8位中,1的个数是奇数。
    temp_Key64 = ['1'] * 64 #默认全是0,就是说如果某7bit的和是odd number,就可以改为1,这里使用奇校验
    for i in range(56):
        temp_Key64[K64_56[i] - 1] = right_Key56[i]  # 注意下标减1
    for i in range(8):
        sum = 0
        for j in range(7):
            sum = (sum+int(temp_Key64[i*8+j],2))%2
        if not sum%2 == 0:
            temp_Key64[i*8-1] = '0'
    return ''.join(temp_Key64)

(完整代码见附录)

DES线性分析

线性分析原理

​ 线性密码分析的目的是在选择明文攻击中,找到一个密码算法明文密文的某些比特,和秘钥的某些比特的线性关系,例如如下线性表达式:

P [ i 1 , i 2 . . . ] ⊕ C [ j 1 , j 2 . . . ] = K [ k 1 , k 2 . . . ] P[i_1,i_2...]\oplus C[j_1,j_2...]=K[k_1,k_2...] P[i1,i2...]C[j1,j2...]=K[k1,k2...]

​ 要求对任意随机的明密文(P,C),上式成立的概率 p ≠ 1 2 p\neq \frac{1}{2} p=21,并且用 ∣ p − 1 2 ∣ |p-\frac{1}{2}| p21 来表示该表达式的有效性。对DES的线性分析是目前共计DES最有效的方式,这种方法可以用 1.49 ∗ 2 17 1.49*2^{17} 1.49217个已知 明密文对,破译8轮DES算法,可用 2 43 2^{43} 243个已知明密文破译16轮DES算法。(数据来源于网络)

S盒的线性分布:

定义:

∑ i = 0 n < α , x > = x [ α i ] ⊕ x [ α i ] ⊕ . . . \sum_{i=0}^n<\alpha,x>=x_{[\alpha_i]}\oplus x_{[\alpha_i]}\oplus... i=0n<α,x>=x[αi]x[αi]...

N S i ( α , β ) = ∣ { x ∣ 0 ≤ x ≤ 63 , ∑ i = 0 n < α , x > = ∑ i = 0 n < β , S i ( x ) > } ∣ NS_i(\alpha,\beta) = |\{x | 0 \le x \le 63,\sum_{i=0}^n <\alpha,x>=\sum_{i=0}^n <\beta,S_i(x)>\}| NSi(α,β)={x0x63,i=0n<α,x>=i=0n<β,Si(x)>}

其中,称 α \alpha α β \beta β 为掩码,在二进制层面比较好理解,假如 α = 0011 \alpha=0011 α=0011 x = 1110 x=1110 x=1110,那么 < α , x > = 1 ⊕ 0 = 1 <\alpha,x>=1\oplus 0=1 <α,x>=10=1

pythonS盒线性分布代码:

S_i_mask = [
            [
                [
                    -32 for beta in range(16)  #最后要减32,直接从-32开始计算
                ]for alpha in range(64)
            ]for i in range(8)
]

#返回自身逐位异或值, <α,x> = self_xor(α&x) ,先求与,再求结果的自身异或
def self_xor(num,wide):
    count=0
    for i in range(wide):
        if (num&1):
            count+=1
        num>>=1
    if(count%2==0):  #偶数个1,异或就是0,
        return 0
    return 1         #奇数个1,异或就是1,

def get_S_i_mask():
    for i in range(8):
        for alpha in range(64):
            for beta in range(16):
                for x in range(64):
                    if(self_xor(x & alpha,6)==self_xor(S(i,x) & beta,4)):
                        S_i_mask[i][alpha][beta] += 1
#运行结果:
#初S1分布:
						S1线性分布表(部分)
α\β		0	1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	

0		-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	

1		-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	

2		-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	

3		-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	

4		-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	

5		-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	

6		-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	

7		-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	

8		-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	-32	

			.............................
    
#计算后分布:
					S1线性分布表(部分)
α\β	0	1	2	3	4	5	6	7	8	9	10	11	12	13	14	15	

0		32	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	

1		0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	0	

2		0	-2	-2	-4	-2	0	-4	6	2	0	0	6	4	-2	-6	4	

3		0	-2	-2	-4	-2	0	-4	6	2	8	0	-2	4	6	-6	-4	

4		0	2	-2	-4	-2	0	-4	-6	-2	4	8	2	0	-2	-6	12	

5		0	-2	-2	0	-2	-4	-4	-2	2	-4	-4	2	4	-10	-2	-4	

6		0	0	0	4	0	4	0	0	0	-4	4	4	0	0	-4	-8	

7		0	-4	0	8	0	0	0	4	4	-4	-8	-4	4	0	0	0	

8		0	4	-2	6	-6	-6	0	-4	-4	-4	2	-2	2	-2	0	0	

9		0	0	6	-6	-2	-6	4	-4	0	-4	-2	6	2	-6	0	-4	

10		0	-2	0	2	0	6	8	2	-2	0	-2	4	-2	0	-2	4	

11		0	2	-8	-2	-4	-10	4	2	-6	8	2	4	-2	-4	-2	0	

12		0	-2	0	6	0	2	0	2	2	0	6	-4	2	-4	6	0	

13		0	6	0	6	4	-2	-4	-2	2	0	6	4	-2	8	-6	-4	

14		0	0	-2	-2	2	2	0	0	4	4	6	-2	2	2	-4	4	

15		0	0	-2	6	-2	-2	4	-4	-4	-4	-2	-2	-2	-2	0	0	

16		0	2	2	0	-2	0	4	-6	0	6	2	-4	6	-4	-4	-18	
	
   		 .....................................
        
(完整代码见附录)

​ 例如上述结果中的 N S 1 ( 16 , 15 ) = − 18 + 32 = 14 NS_1(16,15)=-18+32=14 NS1(16,15)=18+32=14,就意味着, S 1 S_1 S1盒的6bit输入从左数第2位(掩码是16 = 010000),和其输出的4bit所有位(掩码是15 =1111)的异或值,它们相等的概率是 14 64 \frac{14}{64} 6414

DES线性分析实现

​ 根据三轮的加密方法,通过手动绘图,我们细节研究一下具体S1盒的输入输出:

三轮加密:

N S 1 ( 16 , 15 ) = 12 NS_1(16,15)=12 NS1(16,15)=12下,讨论 S 1 S_1 S1的输入输出,具体流程如下图:

可以写出公式:
< α , E ( R 0 ) ⊕ K 1 > = < β , S 1 ( E ( R 0 ) ⊕ K 1 ) > ( α = 16 , β = 15 ) <\alpha,E(R_0)\oplus K_1> = <\beta,S_1(E(R_0)\oplus K_1)> (\alpha = 16,\beta = 15) <α,E(R0)K1>=<β,S1(E(R0)K1)>(α=16,β=15)

( E ( R 0 ) ⊕ K 1 ) [ 46 ] = S 1 ( E ( R 0 ) ⊕ K 1 ) [ 0 , 1 , 2 , 3 ] = F ( R 0 , K 1 ) [ 23 , 15 , 9 , 1 ] (E(R_0)\oplus K_1)[46] = S_1(E(R_0)\oplus K_1)[0,1,2,3]=F(R_0,K_1)[23,15,9,1] (E(R0)K1)[46]=S1(E(R0)K1)[0,1,2,3]=F(R0,K1)[23,15,9,1]

找到 S 1 S_1 S1的具体输入输出的上一级和下一级,其中 R 2 = L 0 ⊕ F ( R 0 , K 1 ) R2 =L_0\oplus F(R_0,K_1) R2=L0F(R0,K1),一步步往下化简:
R 0 [ 31 ] ⊕ K 1 [ 46 ] = F ( R 0 , K 1 ) [ 23 , 15 , 9 , 1 ] = ( R 2 ⊕ L 0 ) [ 23 , 15 , 9 , 1 ] R_0[31]\oplus K_1[46] = F(R_0,K_1)[23,15,9,1] = (R_2\oplus L_0)[23,15,9,1] R0[31]K1[46]=F(R0K1)[23,15,9,1]=(R2L0)[23,15,9,1]

⟹ R 0 [ 31 ] ⊕ R 2 [ 23 , 15 , 9 , 1 ] ⊕ L 0 [ 23 , 15 , 9 , 1 ] = K 1 [ 46 ] \Longrightarrow R_0[31]\oplus R_2[23,15,9,1]\oplus L_0[23,15,9,1] = K_1[46] R0[31]R2[23,15,9,1]L0[23,15,9,1]=K1[46]

这是有关第一轮加密的,对于第三轮加密我们也可以得到格式一致的式子:
R 2 [ 31 ] ⊕ K 3 [ 46 ] = F ( R 2 , K 3 ) [ 23 , 15 , 9 , 1 ] = ( R 2 ⊕ L 3 ) [ 23 , 15 , 9 , 1 ] R_2[31]\oplus K_3[46] = F(R_2,K_3)[23,15,9,1] = (R_2\oplus L_3)[23,15,9,1] R2[31]K3[46]=F(R2K3)[23,15,9,1]=(R2L3)[23,15,9,1]

⟹ R 0 [ 31 ] ⊕ R 2 [ 23 , 15 , 9 , 1 ] ⊕ L 0 [ 23 , 15 , 9 , 1 ] = K 1 [ 46 ] \Longrightarrow R_0[31]\oplus R_2[23,15,9,1]\oplus L_0[23,15,9,1] = K_1[46] R0[31]R2[23,15,9,1]L0[23,15,9,1]=K1[46]

将二者异或, R 2 R_2 R2项就被消掉了,并且利用明密文对 R 0 R_0 R0等替代表示:

P L [ 31 ] ⊕ P H [ 1 , 9 , 15 , 23 ] ⊕ C H [ 1 , 9 , 15 , 23 ] ⊕ C L [ 31 ] = K 1 [ 46 ] ⊕ K 3 [ 46 ] P_{L}[31]\oplus P_H[1,9,15,23]\oplus C_H[1,9,15,23]\oplus C_L[31]=K_1[46]\oplus K_3[46] PL[31]PH[1,9,15,23]CH[1,9,15,23]CL[31]=K1[46]K3[46]

这是S1盒的最佳线性逼近式,同理我们可以用程序快速求出8个盒子的线性逼近式。

各S盒最佳线性逼近式:

S盒 α , β \alpha,\beta α,β线性逼近式
S1 α = 16 , β = 15 α=16,β=15 α=16,β=15 P L [ 31 ] ⊕ C L [ 31 ] ⊕ P H [ 1 , 9 , 15 , 23 ] ⊕ C H [ 1 , 9 , 15 , 23 ] = K 1 [ 46 ] ⊕ K 3 [ 46 ] P_{L}[31]\oplus C_L[31]\oplus P_H[1,9,15,23]\oplus C_H[1,9,15,23]=K_1[46]\oplus K_3[46] PL[31]CL[31]PH[1,9,15,23]CH[1,9,15,23]=K1[46]K3[46]
S2 α = 34 , β = 11 α=34,β=11 α=34,β=11 P L [ 28 , 24 ] ⊕ C L [ 28 , 24 ] ⊕ P H [ 19 , 30 , 14 ] ⊕ C H [ 19 , 30 , 14 ] = K 1 [ 41 , 37 ] ⊕ K 3 [ 41 , 37 ] P_{L}[28,24]\oplus C_L[28,24]\oplus P_H[19,30,14]\oplus C_H[19,30,14]=K_1[41,37]\oplus K_3[41,37] PL[28,24]CL[28,24]PH[19,30,14]CH[19,30,14]=K1[41,37]K3[41,37]
S3 α = 34 , β = 15 α=34,β=15 α=34,β=15 P L [ 24 , 20 ] ⊕ C L [ 24 , 20 ] ⊕ P H [ 8 , 16 , 2 , 26 ] ⊕ C H [ 8 , 16 , 2 , 26 ] = K 1 [ 35 , 31 ] ⊕ K 3 [ 35 , 31 ] P_{L}[24,20]\oplus C_L[24,20]\oplus P_H[8,16,2,26]\oplus C_H[8,16,2,26]=K_1[35,31]\oplus K_3[35,31] PL[24,20]CL[24,20]PH[8,16,2,26]CH[8,16,2,26]=K1[35,31]K3[35,31]
S4 α = 34 , β = 15 α=34,β=15 α=34,β=15 α = 40 , β = 15 α=40,β=15 α=40,β=15 α = 43 , β = 9 α=43,β=9 α=43,β=9 P L [ 20 , 16 ] ⊕ C L [ 20 , 16 ] ⊕ P H [ 6 , 12 , 22 , 31 ] ⊕ C H [ 6 , 12 , 22 , 31 ] = K 1 [ 29 , 25 ] ⊕ K 3 [ 29 , 25 ] P_L[20, 16]\oplus C_L[20, 16]\oplus P_H[6, 12, 22, 31]\oplus C_H[6, 12, 22, 31]=K_1[29, 25]\oplus K_3[29, 25] PL[20,16]CL[20,16]PH[6,12,22,31]CH[6,12,22,31]=K1[29,25]K3[29,25] P L [ 20 , 18 ] ⊕ C L [ 20 , 18 ] ⊕ P H [ 6 , 12 , 22 , 31 ] ⊕ C H [ 6 , 12 , 22 , 31 ] = K 1 [ 29 , 27 ] ⊕ K 3 [ 29 , 27 ] P_L[20, 18]\oplus C_L[20, 18]\oplus P_H[6, 12, 22, 31]\oplus C_H[6, 12, 22, 31]=K_1[29, 27]\oplus K_3[29, 27] PL[20,18]CL[20,18]PH[6,12,22,31]CH[6,12,22,31]=K1[29,27]K3[29,27] P L [ 20 , 18 , 16 , 15 ] ⊕ C L [ 20 , 18 , 16 , 15 ] ⊕ P H [ 6 , 31 ] ⊕ C H [ 6 , 31 ] = K 1 [ 29 , 27 , 25 , 24 ] ⊕ K 3 [ 29 , 27 , 25 , 24 ] P_L[20, 18, 16, 15]\oplus C_L[20, 18, 16, 15]\oplus P_H[6, 31]\oplus C_H[6, 31]=K_1[29, 27, 25, 24]\oplus K_3[29, 27, 25, 24] PL[20,18,16,15]CL[20,18,16,15]PH[6,31]CH[6,31]=K1[29,27,25,24]K3[29,27,25,24]
S5 α = 16 , β = 15 α=16,β=15 α=16,β=15 P L [ 15 ] ⊕ C L [ 15 ] ⊕ P H [ 24 , 18 , 7 , 29 ] ⊕ C H [ 24 , 18 , 7 , 29 ] = K 1 [ 22 ] ⊕ K 3 [ 22 ] P_L[15]\oplus C_L[15]\oplus P_H[24, 18, 7, 29]\oplus C_H[24, 18, 7, 29]=K_1[22]\oplus K_3[22] PL[15]CL[15]PH[24,18,7,29]CH[24,18,7,29]=K1[22]K3[22]
S6 α = 16 , β = 7 α=16,β=7 α=16,β=7 P L [ 11 ] ⊕ C L [ 11 ] ⊕ P H [ 3 , 21 , 13 ] ⊕ C H [ 3 , 21 , 13 ] = K 1 [ 16 ] ⊕ K 3 [ 16 ] P_L[11]\oplus C_L[11]\oplus P_H[3, 21, 13]\oplus C_H[3, 21, 13]=K_1[16]\oplus K_3[16] PL[11]CL[11]PH[3,21,13]CH[3,21,13]=K1[16]K3[16]
S7 α = 59 , β = 4 α=59,β=4 α=59,β=4 P L [ 8 , 7 , 6 , 4 , 3 ] ⊕ C L [ 8 , 7 , 6 , 4 , 3 ] ⊕ P H [ 20 ] ⊕ C H [ 20 ] = K 1 [ 11 , 10 , 9 , 7 , 6 ] ⊕ K 3 [ 11 , 10 , 9 , 7 , 6 ] P_L[8, 7, 6, 4, 3]\oplus C_L[8, 7, 6, 4, 3]\oplus P_H[20]\oplus C_H[20]=K_1[11, 10, 9, 7, 6]\oplus K_3[11, 10, 9, 7, 6] PL[8,7,6,4,3]CL[8,7,6,4,3]PH[20]CH[20]=K1[11,10,9,7,6]K3[11,10,9,7,6]
S8 α = 16 , β = 15 α=16,β=15 α=16,β=15 P L [ 3 ] ⊕ C L [ 3 ] ⊕ P H [ 27 , 5 , 17 , 11 ] ⊕ C H [ 27 , 5 , 17 , 11 ] = K 1 [ 4 ] ⊕ K 3 [ 4 ] P_L[3]\oplus C_L[3]\oplus P_H[27, 5, 17, 11]\oplus C_H[27, 5, 17, 11]=K_1[4]\oplus K_3[4] PL[3]CL[3]PH[27,5,17,11]CH[27,5,17,11]=K1[4]K3[4]

​ 注意到,在所有的 ∣ N S i ( α , β ) − 32 ∣ |NS_i(\alpha,\beta)-32| NSi(α,β)32中,最大的是 S 5 S_5 S5的,即 ∣ N S 5 ( 16 , 15 ) − 32 ∣ = 20 |NS_5(16,15)-32|=20 NS5(16,15)32=20,且 S 4 S_4 S4有三个相同概率的最佳线性逼近式。

思考与总结:

​ DES 差分分析,作为一种选择明文攻击,其基本思想是通过分析特定明文差分对相对应密文差分影响,来获得尽可能多的密钥比特。差分分析的关键就在于找到非线性组件中的高概率差分路径。差分分析以一定概率,通过中间状态S盒的输入输入,计算可能的秘钥。 差分过程中,主要分为离线建表和在线分析两个时间,由于我们是选择明文攻击,所以可以获得想要的高概率差分路径,就像数据中所给的 R 0 = R 0 ∗ R_0=R_0^* R0=R0一样,而在更多的轮数中,概率相乘也会下降。

​ 差分攻击的效率问题:如果 DES 只是使用 3 轮的话,则在个人计算机 上很快就可以破译。但要是在完全的 16 轮情况下,差分分析仅比穷尽密钥13搜索稍微有效。然而如果增加到 17 或者 18 轮,则差分分析和穷尽密钥搜索攻击花费同样的时间。如果再把轮数增加到 19 轮的话,则用穷尽搜索攻击比差分分析更容易了。

​ DES 线性分析,主要思路是:“找到一个密码算法明文密文的某些比特,和秘钥的某些比特的线性关系”, 如果加密算法具有足够的随机性, 则任意的明密文和秘钥的关系,都会满足一个 1 2 \frac{1}{2} 21的概率,如果我们能在S盒中的差分分布中(横纵坐标是掩码,就是有关S盒的掩码和输出输出之间的关系),找到偏离 1/2 很多的掩码关系, 就可以获得一些与密钥有关的信息。对于DES的差分,要用好上下文相等的关系,有利于化简替代等式,缩小未知量的范围。

附录

DES加解密代码:

################################################################
#                       DES加解密,                              #
################################################################
import time
hex_bin_box = {
        '0': '0000', '1': '0001', '2': '0010', '3': '0011',
        '4': '0100', '5': '0101', '6': '0110', '7': '0111',
        '8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
        'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111',
}
hex2bin = lambda hexstr: ''.join(hex_bin_box[i] for i in hexstr)             #用于16进制字符串转二进制字符串
# 矩阵转换lambda表达式
translation = lambda item, box: ''.join(item[i - 1] for i in box)

#秘钥的PC1矩阵
K64_56 = [
                57, 49, 41, 33, 25, 17, 9,
                1, 58, 50, 42, 34, 26, 18,
                10, 2, 59, 51, 43, 35, 27,
                19, 11, 3, 60, 52, 44, 36,
                63, 55, 47, 39, 31, 23, 15,
                7, 62, 54, 46, 38, 30, 22,
                14, 6, 61, 53, 45, 37, 29,
                21, 13, 5, 28, 20, 12, 4,
        ]

#秘钥的PC2矩阵
K56_48 = [
        14, 17, 11, 24, 1, 5, 3, 28,
        15, 6, 21, 10, 23, 19, 12, 4,
        26, 8, 16, 7, 27, 20, 13, 2,
        41, 52, 31, 37, 47, 55, 30, 40,
        51, 45, 33, 48, 44, 49, 39, 56,
        34, 53, 46, 42, 50, 36, 29, 32
]

Kleft_shift = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]

#获取轮秘钥
def get_Kn(Key):# 生成Ki(共16轮密钥)
        K64 = hex2bin(Key)  #转bit串
        K56 = translation(K64, K64_56)
        Cn = [K56[:28]]
        Dn = [K56[28:]]
        for i in range(16):
                Cn.append(Cn[-1][Kleft_shift[i]:] + Cn[-1][:Kleft_shift[i]])
        for i in range(16):
                Dn.append(Dn[-1][Kleft_shift[i]:] + Dn[-1][:Kleft_shift[i]])
        Kn = [translation(Cn[i] + Dn[i], K56_48) for i in range(1, 17)]
        return Kn

##处理输入,输入的字符串转ascii(0x),再分组,每16位0x一组,不足用0补齐
def divide(P):
        l = len(P)
        Pn = []
        while not l % 16 == 0:
                P = P + '0'
                l = len(P)
        else:
                for i in range(0, l, 16):
                        Pn.append(P[i:i + 16])
        return Pn

#E扩展
E_box = [
        32,1,2,3,4,5,
        4,5,6,7,8,9,
        8,9,10,11,12,13,
        12,13,14,15,16,17,
        16,17,18,19,20,21,
        20,21,22,23,24,25,
        24,25,26,27,28,29,
        28,29,30,31,32,1,
]

S_box = [
        [
                0xe, 0x4, 0xd, 0x1, 0x2, 0xf, 0xb, 0x8, 0x3, 0xa, 0x6, 0xc, 0x5, 0x9, 0x0, 0x7,
                0x0, 0xf, 0x7, 0x4, 0xe, 0x2, 0xd, 0x1, 0xa, 0x6, 0xc, 0xb, 0x9, 0x5, 0x3, 0x8,
                0x4, 0x1, 0xe, 0x8, 0xd, 0x6, 0x2, 0xb, 0xf, 0xc, 0x9, 0x7, 0x3, 0xa, 0x5, 0x0,
                0xf, 0xc, 0x8, 0x2, 0x4, 0x9, 0x1, 0x7, 0x5, 0xb, 0x3, 0xe, 0xa, 0x0, 0x6, 0xd,
        ],
        [
                0xf, 0x1, 0x8, 0xe, 0x6, 0xb, 0x3, 0x4, 0x9, 0x7, 0x2, 0xd, 0xc, 0x0, 0x5, 0xa,
                0x3, 0xd, 0x4, 0x7, 0xf, 0x2, 0x8, 0xe, 0xc, 0x0, 0x1, 0xa, 0x6, 0x9, 0xb, 0x5,
                0x0, 0xe, 0x7, 0xb, 0xa, 0x4, 0xd, 0x1, 0x5, 0x8, 0xc, 0x6, 0x9, 0x3, 0x2, 0xf,
                0xd, 0x8, 0xa, 0x1, 0x3, 0xf, 0x4, 0x2, 0xb, 0x6, 0x7, 0xc, 0x0, 0x5, 0xe, 0x9,
        ],
        [
                0xa, 0x0, 0x9, 0xe, 0x6, 0x3, 0xf, 0x5, 0x1, 0xd, 0xc, 0x7, 0xb, 0x4, 0x2, 0x8,
                0xd, 0x7, 0x0, 0x9, 0x3, 0x4, 0x6, 0xa, 0x2, 0x8, 0x5, 0xe, 0xc, 0xb, 0xf, 0x1,
                0xd, 0x6, 0x4, 0x9, 0x8, 0xf, 0x3, 0x0, 0xb, 0x1, 0x2, 0xc, 0x5, 0xa, 0xe, 0x7,
                0x1, 0xa, 0xd, 0x0, 0x6, 0x9, 0x8, 0x7, 0x4, 0xf, 0xe, 0x3, 0xb, 0x5, 0x2, 0xc,
        ],
        [
                0x7, 0xd, 0xe, 0x3, 0x0, 0x6, 0x9, 0xa, 0x1, 0x2, 0x8, 0x5, 0xb, 0xc, 0x4, 0xf,
                0xd, 0x8, 0xb, 0x5, 0x6, 0xf, 0x0, 0x3, 0x4, 0x7, 0x2, 0xc, 0x1, 0xa, 0xe, 0x9,
                0xa, 0x6, 0x9, 0x0, 0xc, 0xb, 0x7, 0xd, 0xf, 0x1, 0x3, 0xe, 0x5, 0x2, 0x8, 0x4,
                0x3, 0xf, 0x0, 0x6, 0xa, 0x1, 0xd, 0x8, 0x9, 0x4, 0x5, 0xb, 0xc, 0x7, 0x2, 0xe,
        ],
        [
                0x2, 0xc, 0x4, 0x1, 0x7, 0xa, 0xb, 0x6, 0x8, 0x5, 0x3, 0xf, 0xd, 0x0, 0xe, 0x9,
                0xe, 0xb, 0x2, 0xc, 0x4, 0x7, 0xd, 0x1, 0x5, 0x0, 0xf, 0xa, 0x3, 0x9, 0x8, 0x6,
                0x4, 0x2, 0x1, 0xb, 0xa, 0xd, 0x7, 0x8, 0xf, 0x9, 0xc, 0x5, 0x6, 0x3, 0x0, 0xe,
                0xb, 0x8, 0xc, 0x7, 0x1, 0xe, 0x2, 0xd, 0x6, 0xf, 0x0, 0x9, 0xa, 0x4, 0x5, 0x3,
        ],
        [
                0xc, 0x1, 0xa, 0xf, 0x9, 0x2, 0x6, 0x8, 0x0, 0xd, 0x3, 0x4, 0xe, 0x7, 0x5, 0xb,
                0xa, 0xf, 0x4, 0x2, 0x7, 0xc, 0x9, 0x5, 0x6, 0x1, 0xd, 0xe, 0x0, 0xb, 0x3, 0x8,
                0x9, 0xe, 0xf, 0x5, 0x2, 0x8, 0xc, 0x3, 0x7, 0x0, 0x4, 0xa, 0x1, 0xd, 0xb, 0x6,
                0x4, 0x3, 0x2, 0xc, 0x9, 0x5, 0xf, 0xa, 0xb, 0xe, 0x1, 0x7, 0x6, 0x0, 0x8, 0xd,
        ],
        [
                0x4, 0xb, 0x2, 0xe, 0xf, 0x0, 0x8, 0xd, 0x3, 0xc, 0x9, 0x7, 0x5, 0xa, 0x6, 0x1,
                0xd, 0x0, 0xb, 0x7, 0x4, 0x9, 0x1, 0xa, 0xe, 0x3, 0x5, 0xc, 0x2, 0xf, 0x8, 0x6,
                0x1, 0x4, 0xb, 0xd, 0xc, 0x3, 0x7, 0xe, 0xa, 0xf, 0x6, 0x8, 0x0, 0x5, 0x9, 0x2,
                0x6, 0xb, 0xd, 0x8, 0x1, 0x4, 0xa, 0x7, 0x9, 0x5, 0x0, 0xf, 0xe, 0x2, 0x3, 0xc,
        ],
        [
                0xd, 0x2, 0x8, 0x4, 0x6, 0xf, 0xb, 0x1, 0xa, 0x9, 0x3, 0xe, 0x5, 0x0, 0xc, 0x7,
                0x1, 0xf, 0xd, 0x8, 0xa, 0x3, 0x7, 0x4, 0xc, 0x5, 0x6, 0xb, 0x0, 0xe, 0x9, 0x2,
                0x7, 0xb, 0x4, 0x1, 0x9, 0xc, 0xe, 0x2, 0x0, 0x6, 0xa, 0xd, 0xf, 0x3, 0x5, 0x8,
                0x2, 0x1, 0xe, 0x7, 0x4, 0xa, 0x8, 0xd, 0xf, 0xc, 0x9, 0x0, 0x3, 0x5, 0x6, 0xb,
        ],
]

# P盒
P_box = [
        16, 7,20,21,29,12,28,17,
        1 ,15,23,26, 5,18,31,10,
        2 ,8 ,24,14,32,27, 3, 9,
        19,13,30, 6,22,11, 4,25,
]

#F函数
def F(R,Ki): #R是32bits,Ki是48bits,
        R = translation(R, E_box)      #对R进行E拓展
        R = ''.join('0' if R[i]==Ki[i] else '1' for i in range(48))  #对R和Ki进行异或
        # 确定过S盒的坐标
        ij = [R[i:i+6] for i in range(0,48,6)]     # 按6bit分组
        S_box_loca = [int(ij[n][0]+ij[n][-1],2) * 16 + int(ij[n][1:-1],2) for n in range(8)] # 首尾拼接做行,中间四位做列,这里的行列不用减1,因为4行16列,已经包括0的可能性了
        # print(S_box_loca)
        R = ''.join(hex_bin_box[hex((S_box[n][S_box_loca[n]]))[-1]] for n in range(8))
        # 过P盒
        R = translation(R,P_box)
        return R

def festial(l,r,Ki):
        l_n = r
        #print(l_n)
        r_n = F(l_n,Ki)
        r_n = ''.join('0'if l[i] ==r_n[i] else '1' for i in range(32))

        return l_n,r_n

#IP置换
IP = [
        58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
        62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
        57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
        61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7
]
#IP逆置换
IP_1=[
        40,8,48,16,56,24,64,32,39,7,47,15,55,23,63,31,
        38,6,46,14,54,22,62,30,37,5,45,13,53,21,61,29,
        36,4,44,12,52,20,60,28,35,3,43,11,51,19,59,27,
        34,2,42,10,50,18,58,26,33,1,41, 9,49,17,57,25
]

def Enc_Dec(P,Key,choice):
        Pn = divide(P)
        result=''
        Kn = get_Kn(Key)
        if choice=='2':
                Kn.reverse()
        for p in Pn:
                p = ''.join(hex_bin_box[i] for i in P)      #转为二进制     
                p = translation(p, IP)                  #IP置换   
                l = p[0:32]
                r = p[32:]
                for i in range(16):
                        l,r = festial(l,r,Kn[i])

                l,r = r,l
                result = result + l+r
        result = translation(result,IP_1)               #IP_1 置换
        return result

if __name__ == '__main__':
        choice = input("Encrypt to 1,Decrypt to 2:")
        if choice == '1':
                #P = str(input("请输入要加密的字符串:").encode('ascii').hex())  # 明文P
                #P = input("请输入要加密的16进制串:")
                #Key = input("请输入秘钥:")  # 密钥Key(64bits)
                P = '02468aceeca86420'
                Key = '0f1571c947d9e859'
                while not len(Key) == 16:
                        Key = input("请输入秘钥:")  # 密钥Key(64bits)
                start = time.time()
                for i in range(10):
                        M = Enc_Dec(P,Key,choice)
                print("10 times average it costs {}s ".format((time.time()-start)/10))
                #print(M)
                s = ''.join(hex(int(M[i:i+4],2))[-1]for i in range(0,64,4))
                print("{} 经过秘钥 {} 加密得到:{}".format(P,Key,s))

        elif choice == '2':
                #M = input("请输入要解密的16进制串:")  # 密文M
                # Key = input("请输入秘钥:")  # 密钥Key(64bits)
                M = 'da02ce3a89ecac3b'
                Key = '0f1571c947d9e859'
                while not len(Key) == 16:
                        Key = input("请输入秘钥:")  # 密钥Key(64bits)
                P = Enc_Dec(M,Key,choice)
                s = ''.join(hex(int(P[i:i + 4], 2))[-1] for i in range(0, 64, 4))
                print("解密得到:", s)

DES差分分析

################################################################
#                       DES三轮差分分析                          #
################################################################
from DES import *

P_C=[] #存放输入5组的明密文对,是16进制的
#S盒输入输出异或分布S[8][64][16]=[int(11..101),...]
Sxor_i_in_out = [
                    [
                        [
                            [] for outxor in range(16)  # 然后是输出异或 ,最内层存的是B和B_,满足BxorB_
                        ] for inxor in range(64)  # 然后是输入异或
                    ] for i in range(8)  # 最外层是8个S盒
]
#P_1   P逆盒
P_1 =[
      9, 17, 23, 31, 13, 28, 2, 18,
      24, 16, 30, 6, 26, 20, 10, 1,
      8, 14, 25, 3, 4, 29, 11, 19,
      32, 12, 22, 7, 5, 27, 15, 21
]
diff_Key = [[]for i in range(8)]
int2bit6 = lambda num: '{:0>6}'.format(bin(num)[2:])    #用于10进制数字转二进制字符串
int2bit4 = lambda num: '{:0>4}'.format(bin(num)[2:])
xor_bstr = lambda a,b: hex2bin(hex(int(a, 2) ^ int(b, 2))[2:])  #两个二进制字符串异或,返回二进制字符串异或值

#先写明白差分的基础模型
def ChiperOne(P1,C1,P2,C2):
    sbox =  ['6',  '4', 'c', '5', '0', '7', '2', 'e', '1', 'f', '3', 'd', '8', 'a', '9', 'b']
    sbox_1 = ['4', '8' ,'6' ,'a', '1', '3', '0', '5', 'c','e','d', 'f', '2', 'b', '7', '9']
    p1 = hex2bin(P1)
    p2 = hex2bin(P2)
    p1p2 = xor_bstr(p1,p2)
    c1 = hex2bin(C1)
    c2 = hex2bin(C2)
    k2=[]
    # 穷举密钥k2,找到 让式子 p1p2 = S^-1[k2 xor c1] xor S^-1[k2 xor c2]成立的k2
    for i in range(15):
        k = hex_bin_box[hex(i)[-1]]
        # 为了异或和S盒,需要在int和hex和bstr间来回转换,不过为了看清楚逻辑,就先不考虑代码了
        if int(p1p2,2) == int(sbox_1[int(xor_bstr(k,c1),2)],16)^int(sbox_1[int(xor_bstr(k,c2),2)],16):
            k2.append(k)
    k1=[]
    for k in k2:
        k1.append(xor_bstr(hex2bin(sbox_1[int(xor_bstr(k, c1), 2)]), p1))
    return k1,k2
def ChiperTwo(P1,C1,P2,C2): #使用异或可以不转成2进制串,操作16进制就行了
    #关键:S盒的输出异或,如果输入异或是f,那么输出异或是d的概率是10/16,选择密文攻击情况下,取密文c1c2 = f,
    p1p2 = int(P1,16)^int(P2,16) #完全可以用int

    for i in range(15):
        count = 0
    pass

#将输入的int过 S 盒,再转为int,返回int
def S(i,IN):
    bit6 = int2bit6(IN)
    location = int(bit6[0]+bit6[-1],2)*16 + int(bit6[1:-1],2)
    return int(S_box[i][location])

#S盒的输入输出异或分布
def get_Sxor_i_in_out():
    # 每个S盒的输入输出异或分布,共八项,每项都是一个
    global Sxor_i_in_out
    for i in range(8):
        for B in range(64):
            for B_ in range(64):
                inxor=B^B_
                outxor=S(i,B)^S(i,B_)
                Sxor_i_in_out[i][inxor][outxor].append(B)

def DES_Diff(PC1,PC2): # PC1=(P1,C1)

    #hexstr to binstr
    P1 = hex2bin(PC1[0].lower())        ### 出错位置:第一项索引是0,最后一项是-1,无语子
    C1 = hex2bin(PC1[-1].lower())
    P2 = hex2bin(PC2[0].lower())
    C2 = hex2bin(PC2[-1].lower())

    # 以下都是bin_str
    L0 = P1[:32];     L0_ = P2[:32]

    L3 =  C1[:32];    R3 = C1[32:]
    L3_ = C2[:32];    R3_ = C2[32:]       ### 出错位置:尽量不要复制粘贴写代码,浪费我一上午!!!!

    #过E拓展,得到48bit的S盒预备输入
    E = translation(L3,E_box) #bin_str
    E_ = translation(L3_,E_box)

    OUTxor = translation(xor_bstr(xor_bstr(L0,L0_),xor_bstr(R3,R3_)),P_1) #deltaC = P_1(deltaL0^deltaR3)
    INxor = xor_bstr(E,E_)

    #分组得到S盒的异或输入和输出
    En = [E[i:i+6] for i in range(0,48,6)]
    INxor_n = [INxor[i:i+6] for i in range(0,48,6)]
    OUTxor_n = [OUTxor[i:i+4] for i in range(0,32,4)]

    global diff_Key
    for i in range(8):
        E_ten = int(En[i],2)
        for B in Sxor_i_in_out[i][int(INxor_n[i],2)][int(OUTxor_n[i],2)]:
            diff_Key[i].append(B^E_ten)      #Sxor_i_in_out存的是int,最多的就是秘钥K3

def round3_DES(P,test_Key):
    #拿到的test_Key是56位的,P是不需要过IP置换的,最后也不需要过IP逆置换
    # 获取三轮轮秘钥 Kn,没有用global关键字,应该没有问题,如果有问题回来看一下
    Cn = [test_Key[:28]]
    Dn = [test_Key[28:]]
    for i in range(3):
        Cn.append(Cn[-1][Kleft_shift[i]:] + Cn[-1][:Kleft_shift[i]]) #注意轮秘钥K1的下标是1
        Dn.append(Dn[-1][Kleft_shift[i]:] + Dn[-1][:Kleft_shift[i]])
    Kn = [translation(Cn[i] + Dn[i], K56_48) for i in range(1, 4)]

    # 开始加密过程
    p = ''.join(hex_bin_box[i] for i in P)
    l = p[0:32]
    r = p[32:]
    for i in range(3):
        l, r = festial(l, r, Kn[i])
    return l + r

def get_key():
    #先确定秘钥的48位,
    key_choice = [{} for i in range(8)]
    for i in range(8):
        for key in diff_Key[i]:
            count = 0
            for other in diff_Key[i]:
                if other == key:
                    count = count + 1
            key_choice[i][key] = count
    round3_Key = ''
    for i in range(8):
        for k in key_choice[i].keys():
            if key_choice[i][k] == 5:
                round3_Key += int2bit6(k)
    #现在我们得到了48位的第三轮秘钥,通过秘钥生成方案,反向计算回去看看原始秘钥是什么
    print('round3_Key',round3_Key)
    
    temp_Key = ['2']*56
    #先过PC2的逆
    for i in range(48):
        temp_Key[K56_48[i]-1] = round3_Key[i]

    temp_Key = ''.join(temp_Key)

    temp_Key_l = temp_Key[:28]
    temp_Key_r = temp_Key[28:]
    shift = Kleft_shift[0] +  Kleft_shift[1] + Kleft_shift[2]
    Key56_l = temp_Key_l[-shift:] + temp_Key_l[:-shift]
    Key56_r = temp_Key_r[-shift:] + temp_Key_r[:-shift]
    temp_Key56 = Key56_l + Key56_r
    #穷举Key56的剩余位置,然后经过一次三轮加密,
    # 先找到需要猜测的秘钥位置,顺便初始化一下, 注意字符串是不可变的(不可以用下标索引来修改,但是可以下标访问)
    guess_Key = ['0']*56
    guess_loca = []
    for i in range(56):
        if temp_Key56[i] == '2':
            guess_loca.append(i)  #注意此处下标从0开始算的
        else:
            guess_Key[i] = temp_Key56[i]

    right_Key56 = ''
    for i in range(2**8):
        guess = '{:0>8}'.format(bin(i)[2:])
        for j in range(8):
            guess_Key[guess_loca[j]] = guess[j]
        test_Key = ''.join(guess_Key)
        #print(test_Key)
        # 接下来要用我们的test_Key去计算三轮秘钥,然后跑一次DES,如果可以把明文顺利加密为密文,就说明这个是对的秘钥
        if (round3_DES(P_C[0][0].lower(),test_Key)) == hex2bin(P_C[0][1].lower()):
            right_Key56 = test_Key
            break
    print('right_Key56',right_Key56)
    # 过PC1逆置换得到Key64的56个位置,剩余的位置是奇偶校验位
    temp_Key64 = ['1'] * 64 #默认全是0,就是说如果某7bit的和是odd number,就可以改为1,这里使用偶校验
    for i in range(56):
        temp_Key64[K64_56[i] - 1] = right_Key56[i]  # 注意下标减1
    for i in range(8):
        sum = 0
        for j in range(7):
            sum = (sum+int(temp_Key64[i*8+j],2))%2
        if not sum%2 == 0:
            temp_Key64[i*8-1] = '0'
    return ''.join(temp_Key64)

if __name__ == '__main__':
    # for i in range(32):
    #     print(31-i,end='  ')
    # filename = 'P_C.text'
    # with open(filename,encoding='UTF-8') as f:
    #     for row in f.readlines():
    #         P_C.append((row[5:21],row[27:-1]))  #比较笨的方法,注意检查一下,-1的索引对不对,因为-1是算进\n的
    # print(P_C)
    P_C=[('5E870BA0B559A8CF', '71BF939C0CEEE3B1'), ('E7C1F970B559A8CF', 'EAA6CE7BC9DB808B'),
         ('5D6F0803ED9FAC45', 'D99FDDD5A3016E53'), ('1EB2B007ED9FAC45', 'B49E2F61B4172078'),
         ('7ECF80BD2FE0EA99', 'C9BE22F6DA261B9A'), ('8B2CBE002FE0EA99', '2360C6F9ACD3982D'),
         ('97D2078984F010B4', '719849F28E5313BF'), ('4A5C783384F010B4', 'E4DDEEDB66776D42'),
         ('641E10E96186B8A0', '7918C1C6400F4AA2'), ('CA4E94596186B8A0', 'B8D0DC72CD2F6579')]
    get_Sxor_i_in_out()
    for i in range(0,10,2):
        DES_Diff(P_C[i],P_C[i+1]) #(P1,C1) (P2,C2)
    Key64 = get_key()
    print('Key is ',Key64,hex(int(Key64,2)))
    #f.close()


DES线性分析

from DES_Different import *

S_i_mask = [
            [
                [
                    -32 for beta in range(16)
                ]for alpha in range(64)
            ]for i in range(8)
]
def self_xor(num,wide):
    count=0
    for i in range(wide):
        if (num&1):
            count+=1
        num>>=1
    if(count%2==0):  #偶数个1异或就是0,
        return 0
    return 1         #奇数个1异或就是1,

def get_S_i_mask():
    for i in range(8):
        for alpha in range(64):
            for beta in range(16):
                for x in range(64):
                    if(self_xor(x & alpha,6)==self_xor(S(i,x) & beta,4)):
                        S_i_mask[i][alpha][beta] += 1
def print_S_i_mask(num):
    print('\t\t\t\t\tS{}线性分布表'.format(num))
    print('α\β\t',end='')
    for i in range(16):
        print(i,end='\t')
    print('\n')
    count = 0
    for alpha in S_i_mask[num-1]:
        print(count,end='\t\t')
        count +=1
        for NS in alpha:
            print(NS,end='\t')
        print('\n')

def get_best_linear(num):
    max = [0,0,0]  #(alpha,beta,NS)
    for i in range(1,64):
        for j in range(16):
            if abs(S_i_mask[num][i][j]) > abs(max[-1]):
                max[0],max[1],max[2] = i,j,S_i_mask[num][i][j]
    maxs=[]
    for i in range(1,64):
        try:
            maxs.append([i, S_i_mask[num][i].index(max[-1]), max[-1]])
        except ValueError:
            pass
    print('在第{}个S盒中:'.format(num+1))
    for item in maxs:
        a = int2bit6(item[0])
        b = int2bit4(item[1])
        ##要通过a和b确定秘钥位置,这是第num个盒子,
        #求(E(R0)xorK0)·a的位置
        alpha_loca = []
        beta_loca = []
        for i in range(6):
            if a[i]=='1':
                alpha_loca.append(num*6+i)   # 此时是左向右计数,从0开始计
        for i in range(4):
            if b[i]=='1':
                beta_loca.append(num*4+i)   # 此时是左向右计数
        R_local = []
        K_local = []
        F_local = []
        for i in alpha_loca:
            R_local.append(32 - E_box[i])     # 此时是从右开始计数,左到右分别是 31,30,29......2,1,0
            K_local.append(47 - i)
        for i in beta_loca:
            F_local.append(31-P_box.index(i+1))
        print('\tα={},β={},NS={},\t逼近式:P_low{r}⊕C_low{r}⊕P_h{f}⊕C_h{f}=K1{k}⊕K3{k}'.
              format(item[0],item[1],item[-1],r=R_local,f=F_local,k=K_local))

if __name__=='__main__':
    get_S_i_mask()
    #print_S_i_mask(1)
    print('最佳线性逼近式如下:')
    for i in range(8):
        get_best_linear(i)
       maxs.append([i, S_i_mask[num][i].index(max[-1]), max[-1]])
        except ValueError:
            pass
    print('在第{}个S盒中:'.format(num+1))
    for item in maxs:
        a = int2bit6(item[0])
        b = int2bit4(item[1])
        ##要通过a和b确定秘钥位置,这是第num个盒子,
        #求(E(R0)xorK0)·a的位置
        alpha_loca = []
        beta_loca = []
        for i in range(6):
            if a[i]=='1':
                alpha_loca.append(num*6+i)   # 此时是左向右计数,从0开始计
        for i in range(4):
            if b[i]=='1':
                beta_loca.append(num*4+i)   # 此时是左向右计数
        R_local = []
        K_local = []
        F_local = []
        for i in alpha_loca:
            R_local.append(32 - E_box[i])     # 此时是从右开始计数,左到右分别是 31,30,29......2,1,0
            K_local.append(47 - i)
        for i in beta_loca:
            F_local.append(31-P_box.index(i+1))
        print('\tα={},β={},NS={},\t逼近式:P_low{r}⊕C_low{r}⊕P_h{f}⊕C_h{f}=K1{k}⊕K3{k}'.
              format(item[0],item[1],item[-1],r=R_local,f=F_local,k=K_local))

if __name__=='__main__':
    get_S_i_mask()
    #print_S_i_mask(1)
    print('最佳线性逼近式如下:')
    for i in range(8):
        get_best_linear(i)

  • 16
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值