TokyoWesterns CTF 6th 2020 | XOR and shift cipher

https://furutsuki.hatenablog.com/entry/2020/09/21/092947

xorshift 行列累乗 様々な行列の作り方

#!/usr/bin/python3

s = []
p = 0

def init():
  global s,p
  s = [i for i in range(0,64)]
  p = 0
  return

def randgen():
  global s,p
  a = 3
  b = 13
  c = 37
  s0 = s[p]
  p = (p + 1) & 63
  s1 = s[p]
  res = (s0 + s1) & ((1<<64)-1)
  s1 ^= (s1 << a) & ((1<<64)-1)
  s[p] = (s1 ^ s0 ^ (s1 >> b) ^ (s0 >> c))  & ((1<<64)-1)
  return res

def jump(to):
  # Deleted...
  return

def check_jump():
   ...

check_jump()

init()
for a in range(31337):randgen()

flag = open("flag.txt").read()
assert len(flag) == 256

enc = b""

for x in range(len(flag)):
  buf = randgen()
  sh = x//2
  if sh > 64:sh = 64
  mask = (1 << sh) - 1
  buf &= mask
  jump(buf)
  enc += bytes([ ord(flag[x]) ^ (randgen() & 0xff) ])
  print ("%r" % enc)

open("enc.dat","wb").write(bytearray(enc))
N = 64
F = GF(2)

def L(n):
  m = [[0 for x in range(N)] for y in range(N)]

  for i in range(N - n):
    m[i + n][i] = 1

  return matrix(F, m)

def R(n):
  m = [[0 for x in range(N)] for y in range(N)]

  for i in range(N - n):
    m[i][i + n] = 1

  return matrix(F, m)


def I():
  m = [[0 for x in range(N)] for y in range(N)]

  for i in range(N):
    m[i][i] = 1

  return matrix(F, m)

def O():
  m = [[0 for x in range(N)] for y in range(N)]
  return matrix(F, m)

def genM():
  a = 3
  b = 13
  c = 37

  o = O()
  i = I()
  la = L(a)
  rb = R(b)
  rc = R(c)

  blocks = [
    [i + rc, i + la + rb + la*rb] + [o for _ in range(62)]
  ]
  for j in range(1, N):
    row = [o for _ in range(N)]
    row[(j+1) % N] = i
    blocks.append(row)

  M = block_matrix(F, [*zip(*blocks)])

  return M


def initial_state():
  s = "".join(["{:064b}".format(i) for i in range(N)])
  vec = []
  for c in s:
    vec.append(F(int(c)))
  return Matrix(F, vec)

def getvalue(row, index):
  v = 0
  for i in range(N):
    v = v*2 + int(row[0][index*N + i])
  return v


def dumpstate(a):
  xs = []
  for i in range(N):
    xs.append(getvalue(a, i))
  print(xs)

s = initial_state()
M = genM()

def init():
  global s, M
  s = initial_state()
  M = genM()

def randgen():
  global s, M
  res = (getvalue(s, 0) + getvalue(s, 1)) % ((1<<64)-1)
  s = s * M
  return res

def jump(n):
  global s,M
  s = s * (M^n)


init()
jump(31337)
for x in range(256):
  buf = randgen()
  sh = x//2
  if sh > 64:sh = 64
  mask = (1 << sh) - 1
  buf &= mask
  jump(buf)
  print(randgen() & 0xff)