Round’n’round
Introduction
For this challenge we are actually given a lot of things. Firstly we have an encrypted file, we also have the program used for encryption and decryption. Finally we have a remote service which will encrypt arbitrary messages under the same key as the encrypted flag.
Affine encryption
Examining the encryption program it turns out that the encryption algorithm is simply a lot of additions (xors) and multiplications over the polynomial $x^{64} + x^4 + x^3 + x + 1$, ie. the CRC-64-ISO polynomial. This means that we can represent the encryption of a single block as an affine function
$$ c = \mathbf{A} p \oplus \mathbf{b} $$
Where $p$ is the plaintext, $c$ is the ciphertext and $\mathbf{A}$ is a matrix representing the linear portion of the encryption and $\mathbf{b}$ a vector representing the affine portion. If we can determine these, decryption is trivial as a slight rearrangement gives:
$$ p = \mathbf{A}^{-1} (c \oplus \mathbf{b}) $$
The affine part
The first thing we tackle the affine portion $\mathbf{b}$. To do this we pick two plaintexts $p_1$ and $p_2$ and ask our encryption service to encrypt $p_1$, $p_2$ and $p_1 \oplus p_2$ giving ciphertexts $c_1$, $c_2$, and $c_{1 \oplus 2}$. With these three we can determine $\mathbf{b}$ as follows:
$$ \begin{aligned} c_1 \oplus c_2 &= \mathbf{A} p_1 \oplus \mathbf{b} \oplus \mathbf{A} p_2 \oplus \mathbf{b} \\ c_1 \oplus c_2 &= \mathbf{A} (p_1 \oplus p_2) \oplus \mathbf{b} \oplus\mathbf{b} \\ c_1 \oplus c_2 &= c_3 \oplus\mathbf{b} \\ \mathbf{b} &= c_1 \oplus c_2 \oplus c_{1 \oplus 2} \\ \end{aligned} $$
So we can simply xor these three ciphertexts together to determine $\mathbf{b}$. With this determined we can reduce the system to being linear.
The linear part
For the linear portion we want to determine the matrix $\mathbf{A}$. This is done by isolating determining each row of $\mathbf{A}$ one at a time by encrypting all the unit vectors.
$$ \begin{aligned} c = \mathbf{A} p \oplus \mathbf{b} \\ c \oplus \mathbf{b} = \mathbf{A} p \\ \end{aligned} $$
If $p$ is the $i$th unit vector, then the $i$th row of $\mathbf{A}$ is $c \oplus \mathbf{b}$. Once $\mathbf{A}$ is determined, we invert it. We are guaranteed that the matrix is invertable as otherwise the encryption would not be reversible. As we had a hard time finding python code to do this inversion in xor, here is our elimination based routine nicely hardcoded for 64 bits:
|
|
Putting it together
Once both $\mathbf{b}$ and $\mathbf{A}^{-1}$ are known we simply apply the decryption formula to each block in the encrypted flag:
$$ p = \mathbf{A}^{-1} (c \oplus \mathbf{b}) $$
Viola!