solve.py (2526B)
1#!/usr/bin/env python3 2 3from pwn import process, context 4from Crypto.PublicKey import RSA 5from OpenSSL.crypto import load_privatekey, FILETYPE_PEM 6from sympy.ntheory.modular import crt 7from sympy.ntheory import discrete_log 8from sympy import nextprime, isprime, prod, sieve 9from math import isqrt, gcd 10import random 11 12# We can split the decryption into two discrete logarithm problems: 13# m1^((p-1)*j+dp) = m2 (mod p) and m1^((q-1)*k+dq) = m2 (mod q) 14# If we construct p and q in a way that the factors of phi(n) are small, 15# we can use the Pohlig-Hellman algorithm for finding dp and dq quickly. 16# To construct d from dp and dq we use the Chinese-Remainder-Theorem. 17# CRT requires the moduli to be coprime, so we use sp = p and sq = q/gcd(p-1,q-1) 18# as moduli instead. To expand the resulting solution di to the (p-1)*(q-1) 19# ring again, we need to try all solutions di + i * sp * sq * gcd(p-1,q-1) 20# for i in [0,gcd(p-1,q-1)[. 21 22# P.S: Some players solved this by trial, choosing d and factoring 23# m1**d - m2 using FactorDB, but that solution seems less intended. 24 25m1 = int.from_bytes(b"Quack! Quack!", "big") 26m2 = int.from_bytes(b"Hello! Can you give me the flag, please? I would really appreciate it!", "big") 27 28def build_prime(primes, limit): 29 while True: 30 ps = [] 31 while prod(ps) < limit: 32 ps += [random.choice(primes),] * random.randint(1, 10) 33 if isprime(prod(ps) * 2 + 1): 34 return ps, prod(ps), prod(ps) * 2 + 1 35 36def build_params(primes): 37 while True: 38 pp, sp, p = build_prime(list(primes), isqrt(m2)+1) 39 _, sq, q = build_prime(list(primes - set(pp)), isqrt(m2)+1) 40 try: 41 dp = int(discrete_log(p, m2, m1)) 42 dq = int(discrete_log(q, m2, m1)) 43 except: 44 continue # m2 not in group m1^x mod .. 45 gcdpq = gcd(p, q) # = 2 by construction 46 di = int(crt([sp * gcdpq, sq], [dp % (sp * gcdpq), dq % sq])[0]) 47 for i in range(gcdpq): 48 d = di + i * sp * sq * gcdpq 49 if pow(m1, d, p*q) != m2: 50 continue # wrong i 51 if gcd(d, (p-1)*(q-1)) != 1: 52 continue # unlucky 53 e = pow(d, -1, (p-1)*(q-1)) 54 return p*q, e, d 55 56params = build_params(set(sieve[1:100])) 57key = RSA.construct(params, consistency_check=False) 58 59io = process(["python", "server.py"]) 60io.sendlineafter(b"PEM format:", key.exportKey("PEM") + b"\n") 61io.sendlineafter(b"your message:", b"Quack! :D") 62io.readline().decode() 63print(io.readline().decode())