Securinets Quals CTF 2023 - 0-8-4 Quantum Writeup

Securinets Quals CTF 2023 - 0-8-4 Quantum Writeup

ADM-MIDA0UI

While on a mission, one of our teams discovered a weird suitecase locked with a 7-character password. The object had this matrix carved on its side, barely readable:


0.7056 0. 0. 0.9
0. 0.5318 0.168 0.
0. 0.168 -1.1246 0.
0.9 0. 0. 0.7882

After analysis, we found out that the suitecase was emitting a transmission containing more informations, and we are convinced that the password is supposed to be the real part of the smallest eigenvalue, that needs to be calculated from the unitary matrix of the quantum circuit which corresponds to some simulation with suitcase matrix.

The transmission also stated some weird conditions: the trotter number is 1 and the simulation is done at t=1???

I don’t know what that means exacly but I figued maybe you will?

Flag format: Securinets{XXXXXXX}

Author: mida0ui

Decomposing Hamiltonian into Pauli Terms

import numpy as np
import itertools
import functools as ft
from numpy.linalg import eig

from qiskit import *

def decompose_ham_to_pauli(H):
    n = int(np.log2(len(H)))
    N = 2 ** n

    # Sanity Checks
    if H.shape != (N, N):
        raise ValueError(
            "The Hamiltonian should have shape (2**n, 2**n), for any qubit number n>=1"
        )

    if not np.allclose(H, H.conj().T):
        raise ValueError("The Hamiltonian is not Hermitian")

    sI = np.eye(2, 2, dtype=complex)
    sX = np.array([[0, 1], [1, 0]], dtype=complex)
    sZ = np.array([[1, 0], [0,-1]], dtype=complex)
    sY = complex(0,-1)*np.matmul(sZ,sX)
    paulis = [sI, sX, sY, sZ]
    paulis_label = ['I', 'X', 'Y', 'Z']
    obs = []
    coeffs = []
    matrix = []
    
    for term in itertools.product(paulis, repeat=n):
        matrices = [pauli for pauli in term]
        coeff = np.trace(ft.reduce(np.kron, matrices) @ H) / N 
        coeff = np.real_if_close(coeff).item()
        
        if not np.allclose(coeff, 0): 
            coeffs.append(coeff)
            obs.append(''.join([paulis_label[[i for i, x in enumerate(paulis) 
            if np.all(x == t)][0]]+str(idx) for idx, t in enumerate(reversed(term))]))
            matrix.append(ft.reduce(np.kron, matrices))

    return obs, coeffs , matrix
H0 = np.array([
                [ 0.7056    ,  0.         , 0.       ,  0.9        ],
                [ 0.        ,  0.5318     , 0.168     ,  0.         ],
                [0.        , 0.168       , -1.1246   , 0.         ],
                [ 0.9       ,  0.         , 0.       ,  0.7882     ]
                ])

a, b , c = decompose_ham_to_pauli(H0)
print(a,b)
['I0I1', 'Z0I1', 'X0X1', 'Y0Y1', 'I0Z1', 'Z0Z1'] [0.22525, -0.43475, 0.534, -0.366, 0.39345, 0.52165]

Hamiltonian Simulation

This code applies to any Hamiltonian, not just this task

from qiskit import *
from qiskit.circuit.library import U2Gate
from qiskit.quantum_info.operators import Operator, Pauli

def exp_all_z(circuit, quantum_register, pauli_idexes, control_qubit=None, t=1):
    
    if len(pauli_idexes)== 0:
        return
    
    # the controlled_exp(iIt) special case
    if len(pauli_idexes) == 0 and control_qubit is not None:
        circuit.add_register(control_qubit.register)
        circuit.u1(t, control_qubit)
        return
        
    # the first CNOTs
    for i in range(len(pauli_idexes) - 1):
        circuit.cx(quantum_register[pauli_idexes[i]], quantum_register[pauli_idexes[i + 1]])
    
    # Rz gate
    if control_qubit is None:
        #print(pauli_idexes)
        circuit.rz(-2 * t, quantum_register[pauli_idexes[-1]])
    else:
        circuit.add_register(control_qubit.register)
        circuit.crz(-2 * t, control_qubit, quantum_register[pauli_idexes[-1]])
    
    # the second CNOTs
    for i in reversed(range(len(pauli_idexes) - 1)):
        circuit.cx(quantum_register[pauli_idexes[i]], quantum_register[pauli_idexes[i + 1]])


