That one RSA challenge - TCP1P CTF 2024 International
Challenge:
from Crypto.Util.number import *
from secret import flag
z = 567
p = getPrime(1024)
q = getPrime(1024)
n = p*q
c = pow(bytes_to_long(flag), 65537, n)
tot = (p-1) * (q-1)
d = int(pow(65537, -1, tot))
dinv = int(pow(d, -1, n))
h = int(dinv >> z)
hpq = (int((p+q)>> z))
with open('out.txt', 'w+') as f:
f.write(f'{n=}\n')
f.write(f'{h=}\n')
f.write(f'{hpq=}\n')
f.write(f'{c=}\n')
n=13986357905153484822874300783445968480194277882812317554826224241536479785567487956712558237728345348661360577246137576216953724039680969623887884690471844396542763308129517234365819619617071449273126659007918716307793788623728052337632935762139796688014791419718949572448772521789488223910450877828732015095423443037519388747356327730350934152781671783952028215703864406564741666179193772037496984854699143314813242721157017296866888522135989818414587193505121794302821401677072507471357592358012342178011963104524959087968374300060349343826214249928530346877968114749229074874962737714935221065368318487049394644831
h=10474216468878927114435400909130676124750910912012236182806861194655854223324539867768381265996955193355030239325750528328250897464859373863289680002879536341349759323910048168674147097644573874679268018966497862685092382336865554114348153248267599439087357199554652601126191061921516650448119261614064051599968120061991607030873881013657693987836636730528537557619595799676312850875727477092697270452300532360780188724484703363561848754770976459
hpq=492124417091708682668644108145880307537308922842816506360717440112116492381514432506339907757228214359689270777951081610062506962769167209
c=4715651972688371479449666526727240348158670108161494767004202259402013317642418593561463200947908841531208327599049414587586292570298317049448560403558027904798159589477994992384199008976859139072407664659830448866472863679123027179516506312536814186903687358198847465706108667279355674105689763404474207340186200156662095468249081142604074167178023479657021133754055107459927667597604156397468414872149353231061997958301747136265344906296373544580143870450924707559398134384774201700278038470171319329716930036843839101955981274793386611943442507144153946307781795085665793554799349509983282980591388585613674226899
Solve:
The 2 hints given to us are the msb of dinv and the msb of p+q.
First I bit-shift them back.
Then denote the 2 unknowns lsbs x and y, so:
\[\text{dinv} = \text{h} + \text{x}\] \[\text{p+q} = \text{hpq} + \text{y}\]Initial equation:
\[\text{d} \cdot \text{dinv} - 1 \equiv 0 \text{ (mod n)}\]An equation for d (some unknown k where 0<k<e):
\[\text{d} = \frac{1 + \text{k} \cdot \text{phi}}{\text{e}}\]Equation for phi:
\[\text{phi} = \text{n} + 1 - (\text{p}+\text{q}) = \text{n} + 1 - (\text{hpq} + \text{y})\]Sub phi into d equation:
\[\text{d} = \frac{1 + \text{k} \cdot (\text{n} + 1 - (\text{hpq} + \text{y}))}{\text{e}}\]Now sub d and dinv into initial equation:
\[\big(\frac{1 + \text{k} \cdot (\text{n} + 1 - (\text{hpq} + \text{y}))}{\text{e}} \big) \cdot (\text{h} + \text{x}) - 1 \equiv 0 \text{ (mod n)}\] \[\big(1 + \text{k} \cdot (\text{n} + 1 - (\text{hpq} + \text{y})) \big) \cdot (\text{h} + \text{x}) - \text{e} \equiv 0 \text{ (mod n)}\]Now x and y are solvable with bivariate coppersmith, although you have to bruteforce k…
import itertools
from tqdm import trange
def bivariate(f, bounds, m=1, d=None):
if d is None:
d = f.degree()
R = f.base_ring()
N = R.cardinality()
f_ = (f // f.lc()).change_ring(ZZ)
f = f.change_ring(ZZ)
l = f.lm()
M = []
for k in range(m+1):
M_k = set()
T = set((f^(m-k)).monomials())
for mon in (f^m).monomials():
if mon//l^k in T:
for extra in itertools.product(range(d), repeat=f.nvariables()):
g = mon * prod(map(power, f.variables(), extra))
M_k.add(g)
M.append(M_k)
M.append(set())
shifts = Sequence([], f.parent())
for k in range(m+1):
for mon in M[k] - M[k+1]:
g = mon//l^k * f_^k * N^(m-k)
shifts.append(g)
B, monomials = shifts.coefficients_monomials()
factors = [monomial(*bounds) for monomial in monomials]
for i, factor in enumerate(factors):
B.rescale_col(i, factor)
B = B.dense_matrix().LLL()
B = B.change_ring(QQ)
for i, factor in enumerate(factors):
B.rescale_col(i, 1/factor)
B = B.change_ring(ZZ)
H = [h for h in B * monomials if not h.is_zero()]
H = H[:2] # speedup
for f1, f2 in itertools.combinations(H, r=2):
x, y = f.parent().gens()
x = f1.parent()(x)
y = f1.parent()(y)
res = f1.resultant(f2,y).univariate_polynomial()
if res == 0:
continue
rs = res.roots()
if rs:
x = rs[0][0]
if x != 0:
y = f1.subs(x=x).univariate_polynomial().roots()
if y:
y = y[0][0]
return (x, y)
n=13986357905153484822874300783445968480194277882812317554826224241536479785567487956712558237728345348661360577246137576216953724039680969623887884690471844396542763308129517234365819619617071449273126659007918716307793788623728052337632935762139796688014791419718949572448772521789488223910450877828732015095423443037519388747356327730350934152781671783952028215703864406564741666179193772037496984854699143314813242721157017296866888522135989818414587193505121794302821401677072507471357592358012342178011963104524959087968374300060349343826214249928530346877968114749229074874962737714935221065368318487049394644831
h=10474216468878927114435400909130676124750910912012236182806861194655854223324539867768381265996955193355030239325750528328250897464859373863289680002879536341349759323910048168674147097644573874679268018966497862685092382336865554114348153248267599439087357199554652601126191061921516650448119261614064051599968120061991607030873881013657693987836636730528537557619595799676312850875727477092697270452300532360780188724484703363561848754770976459
hpq=492124417091708682668644108145880307537308922842816506360717440112116492381514432506339907757228214359689270777951081610062506962769167209
c=4715651972688371479449666526727240348158670108161494767004202259402013317642418593561463200947908841531208327599049414587586292570298317049448560403558027904798159589477994992384199008976859139072407664659830448866472863679123027179516506312536814186903687358198847465706108667279355674105689763404474207340186200156662095468249081142604074167178023479657021133754055107459927667597604156397468414872149353231061997958301747136265344906296373544580143870450924707559398134384774201700278038470171319329716930036843839101955981274793386611943442507144153946307781795085665793554799349509983282980591388585613674226899
e = 65537
z = 567
h <<= z
hpq <<= z
PR.<x, y> = PolynomialRing(Zmod(n))
#for k in trange(e):
for k in trange(8500, e): # skip
f = (1 + k * (n + 1 - (hpq + y))) * (h + x) - e
roots = bivariate(f, bounds=(2**z, 2**z), m=2, d=1)
if roots is None:
continue
d = pow(h + roots[0], -1, n)
flag = int(pow(c, d, n))
break
print(f'{k = }')
print(bytes.fromhex(f'{flag:x}'))
ETA: trivariate is possible:
load('https://raw.githubusercontent.com/Connor-McCartney/coppersmith/refs/heads/main/coppersmith.sage')
n=13986357905153484822874300783445968480194277882812317554826224241536479785567487956712558237728345348661360577246137576216953724039680969623887884690471844396542763308129517234365819619617071449273126659007918716307793788623728052337632935762139796688014791419718949572448772521789488223910450877828732015095423443037519388747356327730350934152781671783952028215703864406564741666179193772037496984854699143314813242721157017296866888522135989818414587193505121794302821401677072507471357592358012342178011963104524959087968374300060349343826214249928530346877968114749229074874962737714935221065368318487049394644831
h=10474216468878927114435400909130676124750910912012236182806861194655854223324539867768381265996955193355030239325750528328250897464859373863289680002879536341349759323910048168674147097644573874679268018966497862685092382336865554114348153248267599439087357199554652601126191061921516650448119261614064051599968120061991607030873881013657693987836636730528537557619595799676312850875727477092697270452300532360780188724484703363561848754770976459
hpq=492124417091708682668644108145880307537308922842816506360717440112116492381514432506339907757228214359689270777951081610062506962769167209
c=4715651972688371479449666526727240348158670108161494767004202259402013317642418593561463200947908841531208327599049414587586292570298317049448560403558027904798159589477994992384199008976859139072407664659830448866472863679123027179516506312536814186903687358198847465706108667279355674105689763404474207340186200156662095468249081142604074167178023479657021133754055107459927667597604156397468414872149353231061997958301747136265344906296373544580143870450924707559398134384774201700278038470171319329716930036843839101955981274793386611943442507144153946307781795085665793554799349509983282980591388585613674226899
e = 65537
z = 567
h <<= z
hpq <<= z
PR.<k, x, y> = PolynomialRing(Zmod(n))
f = (1 + k * (n + 1 - (hpq + y))) * (h + x) - e
k, x, y = multivariate(f, (e, 2**z, 2**z), "shift_polynomials", "jacobian", m=2, d=1)[0]
d = pow(h+x, -1, n)
flag = int(pow(c, d, n))
print(bytes.fromhex(f'{flag:x}'))