基于已知部分明文的单个LFSR序列密码的攻击
单个LFSR的工作原理
在该通用LFSR中,初始加载值为s0,…,sm-1,且在计算sm以及sm+1等值的时候会用到前m个s和m个p。称p为反馈系数。p的值为0,1。
那么LFSR的输出公式可以表示为:
i
=
0
,
s
m
=
p
m
−
1
s
m
−
1
+
.
.
.
+
p
1
s
1
+
p
0
s
0
m
o
d
2
i
=
1
,
s
m
+
1
=
p
m
−
1
s
m
+
.
.
.
+
p
1
s
2
+
p
0
s
1
m
o
d
2
.
.
.
i
=
m
−
1
,
s
2
m
−
1
=
p
m
−
1
s
2
m
−
2
+
.
.
.
+
p
1
s
m
+
p
0
s
m
−
1
m
o
d
2
i=0,\ s_m=p_{m-1}s_{m-1}+...+p_1s_1+p_0s_0\ mod\ 2\\ i=1,\ s_{m+1}=p_{m-1}s_{m}+...+p_1s_2+p_0s_1\ mod\ 2\\ .\\ .\\ .\\ i=m-1,\ s_{2m-1}=p_{m-1}s_{2m-2}+...+p_1s_m+p_0s_{m-1}\ mod\ 2\\
i=0, sm=pm−1sm−1+...+p1s1+p0s0 mod 2i=1, sm+1=pm−1sm+...+p1s2+p0s1 mod 2...i=m−1, s2m−1=pm−1s2m−2+...+p1sm+p0sm−1 mod 2
所以知道最开始的输出值以及p系数的所有值,可以一直推出LFSR的输出,而对于p的求解显然要用到高斯消去、矩阵求逆等其他线性代数的知识。
对于LFSR的攻击
攻击的讨论基于《深入浅出密码学》中的一题(仅做部分摘录并有所更改)。
现在我们攻击另一个基于LFSR的序列密码。为了方便处理字母,26个大写字母和数字0,1,2,3,4,5都使用下 面的映射表示成一个5位向量:
A <–> 0 = 000002
…
Z <–> 25 = 110012
0 <–> 26 = 110102
…
5 <–> 31 = 111112
我们碰巧已知该系统以下信息:
- LFSR的度m=6.
- 每个消息都是以WPI头部开始。
并在信道上发现此系统的以下信息(第四个字母为0):
j5a0edj2b
。。。
所以明文是什么?
此题的关键便是求出LFSR的反馈系数,然后生成所有的密钥从而获得明文。
由于每个信息二点头部都为WPI,用 m ⊗ c = k m \otimes c = k m⊗c=k可以得到部分密钥,从而得到LFSR的初始输入(前m个二进制位)。
题目中已经知道了消息头部,可以得出15个二进制位,而此LFSR的度仅为6,因此可以求出反馈系数。
所以另A = [ s 0 s 1 ⋯ s m − 1 s 1 s 2 ⋯ s m ⋮ ⋮ ⋱ ⋮ s m − 1 s m ⋯ s 2 m − 1 ] \left[ \begin{matrix} s_0 & s_1 & \cdots & s_{m-1} \\ s_1 & s_2 & \cdots & s_{m} \\ \vdots & \vdots & \ddots & \vdots \\ s_{m-1} & s_{m} & \cdots & s_{2m-1} \\ \end{matrix} \right] ⎣⎢⎢⎢⎡s0s1⋮sm−1s1s2⋮sm⋯⋯⋱⋯sm−1sm⋮s2m−1⎦⎥⎥⎥⎤,B = [ p 0 p 1 ⋮ p m ] \left[ \begin{matrix} p_0\\p_1\\ \vdots\\ p_m \end{matrix} \right] ⎣⎢⎢⎢⎡p0p1⋮pm⎦⎥⎥⎥⎤,C = [ s m s m + 1 ⋮ s 2 m − 1 ] \left[ \begin{matrix} s_m\\ s_{m+1} \\ \vdots \\ s_{2m-1} \end{matrix} \right] ⎣⎢⎢⎢⎡smsm+1⋮s2m−1⎦⎥⎥⎥⎤。
那么可列方程A x B mod 2 = C,及B = A-1 x C mod 2。
得出B后,便可以利用LFSR中的递推关系来构造后面的s。
python攻击脚本
hash map构造题目的映射
def map_fun():
hash_map = {}
for idx in range(0, 26):
hash_map[chr(ord('A') + idx)] = idx
hash_map['0'] = 26
hash_map['1'] = 27
hash_map['2'] = 28
hash_map['3'] = 29
hash_map['4'] = 30
hash_map['5'] = 31
return hash_map
当然,不同的序列密码可能使用不同的映射,甚至没有映射,代码仅针对此题使用。
解方程得反馈系数
用映射将WPI转为相应的二进制流,再与对应长度的密文转换的二进制流进行异或可得到密钥流及输入LFSR的初始向量。之后解方程得到系数。
def get_pi(Bin_K, m):
s = list(map(int, Bin_K))
martix_s = []
for idx in range(0, m):
martix_s.append(s[idx:idx+m])
S = np.array(martix_s)
C = np.array(s[m:2*m]).reshape(m, 1)
P = np.matmul(inv_martix(S), C)
pi = list(P)
for idx in range(0, len(pi)):
pi[idx] = int(pi[idx] % 2)
return pi
构造LFSR PRG
利用已知的反馈系数以及初始输入值来推出整个密文。
def LFSR_PRG(p, seed, L):
k = seed
m = len(seed)
for idx in range(m, L):
sidx = 0
for t in range(0, m):
sidx += p[t] * int(k[idx-m+t])
sidx = sidx % 2
k += str(sidx)
return k
完整代码
import numpy as np
#get the invert of the martix
def inv_martix(C):
inv_C = np.linalg.inv(C)
return inv_C
#get the value of pi(i from 0 to m-1)
def get_pi(Bin_K, m):
s = list(map(int, Bin_K))
martix_s = []
for idx in range(0, m):
martix_s.append(s[idx:idx+m])
S = np.array(martix_s)
C = np.array(s[m:2*m]).reshape(m, 1)
P = np.matmul(inv_martix(S), C)
pi = list(P)
for idx in range(0, len(pi)):
pi[idx] = int(pi[idx] % 2)
return pi
#some strange map functions
def map_fun():
hash_map = {}
for idx in range(0, 26):
hash_map[chr(ord('A') + idx)] = idx
hash_map['0'] = 26
hash_map['1'] = 27
hash_map['2'] = 28
hash_map['3'] = 29
hash_map['4'] = 30
hash_map['5'] = 31
return hash_map
def s2b(s, map_fun, m):
bin_str = ""
f = "{:0" + str(m) + "b}"
for idx in range(0, len(s)):
bin_str += f.format(map_fun[s[idx]])
return bin_str
def bin_xor(a, b):
if(len(a) > len(b)):
return "".join([str(int(x) ^ int(y)) for (x, y) in zip(a[:len(b)], b)])
else:
return "".join([str(int(x) ^ int(y)) for (x, y) in zip(a, b[:len(a)])])
def LFSR_PRG(p, seed, L):
k = seed
m = len(seed)
for idx in range(m, L):
sidx = 0
for t in range(0, m):
sidx += p[t] * int(k[idx-m+t])
sidx = sidx % 2
k += str(sidx)
return k
#begin
m = 6
hash_map = map_fun()
Chose_PT = s2b("WPI", hash_map, 5)
CT = "j5a0edj2b"
CT = CT.upper()
CT = s2b(CT, hash_map, 5)
s = bin_xor(Chose_PT, CT)
pi = get_pi(s, m)
k = LFSR_PRG(pi, s[0:m], len(CT) * 5)
PT = bin_xor(CT, k)
message = ""
for idx in range(0, len(PT), 5):
c = PT[idx:idx+5]
for key in hash_map:
if(hash_map[key] == int(c, 2)):
message += key
print(message)
#end
(脚本中的数字5是映射后的二进制位数,根据题目决定)