def exp_pauli(pauli, quantum_register, control_qubit=None, t=1):

    if len(pauli) != len(quantum_register):
        raise Exception("Pauli string doesn't match to the quantum register")

    pauli_circuit = QuantumCircuit(quantum_register)
    circuit_bracket = QuantumCircuit(quantum_register)
    pauli_idexes = []

    for i in range(len(quantum_register)):
        if pauli[i] == 'I':
            continue
        elif pauli[i] == 'Z':
            pauli_idexes.append(i)
        elif pauli[i] == 'X':
            circuit_bracket.h(quantum_register[i])
            pauli_idexes.append(i)
        elif pauli[i] == 'Y':
            circuit_bracket.append(U2Gate(np.pi / 2, np.pi / 2), [quantum_register[i]])
            pauli_idexes.append(i)
        

    pauli_circuit &= circuit_bracket
    exp_all_z(pauli_circuit, quantum_register, pauli_idexes, control_qubit, t)
    pauli_circuit &= circuit_bracket

    return pauli_circuit

def hamiltonian_simulation(hamiltonian, quantum_register=None, control_qubit=None, t=1, trotter_number=1):

    if quantum_register is None:
        quantum_register = QuantumRegister(len(list(hamiltonian.keys())[0]))
    if control_qubit in quantum_register:
        raise Exception("the control qubit is in the target register")

    delta_t = t / trotter_number
    exp_hamiltonian = QuantumCircuit(quantum_register)
    exp_delta_t = QuantumCircuit(quantum_register)

    for pauli in hamiltonian:
        weight = hamiltonian[pauli]
        exp_delta_t &= exp_pauli(pauli, quantum_register, control_qubit, weight * delta_t)

    for i in range(trotter_number):
        exp_hamiltonian &= exp_delta_t

    return exp_hamiltonian

hamiltonian = {"II": 0.22525, "ZI": -0.43475, "XX": 0.534, "YY": -0.366, "IZ": 0.39345, "ZZ": 0.52165}

quantum_register = QuantumRegister(2, 'q')
circuit = hamiltonian_simulation(hamiltonian, quantum_register, t= 1)

print(circuit)


op = Operator(circuit)
hamiltonian_matrix = op.to_matrix()

w,v=eig(hamiltonian_matrix)
print('E-value:', w)
print('E-vector', v)

w.sort()
print(w[0])
     ┌────────────┐┌───┐                        ┌───┐┌─────────────┐     »
q_0: ┤ Rz(0.8695) ├┤ H ├──■──────────────────■──┤ H ├┤ U2(π/2,π/2) ├──■──»
     └───┬───┬────┘└───┘┌─┴─┐┌────────────┐┌─┴─┐├───┤├─────────────┤┌─┴─┐»
q_1: ────┤ H ├──────────┤ X ├┤ Rz(-1.068) ├┤ X ├┤ H ├┤ U2(π/2,π/2) ├┤ X ├»
         └───┘          └───┘└────────────┘└───┘└───┘└─────────────┘└───┘»
«                       ┌─────────────┐                                        
«q_0: ───────────────■──┤ U2(π/2,π/2) ├─────────────────■───────────────────■──
«     ┌───────────┐┌─┴─┐├─────────────┤┌─────────────┐┌─┴─┐┌─────────────┐┌─┴─┐
«q_1: ┤ Rz(0.732) ├┤ X ├┤ U2(π/2,π/2) ├┤ Rz(-0.7869) ├┤ X ├┤ Rz(-1.0433) ├┤ X ├
«     └───────────┘└───┘└─────────────┘└─────────────┘└───┘└─────────────┘└───┘
E-value: [0.92902529-0.37001624j 0.14792497+0.98899859j 0.20660057-0.97842537j
 0.94942492+0.31399415j]
E-vector [[ 7.18591252e-01+0.00000000e+00j  4.70253625e-01+5.12335965e-01j
  -2.50284233e-17-2.85363428e-17j  1.45587101e-16+5.50711599e-17j]
 [-2.11885760e-17+1.21782362e-17j  2.40755729e-17+7.62058536e-18j
  -1.12796042e-01+4.66112698e-03j  9.93607230e-01+0.00000000e+00j]
 [-6.58636545e-18+4.88403480e-17j  4.17816855e-17-2.91119216e-17j
   9.93607230e-01+0.00000000e+00j  1.12796042e-01+4.66112698e-03j]
 [-4.70253625e-01+5.12335965e-01j  7.18591252e-01+0.00000000e+00j
   9.35231913e-17-7.66091811e-18j -5.45225695e-17+9.51397128e-17j]]
(0.14792496868373806+0.9889985862679043j)

Flag: Securinets{0.14792}

gg idek for solving this challenge!

  • Title: Securinets Quals CTF 2023 - 0-8-4 Quantum Writeup
  • Author: ADM-MIDA0UI
  • Created at: 2023-08-06 20:00:00
  • Updated at: 2024-05-17 21:05:17
  • Link: https://admida0ui.de/2023/08/06/0-8-4/
  • License: This work is licensed under CC BY-NC-SA 4.0.
On this page
Securinets Quals CTF 2023 - 0-8-4 Quantum Writeup