0CTF Quals 2019: "babyrsa" writeup

This challenge is about a variant of RSA encryption where the integers are replaced by a polynomial ring over F2.

Recall that RSA works in a ring Z/n where n=pq is a product of two large random primes. To break RSA, it is sufficient to compute the size φ(n) of the unit group (Z/n) since this allows one to compute eth roots in that group. The owner of the private key can compute φ(n) as (p1)(q1); without knowing p and q this problem is assumed to be hard.

In this challenge, Z is replaced by F2[x] and n is replaced by a big polynomial fF2[x] of degree 2049.

The crucial observation now is that factoring polynomials over finite fields is easy, hence we can split f into its two irreducible factors g,hF2[x] and by CRT there exists a ring isomorphism F2[x]/fF2[x]/g×F2[x]/h.

Now since F2[x]/gF2degg and similarly F2[x]/hF2degh, we know that |(F2[x]/f)|=(2degg1)(2degh1). Therefore, we can compute eth roots in (F2[x]/f), which breaks babyrsa:

#!/usr/bin/env sage
import re
from pubkey import P,n,e

R.<a> = GF(2**2049)
c_int = Integer(open('flag.enc','rb').read().encode('hex'), 16)
c_poly = P(R.fetch_int(c_int))

(g,_), (h,_) = factor(n)
phi = (2**g.degree() - 1) * (2**h.degree() - 1)

d = ZZ(Mod(e,phi)**-1)

m_poly = pow(c_poly, d, n)
m_int = R(m_poly).integer_representation()
m = format(m_int, '0256x').decode('hex')
for flag in re.findall(b'flag{[^}]+}', m):
    print(flag)

This sage script prints the flag:

$ ./pwn.sage
flag{P1ea5e_k33p_N_as_A_inTegeR~~~~~~}
$ 

@hxp