记录一下xyctf2024

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 2512p1,

q q q 2 512 − p − 1 2^{512}-p-1 2512p1的下一个素数也就是说 q q q 2 512 − p − 1 2^{512}-p-1 2512p1 很接近,也即 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) (p31)(q31) 下的逆元, ( p 3 − 1 ) ( q 3 − 1 ) (p^3 - 1) (q^3 - 1) (p31)(q31) n 3 n^3 n3 较为接近(当然可以进行更精确的估计,但本题已经足够了),想到如果以 n 3 n^3 n3 为分母做连分数攻击,求出d之后再求出 ( p 3 − 1 ) ( q 3 − 1 ) (p^3 - 1) (q^3 - 1) (p31)(q31) 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} badc 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+1k(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+q2n21, k ≈ d k\approx d kd, 最右边那一部分与 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} nedk2d21
因此 2 n 1 2 ≤ 1 2 d 2 \frac{2}{n^{\frac{1}{2}}}\le \frac{1}{2d^2} n2122d21,推出来 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+1k(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 ed10(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) ed1=k(p31)(q31)化为 e d − 1 = k ′ ( p − 1 ) ( q − 1 ) ed-1=k^{'}(p-1)(q-1) ed1=k(p1)(q1),

通过以下方法分解 n n n:

k = e d − 1 k=ed-1 k=ed1 ,选取小于 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) gk1(modn)(g2k1)(g2k+1)0(modn),这样迭代下去,则 g k 2 − 1 g^{\frac{k}{2}}-1 g2k1很可能与 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 gk1能够分解成两个因数,因此要么其中一个因数含有因子 p q pq pq,要么一个因数含有因子 p p p,另一个含有因子 q q q.

对于第一种情况,若是 g k 2 − 1 g^{\frac{k}{2}}-1 g2k1含有因子 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 pollards p1算法分解 n n n

Pollard’s  p − 1 \text{Pollard's }p-1 Pollard’s p1

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 p1=p1p2p3pk p − 1 p-1 p1的素因子不同且均小于 B B B则有 p 1 p 2 p 3 … p k ∣ B ! p_1p_2p_3\dots p_k|B! p1p2p3pkB!(考虑到有相同的时候,素因子较小时仍然可以被涵盖,例如 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) ap11(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=kp,

于是 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) cxm4(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,q1)=2,GCD(e,p1)=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) m2c1(modp),m2c2(modq)的加密, p , q ≡ 3 (   m o d   4 ) p,q\equiv 3(\bmod 4) p,q3(mod4)

对于 x 2 ≡ c (   m o d   p ) x^2\equiv c(\bmod p) x2c(modp),因为 c c c是二次剩余,根据欧拉判别法, c p − 1 2 ≡ 1 (   m o d   p ) c^{\frac{p-1}{2}}\equiv 1(\bmod p) c2p11(modp),

于是有 c p + 1 2 ≡ c (   m o d   p ) c^{\frac{p+1}{2}}\equiv c (\bmod p) c2p+1c(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+1c21m(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= 90366027010 ,转化为 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=TJT1An=TJnT1m[a,b,c]T 18006n180n2n6n18 =[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 18x11a(6na+18b)x21(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)na+bi(modp),在等式两端同时乘上其共轭,依然成立, 5 8 n ≡ a 2 + b 2 ( m o d p ) 58^n\equiv a^2+b^2\pmod p 58na2+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}} Xneβ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 dn 考虑 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 pq 的情况,其主要思想就是根据泄露的信息先还原出因数的一定位数,再利用 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 pq 泄露进行搜索,与前面的思路差不多,都是根据异或结果确定 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)=ked1 得到 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 pq 的情况,有

在这里插入图片描述

对于更一般的情况 p − ρ q p-\rho q pρq, 有如下估计证明

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

本题是 ρ = 2 \rho=2 ρ=2 的情况,直接带入就好了, n − 3 2 n + 1 n-\frac{3}{\sqrt{2}}\sqrt{n}+1 n2 3n +1 可以作为分母进行连分数攻击

  • 16
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值