1. 程式人生 > 實用技巧 >Coppersmith 's method 學習

Coppersmith 's method 學習

這幾天發現一道學習coppersmith的題目,一共6個挑戰,這裡記錄一下自己的解題思路方便以後再來回看。
題目地址:https://github.com/CTFTraining/qwb_2019_crypto_copperstudy

challange 1

題目一開始給了n,e,c和部分m的值(高位440位)

[+]n=13112061820685643239663831166928327119579425830632458568801544406506769461279590962772340249183569437559394200635526183698604582385769381159563710823689417274479549627596095398621182995891454516953722025068926293512505383125227579169778946631369961753587856344582257683672313230378603324005337788913902434023431887061454368566100747618582590270385918204656156089053519709536001906964008635708510672550219546894006091483520355436091053866312718431318498783637712773878423777467316605865516248176248780637132615807886272029843770186833425792049108187487338237850806203728217374848799250419859646871057096297020670904211 
\n[+]e=3
\n[+]m=random.getrandbits(512)
\n[+]c=pow(m,e,n)=15987554724003100295326076036413163634398600947695096857803937998969441763014731720375196104010794555868069024393647966040593258267888463732184495020709457560043050577198988363754703741636088089472488971050324654162166657678376557110492703712286306868843728466224887550827162442026262163340935333721705267432790268517
\n[+]((m>>72)<<72)=2519188594271759205757864486097605540135407501571078627238849443561219057751843170540261842677239681908736

假設不知道的那部分為x,那麼就有等式
\(f=(m+x)^e-c(mod N)\)該等式存在\(f(x_{0})\equiv 0(mod N)\)其中x0就是我們所想要求的值,借用github上的工具:https://github.com/mimoo/RSA-and-LLL-attacks
修改後關鍵程式碼如下:

# RSA gen options (for the demo)
  # size of the modulus
Kbits = 72
N=13112061820685643239663831166928327119579425830632458568801544406506769461279590962772340249183569437559394200635526183698604582385769381159563710823689417274479549627596095398621182995891454516953722025068926293512505383125227579169778946631369961753587856344582257683672313230378603324005337788913902434023431887061454368566100747618582590270385918204656156089053519709536001906964008635708510672550219546894006091483520355436091053866312718431318498783637712773878423777467316605865516248176248780637132615807886272029843770186833425792049108187487338237850806203728217374848799250419859646871057096297020670904211
length_N=N.nbits()
e = 3
M=2519188594271759205757864486097605540135407501571078627238849443561219057751843170540261842677239681908736
# RSA gen (for the demo)
ZmodN = Zmod(N);

# Create problem (for the demo)
K=11111
c=15987554724003100295326076036413163634398600947695096857803937998969441763014731720375196104010794555868069024393647966040593258267888463732184495020709457560043050577198988363754703741636088089472488971050324654162166657678376557110492703712286306868843728466224887550827162442026262163340935333721705267432790268517
# Problem to equation (default)
P.<x> = PolynomialRing(ZmodN) #, implementation='NTL')

pol =(M+x)^e-c
dd = pol.degree()

# Tweak those
beta = 1                                # b = N
epsilon = beta / 7                      # <= beta / 7
mm = ceil(beta**2 / (dd * epsilon))     # optimized value
tt = floor(dd * mm * ((1/beta) - 1))    # optimized value
XX =2^Kbits #ceil(N**((beta**2/dd) - epsilon))  # optimized value

執行後就可以得到未知的那72bit位的數了

challange 2

和上面題目原理差不多,這裡我們設\(f=q_0+x,f(x_0)\equiv 0 mod(q),x_0+q_0=q\)關鍵程式碼如下

