RSA中coppersmith定理的应用条件


k e y w o r d s : keywords: keywords: coppersmithRSAsmall_roots()

I n t r o d u c t i o n Introduction Introduction

一般来说,coppersmithRSA中应用最多的就是,高位攻击一类的已知部分二进制位,通过coppersmith定理(里面应用了LLL算法)求得剩余的二进制位

大家有没有想过,究竟在已知多少二进制位数的情况下,coppersmith定理能够生效?

D e d u c t i o n Deduction Deduction

(本文提到的coppersmith定理均只针对单变量)

先来看看coppersmith定理本身的应用场景是:

现有一个 e e e阶的多项式 f f f,那么可以:

  • 给定 β \beta β,快速求出模某个 n n n的因数 b b b意义下较小的根,其中 b ≥ n β b\geq n^{\beta} bnβ,( 0 < β ≤ 1 0< \beta \leq 1 0<β1
  • 在模 n n n意义下,快速求出 n β 2 e n^{\frac{\beta^2}{e}} neβ2以内的根

而前面提到的高位攻击显然是应用了第二条性质,此时构造的多项式 f = p h i g h + p u n k n o w n f=p_{high}+p_{unknown} f=phigh+punknown,阶数 e = 1 e=1 e=1

应用coppersmith定理求解 p u n k n o w n p_{unknown} punknown,前提条件是 p u n k n o w n ≤ n β 2 e p_{unknown}\leq n^{\frac{\beta^2}{e}} punknownneβ2

那么 n β 2 e n^{\frac{\beta^2}{e}} neβ2究竟有多大呢?在高位攻击的情况下 e = 1 e=1 e=1,所以转换为是求 n β 2 n^{\beta^2} nβ2

β \beta β的定义是

n n n的某个因数 b b b使得 b ≥ n β b\geq n^{\beta} bnβ,( 0 < β ≤ 1 0< \beta \leq 1 0<β1

当然,在RSA n n n的因数也就是组成 n n n的大素数,一般来说, n = p ⋅ q n=p\cdot q n=pq,其中 p , q p,q p,q均为大素数

python中的实现代码(这里以 1024 b i t s 1024bits 1024bits为例)

from Crypto.Util.number import getPrime
p = getPrime(1024)
q = getPrime(1024)
n = p * q

那么就意味着 n n n的大小为 2048 b i t s 2048bits 2048bits或者 2047 b i t s 2047bits 2047bits(大小涉及到乘法对二进制位数的关系,这里就不过多讲述了)

很显然 n 0.5 n^{0.5} n0.5就是 1024 b i t s 1024bits 1024bits左右的数,与 p , q p,q p,q很接近,但是仍有可能不满足 p , q ≥ n 0.5 p,q\geq n^{0.5} p,qn0.5

检验一下

import gmpy2
assert gmpy2.iroot(n,2)[0] <= p
assert gmpy2.iroot(n,2)[0] <= q
'''
>>> assert gmpy2.iroot(n,2)[0] <= p
>>> assert gmpy2.iroot(n,2)[0] <= q
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
'''

此时不满足条件

回到问题的开头,我们想要知道的是在已知多少 p p p或者 q q q的二进制位数下,我们能够通过coppersmith定理求得剩下的二进制位(所需构造的多项式前面已经构造好了)

所以我们可以来看看如果此时满足条件,那么我们至少需要已知多少二进制位数(这里假设求 p p p

计算此时 n β 2 n^{\beta ^2} nβ2 e = 1 e=1 e=1,所以就忽略不代入运算了)的二进制位数,我们未知的二进制位数需要小于 n β 2 n^{\beta ^2} nβ2的二进制位数

gmpy2.iroot(n,2**2)[0].bit_length()
'''
>>> gmpy2.iroot(n,2**2)[0].bit_length()
512
'''

这里我们看到如果 p , q ≥ n β p,q\geq n^{\beta} p,qnβ,那么我们未知 p p p或者 q q q(任意一个均可)的二进制位数必须小于 512 512 512

以上都是假设的,我们来看看真实地满足 p , q ≥ n β p,q\geq n^{\beta} p,qnβ情况下,我们未知的二进制位数必须小于多少?

既然 n 0.5 n^{0.5} n0.5(即是 β = 0.5 \beta=0.5 β=0.5情况下)不满足条件,那么与之最接近的 β = 0.49 \beta=0.49 β=0.49的情况(也就是 β < 0.5 \beta < 0.5 β<0.5时)一定能满足条件(这里又涉及到指数运算对二进制位数的影响,也不过多讲述了,可以自己计算试一试)

还是刚才那套检验过程以及计算 n β 2 n^{\beta ^2} nβ2的过程

assert gmpy2.iroot(n**49,100)[0] <= p
assert gmpy2.iroot(n**49,100)[0] <= q
gmpy2.iroot(n**(49**2),100**2)[0].bit_length()
gmpy2.iroot(n**(48**2),100**2)[0].bit_length()
gmpy2.iroot(n**(47**2),100**2)[0].bit_length()
'''
>>> assert gmpy2.iroot(n**49,100)[0] <= p
>>> assert gmpy2.iroot(n**49,100)[0] <= q
>>> gmpy2.iroot(n**(49**2),100**2)[0].bit_length()
492
>>> gmpy2.iroot(n**(48**2),100**2)[0].bit_length()
472
>>> gmpy2.iroot(n**(47**2),100**2)[0].bit_length()
453
'''

本来按照定理条件的推导,只要未知二进制位数少于 492 492 492均可应用coppersmith定理求解


sagemath中应用coppersmith定理的函数有两个:small_rootscoppersmith_howgrave_univariate

后者的使用方法可以参考 Coppersmith 相关攻击 - CTF Wiki (ctf-wiki.org)

对RSA-Factoring with High Bits Known理解 - 简书 (jianshu.com)

这里求解coppersmith我们统一使用sagemathsmall_roots()函数;该函数导入的 β \beta β 起作用的只有一位小数(如果是两位小数,其求解范围还是相当于一位小数的求解范围),这就意味着一般形如p = getPrime(bits),q = geyPrime(bits)的RSA应用coppersmith求解p的低位时, β \beta β只能是最接近 0.5 0.5 0.5 0.4 0.4 0.4

关于small_roots的使用方法:

small_roots(X = ,beta = ) 有两个参数

X代表所需要求解根的上限;虽然是根的上限,并不是说上限越高越好,当上限超过某个值的时候就会计算失效,即使已知二进制位数满足条件,也无法用此函数求得结果;所以一般来说X取在给定情况下的最大求解上限

beta即是前面提到的 β \beta β ,当p,q二进制位数相同时一般只能取 0.4 0.4 0.4;如果p,q二进制位数不同,就按照之前的方法具体问题具体分析

那么实际操作下,使用small_roots()我们至少需要已知多少二进制位数呢?(以下是sagemath环境)

from Crypto.Util.number import getPrime
p = getPrime(int(1024))
q = getPrime(int(1024))
n = p * q
test_bits = 454
p_h = p >> test_bits
R.<x> = PolynomialRing(Zmod(n))
f = (p_h << test_bits) + x
f.small_roots(2**test_bits,0.4)

经过测试得到,当未知量小于等于 454 b i t s 454bits 454bits时(p,q 1024 b i t s 1024bits 1024bits),coppersmith定理可以求解

之后改变了p,q的大小,经过测试发现,当p,q同二进制位数时,要使用small_roots()应用coppersmith定理求解 n n n的某个因数的低位,需要满足未知的二进制位数与因数之间的二进制位数的关系是
u n k n o w n _ b i t s ÷ p _ b i t s ≤ 0.44 unknown\_bits \div p\_bits \leq 0.44 unknown_bits÷p_bits0.44
下面来以例题为标准应用一下small_roots的灵活应用

P r o b l e m Problem Problem

问题来自鹤城杯2021,babyrsa

D e s c r i p t i o n Description Description

from Crypto.Util.number import getPrime, bytes_to_long
from secret import flag

p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 65537
hint1 = p >> 724
hint2 = q % (2 ** 265)
ct = pow(bytes_to_long(flag), e, n)
print(hint1)
print(hint2)
print(n)
print(ct)

A n a l y s i s Analysis Analysis

显然是应用高位攻击

但是由于之前测试的结果,未知的二进制位数需要小于 454 454 454,而这里p未知的二进制位数达到了 724 724 724,而q则更多;

但是这里既然给出了p,q两者分别的高位,低位;按照高位攻击的思路,需要是某一个因数的多个二进制位数,所以找方法能不呢把q的低位转换为p的低位,或者相反
{ q l o w ≡ q ( m o d 2 265 ) q h i g h = k ⋅ 2 265 n = p ⋅ ( q h i g h + q l o w ) ⇒ n = p ⋅ k ⋅ 2 265 + p ⋅ q ( m o d 2 265 ) \begin{cases} q_{low} \equiv q\pmod {2^{265}}\\ q_{high} = k\cdot 2^{265}\\ n = p\cdot (q_{high} + q_{low}) \end{cases} \Rightarrow n =p\cdot k\cdot 2^{265} + p\cdot q\pmod {2^{265}} qlowq(mod2265)qhigh=k2265n=p(qhigh+qlow)n=pk2265+pq(mod2265)
等式两边同时求余 2 265 2^{265} 2265
n l o w ≡ p ⋅ k ⋅ 2 265 + p ⋅ q l o w ( m o d 2 265 ) ⇒ p l o w ≡ n l o w ⋅ q l o w − 1 ( m o d 2 265 ) n_{low}\equiv p\cdot k\cdot 2^{265} + p\cdot q_{low}\pmod {2^{265}}\\ \Rightarrow p_{low}\equiv n_{low} \cdot q^{-1}_{low} \pmod{2^{265}} nlowpk2265+pqlow(mod2265)plownlowqlow1(mod2265)
这样一来我们就知道了 p p p的低 265 265 265位,此时未知的二进制位数仍然有 459 459 459位,而我们需要的是未知二进制位要在 454 454 454之下

只剩下 5 5 5位了,那么直接就可以爆破剩余的 5 5 5位二进制

S o l v i n g   c o d e Solving~code Solving code

from Crypto.Util.number import *
import gmpy2
p_high = 1514296530850131082973956029074258536069144071110652176122006763622293335057110441067910479
q_low = 40812438243894343296354573724131194431453023461572200856406939246297219541329623
n = 21815431662065695412834116602474344081782093119269423403335882867255834302242945742413692949886248581138784199165404321893594820375775454774521554409598568793217997859258282700084148322905405227238617443766062207618899209593375881728671746850745598576485323702483634599597393910908142659231071532803602701147251570567032402848145462183405098097523810358199597631612616833723150146418889589492395974359466777040500971885443881359700735149623177757865032984744576285054725506299888069904106805731600019058631951255795316571242969336763938805465676269140733371287244624066632153110685509892188900004952700111937292221969
enc = 19073695285772829730103928222962723784199491145730661021332365516942301513989932980896145664842527253998170902799883262567366661277268801440634319694884564820420852947935710798269700777126717746701065483129644585829522353341718916661536894041337878440111845645200627940640539279744348235772441988748977191513786620459922039153862250137904894008551515928486867493608757307981955335488977402307933930592035163126858060189156114410872337004784951228340994743202032248681976932591575016798640429231399974090325134545852080425047146251781339862753527319093938929691759486362536986249207187765947926921267520150073408188188
e = 65537

mod = pow(2,265)
p0 = n * inverse_mod(q_low,mod) % mod
PR.<x> = PolynomialRing(Zmod(n))
for i in range(2**5):
    f = p_high * (2^724) + p0 + (x * 32 + i) * mod
    f = f.monic()
    out_p = f.small_roots(2^454,0.4)
    if len(out_p) != 0:
        print(out_p[0])
        break
p = out_p[0] * 32 + i * mod + p_high * (2^724) + p0
# print(p)
p = 133637329398256221348922087205912367118213472434713498908220867690672019569057789598459580146410501473689139466275052698529257254973211963162087316149628000798221014338373126500646873612341158676084318494058522014519669302359038980726479317742766438142835169562422371156257894374341629012755597863752154328407
assert n % p == 0
q = n // p
fai_n = (p-1) * (q-1)
d = inverse_mod(e,fai_n)
m = pow(enc,d,n)
print(bytes.decode(long_to_bytes(m)))

O t h e r Other Other

关于RSA中应用coppersmith还有部分私钥泄露攻击等等

这里就不过多阐述了,可以参考 CTF中的RSA基本套路(3) - 食兔人的博客 (ycdxsb.cn)

在AT89C51单片机中,要实现初始状态下P2口八个LED灯全亮并使用if语句逐位熄灭,你需要按照以下步骤操作: 1. **初始化P2口**:首先,在程序开始时,设置P2口的所有IO作为输出(OUTPUT),并且将它们的值都置为1,这样所有LED就处于高电平点亮状态。 ```c ; 初始化P2口为输出 MOV P2, #0xFF ; 将P2口清零,然后置为全1,表示所有LED点亮 ``` 2. **定时循环**:创建一个无限循环,用于控制流水灯的操作。你可以选择使用定时器T0配合中断来实现延时,每完成一次计数,就执行一次循环体里的操作。 ```c LOOP: SJMP LOOP ; 无条件跳转回LOOP,形成无限循环 ; 定义定时器T0中断服务函数 TIMER0_ISR: CLI ; 关闭总中断,防止干扰 RETI ; 返回并关闭中断 ; 开启定时器T0,例如1ms定时 MOV TMOD, #0x01 ; 设置TMOD为工作模式1(16位定时器) MOV TH0, #0xFE ; 高8位计数值,1ms约等于0xFE * 1us MOV TL0, #0xFD ; 低8位计数值 SETB TR0 ; 启动定时器T0 ``` 3. **熄灭LED**:在中断服务函数中,检查P2口最低位,如果为1,则将其设为0,然后返回,继续下一次循环。 ```c TIMER0_ISR: JNB P2.0, END ; 如果P2.0(最低位)为1,则进入END标签 MOV P2.0, #0 ; 熄灭该LED END: RETI ; 结束当前中断并回到主循环 ``` 4. **主循环内等待中断**:在主循环中,等待定时器T0中断,每次中断结束后,P2口会按位顺序熄灭LED。 注意:这个示例假设了你的CPU时钟频率已知,并且1ms大约等于0xFE个机器周期。实际编程时可能需要根据你的硬件配置调整定时器的参数。
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M3ng@L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值