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('--------------------------------------------')
posted @ 2022-02-02 20:20  hash_hash  阅读(145)  评论(0编辑  收藏  举报