Crypto
1.happy_to_solve1
from Crypto.Util.number import *
import sympy
from secrets import flag
def get_happy_prime():
p = getPrime(512)
q = sympy.nextprime(p ^ ((1 << 512) - 1))
return p, q
m = bytes_to_long(flag)
p, q = get_happy_prime()
n = p * q
e = 65537
print(n)
print(pow(m, e, n))
# 24852206647750545040640868093921252282805229864862413863025873203291042799096787789288461426555716785288286492530194901130042940279109598071958012303179823645151637759103558737126271435636657767272703908384802528366090871653024192321398785017073393201385586868836278447340624427705360349350604325533927890879
# 14767985399473111932544176852718061186100743117407141435994374261886396781040934632110608219482140465671269958180849886097491653105939368395716596413352563005027867546585191103214650790884720729601171517615620202183534021987618146862260558624458833387692782722514796407503120297235224234298891794056695442287
由 q = s y m p y . n e x t p r i m e ( p ⨁ ( 1 << 512 ) − 1 ) ) q = sympy.nextprime(p\bigoplus(1\texttt{<<}512)-1)) q=sympy.nextprime(p⨁(1<<512)−1)) 的关系,可以将右边的式子转化为 2 512 − p − 1 2^{512}-p-1 2512−p−1,
q q q是 2 512 − p − 1 2^{512}-p-1 2512−p−1的下一个素数也就是说 q q q 与 2 512 − p − 1 2^{512}-p-1 2512−p−1 很接近,也即 p + q p+q p+q与 2 512 2^{512} 2512接近,
因此可以直接将 n n n减去 2 512 2^{512} 2512后枚举偏置 i i i,算出 φ ( n ) \varphi(n) φ(n)(相邻两个素数的距离不会很大,因此i也不会很大)
from Crypto.Util.number import *
n=24852206647750545040640868093921252282805229864862413863025873203291042799096787789288461426555716785288286492530194901130042940279109598071958012303179823645151637759103558737126271435636657767272703908384802528366090871653024192321398785017073393201385586868836278447340624427705360349350604325533927890879
c=14767985399473111932544176852718061186100743117407141435994374261886396781040934632110608219482140465671269958180849886097491653105939368395716596413352563005027867546585191103214650790884720729601171517615620202183534021987618146862260558624458833387692782722514796407503120297235224234298891794056695442287
e = 65537
for i in range(0,100000):
f=n-(1<<512)-i
d=inverse(e,f)
m=pow(c,d,n)
flag=long_to_bytes(m)
flag=str(flag)
if flag[2:7]=="XYCTF":
print(flag)
2.factor1
import gmpy2
import hashlib
from Crypto.Util.number import *
p = getPrime(512)
q = getPrime(512)
d = getPrime(512)
e = gmpy2.invert(d, (p**3 - 1) * (q**3 - 1))
flag = "XYCTF{" + hashlib.md5(str(p + q).encode()).hexdigest() + "}"
print(e)
print(p * q)
# 172005065945326769176157335849432320425605083524943730546805772515111751580759726759492349719668775270727323745284785341119685198468883978645793770975366048506237371435027612758232099414404389043740306443065413069994232238075194102578269859784981454218948784071599231415554297361219709787507633404217550013282713899284609273532223781487419770338416653260109238572639243087280632577902857385265070736208291583497988891353312351322545840742380550393294960815728021248513046077985900158814037534487146730483099151396746751774427787635287611736111679074330407715700153025952858666841328055071403960165321273972935204988906850585454805923440635864200149694398767776539993952528995717480620593326867245714074205285828967234591508039849777840636255379730281105670496110061909219669860172557450779495125345533232776767292561378244884362014224844319802810586344516400297830227894063759083198761120293919537342405893653545157892446163
# 99075185389443078008327214328328747792385153883836599753096971412377366865826254033534293886034828804219037466246175526347014045811852531994537520303063113985486063022444972761276531422538694915030159420989401280012025249129111871649831185047820236417385693285461420040134313833571949090757635806658958193793
如题,需要把 n n n 给分解出来才能得出 flag ,但可以看出 e e e 是 ( p 3 − 1 ) ( q 3 − 1 ) (p^3 - 1)(q^3 - 1) (p3−1)(q3−1) 下的逆元, ( p 3 − 1 ) ( q 3 − 1 ) (p^3 - 1) (q^3 - 1) (p3−1)(q3−1)与 n 3 n^3 n3 较为接近(当然可以进行更精确的估计,但本题已经足够了),想到如果以 n 3 n^3 n3 为分母做连分数攻击,求出d之后再求出 ( p 3 − 1 ) ( q 3 − 1 ) (p^3 - 1) (q^3 - 1) (p3−1)(q3−1) , n 3 n^3 n3大概为 3000 多位, d d d 只有 512 位,满足维纳攻击条件.
连分数攻击
(1)连分数:
将一个分数进行连分数展开,可以表示如下, [ a 0 , a 1 , … , a k ] [a_0,a_1,\dots,a_k] [a0,a1,…,ak],即对应 a 0 + 1 a 1 + 1 a 2 + … … a_0+\frac{1}{a_1+\frac{1}{a_2+\frac{\dots}{\dots}}} a0+a1+a2+……11
eg.将 57 109 \frac{57}{109} 10957进行连分数展开,一种展开方式如下,跟辗转相除法很像,每次通过提出整数,变成真分数,再取倒数,这样可以一直提取下去
57
109
=
0
+
1
109
57
=
0
+
1
1
+
1
57
52
=
0
+
1
1
+
1
1
+
1
52
5
=
0
+
1
1
+
1
1
+
1
10
+
1
5
2
=
0
+
1
1
+
1
1
+
1
10
+
1
2
+
1
2
\frac{57}{109}=0+\frac{1}{\frac{109}{57}}=0+\frac{1}{1+\frac{1}{\frac{57}{52}}}=0+\frac{1}{1+\frac{1}{1+\frac{1}{\frac{52}{5}}}}=0+\frac{1}{1+\frac{1}{1+\frac{1}{10+\frac{1}{\frac{5}{2}}}}}=0+\frac{1}{1+\frac{1}{1+\frac{1}{10+\frac{1}{2+\frac{1}{2}}}}}
10957=0+571091=0+1+525711=0+1+1+552111=0+1+1+10+251111=0+1+1+10+2+211111
因此连分数可以表示为
[
0
,
1
,
1
,
10
,
2
,
2
]
[0,1,1,10,2,2]
[0,1,1,10,2,2]
而渐进分数可以看作是对一个分数的近似, 即在连分数的基础上之间忽略掉后面的展开,当取的位数越多,近似程度越高。
第一渐进分数为 0 0 0,第二渐进分数为 1 1 1,第三渐进分数为 1 2 \frac{1}{2} 21,第四渐进分数为 11 21 \frac{11}{21} 2111,第五渐进分数为 23 44 \frac{23}{44} 4423,第六渐进分数为 57 109 \frac{57}{109} 10957
(2)拉格朗日定理:
若两个既约分数
a
b
\frac{a}{b}
ba 与
c
d
\frac{c}{d}
dc 之间满足
∣
a
b
−
c
d
∣
≤
1
2
d
2
\left|\frac{a}{b}-\frac{c}{d}\right|\le \frac{1}{2d^2}
ba−dc
≤2d21
则
a
b
\frac{a}{b}
ba 的其中一个渐进分数一定等于
c
d
\frac{c}{d}
dc,则有
a
=
c
,
b
=
d
a=c,b=d
a=c,b=d
在一般情况下的 RSA 中,有
e
d
=
1
+
k
φ
(
n
)
ed=1+k\varphi(n)
ed=1+kφ(n), 进行变换, 有
e
n
=
k
d
+
k
+
1
−
k
(
p
+
q
)
n
d
\frac{e}{n}=\frac{k}{d}+\frac{k+1-k(p+q)}{nd}
ne=dk+ndk+1−k(p+q)
由于
e
<
φ
(
n
)
e<\varphi(n)
e<φ(n), 因此
k
<
d
k<d
k<d,
p
+
q
≈
2
n
1
2
p+q\approx2n^{\frac{1}{2}}
p+q≈2n21,
k
≈
d
k\approx d
k≈d, 最右边那一部分与
2
n
1
2
\frac{2}{n^{\frac{1}{2}}}
n212 近似,而根据条件
∣
e
n
−
k
d
∣
≤
1
2
d
2
|\frac{e}{n}-\frac{k}{d}|\le \frac{1}{2d^2}
∣ne−dk∣≤2d21
因此
2
n
1
2
≤
1
2
d
2
\frac{2}{n^{\frac{1}{2}}}\le \frac{1}{2d^2}
n212≤2d21,推出来
d
<
1
2
n
0.25
d<\frac{1}{2}n^{0.25}
d<21n0.25.
回归本题, e n 3 = k d + k + 1 − k ( p 3 + q 3 ) n 3 d \frac{e}{n^3}=\frac{k}{d}+\frac{k+1-k(p^3+q^3)}{n^3d} n3e=dk+n3dk+1−k(p3+q3),推导右边的部分近似等于 2 n 3 2 \frac{2}{n^{\frac{3}{2}}} n232,远小于 1 2 d 2 \frac{1}{2d^2} 2d21,因此以 n 3 n^3 n3为分母进行连分数攻击
求出来的渐进分数需要满足 e d − 1 ≡ 0 ( m o d k ) ed-1\equiv0\pmod k ed−1≡0(modk)才合法.
def rational_to_contfrac(x,y):
a = x//y
pquotients = [a]
while a * y != x:
x,y = y,x-a*y
a = x//y
pquotients.append(a)
return pquotients
def convergents_from_contfrac(frac):
convs = []
for i in range(len(frac)):
convs.append(contfrac_to_rational(frac[0:i]))
return convs
def contfrac_to_rational (frac):
if len(frac) == 0:
return (0,1)
num = frac[-1]
denom = 1
for _ in range(-2,-len(frac)-1,-1):
num, denom = frac[_]*num+denom, num
return (num,denom)
def hack_RSA(e,n):
frac = rational_to_contfrac(e, n)
convergents =convergents_from_contfrac(frac)
for (k,d) in convergents:
#这里可以修改条件,适用于不同的场景
if k!=0 and (e*d-1)%k==0:
print(d)
e=172005065945326769176157335849432320425605083524943730546805772515111751580759726759492349719668775270727323745284785341119685198468883978645793770975366048506237371435027612758232099414404389043740306443065413069994232238075194102578269859784981454218948784071599231415554297361219709787507633404217550013282713899284609273532223781487419770338416653260109238572639243087280632577902857385265070736208291583497988891353312351322545840742380550393294960815728021248513046077985900158814037534487146730483099151396746751774427787635287611736111679074330407715700153025952858666841328055071403960165321273972935204988906850585454805923440635864200149694398767776539993952528995717480620593326867245714074205285828967234591508039849777840636255379730281105670496110061909219669860172557450779495125345533232776767292561378244884362014224844319802810586344516400297830227894063759083198761120293919537342405893653545157892446163
n=99075185389443078008327214328328747792385153883836599753096971412377366865826254033534293886034828804219037466246175526347014045811852531994537520303063113985486063022444972761276531422538694915030159420989401280012025249129111871649831185047820236417385693285461420040134313833571949090757635806658958193793
n=n**3
hack_RSA(e,n)
接下来需要通过 e , d e,d e,d分解 n n n.
已知ed分解n
在一般情况下,可以直接通过 φ ( n ) \varphi(n) φ(n)与 n n n联立来分解 n n n,但对于本题的计算较为复杂,
因此我们想到将本题中的 e d − 1 = k ( p 3 − 1 ) ( q 3 − 1 ) ed-1=k(p^3-1)(q^3-1) ed−1=k(p3−1)(q3−1)化为 e d − 1 = k ′ ( p − 1 ) ( q − 1 ) ed-1=k^{'}(p-1)(q-1) ed−1=k′(p−1)(q−1),
通过以下方法分解 n n n:
令 k = e d − 1 k=ed-1 k=ed−1 ,选取小于 n n n 的 g g g ,则有 g k ≡ 1 ( m o d n ) → ( g k 2 − 1 ) ( g k 2 + 1 ) ≡ 0 ( m o d n ) g^k\equiv1(\bmod n)\rightarrow(g^{\frac{k}{2}}-1)(g^{\frac{k}{2}}+1)\equiv0(\bmod n) gk≡1(modn)→(g2k−1)(g2k+1)≡0(modn),这样迭代下去,则 g k 2 − 1 g^{\frac{k}{2}}-1 g2k−1很可能与 n n n的最大公因数在 ( 1 , n ) (1,n) (1,n)之间,由此可以分解 n n n.
p r o o f . \bf proof. proof.
g k − 1 g^k-1 gk−1能够分解成两个因数,因此要么其中一个因数含有因子 p q pq pq,要么一个因数含有因子 p p p,另一个含有因子 q q q.
对于第一种情况,若是 g k 2 − 1 g^{\frac{k}{2}}-1 g2k−1含有因子 p q pq pq,那么可以继续操作下去,否则应该换一个 g g g.
对于第二种情况,可以通过 gcd \gcd gcd得到 n n n的因子.
def getpq(n,e,d):
while True:
k = e * d - 1
g = random.randint(0, n)
while k%2==0:
k=k//2
temp=gmpy2.powmod(g,k,n)-1
if gmpy2.gcd(temp,n)>1 and temp!=0:
return gmpy2.gcd(temp,n)
3.babyRSAMAX
from Crypto.Util.number import *
from gmpy2 import *
from random import choice
flag = b'XYCTF{******}'
e = '?'
def getBabyPrime(nbits):
while True:
p = 1
while p.bit_length() <= nbits:
p *= choice(sieve_base)
if isPrime(p+1):
return p+1
p = getBabyPrime(512)
q = getBabyPrime(512)
n = p*q
gift1 = (pow(p,e,n)-pow(q,e,n)) % n
gift2 = pow(p+q,e,n)
t = 65537
x = bytes_to_long(e)
y = pow(x, t, n)
m = bytes_to_long(flag)
c = powmod(m, e, n)
print(f'n = {n}')
print(f'gift1 = {gift1}')
print(f'gift2 = {gift2}')
print(f'c = {c}')
print(f'y = {y}')
'''
n = 39332423872740210783246069030855946244104982381157166843977599780233911183158560901377359925435092326653303964261550158658551518626014048783435245471536959844874036516931542444719549997971482644905523459407775392702211086149279473784796202020281909706723380472571862792003687423791576530085747716706475220532321
gift1 = 4549402444746338327349007235818187793950285105091726167573552412678416759694660166956782755631447271662108564084382098562999950228708300902201571583419116299932264478381197034402338481872937576172197202519770782458343606060544694608852844228400457232100904217062914047342663534138668490328400022651816597367310
gift2 = 111061215998959709920736448050860427855012026815376672067601244053580566359594802604251992986382187891022583247997994146019970445247509119719411310760491983876636264003942870756402328634092146799825005835867245563420135253048223898334460067523975023732153230791136870324302259127159852763634051238811969161011462
c = 16938927825234407267026017561045490265698491840814929432152839745035946118743714566623315033802681009017695526374397370343984360997903165842591414203197184946588470355728984912522040744691974819630118163976259246941579063687857994193309554129816268931672391946592680578681270693589911021465752454315629283033043
y = 1813650001270967709841306491297716908969425248888510985109381881270362755031385564927869313112540534780853966341044526856705589020295048473305762088786992446350060024881117741041260391405962817182674421715239197211274668450947666394594121764333794138308442124114744892164155894256326961605137479286082964520217
'''
注意到素数的生成是由一些小素数的乘积再加1的,因此可以用 p o l l a r d ′ s p − 1 pollard's~p-1 pollard′s p−1算法分解 n n n
Pollard’s p − 1 \text{Pollard's }p-1 Pollard’s p−1
若 n n n的因子 p p p,有 p − 1 = p 1 p 2 p 3 … p k p-1=p_1p_2p_3\dots p_k p−1=p1p2p3…pk, p − 1 p-1 p−1的素因子不同且均小于 B B B则有 p 1 p 2 p 3 … p k ∣ B ! p_1p_2p_3\dots p_k|B! p1p2p3…pk∣B!(考虑到有相同的时候,素因子较小时仍然可以被涵盖,例如 7 2 7^2 72,此时49可以被 B ! B! B!涵盖).
由欧拉定理,若 ( a , p ) = 1 (a,p)=1 (a,p)=1,则 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv1(\bmod p) ap−1≡1(modp), 因此有 a B ! ≡ 1 ( m o d p ) a^{B!}\equiv1(\bmod p) aB!≡1(modp), 即 a B ! − 1 = k ∗ p a^{B!}-1=k*p aB!−1=k∗p,
于是 gcd ( a B ! − 1 , n ) = p \gcd(a^{B!}-1,n)=p gcd(aB!−1,n)=p
def Pollard(n):
a=2
while True:
for i in range(2,80000):
a=pow(a,i,n)
for j in range(80000,104729+1):#change the range as you wish
a=pow(a,j,n)
d=GCD(a-1,n)
if(1<d<n):
return(d)
a+=1
p=Pollard(n)
print(p)
分解完 n n n之后,可以很容易地算出 x x x,得到 e = 4096 e=4096 e=4096,发现 gcd ( e , φ ( n ) ) = 4 \gcd(e,\varphi(n))=4 gcd(e,φ(n))=4,不能直接解密,先自己写个扩展欧几里得,得到 d d d, c x = p o w ( c , d , n ) c_x=\mathrm{pow}(c,d,n) cx=pow(c,d,n)将次数降到4,即 c x ≡ m 4 ( m o d n ) c_x\equiv m^4(\bmod n) cx≡m4(modn)
def inverse_get(x,y):
r0=x
r1=y
r2=x%y
m=0
n=1
ln=0
q=x//y
while r2>0:
ln=m
m=n-q*m
n=ln
r0=r1
r1=r2
r2=r0%r1
q=r0//r1
return (m+y)%y
由于在素数模下开根是简单的,因此想到将 c x c_x cx在模 p , q p,q p,q下分别在有限域开四次根,再用中国剩余定理算出 f l a g flag flag.
R.<x> = Zmod(p)[]
f = x ^ 4 - c
f = f.monic()
res1 = f.roots()
R.<x> = Zmod(q)[]
f = x ^ 4 - c
f = f.monic()
res2 = f.roots()
r1=[236438400477521597922950445153796265199072404577183190953114805170522875904551780358338769404214275971858584747720119200208117068403781208566503489403215434, 36344540379246669047243921781711114415694316462518391812884210045]
r2=[166353789373057352195268575168397750362643822201253508941052835945420624983216456266478176543306949701450304802363434626984929302798183530544471602540368154, 36344540379246669047243921781711114415694316462518391812884210045]
import math
def Get_Mi(m_list, m):
M_list = []
for mi in m_list:
M_list.append(m // mi)
return M_list
def Get_resMi(M_list, m_list):
resM_list = []
for i in range(len(M_list)):
resM_list.append(Get_ni(M_list[i], m_list[i])[0])
return resM_list
def Get_ni(a, b):
if b == 0:
x = 1
y = 0
q = a
return x, y, q
ret = Get_ni(b, a % b)
x = ret[0]
y = ret[1]
q = ret[2]
temp = x
x = y
y = temp - a // b * y
return x, y, q
def result(a_list, m_list):
for i in range(len(m_list)):
for j in range(i + 1, len(m_list)):
if 1 != math.gcd(m_list[i], m_list[j]):
print("不能直接利用中国剩余定理")
return
m = 1
for mi in m_list:
m *= mi
Mi_list = Get_Mi(m_list, m)
Mi_inverse = Get_resMi(Mi_list, m_list)
x = 0
for i in range(len(a_list)):
x += Mi_list[i] * Mi_inverse[i] * a_list[i]
x %= m
return x
for i in r1:
for j in r2:
L1=[i,j]
L2=[p,q]
m=result(L1,L2)
flag=long_to_bytes(m)
flag=str(flag)
print(flag)
但是看到flag跟rabin有关,本题的正解应该是用 r a b i n rabin rabin算法,求得 G C D ( e , q − 1 ) = 2 , G C D ( e , p − 1 ) = 2 GCD(e,q-1)=2,GCD(e,p-1)=2 GCD(e,q−1)=2,GCD(e,p−1)=2,
Rabin算法
解决形如 m 2 ≡ c 1 ( m o d p ) , m 2 ≡ c 2 ( m o d q ) m^2\equiv c_1(\bmod p),m^2\equiv c_2(\bmod q) m2≡c1(modp),m2≡c2(modq)的加密, p , q ≡ 3 ( m o d 4 ) p,q\equiv 3(\bmod 4) p,q≡3(mod4)
对于 x 2 ≡ c ( m o d p ) x^2\equiv c(\bmod p) x2≡c(modp),因为 c c c是二次剩余,根据欧拉判别法, c p − 1 2 ≡ 1 ( m o d p ) c^{\frac{p-1}{2}}\equiv 1(\bmod p) c2p−1≡1(modp),
于是有 c p + 1 2 ≡ c ( m o d p ) c^{\frac{p+1}{2}}\equiv c (\bmod p) c2p+1≡c(modp),因为 p = 4 k + 3 p=4k+3 p=4k+3,
于是有 c k + 1 ≡ c 1 2 ≡ m ( m o d p ) c^{k+1}\equiv c^{\frac{1}{2}}\equiv m(\bmod p) ck+1≡c21≡m(modp)
再通过 C R T CRT CRT得出 m m m
4.nc版签到
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os
flag = open("flag.txt", "rb").read()
key = os.urandom(16)
iv = os.urandom(16)
flag = pad(flag, 16)
def aes_encrypt(key, plaintext):
cipher = AES.new(key, AES.MODE_ECB)
return cipher.encrypt(plaintext)
def encrypt(key, plaintext, iv):
ciphertext = b""
for i in range(0, len(plaintext), AES.block_size):
key_block = aes_encrypt(key, iv)
ciphertext_block = bytes(
[plaintext[i + j] ^ key_block[j] for j in range(AES.block_size)]
)
ciphertext += ciphertext_block
iv = key_block
return ciphertext
while 1:
try:
print("1.print\n2.input\n3.exit")
a = input("> ")
if a == "1":
print((iv + encrypt(key, flag, iv)).hex())
elif a == "2":
ivs = bytes.fromhex(input("iv: "))
inputs = bytes.fromhex(input("message: "))
print(encrypt(key, inputs, ivs).hex())
elif a == "3":
exit(0)
else:
print("You need input 1,2,3")
except:exit(0)
一个简单的交互题,需要用 k a l i n c kali~nc kali nc到端口进行交互
可以看出AES加密的是密钥 k e y key key,但我们不需要还原 k e y key key,因为本题只用了 k e y _ b l o c k key\_block key_block对 p l a i n t e x t plaintext plaintext进行异或加密,只需要反推出 k e y _ b l o c k key\_block key_block即可
为此,先输入1,得到iv向量与密文,
为了得到 k e y _ b l o c k key\_block key_block,我们输入2后输入相同的iv则能以相同的 k e y _ b l o c k key\_block key_block进行加密,
由于实质的加密方式只有 k e y _ b l o c k key\_block key_block与 m e s s a g e message message异或,我们将 m e s s a g e message message填上全0,最后print出的就是 k e y _ b l o c k key\_block key_block
再用 k e y _ b l o c k key\_block key_block与密文异或,得到明文
5.fakeRSA
from Crypto.Util.number import *
flag = b'XYCTF{******}'
n = ZZ(bytes_to_long(flag))
p = getPrime(int(320))
print(p)
G = Zmod(p)
def function(X, Y, Z):
def part(a, b, c):
return vector([9 * a - 36 * c, 6 * a - 27 * c, b])
def parts(n):
Gx.<a, b, c> = G[]
if n == 0: return vector([a, b, c])
mid = parts(n // 2)
result = mid(*mid)
if n % 2 == 0: return result
else: return part(*result)
return parts(n)(X, Y, Z)
print(function(69, 48, 52))
#1849790472911267366045392456893126092698743308291512220657006129900961168811898822553602045875909
#(1431995965813617415860695748430644570118959991271395110995534704629241309597572003500157255135707, 1011565891130611736600822618382801465506651972373410962205810570075870804325974377971089090196019, 784497518859893244278116222363814433595961164446277297084989532659832474887622082585456138030246)
最开始不太看得懂函数,就试了几下找到了规律,是矩阵的快速幂
因此根据提示,将变换 A = [ 9 6 0 0 0 1 − 36 − 27 0 ] , 转化为 A= \left[ \begin{matrix} 9 & 6 & 0 \\ 0 & 0 & 1 \\ -36 & -27 & 0 \end{matrix} \right] , 转化为 A= 90−3660−27010 ,转化为 J o r d a n Jordan Jordan标准型 J = [ 3 1 0 0 3 1 0 0 3 ] J= \left[ \begin{matrix} 3 & 1 & 0 \\ 0 & 3 & 1 \\ 0 & 0 & 3 \end{matrix} \right] J= 300130013 ,
p=1849790472911267366045392456893126092698743308291512220657006129900961168811898822553602045875909
A = Matrix(GF(p),
[[9, 6, 0],
[0, 0, 1],
[-36, -27, 0]])
AJor, T = A.jordan_form(transformation=True)
print(AJor,T)
A = T J T − 1 → A n = T J n T − 1 → m [ a , b , c ] T [ 18 6 n n 2 − n 0 18 6 n 0 0 18 ] = [ x 1 , x 2 , x 3 ] A=TJT^{-1}\rightarrow A^n=TJ^nT^{-1}\rightarrow m[a,b,c] T\left[ \begin{matrix} 18 & 6n & n^2-n \\ 0 & 18 & 6n \\ 0 & 0 & 18 \end{matrix} \right]=[x_1,x_2,x_3] A=TJT−1→An=TJnT−1→m[a,b,c]T 18006n180n2−n6n18 =[x1,x2,x3]
可以得到
18
x
1
−
1
a
≡
(
6
n
a
+
18
b
)
x
2
−
1
(
m
o
d
p
)
18x^{-1}_1a\equiv(6na+18b)x_2^{-1}\pmod p
18x1−1a≡(6na+18b)x2−1(modp)
即可算出来
n
n
n 了
6.Complex_dlp
from Crypto.Util.number import *
from secrets import flag
class Complex:
def __init__(self, re, im):
self.re = re
self.im = im
def __mul__(self, c):
re_ = self.re * c.re - self.im * c.im
im_ = self.re * c.im + self.im * c.re
return Complex(re_, im_)
def __str__(self):
if self.im == 0:
return str(self.re)
elif self.re == 0:
if abs(self.im) == 1:
return f"{'-' if self.im < 0 else ''}i"
else:
return f"{self.im}i"
else:
return f"{self.re} {'+' if self.im > 0 else '-'} {abs(self.im)}i"
def complex_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
flag = flag.strip(b"XYCTF{").strip(b"}")
p = 1127236854942215744482170859284245684922507818478439319428888584898927520579579027
g = Complex(3, 7)
x = bytes_to_long(flag)
print(complex_pow(g, x, p))
# 5699996596230726507553778181714315375600519769517892864468100565238657988087817 + 198037503897625840198829901785272602849546728822078622977599179234202360717671908i
本题关键在于把复数化成实数,因为 ( 3 + 7 i ) n ≡ a + b i ( m o d p ) (3+7i)^n\equiv a+bi\pmod p (3+7i)n≡a+bi(modp),在等式两端同时乘上其共轭,依然成立, 5 8 n ≡ a 2 + b 2 ( m o d p ) 58^n\equiv a^2+b^2\pmod p 58n≡a2+b2(modp),再求解离散对数问题即可
p =1127236854942215744482170859284245684922507818478439319428888584898927520579579027
g = 58
y =5699996596230726507553778181714315375600519769517892864468100565238657988087817 ^ 2 +198037503897625840198829901785272602849546728822078622977599179234202360717671908 ^ 2
x = discrete_log(y, mod(g, p))### 7.
7.反方向的密码-相思
from Crypto.Util.number import *
import hashlib
from secrets import flag
def hash(x):
return hashlib.sha256(x.encode()).digest()
def pad(message):
return message + hash(str(len(message)))
m = bytes_to_long(pad(flag))
p = getStrongPrime(512)
q = getStrongPrime(512)
n = p * q
e = 3
print(pow(m, e, n))
print(n)
# 120440199294949712392334113337541924034371176306546446428347114627162894108760435789068328282135879182130546564535108930827440004987170619301799710272329673259390065147556073101312748104743572369383346039000998822862286001416166288971531241789864076857299162050026949096919395896174243383291126202796610039053
# 143413213355903851638663645270518081058249439863120739973910994223793329606595495141951165221740599158773181585002460087410975579141155680671886930801733174300593785562287068287654547100320094291092508723488470015821072834947151827362715749438612812148855627557719115676595686347541785037035334177162406305243
可以看到填充的内容与 m e s s a g e message message的长度有关,因此可以先算出 h a s h ( l e n ( m e s s a g e ) ) hash(len(message)) hash(len(message)),利用 c o p p e r s m i t h coppersmith coppersmith攻击求解 m m m。
Coppersmith攻击
利用格基约化LLL算法,将系数较大的多项式化成系数较小的多项式,再根据牛顿迭代法进行求解,原理较为复杂。
假设 n n n 有因数 p p p ,且 p > n β p\gt n^{\beta} p>nβ , 则 c o p p e r s m i t h coppersmith coppersmith方法可以快速求出 m o d p \bmod p modp下的所有根 X X X,其中
X ≤ n β 2 e X\le n^{\frac{\beta^2}{e}} X≤neβ2
对于单元的 c o p p e r s m i t h coppersmith coppersmith可以直接使用 s a g e sage sage内置函数 s m a l l _ r o o t s ( ) small\_roots() small_roots()
P.<x> = PolynomialRing(Zmod(n), implementation='NTL')
for i in range(1,len(hash_ans)):
print(f"time:{i}")
m=hash_ans[i]
f=(m+x*2^256)^3-c
f=f.monic()
mroot=f.small_roots(X=2^(i*8+9),beta=1,epsilon=1/32)#对于卡上限的题,epsilon参数很重要
if mroot!=[]:
print(mroot[0])
break
7.factor3
from Crypto.Util.number import *
import random
flag = b'XYCTF{*****}'
m = bytes_to_long(flag)
def gainPrime():
while True:
x = random.getrandbits(256)
y = random.getrandbits(256)
if y % 2 == 0:
continue
p = x ** 3 + 3 * y ** 3
if p.bit_length() == 768 and p % 2 == 1 and isPrime(p):
return p
p, q = gainPrime(), gainPrime()
N = p * q
phi = (p ** 2 + p + 1) * (q ** 2 + q + 1)
d = getPrime(320)
e = inverse(d, phi)
c = d**2^m
print(f"N: {N}")
print(f"e: {e}")
print(f"c: {c}")
N: 913125842482770239379848062277162627509794409924607555622246822717218133091223291889541294440266178282194506242444509803611492259403578922020590849630191477864719052980160940803309686069818208833547621252544423652489179493083138385424424384165228024273745733240109761707533778691158938848158094054261174692601673435971526522219273943464877956131040249169850420336023942653021547841666224446678539579529590840999008107782784268926145671962239929431694391039559247
e: 494518390582436635999115147756676313570637682518235195828939117782099618734167908630788943568232122157772909140885391963441876427590731524706959546524212914108888799081844320513851526790475333924396837458796755678072486028072639014677580265244176441153444956871730684233063789931539669072735599696830757690822185323538738397827461580678488181113667710378657058297572328491762536595872579603698945272140918157163640403488075948987156585480146162739943419183496337465468187233821931312507662218106713861638334075899266373256620752680354704533272722692596941861606161634082613228896420520465402725359166156632884432690715903666803067996854084671477445131853993177110154928274312496230096270510089973592664248613332000290545537840595645944390047611474888693558676781309912289044962293014118087259307560444929227407113819165713213046898243995956550944640168932947118400215917515277554126694376415569909534496134700668701465649939
c: 4450931337369461482106945992542133557585962894030505065110870389112565329875502952762182372926117037373210509516570958483606566274369840551132381128665744266165792377925899683228751870742727716
d ≪ n d\ll n d≪n 考虑 wiener attack,其中需要用 n 2 + 2 n 3 2 + 3 n + 2 n 1 2 n^2+2n^{\frac{3}{2}}+3n+2n^{\frac{1}{2}} n2+2n23+3n+2n21 来近似 p h i phi phi
8.happy_to_solve2
import random
from Crypto.Util.number import *
from secrets import flag
def get_happy_prime():
while 1:
p = int("".join([random.choice("123") for _ in range(512)]))
q = int("".join([random.choice("567") for _ in range(512)]))
if isPrime(p) and isPrime(q):
return (p,q)
m = bytes_to_long(flag)
p ,q= get_happy_prime()
n = p * q
e = 65537
print(n)
print(pow(m, e, n))
# 697906506747097082736076931509594586899561519277373830451275402914416296858960649459482027106166486723487162428522597262774248272216088755005277069446993003521270487750989061229071167729138628583207229945902389632065500739730301375338674342457656803764567184544685006193130563116558641331897204457729877920989968662546183628637193220770495938729301979912328865798266631957128761871326655572836258178871966196973138373358029531478246243442559418904559585334351259080578222274926069834941166567112522869638854253933559832822899069320370733424453856240903784235604251466010104012061821038897933884352804297256364409157501116832788696434711523621632436970698827611375698724661553712549209133526623456888111161142213830821361143023186927163314212097199831985368310770663850851571934739809387798422381702174820982531508641022827776262236373967579266271031713520262606203067411268482553539580686495739014567368858613520107678565628269250835478345171330669316220473129104495659093134763261751546990704365966783697780787341963138501
# 153383826085102296581238539677668696644156148059026868813759015106139131297135097831661048493079405226972222492151356105759235749502324303047037349410709021152255315429280760639113724345836532087970918453353723090554450581657930847674930226113840172368662838756446364482977092478979838209396761279326533419699056209983721842484996150025403009644653678928025861445324715419893797015875541525590135843027312322236085581571452084477262582966972702577136904385741443870527205640874446616413917231260133364227248928492574610248881137364204914001412269740461851747883355414968499272944590071623223603501698004227753335552646715567802825755799597955409228004284739743749531270833084850113574712041224896044525292591264637452797151098802604186311724597450780520140413704697374209653369969451501627583467893160412780732575085846467289134920886789952338174193202234175299652687560232593212131693456966318670843605238958724126368185289703563591477049105538528244632434869965333722691837462591128379816582723367039674028619947057144546
本题很有趣, p p p 的数字限定在123, q q q 的数字限定在567,情况较少,可以进行剪枝搜索
因为 x x x 位的 p p p 与 q q q 相加取模 p o w ( 10 , x ) pow(10,x) pow(10,x) 与 n n n 取模 p o w ( 10 , x ) pow(10,x) pow(10,x) 相等,可以从低位到高位搜索
l1=[1,2,3]
l2=[5,6,7]
def dfs(x,y,t):
if t==512 and x*y==n:
print(x)
return
for i in l1:
for j in l2:
x1=x+i*pow(10,t)
x2=y+j*pow(10,t)
if x1*x2%pow(10,t)==n%pow(10,t):
dfs(x1,x2,t+1)
dfs(p,q,0)
9.重生之我要当oi爷 pro
def f(a, b, p):
t = 0
for i in range(len(b)):
t += pow(a, i, p) * b[i] % p
return t % p
p = 1041231053
a = open("flag.png", "rb").read()
b = open("enc.txt", "w")
for i in range(len(a)):
b.write(str(i) + str(f(i, a, p)) + "\n")
拉格朗日插值法,有 $n $ 对 ( x , y ) (x,y) (x,y)即可求出唯一的 n n n 次多项式,由范德蒙德矩阵的可逆性即可证明。拉个朗日插值的性质也用于群密钥的场景,即一个群体中必须大于k个人同时参与解密才可以解密成功,也就是大于 k 个人才能构造唯一的k次多项式。
10.Random_rr
from Crypto.Util.number import *
from random import randint
flag=b'XYCTF{uuid}'
flag = bytes_to_long(flag)
n = 472993274721871037103726599805149366727531552333249750035977291933239067588481589544777397613192273114354221827196579379954069925604091911249655707080927769808587176515295614018992848517984372306879552247519117116110554431341268358177159108949791969262793325836353834899335531293329721598226413212541536002401507477776699642647348576111445702197483449777741566350285229621935507081895389023444249054515395783080003733803406382744631528246608154546123270319561514117323480441428953306734274538511770278887429407127143049023747710881993279361892937905382946820141513009017756811296722630617325141162244806884220212939955235410280899112731530527048274396186038160728562551536558223235783656985493518204710943916486379681906506757757594165379493317173050550893487151879681122510523721157284728808336110950008840684602353984682117748018347433177541603140491131603068512706893984834735290809952944273565203183330739252949245209529232254867201402656024997949207918675051941911990640248052951780195402390132237903538546705181463959793972284823588987652138458328270662652334799233015314673544813649692428544375538627858921763941533600553536579901589575693816746953261108022490849251974419402753031545629158199093099096735356165044275617408697
rr = 11898141078345200236264081467585899457224809417108457314508072413792599039332439547789237898270544336909458761754683941320649771736625000667170176071314483
def generate():
fw = open("random", "w")
for i in range(648):
fw.write(str(random.getrandbits(32))+"\n")
fw.close()
generate()
key = str(random.getrandbits(32))
key1= int(str(key)[0])
ks = [randint(0, rr**(i+key1-1)) for i in range(16)]
c1 = pow(sum(k*flag**i for i, k in enumerate(ks)), 127, n)
c2 = pow(flag, 65537, n)
ks = [pow(69, k+key, rr**(i+key1)) for i, k in enumerate(ks)]
print(f"{ks = }")
print(f"{c1 = }")
print(f"{c2 = }")
r a n d o m . g e t r a n d b i t s ( ) random.getrandbits() random.getrandbits() 随机数的生成是利用梅森旋转算法 ( M e r s e n n e T w i s t e r ) ( Mersenne Twister ) (MersenneTwister) ,来实现的.
首先需要安装库
用 pip install 方法
mersenne-twister-predictor
通过创建实例来预测 key ,用法如下
from mt19937predictor import MT19937Predictor
with open("random", 'r') as f:
mt = [int(i.strip('\n')) for i in f.readlines()]
predictor =MT19937Predictor()
for i in range(648):
x = mt[i]
predictor.setrandbits(x, 32)
key = predictor.getrandbits(32)
print(key)
11.easy_ecc
from Crypto.Util.number import *
from hashlib import sha256
from secret import flag, secret,SECRET
assert flag[6:-1] == sha256(long_to_bytes(secret)).hexdigest().encode()
class ECC_easy:
def __init__(self):
self.a = 1365855822212045061018261334821659180641576788523935479
self.b = 17329427219955161804703200105884322768704934833367341
self.p = 1365855822212045061018261334821659180641576788523935481
def add(self, P, Q):
mul_inv = lambda x: pow(x, -1, self.p)
x1, y1 = P
x2, y2 = Q
if P!=Q:
l=(y2-y1)*inverse(x2-x1,self.p)%self.p
else:l=(3*x1**2+2*self.a*x1+1)*inverse(2*self.b*y1,self.p)%self.p
temp1 = (self.b*l**2-self.a-x1-x2)%self.p
temp2 = ((2*x1+x2+self.a)*l-self.b*l**3-y1)%self.p
x3 = temp1
y3 = temp2
return x3, y3
def mul(self, x, P):
Q = SECRET
x = x % self.p
while x > 0:
if x & 1:
Q = self.add(Q, P)
P = self.add(P, P)
x >>= 1
return Q
def ispoint(self, x, y):
return (self.a * x ** 2 + x ** 3+x) % self.p == (self.b * y ** 2) % self.p
ecc = ECC_easy()
LLLL = (1060114032187482137663886206406014543797784561116139791,752764811411303365258802649951280929945966659818544966)
assert ecc.ispoint(LLLL[0], LLLL[1])
END = ecc.mul(secret, LLLL)
print(END)
# (695174082657148306737473938393010922439779304870471540,414626357054958506867453055549756701310099524292082869)
待更。。。
12.铜匠
from Crypto.Util.number import *
from secrets import flag
m = bytes_to_long(flag)
m1 = getRandomRange(1, m)
m2 = getRandomRange(1, m)
m3 = m - m1 - m2
def task1():
e = 149
p = getPrime(512)
q = getPrime(512)
n = p * q
d = inverse(e,(p-1)*(q-1))
return (pow(m1, e, n), d >> 222 << 222, n)
def task2():
e = 65537
p = getPrime(1024)
q = getPrime(1024)
n = p * q
return (pow(m2, e, n), (p + q) & ((1 << 624) - 1), n)
def task3():
e = 65537
p = getPrime(512)
q = getPrime(512)
n = p * q
return (pow(m3, e, n), (p ^ q) >> 200, n)
c1, leak1, n1 = task1()
c2, leak2, n2 = task2()
c3, leak3, n3 = task3()
print(c1, leak1, n1)
print(c2, leak2, n2)
print(c3, leak3, n3)
# (89623543982221289730635223555830551523170205418976759060541832483843039074358160566735322009158596405712449020903311144480669706226166537602750967447480664875090686428406188847601970724994074417752345460791736191511081890913041143570455636020205647345764692062144226011846336769477026234106683791286490222089, 138474880017294332349992670187778287774153347391371789199005713563195654993662610111557185709277805165708109047494971468100563949333712521647405765501704478862377527360106399421209690341743821320754482338590671565510629203215009008479290995785318405211007685664887994061938667418220613430135743123498167435264, 146331610798417415036517077006943013321623040860385791423062775325646472298267580898028515394910588437521335092742913111680737790430660749825981979147191282007208147041227246620008377726207734522466015971515317594545750944838673018946440525615131606652748549901880641896940968837669894325535750125282351577689)
# (5473961166821344576614003060897848014482672788904094340704447882490091115468180944569409159343222459801235179587721232166576530621751748850763430788036935832653535110975154800191323351333883661451331971042214143454441651165718504131976920077768864850035471790911190772583977173388975105970866592974077361193822302653046511418190410043729876062517148438923211673300268277037622337403526399584759202097925983596261279676101079771530118525540842499517622478819211528345668513680064744443628779623340175578509413636950545134641050948185944279029949957748464529075830770617236599864271566534714755373562527112224962839980,62964793504732923650344975634773688108135580826064134090114181449607062497690184718845295432644650580930430061411603385577783897575232298084007041511817031640186953023971498914096209984730,20748652069632848434897928314437138341436264859802586939154590237186029244907936870477844563166827587536149170710720365760129683024401957095447466056746469055173897234659911291443605912459271248059341147358511860537769587963189092648473894868209838600346115919726589891777340166174017389513260737891557712152871714337946675533597049874155202056200170954033849176655928144354665553271709442011723088448485570394208728775665739819536229908847043007472178803394055783543378990699834066614262050119443421709878598533329555838915158259138297060574425019923291353077080236769586821808150397875920110335669136563171420068201)
# (34640310217688693418173336791306022698488916505988760907493665070072623574363578354500529023855888624978101429772253437880445815006839767802433777006836665709335479076676231470534690281412388704665083311568028710188940132495410474044569100181764053297307413009214044407982980600917158732129850605715306726034, 3763587651775261955566046684899146246387485307521708557610018711932791630073528010801142052497, 94848869174430082173244966077736519396702141299429965725051314270443078621842166791082354594193554380275167898342497998871366256044865879909218309960595691008663632410356093966762639308253848450178310347612630814852054763933063313537694449475061227647475480417779126252294793767003555255527593551612032661749)
先简要分析一下, task1 对应的是 d d d 高位泄露的情况,task2 对应部分 $p+q $泄露的情况,task3对应部分 p ⊕ q p\oplus q p⊕q 的情况,其主要思想就是根据泄露的信息先还原出因数的一定位数,再利用 c o p p e r s m i t h coppersmith coppersmith 攻击,还原剩下的位数。
在本题需要定义三个搜索函数,针对每种泄露情况的特定进行剪枝。
(1)根据高位 p + q p+q p+q 泄露进行搜索,代码的逻辑就是每次根据 $ p+q$ ( gift ) 的最高位来确定 p , q 的最高位,递归执行。根据泄露的 gift 来分几种情况搜索,gift 是指根据前面搜索情况得到的,已经确定高位后剩下的 $ p+q$ , 看 gift 的最高两位即可 (考虑到进位,p+q 的 x 位和会影响 gift 上的 x 位与 x+1 位) , 把这个定义为当前位。
当 gift 当前位为 1, p 与 q 的最高位 (这里的最高位是指搜索到一定位数的最高位,不是一开始的最高位) 一定没有发生进位, p,q 在 x 位上有一个为 1或者全为0 ;
当 gift 当前位为 2, 则一定发生了进位,要么是 p,q 的最高位进位,此时 p,q 最高位都是1,要么是 p,q 的下一位进位,且 p 与 q 最高位其中一个是 1, 此时 p,q 最高位有一个是1;
当 gift 当前位为 3, 一定发生了进位,而且是 p,q 最高位带来的进位,因此 p,q 最高位全为1.
根据这种策略搜索下去,并注意 p,q 大小上的剪枝 。
def pq_add(tp,tq,tgift,idx):
global ok
if ok:
return
if tp*tq>N:
return
if (tp+(2<<idx))*(tq+(2<<idx))<N:
return
if idx<=227:
try:
print("time")
f=x+tp
f=f.monic()
root=f.small_roots(X=2^230,beta=0.5,epsilon=1/32)
if root!=[]:
print(root[0]+tp)
ok=True
except:
pass
return
idx -=1
b = tgift >>idx
one = 1<<idx
if b==0 or b==1:
pq_add(tp,tq,tgift,idx)
if b==1 or b==2:
pq_add(tp+one,tq,tgift-one,idx)
pq_add(tp,tq+one,tgift-one,idx)
if b==2 or b==3:
pq_add(tp+one,tq+one,tgift-(one<<1),idx)
(2) 根据高位 p ⊕ q p \oplus q p⊕q 泄露进行搜索,与前面的思路差不多,都是根据异或结果确定 p,q 当前位。
def pq_high_xor(p="", q=""):
lp, lq = len(p), len(q)
tp0 = int(p + (512-lp) * "0", 2)
tq0 = int(q + (512-lq) * "0", 2)
tp1 = int(p + (512-lp) * "1", 2)
tq1 = int(q + (512-lq) * "1", 2)
if tp0 * tq0 > n or tp1 * tq1 < n:
return
if lp == leak_bits:
pq.append(tp0)#定义一个列表保存结果
return
if xor[lp] == "1":
pq_high_xor(p + "0", q + "1")
pq_high_xor(p + "1", q + "0")
else:
pq_high_xor(p + "0", q + "0")
pq_high_xor(p + "1", q + "1")
for p_high in pq:
R.<x> = PolynomialRing(Zmod(n), 'x')
f = p_high + x
res = f.monic().small_roots(X=2**201, beta=0.4)
if res:
p = int(f(res[0]))
print(p)
break
(3) 根据 p + q p+q p+q 低位泄露进行搜索,这种思路在 happy_to_solve2 里提到过, x x x 位的 p p p 与 q q q 相加取模 p o w ( 2 , x ) pow(2,x) pow(2,x) 与 n n n 取模 p o w ( 2 , x ) pow(2,x) pow(2,x) 相等, 不同的是,需要更新进位 carry .
def get_pq_add(p,q,kb,gift,carry):
if n%pow(2,kb)!=p*q%pow(2,kb):
return
if kb==624:
pq.append(p)#定义一个列表保存结果
return
xg=gift%2
gift=gift>>1
if xg==0:
if carry==0:
get_pq_add(p,q,kb+1,gift,0),get_pq_add(p+(1<<kb),q+(1<<kb),kb+1,gift,1)
else:
get_pq_add(p,q+(1<<kb),kb+1,gift,1),get_pq_add(p+(1<<kb),q,kb+1,gift,1)
if xg==1:
if carry==0:
get_pq_add(p,q+(1<<kb),kb+1,gift,0),get_pq_add(p+(1<<kb),q,kb+1,gift,0)
else:
get_pq_add(p,q,kb+1,gift,0),get_pq_add(p+(1<<kb),q+(1<<kb),kb+1,gift,1)
for p in pq:
f=x*2^624+p
f=f.monic()
root=f.small_roots(X=2^410,beta=0.4)
if root!=[]:
print(root[0]*2**624+p)
其中task1最难,需要先确定k的取值,来通过 φ ( n ) = e d − 1 k \varphi (n)=\frac{ed-1}{k} φ(n)=ked−1 得到 p + q p+q p+q 的高位,我用的下面的方法确定
PR.<x> = PolynomialRing(ZZ)
for i in range(1,e+1):
f=x+e*d1-1-i*(n1+1)
m=f.roots()[0][0]
if m>=0 and m<n1:
print(i,m)
k=141
13.happy_to_solve3
import gmpy2
from Crypto.Util.number import *
import random
n = 1024
rho = 2
gamma = 0.352
delta = (1 - gamma) / 2
def get_happy_prime():
p = getPrime(n // 2)
q = getPrime(n // 2)
N = p * q
if 2 * q - p > gmpy2.iroot(N, 3)[0] and 2 * q - p < int(N**gamma / 16):
d = random.randint(int(N ** (delta - 0.001)), int(N**delta))
e = gmpy2.invert(d, (p - 1) * (q - 1))
return N, e
N, e = get_happy_prime()
m = bytes_to_long(1)
print(N)
print(e)
print(pow(m, e, N))
# 262520792590557046276663232446026434827811478251833823563978322470880473096166170973396967071440576532100045206129176936699321058518888771220060450723297988984995658582283884954240129867538637146924315603797818982078845855992582229939200744016516540207525916858674450616913948112117516831530578235916528257187
# 202935305174706906986376186864051444100197589482194720650385604617995167023220940138899902073948844285283834058445151666398192104922822110762965750590312021079147170573038600118139119881722125346545331027913695559130179278058419339157557267195396326664433859683010193688491694572425231599139974726246205888139
# 66173406204647583141174847814283540389326401056490970592017577810775087017197392456634167609680060931553017712058528056769618111925816921272571306246667527546366331057781905353553621522209713637253380859235235590286779517552635935384231694255450099095196595516174794966487058693308544109311774074970769293357
本题是针对特定情况下的wiener攻击的研究,要点是根据 N 来尽量准确估计 $\varphi(n) $ 。参考论文 Revisiting Wiener’s Attack – New Weak Keys in RSA?
论文中提到,
与本题正好相符
对于 p − q p-q p−q 的情况,有
对于更一般的情况 p − ρ q p-\rho q p−ρq, 有如下估计证明
本题是 ρ = 2 \rho=2 ρ=2 的情况,直接带入就好了, n − 3 2 n + 1 n-\frac{3}{\sqrt{2}}\sqrt{n}+1 n−23n+1 可以作为分母进行连分数攻击