[Crypto] Primordial

This is a puzzle from the ASIS CTF Final 2019.

Here, we're faced with the identical set-up to Serifin. Given N=p1p2N = p_1p_2, a product of two primes, and c=m65537(modN)c = m^{65537} \pmod{N} where mm is our message, determine mm.

N = 129267954332200676615739227295907855158658739979210900708976549380609989409956408435684374935748935918839455337906315852534764123844258593239440161506513191263699117749750762173637210021984649302676930074737438675523494086114284695245002078910492689149197954131695708624630827382893369282116803593958219295071;
c = 123828011786345664757585942310038992331055176660679165398920365204623335291878173959876308977115607518900415801962848580747200997185606420410437572095447682798017319498742987210291931673054112968527192210375048958877146513037193636705010232608708929769672565897606711155251354598146987357344810260248226805138;
0.2s

The vulnerability once again is due to poor generation of the 512-bits primes p1p_1 and p2p_2.

def primorial(p):
    # Computes the product of all primes <= p
    q = 1
    s = 1
    while q < p:
        r = gmpy2.next_prime(q)
        if r <= p:
            s *= r
            q = r
        else:
            break
    return s
def gen_prime(nbit):
    while True:
        s = getPrime(36)
        # Generates a Random 36 Bit Prime
        a = primorial(getPrime(random.randint(7, 9))) #[7, 9] inclusive
        b = primorial(getPrime(random.randint(2, 5))) #[2, 5] inclusive
        for r in range(10**3, 3*10**3, 2):
            p = (s * a // b) - r
            if gmpy2.is_prime(p) and len(bin(p)[2:]) == nbit:
                # Checks that p is prime and with nbit bits.
                return int(p)
Generation of Primes
Python

Let us denote the primorial of nn as n#n\#. We can see that our primes will be of the form,

pi=siai#bi#ri,p_i = s_i\cdot \frac{a_i\#}{b_i\#} - r_i,

where ss is a 36 bit prime, ai[67,509]a_i \in [67, 509], bi[2,31]b_i \in [2, 31] and ri[103,3103]r_i \in [10^3, 3\cdot 10^3]. Our first step is to find r1r_1 and r2r_2.

Observe that

N=(s1a1#b1#r1)(s2a2#b2#r2)=s1s2a1#a2#b1#b2#s1r2a1#b1#s2r1a2#b2#+r1r2.\begin{aligned} N &= (s_1 \frac{a_1\#}{b_1\#} - r_1)(s_2 \frac{a_2\#}{b_2\#} - r_2)\\ &= s_1s_2\frac{a_1\# \cdot a_2\#}{b_1\# \cdot b_2\#} - s_1 r_2 \frac{a_1\#}{b_1\#} - s_2 r_1 \frac{a_2\#}{b_2\#} + r_1r_2. \end{aligned}

Hence, we expect by enumerating all the possible rir_i, and then compute Nr1r2N - r_1r_2, we would expect to find a number that is divisible by all the primes in (31,67](31, 67].

using Pkg; Pkg.add("Primes")
using Primes
0.7s
function Findrs()
  Divisors = BigInt.(primes(32, 67)) # Exclude 31
  Π = prod(Divisors)
  
  for r1 in 1000:2:3000
    for r2 in r1:2:3000
      (N - r1*r2) % Π == 0 && return (r1, r2)
    end
  end
end
r1, r2 = Findrs()
0.4s
(1000, 1574)

Without loss of generality, let r1r2.r_1 \leq r_2. When we reach this stage we thought of two directions to proceed.

  • Enumerate all the possible sis_i, then compute the possible aia_i and bib_i such that pip_i is a 512-bits prime. This is potentially feasible, but may take a few hours, as such was not the direction we greatly pursued.

  • If we are lucky, λ1=min(a1,a2)<<λ2=max(a1,a2)\lambda_1 = \min(a_1,a_2) << \lambda_2 = \max(a_1, a_2). Then, we can consider modulo λ2#λ1#\frac{\lambda_2\#}{\lambda_1\#} and reconstruct a small list of possible sis_i. This was indeed the direction we have taken.

First let us compute max(b1,b2)\max(b_1,b_2) and min(a1,a2)\min(a_1,a_2).

filter(x -> (N - r1*r2) % x == 0, primes(1<<9) )
0.3s
67-element Array{Int64,1}: 3 11 31 37 41 43 47 53 59 61 ⋮ 331 337 347 349 353 359 367 373 379

It is very likely that max(b1,b2)=31\max(b_1,b_2) = 31 and min(a1,a2)=379\min(a_1,a_2) = 379. We then perform the following algorithm.

function CRT(N, Residue)
  Π = prod(N)
  mod(sum(ai * invmod(Π ÷ ni, ni) * Π ÷ ni for (ni, ai) in zip(N, Residue)), Π)
end
function Finds1()
  Mods = primes(383, 419) # This is as prod(Mods) will be large than a 31 bit s_1
  Π = prod(BigInt.(Mods))
  # t = s_1*r_2*a_1#/b_1# or t = s_2*r_1*a_2#/b_1#
  t = Π - CRT(Mods, (N - r1*r2) .% Mods)
  
  # We try possible b_i
  b = 380 # min(a_1, a_2) + 1, we call prevprime to get 379
  while b > 2
    b = prevprime(b-1)
    t *= invmod(b, Π); t %= Π
    
    # Assume t = s_1
    s1 = t * invmod(r2, Π); s1 %= Π
    p1 = s1*prod(BigInt.(primes(b, 379))) - r1
    (N % p1 == 0) && return p1
    
    # Else t = s_2
    s2 = t * invmod(r1, Π); s2 %= Π
    p2 = s2*prod(BigInt.(primes(b, 379))) - r2
    (N % p2 == 0) && return p2
  end
  return -1
end
Finds1()
0.5s
-1

As it turns out, approach 2 doesn't work as a1a_1 and a2a_2 are too close (the precise reason will be clear in a minute). Thus, the above code sadly returns -1. While doing this process, we found a third method.

  • Let K=379#29#.K = \frac{379\#}{29\#}. Then there exist cic_i such that pi=ciKrip_i = c_iK -r_i. Then,

N=(c1c2)K2(r1c2+r2c1)K+r1r2.N = (c_1c_2)K^2 - (r_1c_2 + r_2c_1)K + r_1r_2.

Hence, as KK is really big, we can find cic_i by solving a quadratic equation.

In fact, more is true as

K = prod(BigInt.(primes(31, 379)))
36 + log2(K)
0.2s
512.446

Thus, ci=sic_i = s_i! This explains why the previous method had no chance of working, indeed we have

K=a1#b1#=a2#b2#=379#29#.K = \frac{a_1\#}{b_1\#} = \frac{a_2\#}{b_2\#} = \frac{379\#}{29\#}.

Let us compute sis_i, shall we?

Sum = (K - ((N - r1*r2) ÷ K) % K)
Prod = (N - r1*r2 + Sum*K) ÷ K^2
s2, s1 = map(first, factor(Prod).pe) # The order needs to be checked
1.3s
2-element Array{BigInt,1}: 36308176459 50395155227

Now with the private key, we just need to undo the RSA.

function long_to_bytes(Val)
  Val == 0 && return ""
  return long_to_bytes(Val ÷ 256) * Char(Val % 256)
end
p1 = s1*K - r1
p2 = s2*K - r2
invert(message, prime) = powermod(c, invmod(65537, prime - 1), prime)
m = CRT([p1, p2], [invert(c, p1), invert(c, p2)]) % N
long_to_bytes(m)
0.4s
"ASIS{f4C7OR1ZIn9_PrimoR!4L_pR1m3z_Iz_3A5Y_I5nt_iT?}"
Runtimes (1)