Streamcipher Learning1
Streamcipher Learning
LFSR
简介
对于给定的初状态(a1 , ... ,an)和反馈函数f
[反馈函数一般为\(a_{i+n}=\sum_{j=1}^nc_ja_{i+n-j}\)]
有f(ai , ... ,ai+n-1) = (ai+1 , ... ,ai+n)
考虑到f是线性函数,我们将其表示为矩阵的形式
=>(ai+1 , ... ,ai+n) = A(ai , ... ,ai+n-1)
而在学习了矩阵后我们知道移位运算,取位运算,异或运算均能看出线性变换,能够由矩阵乘法表示,这就将本来抽象的位运算转化为了熟悉的矩阵运算
对几种伪随机数生成器的分析都可能会用到这种思想
为了使得密钥流输出更加复杂,会对lfsr进行一些变化,使其成为非线性反馈移位寄存器,常见的有三种:
1.非线性组合生成器,对多个lfsr使用一个非线性组合函数
2.非线性滤波器,对一个lfsr输出使用一个非线性组合函数
3.钟控生成器,使用一个或多个lfsr控制其它lfsr输出
下面我们考虑lfsr的生成周期
通常N位的线性反馈寄存器最多有 \(2^N\)个不同的状态。但是如果出现初值为N个0的情况,线性反馈寄存器陷入死循环,要排除掉。所以N位线性反馈寄存器能产生最长的不重复序列为\(2^N-1\)
引入线性变换的特征多项式\(f(x)=x^n-\sum_{i=1}^{n}c_ix^{n-i}\)
定义互反多项式为f'(x)=\(x^nf(\frac{1}{x}\))
已知某个n级线性反馈移位寄存器的特征多项式,那么该序列对应的生成函数为A(x)=\(\frac{p(x)}{f'(x)}\)
p(x)=\(\sum_{i=1}^n(c_{n-i}x^{n-i}\sum_{j=1}^ia_jx^{j-1})\)
序列的周期为生成函数既约真分式的分母的周期
达到最长周期的序列称为m序列
首先python实现一下lfsr的加密过程
实现
def lfsr(seed,mask):
state=seed
Mask=int(mask,2)
statelen=2**len(mask)-1
t=state&Mask
out=0
while t:
out^=t&1
t=t>>1
state=(state<<1^out)&statelen
return state,out
逆向
下面分别采用位运算和矩阵运算(本质是一样的)对lfsr进行逆向
我们已知生成的连续len(初态)的输出序列(这里为了方便使用(\(a_{len+1} , ··· , a_{2len}\)),和掩码
1.矩阵方式
c=''
mask=''
Mask=Matrix.zero(len(mask))
for i in range(len(mask)-1):
Mask[i,i+1]=1
for i in range(len(mask)):
Mask[len(mask)-1,i]=mask[i]
Mask=Matrix(GF(2),Mask)
v=[]
for i in range(len(c)):
v.append(c[i])
V=vector(GF(2),v)
m=Mask^(-1)*V
2.位运算
c=''
c=int(c,2)
def backtrace(state, mask):
mask=int(mask,2)
mask = ((mask & (2**7-1)) << 1) + 1
i = state & mask
outPut = 0
while i != 0:
outPut ^= (i & 1)
i = i >> 1
state >>= 1
return (outPut <<7) + state
for i in range(8):
c=backtrace(c,mask)
print(bin(c)[2:].rjust(8,'0'))
在位运算操作的时候可以发现mask的首位不为0才能还原,用矩阵同样会发现当首位为1时矩阵是不满秩的,很显然因为第一列是全0的
示例
babylfsr
import hashlib
from secret import KEY,FLAG,MASK
assert(FLAG=="de1ctf{"+hashlib.sha256(hex(KEY)[2:].rstrip('L')).hexdigest()+"}")
assert(FLAG[7:11]=='1224')
LENGTH = 256
assert(KEY.bit_length()==LENGTH)
assert(MASK.bit_length()==LENGTH)
def pad(m):
pad_length = 8 - len(m)
return pad_length*'0'+m
class lfsr():
def __init__(self, init, mask, length):
self.init = init
self.mask = mask
self.lengthmask = 2**(length+1)-1
def next(self):
nextdata = (self.init << 1) & self.lengthmask
i = self.init & self.mask & self.lengthmask
output = 0
while i != 0:
output ^= (i & 1)
i = i >> 1
nextdata ^= output
self.init = nextdata
return output
if __name__=="__main__":
l = lfsr(KEY,MASK,LENGTH)
r = ''
for i in range(63):
b = 0
for j in range(8):
b = (b<<1)+l.next()
r += pad(bin(b)[2:])
with open('output','w') as f:
f.write(r)
#001010010111101000001101101111010000001111011001101111011000100001100011111000010001100101110110011000001100111010111110000000111011000110111110001110111000010100110010011111100011010111101101101001110000010111011110010110010011101101010010100101011111011001111010000000001011000011000100000101111010001100000011010011010111001010010101101000110011001110111010000011010101111011110100011110011010000001100100101000010110100100100011001000101010001100000010000100111001110110101000000101011100000001100010
常见攻击方式:
给出2n个连续生成数据(ai , ... ,ai+2n-1)
记Sj=(ai+(j-1) , ... ,ai+(j-1)+n-1) (j=1,2,...,n+1)
构造矩阵X=[S1,S2,...,Sn] => Sn+1 = (c1 , ... ,cn)X =>(c1 , ... ,cn) =Sn+1 X-1
对所给数据爆破8位
import hashlib
c1='001010010111101000001101101111010000001111011001101111011000100001100011111000010001100101110110011000001100111010111110000000111011000110111110001110111000010100110010011111100011010111101101101001110000010111011110010110010011101101010010100101011111011001111010000000001011000011000100000101111010001100000011010011010111001010010101101000110011001110111010000011010101111011110100011110011010000001100100101000010110100100100011001000101010001100000010000100111001110110101000000101011100000001100010'
for i in range(256):
c=c1+bin(i)[2:].rjust(8,'0')
A=Matrix.zero(256)
for i in range(256):
for j in range(256):
A[i,j]=c[i+j]
G=GF(2)
A=Matrix(G,A)
v=[]
for i in range(256,512):
v.append(int(c[i]))
V=Matrix(G,v)
try:
M=V*A^(-1)
M=list(vector(M))
maskM=Matrix.zero(256)
for i in range(255):
maskM[i,i+1]=1
for i in range(256):
maskM[255,i]=M[i]
maskM=Matrix(G,maskM)
V=vector(V)
mask=maskM^512
m=((mask)^(-1))*V
key=''
for i in range(256):
key+=str(m[i])
key=int(key,2)
FLAG="de1ctf{"+hashlib.sha256(hex(key)[2:].rstrip('L').encode()).hexdigest()+"}"
if FLAG[7:11]=='1224':
print(FLAG)
except:
continue
c=''
Tiny LFSR
import sys
from binascii import unhexlify
if(len(sys.argv)<4):
print("Usage: python Encrypt.py keyfile plaintext ciphername")
exit(1)
def lfsr(R, mask):
output = (R << 1) & 0xffffffffffffffff
i=(R&mask)&0xffffffffffffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)
R = 0
key = ""
with open(sys.argv[1],"r") as f:
key = f.read()
R = int(key,16)
f.close
mask = 0b1101100000000000000000000000000000000000000000000000000000000000
a = ''.join([chr(int(b, 16)) for b in [key[i:i+2] for i in range(0, len(key), 2)]])
f=open(sys.argv[2],"r")
ff = open(sys.argv[3],"wb")
s = f.read()
f.close()
lent = len(s)
for i in range(0, len(a)):
ff.write((ord(s[i])^ord(a[i])).to_bytes(1, byteorder='big'))
for i in range(len(a), lent):
tmp=0
for j in range(8):
(R,out)=lfsr(R,mask)
tmp=(tmp << 1)^out
ff.write((tmp^ord(s[i])).to_bytes(1, byteorder='big'))
ff.close()
此题并没有考lfsr的细节,因为异或的性质,我们只需爆破完key,重走一遍加密过程即可
代码如下
f=open('cipher.txt','rb')
ff=open('flag_encode.txt','rb')
pt='sdgfjkahblskdjxbvfskljdfbguisldfbvghkljsdfbghsjkldhbgjklsdbgvlkjsdgbkljb sdkljfhwelo;sdfghioeurthgbnjl k'
c=f.read()
flagc=ff.read()
mask = 0b1101100000000000000000000000000000000000000000000000000000000000
def lfsr(R, mask):
output = (R << 1) & 0xffffffffffffffff
i=(R&mask)&0xffffffffffffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)
def decrypt1(key, c,m):
for i in range(len(a)):
m+=chr(c[i]^ord(key[i]))
return m
def decrypt2(R,mask,m,c):
for i in range(len(a),len(c)):
tmp=0
for j in range(8):
(R,out)=lfsr(R,mask)
tmp=(tmp<<1)^out
m+=chr(c[i]^tmp)
return m
for i in range(1,len(c)):
a1=''
for j in range(i):
a1+=chr(c[j]^ord(pt[j]))
key=''
for i in range(len(a1)):
key+=hex(ord(a1[i]))[2:].rjust(2,'0')
R=int(key,16)
a = ''.join([chr(int(b, 16)) for b in [key[i:i + 2] for i in range(0, len(key), 2)]])
m=''
m+=decrypt1(key,flagc,m)
m+=decrypt2(R,mask,m,flagc)
print(m)
FilterRandom[西湖论剑]
import random
from secret import init1,init2,flag
assert flag==b'DASCTF{%d-%d}'%(init1,init2)
class lfsr():
def __init__(self, init, mask, length):
self.init = init
self.mask = mask
self.lengthmask = 2**length-1
def next(self):
nextdata = (self.init << 1) & self.lengthmask
i = self.init & self.mask & self.lengthmask
output = 0
while i != 0:
output ^= (i & 1)
i = i >> 1
nextdata ^= output
self.init = nextdata
return output
def my_filter(c1,c2):
if random.random()>0.1:
return str(c1)
else:
return str(c2)
N=64
mask1=random.getrandbits(N)
mask2=random.getrandbits(N)
print(mask1)
print(mask2)
l1=lfsr(init1,mask1,N)
l2=lfsr(init2,mask2,N)
output=''
for i in range(2048):
output+=my_filter(l1.next(),l2.next())
print(output)
'''
17638491756192425134
14623996511862197922
10001011010100011000100101001011100010110111001100001110000111011011100101101101000111101100010111100011000011111111010101111100101010101100010100000111011010011110111000100000101100101010110100111100011000101010101011011111011011000001101001011000010000011110001111001111011100110011111111101000111101001010000110001110111101001001101011101101001010001101010010110000000000001001101100101011110011010110011010110110011001001111001010100011110111100100010110111100110010000000010010011110001100000011000001110001000000010000100100101100000011100000011110101001011010011010100001101000010100100000011001011001000110000000000111011101000110010110111110010101110010001010001111111000011010000011001110111001000010011000000111010111100000100010011001111101110110100100011111000111000011111101010010110011111100010000100101011000001010101111101111001000011101111000111000101011010111100110001011011100101001010110110110110011100100111100110001101110010100010111100000110000010110100010001100011011001100100110101110010100011101110110010000010011100000011100000101010011011111110000100000010001010111011011111110100111100011100011110110010001011101111001011101010110111001001000111001001111001111110111111100001111100100110011111110110101000011010111110010001100000111100010011100011010000101010111010101101000011001110011000000110110110001101100110101110010010111011100110101000110000011001010100000110000000001110010001010001001101111100001111111011010010011100110010000111010001001111111110000010101110011010100100101101100111000010110100110010001010110111110011000111011101110100010000110110110011001011111011111000000000000001110000001000011000110111000000110100110110001111011111100010010011100101010000111000011111010000001010010011101010010110011000000001111110000000010111011000010001111000100110101110001000011111001101111111100011111011001001110000101001101110100111010011011101000110010000001001000001100110001110101100001000110100100010111101100010100110011111010011100100001101111010000110110101111111001111011100001101100000001101111100100
'''
第一次做带滤波的lfsr,调了一下午总算整出来了,一直给我卡在把sage的^当异或上😢
但是对这个大概率存在64位连续lfsr1所生成的数,企图具体算过,但是太复杂没算出来,就只能先这么理解着了,但是我在调的时候很容易发现有几次的degree(拟合度)相当地高,也能说明这个问题
后面看大佬的wp就觉得写的代码还是太丑了,有时候虽然矩阵运算能给你在理解问题的时候提供更加清晰的思路,但是在写代码的时候真就比用位运算要多写很多,争取下次复现MT19937的时候两种方式都逆一下吧
附上代码
mask1=17638491756192425134
mask2=14623996511862197922
out='10001011010100011000100101001011100010110111001100001110000111011011100101101101000111101100010111100011000011111111010101111100101010101100010100000111011010011110111000100000101100101010110100111100011000101010101011011111011011000001101001011000010000011110001111001111011100110011111111101000111101001010000110001110111101001001101011101101001010001101010010110000000000001001101100101011110011010110011010110110011001001111001010100011110111100100010110111100110010000000010010011110001100000011000001110001000000010000100100101100000011100000011110101001011010011010100001101000010100100000011001011001000110000000000111011101000110010110111110010101110010001010001111111000011010000011001110111001000010011000000111010111100000100010011001111101110110100100011111000111000011111101010010110011111100010000100101011000001010101111101111001000011101111000111000101011010111100110001011011100101001010110110110110011100100111100110001101110010100010111100000110000010110100010001100011011001100100110101110010100011101110110010000010011100000011100000101010011011111110000100000010001010111011011111110100111100011100011110110010001011101111001011101010110111001001000111001001111001111110111111100001111100100110011111110110101000011010111110010001100000111100010011100011010000101010111010101101000011001110011000000110110110001101100110101110010010111011100110101000110000011001010100000110000000001110010001010001001101111100001111111011010010011100110010000111010001001111111110000010101110011010100100101101100111000010110100110010001010110111110011000111011101110100010000110110110011001011111011111000000000000001110000001000011000110111000000110100110110001111011111100010010011100101010000111000011111010000001010010011101010010110011000000001111110000000010111011000010001111000100110101110001000011111001101111111100011111011001001110000101001101110100111010011011101000110010000001001000001100110001110101100001000110100100010111101100010100110011111010011100100001101111010000110110101111111001111011100001101100000001101111100100'
def lfsr(seed,mask):
state=seed
Mask=int(mask,2)
statelen=2**len(mask)-1
t=state&Mask
out=0
while t:
out^^=t&1
t=t>>1
state=(state<<1^^out)&statelen
return state,out
def fit(a,b):
degree=0
dif=[]
for i in range(len(a)):
if a[i]==b[i]:
degree+=1
else:
dif.append((i,b[i]))
degree+=0
return dif,degree
M1=Matrix.zero(64)
mask1=bin(mask1)[2:].rjust(64,'0')
for i in range(63):
M1[i,i+1]=1
for i in range(64):
M1[63,i]=mask1[i]
M1=Matrix(GF(2),M1)
M2=Matrix.zero(64)
mask2=bin(mask2)[2:].rjust(64,'0')
v2=[]
for i in range(63):
M2[i,i+1]=1
for i in range(64):
M2[63,i]=mask2[i]
v2.append(mask2[i])
M2=Matrix(GF(2),M2)
v2=vector(GF(2),v2)
for i in range(len(out)-64):
c=out[i:i+64]
v=[]
for j in range(64):
v.append(int(c[j]))
V=vector(GF(2),v)
m=list((M1^(i+64))^(-1)*V)
mm=''
for i in range(len(m)):
mm+=str(m[i])
state=int(mm,2)
line=''
for i in range(2048):
state,l=lfsr(state,mask1)
line+=str(l)
dif,degree=fit(line,out)
if degree>1700:
answer=[[0,1]]
print(mm)
num=[]
val=''
for i in dif:
a,b=i
num.append(a)
val+=b
for i in range(len(val)-64):
B=[]
numm=[]
vall=[]
for j in range(64):
numm.append(num[i+j])
vall.append(val[i+j])
for i in range(64):
BB=vector(v2*M2^numm[i])
B.append(list(BB))
B=Matrix(GF(2),B)
vv=vector(GF(2),vall)
try:
mmm=B^(-1)*vv
mmm=list(vector(mmm))
mmmm=''
for j in range(len(mmm)):
mmmm+=str(mmm[j])
for i in range(len(answer)):
if mmmm == str(answer[i][1]):
answer[i][0]+=1
else:
answer.append([0,mmmm])
for i in range(len(answer)):
if answer[i][0]>10:
print(answer[i][1])
except:
continue
print('--------------------------------------------')