CHALLENGE DESCRIPTION
Beneath Qubitrix’s corporate towers lies the Global Hyperlink Zone - their prototype quantum internet. Its five access nodes authenticate through specific quantum gate patterns. Retrieve the sequence, stabilize the hyperlink, and force entry into their hidden backbone. One wrong move, and the link collapses.
Datos iniciales:
server.py: Código fuente del servidor.154.57.164.73:32491: Conexión al servidor
Fundamentos#
Qubits#
A diferencia de los bits normales, que pueden tener un valor de 0 o 1, un qubit puede tener un valor de \(0\), \(1\), o una superposición entre ellos. Esto último significa que en un momento dado, el valor de un qubit no necesariamente está determinado, sino que se encuentra en un estado puramente probabilístico, y su valor colapsará a \(0\) o \(1\) al medirlo.
El valor al que colapse al medirlo dependerá de lo que se denomina amplitudes de probabilidad (\(\alpha\), \(\beta\)). La probabilidad de que el qubit sea \(0\) o \(1\) se describe mediante su estado cuántico, representado por la siguiente ecuación:
$$ \ket{\psi} = \alpha\ket{0} + \beta\ket{1} $$\(\alpha\) y \(\beta\) son las amplitudes de probabilidad. Estas amplitudes son números complejos, es decir, pueden ser negativos e imaginarios, lo que permite que ambas amplitudes interfieran entre sí cancelándose y sumándose.
La probabilidad real (un valor entre \(0\) y \(1\)) de que el qubit colapse a \(0\) al medirlo es el cuadrado de su amplitud: \(\alpha^2\). Para el \(1\), la probabilidad es \(\beta^2\). La suma de ambas debe dar 1 por definición.
Para poder visualizar bien estos estados, se suele usar la esfera de Bloch:

Y como representar números complejos (\(\alpha\), \(\beta\)) espacialmente es complicado, suelen traducirse a coordenadas 3D para representarlos en la esfera como otros dos valores \(\theta\) y \(\phi\):
$$ \alpha = cos(\frac{\theta}{2}),\space\beta = e^{i\phi} sin(\frac{\theta}{2})\ \to\ \ket{\psi} = cos(\frac{\theta}{2})\ket{0} + e^{i\phi} sin(\frac{\theta}{2})\ket{1} $$- \(\theta\) mide la probabilidad de colapso: Si es \(0\), la probabilidad de medir 0 en ese qubit es del 100%. Si es \(1\), la probabilidad de medir 1 es del 100%. Si tiene un valor de \(90º\), la probabilidad de medir \(0\) o \(1\) es exactamente 50/50.
- \(\phi\) no cambia la probabilidad de medir \(0\) o \(1\) absolutamente nada, pero afecta a cómo interfiere el qubit a otros al aplicarles puertas lógicas.
Representación#
Para poder aplicar puertas lógicas cuánticas a los qubits, primero hay que dar un paso en la representación: El estado de un qubit se representa como un vector columna de dos dimensiones.
$$ \text{Estado } \ket{0}=\ \begin{pmatrix} 1 \\ 0 \end{pmatrix} $$$$ \text{Estado } \ket{1}=\ \begin{pmatrix} 0 \\ 1 \end{pmatrix} $$Por tanto, cualquier estado es simplemente
$$ \text{Estado } \ket{\psi}=\ \alpha\begin{pmatrix} 1 \\ 0 \end{pmatrix} + \beta\begin{pmatrix} 0 \\ 1 \end{pmatrix} = \begin{pmatrix} \alpha \\ \beta \end{pmatrix} $$Por otro lado, las puertas lógicas, que no hemos visto aún, se representan como matrices. Por ejemplo, la puerta X (Equivalente al NOT), se representa como
$$ X=\ \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} $$Si se lo aplicamos a un qubit en estado \(\ket{0}\):
$$ X\ket{0}=\ \begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix}\begin{pmatrix} 1 \\ 0 \end{pmatrix} = \begin{pmatrix} 0 \\ 1 \end{pmatrix} = \ket{1} $$Inicio#
La idea del challenge es que consigamos hacer un circuito cuántico que haga algo que se nos pida. Si abrimos el código fuente del servidor, veremos que se trata de un sistema que nos permite introducir puertas lógicas cuánticas y sus respectivos parámetros. La librería en uso es Qiskit, que permite generar circuitos cuánticos y simularlos o ejecutarlos en ordenadores cuánticos reales.
Antes de nada, nos fijamos en el código, veremos que en generate_circuit() está la siguiente instrucción:
| |
Esto significa que el circuito usará 5 qubits.
Funcionamiento#
El flujo del programa es el siguiente, según el código fuente:
- El usuario introduce una serie de puertas lógicas cuánticas y los qubits a los que se aplican, con la sintaxis
PUERTA:QUBITS;PUERTA:QUBITS;PUERTA:QUBITS...(yQUBITSes p.ej1o5,2). - Luego se ejecuta el circuito y se miden los valores de los 5 qubits.
- Se repite la ejecución y medición del circuito 256 veces. El proceso en cada ejecución es el siguiente:
- Cada vez, los qubits inician en el estado \(\ket{0}\)
- Se aplican las puertas lógicas especificadas en orden.
- Al llegar al final del circuito, se miden los estados de los qubits, haciéndoles colapsar a \(0\) o \(1\).
- El resultado de cada ejecución se guarda en memoria como una cadena de bits en la que cada uno representa el valor de su respectivo qubit en esa ejecución. P.ej
10110
Requisitos#
Para conseguir el flag, ha de cumplirse lo siguiente:
- El circuito funciona (evidentemente).
| |
- Ningún qubit puede dar como resultado siempre 0 o siempre 1 en las 256 ejecuciones. Tienen que comportarse de forma aleatoria.
| |
- Los qubits 0, 1 y 3 deben dar exactamente la misma secuencia de ceros y unos en las 256 ejecuciones. Lo mismo para los qubits 2 y 4. Además, la secuencia de los qubits 2,4 debe ser diferente a la del grupo 0,1,3.
| |
Puertas lógicas#
Las puertas que pone a nuestra disposición el programa son las siguientes:
| |
De 1 parámetro#
Puerta \(H\)#
Crea una superposición. Mezcla las amplitudes \(\alpha\) y \(\beta\) de forma que el qubit tenga exactamente un 50% de colapsar a 0 y un 50% de colapsar a 1.
$$ H=\frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 1 \\ 1 & -1 \end{pmatrix} $$Puerta \(X\)#
La NOT vista antes. Intercambia las amplitudes \(\alpha\) y \(\beta\). Es como un giro de 180º en la esfera de Bloch.
$$ X=\begin{pmatrix} 0 & 1 \\ 1 & 0 \end{pmatrix} $$Puerta \(S\)#
El estado \(\ket{0}\) no se toca, pero si el qubit tiene alguna probabilidad de ser \(\ket{1}\), se multiplica esa parte por \(i\). Esto equivale a rotar el qubit 90º por el ecuador, sin modificar la probabilidad de colapso.
$$ S=\begin{pmatrix} 1 & 0 \\ 0 & i \end{pmatrix} $$Puerta \(T\)#
Exactamente igual que la S, pero en lugar de rotar 90º, rota 45º.
$$ T=\begin{pmatrix} 1 & 0 \\ 0 & e^{i\pi/4} \end{pmatrix} $$De 2 parámetros#
Estas son puertas controladas. Trabajan (en este caso) sobre 2 qubits, y por tanto sobre vectores de 4 elementos: \(\ket{00},\ket{01},\ket{10} \text{y}\ket{11}\).
Estos estados se definen mediante el producto tensorial y quedan así:
$$ \ket{00}=\begin{pmatrix} 1 \\ 0 \\ 0 \\ 0 \end{pmatrix},\space\ket{01}=\begin{pmatrix} 0 \\ 1 \\ 0 \\ 0 \end{pmatrix},\space\ket{10}=\begin{pmatrix} 0 \\ 0 \\ 1 \\ 0 \end{pmatrix},\space\ket{11}=\begin{pmatrix} 0 \\ 0 \\ 0 \\ 1 \end{pmatrix} $$En \(\ket{AB}\), \(A\) es el estado del primer qubit y \(B\) el del segundo. Solo explico la puerta CX porque las otras dos (CY,CZ) son más complejas y no sirven en este reto.
Puerta \(CX\) (CNOT)#
Si el primer qubit ("qubit de control") es 1, aplica una puerta X al segundo (qubit objetivo).
$$ CX=\begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{pmatrix} $$En sí, lo que hace es cambiar el orden de las dos últimas filas en la matriz columna 4x1.
- \(\ket{00}\) se queda como \(\ket{00}\)
- \(\ket{01}\) se queda como \(\ket{01}\)
- \(\ket{10}\) se transforma en \(\ket{11}\)
- \(\ket{11}\) se transforma en \(\ket{10}\)
Creando el circuito#
Recordando los requisitos para conseguir el flag, necesitamos hacer dos grupos diferentes cuyos elementos siempre den el mismo resultado: Los qubits (\(0,1,3\)) y (\(2,4\)). Además, debe haber aleatoriedad, esto significa que al menos vamos a necesitar puertas Hadamard (\(H\)) para generarla, puesto que todos los qubits inician a \(\ket{0}\).
Para conseguir grupos de qubits con las mismas secuencias, una forma de entrelazar las probabilidades de dos qubits es mediante una puerta \(H\) y otra \(CX\).
Por ejemplo, vamos a entrelazar las probabilidades del qubit 0 y 1 primero. La prueba de esto la hago con SageMath, aunque realmente vale cualquier software similar.
Primero, ambos empiezan como \(\ket{0}\) (ket0)
| |
Aplicamos una puerta \(H\) al qubit de control.
| |
Ahora calculamos el vector de 4 elementos (que une los qubits \(0\) y \(1\))
| |
Ahora qubits01 será algo así:
Ahora, como sabemos que la puerta \(CX\) invierte el orden de las dos últimas filas, podemos aplicarla a qubits01:
| |
Ahora se habrán intercambiado las dos filas inferiores, por lo que qubits01 será:
Esto significa que las probabilidades reales son las siguientes (recordemos que son el cuadrado de la amplitud de probabilidad de cada estado):
| |
Al medir los qubits, pueden darse dos situaciones únicamente, ambas con probabilidad 0.5:
- Ambos qubits (\(0,1\)) tienen el valor \(0\).
- Ambos qubits (\(0,1\)) tienen el valor \(1\).
Una vez tenemos esto, lo repetimos con el qubit \(3\).
Como ya hemos aplicado la puerta \(H\) al qubit \(0\), simplemente usamos una puerta \(CX\) con el qubit \(0\) como qubit de control y el qubit \(3\) como objetivo. Esto hará que el qubit \(3\) también dé siempre la misma medición que el \(0\).
Una vez hecho esto, tendremos el primer grupo con las mismas mediciones siempre, (\(0,1,3\)). Las puertas que tendremos que aplicar al conectarnos al servidor:
- \(H\) a qubit \(0\)
- \(CX\) a qubits \(0,1\)
- \(CX\) a qubits \(0,3\)
Como con este primer grupo ya tenemos aleatoriedad verdadera gracias a la puerta \(H\) y necesitamos únicamente que el otro grupo (\(2,4\)) no tenga la misma secuencia, podemos simplemente hacer que sus qubits también tengan el mismo valor que el \(0\) y luego aplicarles una \(X\) (NOT). Esto hará que tengan exactamente la secuencia inversa. Las puertas para esto resultan:
- \(CX\) a qubits \(0,2\)
- \(CX\) a qubits \(0,4\)
- \(X\) a qubit \(2\)
- \(X\) a qubit \(4\)
Otra solución alternativa sería crear un grupo independiente nuevo aplicando una puerta \(H\) a, por ejemplo, el qubit \(2\), y luego entrelazar la probabilidad del qubit \(4\) con él. La distribución sería diferente, pero la solución valdría igual.
Probamos con la primera, la solución con la sintaxis que espera el programa es:
| |
Además, por curiosidad, el circuito representado (sacado de aquí) sería algo así:

Ahora nos conectamos al servidor y mandamos el circuito.
| |
Y hemos acabado.




