We were provided with the files knapsack.py, utils.py, ciphertext.txt and pubkey.txt. Before having any look at the code, I already suspected that this might be about the Merkle-Hallman knapsack cryptosystem [1] and after a quick one I was proven right. In principle the idea with this system is to generate a superincreasing sequence of numbers $$(x_i)$$, $$x_i \in \mathbb{N}_{>0}$$, thus

$$\forall j: x_j > \sum_{i=0}^{j-1} x_i.$$

To hide the superincreasing property we further choose a random $$r$$ and a modulus $$N$$ and compute the sequence $$(y_i)$$ where $$y_i = rx_i \mod N$$. The public key is then given by $$(y_i)$$ and the private key by $$((x_i), r, N)$$. Now to encrypt a message $$m$$ we split it up in its bits $$(b_i)$$ and compute $$c = \sum_i y_i b_i$$. Decryption is then done by first computing $$c' = c r^{-1} \mod N$$ and recovering $$(b_i)$$ from $$c'$$, which is possible due to the superincreasing nature of $$(x_i)$$. Thus we would check for the first $$x_i \leq c'$$ yielding the first bit, then update $$c'' = c'-x_i$$ and start anew.

Fortunately for us, attacking the scheme is not too hard either [2]. We can use lattice methods to solve the equation

$$c = y_1b_1 + y_2b_2 + \cdots + y_Nb_N$$

for the $$b_i \in \{0,1\}$$. This is done by considering the lattice

$$\begin{pmatrix} y_1 & 1 & 0 & \cdots & 0 \\ y_2 & 0 & 1 & \cdots & 0 \\ \vdots & \vdots & \vdots & \ddots & \vdots \\ y_N & 0 & 0 & \cdots & 1 \\ -c & 0 & 0 & \cdots & 0 \end{pmatrix}$$

and using LLL reduction. After a bit of Sage, finding the right row (basically looking for zeroes and ones) and guessing some non-recovered characters the flag read lenstra_and_lovasz_liked_lattices.

## References

1. Merkle, Ralph; Hellman, Martin (1978). Hiding information and signatures in trapdoor knapsacks.
2. Shamir, Adi (1984). A polynomial-time algorithm for breaking the basic Merkle - Hellman cryptosystem.