[湖湘杯 2021]signin
题目分数:321
题目评分:4.5
题目标签: CryptoRSA连分数
附件内容:
from Crypto.Util.number import *
from secret import flag
import random
m1 = bytes_to_long(flag[:len(flag) // 2])
m2 = bytes_to_long(flag[len(flag) // 2:])
def gen(pbits, qbits):
p1, q1 = getPrime(pbits), getPrime(qbits)
n1 = p1**4*q1
q2 = getPrime(qbits)
bound = p1 // (8*q1*q2) + 1
p2 = random.randrange(p1, p1 + bound)
while not isPrime(p2):
p2 = random.randrange(p1, p1 + bound)
n2 = p2**4*q2
return (n1, n2), (p1, q1), (p2, q2)
e = 0x10001
pbits = int(360)
qbits = int(128)
pk, sk1, sk2 = gen(pbits, qbits)
c1 = pow(m1, e, pk[0])
c2 = pow(m2, e, pk[1])
print(f'pk = {pk}')
print(f'c1, c2 = {c1, c2}')
"""
pk = (1150398070565459492080597718626032792435556703413923483458704675295997646493249759818468321328556510074044954676615760446708253531839417036997811506222349194302791943489195718713797322878586379546657275419261647635859989280700191441312691274285176619391539387875252135478424580680264554294179123254566796890998243909286508189826458854346825493157697201495100628216832191035903848391447704849808577310612723700318670466035077202673373956324725108350230357879374234418393233, 1242678737076048096780023147702514112272319497423818488193557934695583793070332178723043194823444815153743889740338870676093799728875725651036060313223096288606947708155579060628807516053981975820338028456770109640111153719903207363617099371353910243497871090334898522942934052035102902892149792570965804205461900841595290667647854346905445201396273291648968142608158533514391348407631818144116768794595226974831093526512117505486679153727123796834305088741279455621586989)
c1, c2 = (361624030197288323178211941746074961985876772079713896964822566468795093475887773853629454653096485450671233584616088768705417987527877166166213574572987732852155320225332020636386698169212072312758052524652761304795529199864805108000796457423822443871436659548626629448170698048984709740274043050729249408577243328282313593461300703078854044587993248807613713896590402657788194264718603549894361488507629356532718775278399264279359256975688280723740017979438505001819438, 33322989148902718763644384246610630825314206644879155585369541624158380990667828419255828083639294898100922608833810585530801931417726134558845725168047585271855248605561256531342703212030641555260907310067120102069499927711242804407691706542428236208695153618955781372741765233319988193384708525251620506966304554054884590718068210659709406626033891748214407992041364462525367373648910810036622684929049996166651416565651803952838857960054689875755131784246099270581394)
"""
解题:
参考勒让德定理
关于连分数逼近问题离不开的就是勒让德定理:
假设x∈R+,那么在x的连分数逼近展开中,如果有∣x−bc∣<12c2,其中gcd(b,c)==1,那么可以认为bc是x的连分数展开中其中一项的值。假设x∈R+,那么在x的连分数逼近展开中,如果有∣x−cb∣<2c21,其中gcd(b,c)==1,那么可以认为cb是x的连分数展开中其中一项的值。
经过推导可以得到
N1N2的连分数展开是对q1q2的一个逼近N2N1的连分数展开是对q2q1的一个逼近
解题脚本:
from Crypto.Util.number import *
import gmpy2
def continuedFra(x,y):
cf = []
while y != 0:
cf.append(x // y)
x,y = y,x % y
return cf
def exp(x,y):
cf = continuedFra(x,y)
fz = [cf[0],cf[0] * cf[1] + 1]
fm = [1,cf[1]]
for i in range(2,len(cf)):
z = fz[i - 1] * cf[i] + fz[i - 2]
m = fm[i - 1] * cf[i] + fm[i - 2]
fz.append(z)
fm.append(m)
return fz,fm
def get(x,y):
tmp1,tmp2 = exp(x,y)
for i in range(2,len(tmp1)):
_x,_y = tmp1[i],tmp2[i]
if x % _x == 0 and y % _y == 0 and x != _x and y != _y:
return _x,_y
e = 0x10001
n1 = 1150398070565459492080597718626032792435556703413923483458704675295997646493249759818468321328556510074044954676615760446708253531839417036997811506222349194302791943489195718713797322878586379546657275419261647635859989280700191441312691274285176619391539387875252135478424580680264554294179123254566796890998243909286508189826458854346825493157697201495100628216832191035903848391447704849808577310612723700318670466035077202673373956324725108350230357879374234418393233
n2 = 1242678737076048096780023147702514112272319497423818488193557934695583793070332178723043194823444815153743889740338870676093799728875725651036060313223096288606947708155579060628807516053981975820338028456770109640111153719903207363617099371353910243497871090334898522942934052035102902892149792570965804205461900841595290667647854346905445201396273291648968142608158533514391348407631818144116768794595226974831093526512117505486679153727123796834305088741279455621586989
c1 = 361624030197288323178211941746074961985876772079713896964822566468795093475887773853629454653096485450671233584616088768705417987527877166166213574572987732852155320225332020636386698169212072312758052524652761304795529199864805108000796457423822443871436659548626629448170698048984709740274043050729249408577243328282313593461300703078854044587993248807613713896590402657788194264718603549894361488507629356532718775278399264279359256975688280723740017979438505001819438
c2 = 33322989148902718763644384246610630825314206644879155585369541624158380990667828419255828083639294898100922608833810585530801931417726134558845725168047585271855248605561256531342703212030641555260907310067120102069499927711242804407691706542428236208695153618955781372741765233319988193384708525251620506966304554054884590718068210659709406626033891748214407992041364462525367373648910810036622684929049996166651416565651803952838857960054689875755131784246099270581394
q1,q2 = get(n1,n2)
p1 = gmpy2.iroot(n1 //q1,4)[0]
p2 = gmpy2.iroot(n2 //q2,4)[0]
def atttack(p,q,c,e):
n = p ** 4 * q
phi = p ** 3 * (p - 1) * (q - 1)
d = gmpy2.invert(e,phi)
m = pow(c,d,n)
flag = long_to_bytes(m)
return flag
print(atttack(p1,q1,c1,e) + atttack(p2,q2,c2,e))
flag{8ef333ac-21a7-11ec-80f1-00155d83f114}