# [crypto] matrix cipher - CakeCTF 2021 ###### tags: `CakeCTF 2021` ```python= with open("flag.txt", "rb") as f: flag = list(f.read().strip()) def hadamard(M): d = M.determinant() x = 1.0 for row in M: x *= row.norm() return (d / x) ** (1.0/M.ncols()) def keygen(n, d): k = int(floor(sqrt(n) * d)) while True: R = k * Matrix.identity(ZZ, n) + random_matrix(ZZ, n, n, x=-d, y=d) if hadamard(R) >= 0.7: break B = R while True: U = random_matrix(ZZ, n, n, algorithm="unimodular") B = U * B if hadamard(B) <= 0.2: break return B, R def encrypt(B, m): assert B.nrows() == len(m) return vector(ZZ, m) * B + random_vector(ZZ, len(m), x=-50, y=50) B, R = keygen(len(flag), 100) c = encrypt(B, flag) print(list(B)) print(c) ``` これはGGH Cryptosystemという暗号方式(たぶん) なにやら $R$という行列を作った後、ユニモジュラ行列$U$をかけて$B = UR$を作っている。$R$は対角成分が比較的大きく、$R$はhadamard ratioが0.2以下と非常に小さい 暗号化は$c = mB + r$で$r$は比較的小さい乱数のベクトル したがって復号するにはどうにかして$r$の影響を無視して$B^{-1}$をかけることになる。誤差の影響を$c$から$mB$を得るにはCVPを解くとよい。$B$で張られる格子の小さい基底がわかっていれば、Babai's RoundingによってCVPが解ける。 ```python= import ast def babai_rounding(w, basis): b_, _ = basis.gram_schmidt() b = w for i in range(len(b))[::-1]: c = round(b_[i].dot_product(b) / (b_[i].norm() ** 2)) b = b - c * basis[i] return w - b def decrypt(B, R, c): mB = babai_rounding(c, R) return mB * B.inverse() B, c = open("output.txt").read().strip().split("\n") B = Matrix(ZZ, ast.literal_eval(B)) c = vector(ZZ, ast.literal_eval(c)) m = decrypt(B, B.LLL(), c) print("".join([chr(c) for c in m])) ```