# Karen - zer0pts CTF 2022 ###### tags: `zer0pts CTF 2022` Writeups: https://hackmd.io/@ptr-yudai/rJgjygUM9 ## overview It calculates $h = xA$ where $x \in GF(p)^n$ and $A$ is $n \times m$ matrix all values are $0$ or $1$. We are given only $h$ and $p$ and our purpose is to recover $A$ ## observation This problem is known as "Hidden Subset Sum Problem". It is also known that it can be solved by Nguyen-Stern algorithm. The detailed description is written in the some papers. For example https://eprint.iacr.org/2021/1007.pdf ## exploit The core concept of this algorithm is recovering the lattice $L$ such that $A \in L$ form orthogonal lattice of $h$, then find small basis of $L$. The basis may be identical to $A$ because all values of $A$ are consists of $0$ or $1$. ```python= def find_orthogonal_lattice(v, p): """ basis of orthogonal lattice is the n x n matrix, where n is the length of v each rows of orthogonal lattice is orthogonal to v each rows are independent each other """ M = matrix.identity(ZZ, len(v)) for i in range(len(v)): M[i,0] = - v[i] * inverse_mod(v[0], p) % p M[0,0] = p return M def kernelLLL(M): n=M.nrows() m=M.ncols() if m < 2*n: return M.right_kernel().matrix() K=2^(m//2)*M.height() MB = (K*M).T.augment(matrix.identity(m)) # m x (n + m) MB2=MB.LLL().T assert MB2[:n,:m-n]==0 Ke=MB2[n:,:m-n].T return Ke def allones(v): if all([ x in [0, 1] for x in v]): return v if all([ x in [-1, 0] for x in v]): return -v return None def recoverBinary(M5): # 適当に結合して0,1からなるベクトルを作る # -1と1が両方あるようなやつも足して見るといい感じになったりするので # alloneを通してないやつを足している lv = [allones(vi) for vi in M5 if allones(vi)] n = M5.nrows() for v in lv: for i in range(n): nv = allones(M5[i] - v) if nv and nv not in lv: lv.append(nv) nv = allones(M5[i] + v) if nv and nv not in lv: lv.append(nv) return matrix(lv) import ast with open("output.txt", "r") as f: p = ast.literal_eval(f.readline().strip()) h = ast.literal_eval(f.readline().strip()) m = len(h) n = 8 # --- solving --- print("--- start ({}, {}) ---".format(n, m)) # 1. find orthogonal lattice M M = find_orthogonal_lattice(h,p) # m x m matrix print("LLL...") t1 = cputime() M2 = M.LLL() t2 = cputime() print("Done ", t2-t1) MOrtho = M2[:m-n] # n x m matrix # x = orthogonalLatticeAttack(M2[:m-n]) # print(x.nrows(), x.ncols()) ke = kernelLLL(MOrtho) print(ke.nrows(), ke.ncols()) blocksize=2 while blocksize < n: print("bs=",blocksize) if blocksize==2: M5=ke.LLL() else: M5=M5.BKZ(block_size=blocksize) if all([x in [-1, 0, 1] for row in M5 for x in row]): break if blocksize == 2: blocksize = 10 else: blocksize += 10 MB = recoverBinary(M5) for row in MB: flag = int("".join([str(x) for x in row])[::-1], 2) flag = flag.to_bytes((m + 7) // 8, "big") if b"zer0pts{" in flag: print(flag) ```