c = 2420624631315473673388732074340410215657378096737020976722603529598864338532404224879219059105950005655100728361198499550862405660043591919681568611707967 N = 1476751427633071977599571983301151063258376731102955975364111147037204614220376883752032253407881568290520059515340434632858734689439268479399482315506043425541162646523388437842149125178447800616137044219916586942207838674001004007237861470176454543718752182312318068466051713087927370670177514666860822341380494154077020472814706123209865769048722380888175401791873273850281384147394075054950169002165357490796510950852631287689747360436384163758289159710264469722036320819123313773301072777844457895388797742631541101152819089150281489897683508400098693808473542212963868834485233858128220055727804326451310080791 e1 = 425735006018518321920113858371691046233291394270779139216531379266829453665704656868245884309574741300746121946724344532456337490492263690989727904837374279175606623404025598533405400677329916633307585813849635071097268989906426771864410852556381279117588496262787146588414873723983855041415476840445850171457530977221981125006107741100779529209163446405585696682186452013669643507275620439492021019544922913941472624874102604249376990616323884331293660116156782891935217575308895791623826306100692059131945495084654854521834016181452508329430102813663713333608459898915361745215871305547069325129687311358338082029 e2 = 1004512650658647383814190582513307789549094672255033373245432814519573537648997991452158231923692387604945039180687417026069655569594454408690445879849410118502279459189421806132654131287284719070037134752526923855821229397612868419416851456578505341237256609343187666849045678291935806441844686439591365338539029504178066823886051731466788474438373839803448380498800384597878814991008672054436093542513518012957106825842251155935855375353004898840663429274565622024673235081082222394015174831078190299524112112571718817712276118850981261489528540025810396786605197437842655180663611669918785635193552649262904644919 a = (e2-e1)*(e1*e2).inverse_mod(N) % N X = 2^1000
R = Zmod(N) P.<x> = PolynomialRing(R, 1); f = x-a bounds = [X] xs = small_roots(f, bounds, m=8)
assertlen(xs) > 0 x0 = xs[0][0] p6 = gcd(f(x=x0), N) p = iroot(int(p6), 6) assert p[1] == True p = p[0] q = N // p^7
# sage # stolen from 2022 TQLCTF wp defmatrix_overview(BB): for ii inrange(BB.dimensions()[0]): a = ('%02d ' % ii) for jj inrange(BB.dimensions()[1]): if BB[ii, jj] == 0: a += ' ' else: a += 'X' if BB.dimensions()[0] < 60: a += ' ' print(a)
# input p = 2^30 + 3 q = 2^32 + 15 N = p*q a0, a1, a2, a3 = (1942528644709637042, 1234567890123456789, 987654321987654321, 1) P.<x> = PolynomialRing(ZZ) f = a0 + a1*x + a2*x^2 + a3*x^3 X = 2^14
d = f.degree() # choose yourself, bigger better and slower t = 3
# coustruct gi g_ij = [] for i inrange(t+1): for j inrange(d): g_ij.append( N^(t-i) * f^i * x^j ) g_ij = sorted(g_ij) #print(g_ij)
monomials = [] for g in g_ij: monomials += g.monomials() monomials = sorted(set(monomials)) print('\nmonomials:') print(monomials)
# construct matrix w = d * (t+1) assert w == len(monomials) M = matrix(ZZ, w, w) for i inrange(w): for j inrange(w): if monomials[j] in g_ij[i].monomials(): M[i, j] = g_ij[i](x=x*X).monomial_coefficient(monomials[j]) print('\nConstructed matrix:') matrix_overview(M)
# reduce coeffcients L = M.LLL() h = sum([(L[0][i]/monomials[i](x=X)) * monomials[i] for i inrange(w)]) print('\nReduced poly:') print(h) print('\nRoots of h:') print(h.roots()) # simple check res = [] for r in h.roots(): xx = r[0] if h(x=xx) < N^t: res.append(xx)
# sage # from 2022 tql wp defmatrix_overview(BB): for ii inrange(BB.dimensions()[0]): a = ('%02d ' % ii) for jj inrange(BB.dimensions()[1]): if BB[ii, jj] == 0: a += ' ' else: a += 'X' if BB.dimensions()[0] < 60: a += ' ' print(a)
# https://eprint.iacr.org/2014/343.pdf (3.1) defYaoLu(f, N, m, t, X): P = f.parent() dim = m+1 # as g_k(x) in YaoLu's paper gks = [] for k inrange(dim): gks.append( N^max(ceil(v*(t-k)/u), 0) * f^k ) gks = sorted(gks)
# order is not important # but monomials would be convenient (: monomials = [] for gk in gks: monomials += gk.monomials() monomials = sorted(set(monomials)) print(monomials)
assert dim == len(monomials) M = matrix(QQ, dim, dim) for i inrange(dim): for j inrange(dim): if monomials[j] in gks[i].monomials(): M[i, j] = gks[i](x=x*X).monomial_coefficient(monomials[j]) # just debug matrix_overview(M)
# LLL reduce coefficients L = M.LLL() h = sum([(L[0][i]/monomials[i](x=X)) * monomials[i] for i inrange(dim)])
# simple check res = [] for r in h.roots(): xx = r[0] if h(x=xx) < N^t: # p^{vt} < N^t, loose res.append(xx) return res
if __name__ == '__main__': from gmpy2 import iroot import libnum
# known c = 2420624631315473673388732074340410215657378096737020976722603529598864338532404224879219059105950005655100728361198499550862405660043591919681568611707967 N = 1476751427633071977599571983301151063258376731102955975364111147037204614220376883752032253407881568290520059515340434632858734689439268479399482315506043425541162646523388437842149125178447800616137044219916586942207838674001004007237861470176454543718752182312318068466051713087927370670177514666860822341380494154077020472814706123209865769048722380888175401791873273850281384147394075054950169002165357490796510950852631287689747360436384163758289159710264469722036320819123313773301072777844457895388797742631541101152819089150281489897683508400098693808473542212963868834485233858128220055727804326451310080791 e1 = 425735006018518321920113858371691046233291394270779139216531379266829453665704656868245884309574741300746121946724344532456337490492263690989727904837374279175606623404025598533405400677329916633307585813849635071097268989906426771864410852556381279117588496262787146588414873723983855041415476840445850171457530977221981125006107741100779529209163446405585696682186452013669643507275620439492021019544922913941472624874102604249376990616323884331293660116156782891935217575308895791623826306100692059131945495084654854521834016181452508329430102813663713333608459898915361745215871305547069325129687311358338082029 e2 = 1004512650658647383814190582513307789549094672255033373245432814519573537648997991452158231923692387604945039180687417026069655569594454408690445879849410118502279459189421806132654131287284719070037134752526923855821229397612868419416851456578505341237256609343187666849045678291935806441844686439591365338539029504178066823886051731466788474438373839803448380498800384597878814991008672054436093542513518012957106825842251155935855375353004898840663429274565622024673235081082222394015174831078190299524112112571718817712276118850981261489528540025810396786605197437842655180663611669918785635193552649262904644919 a = (e1-e2)*(e1*e2).inverse_mod(N) % N
P.<x> = PolynomialRing(ZZ) f = x-a X = 2^1000 u = 7 v = u-1
# YaoLu's params m = 8 t = 6
# main xs = YaoLu(f, N, m, t, X) assertlen(xs) > 0 x0 = xs[0]
# sage # # https://eprint.iacr.org/2014/343.pdf (3.1) defYaoLu(f, N, m, t, X): P = f.parent() dim = m+1 # as g_k(x) in YaoLu's paper gks = [] for k inrange(dim): gks.append( N^max(ceil(v*(t-k)/u), 0) * f^k ) gks = sorted(gks)
# order is not important # but monomials would be convenient (: monomials = [] for gk in gks: monomials += gk.monomials() monomials = sorted(set(monomials))
assert dim == len(monomials) M = matrix(QQ, dim, dim) for i inrange(dim): for j inrange(dim): if monomials[j] in gks[i].monomials(): M[i, j] = gks[i](x=x*X).monomial_coefficient(monomials[j]) # just debug #matrix_overview(M)
# LLL reduce coefficients L = M.LLL() h = sum([(L[0][i]/monomials[i](x=X)) * monomials[i] for i inrange(dim)])
# simple check res = [] for r in h.roots(): xx = r[0] if h(x=xx) < N^t: # p^{vt} < N^t, loose res.append(xx) return res
# HG97 print('HG97:') try: P.<x> = PolynomialRing(ZZ) a = (e1-e2)*(e1*e2).inverse_mod(N) % N f = x-a X = 2^size
d = f.degree() # choose yourself, bigger better and slower t = 21
# coustruct gi g_ij = [] for i inrange(t+1): for j inrange(d): g_ij.append( N^(t-i) * f^i * x^j ) g_ij = sorted(g_ij) #print(g_ij)
monomials = [] for g in g_ij: monomials += g.monomials() monomials = sorted(set(monomials))
# construct matrix w = d * (t+1) assert w == len(monomials) M = matrix(ZZ, w, w) for i inrange(w): for j inrange(w): if monomials[j] in g_ij[i].monomials(): M[i, j] = g_ij[i](x=x*X).monomial_coefficient(monomials[j])
# reduce coeffcients L = M.LLL() h = sum([(L[0][i]/monomials[i](x=X)) * monomials[i] for i inrange(w)]) r # simple check res = [] for r in h.roots(): xx = r[0] if h(x=xx) < N^t: res.append(xx)
# sage defmatrix_overview(BB): for ii inrange(BB.dimensions()[0]): a = ('%02d ' % ii) for jj inrange(BB.dimensions()[1]): if BB[ii, jj] == 0: a += ' ' else: a += 'X' if BB.dimensions()[0] < 60: a += ' ' print(a)
deflattice_attack(PR, pol, e, mm, tt, X, Y): x, y = PR.gens() polys = []
for ii inrange(mm + 1): for jj inrange(0, mm - ii + 1): poly = e ^ (mm - ii) * x ^ jj * pol ^ ii polys.append(poly)
for ii inrange(mm + 1): for jj inrange(1, tt + 1): poly = e ^ (mm - ii) * y ^ jj * pol ^ ii polys.append(poly)
polys = sorted(polys) monomials = [] for poly in polys: monomials += poly.monomials() monomials = sorted(set(monomials)) dims1 = len(polys) dims2 = len(monomials) M = matrix(QQ, dims1, dims2)
for ii inrange(dims1): M[ii, 0] = polys[ii](0, 0) for jj inrange(dims2): if monomials[jj] in polys[ii].monomials(): M[ii, jj] = polys[ii](x * X, y * Y).monomial_coefficient(monomials[jj]) matrix_overview(M) print('=' * 128) print(len(M.rows()), len(M.columns())) #M = M.hermite_form() B = M.LLL() print('LLL done')
det = B.det() print(f"monomials: {monomials}") nn = len(monomials) matrix_overview(B) H = [(i, 0) for i inrange(dims1)] H = dict(H) for j inrange(dims2): for i inrange(dims1): H[i] += (monomials[j] * B[i, j]) / monomials[j](X, Y) PQ.<q> = PolynomialRing(ZZ) H = list(H.values()) solutions = [] print(len(H)) for i inrange(len(H)): for j inrange(i + 1, len(H)): pol1 = PR(H[i]) pol2 = PR(H[j]) rr = pol1.resultant(pol2, y) if rr.is_zero() or rr.monomials() == [1]: continue sols = rr(q, q).roots() for sol in sols: solx = sol[0] if solx == -1: continue try: soly = pol1(solx, q).roots()[0][0] solutions.append((solx, soly)) print('=' * 128) except: pass iflen(solutions) > 0: break iflen(solutions) > 0: break iflen(solutions) > 0: break return solutions
[BD00] Boneh D, Durfee G. Cryptanalysis of RSA with private key d less than N/sup 0.292[J]. IEEE transactions on Information Theory, 2000, 46(4): 1339-1349.
[Cop96a] D. Coppersmith, Finding a Small Root of a Univariate Modular Equation, proceedings of Eurocrypt ’96, vol. 1070, Lecture Notes in Computer Science.
[Cop96b] D. Coppersmith, Finding a Small Root of a Bivariate Integer Equation; Factoring with High Bits Known, proceedings of Eurocrypt’ 96, vol. 1070, Lecture Notes in Computer Science.
[Cor04] Coron J S. Finding small roots of bivariate integer polynomial equations revisited[C]//International Conference on the Theory and Applications of Cryptographic Techniques. Springer, Berlin, Heidelberg, 2004: 492-505.
[Cor07] Coron J S. Finding small roots of bivariate integer polynomial equations: A direct approach[C]//Annual International Cryptology Conference. Springer, Berlin, Heidelberg, 2007: 379-394.
[Gal12] Galbraith, Steven D. Mathematics of public key cryptography. Cambridge University Press, 2012.
[HG97] N. A. Howgrave-Graham, Finding small roots of univariate modular equations revisited. In Cryptography and Coding, volume 1355 of LNCS, pp. 131-142. Springer Verlag, 1997.
[HPS14] Hoffstein, Jeffrey, et al. An introduction to mathematical cryptography. Vol. 1. New York: springer, 2008.
[Jou09] Joux, Antoine. Algorithmic cryptanalysis. Chapman and Hall/CRC, 2009.
[LZP15] Lu Y, Zhang R, Peng L, et al. Solving linear equations modulo unknown divisors: revisited[C]//International Conference on the Theory and Application of Cryptology and Information Security. Springer, Berlin, Heidelberg, 2015: 189-213.
[NR15] Nitaj A, Rachidi T. New Attacks on RSA with Moduli N= p r q[C]//International Conference on Codes, Cryptology, and Information Security. Springer, Cham, 2015: 352-360.