cscg20-rsa

CSCG 2020 Challenge 'RSA Service'
git clone https://git.sinitax.com/sinitax/cscg20-rsa
Log | Files | Refs | sfeed.txt

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())