Rating:

You can see that `n, e, cf` are exported in `pubkey.json`.

These values are a bit strange:

```rb
def initialize
# ...
@cf = modinv(@q, @p) # (1)
end
def pubkey
privkey.to_a[..2].to_h
end
def privkey
{
e: @e,
n: @d, # (2)
cf: @cf,
# ...
}
end
```

First, you can see that `cf` is `q^{-1} mod p`.

Second, this is important, `n` is NOT `n`, but`d`.

Therefore, the problem is: Given `e, d, cf`, calculate `n`.

First observation, from definition, `ed = 1 + k(p-1)(q-1)`. Comparing the bit length, `k` should be the same bit length as `e`.
This is small enough to bruteforce. From this, we can get `phi(n) = (p-1)(q-1) = (ed-1)/k`.

Second, to use `cf*q = 1 (mod p)` condition, let the above equation be in `mod p`.
That will be `phi(n) = -(q-1) (mod p)`, and from these 2 equations, `1 + cf*phi(n) - cf = 0 (mod p)`.
This means `x := 1 + cf*phi(n) - cf` is a multiple of `p`.

Finally, because we know `phi(n)`, for some integer `m`, `m^{phi(n)} - 1 = 0 (mod n)`.
These can be written as `m^{phi(n)} - 1 = 0 (mod p)`, so `m^{phi(n)} - 1` are also multiples of `p`.

So, we can get `p` by simply gcd of these values.
`m^{phi(n)}` will be huge numbers, so we should calculate as `pow(m, phi(n), x)`.

From `p` and `phi(n)`, we can recover `q` and `n`, and decrypted flag using `n, d`.

Original writeup (https://gist.github.com/n-ari/a2db9af7fd3c172e4fa65b923a66beff).