LFSR

線型回帰フィードバックレジスタ。乱数列を生成する

構造

n bit シフトレジスタのいくつかのbitをXORで線型結合したやつ。雑なイメージだと↓な感じ。(本当は初期状態やtapをランダムに決めてはだめ) tapのことを特性多項式や帰還多項式と言ったりする

class LFSR():
    def __init__(self, n):
        self.state = [getRandBit(1) for _ in range(n)]
        self.taps = [getRandBit(1) for _ in range(n)]
    def next(self):
        output = reduce(xor, [bit&tap for bit,tap in zip(self.state, self.taps)])
        self.state = self.state[1:] + [output]
        return output

入力

出力した0または1が入力としてフィードバックされる。いやフィードバックはどこでも良いが……

出力

0または1が出力される。

周期

0 / 1に周期はあるのか→ある

内部状態が n bit、すなわち 2^n通りなので、最大で 2^n回の出力で必ず同じ内部状態が現れることになる。周期は tap によって決まる

GF(2n)上の多項式との対応

tapは  GF(2^n)上の多項式とみなせる(多項式の係数をベクトルで表したものに見えるので)

帰還多項式と周期の関係

帰還多項式の位数がLFSRの最短周期になる。原始多項式の時最長

実装

まともな実装は多項式の係数をbit列で表して整数になる。fibonacci型とgalois型があるっぽい?

これはzer0lfsrという問題から拝借してきた実装

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

これもわかりやすい実装ですね

def generate_bit_sequence(initial_state):
    state = initial_state
    while True:
        last_bit = state & 1
        yield last_bit
        middle_bit = state >> 3 & 1
        state = (state >> 1) | ((last_bit ^ middle_bit) << 4)

状態の復元

LFSRを「逆に回す」ということもできる(線形性があるので)

参考: 0CTF 2019 Quals | zer0lfsr, 2018 CISCN oldstreamgame | oldstreamgame

ただし、状態を左シフトしていく場合は最上位bitが、右シフトしていく場合は最下位bitが立っていないと状態が一意に定まらないので、これは出来ない。たいていの原始根では最上位bitも最下位bitも立っているはずなので心配しなくてよいが……