- 具体步骤
- 获得S盒的差分对应表
在具体程序实现时可以建一个8*64*16的三维集合数组,经过如下算法,就可以生成差分分布表Sd(代码中“this.”指实例化后的操作对象),例如Sd[2][32][12]存放了第二个S盒输入差为32,输出差为12的所有可能输入值(输入值可能不止一个,也可能为空,所以Sd[i][j][k]本身是一个集合)。

差分对应表的一部分如下图所示,下图中S8表示第8个S盒,dSin表示S盒的输入差,dSout表示输出差,冒号后的值表示可能的输入值,该输入值为用十六进制表示的6比特值。

- 计算S盒的输入差与输出差(第三轮不交换)
如下图所示,dSin,dSout分别表示S盒的输入差和输出差,dSin是对第三轮密文差的右32比特做一个E盒扩展,dSout是将第一轮明文差的左32比特与第三轮密文差的左32比特进行异或,再进行一次逆P盒变换的结果。

- 更新密钥集合
根据第2步计算的S盒输入输出差查S盒的差分分布表,以获得S盒可能的输入值,再将这些输入值与密文的右32比特进行异或,即可得到可能的密钥值,将这些可能的密钥值与上一次计算所得的密钥集合取交集,即可完成密钥集合的更新(如果是第一次计算密钥集合,则直接将本次所得的密钥可能值赋给密钥集合)。

- 多次计算得出唯一确定的密钥值
- 运行结果
本次测试要计算的密钥K3值为0x39,0x08,0x19,0x36,0x09, 0x36,0x00,0x18,可以看到,经过3次对密钥集合进行更新,即可唯一确定最终的密钥K3。



