作业要求:
网上搜索DES的源代码。
利用DES源代码实现下面功能:
1:给定某个Sbox的输入差分情况下,计算所有输入对和所有Sbox输出差分的分布情况
2:统计DES算法在密钥固定情况,输入明文改变1位、2位,。。。64位时。输出密文位数改变情况。
3:统计DES算法在明文固定情况,输入密钥改变1位、2位,。。。64位时。输出密文位数改变情况。
为了具有客观性,2,3小题需要对多次进行统计,并计算其平均值。
实现:
DES源码:https://github.com/sliveryou/des-python-cipher
编程语言为python,利用pyqt库实现GUI界面。
第一题
该程序也即要寻找满足公式:\(S_j(B_j)\bigoplus S_j(B_j\bigoplusΔB_j)=ΔS_j\)的\(B_j\)的集合。
其中:\(S_j()\)为Sbox运算,\(ΔB_j\)为输入差分(6位),\(ΔS_j\)为输出差分(4位),\(B_j\)为所有可能的输入。
首先要求选定一个Sbox(S1,S2,S3,S4,S5,S6,S7,S8),再输入一个输入差分\(ΔB_j\)。可以通过PyQt5.QtWidgets.comboBox
和PyQt5.QtWidgets.QLineEdit
实现在GUI界面中选择Sbox和输入一个输入差分的功能。
拿到两个变量后,首先遍历4位的二进制数\(ΔS_j\),然后再遍历6位的二进制数\(B_j\),与输入差分\(ΔB_j\)先进行异或运算,然后进行Sbox运算,也即:\(S_j(B_j\bigoplusΔB_j)\)。另外同时再运算\(S_j(B_j)\),代码:
for Delta_S in range(0, 16):
#遍历4位的二进制数
Delta_S = encrypt_function.convert_bin(Delta_S, 4)
for num in range(0, 64):
#遍历6位的二进制数
a = encrypt_function.convert_bin(num, 6)
#进行异或运算
b = encrypt_function.XOR(a, input2)
#进行两次Sbox运算
a_value = get_sbox_value(a, Sbox)
b_value = get_sbox_value(b, Sbox)
根据公式,接下去就要对两次Sbox运算的结果进行异或运算\(S_j(B_j)\bigoplus S_j(B_j\bigoplusΔB_j)\),然后判断结果是否与\(ΔS_j\)相等,如果相等的话就把此时的\(B_j\)保存下来。代码:
xor_reasult = encrypt_function.XOR(a_value, b_value)
if xor_reasult == Delta_S:
reasult = reasult + ' ' + a
最终再将保存的全部结果输出,可以通过PyQt5.QtWidgets.QTableWidgetItem
实现在GUI界面中输出。代码:
def addline(self, Delta_S, count, result):
row = self.tableWidget.rowCount()
self.tableWidget.setRowCount(row + 1)
Delta_S_item = QtWidgets.QTableWidgetItem(Delta_S)
Delta_S_item.setTextAlignment(QtCore.Qt.AlignCenter)
self.tableWidget.setItem(row, 0, QtWidgets.QTableWidgetItem(Delta_S_item))
count_item = QtWidgets.QTableWidgetItem(count)
count_item.setTextAlignment(QtCore.Qt.AlignCenter)
self.tableWidget.setItem(row, 1, count_item)
self.tableWidget.setItem(row, 2, QtWidgets.QTableWidgetItem(result))
运行结果
指定Sbox为S1,输入差分为000001时,运行结果:
分析结果与书本中的分布情况一致。
第二题第三题
第二题与第三题由于实现原理大致相同,因此做了整合。要求首先确定是密钥固定,明文改变模式还是明文固定,密钥改变模式,然后再确定要进行多少次统计。
仍然可以通过PyQt5.QtWidgets.comboBox
和PyQt5.QtWidgets.QLineEdit
实现在GUI界面中选择模式和输入要统计的次数。
随机生成密钥和明文
由于原先DES加密程序中,密钥和明文都是可输入字符,也即普通的ASCII码(32到127),同时为了多次统计的方便,因此考虑每一次统计都在在这个ASCII码区段随机生成8个字符的密钥和明文。代码:
import random
# key_bin、key_str:密钥的二进制形式和字符串形式
# text_bin、text_str:明文的二进制形式和字符串形式
key_bin = ''
text_bin = ''
key_str = ''
text_str = ''
for i in range(0, 8):
key = random.randint(32, 128)
text = random.randint(32, 128)
key_bin = key_bin + encrypt_function.convert_bin(key, 8)
text_bin = text_bin + encrypt_function.convert_bin(text, 8)
key_str = key_str + chr(key)
text_str = text_str + chr(text)
每次改变明文(或密钥)位数
这里利用了异或运算的特点:无论是0和1,与1进行异或结果都会改变。
因此对于要改变的二进制数(假设为8位),那么只需要分别与00000001
,000000011
,00000111
,······,11111111
这8个二进制数进行异或运算,就可以实现改变1位,2位,3位,······,8位。而这8个二进制数对应的十进制数分别是:2^i-1
,其中i=1,2,3,···,8
,这可以利用for循环简单实现。
由于这里要改变的是明文(或密钥)是64位,同理就可以与64个二进制数进行异或运算。代码:
key_num=int(key_bin,2)
for i in range(1, 65):
change_key_bin = encrypt_function.convert_bin(text_num ^ (2 ** i - 1), 64)
局限性在于:没有实现随机改变某一位,只是逐位进行了改变。
计算密文对应改变的位数
通过遍历密文改变前后的每一位,比较是否相同,如果不相同就计数加一,即可计算出改变的位数:
change_num=0
for j in range(0, 64):
if change_cipher_bin[j] != cipher_bin[j]:
change_num = change_num + 1
运行结果
密钥固定,明文改变,统计次数10次:
明文固定,密钥改变,统计次数10次:
总结:
无论是密钥固定、明文改变,还是明文固定、密钥改变;无论每次改变几位,最终密文的改变位数都为在32左右,也即总的64位的一半。每次都只变化50%的位数,使得企图通过该改变密钥或明文、根据密文变化来分析的攻击者根本难以分析出结果。DES不愧为当时著名的加密算法。