# ELLIPTIC CURVES IN CRYPTOGRAPHY ## Đường cong Elliptic ### Lý thuyết Một đường cong Elliptic là tập hợp các nghiệm của phương trình có dạng $y^2 = x^3 + ax + b$. Ví dụ như ![image](https://hackmd.io/_uploads/Hkycdlps6.png) ### Định luật cộng Đường cong elliptic có một định luật cộng giữa hai điểm trên một đường cong và sẽ tạo ra đường thứ ba nhưng mà không giống như một số phép cộng thông thường. Cho P và Q là hai điểm trên đường cong elliptic E, ta vẽ đường thằng L qua P và Q. Đường L này cắt E tại ba điểm, cụ thể là P, Q và 1 điểm R. Chúng lấy điểm R đó phản xạ qua trục hoành để thu được một điểm R'. Điểm R' này được gọi là tổng của P và Q. Hiện tại thì người ta ký hiệu định luật cộng này bằng ký hiệu ⊕. $P ⊕ Q = R'$. Ví dụ như sau: Ta có đường cong $y^2 = x^3 - 15x + 18$ Ta lấy 2 điểm $P = (7,16)$ và $Q = (1,2)$ trên đường cong E. Đường thẳng L đi qua cả P và Q có phương trình $L: Y = \frac{7}{3}x - \frac{1}{3}$. Ta sẽ được hình như sau: ![image](https://hackmd.io/_uploads/SJoG3x6sT.png) $$(\frac{7}{3}x - \frac{1}{3})^2 = x^3 - 15x +18$$ $$\frac{49}{9}x^2 - \frac{14}{9}x + \frac{1}{9} = x^3 - 15x + 18$$ $$x^3 - \frac{49}{9}x^2 - \frac{121}{9}x + \frac{161}{9} = 0$$ $$(x-7)(x-1)(x+\frac{23}{9}) = 0$$ $$\text{=> Có nghiệm là } x = -\frac{23}{9}.$$ $$P ⊕ Q = (-\frac{23}{9},\frac{170}{27})$$ Thế nếu $P \equiv Q$ thì sao, thì ta sẽ được như này ![image](https://hackmd.io/_uploads/BJ_feZTo6.png) Thế giờ ta muốn cộng hai điểm $P = (a,b)$ và $P' = (-a,b)$ thì sao ??? Không có điểm giao nhau thứ ba, vì vậy giải pháp là tạo thêm một điểm $O$ ở vô cực. Chính xác hơn là điểm $O$ không tồn tại trên mặt phẳng XY, sau đó ta thiết lập $P ⊕ P' = O$. ![image](https://hackmd.io/_uploads/SJ8w7Zasa.png) Và điểm $O$ này hoạt động như là một số không trong đường cong elliptic, thế nên $P ⊕ O = P$. Cho $E: y^2 = x^3 + ax + b$, cho hai điểm $P = (x1,y1)$ và $Q = (x2,y2)$, thuật toán tính tổng của hai điểm P và Q như sau: - Nếu $x1 = x2$ và $y1 = -y2$ thì $P + Q = O$ - Ngược lại thì ta có như sau: $$\lambda = \frac{y2 - y1}{x2 - x1} \text{ nếu } P \neq Q$$ $$\lambda = \frac{3x_1^2 + a}{2y_1} \text{ nếu } P = Q$$ - Và ta có như sau $\hspace{2cm} x_3 = \lambda^2 - x_1 - x_2 \hspace{1cm} \text{và} \hspace{1cm} y_3 = \lambda(x_1 - x_3) - y_1$ - Từ đó: $P + Q = (x_3,y_3)$. ### Định luật cộng cũng là một nhóm Abel Cho $E$ là đường cong elliptic, phép cộng trong đường cong $E$ thỏa mãn các tính chất như sau: - $P + O = O + P = P \hspace{3,6cm} \forall P \in E$ - $P + (-P) = O \hspace{4,8cm} \forall P \in E$ - $(P + Q) + R = P + (Q + R) \hspace{1,9cm} \forall P, Q, R \in E$ - $P + Q = Q + P \hspace{4,6cm} \forall P,Q \in E$ --> Phép cộng trong Elliptic là một nhóm giao hoán Abel. ## Đường cong Elliptic trong trường hữu hạn (Fp) ### Lý thuyết Vẫn như lý thuyết ở trên thôi, nhưng mà giờ ở trong trường hữu hạn p. $$y^2 = x^3 + ax + b \pmod{p}$$ Trong đó a và b là các hệ số thỏa mãn điều kiện $4a^3 + 27b^2 \neq 0$. Đường cong trong trường hữu hạn có tính chất khó tính ngược lại, tạo ra một cơ chế bảo mật mạnh để sử dụng trong các giao thức mật mã học như là mã hóa khóa công khai hoặc là chữ kỹ số. ### Phép cộng Cũng khá giống trong trường số thực, trường số nguyên tố thì chỉ cần mod với số nguyên tố p nữa thui. Ta có: $E: y^2 = x^3 + 2x + 2 \pmod{17}$ và $P = (5,1)$. Giờ ta muốn cộng 2 điểm $P$ này thì sao Ta sẽ tính được như sau ![image](https://hackmd.io/_uploads/HJyNVzpoa.png) Tính toán bằng python thì sao nhỉii ```python= from Crypto.Util.number import* def add_point(P, Q, p, a, b): if P== (0, 0): return Q if Q == (0,0): return P x1, y1 = P x2, y2 = Q if x1 == x2 and y1 == -y2: return (0, 0) s = 0 if P == Q: s = ((3*pow(x1,2,p)+a) * inverse(2*y1, p)) else: s = ((y2-y1) * inverse(x2-x1, p)) x3 = (pow(s, 2) - x1 - x2) % p y3 = (s*(x1 - x3) - y1) % p return (x3, y3) #E = EllipticCurve(GF(17),[2,2]) P = (5,1) print(add_point(P,P,17,2,2)) #Output: (6, 3) ``` Tính toán bằng sagemath ```sage= E = EllipticCurve(GF(17),[2,2]) P = E(5,1) Q = P + P Q (6 : 3 : 1) ``` ### Phép nhân Ta có: $E: y^2 = x^3 + 2x + 2 \pmod{17}$ và $P = (5,1)$. Ta sẽ được bảng như sau ![image](https://hackmd.io/_uploads/BkSpHzToT.png) Ta sẽ sử dụng thuật toán ``double-and-add`` như sau: ``` Input: P in E(Fp) and an integer n > 0 1. Set Q = P and R = O. 2. Loop while n > 0. 3. If n ≡ 1 mod 2, set R = R + Q. 4. Set Q = 2 Q and n = ⌊n/2⌋. 5. If n > 0, continue with loop at Step 2. 6. Return the point R, which equals nP. ``` Sử dụng thuật toán đó vào python ```python3= from Crypto.Util.number import* def add_point(P, Q, p, a, b): if P== (0, 0): return Q if Q == (0,0): return P x1, y1 = P x2, y2 = Q if x1 == x2 and y1 == -y2: return (0, 0) s = 0 if P == Q: s = ((3*pow(x1,2,p)+a) * inverse(2*y1, p)) else: s = ((y2-y1) * inverse(x2-x1, p)) x3 = (pow(s, 2) - x1 - x2) % p y3 = (s*(x1 - x3) - y1) % p return (x3, y3) def Scalar_Mul(P, n): Q = P R = (0, 0) while n > 0: if n % 2 == 1: R = add_point(R, Q, 17, 2, 2) Q = add_point(Q, Q, 17, 2, 2) n = n//2 return R # E = EllipticCurve(GF(17),[2,2]) P = (5,1) print(Scalar_Mul(P,16)) # Output: (10, 11) ``` Sử dụng trong sage ```sage= sage: E = EllipticCurve(GF(17),[2,2]) ....: P = E(5,1) sage: 3*P (10 : 6 : 1) sage: 16*P (10 : 11 : 1) ``` ### Order của đường cong Elliptic Ta có đường cong $E: y^2 = x^3 + 3x + 8 \pmod{13}$. Ta có được: $E(\mathbb{F}_{13}) = \{ O, (1,5), (1,8), (2,3), (2,10), (9,6), (9,7), (12,2), (12,11) \}.$ Qua đó $E(\mathbb{F}_{13})$ bao gồm 9 điểm. Ta nói Order của đường cong $E$ này là 9. Bây giờ, ta có thể dùng định lý Hasse để có thể giới hạn được order của một đường cong $E$ theo công thức như sau: $$p + 1 - 2\sqrt{p} \leq |E(\mathbb{F}_p)| \leq p + 1 + 2\sqrt{p}$$ Như ví dụ trên, ta có được $8 \leq |E(\mathbb{F}_p)| \leq 20$ thì $E(\mathbb{F}_p) = 9$. # Đường cong Elliptic ứng dụng trong mật mã ### Elliptic Diffie–Hellman Key Exchange | Alice | Bob | |:--------------------:|:-----------------------:| | Chọn cùng 1 số nguyên tố $p$ lớn | Chọn cùng 1 số nguyên tố $p$ lớn | | Chọn cùng 1 đường cong $E$ | Chọn cùng 1 đường cong $E$ | | Chọn cùng 1 điểm $P$ | Chọn cùng 1 điểm $P$ | | Chọn số bí mật $n_A$ | Chọn số bí mật $n_B$ | | Tính toán $Q_A = n_A.P$| Tính toán $Q_B = n_B.P$ | |Gửi cho Bob số $Q_A$|Gửi cho Alice số $Q_B$| |Tính toán điểm $n_A.Q_B$|Tính toán điểm $n_B.Q_A$| |Cả hai đều có cùng một điểm bí mật|Cả hai đều có cùng một điểm bí mật| Ví dụ: Ta có đường cong $E: y^2 = x^3 + 324x + 1287 \pmod{3851}$ và điểm $P = E(920,303)$ Số bí mật của Alice là $n_A = 1194$, ta tính được $Q_A = n_A.P = E(2067,2178)$. Số bí mật của Bob là $n_B = 1759$, ta tính được $Q_B = n_B.P = E(3684,3125)$. Giờ Alice và Bob trao đổi cho nhau $Q_A$ và $Q_B$. Cuối cùng, cả hai đều có: $$\text{Alice:} \hspace{1cm} n_AQ_B = 1194(3684,3125) = (3347,1242)$$ $$\text{Bob:} \hspace{1cm} n_BQ_A = 1759(2067,2178) = (3347,1242) $$ Sau đó cả hai sẽ sử dụng tọa độ điểm $x=3347$ để làm giá trị bí mật chung để trao đổi. Thường là sẽ hash để lấy làm key cho các hệ mã hóa đối xứng.n ### ECDSA Sign ECDSA Sign cũng là một phần rất quan trọng trong đường cong Elliptic. Đây cũng là một dạng chữ ký số phổ biến hiện nay. Thuật toán của ECDSA như sau: - Chọn một đường cong Elliptic và chọn điểm $G \in E(F_p)$ với một số nguyên tố $p$ và $q$ an toàn. - Quá trình tạo khóa: - Chọn một số bất kỳ là $\text{Private Key}$ $s$ sao cho $1 < s < q - 1$ - $\text{Public Key}$ sẽ được tính bằng $V = sG \in E(F_p)$ - Quá trình ký số - Gọi văn bản cần ký là $d \pmod{q}$ - Chọn một số bất kỳ $e \pmod{q}$ - $s_1 = (eG)[x] \pmod{q}$ - $s_2 = e^{-1}.(d + ss_1) \pmod{q}$ - Công khai chữ ký $(s_1, s_2)$ - Quá trình xác minh - Ta tính $v_1 = d.s_2^{-1} \pmod{q}$ - Ta tính $v_2 = s_1.s_2{-1} \pmod{q}$ - Ta tính $v_1G + v_2V \in E(F_p)$ - Ta xác minh $(v_1G + v_2V)[x] \pmod{q}$ với $s_1$. Nếu bằng nhau thì là True. ![image](https://hackmd.io/_uploads/HkvzEiB26.png) Giải thích vì sao $(v_1G + v_2V)[x] \pmod{q} == s_1$: $$v_1G + v_2V = ds_2^{-1}G + s_1s_2^{-1}(sG)$$ $$v_1G + v_2V = (s_2^{-1}G)(d+ss_1)$$ $$v_1G + v_2V = (s_2^{-1}G)(es_2)$$ $$v_1G + v_2V = (G)(e)$$ Đoạn code sage minh họa như sau: ```sage= import hashlib from random import randint p = 115792089237316195423570985008687907853269984665640564039457584007908834671663 a = 0 b = 7 E = EllipticCurve(GF(p), [a, b]) P = E(55066263022277343669578718895168534326250603453777594175500187360389116729240, 32670510020758816978083085130507043184471273380659243275938904335757337482424) q = 115792089237316195423570985008687907852837564279074904382605163141518161494337 Fn = FiniteField(q) s = 12345 V = s*P print("Public Key:", V) message = "Hello KCSC" hash_message = int(hashlib.sha256(message.encode('utf-8')).hexdigest(), 16) % q # Signing e = 123456789 s1 = Fn((P * e)[0]) s2 = (pow(e, -1, q) * (hash_message + s * s1)) % q print("Signature: ", ((s1), (s2))) # Verification v1 = (pow(s2, -1, q) * hash_message) % q v2 = (pow(s2, -1, q) * s1) % q Verify = (P * ZZ(v1)) + (V * ZZ(v2)) x,y = Verify.xy() if (Fn(x)) == s1: print("Valid") else: print("Invalid") ``` # Cryptohack ### Background Reading **Flag: crypto{Abelian}** ### Point Negation Chall này ta chỉ cần tìm điểm $P'$ sao cho nó là điểm đối xứng với $P$ qua trục hoành. ```sage= sage: E = EllipticCurve(GF(9739),[497,1768]) sage: P = E(8045,6936) sage: -P (8045 : 2803 : 1) ``` **Flag: crypto{8045,2803}** ### Point Addition Chall này ta chỉ cần cộng các điểm trên đường cong lại với nhau thui là được hihihi :kissing_smiling_eyes: ```sage= sage: E = EllipticCurve(GF(9739),[497,1768]) sage: P = E(493, 5564) sage: Q = E(1539, 4742) sage: R = E(4403,5202) sage: P + P + Q + R (4215 : 2162 : 1) ``` **Flag: crypto{4215,2162}** ### Scalar Multiplication Chall này chỉ là nhân điểm trong đường cong với một số tự nhiên thui. ```sage= sage: E = EllipticCurve(GF(9739),[497,1768]) sage: P = E(2339, 2213) sage: 7863*P (9467 : 2742 : 1) ``` **Flag: crypto{9467,2742}** ### Curves and Logs The Elliptic Curve Discrete Logarithm Problem (ECDLP) là bài toán rời rạc logarit tìm số nguyên n sao cho $Q = nP$. Dạng này khá là giống Diffie Hellman vì đều là dạng trao đổi và bí mật chung. Alice và Bob đều đồng ý một đường cong $E$, một số nguyên tố $p$ và một điểm $G$ trên đường cong $E$. Alice lấy số bí mật của cô ấy là $n_A$ và tính được $Q_A = n_A.G$ Bob cũng thế, tính được $Q_B = n_B.G$ với $n_B$ là số bí mật. Alice sẽ tính được $n_A.Q_B$ và Bob sẽ tính được $n_B.Q_A$ Do tính kết hợp, ta có được rằng: $$S = n_A.Q_B = n_B.Q_A$$ Và $S$ sẽ là số chung của hai người để trao đổi. ```sage= sage: E = EllipticCurve(GF(9739),[497,1768]) sage: Q_a = E(815,3190) sage: n_b = 1829 sage: Q_a*n_b (7929 : 707 : 1) sage: from hashlib import* sage: print(sha1(b"7929").hexdigest()) 80e5212754a824d3a4aed185ace4f9cac0f908bf ``` **Flag: crypto{80e5212754a824d3a4aed185ace4f9cac0f908bf}** ### Efficient Exchange Chall này thì cho ta tọa độ $x$ của điểm $Q_A$, giờ ta cần phải tìm được tọa độ $y$ của điểm $Q_A$ này. Ta có rằng $y^2 = x^3 + 467x + 1768 \pmod{9739}$. Thế nên giờ ta sẽ thay giá trị $x$. Ta thu được $y^2 = 5507 \pmod{9739}$. Giờ ta sẽ sử dụng kiến thức trong phần Mathematics để làm cái này. Vì $p \equiv 3 \pmod{4}$ thế nên ta có thể sử dụng như trong bài **LEGENDRE SYMBOL**. Tọa độ y của $Q_A$ sẽ được tính bằng công thức $y = 5507^\frac{p+1}{4} \pmod{p}$ Từ đó ta tính được $y = 6287$ Giờ ta lấy điểm đó nhân với $n_B$ là sẽ tìm được secret thuiii ```sage= sage: E = EllipticCurve(GF(9739),[497,1768]) sage: x = 4726 sage: y_square = x**3 + 497*x + 1768 sage: y_square = y_square % 9739 sage: y_square 5507 sage: y = pow(y_square,(9739+1)//4,9739) sage: y 6287 sage: Q_a = E(x,y) sage: nB = 6534 sage: Q_a*nB (1791 : 2181 : 1) ``` ```python3= from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import hashlib def is_pkcs7_padded(message): padding = message[-message[-1]:] return all(padding[i] == len(padding) for i in range(0, len(padding))) def decrypt_flag(shared_secret: int, iv: str, ciphertext: str): # Derive AES key from shared secret sha1 = hashlib.sha1() sha1.update(str(shared_secret).encode('ascii')) key = sha1.digest()[:16] # Decrypt flag ciphertext = bytes.fromhex(ciphertext) iv = bytes.fromhex(iv) cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(ciphertext) if is_pkcs7_padded(plaintext): return unpad(plaintext, 16) else: return plaintext data = {'iv': 'cd9da9f1c60925922377ea952afc212c', 'encrypted_flag': 'febcbe3a3414a730b125931dccf912d2239f3e969c4334d95ed0ec86f6449ad8'} iv = data["iv"] encrypted_flag = data['encrypted_flag'] shared_secret = 1791 print(decrypt_flag(shared_secret, iv,encrypted_flag)) ``` **Flag: crypto{3ff1c1ent_k3y_3xch4ng3}** ### Smooth Criminal Source code của chall như sau: ```python3= from Crypto.Cipher import AES from Crypto.Util.number import inverse from Crypto.Util.Padding import pad, unpad from collections import namedtuple from random import randint import hashlib import os # Create a simple Point class to represent the affine points. Point = namedtuple("Point", "x y") # The point at infinity (origin for the group law). O = 'Origin' FLAG = b'crypto{??????????????????????????????}' def check_point(P: tuple): if P == O: return True else: return (P.y**2 - (P.x**3 + a*P.x + b)) % p == 0 and 0 <= P.x < p and 0 <= P.y < p def point_inverse(P: tuple): if P == O: return P return Point(P.x, -P.y % p) def point_addition(P: tuple, Q: tuple): # based of algo. in ICM if P == O: return Q elif Q == O: return P elif Q == point_inverse(P): return O else: if P == Q: lam = (3*P.x**2 + a)*inverse(2*P.y, p) lam %= p else: lam = (Q.y - P.y) * inverse((Q.x - P.x), p) lam %= p Rx = (lam**2 - P.x - Q.x) % p Ry = (lam*(P.x - Rx) - P.y) % p R = Point(Rx, Ry) assert check_point(R) return R def double_and_add(P: tuple, n: int): # based of algo. in ICM Q = P R = O while n > 0: if n % 2 == 1: R = point_addition(R, Q) Q = point_addition(Q, Q) n = n // 2 assert check_point(R) return R def gen_shared_secret(Q: tuple, n: int): # Bob's Public key, my secret int S = double_and_add(Q, n) return S.x def encrypt_flag(shared_secret: int): # Derive AES key from shared secret sha1 = hashlib.sha1() sha1.update(str(shared_secret).encode('ascii')) key = sha1.digest()[:16] # Encrypt flag iv = os.urandom(16) cipher = AES.new(key, AES.MODE_CBC, iv) ciphertext = cipher.encrypt(pad(FLAG, 16)) # Prepare data to send data = {} data['iv'] = iv.hex() data['encrypted_flag'] = ciphertext.hex() return data # Define the curve p = 310717010502520989590157367261876774703 a = 2 b = 3 # Generator g_x = 179210853392303317793440285562762725654 g_y = 105268671499942631758568591033409611165 G = Point(g_x, g_y) # My secret int, different every time!! n = randint(1, p) # Send this to Bob! public = double_and_add(G, n) print(public) # Bob's public key b_x = 272640099140026426377756188075937988094 b_y = 51062462309521034358726608268084433317 B = Point(b_x, b_y) # Calculate Shared Secret shared_secret = gen_shared_secret(B, n) # Send this to Bob! ciphertext = encrypt_flag(shared_secret) print(ciphertext) ``` Output ```sage= Point(x=280810182131414898730378982766101210916, y=291506490768054478159835604632710368904) {'iv': '07e2628b590095a5e332d397b8a59aa7', 'encrypted_flag': '8220b7c47b36777a737f5ef9caa2814cf20c1c1ef496ec21a9b4833da24a008d0870d3ac3a6ad80065c138a2ed6136af'} ``` Ta thấy rằng, đường cong Elliptics này sử dụng số nguyên tố $p$ không đủ lớn, thế nên ta có thể sử dụng hàm ``discrete_log`` được giá trị $n$ khi biết được $G$ và $Q_A$. ```sage= from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad import hashlib def is_pkcs7_padded(message): padding = message[-message[-1]:] return all(padding[i] == len(padding) for i in range(0, len(padding))) def decrypt_flag(shared_secret: int, iv: str, ciphertext: str): # Derive AES key from shared secret sha1 = hashlib.sha1() sha1.update(str(shared_secret).encode('ascii')) key = sha1.digest()[:16] # Decrypt flag ciphertext = bytes.fromhex(ciphertext) iv = bytes.fromhex(iv) cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(ciphertext) if is_pkcs7_padded(plaintext): return unpad(plaintext, 16) else: return plaintext p = 310717010502520989590157367261876774703 a = 2 b = 3 E = EllipticCurve(GF(p),[a,b]) Q_A = E(280810182131414898730378982766101210916, 291506490768054478159835604632710368904) Q_B = E(272640099140026426377756188075937988094, 51062462309521034358726608268084433317) G = E(179210853392303317793440285562762725654, 105268671499942631758568591033409611165) n = G.discrete_log(Q_A) print(n) print("SECRET:", n*Q_B) x = 171172176587165701252669133307091694084 data = {'iv': '07e2628b590095a5e332d397b8a59aa7', 'encrypted_flag': '8220b7c47b36777a737f5ef9caa2814cf20c1c1ef496ec21a9b4833da24a008d0870d3ac3a6ad80065c138a2ed6136af'} iv = data["iv"] encrypted_flag = data['encrypted_flag'] print(decrypt_flag(x, iv,encrypted_flag)) ``` **Flag: crypto{n07_4ll_curv3s_4r3_s4f3_curv3s}** ### Exceptional Curves Source code như sau: ```sage= from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from random import randint import hashlib FLAG = b'crypto{??????????????????????}' def gen_public_key(): private = randint(1, E.order() - 1) public = G * private return(public, private) def shared_secret(public_key, private_key): S = public_key * private_key return S.xy()[0] def encrypt_flag(flag): # Bob's public key b_x = 0x7f0489e4efe6905f039476db54f9b6eac654c780342169155344abc5ac90167adc6b8dabacec643cbe420abffe9760cbc3e8a2b508d24779461c19b20e242a38 b_y = 0xdd04134e747354e5b9618d8cb3f60e03a74a709d4956641b234daa8a65d43df34e18d00a59c070801178d198e8905ef670118c15b0906d3a00a662d3a2736bf B = E(b_x, b_y) # Calculate shared secret A, nA = gen_public_key() print(f'Public Key: {A}') secret = shared_secret(B, nA) # Derive AES key from shared secret sha1 = hashlib.sha1() sha1.update(str(secret).encode('ascii')) key = sha1.digest()[:16] # Encrypt flag iv = os.urandom(16) cipher = AES.new(key, AES.MODE_CBC, iv) ciphertext = cipher.encrypt(pad(FLAG, 16)) # Prepare encryption to send data = {} data['iv'] = iv.hex() data['encrypted_flag'] = ciphertext.hex() return data # Curve params p = 0xa15c4fb663a578d8b2496d3151a946119ee42695e18e13e90600192b1d0abdbb6f787f90c8d102ff88e284dd4526f5f6b6c980bf88f1d0490714b67e8a2a2b77 a = 0x5e009506fcc7eff573bc960d88638fe25e76a9b6c7caeea072a27dcd1fa46abb15b7b6210cf90caba982893ee2779669bac06e267013486b22ff3e24abae2d42 b = 0x2ce7d1ca4493b0977f088f6d30d9241f8048fdea112cc385b793bce953998caae680864a7d3aa437ea3ffd1441ca3fb352b0b710bb3f053e980e503be9a7fece # Define curve E = EllipticCurve(GF(p), [a, b]) # Protect against Pohlig-Hellman Algorithm assert is_prime(E.order()) # Create generator G = E.gens()[0] print(f'Generator: {G}') encrypted_flag = encrypt_flag(FLAG) print(encrypted_flag) ``` Output như sau: ```sage= Generator: (3034712809375537908102988750113382444008758539448972750581525810900634243392172703684905257490982543775233630011707375189041302436945106395617312498769005 : 4986645098582616415690074082237817624424333339074969364527548107042876175480894132576399611027847402879885574130125050842710052291870268101817275410204850 : 1) Public Key: (4748198372895404866752111766626421927481971519483471383813044005699388317650395315193922226704604937454742608233124831870493636003725200307683939875286865 : 2421873309002279841021791369884483308051497215798017509805302041102468310636822060707350789776065212606890489706597369526562336256272258544226688832663757 : 1) {'iv': '719700b2470525781cc844db1febd994', 'encrypted_flag': '335470f413c225b705db2e930b9d460d3947b3836059fb890b044e46cbb343f0'} ``` Bài này không còn như bài trước nữa, vì E.order đã là số nguyên tố. Mình thử hiển thị E.order thì nó đúng bằng giá trị p. Sau khi mình tra google với từ khóa E.order is prime and equal p thì mình thu được một đường [**link**](https://crypto.stackexchange.com/questions/70454/why-smarts-attack-doesnt-work-on-this-ecdlp) này. Bài này thuộc dạng tấn công [**Smart Attack**](https://wstein.org/edu/2010/414/projects/novotney.pdf) nên mình có được hàm sau đây: ```sage= def SmartAttack(P,Q,p): E = P.curve() Eqp = EllipticCurve(Qp(p, 2), [ ZZ(t) + randint(0,p)*p for t in E.a_invariants() ]) P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True) for P_Qp in P_Qps: if GF(p)(P_Qp.xy()[1]) == P.xy()[1]: break Q_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True) for Q_Qp in Q_Qps: if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]: break p_times_P = p*P_Qp p_times_Q = p*Q_Qp x_P,y_P = p_times_P.xy() x_Q,y_Q = p_times_Q.xy() phi_P = -(x_P/y_P) phi_Q = -(x_Q/y_Q) k = phi_Q/phi_P return ZZ(k) ``` Giờ thì giải bài thôi nào ```sage= from Crypto.Cipher import AES from Crypto.Util.number import inverse from Crypto.Util.Padding import pad, unpad from collections import namedtuple from random import randint import hashlib import os def is_pkcs7_padded(message): padding = message[-message[-1]:] return all(padding[i] == len(padding) for i in range(0, len(padding))) def decrypt_flag(shared_secret: int, iv: str, ciphertext: str): # Derive AES key from shared secret sha1 = hashlib.sha1() sha1.update(str(shared_secret).encode('ascii')) key = sha1.digest()[:16] # Decrypt flag ciphertext = bytes.fromhex(ciphertext) iv = bytes.fromhex(iv) cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(ciphertext) if is_pkcs7_padded(plaintext): return unpad(plaintext, 16).decode('ascii') else: return plaintext.decode('ascii') def SmartAttack(P,Q,p): E = P.curve() Eqp = EllipticCurve(Qp(p, 2), [ ZZ(t) + randint(0,p)*p for t in E.a_invariants() ]) P_Qps = Eqp.lift_x(ZZ(P.xy()[0]), all=True) for P_Qp in P_Qps: if GF(p)(P_Qp.xy()[1]) == P.xy()[1]: break Q_Qps = Eqp.lift_x(ZZ(Q.xy()[0]), all=True) for Q_Qp in Q_Qps: if GF(p)(Q_Qp.xy()[1]) == Q.xy()[1]: break p_times_P = p*P_Qp p_times_Q = p*Q_Qp x_P,y_P = p_times_P.xy() x_Q,y_Q = p_times_Q.xy() phi_P = -(x_P/y_P) phi_Q = -(x_Q/y_Q) k = phi_Q/phi_P return ZZ(k) # Curve params p = 0xa15c4fb663a578d8b2496d3151a946119ee42695e18e13e90600192b1d0abdbb6f787f90c8d102ff88e284dd4526f5f6b6c980bf88f1d0490714b67e8a2a2b77 a = 0x5e009506fcc7eff573bc960d88638fe25e76a9b6c7caeea072a27dcd1fa46abb15b7b6210cf90caba982893ee2779669bac06e267013486b22ff3e24abae2d42 b = 0x2ce7d1ca4493b0977f088f6d30d9241f8048fdea112cc385b793bce953998caae680864a7d3aa437ea3ffd1441ca3fb352b0b710bb3f053e980e503be9a7fece # Define curve E = EllipticCurve(GF(p), [a, b]) G = E(3034712809375537908102988750113382444008758539448972750581525810900634243392172703684905257490982543775233630011707375189041302436945106395617312498769005,4986645098582616415690074082237817624424333339074969364527548107042876175480894132576399611027847402879885574130125050842710052291870268101817275410204850) b_x = 0x7f0489e4efe6905f039476db54f9b6eac654c780342169155344abc5ac90167adc6b8dabacec643cbe420abffe9760cbc3e8a2b508d24779461c19b20e242a38 b_y = 0xdd04134e747354e5b9618d8cb3f60e03a74a709d4956641b234daa8a65d43df34e18d00a59c070801178d198e8905ef670118c15b0906d3a00a662d3a2736bf B = E(b_x, b_y) A = E(4748198372895404866752111766626421927481971519483471383813044005699388317650395315193922226704604937454742608233124831870493636003725200307683939875286865, 2421873309002279841021791369884483308051497215798017509805302041102468310636822060707350789776065212606890489706597369526562336256272258544226688832663757) n_A = SmartAttack(G, A, p) x = (ZZ(n_A)*B)[0] data = {'iv': '719700b2470525781cc844db1febd994', 'encrypted_flag': '335470f413c225b705db2e930b9d460d3947b3836059fb890b044e46cbb343f0'} iv = data["iv"] encrypted_flag = data['encrypted_flag'] print(decrypt_flag(x, iv,encrypted_flag)) ``` **Flag: crypto{H3ns3l_lift3d_my_fl4g!}** ### Micro Transmissions Source code của chall như sau: ```sage= from Crypto.Util.number import getPrime from Crypto.Random import random from Crypto.Cipher import AES from Crypto.Util.number import inverse from Crypto.Util.Padding import pad, unpad import hashlib FLAG = b"crypto{???????????????????}" def gen_key_pair(G, nbits): n = random.getrandbits(nbits) P = n*G return P.xy()[0], n def gen_shared_secret(P, n): S = n*P return S.xy()[0] def encrypt_flag(shared_secret: int): # Derive AES key from shared secret sha1 = hashlib.sha1() sha1.update(str(shared_secret).encode('ascii')) key = sha1.digest()[:16] # Encrypt flag iv = os.urandom(16) cipher = AES.new(key, AES.MODE_CBC, iv) ciphertext = cipher.encrypt(pad(FLAG, 16)) # Prepare data to send data = {} data['iv'] = iv.hex() data['encrypted_flag'] = ciphertext.hex() return data # Efficient key exchange nbits = 64 pbits = 256 # Curve parameters p = getPrime(pbits) a = 1 b = 4 E = EllipticCurve(GF(p), [a,b]) G = E.gens()[0] print(f"Sending curve parameters:") print(f"{E}") print(f"Generator point: {G}\n") # Generate key pairs ax, n_a = gen_key_pair(G, nbits) bx, n_b = gen_key_pair(G, nbits) print(f"Alice sends public key: {ax}") print(f"Bob sends public key: {bx}\n") # Calculate point from Bob B = E.lift_x(bx) # Encrypted flag with shared secret shared_secret = gen_shared_secret(B,n_a) encrypted_flag = encrypt_flag(shared_secret) print(f"Alice sends encrypted_flag: {encrypted_flag}") ``` ```sage= Sending curve parameters: Elliptic Curve defined by y^2 = x^3 + x + 4 over Finite Field of size 99061670249353652702595159229088680425828208953931838069069584252923270946291 Generator point: (43190960452218023575787899214023014938926631792651638044680168600989609069200 : 20971936269255296908588589778128791635639992476076894152303569022736123671173 : 1) Alice sends public key: 87360200456784002948566700858113190957688355783112995047798140117594305287669 Bob sends public key: 6082896373499126624029343293750138460137531774473450341235217699497602895121 Alice sends encrypted_flag: {'iv': 'ceb34a8c174d77136455971f08641cc5', 'encrypted_flag': 'b503bf04df71cfbd3f464aec2083e9b79c825803a4d4a43697889ad29eb75453'} ``` Bài này ta thấy rằng số nguyên tố $p$ có độ dài là 256 bits, còn số $n$ thì sẽ có độ dài là 64 bits thế nên $n < 18446744073709551615$. Ta sẽ sử dùng Pohlig-Hellman để tấn công challenge này. Bạn có thể tham khảo tại 2 nguồn [**Này**](https://koclab.cs.ucsb.edu/teaching/ecc/project/2015Projects/Sommerseth+Hoeiland.pdf) và [**Này**](https://wstein.org/edu/2010/414/projects/novotney.pdf). Ngoài ra bạn cũng có thể coi [**Video**](https://www.youtube.com/watch?v=BXFNYVmdtJUs) này để có thể hiểu về Pohlig-Hellman. Order của đường cong này được factor như sau: $7 * 11 * 17 * 191 * 317 * 331 * 5221385621 * 5397618469 * 210071842937040101 * 637807437018177170959577732683$, mà số $n < 18446744073709551615$, thế nên ta sẽ bỏ 2 số nguyên tố cuối cùng và sử dụng hàm $discrete_log$ như challenge ``Smooth`` trên. Code sage như sau: ```sage= from Crypto.Cipher import AES from Crypto.Util.number import inverse from Crypto.Util.Padding import pad, unpad from collections import namedtuple from random import randint import hashlib import os def is_pkcs7_padded(message): padding = message[-message[-1]:] return all(padding[i] == len(padding) for i in range(0, len(padding))) def decrypt_flag(shared_secret: int, iv: str, ciphertext: str): # Derive AES key from shared secret sha1 = hashlib.sha1() sha1.update(str(shared_secret).encode('ascii')) key = sha1.digest()[:16] # Decrypt flag ciphertext = bytes.fromhex(ciphertext) iv = bytes.fromhex(iv) cipher = AES.new(key, AES.MODE_CBC, iv) plaintext = cipher.decrypt(ciphertext) if is_pkcs7_padded(plaintext): return unpad(plaintext, 16).decode('ascii') else: return plaintext.decode('ascii') p = 99061670249353652702595159229088680425828208953931838069069584252923270946291 E = EllipticCurve(GF(p), [1,4]) G = E(43190960452218023575787899214023014938926631792651638044680168600989609069200, 20971936269255296908588589778128791635639992476076894152303569022736123671173) A = E.lift_x(87360200456784002948566700858113190957688355783112995047798140117594305287669) B = E.lift_x(6082896373499126624029343293750138460137531774473450341235217699497602895121) order = G.order() print(factor(order)) max_value_n = 18446744073709551615 results = [] factors = [] mul = 1 for prime, exponent in factor(order): e = (order//(prime**exponent)) G_new = G*e A_new = A*e dlog = G_new.discrete_log(A_new) print(prime, dlog) results.append(dlog) factors.append(prime**exponent) mul *= prime if mul > max_value_n: break n_A = crt(results,factors) print("Found:",n_A) x = (ZZ(n_A)*B)[0] data = {'iv': 'ceb34a8c174d77136455971f08641cc5', 'encrypted_flag': 'b503bf04df71cfbd3f464aec2083e9b79c825803a4d4a43697889ad29eb75453'} iv = data["iv"] encrypted_flag = data['encrypted_flag'] print(decrypt_flag(x, iv,encrypted_flag)) ``` **Flag: crypto{d0nt_l3t_n_b3_t00_sm4ll}**