Exp2 Theory: Many Time Pad (MTP)多次使用流密码
参考实验代码:
密码学实验一:Many Time Pad(多次使用流密码)_ustcsse2019-lee的博客-CSDN博客
参考原理:
OTP是无条件安全的:即使攻击者拥有无限的计算资源,都不可能破译OTP加密的密文。探索在使用一次一密的方法加密时,密钥被重复使用时的已知密文攻击方法。
题目:如下所示是 11 个使用同一个密钥按照 one time pad 方法进行加密得到的密文。请使用前 10 个密文进行分析,并对目标密文进行解密。其中消息(明文)是[a-zA-Z]以及空格组成的字符,使用 ascii 编码(十进制),密文使用 hex 编码(字符的ascii码值的16进制表示)。
密文 = 明文 ⊕ 密钥 密文 1 ⊕ 密文 2 = 明文 1 ⊕ 明文 2 ⊕ 密钥 ⊕ 密钥 = 明文 1 ⊕ 明文 2 \text{密文}=\text{明文}\oplus \text{密钥} \\ \text{密文}_1\oplus \text{密文}_2=\text{明文}_1\oplus \text{明文}_2\oplus \text{密钥}\oplus \text{密钥}=\text{明文}_1\oplus \text{明文}_2 密文=明文⊕密钥密文1⊕密文2=明文1⊕明文2⊕密钥⊕密钥=明文1⊕明文2
上述运算表明两个密文的异或,等于对应明文的异或。这是很危险的性质,高明的攻击者可以通过频率分析,来破译这些密文。如果字符串C1异或上其他所有密文可能得到以下内容,只保留英文字符,其余字符以 “.” 代替。
可以观察到,有些列上有大量的英文字符,有些列一个英文字符都没有。
🐒ASCII码表
ASCII 全称为 ( American Standard Code for Information Interchange),简单的说,就是用 7 位二进制 ( 即 十进制表示为 0 到 127 ) 去编码我们生活中常见的数字,大小写字母。
ascii 码表在 Linux 下可以通过 man ascii
指令查看。它的性质有:
0x20
是空格。 低于0x20
的,全部是起特殊用途的字符;0x20~0x7E
的,是可打印字符。0x30~0x39
是数字0,1,2...9
。0x41~0x5A
是大写字母A-Z
;0x61~0x7A
是小写字母a-z
.
💡 存在一个至关重要的规律:小写字母 xor 空格,会得到对应的大写字母;大写字母 xor 空格,会得到小写字母。所以如果 x ⊕ y x\oplus y x⊕y得到一个英文字母,那么 x , y x,y x,y中的某一个有很大概率是空格。再来回头看上面密文C1 xor 其他密文——也就等于明文M1 xor 其他明文,如果第col列存在大量的英文字母,可以猜测 M 1 [ c o l ] M_1[col] M1[col]是一个空格。那一列英文字母越多,把握越大。如果M1的第col列是空格,则有: M i [ c o l ] = M 1 [ c o l ] ⊕ M i [ c o l ] ⊕ M 1 [ c o l ] = M 1 [ c o l ] ⊕ M i [ c o l ] ⊕ 0 x 20 M_i[col]=M_1[col] \oplus M_i[col] \oplus M_1[col] = M_1[col] \oplus M_i[col] \oplus 0x20 Mi[col]=M1[col]⊕Mi[col]⊕M1[col]=M1[col]⊕Mi[col]⊕0x20,也就是只要知道某个字符串C1的某一位是空格,就可以恢复出所有明文在这一列的值。恢复过程为: M i [ c o l ] = C 1 [ c o l ] ⊕ C i [ c o l ] ⊕ 0 x 20 M_i[col]=C_1[col] \oplus C_i[col] \oplus 0x20 Mi[col]=C1[col]⊕Ci[col]⊕0x20。
攻击
攻击过程显而易见:对于每一条密文 C i C_i Ci,去异或其他所有10条密文。然后计数异或结果中每一列有多少个英文字符,作为 M i M_i Mi在这一位是空格的评分。然后记录前10条密文空格评分为10~6处的下标,每一轮依次使用前10条密文,根据 M i [ c o l ] = C 1 [ c o l ] ⊕ C i [ c o l ] ⊕ 0 x 20 M_i[col]=C_1[col] \oplus C_i[col] \oplus 0x20 Mi[col]=C1[col]⊕Ci[col]⊕0x20恢复第11条密文相应下标处的值,一共进行5轮,观察恢复过程。最后对明文结果进行修正获得 M i M_i Mi,同时可得到密钥 k e y = M i ⊕ C i key=M_i \oplus C_i key=Mi⊕Ci,使用密钥可获得此后任意一条密文对应的明文。
- 代码示例(Linux):
将明文、密文和密钥都转化为16进制整数, 密文 = 明文 ⊕ 密钥 密文=明文 \oplus 密钥 密文=明文⊕密钥。
两个长度不同的16进制字符串进行异或时,会将长度较短的字符串前面补0与长字符串长度一致,任何数与0异或后结果不变。例如16进制1
与11
异或:1 ^ 11 = 00000001 ^ 00010001 = 00010000 = (16)d = (10)h。
由于只有一个密钥,当明文长度<密钥长度时,密文长度=密钥长度;当明文长度>密钥长度时,密文长度=明文长度。即不能断言密文长度等于明文长度。由上述示例可以看出:当两个16进制字符串长度不同时(令长字符串长为L,短字符串长为l),在结果中会保留长字符串前L-l位,然后对两个字符串的后l位异或,追加到前L-l位后。即长字符串的前L-l位并没有和短字符串实际值异或,而是和0异或,没有用于判断是否为空格的参考价值。
综上所述,当两个密文长度不同时,应当对较长的密文从第0位开始截取长度较短的密文长度的字符串,然后进行异或分析空格位置。此题要分析第11个密文对应的明文,则其他密文长度一定都不小于该密文,所以可以在初始时都截取前10个密文的后l个字符。
问题记录:以上处理方法基于将16进制整数都转换为10进制整数后再进行异或,但是发现并不能这样处理,因为会违背明文是字符串而不是整数的本意。所以需要将16进制字符串中的每2个字符转换为ascii码(即每2个16进制字符对应明文/密文字符串中的一个字符),需要注意bytes类型没有内置的__xor__函数,所以不能直接使用^进行异或,可以依次对两个bytes类型中的对应的每个ascii码进行异或。【binascii.unhexlify()
将16进制字符串转化为字节数组bytes
类型,字节数组中的每一个元素等于实际字符串中对应每一个字符的ascii码,每两位十六进制字符对应一个ascii码】