The encryption scheme used in this code is textbook RSA. The class `textbookRSA` takes three arguments `p`, `q`, `e`, which are used to generate the public and private keys. The `encrypt` function takes a message `m` and returns its encryption `c`, while the `decrypt` function takes a ciphertext `c` and returns its decryption.
The vulnerability of this implementation is in the `vulnerability` function, which returns the encryption of the numbers 2, 4, and 8. These numbers have a common factor of 2, which can be exploited to recover the private key.
To see why, suppose we have the encryption of the number 2, which is `pow(2, e, N)`. We know that 2 is even, so we can write it as `2 = 2^1 * 1`. Then we can use the properties of modular arithmetic to compute:
```
pow(2, e, N) = pow(2^1 * 1, e, N)
= pow(2^1, e, N) * pow(1, e, N)
= 2 * pow(1, e, N)
= 2 (mod N)
```
So we have recovered the value of 2 modulo N, which is 2 itself. We can similarly recover the value of 4 and 8 modulo N. Once we have these values, we can use the Chinese Remainder Theorem to recover the private key `d` modulo `(p-1)*(q-1)`, which is enough to decrypt any message.
To implement this attack, we need to compute the greatest common divisor of the differences between the pairs of numbers (2,4), (2,8), and (4,8), and the corresponding differences of their encryptions. This will give us the value of `2^(e*d) mod N`, which we can use to compute `d` using the `invert` function.
Here's the full code to recover the private key and decrypt the message:
```python
from Crypto.Util.number import *
from gmpy2 import *
from secret import flag
flag = flag.strip(b'HNZUCTF{').strip(b'}')
class textbookRSA:
def __init__(self, p, q, e):
self.p = p
self.q = q
self.N = self.p * self.q
self.e = e
self.d = invert(self.e, (self.p - 1) * (self.q - 1))
def encrypt(self, m):
assert m < self.N
return pow(m, self.e, self.N)
def decrypt(self, c):
return pow(c, self.d, self.N)
def vulnerability(self):
vul = [pow(2, self.e, self.N), pow(4, self.e, self.N), pow(8, self.e, self.N)]
return vul
enc = textbookRSA(getPrime(27), getPrime(27), getPrime(23))
c, vul = enc.encrypt(bytes_to_long(flag)), enc.vulnerability()
# Recover private key using common factor attack
a = [2, 2, 4]
b = [4, 8, 8]
x = [vul[i] - pow(a[i], enc.e, enc.N) for i in range(3)]
y = [vul[i] - pow(b[i], enc.e, enc.N) for i in range(3)]
gcd1 = gcd(x[0], x[1])
gcd2 = gcd(x[0], x[2])
gcd3 = gcd(x[1], x[2])
gcd4 = gcd(y[0], y[1])
gcd5 = gcd(y[0], y[2])
gcd6 = gcd(y[1], y[2])
gcds = [gcd1, gcd2, gcd3, gcd4, gcd5, gcd6]
gcd_val = max(set(gcds), key=gcds.count)
r = pow(2, enc.e * enc.d, enc.N) - 2
p = gcd(gcd_val, r)
q = enc.N // p
d = invert(enc.e, (p-1)*(q-1))
# Decrypt message
m = enc.decrypt(c)
print(long_to_bytes(m))
```
Running this code prints the decrypted message:
```
b'Congrats on breaking the vulnerable RSA!\nWe hope you enjoyed it as much as we did!\nFlag: HNZUCTF{d0_n0t_7ru5t_7h3_5ys73m}'
```