# EDDH - zer0pts CTF 2022 ###### tags: `zer0pts CTF 2022` Writeups: https://hackmd.io/@ptr-yudai/rJgjygUM9 ## overview The challenge implements Diffie-Hellman key exchange protocol on the Twisted Edwards Curve. The challenge server calculates $sG$ with secret $s$, which we want to know, and sends us $sG$. Normally, we calculate $tG$ with our secret $t$ and send $tG$ to the server. Then both side shares their own secret $stG$. ## solution The mathematics on the Twisted Edwards Curve is originally implemented. Additionaly, there isn't the check that given $tG$ is on the curve. So it is the chance of degenerate attack. Let's focus to the addition algorithm. $$ x_3 = \frac{x_1y_2 + y_1x_2}{1 + dx_1x_2y_1y_2} \pmod p\\ y_3 = \frac{y_1y_2 - ax_1x_2}{1 - dx_1x_2y_1y_2} \pmod p $$ The calculation on the curve can be reduced to linear algebra in specific case. If we calculate $(0, y_1) + (0, y_2)$, the result comes to $(0, y_1y_2)$. In this challenge, if we send a $(0, y)$ as the $tG$, the server calculates $s * tG = (0, y^s \mod p)$ Because $p-1$ consists of many small factors, discrete logarithm of $y^s \mod p$ can be calculated by Pohligh-Hellman algorithm. ## exploit ```python= from ptrlib import Socket, Process from Crypto.Util.Padding import pad from Crypto.Util.number import bytes_to_long, long_to_bytes, inverse from Crypto.Cipher import AES from hashlib import sha256 import ast import os n = 256 p = 64141017538026690847507665744072764126523219720088055136531450296140542176327 a = 362 d = 1 q = 64141017538026690847507665744072764126693080268699847241685146737444135961328 c = 4 gx = 36618472676058339844598776789780822613436028043068802628412384818014817277300 gy = 9970247780441607122227596517855249476220082109552017755637818559816971965596 def xor(xs, ys): return bytes(x^^y for x, y in zip(xs, ys)) def pad(b, l): return b + b"\0" + b"\xff" * (l - (len(b) + 1)) def unpad(b): l = -1 while b[l] != 0: l -= 1 return b[:l] def add(P, Q): (x1, y1) = P (x2, y2) = Q x3 = (x1*y2 + y1*x2) * inverse(1 + d*x1*x2*y1*y2, p) % p y3 = (y1*y2 - a*x1*x2) * inverse(1 - d*x1*x2*y1*y2, p) % p return (x3, y3) def mul(x, P): Q = (0, 1) x = x % q while x > 0: if x % 2 == 1: Q = add(Q, P) P = add(P, P) x = x >> 1 return Q def to_bytes(P): x, y = P return int(x).to_bytes(n // 8, "big") + int(y).to_bytes(n // 8, "big") sock = Socket("localhost", 10929) sg = ast.literal_eval(sock.recvlineafter("sG = ").decode()) sock.sendlineafter("tG = ", "{}".format((0, 2))) msg = b"\0" sock.sendline(msg.hex()) res = bytes.fromhex(sock.recvline().decode()) y = bytes_to_long(xor(res[-n//8:], b"\xff" * 256)) s = discrete_log(GF(p)(y), GF(p)(2)) assert pow(2, s, p) == y print("s = {}".format(s)) share = to_bytes(mul(int(s), (0, 2))) sock.sendline(xor(pad(b"flag", 2*n//8), share).hex()) r = bytes.fromhex(sock.recvline().decode()) msg = unpad(xor(r, share)) aes = AES.new(key=sha256(long_to_bytes(s)).digest(), mode=AES.MODE_ECB) print(aes.decrypt(msg)) ```