Featured image of post Coppersmith定理的几种攻击情况的学习

Coppersmith定理的几种攻击情况的学习

coppersmith 定理的几种攻击情况的学习

Coppersmith 可以用于求多项式的小根,经常用于 RSA 攻击中“已知某些二进制位,求剩余位”这一类问题

给定:

  • 模数 N(通常很大,如 RSA 模数)
  • 模 N 的多项式 f(x)(通常为单变量)
  • 边界 X

寻找所有满足 f(x0) ≡ 0 (mod N)且 ∣x0∣ < X 的整数根 x0

成立的条件:

$$ |x_0|\ <\ N^{1/deg(f)}\\ $$

deg(f)为多项式最高项次数

1.basic boradcast attack 广播攻击

攻击原理

1
Broadcast attack 是 Håstad attack,不是 Coppersmith,只是经常一起出现

使用同一个指数 e 加密同一个密文 m,产生 e 个明文,形如:

$$ c_1 \equiv m^e \pmod{n_1} \\ c_2 \equiv m^e \pmod{n_2} \\ \vdots \\ c_e \equiv m^e \pmod{n_e} $$

n1, n2…互素,

是 m ≤ min(n₁, n₂, …, nₑ),即明文 m 必须小于等于所有模数中的最小值

然后解中国剩余定理

根据中国剩余定理(CRT),可以求解:

mᵉ ≡ C (mod N)

其中 N = n₁ × n₂ × … × nₑ

因为 m ≤ min(n₁, n₂, …, nₑ),m^e^ < n₁ × n₂ × … × nₑ

mᵉ < N,则可以直接对 C 开 e 次方根得到明文 m:m = ᵉ√C

RSA 高位攻击

内容参考:

RSA_高位攻击学习记录_rsa 已知高位攻击-CSDN 博客

RSA 中 coppersmith 定理的应用条件-CSDN 博客

Coppersmith 攻击

知道部分二进制位,求剩余二进制位,只有未知的 bit 位满足 unknown_bits / p_bits <= 0.44(即 coppersmith 定理)时,才能通过 small_roots()方法求出未知的 bit 位,进而求解 flag

small_roots(X = , beta = )的使用方法:

1
2
3
R.<x> = PolynomialRing(Zmod(N))      # 定义模N的多项式环
f = (x + a)^e - c                     # 定义多项式
roots = f.small_roots(X=,beta=)

看不懂,让 ai 细说了一下

边界因素 X:

  1. 已知根的比特 k 的情况下,X = 2^k^
  2. 已知根的大致范围 [0,1000],X = 1000
  3. 理论边界:根据 coppersmith: X < N^1/e^,所以 X = floor(N^1/e^)
  4. 因子的部分比特攻击,知道了 p 的高位,p = p_high+x,所以 X = 2^unknown_bits^

beta 参数(β):当 p, q 二进制位数相同时一般只能取 0.4

  1. 在模 N 上寻找根,如果根满足 f(x) ≡ 0 (mod N),beta = 1

  2. 在模 p 上寻找根,且 p 约等于 N^0.5^(RSA 情况),p 是 N 的一个因子,p ≈ √N ≈ N^0.5,beta = 0.5,因为 p ≈ N^0.5,所以 β = 0.5

  3. 在模 p 上寻找根,但 p 的大小不同:

    如果 p ≈ N^0.3^:beta = 0.3

    如果 p ≈ N^0.25:beta = 0.25

  4. 寻找模 N 的小根:beta = 1.0

攻击目标 模数 β
模 N 求根 N 1.0
模 p(RSA) p ≈ N^0.5 0.5
模 p ≈ N^0.3 p 0.3

已知 p 高位

知道 p 的前 k 位,记低位为 x,所以 p = p_high * 2^k^ + x,算出 p 的低位 x 后直接用 small_roots

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
from Crypto.Util.number import*
p = getPrime(1024)
q = getPrime(1024)
n= p * q
print(p)
p_high = p >> 300#已知高位1024-300bits
pbits = 1024
kbits = pbits - p_high.bit_length()#低位300bits
p_high = p_high << kbits#移动回去把低位空出来
PR.<x> = PolynomialRing(Zmod(n))#多项式环
f = x + p_high
p0=f.small_roots(2**kbits,0.4)[0]
print(p0+p_high)

