差分分析【附code】

算法步骤
  • 收集大量明密文对;
  • 选取一个固定的输入值 x ′ x' x
  • 遍历所有收集到的明密文对,寻找 x x x x ∗ x^* x,使得 x ⨁ x ∗ = x ′ x\bigoplus x^*=x' xx=x
  • 创建4元组 ( x , x ∗ , y , y ∗ ) (x,x^*,y,y^*) (x,x,y,y),其中, y = π ( x ) , y ∗ = π ( x ∗ ) y=\pi(x),y^*=\pi(x^*) y=π(x),y=π(x)
  • 通过4元组构建差分表;
  • 从输入 x ′ x' x开始,在差分表中选取差分值最大的作为输出,找到输出对应的s盒子并将该值作为输入,再在差分表中寻找差分值大的作为输入,以此类推,构建出差分链;
  • 计算该差分链中最后一轮输入对应位置的 u ′ = u ⨁ u ∗ u'=u\bigoplus u^* u=uu的值,记作 u 真 ′ u'_真 u
  • 遍历所有可能的密钥,遍历4元组,要求四元组中差分链最后一轮输入对应位置的 y = y ∗ y=y^* y=y;根据收集到的 y 、 y ∗ y、y^* yy值即当前测试密钥值计算最后一轮输入的 u u u u ∗ u^* u,再根据该值计算 u 测 ′ = u ⨁ u ∗ u'_测=u\bigoplus u^* u=uu;若 u 真 ′ = = u 测 ′ u'_真==u'_测 u==u,则该密钥对应count值加一;
  • 遍历结束后输出count值最大的密钥。
代码

在这里插入图片描述
  首先使用书中给出的差分链分析出第5轮第2、4部分的密钥。再选择新的差分链分析出第5轮第1、3部分密钥,然后穷举高16位密钥情况,并验证密钥正确性。
  与线性分析不同的是,由于差分分析中可以找到一条第5轮仅包含第1、3部分且偏差很大的差分链,因此不需要在已知第5轮第2、4部分的基础上进行差分分析,也因此减小了部分时间开销。
  与线性分析相同的是,差分分析也是基于概率的分析方法,因此仍需要对一定范围内的密钥情况进行遍历并验证是否正确。
  同时,由于对基于不同规模的明密文对进行分析得到的差分分析准确度也有所不同,但在超过阈值后将出现平台期,即当分析的明密文对达到MAX规模后,差分分析得到的结果的准确度几乎保持不变,因此为降低时间开销,MAX作为可调参数需要进行调参摸索。在本题中,该MAX值大约为8000。
  综上代码实现流程为:快速读入数据并存入数组中;根据已有的明密文对分别对第5轮第2、4部分和第1、3部分进行差分分析,记录每一种密钥对应的count值;在一定范围内遍历第5轮第2、4部分和第1、3部分密钥,穷举高16位密钥并验证正确性(一定范围指内循环和外循环的循环次数,该参数需要测试调整到最合适);得到正确密钥后快速输出密钥。
  需要注意的是,穷举验证过程中由于输入数据的随机程度不大(实际上检测的数据具有连续性),因此在一定检测数量下,相同明文在不同密钥下得到相同密文的个数将会有所提升,因此需要适度增加验证数量。

#include <stdio.h>
#include <string.h>
typedef unsigned short ushort;
typedef unsigned int uint;

int n;
ushort ciphertext[65540];
uint key, tail_key;
int cnt13[16][16], cnt24[16][16];
bool flag13[16][16];
ushort key51, key52, key53, key54;
//SPN statement
const unsigned short sBox_4[16] = {0xe, 0x4, 0xd, 0x1, 0x2, 0xf, 0xb, 0x8, 0x3, 0xa, 0x6, 0xc, 0x5, 0x9, 0x0, 0x7}; 
const unsigned short inverse_sBox[16] = {0xe, 0x3, 0x4, 0x8, 0x1, 0xc, 0xa, 0xf, 0x7, 0xd, 0x9, 0x6, 0xb, 0x2, 0x0, 0x5};
const unsigned short pos[17] = {0x0,
								0x8000, 0x4000, 0x2000, 0x1000,
                                0x0800, 0x0400, 0x0200, 0x0100,
                                0x0080, 0x0040, 0x0020, 0x0010,
                                0x0008, 0x0004, 0x0002, 0x0001};
const unsigned short pBox[17] = {0x0,
								 0x8000, 0x0800, 0x0080, 0x0008,
								 0x4000, 0x0400, 0x0040, 0x0004,
								 0x2000, 0x0200, 0x0020, 0x0002,
								 0x1000, 0x0100, 0x0010, 0x0001};
unsigned short sBox_16[65536], spBox[65536];

void get_spBox(){  //获得spBox 
	for(int i = 0; i < 65536; ++i){
		sBox_16[i] = (sBox_4[i >> 12] << 12) | (sBox_4[(i >> 8) & 0xf] << 8) | (sBox_4[(i >> 4) & 0xf] << 4) | sBox_4[i & 0xf];
		spBox[i] = 0;
		for(int j = 1; j <= 16; ++j){
			if(sBox_16[i] & pos[j]) spBox[i] |= pBox[j];
		}
	} 
} 

inline ushort read(){
	char ch;
	ushort buf = 0;
	for(int i = 0; i < 4; ){
		ch = getchar();
		if(ch >= '0' && ch <= '9'){
			buf = (buf << 4) | (ch ^ 48);
			i++;
		}
		else if(ch >= 'a' && ch <= 'z'){
			buf = (buf << 4) | (ch - 'a' + 10);
			i++;
		}
	}
	return buf;
}

