目录
1.题目
from enc import flag
from Crypto.Util.number import *
m = bytes_to_long(flag)
p = getPrime(512)
q = getPrime(512)
r = getPrime(700)
t = getPrime(800)
tmp = getPrime(10)
e = 65537
n = p*q
print(f"c = {pow(m,e,n)}")
print(f"leak = {p*r+q*t+tmp}")
print(f"r = {r}")
print(f"t = {t}")
'''
c = 30075387915048470863070050827629191303436443913395824732907226821054460652219815718226645166341618100700084925720992983286419204902032573926790086035422540879196867669665497753447829812026327367178333296715527968448124126434045869420695221514125724904849358819864918062875310272203931927234053359553779163755
leak = 52407066630998720273731758848751180003129908965730006096464345923549459617438414126937562326106182853585345246472838907532807236677219886418149723311118855918338387062301958904467478605422673207935942348215552655035846974843053690523359908921489934306458440759517598485452824210146131626776354968352617955744704892906092826257342911994753745093428990023111339468228443855826492957321799005181961
r = 4515378990844403115229704433484433833022655205121974667074150580454343643752811757675793959795169040704894972951300994535151410073321406165168737850534934347874731893617893819762340858239387281147345869706971753
t = 6006929234728180950140499814342609393927042935104177663404375056306287820676434237439964941788093406713118055152122129530273026459457066165368157449726903765601586482566299501860863052052121811433466409338266730837645652741024637475218039773
'''
观察题目,关键点在leak这个方程,是一个关于p和q的函数
2.Coppersmith方法介绍
什么是coppersmith
给定一个多项式
要找到在mod N下,的f(x)=0,在不加限制的条件下,这是个很困难的问题,因为f(x)的系数可能会比较大
所以coppersmith想出一个方法,就是在找到一个g(x),这个g(x)系数比较小,系数小到可以用牛顿迭代法来求x0,使得g(x)在模N等于0时,f(x)也等于0
那么如何找这个g(x)呢
我们可以通过乘以x的次幂和乘以f(x)次幂来求
将一系列的h(x)通过线性组合得到g(x)
利用LLL格基规约算法使得系数变小,把h(x)的系数当成g(x)的基向量,那么规约出来的第一个基向量就是g(x)的系数
多元copper的运用
这里时一份模板,就是说可以套用来求方程的多个解
import itertools
def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()
R = f.base_ring()
N = R.cardinality()
f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)
G = Sequence([], f.parent())
for i in range(m+1):
base = N^(m-i) * f^i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(power, f.variables(), shifts))
G.append(g)
B, monomials = G.coefficient_matrix()
monomials = vector(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)
H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B*monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots
return []
def univariate():
print('Univariate')
bounds = (floor(N^.3),)
roots = tuple(randrange(bound) for bound in bounds)
R = Integers(N)
P.<x> = PolynomialRing(R, 1)
monomials = [x, x^2, x^3]
f = sum(randrange(N)*monomial for monomial in monomials)
f -= f(*roots)
print(small_roots(f, bounds, m=7))
def bivariate():
print('Bivariate')
bounds = (floor(N^.15), floor(N^.15))
roots = tuple(randrange(bound) for bound in bounds)
R = Integers(N)
P.<x, y> = PolynomialRing(R)
monomials = [x, y, x*y, x^2]
f = sum(randrange(N)*monomial for monomial in monomials)
f -= f(*roots)
print(small_roots(f, bounds))
def trivariate():
print('Trivariate')
bounds = (floor(N^.12), floor(N^.12), floor(N^.12))
roots = tuple(randrange(bound) for bound in bounds)
R = Integers(N)
P.<x, y, z> = PolynomialRing(R)
monomials = [x, y, x*y, x*z, y*z]
f = sum(randrange(N)*monomial for monomial in monomials)
f -= f(*roots)
print(small_roots(f, bounds))
def boneh_durfee():
print('Boneh Durfee')
bounds = (floor(N^.255), 2^1024)
d = random_prime(bounds[0])
e = inverse_mod(d, (p-1)*(q-1))
roots = (e*d//((p-1)*(q-1)), (p+q)//2)
R = Integers(e)
P.<k, s> = PolynomialRing(R)
f = 2*k*((N+1)//2 - s) + 1
res = small_roots(f, bounds, m=4, d=4)
if res:
kk,ss = res[0]
#s = p+q/2
var('x,y')
s = solve([(x+y)-2*int(ss),x*y-N],x,y)
return [s[0][0].rhs(),s[0][1].rhs()]
return []
def approximate_factor():
print('Approximate factor')
bounds = (floor(N^.05), floor(N^.05))
roots = tuple(randrange(bound) for bound in bounds)
R = Integers(N)
P = PolynomialRing(R, len(bounds), 'x')
f = sum(randrange(2^128)*x for x in P.gens())
f += p - f(*roots)
print(small_roots(f, bounds, m=2, d=4))
def trivariate_v():
print('Trivariate')
bounds = (floor(N^.12), floor(N^.12), floor(N^.12))
roots = tuple(randrange(bound) for bound in bounds)
print(roots)
R = Integers(N)
P.<x, y, z> = PolynomialRing(R)
monomials = [x, y, x*y, x*z, y*z]
f = sum(randrange(N)*monomial for monomial in monomials)
f -= f(*roots)
print(small_roots(f, bounds))
if __name__ == '__main__':
print('Generating primes')
p = random_prime(2^1024)
q = random_prime(2^1024)
N = p*q
univariate()
bivariate()
trivariate()
boneh_durfee()
较于单元 copper 而言,多元 copper 无非就是从原来的找最前面的那一条短的系数向量,变成了找前面的多条短的系数向量,然后再求解一下方程组,就能把解都求出来了。
EXP
import itertools
def small_roots(f, bounds, m=1, d=None):#多元copper
if not d:
d = f.degree()
R = f.base_ring()
N = R.cardinality()
f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)
G = Sequence([], f.parent())
for i in range(m + 1):
base = N ^ (m - i) * f ^ i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(power, f.variables(), shifts))
G.append(g)
B, monomials = G.coefficient_matrix()
monomials = vector(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)
H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B * monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots
return []
c = 30075387915048470863070050827629191303436443913395824732907226821054460652219815718226645166341618100700084925720992983286419204902032573926790086035422540879196867669665497753447829812026327367178333296715527968448124126434045869420695221514125724904849358819864918062875310272203931927234053359553779163755
leak = 52407066630998720273731758848751180003129908965730006096464345923549459617438414126937562326106182853585345246472838907532807236677219886418149723311118855918338387062301958904467478605422673207935942348215552655035846974843053690523359908921489934306458440759517598485452824210146131626776354968352617955744704892906092826257342911994753745093428990023111339468228443855826492957321799005181961
r = 4515378990844403115229704433484433833022655205121974667074150580454343643752811757675793959795169040704894972951300994535151410073321406165168737850534934347874731893617893819762340858239387281147345869706971753
t = 6006929234728180950140499814342609393927042935104177663404375056306287820676434237439964941788093406713118055152122129530273026459457066165368157449726903765601586482566299501860863052052121811433466409338266730837645652741024637475218039773
R.<p, q> = Zmod(leak)[]
for tmp in range(2 ^ 9, 2 ^ 10):
f = p * r + q * t + tmp
res = small_roots(f, bounds = (2 ^ 512, 2 ^ 512), m = 3, d = 2)
if res:
print(res)
break
导入所需的库函数 itertools。
定义 small_roots 函数,接受三个参数:
f: 需要求解的多元多项式方程
bounds: 整数根的范围
m: 控制项的个数
d: 多项式的最大度数
首先对 f 进行标准化处理,即去除常数项。
构建一组多项式 G,包含 f 的不同次幂和偏移项。具体地:
遍历 m+1 个次数 i,对于每个 i,计算 N^(m-i) * f^i,其中 N 是基域的cardinality。
对于每个基本多项式,再遍历 f 的变量个数 d,生成不同的偏移项。
将这些多项式收集到 G 序列中。
构建系数矩阵 B 和单项式向量 monomials。
对 B 的每一列进行缩放,使得每一列的系数都在给定的整数范围 bounds 内。
对 B 执行 LLL 规约,得到规约后的矩阵 B。
将 B 转换为有理数矩阵,并再次对每一列进行缩放,使得系数都在单位范围内。
从 B * monomials 中提取非零向量,构建理想 I。
如果 I 的维数为 0,则说明找到了方程的整数根,将其全部返回。否则继续搜索。
参考资料: