一、原理
攻击条件 $$当A使用同一公钥对两个具有某种线性关系的M_1与M_2进行加密,并将加密后的消息C_1,C_2发送给B$$ $$M_1,M_2具有某种线性关系:M_1 \equiv f(M_2) \pmod N,其中f为一个线性函数$$ $$在具有较小错误概率下的情况下,其复杂度为O(e \log_2 N)$$
攻击原理 $$已知C_1 \equiv M_1^e \pmod N,且M_1 \equiv f(M_2) \pmod N$$ $$那么M_2是方程f(x)^e \equiv C_1 \pmod N的一个解,即M_2是方程f(x)^e-C_1在模N下的一个根$$ $$且M_2是x^e-C_2在模N意义下的一个根,那么x-M_2同时整除上述两个多项式$$ $$故可以求得两个多项式的最大公因子,若最大公因子是线性的,那么就求到了M_2$$
二、例题
1、 题目 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 from secret import flagfrom 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
解答 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import libnum n=51296885372346449295388453471330409021784141081351581975478435681552082076338697136130122011636685327781785488670769096434920591920054441921039812310126089859349902066456998315283909435249794317277620588552441456327265553018986591779396701680997794937951231970194353001576159809798153970829987274504038146741 a=13256631249970000274738888132534852767685499642889351632072622194777502848070957827974250425805779856662241409663031192870528911932663995606616763982320967 b=12614470377409090738391280373352373943201882741276992121990944593827605866548572392808272414120477304486154096358852845785437999246453926812759725932442170 c1=18617698095122597355752178584860764221736156139844401400942959000560180868595058572264330257490645079792321778926462300410653970722619332098601515399526245808718518153518824404167374361098424325296872587362792839831578589407441739040578339310283844080111189381106274103089079702496168766831316853664552253142 c2=14091361528414093900688440242152327115109256507133728799758289918462970724109343410464537203689727409590796472177295835710571700501895484300979622506298961999001641059179449655629481072402234965831697915939034769804437452528921599125823412464950939837343822566667533463393026895985173157447434429906021792720 e=17 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(libnum.n2s(int(m))) flag{a593591a-3749-cc52-0c27-e897fac2c967}
思路 相关消息攻击,找到最大公因子。用辗转相除法求多项式的最大公因子
1 2 3 4 def gcd(g1, g2): while g2: g1, g2 = g2, g1 % g2 # 计算多项式 GCD return g1.monic() # 返回常数项即 M2
2、NeepuSec CTF 题目 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 from Crypto.Util.number import *from sympy import nextprimeimport gmpy2import randomdef 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
解答1(爆破e) 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 from Crypto.Util.number import* import gmpy2 from tqdm import trange import binascii c = 78543767285872349029076059073458316000847341792088805258173041942425687239313215276670106926320359777962661495032475004417723103701253550583245518206305422982968675291500865382213182669036827898932991063338163290845510339896689210314509493839746410486257998875782496654704288722251878269643040214139429715671 n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543 c1 = 10186066785511829759164194803209819172224966119227668638413350199662683285189286077736537161204019147791799351066849945954518642600518196927152098131117402608793752080104402893792812059620726950782670809837962606250674588612783027976958719051829085903720655233948024280118985875980227528403883475592567727892 c2 = 46182103994299145562022812023438495797686077104477472631494150222038404419414100727667171290098624214113241032861128455086601197239761085752413519627251290509474327611253599768650908336142621210005389246714504358370629231557080301516460985022782887233790302054696967900384601182742759555421864610431428746119 def franklin(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] for e in trange(1,1000): m_=franklin(n,7,c1,c2,1,e) if pow(m_,7,n)==c1: print(e,m_) e=71 m_=129256555243625096140386916253259867206651269142565502540823654159666398099455456877012993395632742360829588042575108302297567291349420390228163587340859 for k in range(1,e): if (e*m_-1) % k ==0: p=(e*m_-1)//k-1 if n%p ==0: q=n//p phi=(p-1)*(q-1) d=gmpy2.invert(e,phi) m=pow(c,d,n) flag=long_to_bytes(m) print(flag) Neepu{Have-a-g00d-day12138}
思路 $$已知c_1 \equiv M^7 \pmod n,c_2 \equiv (M+e)^7 \pmod n,又e \in [1,1000)$$ $$那么可以对e爆破,再由相关信息攻击得到M,又M \equiv s \pmod {p+1},se \equiv 1 \pmod {p+1}$$ $$则eM \equiv 1 \pmod {p+1},则eM-1=k(p+1),又M <p+1,那么k<e$$ $$对k进行爆破,类似dp泄露,求到p和q后RSA解密即可$$
解答2(短填充攻击) 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 def short_pad_attack(c1, c2, e, n): PRxy.< x, y > = PolynomialRing(Zmod(n)) PRx.< xn > = PolynomialRing(Zmod(n)) PRZZ.< xz, yz > = PolynomialRing(Zmod(n)) g1 = x ^ e - c1 g2 = (x + y) ^ e - c2 q1 = g1.change_ring(PRZZ) q2 = g2.change_ring(PRZZ) h = q2.resultant(q1) h = h.univariate_polynomial() h = h.change_ring(PRx).subs(y=xn) h = h.monic() kbits = n.nbits() // (2 * e * e) diff = h.small_roots(X=2 ^ kbits, beta=0.4)[0] # find root < 2^kbits with factor >= n^0.4 return diff def related_message_attack(c1, c2, diff, e, n): PRx.< x > = PolynomialRing(Zmod(n)) g1 = x ^ e - c1 g2 = (x + diff) ^ e - c2 def gcd(g1, g2): while g2: g1, g2 = g2, g1 % g2 return g1.monic() return -gcd(g1, g2)[0] if __name__ == '__main__': c2 = 10186066785511829759164194803209819172224966119227668638413350199662683285189286077736537161204019147791799351066849945954518642600518196927152098131117402608793752080104402893792812059620726950782670809837962606250674588612783027976958719051829085903720655233948024280118985875980227528403883475592567727892 c1 = 46182103994299145562022812023438495797686077104477472631494150222038404419414100727667171290098624214113241032861128455086601197239761085752413519627251290509474327611253599768650908336142621210005389246714504358370629231557080301516460985022782887233790302054696967900384601182742759555421864610431428746119 n = 91995272927105081122659192011056020468305570748555849650309966887236871318156855318666540461669669247866754568189179687694315627673545298267458869140096224628114424176937828378360997230874932015701507629238213240839370628366083111028544554453150572165461450371411341485911677167168492357154684642531577228543 e = 7 diff = short_pad_attack(c1, c2, e, n) print("difference of two messages is %d" % diff) m1 = related_message_attack(c1, c2, diff, e, n) print("m1:", m1) print("m2:", m1 + diff)
学习中待补充
3、HongKe 题目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from Crypto.Util.number import *from secret import flagm = 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
解答(常规做法) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from Crypto.Util.number import * import gmpy2 n1 = 52579135273678950581073020233998071974221658902576724000130040488018033110534210901239397446395736563148970863970460542205225993317478251099451639165369081820130823165642873594136020122857712288395352930384057524510346112486008850200845915783772351449146183974239444691330777565342525218070680067550270554767 n2 = 68210568831848267339414957973218186686176324296418282565773310695862151827108036984694027795077376921170907068110296451176263520249799154781062517066423984526868547296781709439425857993705489037768605485740968600877866332458671029054092942851472208033494968784822459369206497698469167909174346042658361616469 c1 = 42941712708129054668823891960764339394032538100909746015733801598044118605733969558717842106784388091495719003761324737091667431446354282990525549196642753967283958283202592037329821712755519455155110675327321252333824912095517427885925854391047828862338332559137577789387455868761466777370476884779752953853 c2 = 62704043252861638895370674827559804184650708692227789532879941590038911799857232898692335429773480889624046167792573885125945511356456073688435911975161053231589019934427151230924004944847291434167067905803180207183209888082275583120633408232749119300200555327883719466349164062163459300518993952046873724005 e=65537 def Franklin(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=Franklin(n2,e,c1,c2,-1,n1) flag=long_to_bytes(int(m)) print(flag)
这里的e很大,常规方法会非常非常慢,一两个小时能出
解答(half-gcd) 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 from Crypto.Util.number import * 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) sys.setrecursionlimit(500000) e = 65537 n1 = 52579135273678950581073020233998071974221658902576724000130040488018033110534210901239397446395736563148970863970460542205225993317478251099451639165369081820130823165642873594136020122857712288395352930384057524510346112486008850200845915783772351449146183974239444691330777565342525218070680067550270554767 n2 = 68210568831848267339414957973218186686176324296418282565773310695862151827108036984694027795077376921170907068110296451176263520249799154781062517066423984526868547296781709439425857993705489037768605485740968600877866332458671029054092942851472208033494968784822459369206497698469167909174346042658361616469 c1 = 42941712708129054668823891960764339394032538100909746015733801598044118605733969558717842106784388091495719003761324737091667431446354282990525549196642753967283958283202592037329821712755519455155110675327321252333824912095517427885925854391047828862338332559137577789387455868761466777370476884779752953853 c2 = 62704043252861638895370674827559804184650708692227789532879941590038911799857232898692335429773480889624046167792573885125945511356456073688435911975161053231589019934427151230924004944847291434167067905803180207183209888082275583120633408232749119300200555327883719466349164062163459300518993952046873724005 R.<x> = PolynomialRing(Zmod(n2)) f = x^e - c1 g = (n1 - x)^e - c2 res = GCD(f,g) print(long_to_bytes(int(-res.monic().coefficients()[0]))) CnHongKe{Fr4nkl1n_R31ter_4nd_gcD}
三、Half gcd 1、从gcd到half gcd $$对于之前提到的多项式gcd,其实是进行了这样一个操作:$$ $$r_{-2}=A(x),r_{-1}=B(x)$$ $$r_i=r_{i-2}-q_ir_{i-1},其中q_i=r_{i-2} // r_{i-1}$$ $$\cdots$$ $$直到r_k=0$$ $$考虑将其写成一个线性变换:$$ $$\begin {pmatrix} 0&1 \ 1&{-q_i} \end {pmatrix} \begin {pmatrix} r_{i-2} \ r_{i-1} \end {pmatrix} =\begin {pmatrix} r_{i-1} \ r_i \end {pmatrix}$$ $$引入一个记号:$$ $$Q_i=\begin {pmatrix} q_i&1 \ 1&0 \end {pmatrix} \qquad 则Q_i^{-1}= \begin {pmatrix} 0&1 \ 1&{-q_i} \end {pmatrix}$$ 那么: $$\qquad Q_k^{-1} \cdots Q_1^{-1}Q_0^{-1} \begin {pmatrix} A(x) \ B(x) \end {pmatrix}= \begin {pmatrix} gcd(A(x),B(x)) \ 0 \end {pmatrix}$$ 记: $$Q_k^{-1} \cdots Q_1^{-1}Q_0^{-1}=Q^{-1},那么Q^{-1} \begin {pmatrix} A \ B \end {pmatrix}=\begin {pmatrix} C \ D \end {pmatrix}$$
我们规定:这个Q必须让C和D满足: $$degC \geq \lceil {\frac{degA}{2}} \rceil >degD$$
$$也就是说:C的次数至少是A的次数的一半,D的次数严格小于A的次数的一半$$ $$那么D的次数就被限制在一个比较小的范围内$$ $$已知degC \geq 阈值 > degD,再做一次多项式带余除法:$$ $$C=G \cdot D+E \qquad(degE<degD)$$ $$于是得到了一对新的多项式(D,E),最高次数小于\lceil \frac{degA}{2} \rceil -1,规模减半$$ $$那么我们可以递归的使用half-gcd来处理(D,E),得到另一矩阵。$$ $$最后将这些矩阵相乘即可得到目标矩阵$$
2、脚本 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 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) sys.setrecursionlimit(500000) n = ? PR.<x> = PolynomialRing(Zmod(n)) e = ? c = ? f = ? g = ? print(GCD(f, g))
参考 https://blog.csdn.net/XiongSiqi_blog/article/details/130978226 https://www.cnblogs.com/whx1003/p/16217087.html https://github.com/rkm0959/Implementations/blob/main/Half_GCD/code.sage 感谢
应无所住,而生其心