You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
5.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import random
import string
from DES import des_encrypt, des_decrypt, S, P, E
def generate_random_8bit_plaintext():
"""生成8位随机明文"""
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(8))
"""定义S盒线性分布掩码表"""
S_i_mask = [
[[0 for _ in range(16)] for _ in range(64)]
for _ in range(8)
]
def self_xor(num, wide):
"""计算异或结果"""
count = 0
for i in range(wide):
count += num & 1
num >>= 1
return count % 2
def get_S_i_mask(plaintexts, ciphertexts):
"""根据大量明密文对计算S盒线性分布掩码表"""
for i in range(8):
for alpha in range(64):
for beta in range(16):
for x, (plain, cipher) in enumerate(zip(plaintexts, ciphertexts)):
# 获取明文和密文的6位输入和4位输出
s_out = S[i][(plain & 0x20) >> 4 | (plain & 1)]
if self_xor(plain & alpha, 6) == self_xor(s_out & beta, 4):
S_i_mask[i][alpha][beta] += 1 # 统计匹配的次数
def print_S_i_mask(num):
"""打印第num个S盒的线性分布表"""
print(f"\t\t\t\tS{num + 1} 线性分布表")
print("α\\β\t", end="")
for i in range(16):
print(f"{i}\t", end="")
print("\n")
for alpha_index, alpha in enumerate(S_i_mask[num]):
print(f"{alpha_index}\t", end="")
for NS in alpha:
print(f"{NS}\t", end="")
print()
def int2bit6(num):
"""6位整数转二进制字符串"""
return bin(num)[2:].zfill(6)
def int2bit4(num):
"""4位整数转二进制字符串"""
return bin(num)[2:].zfill(4)
def get_best_linear(num):
"""获取S盒的最佳线性逼近并返回 (alpha, beta)"""
max_bias = [0, 0, 0] # alpha, beta, NS
for alpha in range(1, 64): # 输入6位alpha
for beta in range(16): # 输出4位beta
if abs(S_i_mask[num][alpha][beta]) > abs(max_bias[-1]):
max_bias = [alpha, beta, S_i_mask[num][alpha][beta]]
# 记录所有最大偏差的组合
maxs = []
for alpha in range(1, 64):
try:
beta_index = S_i_mask[num][alpha].index(max_bias[-1])
maxs.append([alpha, beta_index, max_bias[-1]])
except ValueError:
pass
# 输出最佳线性逼近
print(f"在第 {num + 1} 个S盒中")
for item in maxs:
a = int2bit6(item[0])
b = int2bit4(item[1])
alpha_loca = [num * 6 + i for i in range(6) if a[i] == '1']
beta_loca = [num * 4 + i for i in range(4) if b[i] == '1']
R_local = [32 - E[i] for i in alpha_loca]
K_local = [47 - i for i in alpha_loca]
F_local = [31 - P.index(i + 1) for i in beta_loca]
print(f"\tα={item[0]}, β={item[1]}, NS={item[-1]}, "
f"逼近式P_low{R_local}⊕C_low{R_local}⊕P_h{F_local}⊕C_h{F_local}=K1{K_local}⊕K3{K_local}")
# 返回最佳线性逼近的第一个结果 (alpha, beta)
if maxs:
return maxs[0][:2]
return None
def linear_attack():
key = '123ghyi9'
plaintexts = []
ciphertexts = []
print("生成1000个明密文对...")
for i in range(10000):
plaintext = generate_random_8bit_plaintext()
ciphertext = des_encrypt(plaintext, key)
plaintexts.append(int(plaintext.encode().hex(), 16))
ciphertexts.append(int(ciphertext, 16))
print("计算S盒线性分布掩码...")
get_S_i_mask(plaintexts, ciphertexts)
print("最佳线性逼近式如下:")
for i in range(8):
get_best_linear(i)
# 推测密钥比特
guessed_key_bits = []
for i in range(8):
alpha, beta = get_best_linear(i)
a = int2bit6(alpha)
b = int2bit4(beta)
alpha_loca = [i * 6 + j for j in range(6) if a[j] == '1']
beta_loca = [i * 4 + j for j in range(4) if b[j] == '1']
possible_key_bits = []
for j in range(2 ** len(alpha_loca)):
key_guess = int2bit6(j)
count = 0
for x, (plain, cipher) in enumerate(zip(plaintexts, ciphertexts)):
s_input = [(plain >> loc) & 1 for loc in alpha_loca]
s_output = [(cipher >> loc) & 1 for loc in beta_loca]
f_result = [int(key_guess[k]) ^ s_input[k] for k in range(len(alpha_loca))]
if self_xor(sum(s_input), len(alpha_loca)) == self_xor(sum(s_output), len(beta_loca)) ^ self_xor(sum(f_result), len(alpha_loca)):
count += 1
possible_key_bits.append((j, count))
# 选择出现次数最多的密钥比特作为推测结果
max_count = max([item[1] for item in possible_key_bits])
max_count_keys = [item[0] for item in possible_key_bits if item[1] == max_count]
guessed_key_bits.extend(int2bit6(random.choice(max_count_keys)))
print("推测的密钥比特:", ''.join(guessed_key_bits))
# 对比推测密钥与真实密钥
correct_bits = sum([int(a) == int(b) for a, b in zip(guessed_key_bits, bin(int(key.encode().hex(), 16))[2:].zfill(64))])
print(f"推测密钥与真实密钥的一致性比例: {correct_bits / 64:.2%}")
if __name__ == '__main__':
linear_attack()