from Crypto.Util.number import * m = b'hello world' m = bytes_to_long(m) p = getPrime(256) e = 65537 c = pow(m, e, p) print(f"p={p}, c={c}") # p=104572425491036779976425338195598909418460679281450078773956190724671461860829, c=15413370856595181721630593414126186156343530569374585851966215761918789311844
from gmpy2.gmpy2 import iroot from Crypto.Util.number import * n = e = c = for i inrange(100000): t = iroot(i * n + c, e) # 可开e次方时为1,否则为0 if t[1] == 1: print(long_to_bytes(t[0])) break
例题
1 2 3
n = 10888751337932558679268839254528888070769213269691871364279830513893837690735136476085167796992556016532860022833558342573454036339582519895539110327482234861870963870144864609120375793020750736090740376786289878349313047032806974605398302398698622431086259032473375162446051603492310000290666366063094482985737032132318650015539702912720882013509099961316767073167848437729826084449943115059234376990825162006299979071912964494228966947974497569783878833130690399504361180345909411669130822346252539746722020515514544334793717997364522192699435604525968953070151642912274210943050922313389271251805397541777241902027 e = 3 c = 2449457955338174702664398437699732241330055959255401949300755756893329242892325068765174475595370736008843435168081093064803408113260941928784442707977000585466461075146434876354981528996602615111767938231799146073229307631775810351487333
共模攻击
c1≡me1(modn),c2≡me2(modn)
如果gcd(e1,e2)=1,拓展欧几里得公式可得x⋅e1+y⋅e2=1
c1x⋅c2y≡mx⋅e1⋅my⋅e2≡mx⋅e1+y⋅e2≡m(modn)
1 2 3 4 5 6 7 8 9
import gmpy2 n = c1 = c2 = e1 = e2 = _, x, y = gmpy2.gcdext(e1, e2) m = pow(c1, x, n) * pow(c2, y, n) % n print(m.to_bytes((m.bit_length() + 7) // 8, 'big'))
from Crypto.Util.number import * from gmpy2 import iroot defrsa_not_gcd(p, q, e, c): n = p*q phi = (p-1)*(q-1) g = GCD(e, phi) c = pow(c, inverse(e//g, phi), n) m = iroot(c, g)[0] return m
例题
1 2 3 4 5
p = 134261118796789547851478407090640074022214132682000430136383795981942884853000826171189906102866323044078348933419038543719361923320694974970600426450755845839235949167391987970330836004768360774676424958554946699767582105556239177450470656065560178592346659948800891455240736405480828554486592172443394370831 q = 147847444534152128997546931602292266094740889347154192420554904651813340915744328104100065373294346723964356736436709934871741161328286944150242733445542228293036404657556168844723521815836689387184856871091025434896710605688594847400051686361372872763001355411405782508020591933546964183881743133374126947753 n = 19850163314401552502654477751795889962324360064924594948231168092741951675262933573691070993863763290962945190372400262526595224437463969238332927564085237271719298626877917792595603744433881409963046292095205686879015029586659384866719514948181682427744555313382838805740723664050846950001916332631397606277703888492927635867870538709596993987439225247816137975156657119509372023083507772730332482775258444611462771095896380644997011341265021719189098262072756342069189262188127428079017418048118345180074280858160934483114966968365184788420091050939327341754449300121493187658865378182447547202838325648863844192743 c = 13913396366755010607043477552577268277928241319101215381662331498046080625902831202486646020767568921881185124894960242867254162927605416228460108399087406989258037017639619195506711090012877454131383568832750606102901110782045529267940504471322847364808094790662696785470594892244716137203781890284216874035486302506042263453255580475380742959201314003788553692977914357996982118328587119124144181290753389394149235381045389696841471483947310663329993873046123134587149661347999774958105091103806375702387084149309542351541021140111048408248121408401601979108510758891595550054699719801708646232427198902271953673874 e = 28
from Crypto.Util.number import * defdp_leak(e, dp, n, c): for k inrange(1, e): phi = (e*dp - 1)//k p = GCD(phi + 1, n) if p != 1: q = n//p phi = (p-1)*(q-1) d = inverse(e, phi) m = pow(c, d, n) return m
''' e = 19999999999999999999999999999999999999999999999999999999999999999999999999997 n = 7195506839435218889565105541674965483194164483027741709706696451513641438345177472634371310250998546706062462270851552911697354605048972081656931006641878545036542923897114647393564522132057589249800431430995780074871171268958056358251827104531889348948541240686274977093185746573748206617663459128090693743840574459752890533065398493485714768878646999590143805843490432318539260302521682823958290340460403361801534822098048095280034600065200137857346827560676300256938953222718633375808719441534702981763523406056651752914141143665893462943582116716812913462656214604870428310720751101481210148746546806273965485289 dp = 34961801811050613471700883525108632060492526395401334090302835931304663757529660746363964830407055340550990256271716811099606849841913560556222756478612800702209651907866303152581107449312861896692310607989826809665245295483724533775337076019316812377921373194504440845718347150919782506437242366281376701299 c = 3014636373048664939954772778404195986026862165799593915685719641505606570670923436003664110094703916031096486273947905494103538805486521321522443488182065845367347589071783679908494724693530639371358965655992560909299314626568439587755874253430614726720724608456333450258184012429367293386944954388615812902809362326474915645899324083994448117282677622943580354006160302366855350193039875335543211982510928721395526768129547143054319585071252781483346116972611571317425047748862917945459911485505200762492537496489429730213393936533514665994680707861503489288913062785427211743828345144957201996243444547648085230048 '''
from gmpy2 import gcd a = 2 n = 2 N = whileTrue: a = pow(a, n, N) res = gcd(a - 1, N) if res != 1and res != N: q = N // res print("p =", res) print("q =", q) break n += 1
from random import choice from Crypto.Util.number import isPrime, sieve_base as primes from flag import flag
defmyPrime(bits): whileTrue: n = 2 while n.bit_length() < bits: n *= choice(primes) if isPrime(n + 1): return n + 1
e = 0x10001 m = int.from_bytes(flag.encode(), 'big') p = myPrime(2048) q = getPrime(2048) n = p * q c = pow(m, e, n)
# n = 1224542620373200232525165378018470774334801515191193204875465445916504222883935188890019876845076388385357911730689547124547346252951158814249284724565588433721828377715469374541007756509231399263095022024229078845538543233785364809917406108015271780070196140628158427541531563472532516237632553353655535922926443707617182025475004547531104052989085765070550150028833424395972178427807901747932614235844448614858629761183210600428438018388051958214596857405813088470933109693499438012040822262549119751099008671892966082341548512112435591881692782766559736840448702039918465573051130405935280702181505538733234675792472428666968900055706926735800561218167237812066851519973807203332801575980055838563085817664973968944323258406789203078387708964307931318918136664885818917720073433998810127482159223895026085726623747340692196977140382318293090736558135980651252533606603312148824142669800602887109353065489282386215179238458743567166284295855288783740314247952124965482197632971993708775190564519250754150756867653033527903848903210074426177258586450311109023467944412194124015505951966140443860862968311560843608415723549525497729679097936310538451467530605937684408079363677707513923579164067808729408365886209340192468399685190639 # c = 145742860621666495489510776707734134231023214235535481878205099324276369445463746101469487674333600296204530932386373415987357363515200117271393133347844479863240936801112306080456942844796779477817786176831015954410967693647534326733641573842953783193563678040093734579772976410574013857063137696465850300484753282472377882118892522844694078667622111244886303620349388556315704648609353412177123230438077637042880490566244740468503369707900343076369151796123461132932226563486870411965536062339169788331659119981901553536009275158600580698576110294775989992794065611215170351808698605911258789407992833170968332058255364527244293283228694886707241979238145252395651417561576433516407782575454294499521347378058366557950770592472271985004818847838711060048422015207674862177145761946560579360220239667890707135827136815780729363013864130107808776517514214310689477005999830284272130148939734935547341627208913181919190392205389452185597444280635342938046191904062547803917870268485346888653569349729643793041018550170090471310374856687407102762116819004790791936814214507908374380597027347007448114684844276041116955473180015221164545212550832233007714133699817366745648092776901013502840540012912660742166994968977400188176557657864
from Crypto.Util.number import isPrime, bytes_to_long, sieve_base from random import choice from secret import flag m=bytes_to_long(flag) defuniPrime(bits): whileTrue: n = 2 while n.bit_length() < bits: n *= choice(sieve_base) if isPrime(n + 1): return n + 1 p=uniPrime(512) q=uniPrime(512) n=p*q e= 196608 c=pow(m,e,n) print("n=",n) print("c=",c) ''' n= 3326716005321175474866311915397401254111950808705576293932345690533263108414883877530294339294274914837424580618375346509555627578734883357652996005817766370804842161603027636393776079113035745495508839749006773483720698066943577445977551268093247748313691392265332970992500440422951173889419377779135952537088733 c= 2709336316075650177079376244796188132561250459751152184677022745551914544884517324887652368450635995644019212878543745475885906864265559139379903049221765159852922264140740839538366147411533242116915892792672736321879694956051586399594206293685750573633107354109784921229088063124404073840557026747056910514218246 '''
Schmidt-Samoa
N=p2⋅q,d=invert(N,φ(pq)),公钥对(N,d)
加密
C=mN(modN)
解密
m=Cd(modpq)
d和N在模φ(pq)下互为逆元,故Nd≡1(modφ(pq))
任取一个数字a
aNd≡amodpq⇒aNd−a=k⋅pqpq=gcd(and−a,N)
exp
1 2 3 4 5 6 7 8
from Crypto.Util.number import * from gmpy2 import iroot
defsolve(N, d, c): a = pow(2, N*d, N) n = GCD(a - 2, N) m = pow(c, d, n) return m
from secret import flag from Crypto.Util.number import *
p = getPrime(1024) q = getPrime(1024)
N = p*p*q
d= inverse(N, (p-1)*(q-1)//GCD(p-1, q-1))
m = bytes_to_long(flag)
c = pow(m, N, N)
print('c =', c) print('N =', N) print('d =', d)
# c = 1653396627113549535760516503668455111392369905404419847336187180051939350514408518095369852411718553340156505246372037811032919080426885042549723125598742783778413642221563616358386699697645814225855089454045984443096447166740882693228043505960011332616740785976743150624114653594631779427044055729185392854961786323215146318588164139423925400772680226861699990332420246447180631417523181196631188540323779487858453719444807515638025771586275969579201806909799448813112034867089866513864971414742370516244653259347267231436131850871346106316007958256749016599758599549180907260093080500469394473142003147643172770078092713912200110043214435078277125844112816260967490086038358669788006182833272351526796228536135638071670829206746835346784997437044707950580087067666459222916040902038574157577881880027391425763503693184264104932693985833980182986816664377018507487697769866530103927375926578569947076633923873193100147751463 # N = 1768427447158131856514034889456397424027937796617829756303525705316152314769129050888899742667986532346611229157207778487065194513722005516611969754197481310330149721054855689646133721600838194741123290410384315980339516947257172981002480414254023253269098539962527834174781356657779988761754582343096332391763560921491414520707112852896782970123018263505426447126195645371941116395659369152654368118569516482251442513192892626222576419747048343942947570016045016127917578272819812760632788343321742583353340158009324794626006731057267603803701663256706597904789047060978427573361035171008822467120148227698893238773305320215769410594974360573727150122036666987718934166622785421464647946084162895084248352643721808444370307254417501852264572985908550839933862563001186477021313236113690793843893640190378131373214104044465633483953616402680853776480712599669132572907096151664916118185486737463253559093537311036517461749439 # d = 20650646933118544225095544552373007455928574480175801658168105227037950105642248948645762488881219576174131624593293487325329703919313156659700002234392400636474610143032745113473842675857323774566945229148664969659797779146488402588937762391470971617163496433008501858907585683428652637958844902909796849080799141999490231877378863244093900363251415972834146031490928923962271054053278056347181254936750536280638321211545167520935870220829786490686826062142415755063724639110568511969041175019898031990455911525941036727091961083201123910761290998968240338217895275414072475701909497518616112236380389851984377079
n=p*q*r #n=85492663786275292159831603391083876175149354309327673008716627650718160585639723100793347534649628330416631255660901307533909900431413447524262332232659153047067908693481947121069070451562822417357656432171870951184673132554213690123308042697361969986360375060954702920656364144154145812838558365334172935931441424096270206140691814662318562696925767991937369782627908408239087358033165410020690152067715711112732252038588432896758405898709010342467882264362733 c=pow(flag,e,n) #e=0x1001 #c=75700883021669577739329316795450706204502635802310731477156998834710820770245219468703245302009998932067080383977560299708060476222089630209972629755965140317526034680452483360917378812244365884527186056341888615564335560765053550155758362271622330017433403027261127561225585912484777829588501213961110690451987625502701331485141639684356427316905122995759825241133872734362716041819819948645662803292418802204430874521342108413623635150475963121220095236776428 #so,what is the flag?
N = 0xb8af3d3afb893a602de4afe2a29d7615075d1e570f8bad8ebbe9b5b9076594cf06b6e7b30905b6420e950043380ea746f0a14dae34469aa723e946e484a58bcd92d1039105871ffd63ffe64534b7d7f8d84b4a569723f7a833e6daf5e182d658655f739a4e37bd9f4a44aff6ca0255cda5313c3048f56eed5b21dc8d88bf5a8f8379eac83d8523e484fa6ae8dbcb239e65d3777829a6903d779cd2498b255fcf275e5f49471f35992435ee7cade98c8e82a8beb5ce1749349caa16759afc4e799edb12d299374d748a9e3c82e1cc983cdf9daec0a2739dadcc0982c1e7e492139cbff18c5d44529407edfd8e75743d2f51ce2b58573fea6fbd4fe25154b9964d e = 0x9ab58dbc8049b574c361573955f08ea69f97ecf37400f9626d8f5ac55ca087165ce5e1f459ef6fa5f158cc8e75cb400a7473e89dd38922ead221b33bc33d6d716fb0e4e127b0fc18a197daf856a7062b49fba7a86e3a138956af04f481b7a7d481994aeebc2672e500f3f6d8c581268c2cfad4845158f79c2ef28f242f4fa8f6e573b8723a752d96169c9d885ada59cdeb6dbe932de86a019a7e8fc8aeb07748cfb272bd36d94fe83351252187c2e0bc58bb7a0a0af154b63397e6c68af4314601e29b07caed301b6831cf34caa579eb42a8c8bf69898d04b495174b5d7de0f20cf2b8fc55ed35c6ad157d3e7009f16d6b61786ee40583850e67af13e9d25be3 c = 0x3f984ff5244f1836ed69361f29905ca1ae6b3dcf249133c398d7762f5e277919174694293989144c9d25e940d2f66058b2289c75d1b8d0729f9a7c4564404a5fd4313675f85f31b47156068878e236c5635156b0fa21e24346c2041ae42423078577a1413f41375a4d49296ab17910ae214b45155c4570f95ca874ccae9fa80433a1ab453cbb28d780c2f1f4dc7071c93aff3924d76c5b4068a0371dff82531313f281a8acadaa2bd5078d3ddcefcb981f37ff9b8b14c7d9bf1accffe7857160982a2c7d9ee01d3e82265eec9c7401ecc7f02581fd0d912684f42d1b71df87a1ca51515aab4e58fab4da96e154ea6cdfb573a71d81b2ea4a080a1066e1bc3474
Coppersmith攻击
Coppersmith proved that any root x0 with ∣x0∣<N1/δ can be found in polynomial time, where δ=degp. The technique consists in building a lattice that contains the solutions of the modular polynomial equation; all small solutions are shown be belong to an hyperplane of the lattice; an equation of this hyperplane is obtained by considering that last vector of an LLL−reduced basis; this gives a polynomial h(x) such that h(x0)=0 over the integers, from which one can recover x0. The method can be extended to handle multivariate modular polynomial equations, but the extension is heuristic only.
# Sage p>>128<<128类型 t = p >> 128 << 128 kbits = 128 R.<x> = PolynomialRing(Zmod(n)) f = x + t x0 = f.small_roots(X=2^kbits, beta=0.4)[0] p = p + int(x0)
leak = '2011133132443111302000224204142244403203442000141102312242343143241244243020003333022112141220422134444214010012' n = 85988668134257353631742597258304937106964673395852009846703777410474172989069717247424903079500594820235304351355706519069516847244761609583338251489134035212061654870087550317540291994559481862615812258493738064606592165529948648774081655902831715928483206013332330998262897765489820121129058926463847702821 e = 65537 c = 64708526479058278743788046708923650158905888858865427385501446781738669889375403360886995849554813207230509920789341593771929287415439407977283018525484281064769128358863513387658744063469874845446480637925790150835186431234289848506337341595817156444941964510251032210939739594241869190746437858135599624562
defDecimal_conversion(num): if num == 0: return'0' digits = [] while num: digits.append(str(num % 5)) num //= 5 return''.join(reversed(digits))
defcheck(leak, de): if de < int(2): for i inrange(10): check(leak + str(i), de + 1) elif de == 2: temp = Integer(int(leak)) kbits = 512 - temp.nbits()
temp = temp << kbits R.<x>= PolynomialRing(Zmod(n)) f = x + temp res = f.small_roots(X=2 ^ kbits, beta=0.4, epsilon=0.01) if res: print(res[0] + temp) # 12170789707638469557363249767228204966074269830454332436369564884472290413677820876733675261757160662428920138919536054003455470370040518528641001253487453 # SICTF{1d213ffc-d5dc-42d0-b206-e9a0c1a3cb69}
m高位
me−c=(mh+x)e−c=0
不止可以恢复p,甚至可以用来已知m高位恢复m,构造的式子f则需要变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
from Crypto.Util.number import getPrime,bytes_to_long,long_to_bytes from random import randint from secret import flag
p = getPrime(1024) q = getPrime(1024) n = p*q print(n)
m = bytes_to_long(long_to_bytes(randint(0,30))*208+flag) assert(m.bit_length()==2044) print((m>>315)<<315) c = pow(m,3,n) print(c)
exp
1 2 3 4 5 6 7 8 9 10
# Sage from Crypto.Util.number import * n = 14113948189208713011909396304970377626324044633561155020366406284451614054260708934598840781397326960921718892801653205159753091559901114082556464576477585198060530094478860626532455065960136263963965819002575418616768412539016154873800614138683106056209070597212668250136909436974469812231498651367459717175769611385545792201291192023843434476550550829737236225181770896867698281325858412643953550465132756142888893550007041167700300621499970661661288422834479368072744930285128061160879720771910458653611076539210357701565156322144818787619821653007453741709031635862923191561438148729294430924288173571196757351837 t = 1520800285708753284739523608878585974609134243280728660335545667177630830064371336150456537012842986526527904043383436211487979254140749228004148347597566264500276581990635110200009305900689510908049771218073767918907869112593870878204145615928290375086195098919355531430003571366638390993296583488184959318678321571278510231561645872308920917404996519309473979203661442792048291421574603018835698487725981963573816645574675640357569465990665689618997534740389987351864738104038598104713275375385003471306823348792559733332094774873827383320058176803218213042061965933143968710199376164960850951030741280074168795136 c = 6635663565033382363211849843446648120305449056573116171933923595209656581213410699649926913276685818674688954045817246263487415328838542489103709103428412175252447323358040041217431171817865818374522191881448865227314554997131690963910348820833080760482835650538394814181656599175839964284713498394589419605748581347163389157651739759144560719049281761889094518791244702056048080280278984031050608249265997808217512349309696532160108250480622956599732443714546043439089844571655280770141647694859907985919056009576606333143546094941635324929407538860140272562570973340199814409134962729885962133342668270226853146819 e = 3 R.<x> = PolynomialRing(Zmod(n)) f = (t+x) ^ e - c m = f.small_roots(X=2^315, beta=0.4)[0] print(long_to_bytes(t+m))
defsmall_roots(f, bounds, m=1, d=None): ifnot d: d = f.degree()
ifisinstance(f, Polynomial): x, = polygens(f.base_ring(), f.variable_name(), 1) f = f(x)
R = f.base_ring() N = R.cardinality() f /= f.coefficients().pop(0) f = f.change_ring(ZZ)
G = Sequence([], f.parent()) for i inrange(m+1): base = N^(m-i) * f^i for shifts in itertools.product(range(d), repeat=f.nvariables()): g = base * prod(map(power, f.variables(), shifts)) G.append(g)
factors = [monomial(*bounds) for monomial in monomials] for i, factor inenumerate(factors): B.rescale_col(i, factor)
B = B.dense_matrix().LLL()
B = B.change_ring(QQ) for i, factor inenumerate(factors): B.rescale_col(i, 1/factor)
H = Sequence([], f.parent().change_ring(QQ)) for h infilter(None, B*monomials): H.append(h) I = H.ideal() if I.dimension() == -1: H.pop() elif I.dimension() == 0: roots = [] for root in I.variety(ring=ZZ): root = tuple(R(root[var]) for var in f.variables()) roots.append(root) return roots
y的估计用到了p,q比较均匀的假设。这里delta为预估的小于0.292的值。如果我们求得了该二元方程的根,那么我们自然也就可以解一元二次方程N=pq,p+q=−2y来得到p与q。虽然原理也和没说一样,github上有基于sagemath的拓展脚本,虽然原理看起来并不难懂,算法实现确实异常困难,亦是基于格的攻击。以下是项目文件的源码,同时将其改成可调用版本,使用方法便是example(e, n),实际上后面还有两个参数,第三个参数是用来确定上界,第四个参数是用来确定格的维度。example(e, n, delta=.18),delta默认是0.18,传入时不能大于0.292,example(e, n, delta=.18, m=4),格的维度越大,解的速度越慢,实际上控制好delta的大小便能迅速出答案,m调大虽然也能拿到答案但速度就慢,当delta如何调都跑不出来,而且理论分析的界满足攻击情况时,可以适当将m调大看看,m默认是4。
""" Setting debug to true will display more informations about the lattice, the bounds, the vectors... """ debug = True
""" Setting strict to true will stop the algorithm (and return (-1, -1)) if we don't have a correct upperbound on the determinant. Note that this doesn't necesseraly mean that no solutions will be found since the theoretical upperbound is usualy far away from actual results. That is why you should probably use `strict = False` """ strict = False
""" This is experimental, but has provided remarkable results so far. It tries to reduce the lattice as much as it can while keeping its efficiency. I see no reason not to use this option, but if things don't work, you should try disabling it """ helpful_only = True dimension_min = 7# stop removing if lattice reaches that dimension
# display stats on helpful vectors defhelpful_vectors(BB, modulus): nothelpful = 0 for ii inrange(BB.dimensions()[0]): if BB[ii,ii] >= modulus: nothelpful += 1
print(nothelpful, "/", BB.dimensions()[0], " vectors are not helpful")
# display matrix picture with 0 and X defmatrix_overview(BB, bound): for ii inrange(BB.dimensions()[0]): a = ('%02d ' % ii) for jj inrange(BB.dimensions()[1]): a += '0'if BB[ii,jj] == 0else'X' if BB.dimensions()[0] < 60: a += ' ' if BB[ii, ii] >= bound: a += '~' print(a)
# tries to remove unhelpful vectors # we start at current = n-1 (last vector) defremove_unhelpful(BB, monomials, bound, current): # end of our recursive function if current == -1or BB.dimensions()[0] <= dimension_min: return BB
# we start by checking from the end for ii inrange(current, -1, -1): # if it is unhelpful: if BB[ii, ii] >= bound: affected_vectors = 0 affected_vector_index = 0 # let's check if it affects other vectors for jj inrange(ii + 1, BB.dimensions()[0]): # if another vector is affected: # we increase the count if BB[jj, ii] != 0: affected_vectors += 1 affected_vector_index = jj
# level:0 # if no other vectors end up affected # we remove it if affected_vectors == 0: print("* removing unhelpful vector", ii) BB = BB.delete_columns([ii]) BB = BB.delete_rows([ii]) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii-1) return BB
# level:1 # if just one was affected we check # if it is affecting someone else elif affected_vectors == 1: affected_deeper = True for kk inrange(affected_vector_index + 1, BB.dimensions()[0]): # if it is affecting even one vector # we give up on this one if BB[kk, affected_vector_index] != 0: affected_deeper = False # remove both it if no other vector was affected and # this helpful vector is not helpful enough # compared to our unhelpful one if affected_deeper andabs(bound - BB[affected_vector_index, affected_vector_index]) < abs(bound - BB[ii, ii]): print("* removing unhelpful vectors", ii, "and", affected_vector_index) BB = BB.delete_columns([affected_vector_index, ii]) BB = BB.delete_rows([affected_vector_index, ii]) monomials.pop(affected_vector_index) monomials.pop(ii) BB = remove_unhelpful(BB, monomials, bound, ii-1) return BB # nothing happened return BB
""" Returns: * 0,0 if it fails * -1,-1 if `strict=true`, and determinant doesn't bound * x0,y0 the solutions of `pol` """ defboneh_durfee(pol, modulus, mm, tt, XX, YY): """ Boneh and Durfee revisited by Herrmann and May finds a solution if: * d < N^delta * |x| < e^delta * |y| < e^0.5 whenever delta < 1 - sqrt(2)/2 ~ 0.292 """
# x-shifts gg = [] for kk inrange(mm + 1): for ii inrange(mm - kk + 1): xshift = x^ii * modulus^(mm - kk) * polZ(u, x, y)^kk gg.append(xshift) gg.sort()
# x-shifts list of monomials monomials = [] for polynomial in gg: for monomial in polynomial.monomials(): if monomial notin monomials: monomials.append(monomial) monomials.sort() # y-shifts (selected by Herrman and May) for jj inrange(1, tt + 1): for kk inrange(floor(mm/tt) * jj, mm + 1): yshift = y^jj * polZ(u, x, y)^kk * modulus^(mm - kk) yshift = Q(yshift).lift() gg.append(yshift) # substitution # y-shifts list of monomials for jj inrange(1, tt + 1): for kk inrange(floor(mm/tt) * jj, mm + 1): monomials.append(u^kk * y^jj)
# construct lattice B nn = len(monomials) BB = Matrix(ZZ, nn) for ii inrange(nn): BB[ii, 0] = gg[ii](0, 0, 0) for jj inrange(1, ii + 1): if monomials[jj] in gg[ii].monomials(): BB[ii, jj] = gg[ii].monomial_coefficient(monomials[jj]) * monomials[jj](UU,XX,YY)
# Prototype to reduce the lattice if helpful_only: # automatically remove BB = remove_unhelpful(BB, monomials, modulus^mm, nn-1) # reset dimension nn = BB.dimensions()[0] if nn == 0: print("failure") return0,0
# check if vectors are helpful if debug: helpful_vectors(BB, modulus^mm) # check if determinant is correctly bounded det = BB.det() bound = modulus^(mm*nn) if det >= bound: print("We do not have det < bound. Solutions might not be found.") print("Try with highers m and t.") if debug: diff = (log(det) - log(bound)) / log(2) print("size det(L) - size e^(m*n) = ", floor(diff)) if strict: return -1, -1 else: print("det(L) < e^(m*n) (good! If a solution exists < N^delta, it will be found)")
# display the lattice basis if debug: matrix_overview(BB, modulus^mm)
# LLL if debug: print("optimizing basis of the lattice via LLL, this can take a long time")
BB = BB.LLL()
if debug: print("LLL is done!")
# transform vector i & j -> polynomials 1 & 2 if debug: print("looking for independent vectors in the lattice") found_polynomials = False for pol1_idx inrange(nn - 1): for pol2_idx inrange(pol1_idx + 1, nn): # for i and j, create the two polynomials PR.<w,z> = PolynomialRing(ZZ) pol1 = pol2 = 0 for jj inrange(nn): pol1 += monomials[jj](w*z+1,w,z) * BB[pol1_idx, jj] / monomials[jj](UU,XX,YY) pol2 += monomials[jj](w*z+1,w,z) * BB[pol2_idx, jj] / monomials[jj](UU,XX,YY)
# are these good polynomials? if rr.is_zero() or rr.monomials() == [1]: continue else: print("found them, using vectors", pol1_idx, "and", pol2_idx) found_polynomials = True break if found_polynomials: break
ifnot found_polynomials: print("no independant vectors could be found. This should very rarely happen...") return0, 0 rr = rr(q, q)
# solutions soly = rr.roots()
iflen(soly) == 0: print("Your prediction (delta) is too small") return0, 0
soly = soly[0][0] ss = pol1(q, soly) solx = ss.roots()[0][0]
# return solx, soly
defexample(e, N, delta = .18, m = 4): ############################################ # How To Use This Script ##########################################
# # The problem to solve (edit the following values) #
# the modulus # N = 0xc2fd2913bae61f845ac94e4ee1bb10d8531dda830d31bb221dac5f179a8f883f15046d7aa179aff848db2734b8f88cc73d09f35c445c74ee35b01a96eb7b0a6ad9cb9ccd6c02c3f8c55ecabb55501bb2c318a38cac2db69d510e152756054aaed064ac2a454e46d9b3b755b67b46906fbff8dd9aeca6755909333f5f81bf74db # the public exponent # e = 0x19441f679c9609f2484eb9b2658d7138252b847b2ed8ad182be7976ed57a3e441af14897ce041f3e07916445b88181c22f510150584eee4b0f776a5a487a4472a99f2ddc95efdd2b380ab4480533808b8c92e63ace57fb42bac8315fa487d03bec86d854314bc2ec4f99b192bb98710be151599d60f224114f6b33f47e357517
# the hypothesis on the private exponent (the theoretical maximum is 0.292) # delta = .18 # this means that d < N^delta
# # Lattice (tweak those values) #
# you should tweak this (after a first run), (e.g. increment it until a solution is found) # m = 4 # size of the lattice (bigger the better/slower)
# you need to be a lattice master to tweak these t = int((1-2*delta) * m) # optimization from Herrmann and May X = 2*floor(N^delta) # this _might_ be too much Y = floor(N^(1/2)) # correct if p, q are ~ same size
# # Don't touch anything below #
# Problem put in equation P.<x,y> = PolynomialRing(ZZ) A = int((N+1)/2) pol = 1 + x * (A + y)
# # Find the solutions! #
# Checking bounds if debug: print("=== checking values ===") print("* delta:", delta) print("* delta < 0.292", delta < 0.292) print("* size of e:", int(log(e)/log(2))) print("* size of N:", int(log(N)/log(2))) print("* m:", m, ", t:", t)
from Crypto.Util.number import * from Crypto.PublicKey import RSA from flag import flag import random
m = bytes_to_long(flag)
p = getPrime(512) q = getPrime(512) n = p*q e = random.getrandbits(1024) assert size(e)==1024 phi = (p-1)*(q-1) assert GCD(e,phi)==1 d = inverse(e,phi) assert size(d)==269
pub = (n, e) PublicKey = RSA.construct(pub) withopen('pub.pem', 'wb') as f : f.write(PublicKey.exportKey())
c = pow(m,e,n) print('c =',c) print(long_to_bytes(pow(c,d,n)))
# c = 6838759631922176040297411386959306230064807618456930982742841698524622016849807235726065272136043603027166249075560058232683230155346614429566511309977857815138004298815137913729662337535371277019856193898546849896085411001528569293727010020290576888205244471943227253000727727343731590226737192613447347860 # e = 113449247876071397911206070019495939088171696712182747502133063172021565345788627261740950665891922659340020397229619329204520999096535909867327960323598168596664323692312516466648588320607291284630435682282630745947689431909998401389566081966753438869725583665294310689820290368901166811028660086977458571233 # n = 116518679305515263290840706715579691213922169271634579327519562902613543582623449606741546472920401997930041388553141909069487589461948798111698856100819163407893673249162209631978914843896272256274862501461321020961958367098759183487116417487922645782638510876609728886007680825340200888068103951956139343723
c = 6838759631922176040297411386959306230064807618456930982742841698524622016849807235726065272136043603027166249075560058232683230155346614429566511309977857815138004298815137913729662337535371277019856193898546849896085411001528569293727010020290576888205244471943227253000727727343731590226737192613447347860 e = 113449247876071397911206070019495939088171696712182747502133063172021565345788627261740950665891922659340020397229619329204520999096535909867327960323598168596664323692312516466648588320607291284630435682282630745947689431909998401389566081966753438869725583665294310689820290368901166811028660086977458571233 n = 116518679305515263290840706715579691213922169271634579327519562902613543582623449606741546472920401997930041388553141909069487589461948798111698856100819163407893673249162209631978914843896272256274862501461321020961958367098759183487116417487922645782638510876609728886007680825340200888068103951956139343723 load('boneh_durffe.sage') example(e, n) # 可以看到是无d解出 example(e, n, 0.26) # 将delta调到0.26,就能格约束出来
import gmpy2 from libnum import * import random import math import time
defonemod(e, q): p = random.randint(1, q - 1) while (pow(p, (q - 1) // e, q) == 1): # (r,s)=1 p = random.randint(1, q) return p
defAMM_rth(o, r, q): # r|(q-1) print('start to calculate primitive root...') start = time.time() assert ((q - 1) % r == 0) p = onemod(r, q) print('p:%d' % p) t = 0 s = q - 1 while (s % r == 0): s = s // r t += 1 k = 1 while ((s * k + 1) % r != 0): k += 1 alp = (s * k + 1) // r print('s:%d,t:%d r:%d alp:%d' % (s, t, r, alp)) a = pow(p, r ** (t - 1) * s, q) b = pow(o, r * a - 1, q) c = pow(p, s, q) h = 1 for i inrange(1, t - 1): d = pow(int(b), r ** (t - 1 - i), q) print('d:%d' % d) if d == 1: j = 0 else: j = (-math.log(d, a)) % r b = (b * (c ** (r * j))) % q h = (h * c ** j) % q c = (c * r) % q result = (pow(o, alp, q) * h) end = time.time()
print('result:%d' % result) print('Finish in {} seconds'.format(end - start)) return result
# 2020.4.3 finish defALL_Solution(m, q, rt, cq, e): print('start to calculate all root...') start = time.time() mp = [] for pr in rt: r = (pr * m) % q assert (pow(r, e, q) == cq) mp.append(r) end = time.time() print('Finish in {} seconds'.format(end - start)) return mp
defcalc(mp, mq, e, p, q): print('satrting crt... ') i = 1 j = 1 start = time.time() t1 = gmpy2.invert(q, p) t2 = gmpy2.invert(p, q) for mp1 in mp: if (j % 1000 == 0): print(j) j += 1 for mq1 in mq: ans = (mp1 * t1 * q + mq1 * t2 * p) % (p * q) if check(ans): print('Finish in {} seconds'.format(time.time() - start)) return return
defcheck(m): try: a = n2s(m) if a.startswith('NCTF'): print(a) returnTrue else: returnFalse except: returnFalse
defALL_ROOT2(r, q): # use function set() and .add() ensure that the generated elements are not repeated print('start to find all primitive root...') start = time.time() li = set() while (len(li) < r): p = pow(random.randint(1, q - 1), (q - 1) // r, q) li.add(p) end = time.time() print('Finish in {} seconds'.format(end - start)) return li
if __name__ == '__main__': c = 10562302690541901187975815594605242014385201583329309191736952454310803387032252007244962585846519762051885640856082157060593829013572592812958261432327975138581784360302599265408134332094134880789013207382277849503344042487389850373487656200657856862096900860792273206447552132458430989534820256156021128891296387414689693952047302604774923411425863612316726417214819110981605912408620996068520823370069362751149060142640529571400977787330956486849449005402750224992048562898004309319577192693315658275912449198365737965570035264841782399978307388920681068646219895287752359564029778568376881425070363592696751183359 p = 199138677823743837339927520157607820029746574557746549094921488292877226509198315016018919385259781238148402833316033634968163276198999279327827901879426429664674358844084491830543271625147280950273934405879341438429171453002453838897458102128836690385604150324972907981960626767679153125735677417397078196059 q = 112213695905472142415221444515326532320352429478341683352811183503269676555434601229013679319423878238944956830244386653674413411658696751173844443394608246716053086226910581400528167848306119179879115809778793093611381764939789057524575349501163689452810148280625226541609383166347879832134495444706697124741 e = 0x1337 cp = c % p cq = c % q
from gmpy2 import invert,lcm,is_prime import sys sys.setrecursionlimit(2047)
f = (378122348642214690905411683807377396279362526734068034297186493255873563264253248095197659138069664908850853277799239471404715546747080714581633876343291058815268009173602365587463522797812239900814474973941995698621021680129530453282192603316731832323767320307650941745085796583822798379896337325, 205549984221850341303682190742446959375043769671555741781145106776498798455293849755553794941345135675348633855787880355726112506553703853175271830126908219803257875131387292937296039523359975622001865593227631119497003599166091642640101746534401068507972834895023584843991641629874709898672559632) e = 59107 p = 228517792080140341 q = 1675909164550923263854591345270445396052847869117231939809062226222204253885693425526434134321712288675268468398852452684029376569327518089966506865838909486699078280423099271324646863671350838232140981094611254627738568184261530942469845202934677427234062382272736609418198352717
from Crypto.Util.number import * from secret import flag
l = len(flag) assert l == 56 x = bytes_to_long(flag[:l//2]) y = bytes_to_long(flag[l//2:])
p = getPrime(1024) e = 65537
x = pow(x, e, p) y = pow(y, e, p)
a = (7 * x + x * y + 77 * y ** 7) % p b = (x ** 7 + 777 * y) % p """ print(f'p = {p}') print(f'a = {a}') print(f'b = {b}') """
p = 160676801612994301361202519503059426958636739446670462398261976532859847492256822690640058297338763725128097587993428329580105931247817467950370089691908132361316857330836120708767594061772979871315614755470773991633234068651435625372887767258609941208307491359777513843529144444836847722372845148836203335627 a = 30318995909014771647618268716833486449002423009996671727903532973647046764624121316716790986592523978549131384964872198795285872746623966910764159262479160147876027157581577141632378119375701270068263640642243000011932466519579133761464923463402462812787531220639360431295348786697861069940729757964584951972 b = 51036630170491152581994259808984114372634216659979376101433163181132141957563047348137651942358538069256102718534893846618166559129391336639526588292370462975735415885732360576961407017238385374280336346614960555565504032093702784952402038043052556719843691506943605133036720410419999467125928578673380637828
p = 160676801612994301361202519503059426958636739446670462398261976532859847492256822690640058297338763725128097587993428329580105931247817467950370089691908132361316857330836120708767594061772979871315614755470773991633234068651435625372887767258609941208307491359777513843529144444836847722372845148836203335627 a = 30318995909014771647618268716833486449002423009996671727903532973647046764624121316716790986592523978549131384964872198795285872746623966910764159262479160147876027157581577141632378119375701270068263640642243000011932466519579133761464923463402462812787531220639360431295348786697861069940729757964584951972 b = 51036630170491152581994259808984114372634216659979376101433163181132141957563047348137651942358538069256102718534893846618166559129391336639526588292370462975735415885732360576961407017238385374280336346614960555565504032093702784952402038043052556719843691506943605133036720410419999467125928578673380637828 e = 65537
P.<x, y> = PolynomialRing(Zmod(p)) f1 = 7*x + x*y + 77*y**7 - a f2 = x**7 + 777*y - b
hx = resulant(f1, f2, y) rx = hx.univariate_polynomial().roots() x, _ = zip(*rx) y = [((b - i^7) * inverse_mod(777, p)) % p for i in x]
d = inverse_mod(e, p - 1)
for i inrange(len(x)): m1 = pow(x[i], d, p) m2 = pow(y[i], d, p) try: print(bytes.fromhex(hex(m1)[2:]).decode(), end='') print(bytes.fromhex(hex(m2)[2:]).decode()) except: pass
在sage中,直接使用long_to_bytes这类函数,会因为类型原因报错,因为通过pow运算出来的类型是<class 'sage.rings.finite_rings.integer_mod.IntegerMod_gmp'>,而函数所需的类型是int,这就需要在pow之外套上层int,或者就像题解中所使用的bytes.fromhex,hex进行层转换,好像也没多大差别。但decode这个函数的使用非常精妙,因为非flag中有着不可显字符的错在,会爆'utf-8' codec can't decode byte 0xb1 in position 0: invalid start byte错误,这样通过try能排掉错误答案,不再需要去人眼辨识。
defcomplex_pow(c, exp, n): result = Complex(1, 0) while exp > 0: if exp & 1: result = result * c result.re = result.re % n result.im = result.im % n c = c * c c.re = c.re % n c.im = c.im % n exp >>= 1 return result
m = bytes_to_long(flag) key = getRandomNBitInteger(m.bit_length()) c = m ^ key com = Complex(key, c) p = getPrime(512) q = getPrime(512) e = 9 enc = complex_pow(com, e, p * q) print(enc) print(Complex(p, q) * Complex(p, q)) # 66350931528185981323649477263900844564494528747802437244889229343520648562164950914433406604580038018765783183569276743239888668912948977370163046257917321742455772852779551569446155827368453262479370103326286297164105599131090881306108546341785251895116423206455175290083968296281375908109039893280371271943 + 65266730684129269806656018828265187384002656633231286337130178390517924611751697965395744944541329793503617856896706439809241745206839328124348693486741656130890593895436661857688522977866438805549144904296596887218275440542852887148071837153436265722614658566275517205322945316112048487893204059562830581004i # -28814875173103880290298835537218644402443395484370652510062722255203946330565951328874411874019897676900075613671629765922970689802650462822117767927082712245512492082864958877932682404829188622269636302484189627580600076246836248427780151681898051243380477561480415972565859837597822263289141887833338111120 + 235362412848885579543400940934854106052672292040465052424316433330114813432317923674803623227280862945857543620663672974955235166884830751834386990766053503640556408758413592161122945636548462064584183165189050320898315823173824074873376450569212651128285746330837777597290934043912373820690250920839961482862i
(p+qi)2=p2−q2+2pqi
那么可以通过sympy库解方程得到p,q
1 2
p = 10205509456040823453782883291824829705816298450992336115902525676510986341532486274067943978039013674207011185602314114359146043975207543018267242312660911 q = 11531144714660489617244423924607904114688972598683439999377362467380544567879231460623526800789918614728790840508257630983753525432337178000220918002499321
defcomplex_pow(c, exp, n): result = Complex(1, 0) while exp > 0: if exp & 1: result = result * c result.re = result.re % n result.im = result.im % n c = c * c c.re = c.re % n c.im = c.im % n exp >>= 1 return result
p = 10205509456040823453782883291824829705816298450992336115902525676510986341532486274067943978039013674207011185602314114359146043975207543018267242312660911 q = pq2 // 2 // p
com = Complex(66350931528185981323649477263900844564494528747802437244889229343520648562164950914433406604580038018765783183569276743239888668912948977370163046257917321742455772852779551569446155827368453262479370103326286297164105599131090881306108546341785251895116423206455175290083968296281375908109039893280371271943 , 65266730684129269806656018828265187384002656633231286337130178390517924611751697965395744944541329793503617856896706439809241745206839328124348693486741656130890593895436661857688522977866438805549144904296596887218275440542852887148071837153436265722614658566275517205322945316112048487893204059562830581004) re = int(complex_pow(com, inverse(3,(p^2-1)//3), p).re) im = int(complex_pow(com, inverse(3,(p^2-1)//3), p).im)
PR.<a,b> = PolynomialRing(Zmod(p)) f1 = a^3 - 3*a*b^2 - re f2 = 3*a^2*b - b^3 - im h = f1.sylvester_matrix(f2, a).det() res1 = h.univariate_polynomial().monic().roots() print(res1)
re = int(complex_pow(com, inverse(3,(q^2-1)//3), q).re) im = int(complex_pow(com, inverse(3,(q^2-1)//3), q).im) PR.<a,b> = PolynomialRing(Zmod(q)) f1 = a^3 - 3*a*b^2 - re f2 = 3*a^2*b - b^3 - im h = f1.sylvester_matrix(f2, a).det() res1 = h.univariate_polynomial().monic().roots() print(res1)
[GeekChallenge-2023]EzComplex
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
from Crypto.Util.number import * flag = b'FAKE{Do_You_konw_Complex_numbers}' p = random_prime(1 << 384) q = random_prime(1 << 384) n = p * q e = 0x10001 N = pow(p, 2) + pow(q, 2) m = bytes_to_long(flag) c = pow(m, e, n)
c = 122977267154486898127643454001467185956864368276013342450998567212966113302012584153291519651365278888605594000436279106907163024162771486315220072170917153855370362692990814276908399943293854077912175867886513964032241638851526276 N = 973990451943921675425625260267293227445098713194663380695161260771362036776671793195525239267004528550439258233703798932349677698127549891815995206853756301593324349871567926792912475619794804691721625860861059975526781239293017498 GI = GaussianIntegers() d = divisors(GI(N)) for k in d: if'I'instr(k): p = int(abs(k.imag())) q = int(abs(k.real())) if p.bit_length() inrange(382,385) and is_prime(p) and q.bit_length() inrange(382,385) and is_prime(q): print((p,q))
p,q = (8732781022306464325787401448517171026218291389436971731700810979177651389459896422549428444142746055523338740248707, 29962125885196559918101088622575501736433575381042696980660846307183241725227137854663856022170515177120773072848343) e = 0x10001 c = 122977267154486898127643454001467185956864368276013342450998567212966113302012584153291519651365278888605594000436279106907163024162771486315220072170917153855370362692990814276908399943293854077912175867886513964032241638851526276 f = (p-1)*(q-1) d = inverse_mod(e,f) m = pow(c,d,p*q) print(bytes.fromhex(hex(m)[2:]))