Generate a key using Quantum Key Distribution (QKD) algorithm and decrypt the flag.
Time
15 minutes
Behavior
We are simulating a Quantum satellite that can exchange keys using qubits implementing BB84. You must POST the qubits and basis of measurement to
/qkd/qubits
and decode our satellite response, you can then derive the shared key and decrypt the flag. Send 512 qubits and basis to generate enough key bits.
The service runs BB84 protocol, there is a good explaination of the protocol on wikipedia
Solution
TL;DR
- Exchange shared secret using BB84 protocol
- Convert the bit string to char string with correct order
- XOR the secret and encryped key to decrypt it
Here's a example copied from wikipedia:
Action | ||||||||
---|---|---|---|---|---|---|---|---|
Alice's random bit | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 1 |
Alice's random sending basis | + | + | x | + | x | x | x | + |
Photon polarization Alice sends | ↑ | → | ↘ | ↑ | ↘ | ↗ | ↗ | → |
Bob's random measuring basis | + | x | x | x | + | x | + | + |
Photon polarization Bob measures | ↑ | ↗ | ↘ | ↗ | → | ↗ | → | → |
PUBLIC DISCUSSION OF BASIS | ||||||||
Shared secret key | 0 | 1 | 0 | 1 |
In this task, we are Alice and the server is Bob. To establish a shared secret, we have to choose some random bits first:
state = [random.randrange(2) for _ in range(512)]
Next, for the simplicity, we set all the basis to +
basis = ['+'] * 512
And calculate the qubits state based on the basis an the bits
z = [{'real': 1, 'imag': 0}, {'real': 0, 'imag': 1}]
qubits = [z[s] for s in state]
Send those qubits to the server to get Bob's basis and encrypted key
res = sess.post(
'https://cryptoqkd.web.ctfcompetition.com/qkd/qubits',
json={ 'basis': basis, 'qubits': qubits, }
).json()
Compare the basis and keep those bits with correct basis
bits = [s for s, b in zip(state, res['basis']) if b == '+'][:128]
Convert the bits to string with MSB-first and Left-first order.
shared = int(''.join(map(str, bits)), 2).to_bytes(16, 'big')
XOR the secret and encryped key to decrypt it
encrypted = bytes.fromhex(res['announcement'])
key = bytes(a ^ b for a, b in zip(encrypted, shared)).hex()
print(key)
Now decrypt the flag using the command provided in the description
echo "$KEY" > plain.key
xxd -r -p plain.key > enc.key
echo "$FLAG" | openssl enc -d -aes-256-cbc -pbkdf2 -md sha1 -base64 --pass file:enc.key