已知 p 高位,q 低位

已知的 p 高位不满足 unknown_bits / p_bits <= 0.44,且知道 q 的低位,低位数 x

解题方法省个流就是:

$$ p_{\text{low}} = p \bmod 2^{x} = \left( n \cdot q_{\text{low}}^{-1} \right) \bmod 2^{x} $$

然后用 small_roots 算出中间的未知数然后加起来就可以了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
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)
c = pow(bytes_to_long(flag), e, n)
print(hint1)
print(hint2)
print(n)
print(c)

q 的低位 q_low

$$ q \bmod 2^{265} = q_{\text{low}} $$

所以:

$$ q = k \cdot 2^{265} + q_{\text{low}} $$

两边同时乘 q 可以得到:

$$ n = p \cdot \left( k \cdot 2^{265} + q_{\text{low}} \right) = k \cdot 2^{265} \cdot p + q_{\text{low}} \cdot p $$

同时除以 q_low:

$$ n \cdot q_{\text{low}}^{-1} = k \cdot 2^{265} \cdot p \cdot q_{\text{low}}^{-1} + p $$

同时取模 2^265^得到:

$$ p_{\text{low}} = p \bmod 2^{265} = \left( n \cdot q_{\text{low}}^{-1} \right) \bmod 2^{265} $$
1
2
3
4
5
6
7
8
from Crypto.Util.number import*
p_high = 1514296530850131082973956029074258536069144071110652176122006763622293335057110441067910479
q_low = 40812438243894343296354573724131194431453023461572200856406939246297219541329623
n = 21815431662065695412834116602474344081782093119269423403335882867255834302242945742413692949886248581138784199165404321893594820375775454774521554409598568793217997859258282700084148322905405227238617443766062207618899209593375881728671746850745598576485323702483634599597393910908142659231071532803602701147251570567032402848145462183405098097523810358199597631612616833723150146418889589492395974359466777040500971885443881359700735149623177757865032984744576285054725506299888069904106805731600019058631951255795316571242969336763938805465676269140733371287244624066632153110685509892188900004952700111937292221969
c = 19073695285772829730103928222962723784199491145730661021332365516942301513989932980896145664842527253998170902799883262567366661277268801440634319694884564820420852947935710798269700777126717746701065483129644585829522353341718916661536894041337878440111845645200627940640539279744348235772441988748977191513786620459922039153862250137904894008551515928486867493608757307981955335488977402307933930592035163126858060189156114410872337004784951228340994743202032248681976932591575016798640429231399974090325134545852080425047146251781339862753527319093938929691759486362536986249207187765947926921267520150073408188188
e = 65537
p_low=(n//q_low)%(2**256)
print(p_low)

现在有低位 256 和高位 300,所以目前未知位为 459,最多未知位为 1024 * 0.44 = 454,所以要暴力枚举五位

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from Crypto.Util.number import*
p_high = 1514296530850131082973956029074258536069144071110652176122006763622293335057110441067910479
q_low = 40812438243894343296354573724131194431453023461572200856406939246297219541329623
n = 21815431662065695412834116602474344081782093119269423403335882867255834302242945742413692949886248581138784199165404321893594820375775454774521554409598568793217997859258282700084148322905405227238617443766062207618899209593375881728671746850745598576485323702483634599597393910908142659231071532803602701147251570567032402848145462183405098097523810358199597631612616833723150146418889589492395974359466777040500971885443881359700735149623177757865032984744576285054725506299888069904106805731600019058631951255795316571242969336763938805465676269140733371287244624066632153110685509892188900004952700111937292221969
c = 19073695285772829730103928222962723784199491145730661021332365516942301513989932980896145664842527253998170902799883262567366661277268801440634319694884564820420852947935710798269700777126717746701065483129644585829522353341718916661536894041337878440111845645200627940640539279744348235772441988748977191513786620459922039153862250137904894008551515928486867493608757307981955335488977402307933930592035163126858060189156114410872337004784951228340994743202032248681976932591575016798640429231399974090325134545852080425047146251781339862753527319093938929691759486362536986249207187765947926921267520150073408188188
e = 65537
q_low_inv=pow(q_low,-1,2**265)
p_low = (n*q_low_inv)%(2**265)
print(p_low)
PR.<x> = PolynomialRing(Zmod(n))
p_high=p_high << 724
for i in range(32):
    f = p_high + p_low + (x * 32 + i) * (2**265)
    f = f.monic()
    p = f.small_roots(2**454,0.4)
    if p:
        x0=int(p[0])
        p=(x0*32 + i) * (2**265) + p_high + p_low
        break
print(p)
q=n//p
phi=(p-1)*(q-1)
d=pow(e,-1,phi)
print(long_to_bytes(pow(c,d,n)))

md 还能有这种报错的,夸张哦 😓

已知密文 m 高位

怎么感觉像照着抄啊我干

m 和 c 的关系是

$$ c = m^e \mod n $$

又因为:

$$ m = m_{\text{high}} + x \quad (\text{$x$ 是低位未知}) $$

所以把 c 移过来可以变成:

$$ f(x) = (m_{\text{high}} + x)^e - c \equiv 0 \mod n $$

然后直接套公式秒了

已知 d 的低位求 pq

已知了 d 的低位 d_low 和低位 bits 数 x 还有 e

因为:

$$ ed \equiv 1 \pmod{(p-1)(q-1)} $$

所以:ed = 1 + k * (p-1) * (q-1)

然后对两边取模 2^x^,所以:

$$ ed_{\text{low}} \equiv 1 + k(p-1)(q-1) \pmod{2^x} $$

把 q 换成 n/p,这个式子就为

$$ p \cdot ed_{low} \equiv p + k(n \cdot p - p^{2} - n + p) \pmod{2^{x}} $$

然后 solve_mod 解模方程(sagemath 太好用了家人们)

Franklin-Reiter 相关消息攻击 & Coppersmith’s Short Pad Attack 短填充攻击

攻击原理

由于 m1 和 m2 存在线性关系并且在相同 N 下进行的 RSA 加密,

假设

$$ m_1 = k\ * \ m_2 \ +\ b $$

$$ m_1\ \equiv \ a\ mod \ N\\ m_2\ \equiv \ a\ mod \ N $$

这样就可以列出两个和 m2 相关的方程

$$ f(x)\ =\ x\ -\ a\\f(x)= k\ * \ x \ +\ b\ -\ a $$

这样这两个式子取 x = m2 时都会等于 0,在数学意义下有个 (x-m2) 的因式,所以直接取这两个方程的公因数就可以找到 m2

顺便学习一下 sagemath

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
R.<X> = Zmod(n)[]#定义多项式环
.coefficients()#以列表形式返回多项式的常数项(从低项到高项的升序排列,即7x^5+3x^4+4返回[4,3,7])
.monic()#除以最高项的系数
'''
4x^3+2x^2+x
返回:
x^3+1/2x^2+1/4x
'''
#多项式求公因数用欧几里得算法递归
def gcd(a, b):
    if(b == 0):
        return a.monic()
    else:
        return gcd(b, a % b)

做下例题

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
from Crypto.Util.number import *
from secret import flag
from Crypto.Util.number import *

m1 = bytes_to_long(flag)
N = getPrime(512)*getPrime(512)
e = 17

c1 = pow(m1, e, N)

a = getRandomNBitInteger(512)
b = getRandomNBitInteger(512)
m2 = a * m1 + b
c2 = pow(m2, e, N)

print(f'N={N}', f'a={a}', f'b={b}', f'c1={c1}', f'c2={c2}', sep="\n")
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
N=123659932061175480826793608011321410436349950741155126876095701055301878519455031361569954900678263326118459719432097093055918891948664714258046001339215600541946755989049122600427628642622036472692779340339300158628581419220590868242848311473965406365501747491282544140645468809520929307937285660479491241363
a=12795184347649957548397347648657609712242281197428253341891395348966629724842748696386579533843100833039565312645558384706282291396553602742473087674306336
b=12652561268961103936837420866306660440380907636430718507525953046969196471975210807471094652190751376751348655252244311630569860771118936181419956587387282
c1=51991948160397341594095534046308905334052328129886214140433481879034095448631790463996064234310860642758645828216350970768732856439017324754993220774971081882007838091737307185049230846102962234452369235186705221800322142645874117556212685451861172069436515378795560228077509937484022552758984136308761496373
c2=102367269426206577837660468860715696190840895268468904172938303357188227693435122799965957675733814903345970317009686706066325312156008609593926539768689081990544088825413585508055156925413722623286655819700429518477974133941543843635128782119973290018886257977205103150970429699110833604623614038282346303825
e=17
from Crypto.Util.number import*
def s(N,e,c1,c2,a,b):
    R.<X> = Zmod(N)[]
    f1 = X^e - c1
    f2 = (X*a+ b)^e - c2
    return Integer(-(gcd(f1,f2)).coefficients()[0]) 
def gcd(a, b):
    if(b == 0):
        return a.monic()
    else:
        return gcd(b, a % b)

m=s(N,e,c1,c2,a,b)
print(long_to_bytes(m))
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from Crypto.Util.number import *
from secret import flag

num1 = getPrime(512)
num2 = getPrime(512)
while num1<num2 :
    num2 = getPrime(512)
ring = RealField(1050)
num3 = ring(num1) /ring(num2)
print("num3=",num3)
p = getPrime(512)
q = getPrime(512)
n=p*q
e=65537
m = bytes_to_long(flag)
c=pow(m,e,n)

print("n=",n)
print("c=",c)

n2 = getPrime(512) * getPrime(512)
e1 = randint(1000,2000)
e2 = randint(1000,2000)
c1 = pow(p+num1,e1,n2)
c2 = pow(p+num2,e2,n2)

q1 = getPrime(512)
leak1 = pow(q+q1,2024,n)
leak2 = pow(q1+2024,q,n)


print("n2=",n2)
print("e1=",e1)
print("e2=",e2)
print("c1=",c1)
print("c2=",c2)
print("leak1=",leak1)
print("leak2=",leak2)



"""
num3= 1.36557212221826657073387899060669771982679822943621690677450888408226656788387273887547841291114809989272635809810564202247340711087479554863446719786359395466961253205133910506682801159622545780721946115442033391600881399634390008053822158098121985270501317972263356522400827768601773721146954464269212959784543085
n= 85105083975491693151454182116844944807066048677068373328227644321539783064315677834754866742549592569194223084173286381150626593510265361114796714226058887906219454795525438819082646860141163940340082006344850825956811992304447978369108606993344902286569100146695092703383844402883938506877787042873586279543
c= 8090472119640930864901421058741085326954308673260202542020919764880488559370287585797498390920330336595858609617432370825503480936376306766495089200286004922821787548265246289552343468177624634434613746605553770994437785042510225956023382347023663125411103947669109085411939772215657220674436476279268458980
n2= 101642316595332652021348165259853423287787139517550249986161819826180514401858258316466101056877182367161299111697465439636861942636526396547011727147471566130633614685728563576613692851860684686033186826342178072882901576159305747639645374751566751967096281105148714033096611618024355982220235949274576036321
e1= 1630
e2= 1866
c1= 8857135083997055103287497833179378469532619748945804429057682575070405217145941944821968708809563240261403711644389684947851643865895254406194464015430673347359589677809515117412321862622147634752091324365628790687343748145339949310696116239361890881096088375070083053010564890401663562726144984405628773323
c2= 44531030636557714647477473185500183066851251320453194953972504422367649302810396344051696851757189817391648356459225688318373454949578822468293099948132700460437006478679492801335689493431764882835346904225119630026545592437198370606462285405519745361570058335573353886454277790277663038008240372746639859253
leak1= 82301473255013183706458389946960254392188270550712533886416705365418418731488346328643954589202172816597173052792573628245245948345810581701878535280775967863966009605872386693838526935762655380705962833467046779524956212498594045378770790026387120339093736625186401934354434702063802537686761251873173518029
leak2= 43580171648136008789232340619597144591536098696024883687397347933098380327258730482377138309020375265135558484586783368757872008322883985094403855691297725907800406097129735499961231236473313141257901326737291586051506797429883866846199683028143924054925109557329949641367848264351523500925115860458645738192

"""

DASTF 的题,先连分数逼近来解开 num1 和 num2,然后用相关消息攻击,情报有误,这是短填充攻击

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
num3= 1.36557212221826657073387899060669771982679822943621690677450888408226656788387273887547841291114809989272635809810564202247340711087479554863446719786359395466961253205133910506682801159622545780721946115442033391600881399634390008053822158098121985270501317972263356522400827768601773721146954464269212959784543085
n= 85105083975491693151454182116844944807066048677068373328227644321539783064315677834754866742549592569194223084173286381150626593510265361114796714226058887906219454795525438819082646860141163940340082006344850825956811992304447978369108606993344902286569100146695092703383844402883938506877787042873586279543
c= 8090472119640930864901421058741085326954308673260202542020919764880488559370287585797498390920330336595858609617432370825503480936376306766495089200286004922821787548265246289552343468177624634434613746605553770994437785042510225956023382347023663125411103947669109085411939772215657220674436476279268458980
n2= 101642316595332652021348165259853423287787139517550249986161819826180514401858258316466101056877182367161299111697465439636861942636526396547011727147471566130633614685728563576613692851860684686033186826342178072882901576159305747639645374751566751967096281105148714033096611618024355982220235949274576036321
e1= 1630
e2= 1866
e=65537
c1= 8857135083997055103287497833179378469532619748945804429057682575070405217145941944821968708809563240261403711644389684947851643865895254406194464015430673347359589677809515117412321862622147634752091324365628790687343748145339949310696116239361890881096088375070083053010564890401663562726144984405628773323
c2= 44531030636557714647477473185500183066851251320453194953972504422367649302810396344051696851757189817391648356459225688318373454949578822468293099948132700460437006478679492801335689493431764882835346904225119630026545592437198370606462285405519745361570058335573353886454277790277663038008240372746639859253
leak1= 82301473255013183706458389946960254392188270550712533886416705365418418731488346328643954589202172816597173052792573628245245948345810581701878535280775967863966009605872386693838526935762655380705962833467046779524956212498594045378770790026387120339093736625186401934354434702063802537686761251873173518029
leak2= 43580171648136008789232340619597144591536098696024883687397347933098380327258730482377138309020375265135558484586783368757872008322883985094403855691297725907800406097129735499961231236473313141257901326737291586051506797429883866846199683028143924054925109557329949641367848264351523500925115860458645738192
t=continued_fraction(num3)
for i in range(1000):
    num1=t.numerator(i)
    if is_prime(num1) and num1.nbits()==512:
        num2=t.denominator(i)
        print(num1)
        print(num2)
        break
from Crypto.Util.number import*
def s(n,e1,e2,c1,c2):
    R.<X> = Zmod(n)[]
    f1 = (X+num1)**e1 - c1
    f2 = (X+num2)**e2 - c2
    return Integer(-(gcd(f1,f2)).coefficients()[0]) 
def gcd(a, b):
    if(b == 0):
        return a.monic()
    else:
        return gcd(b, a % b)
p=s(n2,e1,e2,c1,c2)
print(p)
q=n//p
d=pow(e,-1,(p-1)*(q-1))
m=pow(c,d,n)
print(long_to_bytes(m))
Typed.js 彩蛋顺序跳转(首页不触发)