不过需要注意的是,不一定每次唯一确定密钥都只需要更新3次密钥集合,经过100000次的测试,有如下试验结果:
更新次数 | 1 | 2 | 3 | 4 | 5 |
实验次数 | 0 | 1396 | 53903 | 36789 | 6747 |
更新次数 | 6 | 7 | 8 | 9 | 10 |
实验次数 | 971 | 166 | 20 | 6 | 2 |
从表中可以看到,有53903次测试都在3次更新密钥集合后唯一确定了密钥,6次以上的占比就非常少。在一次实验中,有大约90%的可能性在3次或4次更新密钥后唯一确定密钥K3。
完整代码的三个文件如下:
DES_3R:生成测试数据,计算3轮DES,第3轮不交换,第1轮输入数据由随机函数生成。
package De_3R;
/**
* 计算3轮DES,第3轮不交换
* @author 实无所得
*
*/
public class DES_3R {
private byte[] key=null;
private byte[][] ikey=null;
private byte[] L0=null,R0=null;
private byte[] L3=null,R3=null;
private static byte[][] S= {{14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13},
{15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10,3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5,0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15,13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9},
{10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8,13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1,13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7,1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12},
{7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15,13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9,10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4,3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14},
{2,12,4,1,7,10,11,6,5,8,3,15,13,0,14,9,14,11,2,12,4,7,13,1,5,0,15,13,3,9,8,6,4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14,11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3},
{12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11,10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8,9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6,4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13},
{4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1,13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6,1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2,6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12},
{13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7,1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2,7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8,2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11}};
public DES_3R(String key) {
setKey(key);
setLR0();
}
public DES_3R(byte[] key) {
setKey(key);
setLR0();
}
public DES_3R() {
setKey(new byte[0]);
setLR0();
}
private byte[] PC1(byte[] in) {
byte[] result=new byte[8];
result[0]=(byte) (((in[7]>>1)&0x40)|((in[6]>>2)&0x20)|((in[5]>>3)&0x10)|((in[4]>>4)&0x08)|((in[3]>>5)&0x04)|((in[2]>>6)&0x02)|((in[1]>>7)&0x01));
result[1]=(byte) (((in[0]>>1)&0x40)|((in[7]>>1)&0x20)|((in[6]>>2)&0x10)|((in[5]>>3)&0x08)|((in[4]>>4)&0x04)|((in[3]>>5)&0x02)|((in[2]>>6)&0x01));
result[2]=(byte) (((in[1] )&0x40)|((in[0]>>1)&0x20)|((in[7]>>1)&0x10)|((in[6]>>2)&0x08)|((in[5]>>3)&0x04)|((in[4]>>4)&0x02)|((in[3]>>5)&0x01));
result[3]=(byte) (((in[2]<<1)&0x40)|((in[1] )&0x20)|((in[0]>>1)&0x10)|((in[7]>>1)&0x08)|((in[6]>>2)&0x04)|((in[5]>>3)&0x02)|((in[4]>>4)&0x01));
result[4]=(byte) (((in[7]<<5)&0x40)|((in[6]<<4)&0x20)|((in[5]<<3)&0x10)|((in[4]<<2)&0x08)|((in[3]<<1)&0x04)|((in[2] )&0x02)|((in[1]>>1)&0x01));
result[5]=(byte) (((in[0]<<5)&0x40)|((in[7]<<3)&0x20)|((in[6]<<2)&0x10)|((in[5]<<1)&0x08)|((in[4] )&0x04)|((in[3]>>1)&0x02)|((in[2]>>2)&0x01));
result[6]=(byte) (((in[1]<<4)&0x40)|((in[0]<<3)&0x20)|((in[7]<<1)&0x10)|((in[6] )&0x08)|((in[5]>>1)&0x04)|((in[4]>>2)&0x02)|((in[3]>>3)&0x01));
result[7]=(byte) (((in[2]<<3)&0x40)|((in[1]<<2)&0x20)|((in[0]<<1)&0x10)|((in[3]>>1)&0x08)|((in[2]>>2)&0x04)|((in[1]>>3)&0x02)|((in[0]>>4)&0x01));
return result;
}
private byte[] PC2(byte[] in) {
byte[] result=new byte[8];
result[0]=(byte) (((in[1]<<5)&0x20)|((in[2] )&0x10)|((in[1] )&0x08)|((in[3]>>2)&0x04)|((in[0]>>5)&0x02)|((in[0]>>2)&0x01));
result[1]=(byte) (((in[0]<<1)&0x20)|((in[3]<<4)&0x10)|((in[2]>>3)&0x08)|((in[0]<<1)&0x04)|((in[2]<<1)&0x02)|((in[1]>>4)&0x01));
result[2]=(byte) (((in[3] )&0x20)|((in[2]<<2)&0x10)|((in[1]<<1)&0x08)|((in[0]>>1)&0x04)|((in[3]>>1)&0x02)|((in[1]>>6)&0x01));
result[3]=(byte) (((in[2] )&0x20)|((in[0]<<4)&0x10)|((in[3]<<2)&0x08)|((in[2]<<1)&0x04)|((in[1] )&0x02)|((in[0]>>5)&0x01));
result[4]=(byte) (((in[5]<<4)&0x20)|((in[7] )&0x10)|((in[4]>>1)&0x08)|((in[5]>>3)&0x04)|((in[6]>>1)&0x02)|((in[7]>>1)&0x01));
result[5]=(byte) (((in[4] )&0x20)|((in[5]<<2)&0x10)|((in[7]>>2)&0x08)|((in[6]>>2)&0x04)|((in[4]>>1)&0x02)|((in[6]>>1)&0x01));
result[6]=(byte) (((in[6]>>0)&0x20)|((in[6]<<4)&0x10)|((in[5] )&0x08)|((in[7]<<2)&0x04)|((in[4] )&0x02)|((in[7]>>3)&0x01));
result[7]=(byte) (((in[6]<<2)&0x20)|((in[5]<<4)&0x10)|((in[7]>>3)&0x08)|((in[5]>>4)&0x04)|((in[4]>>5)&0x02)|((in[4]>>3)&0x01));
return result;
}
private byte[] shift(byte[] in,int n) {
byte[] result=new byte[8];
for(int i=0;i<8;i++) {
if(i==3)result[3]=(byte) (((in[3]<<n)|(in[0]>>(7-n)))&0x7f);
else if(i==7)result[7]=(byte) (((in[7]<<n)|(in[4]>>(7-n)))&0x7f);
else result[i]=(byte) (((in[i]<<n)|(in[i+1]>>(7-n)))&0x7f);
}
return result;
}
private void geneKey() {
this.ikey=new byte[3][8];
byte[] PC1ShiftOut,PC2Out;
int[] shiftCount= {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1};
PC1ShiftOut=PC1(this.key);
for(int i=0;i<3;i++) {
PC1ShiftOut=shift(PC1ShiftOut, shiftCount[i]);
PC2Out=PC2(PC1ShiftOut);
System.arraycopy(PC2Out, 0, this.ikey[i], 0, 8);
}
}
private byte[] E(byte[] in) {
byte[] result=new byte[8];
result[0]=(byte) (((in[3]<<5)&0x20)|((in[0]>>3)&0x10)|((in[0]>>3)&0x08)|((in[0]>>3)&0x04)|((in[0]>>3)&0x02)|((in[0]>>3)&0x01));
result[1]=(byte) (((in[0]<<1)&0x20)|((in[0]<<1)&0x10)|((in[0]<<1)&0x08)|((in[0]<<1)&0x04)|((in[0]<<1)&0x02)|((in[1]>>7)&0x01));
result[2]=(byte) (((in[0]<<5)&0x20)|((in[1]>>3)&0x10)|((in[1]>>3)&0x08)|((in[1]>>3)&0x04)|((in[1]>>3)&0x02)|((in[1]>>3)&0x01));
result[3]=(byte) (((in[1]<<1)&0x20)|((in[1]<<1)&0x10)|((in[1]<<1)&0x08)|((in[1]<<1)&0x04)|((in[1]<<1)&0x02)|((in[2]>>7)&0x01));
result[4]=(byte) (((in[1]<<5)&0x20)|((in[2]>>3)&0x10)|((in[2]>>3)&0x08)|((in[2]>>3)&0x04)|((in[2]>>3)&0x02)|((in[2]>>3)&0x01));
result[5]=(byte) (((in[2]<<1)&0x20)|((in[2]<<1)&0x10)|((in[2]<<1)&0x08)|((in[2]<<1)&0x04)|((in[2]<<1)&0x02)|((in[3]>>7)&0x01));
result[6]=(byte) (((in[2]<<5)&0x20)|((in[3]>>3)&0x10)|((in[3]>>3)&0x08)|((in[3]>>3)&0x04)|((in[3]>>3)&0x02)|((in[3]>>3)&0x01));
result[7]=(byte) (((in[3]<<1)&0x20)|((in[3]<<1)&0x10)|((in[3]<<1)&0x08)|((in[3]<<1)&0x04)|((in[3]<<1)&0x02)|((in[0]>>7)&0x01));
return result;
}
private byte[] S(byte[] in) {
byte[] result=new byte[8];
for(int i=0;i<8;i++) {
int t=(in[i]&0x20)|((in[i]&0x01)<<4)|((in[i]>>1)&0xf);
result[i]=S[i][t];
}
return result;
}
private byte[] P(byte[] in) {
byte[] result=new byte[4];
result[0]=(byte) (((in[3]<<7)&0x80)|((in[1]<<5)&0x40)|((in[4]<<5)&0x20)|((in[5]<<1)&0x10)|((in[7] )&0x08)|((in[2]<<2)&0x04)|((in[6]<<1)&0x02)|((in[4]>>3)&0x01));
result[1]=(byte) (((in[0]<<4)&0x80)|((in[3]<<5)&0x40)|((in[5]<<4)&0x20)|((in[6]<<2)&0x10)|((in[1] )&0x08)|((in[4] )&0x04)|((in[7] )&0x02)|((in[2]>>2)&0x01));
result[2]=(byte) (((in[0]<<5)&0x80)|((in[1]<<6)&0x40)|((in[5]<<5)&0x20)|((in[3]<<2)&0x10)|((in[7]<<3)&0x08)|((in[6]<<1)&0x04)|((in[0] )&0x02)|((in[2]>>3)&0x01));
result[3]=(byte) (((in[4]<<6)&0x80)|((in[3]<<3)&0x40)|((in[7]<<3)&0x20)|((in[1]<<2)&0x10)|((in[5]<<1)&0x08)|((in[2]<<1)&0x04)|((in[0]<<1)&0x02)|((in[6]>>3)&0x01));
return result;
}
private byte[] XOR(byte[] in,byte[] ikey) {
byte[] result=new byte[in.length];
for(int i=0;i<in.length;i++)
result[i]=(byte) (in[i]^ikey[i]);
return result;
}
public void Excute() {
byte[] Li=new byte[4];
byte[] Ri=new byte[4];
byte[] Xo;
System.arraycopy(L0, 0, Li, 0, 4);
System.arraycopy(R0, 0, Ri, 0, 4);
for(int i=0;i<3;i++) {
Xo=XOR(E(Ri), this.ikey[i]);
Xo=XOR(P(S(Xo)),Li);
Li=Ri;
Ri=Xo;
}
L3=Ri;
R3=Li;
}
public void Excute(byte[] L0,byte[] R0) {
setLR0(L0,R0);
Excute();
}
public void showInfo() {
showKey();
showIkey();
showPlain();
showCipher();
}
public void showIkeyN() {
System.out.print("密钥K3: ");
for(int i=0;i<8;i++)
System.out.print(String.format("%02x ",this.ikey[2][i]));
System.out.println();
}
public void showIkey() {
int i,j;
for(i=0;i<3;i++) {
System.out.print(String.format("K%d: ", i));
for(j=0;j<8;j++)
System.out.print(String.format("%02x ",this.ikey[i][j]));
System.out.println();
}
}
public void showKey() {
int i;
System.out.print("Key:\n Hex:");
for(i=0;i<8;i++)
System.out.print(String.format("%02x ",this.key[i]));
System.out.print("\n CH:");
String strKey=new String(this.key);
System.out.println(strKey);
}
public void showPlain() {
System.out.print("Plain: ");
for(int i=0;i<4;i++)
System.out.print(String.format("0x%02x,",this.L0[i]));
for(int i=0;i<4;i++)
System.out.print(String.format("0x%02x,",this.R0[i]));
System.out.println();
}
public void showCipher() {
System.out.print("Cipher: ");
for(int i=0;i<4;i++)
System.out.print(String.format("0x%02x,",this.L3[i]));
for(int i=0;i<4;i++)
System.out.print(String.format("0x%02x,",this.R3[i]));
System.out.println();
}
public void setKey(byte[] key) {
this.key=new byte[8];
int n=key.length>8?8:key.length;
for(int i=0;i<n;i++)
this.key[i] = key[i];
geneKey();
}
public void setKey(String key) {
setKey(key.getBytes());
}
public void UpdateL0() {
this.L0=new byte[4];
for(int i=0;i<4;i++)
this.L0[i]=(byte) (Math.random()*256);
}
public void UpdateR0() {
this.R0=new byte[4];
for(int i=0;i<4;i++)
this.R0[i]=(byte) (Math.random()*256);
}
public void setLR0() {
this.L0=new byte[4];
this.R0=new byte[4];
for(int i=0;i<4;i++) {
this.L0[i]=(byte) (Math.random()*256);
this.R0[i]=(byte) (Math.random()*256);
}
}
public void setLR0(byte[] L0,byte[] R0) {
this.L0=new byte[4];
this.R0=new byte[4];
int nL=L0.length>4?4:L0.length;
for(int i=0;i<nL;i++)
this.L0[i] = L0[i];
int nR=R0.length>4?4:R0.length;
for(int i=0;i<nR;i++)
this.R0[i] = R0[i];
}
public void setLR0(byte[] LR0) {
this.L0=new byte[4];
this.R0=new byte[4];
int nL=LR0.length>4?4:LR0.length;
for(int i=0;i<nL;i++)
this.L0[i] = LR0[i];
int nR=LR0.length>8?4:(nL-4);
for(int i=0;i<nR;i++)
this.R0[i] = LR0[i+4];
}
public byte[] getLR0() {
if(L0==null||R0==null)return null;
byte[] LR0=new byte[8];
System.arraycopy(L0, 0, LR0, 0, 4);
System.arraycopy(R0, 0, LR0, 4, 4);
return LR0;
}
public byte[] getLR3() {
if(L3==null||R3==null)return null;
byte[] LR3=new byte[8];
System.arraycopy(L3, 0, LR3, 0, 4);
System.arraycopy(R3, 0, LR3, 4, 4);
return LR3;
}
}De_3R:进行3轮差分攻击,输入的两组明文右32比特要相同(由DES_3R类生成)。
package De_3R;
import java.util.HashSet;
import java.util.Set;
/**
* 3轮DES差分攻击
* @author 实无所得
*
*/
@SuppressWarnings("rawtypes")
public class De_3R {
private byte[] dP=null;
private byte[] dC=null;
private byte[] Ex=null;
private byte[][][] count=new byte[8][64][16];
private Set[][][] Sd=new HashSet[8][64][16];
private Set[] KeySet=new HashSet[8];
private static int[][] S= {
{14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7,0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8,4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0,15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13},
{15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10,3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5,0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15,13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9},
{10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8,13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1,13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7,1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12},
{7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15,13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9,10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4,3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14},
{2,12,4,1,7,10,11,6,5,8,3,15,13,0,14,9,14,11,2,12,4,7,13,1,5,0,15,13,3,9,8,6,4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14,11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3},
{12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11,10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8,9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6,4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13},
{4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1,13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6,1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2,6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12},
{13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7,1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2,7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8,2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11}};
public De_3R() {
init();
}
private byte[] E(byte[] in) {
byte[] result=new byte[8];
result[0]=(byte) (((in[3]<<5)&0x20)|((in[0]>>3)&0x10)|((in[0]>>3)&0x08)|((in[0]>>3)&0x04)|((in[0]>>3)&0x02)|((in[0]>>3)&0x01));
result[1]=(byte) (((in[0]<<1)&0x20)|((in[0]<<1)&0x10)|((in[0]<<1)&0x08)|((in[0]<<1)&0x04)|((in[0]<<1)&0x02)|((in[1]>>7)&0x01));
result[2]=(byte) (((in[0]<<5)&0x20)|((in[1]>>3)&0x10)|((in[1]>>3)&0x08)|((in[1]>>3)&0x04)|((in[1]>>3)&0x02)|((in[1]>>3)&0x01));
result[3]=(byte) (((in[1]<<1)&0x20)|((in[1]<<1)&0x10)|((in[1]<<1)&0x08)|((in[1]<<1)&0x04)|((in[1]<<1)&0x02)|((in[2]>>7)&0x01));
result[4]=(byte) (((in[1]<<5)&0x20)|((in[2]>>3)&0x10)|((in[2]>>3)&0x08)|((in[2]>>3)&0x04)|((in[2]>>3)&0x02)|((in[2]>>3)&0x01));
result[5]=(byte) (((in[2]<<1)&0x20)|((in[2]<<1)&0x10)|((in[2]<<1)&0x08)|((in[2]<<1)&0x04)|((in[2]<<1)&0x02)|((in[3]>>7)&0x01));
result[6]=(byte) (((in[2]<<5)&0x20)|((in[3]>>3)&0x10)|((in[3]>>3)&0x08)|((in[3]>>3)&0x04)|((in[3]>>3)&0x02)|((in[3]>>3)&0x01));
result[7]=(byte) (((in[3]<<1)&0x20)|((in[3]<<1)&0x10)|((in[3]<<1)&0x08)|((in[3]<<1)&0x04)|((in[3]<<1)&0x02)|((in[0]>>7)&0x01));
return result;
}
private byte[] InvP(byte[] in) {
byte[] result=new byte[8];
result[0]=(byte) (((in[1]>>4)&0x08)|((in[2]>>5)&0x04)|((in[2] )&0x02)|((in[3]>>1)&0x01));
result[1]=(byte) (((in[1] )&0x08)|((in[3]>>2)&0x04)|((in[0]>>5)&0x02)|((in[2]>>6)&0x01));
result[2]=(byte) (((in[2]<<3)&0x08)|((in[1]<<2)&0x04)|((in[3]>>1)&0x02)|((in[0]>>2)&0x01));
result[3]=(byte) (((in[3]>>3)&0x08)|((in[2]>>2)&0x04)|((in[1]>>5)&0x02)|((in[0]>>7)&0x01));
result[4]=(byte) (((in[0]<<3)&0x08)|((in[1] )&0x04)|((in[3]>>6)&0x02)|((in[0]>>5)&0x01));
result[5]=(byte) (((in[0]>>1)&0x08)|((in[3]>>1)&0x04)|((in[1]>>4)&0x02)|((in[2]>>5)&0x01));
result[6]=(byte) (((in[3]<<3)&0x08)|((in[1]>>2)&0x04)|((in[2]>>1)&0x02)|((in[0]>>1)&0x01));
result[7]=(byte) (((in[0] )&0x08)|((in[3]>>3)&0x04)|((in[1] )&0x02)|((in[2]>>3)&0x01));
return result;
}
private byte[] XOR(byte[] in1,byte[] in2) {
byte[] result=new byte[in1.length];
for(int i=0;i<in1.length;i++)
result[i]=(byte) (in1[i]^in2[i]);
return result;
}
@SuppressWarnings("unchecked")
public void excDecode() {
if(this.dC==null)return;
byte[] dPL=new byte[4];
byte[] dCL=new byte[4];
byte[] dCR=new byte[4];
byte[] dPLXCL,dSout,dSin;
System.arraycopy(dP, 0, dPL, 0, 4);
System.arraycopy(dC, 0, dCL, 0, 4);
System.arraycopy(dC, 4, dCR, 0, 4);
dSin=E(dCR);
dPLXCL=XOR(dPL,dCL);
dSout=InvP(dPLXCL);
Set[] CalKey=new HashSet[8];//查差分对应表并计算后得到的密钥值
for(int i=0;i<8;i++) {
CalKey[i]=new HashSet<Byte>();
for (Object set : this.Sd[i][dSin[i]][dSout[i]]) {
int n=(int) (((Integer) set).byteValue()^this.Ex[i]);
CalKey[i].add(n);
}
}
for(int i=0;i<8;i++)
KeySet[i].retainAll(CalKey[i]);//取交集
}
@SuppressWarnings("unchecked")
public void init() {
for(int i=0;i<8;i++)
KeySet[i]=new HashSet<Integer>();
setKeySet();
for(int i=0;i<8;i++)
for(int j=0;j<64;j++)
for(int k=0;k<16;k++)
this.Sd[i][j][k]=new HashSet<Integer>();
for(int i=0;i<8;i++)
for(int j=0;j<64;j++)
for(int k=0;k<64;k++) {
int t1=(j&0x20)|((j&0x01)<<4)|((j>>1)&0xf);
int t2=(k&0x20)|((k&0x01)<<4)|((k>>1)&0xf);
this.count[i][j^k][S[i][t1]^S[i][t2]]++;
this.Sd[i][j^k][S[i][t1]^S[i][t2]].add(j);
}
}
@SuppressWarnings("unchecked")
public void setKeySet() {
for(int i=0;i<8;i++)
for(int j=0;j<64;j++)
this.KeySet[i].add(j);
}
public void setP(byte[] P1,byte[] P2) {
this.dP=new byte[8];
for(int i=0;i<8;i++)
this.dP[i]=(byte) (P1[i]^P2[i]);
}
public void showDP() {
System.out.print("dP: ");
for(int i=0;i<8;i++)
System.out.print(String.format("%02x ",this.dP[i]));
System.out.println();
}
public void setC(byte[] C1,byte[] C2) {
this.dC=new byte[8];
byte[] RC1=new byte[4];
System.arraycopy(C1, 4, RC1, 0, 4);
this.Ex=E(RC1);
for(int i=0;i<8;i++)
this.dC[i]=(byte) (C1[i]^C2[i]);
}
public void showDC() {
System.out.print("dC: ");
for(int i=0;i<8;i++)
System.out.print(String.format("%02x ",this.dC[i]));
System.out.println();
}
public void ShowSd() {
for(int i=0;i<8;i++) {
for(int j=0;j<64;j++)
for(int k=0;k<16;k++) {
System.out.print("S"+(i+1)+" dSin="+j+" dSout="+k+": ");
for (Object b : this.Sd[i][j][k].toArray())
System.out.print(String.format("%02x ", ((Integer)b).byteValue()));
System.out.println();
}
System.out.println();
}
}
public void ShowCount() {
for(int i=0;i<8;i++) {
for(int j=0;j<64;j++)
for(int k=0;k<16;k++) {
System.out.print("S"+(i+1)+" dSin="+j+" dSout="+k+": ");
System.out.println(String.format("%d", this.count[i][j][k]));
}
System.out.println();
}
}
public void ShowKeySet() {
for(int i=0;i<8;i++) {
System.out.print("KeySet"+(i+1)+": ");
for (Object b : this.KeySet[i].toArray())
System.out.print(String.format("%02x ", ((Integer)b).byteValue()));
System.out.println();
}
}
public boolean IsKeySetUnique() {
boolean isUnique=true;
for(int i=0;i<8;i++) {
if(this.KeySet[i].size()!=1) {
isUnique=false;
break;
}
}
return isUnique;
}
}testDe_3R:用于测试。
package De_3R;
import java.util.Scanner;
/**
* 测试3轮DES差分攻击
* @author 实无所得
*
*/
public class testDe_3R {
public static void main(String[] args) {
String key="de3r_key";
DES_3R dx=new DES_3R(key);
De_3R de=new De_3R();
// de.ShowSd(); //显示差分对应表
test1(dx, de);
// test2(dx, de,10000);
}
public static void test1(DES_3R dx,De_3R de) {
int n=0;
byte[] P1,P2,C1,C2;
String str="";
Scanner scanner=new Scanner(System.in);
dx.showIkeyN();
do{
n++;
System.out.println(String.format("第%d次更新密钥的结果:", n));
//生成一对明文输入和一对密文输出
dx.setLR0();
P1=dx.getLR0();
dx.Excute();
C1=dx.getLR3();
dx.UpdateL0();
P2=dx.getLR0();
dx.Excute();
C2=dx.getLR3();
//更新一次密钥集合
de.setP(P1, P2);
de.setC(C1, C2);
de.excDecode();
de.showDP();
de.showDC();
de.ShowKeySet();
System.out.print("按n继续:");
str=scanner.next();
}while(str.equals("n"));
scanner.close();
}
public static void test2(DES_3R dx,De_3R de,int N) {
if(N<0||N>100000) {
System.out.println("请输入合适的N值!");
return;
}
byte[] P1,P2,C1,C2;
int n=0;
int[] count=new int[20];
for(int i=0;i<N;i++) {
do{
n++;
//生成一对明文输入和一对密文输出
dx.setLR0();
P1=dx.getLR0();
dx.Excute();
C1=dx.getLR3();
dx.UpdateL0();
P2=dx.getLR0();
dx.Excute();
C2=dx.getLR3();
//更新一次密钥集合
de.setP(P1, P2);
de.setC(C1, C2);
de.excDecode();
}while(!de.IsKeySetUnique());
de.setKeySet();
count[n-1]++;
n=0;
}
for(int i=0;i<20;i++)
System.out.printf("%2d次:%5d : %f\n",i+1, count[i],(float)count[i]/N);
}
}