# OK - zer0pts CTF 2022
###### tags: `zer0pts CTF 2022`
Writeups: https://hackmd.io/@ptr-yudai/rJgjygUM9
## overview
The program implements Oblivisous Transfer by RSA. We can get either $K$ or $C$. Our purpose is to get $K \oplus C$.
## solution
### step1
By sending $v$ such that $v - x_1 \equiv -(v - x_2) \mod n$, the $k_1 \equiv -k_2 \mod n$ stands.
Because $k_1 \equiv (v - x_1)^d \mod n$ and $k_2 \equiv (v - x_2)^d \equiv (-(v - x_1))^d \equiv -(v - x_1)^d \mod n$. ($d$ is odd value)
Thus, $c_1 + c_2 \equiv K + C \mod n$ where $c_1 \equiv k_1 + K \mod n$, $c_2 \equiv k_2 + C \mod n$ stands.
Additionaly, the LSB of $c_1 + c_2$ is always fixed because $K \oplus C$ is consistent. Hence we can assume the parity of $c_1 + c_2$ and find $K + C$ without modulo under the assumption. If the parity of $K + C \mod n$ is different from our assumption, we can get $K + C$ by adding $n$ to $|K + C \mod n|$. Otherwise $|K + C \mod n| = K + C$. Because $n$ is odd value.
Now we have $K + C \mod n$. Can we determine $K \oplus C$ uniquely?
Appearently no. But for now, we know $K$ and $C$ are different in every connection while $K \oplus C$ is consistent. So considering about collecting some cases of $(K, C)$ pair.
For now, the problem is turned into to find $X = K_i \oplus C_i$ from some $Y_i = K_i + C_i$.
### step2
Therefore, if the following problem is solved, <code>secret</code> has been recovered.
> **Statement:**
> Generate an integer sequence $B_i=A_i + (m \oplus A_i)$ from a random integer $m$ of $N$ bits and an integer sequence $A$ of random $N$ bits. Recover $m$ from the integer sequence $B$.
> **Constraints:**
> - $N=1024$
> - $|A|=20$
First, let us make an observation about the generated number $A_i + (m \oplus A_i)$. From the facts about addition, the following formula transformation holds.
$$
\begin{align}
A_i + (m \oplus A_i) &= (A_i \oplus m \oplus A_i)+2\cdot (A_i \& (m \oplus A_i)) \\
&= m+2\cdot (A_i \& (m \oplus A_i)) \tag{1}
\end{align}
$$
Here, the XOR in the $A_i \& (m \oplus A_i)$ part can be taken as inverting the bit extracted by &. So it should be the same if all the bits are inverted first and then extracted. Let us denote the bit inversion of $x$ as $\overline{x}$, then the following holds.
$$
\begin{align}
(1) &= m+2\cdot (A_i \& \overline{m})\\
&= m+\overline{m}+(A_i \& \overline{m})+( (A_i \& \overline{m})-\overline{m}) \\
&= \overline{0}+(A_i \& \overline{m})+( (A_i \& \overline{m})-( (A_i \& \overline{m})+(\overline{A_i} \& \overline{m}) ) ) \\
&= \overline{0}+(A_i \& \overline{m})-(\overline{A_i} \& \overline{m}) \tag{2}
\end{align}
$$
The equation transformation regarding bit inversion is a bit cumbersome, but it is no problem to think of it as the one's complement in a sufficiently large finite number of bits. In this case, since $N=1024$ bits, we can write the following using $C=2^\rm{N+1}-1$.
\begin{align}
(2) &= C+(A_i \& (C-m))-(\overline{A_i} \& (C-m) )
\end{align}
This equation can be interpreted as a script like the following.
```python
C = 2 ** (N + 1) - 1
B_i = C
for i in range(N):
if not (C-m & (1 << i)): continue
res += (1 << i) * choice([-1, 1])
```
Now consider the probability that the most significant bit of $|B_i-C|$ is equal to the most significant bit of $C-m$. It turns out that this is 1 if $C-m$ and the most significant bit are equal, and 1/2 if they are not ((symmetry makes it clear)). Since the most significant bit is always smaller when they are not equal, we can find the most significant bit of $C-m$ from the sequence $B$ with probability more than $1-2^{-|B|}$ by taking the largest most significant bit. Next, subtract the most significant bit from $|B_i-C|$ and take its absolute value. Then we have the same problem for the second bit from the top.
Thus, we found that by repeating this process, we can find $m$ with probability greater than $(1-2^{-|B|})^{N}$. The script will be as follows.
```python
def solve(bs):
BIT = 1024
C = 2 ** (BIT + 1) - 1
bs = [abs(b - C) for b in bs]
res = C
while not all([b == 0 for b in bs]):
msb = 1 << (max(bs).bit_length() - 1)
res -= msb
bs = [abs(b - msb) for b in bs]
if any([b != 0 for b in bs]): return None
return res
```