from Crypto.Util.number import * import itertools
""" Setting debug to true will display more informations about the lattice, the bounds, the vectors... """ debug = True
""" Setting strict to true will stop the algorithm (and return (-1, -1)) if we don't have a correct upperbound on the determinant. Note that this doesn't necesseraly mean that no solutions will be found since the theoretical upperbound is usualy far away from actual results. That is why you should probably use `strict = False` """ strict = False
""" This is experimental, but has provided remarkable results so far. It tries to reduce the lattice as much as it can while keeping its efficiency. I see no reason not to use this option, but if things don't work, you should try disabling it """ helpful_only = True dimension_min = 7
def helpful_vectors(BB, modulus): nothelpful = 0 for ii in range(BB.dimensions()[0]): if BB[ii,ii] >= modulus: nothelpful += 1
print (nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")
def matrix_overview(BB, bound): for ii in range(BB.dimensions()[0]): a = ('%02d ' % ii) for jj in range(BB.dimensions()[1]): a += '0' if BB[ii,jj] == 0 else 'X' if BB.dimensions()[0] < 60: a += ' ' if BB[ii, ii] >= bound: a += '~' print (a)
def remove_unhelpful(BB, monomials, bound, current): if current == -1 or BB.dimensions()[0] <= dimension_min: return BB
for ii in range(current, -1, -1): if BB[ii, ii] >= bound: affected_vectors = 0 affected_vector_index = 0 for jj in range(ii + 1, BB.dimensions()[0]): if BB[jj, ii] != 0: affected_vectors += 1 affected_vector_index = jj
if affected_vectors == 0: print ("* removing unhelpful vector", ii) BB = BB.delete_columns([ii]) BB = BB.delete_rows([ii]) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii-1) return BB
elif affected_vectors == 1: affected_deeper = True for kk in range(affected_vector_index + 1, BB.dimensions()[0]): if BB[kk, affected_vector_index] != 0: affected_deeper = False if affected_deeper and abs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]): print ("* removing unhelpful vectors", ii, "and", affected_vector_index) BB = BB.delete_columns([affected_vector_index, ii]) BB = BB.delete_rows([affected_vector_index, ii]) monomials.pop(affected_vector_index) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii-1) return BB return BB
""" Returns: * 0,0 if it fails * -1,-1 if `strict=true`, and determinant doesn't bound * x0,y0 the solutions of `pol` """ def boneh_durfee(pol, modulus, mm, tt, XX, YY): """ Boneh and Durfee revisited by Herrmann and May finds a solution if: * d < N^delta * |x| < e^delta * |y| < e^0.5 whenever delta < 1 - sqrt(2)/2 ~ 0.292 """
PR.<u, x, y> = PolynomialRing(ZZ) Q = PR.quotient(x*y + 1 - u) polZ = Q(pol).lift()
UU = XX*YY + 1
gg = [] for kk in range(mm + 1): for ii in range(mm - kk + 1): xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk gg.append(xshift) gg.sort()
monomials = [] for polynomial in gg: for monomial in polynomial.monomials(): if monomial not in monomials: monomials.append(monomial) monomials.sort() for jj in range(1, tt + 1): for kk in range(floor(mm/tt) * jj, mm + 1): yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk) yshift = Q(yshift).lift() gg.append(yshift) for jj in range(1, tt + 1): for kk in range(floor(mm/tt) * jj, mm + 1): monomials.append(u^kk * y^jj)
nn = len(monomials) BB = Matrix(ZZ, nn) for ii in range(nn): BB[ii, 0] = gg[ii](0, 0, 0) for jj in range(1, ii + 1): if monomials[jj] in gg[ii].monomials(): BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)
if helpful_only: BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1) nn = BB.dimensions()[0] if nn == 0: print ("failure") return 0,0
if debug: helpful_vectors(BB, modulus^mm) det = BB.det() bound = modulus^(mm*nn) if det >= bound: print ("We do not have det < bound. Solutions might not be found.") print ("Try with highers m and t.") if debug: diff = (log(det) - log(bound)) / log(2) print ("size det(L) - size e^(m*n) = ", floor(diff)) if strict: return -1, -1 else: print ("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")
if debug: matrix_overview(BB, modulus^mm)
if debug: print ("optimizing basis of the lattice via LLL, this can take a long time")
BB = BB.LLL()
if debug: print ("LLL is done!")
if debug: print ("looking for independent vectors in the lattice") found_polynomials = False for pol1_idx in range(nn - 1): for pol2_idx in range(pol1_idx + 1, nn): PR.<w,z> = PolynomialRing(ZZ) pol1 = pol2 = 0 for jj in range(nn): pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY) pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)
PR.<q> = PolynomialRing(ZZ) rr = pol1.resultant(pol2)
if rr.is_zero() or rr.monomials() == [1]: continue else: print ("found them, using vectors", pol1_idx, "and", pol2_idx) found_polynomials = True break if found_polynomials: break
if not found_polynomials: print ("no independant vectors could be found. This should very rarely happen...") return 0, 0 rr = rr(q, q)
soly = rr.roots()
if len(soly) == 0: print ("Your prediction (delta) is too small") return 0, 0
soly = soly[0][0] ss = pol1(q, soly) solx = ss.roots()[0][0]
return solx, soly
nbit = 1024 n = 123789043095302886784777548580725867919630872720308267296330863659260260632444171595208750648710642616709290340791408935502415290984231140635423328808872594955139658822363033096014857287439409252367248420356169878044065798634016290690979979625051287064109800759113475629317869327100941592970373827299442569489 e = 112070481298571389221611833986644006256566240788306316765530852688390558290807060037831460397016038678699757261874520899143918664293504728402666398893964929840011110057060969775245481057773655679041350091817099143204028098431544760662690479779286160425059494739419234859710815966582837874194763305328789592245 c = 63662561509209168743977531923281040338804656992093161358503738280395090747786427812762995865224617853709000826994250614233562094619845247321880231488631212423212167167713869682181551433686816142488666533035193128298379649809096863305651271646535125466745409868274019550361728139482502448613835444108383177119 hint1 = 897446442156802074692 hint2 = 1069442646630079275131 ph = int(hint1<<442) qh = int(hint2<<442)
if(1): m = 8 delta = 0.30 t = int((1-2*delta) * m) X = 2*floor(n^delta) Y = 2^442
A = (n+1)//2 - (ph+qh)//2 PR.<x,y> = PolynomialRing(ZZ) pol = 1+x*(A+y)
k2,pl_ql = 1476216873354030897123807900312494643299166545025397520836636477078919248983223765331825148, -5248359230286975998007421528138879480670687275342665043065128666287838030017492213260091350908248072270884523636416440683845503716271 d = (1+k2*(A+pl_ql)) // e print(long_to_bytes(int(pow(c,d,n))))
|