inline void input(){
	for(int i = 0; i < 65536; ++i){
		ciphertext[i] = read();
	}
}

inline void output(){
	char buf[8];  //输出缓冲区
	for(int i = 0; i < 8; ++i){
		buf[7 - i] = key & 0xf;
		if(buf[7 - i] < 10) buf[7 - i] += '0';
		else buf[7 - i] = buf[7 - i] - 10 + 'a'; 
		key >>= 4;
	}
	for(int i = 0; i < 8; ++i) putchar(buf[i]);
	putchar('\n');
}

inline void diff_analysis(){
	uint x, x_, y, y_, x_xor;
	ushort u1, u2, u3, u4, u1_, u2_, u3_, u4_;
	
	x_xor = 0x0b00;
	for(x = 12345; x < 20567; ++x){
		x_ = x ^ x_xor;
		y = ciphertext[x];
		y_ = ciphertext[x_];
		if((y & 0xf0f0) == (y_ & 0xf0f0)){
			for(int L1 = 0; L1 < 16; ++L1){
				for(int L2 = 0; L2 < 16; ++L2){
					//v2 = L1 ^ ((y >> 8) & 0xf);
					//v4 = L2 ^ (y & 0xf);
					u2 = inverse_sBox[L1 ^ ((y >> 8) & 0xf)];
					u4 = inverse_sBox[L2 ^ (y & 0xf)];
					
					//v2_ = L1 ^ ((y_ >> 8) & 0xf);
					//v4_ = L2 ^ (y_ & 0xf);
					u2_ = inverse_sBox[L1 ^ ((y_ >> 8) & 0xf)];
					u4_ = inverse_sBox[L2 ^ (y_ & 0xf)];
					
					//u2_xor = u2 ^ u2_;
					//u4_xor = u4 ^ u4_;
					if(((u2 ^ u2_) == 0x6) && ((u4 ^ u4_) == 0x6)) cnt24[L1][L2]++;
				} 
			}
		}
	}
	x_xor = 0x0020;
	for(x = 12345; x < 20567; ++x){
		x_ = x ^ x_xor;
		y = ciphertext[x];
		y_ = ciphertext[x_];
		if((y & 0x0f0f) == (y_ & 0x0f0f)){
			for(int L1 = 0; L1 < 16; ++L1){
				for(int L2 = 0; L2 < 16; ++L2){
					//v1 = L1 ^ ((y >> 12) & 0xf);
					//v3 = L2 ^ ((y >> 4) & 0xf);
					u1 = inverse_sBox[L1 ^ ((y >> 12) & 0xf)];
					u3 = inverse_sBox[L2 ^ ((y >> 4) & 0xf)];
					
					//v1_ = L1 ^ ((y_ >> 12) & 0xf);
					//v3_ = L2 ^ ((y_ >> 4) & 0xf);
					u1_ = inverse_sBox[L1 ^ ((y_ >> 12) & 0xf)];
					u3_ = inverse_sBox[L2 ^ ((y_ >> 4) & 0xf)];
					
					//u1_xor = u1 ^ u1_;
					//u3_xor = u3 ^ u3_;
					if(((u1 ^ u1_) == 0x5) && ((u3 ^ u3_) == 0x5)) cnt13[L1][L2]++;
				}
			} 
		}
	}
}

int main(){
	
	get_spBox();
	scanf("%d", &n);
	bool flag;
	for(int group = 0; group < n; ++group){
		input();
		flag = false;
		
		//计算2、4位
		memset(cnt24, 0, 256 * sizeof(int));
		memset(cnt13, 0, 256 * sizeof(int));
		diff_analysis();  //差分分析 
				
		//外循环 
		for(int round24 = 0; round24 < 10; ++round24){
			int max24 = -1;
			for(int L1 = 0; L1 < 16; ++L1){
				for(int L2 = 0; L2 < 16; ++L2){
					if(cnt24[L1][L2] > max24){
						max24 = cnt24[L1][L2];
						key52 = L1;
						key54 = L2;
					}
				}
			}
			cnt24[key52][key54] = 0;
			
			//内循环
			memset(flag13, true, 256 * sizeof(bool));
			for(int round13 = 0; round13 < 10; ++round13){
				int max13 = -1;
				for(int L1 = 0; L1 < 16; ++L1){
					for(int L2 = 0; L2 < 16; ++L2){
						if(cnt13[L1][L2] > max13 && flag13[L1][L2]){
							max13 = cnt13[L1][L2];
							key51 = L1;
							key53 = L2;
						}
					}
				}
				flag13[key51][key53] = false;
				
				//开始穷举
				tail_key = (key51 << 12) | (key52 << 8) | (key53 << 4) | key54; 
				int plaintext, k1, k2, k3, k4, k5;
				for(int fore_key = 0; fore_key < 65536; ++fore_key){
					key = (fore_key << 16) | tail_key;
					k5 = tail_key;
					k4 = (key >> 4) & 0xffff;
					k3 = (key >> 8) & 0xffff;
					k2 = (key >> 12) & 0xffff;
					k1 = (key >> 16) & 0xffff;
					for(plaintext = 0; plaintext < 24; ++plaintext){
						if((sBox_16[spBox[spBox[spBox[plaintext ^ k1] ^ k2] ^ k3] ^ k4] ^ k5) != ciphertext[plaintext]) break;
					}
					if(plaintext == 24){
						flag = true;
						break;
					}
				}
				if(flag) break;
			}
			if(flag) break; 
		}
		output();
	}	
	
	return 0;
}

相关资料

SPN线性密码分析【附code】

  • 8
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

D-A-X

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值