# picoCTF - Crypto ## Flag - printer - [encoded.txt](https://artifacts.picoctf.net/c_titan/19/encoded.txt) - [flag_printer.py](https://artifacts.picoctf.net/c_titan/19/flag_printer.py) ```python import galois import numpy as np MOD = 7514777789 points = [] for line in open('encoded.txt', 'r').read().strip().split('\n'): x, y = line.split(' ') points.append((int(x), int(y))) GF = galois.GF(MOD) matrix = [] solution = [] for point in points: x, y = point solution.append(GF(y % MOD)) row = [] for i in range(len(points)): row.append(GF((x ** i) % MOD)) matrix.append(GF(row)) open('output.bmp', 'wb').write(bytearray(np.linalg.solve(GF(matrix), GF(solution)).tolist()[:-1])) ``` ### Solution Ta thấy rằng file `encoded.txt` bao gồm tập hợp các điểm với $x$ nằm từ `0` --> `1769610` và giá trị y random. Và code mẫu đề bài cho là thực hiện giải ma trận dưới đây, tuy nhiên nó chạy rất chậm và độ phức tạp là $O(n^3)$ $\begin{bmatrix} a_0 \\ a_1 \\ a_2 \\ a_3 \\ \vdots \\ a_n \\ \end{bmatrix} \begin{bmatrix} 0^0 & 0^1 & 0^2 & 0^3 & \dots & 0^n \\ 1^0 & 1^1 & 1^2 & 1^3 & \dots & 1^n \\ 2^0 & 2^1 & 2^2 & 2^3 & \dots & 2^n \\ 3^0 & 3^1 & 3^2 & 3^3 & \dots & 3^n \\ \vdots & \vdots & \vdots & \vdots & \ddots & \vdots \\ n^0 & n^1 & n^2 & n^3 & \dots & n^n \\ \end{bmatrix} = \begin{bmatrix} y_0 \\ y_1 \\ y_2 \\ y_3 \\ \vdots \\ y_n \\ \end{bmatrix}$ Sau một lúc osint thì ta nhận thấy rằng đây là ma trận Vandermonde và nó liên quan đến Larange interpolation (nội suy larange). Và ta có được công thức: $f(x)=\sum_{i=1}^{n+1} f(x_i) * \prod_{j\neq i \ j = 1}^{n+1} \frac{x-x_j}{x_i-x_j}$ Cùng với đó ta cũng tìm thấy được một blog: https://codeforces.com/blog/entry/94143 Đầu tiên ta sẽ giảm độ phức tạp của việc tính $\prod_{j\neq i}(x_i-x_j)$ Thay vì với mỗi vòng lặp ta sẽ chạy lại để tính thì ta có thể làm như sau: $\prod_{j\neq i}(x_i-x_j)$ = $\lim_{x\to x_i}\frac{\prod_{j=1}^{n+1} (x-x_j)}{x-x_i}$ Ở đây to có thể nhân hết vào và chia đi $x_i - x_i$. Điều tuyệt vời là ta có thể sử dụng lim để xử lý việc $x_i - x_i = 0$ $\lim_{x\to x_i}\frac{\prod_{j=1}^{n+1} (x-x_j)}{x-x_i}=\lim_{x\to x_i}\frac{d}{dx}(\prod_{i=1}^{n+1} x-x_j)$ Thế nên việc của ta là chỉ cần tìm $P'(x)$ với $P(x) = \prod_{i=1}^{n+1} x-x_j$ rồi thế từng $x_i$ vô. Tiếp theo ta sẽ đến giảm độ phức tạp của việc nhân nhiều đa thức Ta biết rằng nhân đa thức trong sage có $O(n \ log(n))$ Giả sử có m đa thức thì nếu nhân từ từ từng đa thức lại với nhau ta sẽ mất $O(m * n log(n))$ ![image](https://hackmd.io/_uploads/BkANnVh1A.png) Còn nếu ta sử dụng chia để trị để nhân đa thức thì ta sẽ mất $(nlog(n) * log(m))$ ![image](https://hackmd.io/_uploads/Bk7Gp4hy0.png) Giờ việc cuối cùng của ta là là thiết lập công thức Đặt $v_i=\frac{y_i}{P'(x_i)}$ --> $f(x)=\sum_{i=1}^{n+1} v_i\prod_{j\neq i}(x-x_j)$ $f(x)=f_0(x)P_1(x)+f_1(x)P_0(x)$ với: - $f_0(x)=\sum_{i=1}^{\lfloor\frac{n+1}{2}\rfloor} v_i\prod_{j\neq i,1\leq j\leq \lfloor\frac{n+1}{2}\rfloor}(x-x_j)$ - $f_1(x)=\sum_{i=\lfloor\frac{n+1}{2}+1\rfloor}^{n+1} v_i\prod_{j\neq i,\lfloor\frac{n+1}{2}\rfloor\leq j\leq n+1}(x-x_j)$ - $P_0(x)=\prod_{1\leq j\leq \lfloor\frac{n+1}{2}\rfloor}(x-x_j)$ - $P_1(x)=\prod_{\lfloor\frac{n+1}{2}\rfloor\leq j\leq n+1}(x-x_j)$ Code dưới đây đã mất tầm 6 - 7 tiếng để chạy và mình đã để chạy qua đêm để ra FLAG. (huhu) ```python from tqdm import tqdm MOD = 7514777789 points = [] for line in open('encoded.txt', 'r').read().strip().split('\n'): x, y = line.split(' ') points.append((int(x), int(y))) F = GF(MOD) R.<x> = F['x'] def multiply_poly_list(poly_list): if len(poly_list) == 1: return poly_list[0] n = len(poly_list) // 2 return multiply_poly_list(poly_list[:n]) * multiply_poly_list(poly_list[n:]) P = multiply_poly_list([x - i for i in range(len(points))]) d_P = derivative(P) def calc_poly(points, start_index, bar): bar.update(int(1)) n = len(points) if n == 1: x_i = points[0][0] y_i = points[0][1] v = y_i / d_P(x_i) return v f0 = calc_poly(points[:n//2], start_index, bar) f1 = calc_poly(points[n//2:], start_index + n//2, bar) p0 = multiply_poly_list([x - (i + start_index) for i in range(n//2)]) p1 = multiply_poly_list([x - (i + start_index) for i in range(n//2, n)]) return f0 * p1 + f1 * p0 n = 2 * len(points) - 1 bar = tqdm(total=int(n)) poly = calc_poly(points, 0, bar) coeffs = poly.numerator().list() with open('out.txt', 'w') as f: f.write('\n'.join([str(i) for i in coeffs])) print("Finished") ``` ```python listarr = [int(_) for _ in open("out.txt", "r").read().strip().split('\n')] print(listarr) open('output.bmp', 'wb').write(bytearray(listarr[:-1])) ``` ![image](https://hackmd.io/_uploads/SyXKomhkA.png)