Broken RSA

Description

My friend had a secret message for me. However, he made a mistake when encrypting the message, and now I am unable to decrypt it! Could you decrypt and recover the message for me?

812B
Open

TL;DR

  • Notice that e = 1616, which makes phi and e not coprime and which is why normal RSA decryption won't work

  • Perform c%p and c%q and RSA decrypt both with their respective d and phi values

  • Use tonelli shank's algorithm to perform square root mod of p and q

  • Perform CRT on the values to get original plaintext

Solution

Initial Analysis

If you want to skip the explanation, you can go to e and phi are not coprime.

Looking at challenge.py, everything looks like the usual RSA encryption scheme, except for the value of e looking like an invalid value. This e is the one that makes the RSA decryption difficult to perform.

In standard RSA encryption scheme, the public exponent e must be an odd number.

If e is even, it will most definitely share a common factor with phi that is != 1, since p-1 will be an even number. This means that e and phi are not coprime.

If e and phi are not coprime, then there may be multiple decryption exponent d that may decrypt the ciphertext.

In this case, normal RSA decryption will not work.

Luckily, we are given the values of p and q, as well as the ciphertext c.

To determine why normal RSA decryption doesn't work and how to decrypt the ciphertext with this e value, we have to first understand the concept behind the RSA cryptosystem: Why and how it works.

The brilliance behind RSA decryption

We know that RSA encryption goes like this:

n=pβˆ—qct=meβ€Šmodβ€Šnn = p*q \\ct=m^e \bmod n

When decrypting in normal RSA, these steps are performed:

phi=(pβˆ’1)βˆ—(qβˆ’1)d=eβˆ’1β€Šmodβ€Šphipt=ctdβ€Šmodβ€Šnphi = (p-1)*(q-1) \\d = e^{-1} \bmod phi \\pt = ct^d \bmod n

But why does this decryption method work?

Firstly, we can rewrite d=eβˆ’1β€Šmodβ€Šphid = e^{-1} \bmod phi into ed=1+kβˆ—phied = 1+k*phi, where kk is any finite integer.

We can also rewrite pt=ctdβ€Šmodβ€Šnpt = ct^d \bmod n into pt=medβ€Šmodβ€Šnpt = m^{ed} \bmod n.

By substituting eded into the second ptpt equation, we have pt=m1+kβˆ—phiβ€Šmodβ€Šnpt = m^{1+k*phi} \bmod n, and thus pt=m1mkβˆ—phiβ€Šmodβ€Šnpt = m^1m^{k*phi} \bmod n.

Using Euler's Theorem, we can then get this equation pt=m11kβ€Šmodβ€Šnpt = m^11^k \bmod n, and simplifying it gives us our original plaintext! pt=mβ€Šmodβ€Šnpt = m \bmod n (that is if our plaintext < n)

But this only works if e and phi are coprime: gcd(e,phi)=1.

e and phi are not coprime

If we check the gcd of the e and phi we have, we note that gcd(e,phi)=16 in this case, so that is a problem.

According to Euler's Theorem, if gcd(e,phi)=1, we will have ed=1+kβˆ—phied = 1+k*phi.

Since we have gcd(e,phi)=16, so we will have ed=16+kβˆ—phied = 16 + k*phi. Following the decryption process as stated above, we will end up with pt=m16β€Šmodβ€Šnpt = m^{16} \bmod n. Since m16m^{16} is definitely larger than nn, we can't take the 16th16^{th} root and call it a day.

But wait, there is the Tonelli-Shanks Algorithm that can compute the modulus square root of any number, given that the modulus is a prime.

The Tonelli-Shanks Algorithm

We note that our modulus n=pβˆ—qn=p*q and is therefore not a prime, so how do we apply this algorithm?

The only primes we have here are pp and qq. What if we calculate the ciphertext as a modulus of pp or qq instead?

So we can calculate our new ciphertext using the given ciphertext: ctp=cβ€Šmodβ€Špctp = c \bmod p. This is equivalent to ctp=meβ€Šmodβ€Šnβ€Šmodβ€Špctp = m^e \bmod n \bmod p which is simplified to ctp=meβ€Šmodβ€Špctp = m^e \bmod p (note that β€Šmodβ€ŠΒ n\bmod\ n is gone as pp is a factor of nn)

Now we get the Euler's Totient function for pp: phip=pβˆ’1phip = p-1

phip

Then we calculate dp = inverse(e,phip), then decrypt the ciphertext ptp = pow(ctp,dp,p). Since gcd(e,phip)=4, it means we have ptp=m4β€Šmodβ€Špptp = m^4 \bmod p. So we have to use Tonelli twice to get back our plaintext.

Since pp is a prime, we can now use Tonelli-Shanks Algorithm (code)! Let's test this with a test flag of our own:

Output

We successfully got our own flag!

But if we try this on the ciphertext given to us, it doesn't work...

We note that p and q are primes each with512 bits. Because the result we get is β€Šmodβ€ŠΒ p\bmod\ p (result=mβ€Šmodβ€Špresult = m \bmod p), it is very likely that the original message is > pp, which is > 512 bits.

We can test this by testing with a flag shorter than pp, and then testing with a flag longer than pp.

m>p

And our theory is correct, the original message is most likely > 512 bits...

Plaintext > 512 bits

So how do we get the plaintext if it is more than 512 bits?

Since we can decrypt using pp as a modulus, it means we can also decrypt using qq as a modulus. This means we can end up with 2 different equations with 2 different mods:

resultp=mβ€Šmodβ€Špresultq=mβ€Šmodβ€Šqresultp = m \bmod p \\resultq = m \bmod q

Since pp and qq are coprime, the original message is the same but the modulus are different, we can use Chinese Remainder Theorem (CRT) to solve for the original message.

Chinese Remainder Theorem

We can use the crt function in the sympy python library. We will need to supply a list of mods [p,q] and a list of results [resp,resq]

Last updated

Was this helpful?