SRCTF2024-baby-多元Coppersmith

目录

1.题目

2.Coppersmith方法介绍

什么是coppersmith

多元copper的运用

EXP


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

给定一个多项式

f(x)=x^n{}+a_{n-1}x^{n-1}+...a_{1}x^{}+a_{0}

要找到在mod N下,的f(x)=0,在不加限制的条件下,这是个很困难的问题,因为f(x)的系数可能会比较大

所以coppersmith想出一个方法,就是在找到一个g(x),这个g(x)系数比较小,系数小到可以用牛顿迭代法来求x0,使得g(x)在模N等于0时,f(x)也等于0

g(x)=x^n{}+b_{n-1}x^{n-1}+...b_{1}x^{}+b_{0}

g(x)\equiv 0 (mod N)\rightarrow f(x)\equiv 0 (mod N)

那么如何找这个g(x)呢

我们可以通过乘以x的次幂和乘以f(x)次幂来求

h_{ij}(x)=x^{i}(f(x))^{j}

将一系列的h(x)通过线性组合得到g(x)

g(x)=\sum c_{ij}h_{ij}f(x),c_{ij}\varepsilon \mathbb{Z}

利用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,则说明找到了方程的整数根,将其全部返回。否则继续搜索。

参考资料:

密码学攻击之RSA

密码学学习笔记之Coppersmith’s Method

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值