# Crypto2 write up
# Tổng quan
Đề bài:
```python
from Crypto.Util.number import *
from os import urandom
flag = b"KCSC{fake_flag}"
padding = urandom(180 - len(flag))
flag = padding[: (180 - len(flag))//2] + flag + padding[(180 - len(flag))//2 :]
p = getPrime(1024)
q = getPrime(512)
e = 0x10001
n = p * q
c = pow(bytes_to_long(flag), e, n)
key = q
ks = []
noises = []
for i in range(15):
noise = key + 3*i + 5
noise_inv = inverse(noise, p)
k = (noise_inv * noise - 1) // p
ks.append(k)
noises.append(noise_inv % 2**562)
with open("data.txt","w") as f:
f.write(f"{n = }\n{noises = }\n{ks = }\n")
```
Thông qua đề bài ta nhận được dữ kiện sau:
$$
\begin{split}
noises &= [noise_0, noise_1, \dots, noise_{14}]\\
ks &= [k_1, k_2, \dots, k_{14}]
\end{split}
$$
gọi `noise` được khởi tạo trong mỗi vòng thuộc miền $d$, lúc đó ta có:
$$
\begin{split}
d &= [d_0, d_1, \dots, d_{14}]\\
d_i &= key + 3*i + 5 \\
key &= q
\end{split}
$$
Ý tưởng của tác giả (~~ở đây là mình~~) là thí sinh phải tìm ra $p$ và $q$ thông qua các phương trình tổng quan có dạng như sau:
$$
d_i \cdot noise_{inv} - 1 = k_i \cdot p
$$
# Hướng tiếp cận
## Tìm $q$
Do ta chỉ nhận được mảng $noises$ là tập hợp kết quả của phép tính `noise_inv % 2^562` qua các vòng nên ta sẽ viết lại phương trình như sau:
Đặt:
$$
\begin{split}
p &= leak + 2^{562}b \\
noise_{inv} &= noise_i + 2^{562}n_i
\end{split}
$$
Tiếp đến, viết lại phương trình như sau:
$$
k_i (leak + 2^{562}b) - d_i(noise_i + 2^{562}n_i) + 1 = 0
$$
Phân phối và chuyển vế:
$$
k_i * 2^{562}b - 2^{562} n_i \cdot d_i = -1 + d_i \cdot noise_i - k_i \cdot leak
$$
Ta thấy $q$ là số nguyên tố $512$-bit, do đó $q < 2^{562}$ và bên canh đó ta cũng có thể kết luận $d_i < 2^{562}$, như vậy ta có thể mod phương trình trên cho $2^{562}$ và hy vọng có thể tìm được giá trị $q$.
Phương trình mới có dạng như sau:
$$
-1 + d_i \cdot noise_i - k_i \cdot leak = 0 \mod 2^{562}
$$
$$
-1 + (key + 3*i + 5) \cdot noise_i - k_i \cdot leak = 0 \mod 2^{562}
$$
Hiện tại, phươn trình của ta chỉ còn $2$ nghiệm là $key$ và $leak$, và đề cho tận $15$ phương trình nên việc tìm được $key$ và $leak$ là hoàn toàn khả thi.
Ở đây mình dùng `groebner_basis` , một phương pháp dùng đơn giản tập sinh của một ideal trong một vành đa thức.
```python
P.<x,y> = PolynomialRing(Zmod(2**562))
I = P.ideal([
noises[0]*(x+5) -1 - ks[0]*y,
noises[1]*(x+8) -1 - ks[1]*y,
noises[2]*(x+11) -1 - ks[2]*y
])
print(I.groebner_basis())
[x + 15095849699286157176165192141574519938693899918427427740708979667686108913040888895928594081761428218515984585906870777122468994835321959450919640276403457857023062016735, y + 8046359343118037782228277823598020857453001371030183803247456593467443731321455063833959619491793509652535546439016536857407146846080157709770883346704338364262578059073]
```
## Tìm $p$
Khi đã có $key$ và $leak$, ta sẽ dùng chúng để giải phương trình cũ với mục đích tìm lại $p$:
$$
k_i * 2^{562}b - 2^{562} n_i \cdot d_i = -1 + d_i \cdot noise_i - k_i \cdot leak \ \ \ (1)
$$
Hiện tại ta đang có $16$ biến là $b$ và $n_0, n_1, \dots, n_{14}$, và $15$ phương trình có dạng như $(1)$. Bên cạnh đó các biến ta cần tìm có nhiều nhất là $462$-bit, khả năng cao ta có thể lập lattice và tìm các nghiệm thông qua thuật toán [$LLL$](https://en.wikipedia.org/wiki/Lenstra%E2%80%93Lenstra%E2%80%93Lov%C3%A1sz_lattice_basis_reduction_algorithm)
đặt $vs = [-1 + d_i \cdot noise_i - k_i \cdot leak \ \forall x \in \{1, \dots, 14\}] = [v_0, v_1, \dots, v_{14}]$
Ta thiết lập Lattice như sau:
$$
M = \begin{pmatrix}
k_0 \cdot 2^{562} &k_1 \cdot 2^{562} &\cdots &k_{14} \cdot 2^{562} &1 & & & & &\\
-d_0 \cdot 2^{562} &0 &\cdots &0 & &1 & & & &\\
& -d_1 \cdot 2^{562} &\cdots &0 & & &1 & & &\\
\vdots &\vdots &\ddots &\vdots & & & & \ddots & &\\
& &\cdots &-d_{14} \cdot 2^{562} & & & & &1 & \\
-v_0 &-v_1 &\cdots &-v_{14} & & & & & &1 \end{pmatrix}
$$
Sau khi áp dụng $LLL$ với Lattice trên thì vector mà ta mong muốn tìm được có dạng như sau:
$$
[0, 0, \dots, 0, b, n_0, n_1, \dots, n_{14}, 1]
$$
Như vậy ta đã có được $b$. Việc cuối cùng là tìm lại $p$ theo phương trình:
$$
p = leak + 2^{562}b
$$
Kết hợp với $q$ đã tìm được ở trên, ta có thể dễ dàng tìm lại được flag.
```py
from Crypto.Util.number import long_to_bytes
M = []
v = []
M.append([ks[i] * 2^562 for i in range(m)])
for i in range(m):
d = key + 3 * i + 5
v.append(-(-1 + d * noises[i] - ks[i] * leak))
row = [0]*(m)
row[i] = -d * 2^562
M.append(row)
M.append(v)
M = matrix(M)
M = M.augment(identity_matrix(m+2))
L = M.LLL()
b = L[0][m]
p = leak + b*2^562
n = p*q
phi = (p-1)*(q-1)
d = pow(65537, -1, phi)
print(long_to_bytes(pow(c, d, n)))
# KCSC{h0w_do_y0u_know_th1s_is_5olv4b13}
```