# RSA gen
N=12784625729032789592766625203074018101354917751492952685083808825504221816847310910447532133616954262271205877651255598995305639194329607493047941212754523879402744065076183778452640602625242851184095546100200565113016690161053808950384458996881574266573992526357954507491397978278604102524731393059303476350167738237822647246425836482533150025923051544431330502522043833872580483142594571802189321599016725741260254170793393777293145010525686561904427613648184843619301241414264343057368192416551134404100386155751297424616254697041043851852081071306219462991969849123668248321130382231769250865190227630009181759219
q=97522826022187678545924975588711975512906538181361325096919121233043973599759518562689050415761485716705615149641768982838255403594331293651224395590747133152128042950062103156564440155088882592644046069208405360324372057140890317518802130081198060093576841538008960560391380395697098964411821716664506908672
length_N=N.nbits();
e=65537
# qbar is q + [hidden_size_random]
hidden = 128;
#diff = ZZ.random_element(0, 2^hidden-1)
#qbar = q + diff; 
F.<x> = PolynomialRing(Zmod(N), implementation='NTL'); 
pol = x + q
dd = pol.degree()
# PLAY WITH THOSE:
beta = 0.4                      # we should have q >= N^beta
epsilon = beta / 7                     # <= beta/7
mm = ceil(beta**2 / (dd * epsilon))    # optimized
tt = floor(dd * mm * ((1/beta) - 1))   # optimized
#XX = ceil(N**((beta**2/dd) - epsilon)) +2^hidden # we should have |diff| < X
XX=2^hidden
# Coppersmith
start_time = time.time()
roots = coppersmith_howgrave_univariate(pol, N, beta, mm, tt, XX)
print 'root:',roots
# output
print "\n# Solutions"
print "we want to find:" #,qbar-q
print "we found:", q+roots[0]
print("in: %s seconds " % (time.time() - start_time))
print "chose: ",XX,XX-2^hidden

challange 3

對於私鑰洩漏的問題,我找到一片文章,參考該文章寫出攻擊程式碼,首先我們可以由題目看出這裡的e很小,這樣也就提供了一種攻擊的可能,由RSA的原理我們可以得出以下等式:
假設我們知道d0,d=d1M+d0

\[ed=k\phi(N)+1 \]

