在比赛中多次遇到 F r a n k l i n − R e i t e r Franklin-Reiter Franklin−Reiter,今天来系统学习一下。
关于Franklin-Reiter
定理
假设有两条消息 m1
和 m2
,其中
m
1
!
=
m
2
m1 != m2
m1!=m2(均小于模数
n
n
n )。且彼此线性相关:
m
1
=
f
(
m
2
)
(
m
o
d
n
)
m1=f(m2)(mod\textbf{ }n)
m1=f(m2)(mod n),其中
f
=
a
∗
x
+
b
,
(
b
!
=
0
)
f=a*x+b,(b!=0)
f=a∗x+b,(b!=0)。如果这两个消息通过使用相同公钥(n,e)
进行RSA
加密,得到密文c1
和c2
。那么,给定(n,e,c1,c2,f)
,攻击者就可以恢复消息m1
和m2
。
简单来说,相关信息攻击就是如果两个信息之间存在某种线性关系,并且在相同的n和e下进行RSA加密,那么就有可能恢复出这两个消息.
即m
和
a
∗
m
+
b
(
b
!
=
0
)
a*m+b(b!=0)
a∗m+b(b!=0)两个明文在相同的(n,e)
下进行RSA
加密,那么m
就可以破解.
如果两条消息之间仅存在某种线性关系
并且在相同的(n,e)下进行RSA加密
那么就有可能同时恢复它们
m1 = bytes_to_long(flag)
m2 = a * m1 + b,其中b!=0
c1 = pow(m1,e,n)
c2 = pow(m2,e,n)
其中a,b,c1,c2,e,n都已知,那么m1,m2可被破解
证明
c 1 = m 1 e m o d n ⇒ c1=m1^{e}\textbf{ }mod\textbf{ }n \Rightarrow c1=m1e mod n⇒
c 1 = ( f ( m 2 ) m o d n ) e m o d n c1 = (f(m2)\textbf{ }mod\textbf{ }n)^{e}\textbf{ }mod\textbf{ }n c1=(f(m2) mod n)e mod n
于 是 , 就 有 多 项 式 { g 1 ( x ) = x e − c 1 g 2 ( x ) = f ( x ) e − c 2 于是,就有多项式\textbf{ }\textbf{ }\left\{\begin{matrix} g_{1}(x)=x^{e}-c1\\ g_{2}(x)=f(x)^{e}-c2 \end{matrix}\right. 于是,就有多项式 {g1(x)=xe−c1g2(x)=f(x)e−c2
显然,m2
是上述两个多项式的根,因此它们有一个公因子
x
∗
m
2
x*m2
x∗m2
(
g
1
(
x
)
=
0
,
g
2
(
x
)
=
0
)
(g_{1}(x)=0,g_{2}(x)=0)
(g1(x)=0,g2(x)=0),所以我们
g
c
d
(
g
1
,
g
2
)
gcd(g_{1},g_{2})
gcd(g1,g2)就能得到m2
,根据线性关系就能得到m1
.注意e=3时,最大公因子一定是线性的
例题
2023 SICTF#Round2-签到题来咯!
题目
from secret import flag
from Crypto.Util.number import *
m = bytes_to_long(flag)
p = getPrime(1024)
q = getPrime(1024)
e = getPrime(10)
n = p*q
c1 = pow(114*m+2333,e,n)
c2 = pow(514*m+4555,e,n)
print(f'n = {n}')
print(f'c1 = {c1}')
print(f'c2 = {c2}')
'''
n = 18993579800590288733556762316465854395650778003397512624355925069287661487515652428099677335464809283955351330659278915073219733930542167360381688856732762552737791137784222098296804826261681852699742456526979985201331982720936091963830799430264680941164508709453794113576607749669278887105809727027129736803614327631979056934906547015919204770702496676692691248702461766117271815398943842909579917102217310779431999448597899109808086655029624478062317317442297276087073653945439820988375066353157221370129064423613949039895822016206336117081475698987326594199181180346821431242733826487765566154350269651592993856883
c1 = 3089900890429368903963127778258893993015616003863275300568951378177309984878857933740319974151823410060583527905656182419531008417050246901514691111335764182779077027419410717272164998075313101695833565450587029584857433998627248705518025411896438130004108810308599666206694770859843696952378804678690327442746359836105117371144846629293505396610982407985241783168161504309420302314102538231774470927864959064261347913286659384383565379900391857812482728653358741387072374314243068833590379370244368317200796927931678203916569721211768082289529948017340699194622234734381555103898784827642197721866114583358940604520
c2 = 6062491672599671503583327431533992487890060173533816222838721749216161789662841049274959778509684968479022417053571624473283543736981267659104310293237792925201009775193492423025040929132360886500863823523629213703533794348606076463773478200331006341206053010168741302440409050344170767489936681627020501853981450212305108039373119567034948781143698613084550376070802084805644270376620484786155554275798939105737707005991882264123315436368611647275530607811665999620394422672764116158492214128572456571553281799359243174598812137554860109807481900330449364878168308833006964726761878461761560543284533578701661413931
'''
感官上能看出m1
和m2
存在某种线性关系
。所以是相关信息攻击(Franklin-Reiter)
需要注意的是,这里e
是10bit
素数中的一个,遍历一下10bit
素数就行
exp
from Crypto.Util.number import *
import binascii
n =
c1 =
c2 =
def franklinReiter(n,e,c1,c2):
PR.<x> = PolynomialRing(Zmod(n))
g1 = (114*x+2333)^e - c1
g2 = (514*x+4555)^e - c2
def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic()
return -gcd(g1, g2)[0]
def get_all_10_bit_primes(): # 求10bit的所有素数
return [i for i in range(2**9, 2**10) if isPrime(i)]
e_all = get_all_10_bit_primes()
print(e_all)
for e in e_all:
m=franklinReiter(n,e,c1,c2)
flag = long_to_bytes(int(m))
if b'SICTF' in flag:
print(flag)
break
print("over")
# SICTF{hhh!!franklin_reiter_is_easy}
题二
题目
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(N, a, b, c1, c2, sep="\n")
# 51296885372346449295388453471330409021784141081351581975478435681552082076338697136130122011636685327781785488670769096434920591920054441921039812310126089859349902066456998315283909435249794317277620588552441456327265553018986591779396701680997794937951231970194353001576159809798153970829987274504038146741
# 13256631249970000274738888132534852767685499642889351632072622194777502848070957827974250425805779856662241409663031192870528911932663995606616763982320967
# 12614470377409090738391280373352373943201882741276992121990944593827605866548572392808272414120477304486154096358852845785437999246453926812759725932442170
# 18617698095122597355752178584860764221736156139844401400942959000560180868595058572264330257490645079792321778926462300410653970722619332098601515399526245808718518153518824404167374361098424325296872587362792839831578589407441739040578339310283844080111189381106274103089079702496168766831316853664552253142
# 14091361528414093900688440242152327115109256507133728799758289918462970724109343410464537203689727409590796472177295835710571700501895484300979622506298961999001641059179449655629481072402234965831697915939034769804437452528921599125823412464950939837343822566667533463393026895985173157447434429906021792720
已知(a,b,e,n,c1,c2)
,典型的Franklin-Reiter
攻击.
from Crypto.Util.number import *
n =
a =
b =
c1 =
c2 =
e =
import binascii
def franklinReiter(n,e,c1,c2,a,b):
PR.<x> = PolynomialRing(Zmod(n))
g1 = (x)^e - c1
g2 = (a*x+b)^e - c2
def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic() #
return -gcd(g1, g2)[0]
m=franklinReiter(n,e,c1,c2,a,b)
print(lont_to_bytes(m))
# flag{a593591a-3749-cc52-0c27-e897fac2c967}
其中代码
def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic() #
return -gcd(g1, g2)[0]
使用辗转相除法求多项式的最大公因子
在代数中,一个多项式的首项系数
通常被称为该多项式的引导系数
(
l
e
a
d
i
n
g
c
o
e
f
f
i
c
i
e
n
t
)
(leading coefficient)
(leadingcoefficient),而将多项式变成首项系数为1的形式
被称为将多项式化为首一形式(monic form)
调用函数g1.monic()
将g1
转换为首一多项式
(
m
o
n
i
c
p
o
l
y
n
o
m
i
a
l
)
(monic polynomial)
(monicpolynomial),并返回该多项式。
使用g.monic()[0]
,则会返回g(x)
除以引导系数后得到的多项式的常数项
比如:
g
.
m
o
n
i
c
(
)
=
x
+
32412345
g.monic() = x + 32412345
g.monic()=x+32412345
那么:
g
.
m
o
n
i
c
(
)
[
0
]
=
32412345
g.monic()[0] = 32412345
g.monic()[0]=32412345
题三
from Crypto.Util.number import getPrime,bytes_to_long,long_to_bytes
from functools import reduce
from secret import flag, x, y
m = bytes_to_long(flag)
p = getPrime(1024)
q = getPrime(1024)
n = p*q
print(n)
assert(reduce(lambda x,y:x&y,[(i-5)*i+6==0 for i in x]))
assert(reduce(lambda x,y:x&y,[(j-15)*j+44==0 for j in y]))
print(pow(reduce(lambda x,y:x*m+y,x),17,n))
print(pow(reduce(lambda x,y:x*m+y,y),17,n))
# n = 23772599983135215481563178266884362291876571759991288577057472733374903836591330410574958472090396886895304944176208711481780781286891334062794555288959410390926474473859289842654809538435377431088422352076225067494924657598298955407771484146155998883073439266427190212827600119365643065276814044272790573450938596830336430371987561905132579730619341196199420897034988685012777895002554746080384319298123154671447844799088258541911028041717897434816921424155687677867019535399434825468160227242441375503664915265223696139025407768146464383537556265875013085702422829200814612395116961538432886116917063119749068212699
# c1 = 10900151504654409767059699202929100225155892269473271859207513720755903691031362539478242920144073599515746938827937863835169270383721094542639011665235593065932998091574636525973099426040452626893461449084383663453549354608769727777329036059746386523843912382289597182615339786437186169811342356085836838520978047561127661777189045888648773949147220411427306098338616422692914110656004863767719312410906124366000507952960331116878197129010412361636679449281808407214524741732730279777729251515759320442591663641984363061618865267606007355576230009922421807527598213455112981354590909603317525854070358390622096569841
# c2 = 17298679220717326374674940612143058330715465693318467692839033642321129433471254547497087746971317567301086124779289015934582615377165560688447452762043163082394944604062014490446763247008217251611443338103074143809936437694543761369945095202092750900940979469994907399829695696313513303922266742415376818434932335640062684245008822643258497589196668426788916969378417960200705779461808292296450298558001909603602502604228973101048082095642290047196235959438278631661658312398313171590515776453711432353011579809351076532129444735206408591345372296372378396539831385036814349328459266432393612919118094115543053115450
进行题目分析:
先看一下 r e d u c e ( ) reduce() reduce(),
assert(reduce(lambda x,y:x&y,[(i-5)*i+6==0 for i in x]))
assert(reduce(lambda x,y:x&y,[(j-15)*j+44==0 for j in y]))
r e d u c e reduce reduce函数先从列表(或序列)中取出2个元素执行指定函数,并将输出结果与第3个元素传入函数,输出结果再与第4个元素传入函数,…,以此类推,直到列表每个元素都取完。
这里的作用就相当于解一元二次方程
所以, x = [ 2 , 3 ] , y = [ 4 , 11 ] x=[2,3],y=[4,11] x=[2,3],y=[4,11]
所以,
reduce(lambda x,y:x*m+y,x)
reduce(lambda x,y:x*m+y,y)
也就相当于, 2 ∗ m + 3 , 4 ∗ m + 11 2*m+3,4*m+11 2∗m+3,4∗m+11,典型的 F r a n k l i n − R e i t e r Franklin-Reiter Franklin−Reiter
exp
from Crypto.Util.number import *
n =
c1 =
c2 =
e = 17
import binascii
def franklinReiter(n,e,c1,c2):
PR.<x> = PolynomialRing(Zmod(n))
g1 = (2*x+3)^e - c1
g2 = (4*x+11)^e - c2
def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic()
return -gcd(g1, g2)[0]
m=franklinReiter(n,e,c1,c2)
print(long_to_bytes(m))
# flag{r54__r3l473d_m355463_4774ck_4l50_c4ll3d_fr4nkl1n_r3173r_4774ck~}
NEEPUSec CTF 2021 RSA
题目
from Crypto.Util.number import *
from sympy import nextprime
import gmpy2
import random
def encode (p1,p2,e):
not_hint = (p1 + 1) * (p2 + 1)
S = gmpy2.invert(e, not_hint)
not_p = S%(p1+1)
return not_p
flag = b'Neepu{********************}'
flag = bytes_to_long(flag)
p = getPrime(512)
q = getPrime(512)
n = p*q
e = nextprime(random.randint(1,1000))
d = gmpy2.invert(e, (p-1)*(q-1))
c = pow(flag, e, n)
print(c)
print(n)
m = encode(p, q, e)
c1 = pow(m, 7, n)
c2 = pow(m+e, 7, n)
print(c1)
print(c2)
c = 78543767285872349029076059073458316000847341792088805258173041942425687239313215276670106926320359777962661495032475004417723103701253550583245518206305422982968675291500865382213182669036827898932991063338163290845510339896689210314509493839746410486257998875782496654704288722251878269643040214139429715671
n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543
c1 = 10186066785511829759164194803209819172224966119227668638413350199662683285189286077736537161204019147791799351066849945954518642600518196927152098131117402608793752080104402893792812059620726950782670809837962606250674588612783027976958719051829085903720655233948024280118985875980227528403883475592567727892
c2 = 46182103994299145562022812023438495797686077104477472631494150222038404419414100727667171290098624214113241032861128455086601197239761085752413519627251290509474327611253599768650908336142621210005389246714504358370629231557080301516460985022782887233790302054696967900384601182742759555421864610431428746119
理清题目思路,本题要得到 f l a g flag flag重在求解 p , q , e p,q,e p,q,e.
S = gmpy2.invert(e, not_hint)
not_p = S%(p1+1)
似乎类似于
d
p
泄
露
dp泄露
dp泄露问题.
{
n
o
t
p
=
S
m
o
d
(
p
1
+
1
)
e
∗
S
=
1
m
o
d
n
o
t
h
i
n
t
⇒
\left\{\begin{matrix} notp = S\textbf{ }mod\textbf{ }(p1+1)\\ e*S=1\textbf{ }mod\textbf{ }nothint \end{matrix}\right.\Rightarrow
{notp=S mod (p1+1)e∗S=1 mod nothint⇒
n o t p ∗ e = S ∗ e m o d ( p 1 + 1 ) notp*e=S*e\textbf{ }mod\textbf{ }(p1+1) notp∗e=S∗e mod (p1+1)
所 以 , S ∗ e = k ∗ ( p 1 + 1 ) + n o t p ∗ e = 1 m o d ( p 1 + 1 ) ( p 2 + 1 ) ⇒ 所以,S*e=k*(p1+1)+notp*e=1\textbf{ }mod\textbf{ }(p1+1)(p2+1)\Rightarrow 所以,S∗e=k∗(p1+1)+notp∗e=1 mod (p1+1)(p2+1)⇒
n o t p ∗ e = k 1 ∗ ( p 1 + 1 ) ∗ ( p 2 + 1 ) − k ∗ ( p 1 + 1 ) + 1 ⇒ notp*e=k1*(p1+1)*(p2+1)-k*(p1+1)+1\Rightarrow notp∗e=k1∗(p1+1)∗(p2+1)−k∗(p1+1)+1⇒
n o t p ∗ e = ( p 1 + 1 ) ( k 1 ∗ ( p 2 + 1 ) − k ) + 1 notp*e=(p1+1)(k1*(p2+1)-k)+1 notp∗e=(p1+1)(k1∗(p2+1)−k)+1
因 为 , n o t p < ( p 1 + 1 ) , 所 以 0 < ( k 1 ∗ ( p 1 + 1 ) − k ) < e 因为,notp<(p1+1),所以0<(k1*(p1+1)-k)<e 因为,notp<(p1+1),所以0<(k1∗(p1+1)−k)<e
这样,就能求出 p 1 p1 p1. e e e是未知的,所以我们需要先解出 e e e。
e = nextprime(random.randint(1,1000))
爆破求解e
from Crypto.Util.number import *
n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543
c1 = 10186066785511829759164194803209819172224966119227668638413350199662683285189286077736537161204019147791799351066849945954518642600518196927152098131117402608793752080104402893792812059620726950782670809837962606250674588612783027976958719051829085903720655233948024280118985875980227528403883475592567727892
c2 = 46182103994299145562022812023438495797686077104477472631494150222038404419414100727667171290098624214113241032861128455086601197239761085752413519627251290509474327611253599768650908336142621210005389246714504358370629231557080301516460985022782887233790302054696967900384601182742759555421864610431428746119
import binascii
def franklinReiter(n,e,c1,c2):
PR.<x> = PolynomialRing(Zmod(n))
g1 = (x)^7 - c1
g2 = (x+e)^7 - c2
def gcd(g1, g2):
while g2:
g1, g2 = g2, g1 % g2
return g1.monic()
return -gcd(g1, g2)[0]
for e in range(1,1500):
m = franklinReiter(n,e,c1,c2)
if pow(m,7,n) == c1:
print("e =",e)
print("m =",m)
e = 71
m = 129256555243625096140386916253259867206651269142565502540823654159666398099455456877012993395632742360829588042575108302297567291349420390228163587340859
求p,q
from Crypto.Util.number import *
import gmpy2
n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543
c = 78543767285872349029076059073458316000847341792088805258173041942425687239313215276670106926320359777962661495032475004417723103701253550583245518206305422982968675291500865382213182669036827898932991063338163290845510339896689210314509493839746410486257998875782496654704288722251878269643040214139429715671
dp = 129256555243625096140386916253259867206651269142565502540823654159666398099455456877012993395632742360829588042575108302297567291349420390228163587340859
e = 71
for k in range(1,e):
if ((dp*e-1)%k) == 0:
if n%((dp*e-1)//k - 1) == 0:
p1 = (dp*e-1)//k - 1
p = p1
q = n//p
d = gmpy2.invert(e,(p-1)*(q-1))
m = pow(c,d,n)
print(long_to_bytes(int(m)))
# Neepu{Have-a-g00d-day12138}
Half-Gcd
参考:Half-Gcd
该算法计算两个多项式的 GCD 的速度比标准欧几里得算法更快
一般来说, F r a n l i n − R e i t e r Franlin-Reiter Franlin−Reiter攻击时的加密指数 e e e都是比较小的,但也会出现** e e e比较大**的情况,这时用 H a l f − G c d Half-Gcd Half−Gcd能很快解出。
例题:
from Crypto.Util.number import *
from secret import flag
m = bytes_to_long(flag)
p1, q1 = getPrime(512), getPrime(512)
n1 = p1*q1
e = 65537
p2, q2 = getPrime(512), getPrime(512)
n2 = p2*q2
print(f'n1 = {n1}')
print(f'n2 = {n2}')
print(f'c1 = {pow(m,e,n2)}')
print(f'c2 = {pow(n1-m,e,n2)}')
# n1 = 52579135273678950581073020233998071974221658902576724000130040488018033110534210901239397446395736563148970863970460542205225993317478251099451639165369081820130823165642873594136020122857712288395352930384057524510346112486008850200845915783772351449146183974239444691330777565342525218070680067550270554767
# n2 = 68210568831848267339414957973218186686176324296418282565773310695862151827108036984694027795077376921170907068110296451176263520249799154781062517066423984526868547296781709439425857993705489037768605485740968600877866332458671029054092942851472208033494968784822459369206497698469167909174346042658361616469
# c1 = 42941712708129054668823891960764339394032538100909746015733801598044118605733969558717842106784388091495719003761324737091667431446354282990525549196642753967283958283202592037329821712755519455155110675327321252333824912095517427885925854391047828862338332559137577789387455868761466777370476884779752953853
# c2 = 62704043252861638895370674827559804184650708692227789532879941590038911799857232898692335429773480889624046167792573885125945511356456073688435911975161053231589019934427151230924004944847291434167067905803180207183209888082275583120633408232749119300200555327883719466349164062163459300518993952046873724005
exp
import sys
def HGCD(a, b):
if 2 * b.degree() <= a.degree() or a.degree() == 1:
return 1, 0, 0, 1
m = a.degree() // 2
a_top, a_bot = a.quo_rem(x^m)
b_top, b_bot = b.quo_rem(x^m)
R00, R01, R10, R11 = HGCD(a_top, b_top)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
q, e = c.quo_rem(d)
d_top, d_bot = d.quo_rem(x^(m // 2))
e_top, e_bot = e.quo_rem(x^(m // 2))
S00, S01, S10, S11 = HGCD(d_top, e_top)
RET00 = S01 * R00 + (S00 - q * S01) * R10
RET01 = S01 * R01 + (S00 - q * S01) * R11
RET10 = S11 * R00 + (S10 - q * S11) * R10
RET11 = S11 * R01 + (S10 - q * S11) * R11
return RET00, RET01, RET10, RET11
def GCD(a, b):
print(a.degree(), b.degree())
q, r = a.quo_rem(b)
if r == 0:
return b
R00, R01, R10, R11 = HGCD(a, b)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
if d == 0:
return c.monic()
q, r = c.quo_rem(d)
if r == 0:
return d
return GCD(d, r)
P.<x> = PolynomialRing(Zmod(n))
sys.setrecursionlimit(500000)
n = 68210568831848267339414957973218186686176324296418282565773310695862151827108036984694027795077376921170907068110296451176263520249799154781062517066423984526868547296781709439425857993705489037768605485740968600877866332458671029054092942851472208033494968784822459369206497698469167909174346042658361616469
n1 = 52579135273678950581073020233998071974221658902576724000130040488018033110534210901239397446395736563148970863970460542205225993317478251099451639165369081820130823165642873594136020122857712288395352930384057524510346112486008850200845915783772351449146183974239444691330777565342525218070680067550270554767
e = 65537
c1 = 42941712708129054668823891960764339394032538100909746015733801598044118605733969558717842106784388091495719003761324737091667431446354282990525549196642753967283958283202592037329821712755519455155110675327321252333824912095517427885925854391047828862338332559137577789387455868761466777370476884779752953853
c2 = 62704043252861638895370674827559804184650708692227789532879941590038911799857232898692335429773480889624046167792573885125945511356456073688435911975161053231589019934427151230924004944847291434167067905803180207183209888082275583120633408232749119300200555327883719466349164062163459300518993952046873724005
f = (x)^e - c1
g = (n1 - x)^e - c2
km = GCD(f,g)
m = -km.monic()[0]
print(m)
print(long_to_bytes(int(m)))
这里, G C D ( f , g ) GCD(f,g) GCD(f,g)得到的是 a x − b m ax-bm ax−bm,需要用 m o n i c ( ) monic() monic()对这个式子进行首一化(前面讲过)
最后附后参考文章: