|
| 1 | +primes = list(prime_range(3,117)) |
| 2 | +p = 4 * prod(primes) - 1 |
| 3 | +base = bytes((int(p).bit_length() + 7) // 8) |
| 4 | +Fp = GF(p) |
| 5 | + |
| 6 | +def from_weierstrass(EC): |
| 7 | + a, b = EC.a4(), EC.a6() |
| 8 | + F = EC.base_field() |
| 9 | + PR = PolynomialRing(F, name="z") |
| 10 | + z = PR.gens()[0] |
| 11 | + roots = (z**3 + a*z + b).roots() |
| 12 | + assert len(roots) > 0 |
| 13 | + alpha = roots[0][0] |
| 14 | + s = (3*alpha**2 + a).sqrt() ** (-1) |
| 15 | + return -3 * (-1)**s.is_square() * alpha * s |
| 16 | + |
| 17 | +def to_weierstrass(A): |
| 18 | + while True: |
| 19 | + B = Fp.random_element() |
| 20 | + if B.is_square() and B != 0: |
| 21 | + break |
| 22 | + a = (3 - A**2) * pow(3 * B**2, -1, p) |
| 23 | + b = (2 * A**3 - 9 * A) * pow(27 * B**3, -1, p) |
| 24 | + return EllipticCurve(Fp, [a, b]) |
| 25 | + |
| 26 | +def group_action(pub, priv): |
| 27 | + es = priv.copy() |
| 28 | + A = pub |
| 29 | + assert len(es) == len(primes) |
| 30 | + EC = to_weierstrass(A) |
| 31 | + while True: |
| 32 | + if all(e == 0 for e in es): |
| 33 | + break |
| 34 | + x = Fp(randint(1, p-1)) |
| 35 | + r = Fp(x ** 3 + A * x ** 2 + x) |
| 36 | + s = kronecker_symbol(r, p) |
| 37 | + assert (2 * is_square(r)) - 1 == s |
| 38 | + I = [i for i, e in enumerate(es) if sign(e) == s] |
| 39 | + if len(I) == 0: |
| 40 | + continue |
| 41 | + if s == -1: |
| 42 | + EC = EC.quadratic_twist() |
| 43 | + while True: |
| 44 | + tmp = EC.random_element() |
| 45 | + if not tmp.is_zero(): |
| 46 | + break |
| 47 | + x = tmp.xy()[0] |
| 48 | + t = prod([primes[i] for i in I]) |
| 49 | + P = EC.lift_x(x) |
| 50 | + assert (p + 1) % t == 0 |
| 51 | + Q = ((p + 1) // t) * P |
| 52 | + for i in I: |
| 53 | + assert t % primes[i] == 0 |
| 54 | + R = (t // primes[i]) * Q |
| 55 | + if R.is_zero(): |
| 56 | + continue |
| 57 | + phi = EC.isogeny(R) |
| 58 | + EC = phi.codomain() |
| 59 | + Q = phi(Q) |
| 60 | + assert t % primes[i] == 0 |
| 61 | + t = t // primes[i] |
| 62 | + es[i] -= s |
| 63 | + if s == -1: |
| 64 | + EC = EC.quadratic_twist() |
| 65 | + return from_weierstrass(EC) |
| 66 | + |
| 67 | +def truncated(n, ratio): |
| 68 | + |
| 69 | + kbits = int(n.bit_length() * ratio) |
| 70 | + return (n >> kbits) << kbits |
| 71 | + |
| 72 | +class CSIDH: |
| 73 | + |
| 74 | + def __init__(self): |
| 75 | + self.priv = [randint(-2, 2) for _ in primes] |
| 76 | + self.pub = group_action(0, self.priv) |
| 77 | + |
| 78 | + def getPublic(self): |
| 79 | + return self.pub |
| 80 | + |
| 81 | + def getShare(self, other): |
| 82 | + return group_action(other, self.priv) |
| 83 | + |
| 84 | +Angel, Devil = CSIDH(), CSIDH() |
| 85 | +print(f"Angel's Public Key = {Angel.getPublic()}") |
| 86 | +print(f"Devil's Public Key = {Devil.getPublic()}") |
| 87 | + |
| 88 | +choice = input("Deal with [A]ngel or [D]evil? Make your choice carefully: ") |
| 89 | +if choice == "A": |
| 90 | + for i in range(5): |
| 91 | + A = int(input("Enter the montgomery coefficient: ")) |
| 92 | + print(truncated(int(Angel.getShare(A)), 0.4)) |
| 93 | +elif choice == "D": |
| 94 | + for i in range(4): |
| 95 | + D = int(input("Enter the montgomery coefficient: ")) |
| 96 | + print(truncated(int(Devil.getShare(D)), 0.3)) |
| 97 | +else: |
| 98 | + print("Ok ... You are from Super Guesser, right?") |
| 99 | + |
| 100 | +S = int(Angel.getShare(Devil.getPublic())) |
| 101 | +if int(input("Did Angel or Devil tell your the secret: ")) == S: |
| 102 | + try: |
| 103 | + f = open('flag.txt','r') |
| 104 | + FLAG = f.read() |
| 105 | + f.close() |
| 106 | + except: |
| 107 | + FLAG = "idek{debug}" |
| 108 | + print(f"FLAG = {FLAG}") |
| 109 | +else: |
| 110 | + print("G_G") |
| 111 | + |
| 112 | + |
0 commit comments