\[k={{ed-1}\over\phi(N)}<e{d\over\phi(N)}<e (e\in [3,N^{\alpha}],0<\alpha<1/2 \]

由此可以構建公式如下:

\[ed-k\phi(N)=ed-k(N-s+1)當s=p+{p\over N}有: \]

\[ed_0\equiv 1+k(N-x-{N\over x}+1)(mod M) \]

\[kx^2+(ed_0-k(N+1)-1)x+kN\equiv 0(mod M) \]

將k從[1,e]遍歷進行求解,這裡的解\(x_0\equiv p(modM)\)
攻擊程式碼參考

def attack(d0,n,e,c):
        X=var('X')
        for k in range(1,e+1):
                res=solve_mod([e*d0*X-k*X*(n-X+1)+k*n == X],2^512)
                print 'res:',res
                for x in res:
                        p=ZZ(x[0])
                        nbits=1024
                        PR.<z> =PolynomialRing(Zmod(n))
                        f=(2^512*z+p)
                        f=f.monic()
                        r=f.small_roots(X=1,beta=0.3)
                        if r:
                                x0=r[0]
                                print'p:',p
                                p=gcd(2^512*x0+p,n)
                                print 'x:',r
                                print '+x[0]:',x0
                                print 'i find it',p
                                return 0
d0=787673996295376297668171075170955852109814939442242049800811601753001897317556022653997651874897208487913321031340711138331360350633965420642045383644955
n=92896523979616431783569762645945918751162321185159790302085768095763248357146198882641160678623069857011832929179987623492267852304178894461486295864091871341339490870689110279720283415976342208476126414933914026436666789270209690168581379143120688241413470569887426810705898518783625903350928784794371176183
e=3
c=56164378185049402404287763972280630295410174183649054805947329504892979921131852321281317326306506444145699012788547718091371389698969718830761120076359634262880912417797038049510647237337251037070369278596191506725812511682495575589039521646062521091457438869068866365907962691742604895495670783101319608530

attack(d0,n,e,c)

challenge 4

這是一道廣播攻擊的題目,原理在以前提到過,網上也有一大堆參考資料就不再過多闡述,參考程式碼如下:

import hashlib
from Crypto.Util.number import long_to_bytes,bytes_to_long
import gmpy2
n1=78642188663937191491235684351005990853149481644703243255021321296087539054265733392095095639539412823093600710316645130404423641473150336492175402885270861906530337207734106926328737198871118125840680572148601743121884788919989184318198417654263598170932154428514561079675550090698019678767738203477097731989
c1=23419685303892339080979695469481275906709035609088426118328601771163101123641599051556995351678670765521269546319724616458499631461037359417701720430452076029312714313804716888119910334476982840024696320503747736428099717113471541651211596481005191146454458591558743268791485623924245960696651150688621664860
n2=98174485544103863705821086588292917749386955237408645745685476234349659452606822650329076955303471252833860010724515777826660887118742978051231030080666542833950748806944312437614585352818344599399156268450521239843157288915059003487783576003027303399985723834248634230998110618288843582573006048070816520647
c2=72080679612442543693944655041130370753964497034378634203383617624269927191363529233872659451561571441107920350406295389613006330637565645758727103723546610079332161151567096389071050158035757745766399510575237344950873632114050632573903701015749830874081198250578516967517980592506626547273178363503100507676
n3=91638855323231795590642755267985988356764327384001022396221901964430032527111968159623063760057482761918901490239790230176524505469897183382928646349163030620342744192731246392941227433195249399795012672172947919435254998997253131826888070173526892674308708289629739522194864912899817994807268945141349669311
c3=22149989692509889061584875630258740744292355239822482581889060656197919681655781672277545701325284646570773490123892626601106871432216449814891757715588851851459306683123591338089745675044763551335899599807235257516935037356212345033087798267959242561085752109746935300735969972249665700075907145744305255616
M1=n2*n3
M2=n1*n3
M3=n1*n2
M_1=gmpy2.invert(M1,n1)
M_2=gmpy2.invert(M2,n2)
M_3=gmpy2.invert(M3,n3)
C=(c1*M1*M_1+c2*M2*M_2+c3*M3*M_3)%(n1*n2*n3)
m=gmpy2.iroot(C,3)[0]
print(long_to_bytes(m))

challenge 5

這道題目(我給的github的地址上面題目好像有些問題,e=3而不是7)採用的是Franklin-Reiter attack的原理,具體ctfwiki上面有講解,主要是得出兩個多項式的最大公因子如果他是線性的那麼其就是(x-M1),程式參考:https://github.com/ValarDragon/CTF-Crypto/blob/master/RSA/FranklinReiter.sage
程式碼如下:

# If two messages differ only by a known fixed difference between the two messages
# and are RSA encrypted under the same RSA modulus N
# then it is possible to recover both of them.

# Inputs are modulus, known difference, ciphertext 1, ciphertext2.
# Ciphertext 1 corresponds to smaller of the two plaintexts. (The one without the fixed difference added to it)
def franklinReiter(n,e,r,c1,c2):
    R.<X> = Zmod(n)[]
    f1 = X^e - c1
    f2 = (X + r)^e - c2
    # coefficient 0 = -m, which is what we wanted!
    return Integer(n-(compositeModulusGCD(f1,f2)).coefficients()[0])

  # GCD is not implemented for rings over composite modulus in Sage
  # so we do our own implementation. Its the exact same as standard GCD, but with
  # the polynomials monic representation
def compositeModulusGCD(a, b):
    if(b == 0):
        return a.monic()
    else:
        return compositeModulusGCD(b, a % b)

def CoppersmithShortPadAttack(e,n,C1,C2,eps=1/30):
    """
    Coppersmith's Shortpad attack!
    Figured out from: https://en.wikipedia.org/wiki/Coppersmith's_attack#Coppersmith.E2.80.99s_short-pad_attack
    """
    import binascii
    P.<x,y> = PolynomialRing(ZZ)
    ZmodN = Zmod(n)
    g1 = x^e - C1
    g2 = (x+y)^e - C2
    res = g1.resultant(g2)
    P.<y> = PolynomialRing(ZmodN)
    # Convert Multivariate Polynomial Ring to Univariate Polynomial Ring
    rres = 0
    for i in range(len(res.coefficients())):
        rres += res.coefficients()[i]*(y^(res.exponents()[i][1]))

    diff = rres.small_roots(epsilon=eps)
    recoveredM1 = franklinReiter(n,e,diff[0],C1,C2)
    print(recoveredM1)
    print("Message is the following hex, but potentially missing some zeroes in the binary from the right end")
    print(hex(recoveredM1))
    print("Message is one of:")
    for i in range(8):
        msg = hex(Integer(recoveredM1*pow(2,i)))
        if(len(msg)%2 == 1):
            msg = '0' + msg
        if(msg[:2]=='0x'):
            msg = msg[:2]
        print(binascii.unhexlify(msg))

def testCoppersmithShortPadAttack(eps=1/25):
    from Crypto.PublicKey import RSA
    import random
    import math
    import binascii
    M = "flag{This_Msg_Is_2_1337}"
    M = int(binascii.hexlify(M),16)
    e = 3
    nBitSize =  8192
    key = RSA.generate(nBitSize)
    #Give a bit of room, otherwhise the epsilon has to be tiny, and small roots will take forever
    m = int(math.floor(nBitSize/(e*e))) - 400
    assert (m < nBitSize - len(bin(M)[2:]))
    r1 = random.randint(1,pow(2,m))
    r2 = random.randint(r1,pow(2,m))
    M1 = pow(2,m)*M + r1
    M2 = pow(2,m)*M + r2
    C1 = Integer(pow(M1,e,key.n))
    C2 = Integer(pow(M2,e,key.n))
    CoppersmithShortPadAttack(e,key.n,C1,C2,eps)

def testFranklinReiter():
    p = random_prime(2^512)
    q = random_prime(2^512)
    n = p * q # 1024-bit modulus
    e = 7

    m = randint(0, n) # some message we want to recover
    r = randint(0, n) # random padding

    c1 = pow(m + 0, e, n)
    c2 = pow(m + r, e, n)
    print(m)
    recoveredM = franklinReiter(n,e,r,c1,c2)
    print(recoveredM)
    assert recoveredM==m
    print("They are equal!")
    return True
n=113604829563460357756722229849309932731534576966155520277171862442445354404910882358287832757024693652075211204635679309777620586814014894544893424988818766425089667672311645586528776360047956843961901352792631908859388801090108188344342619580661377758180391734771694803991493164412644148805229529911069578061
c1=112992730284209629010217336632593897028023711212853788739137950706145189880318698604512926758021533447981943498594790549326550460216939216988828130624120379925895123186121819609415184887470233938291227816332249857236198616538782622327476603338806349004620909717360739157545735826670038169284252348037995399308
c2=112992730284209629010217336632593897028023711212853788739137950706145189880318698604512926758021552486915464025361447529153776277710423467951041523831865232164370127602772602643378592695459331174613894578701940837730590029577336924367384969935652616989527416027725713616493815764725131271563545176286794438175
e=3
m=franklinReiter(n,e,1,c1,c2)
print 'i get it',m
if pow(m,e,n)==c1:
        print 'it is turth'

challenge 6

題目一上來就點明d<N^0.270,基本就可以考慮boneh_durfee的方法,上面提到的工具裡面有程式碼,這裡我們修改X,Y保證如下方程成立: