# q-solved - zer0pts CTF 2022 ###### tags: `zer0pts CTF 2022` `rev` `quantum` Writeups: https://hackmd.io/@ptr-yudai/rJgjygUM9 ## Overview You're given a quantum circuit to calculate something and prints the flag based on the measurement. ## Analysis The circuit looks like this: ```python= main.x(target) main.h(target) for i in range(circ['memory']): main.h(i) t = circ['memory'] for cs in circ['circuit']: ctrl = ''.join(['0' if x else '1' for (x, _) in cs]) l = [c for (_, c) in cs] sub.append(XGate().control(len(cs), ctrl_state=ctrl), l + [t]) t += 1 sub.append(XGate().control(na, ctrl_state='0'*na), [i for i in range(nq, nq + na)] + [target]) for cs in circ['circuit'][::-1]: t -= 1 ctrl = ''.join(['0' if x else '1' for (x, _) in cs]) l = [c for (_, c) in cs] sub.append(XGate().control(len(cs), ctrl_state=ctrl), l + [t]) sub.h([i for i in range(nq)]) sub.append(XGate().control(nq, ctrl_state='0'*nq), [i for i in range(nq)] + [target]) sub.h([i for i in range(nq)]) for i in range(round(0.785 * int(gmpy2.isqrt(2**nq)) - 0.5)): main.append(sub, [i for i in range(na + nq + 1)]) for i in range(nq): main.measure(i, i) ``` After initializing the input by Hadamard gates, it calls a subroutine `sub` many times. At last, the input bits are measured and the most possible combination of the measured bits become the flag. You can devide the subroutine into 4 parts. The first part calculates some logical operations: ```python t = circ['memory'] for cs in circ['circuit']: ctrl = ''.join(['0' if x else '1' for (x, _) in cs]) l = [c for (_, c) in cs] sub.append(XGate().control(len(cs), ctrl_state=ctrl), l + [t]) t += 1 ``` The second part applies multi-controlled X gate to ancilla bit with the result of the previous operations. ```python sub.append(XGate().control(na, ctrl_state='0'*na), [i for i in range(nq, nq + na)] + [target]) ``` The third part reverses the operations of the first part, which is just restoring the input state. ```python for cs in circ['circuit'][::-1]: t -= 1 ctrl = ''.join(['0' if x else '1' for (x, _) in cs]) l = [c for (_, c) in cs] sub.append(XGate().control(len(cs), ctrl_state=ctrl), l + [t]) ``` The last part is applying Hadamard gates and X gate. ```python sub.h([i for i in range(nq)]) sub.append(XGate().control(nq, ctrl_state='0'*nq), [i for i in range(nq)] + [target]) sub.h([i for i in range(nq)]) ``` This is obviously the circuit for amplitude amplification. So, we can understand the circuit is searching an input that satisfies the given logical operations. This is equivalent to a SAT solver. In fact, you can input the circuit JSON to Z3 without any calculation. ## Solver ```python= import json from z3 import * with open("../distfiles/circuit.json", "r") as f: circ = json.load(f) bits = [Bool(f'b{i}') for i in range(circ['memory'])] s = Solver() for cs in circ['circuit']: l = [] for (x, c) in cs: l.append(bits[c] if x else Not(bits[c])) s.add(Or(l)) r = s.check() if r == sat: m = s.model() f = 0 for i, b in enumerate(bits): f |= (1 if m[b] else 0) << i print(int.to_bytes(f, circ['memory']//8, 'little')) else: print(":(") ```