complex rsa
复数rsa,没遇到过这种类型的题,可以记录一下相关知识
先来看一段
from gmpy2 import invert,lcm,is_prime
import sys
sys.setrecursionlimit(2047)
f = (378122348642214690905411683807377396279362526734068034297186493255873563264253248095197659138069664908850853277799239471404715546747080714581633876343291058815268009173602365587463522797812239900814474973941995698621021680129530453282192603316731832323767320307650941745085796583822798379896337325L, 205549984221850341303682190742446959375043769671555741781145106776498798455293849755553794941345135675348633855787880355726112506553703853175271830126908219803257875131387292937296039523359975622001865593227631119497003599166091642640101746534401068507972834895023584843991641629874709898672559632L)
e = 59107
p = 228517792080140341
q = 1675909164550923263854591345270445396052847869117231939809062226222204253885693425526434134321712288675268468398852452684029376569327518089966506865838909486699078280423099271324646863671350838232140981094611254627738568184261530942469845202934677427234062382272736609418198352717
n = p*q
def cadd(a,b,n):
return (a[0]+b[0]%n,a[1]+b[1]%n)
def cmul(a,b,n):
return ((a[0]*b[0]-a[1]*b[1])%n,(a[0]*b[1]+a[1]*b[0])%n)
def cpow(a,k,n):
if(k==0):
return (1,0)
if(k==1):
return a
if(k%2==0):
a=cmul(a,a,n)
return cpow(a,k/2,n)
else:
return cmul(a,cpow(cmul(a,a,n),(k-1)/2,n),n)
o=lcm((p*p-1),(q*q-1))
fm=cpow(f,invert(e,o),n)
print(fm)
来源:https://github.com/Ariana1729/CTF-Writeups/blob/master/2019/CryptoCTF/Complex%20RSA/README.md
We first consider the order of C/pC* where p is a prime. The real and imaginary parts ranges from 0 to p-1, so a valid assumption is that the order is a multiple of p*p-1(since 0,0 can't be in the group). The order is exactly p*p-1 for p=3 mod 4 since it cannot be expressed as the sum of 2 squares, but for p=1 mod 4 it is less, and it is a multiple of p*p-1. Thus if we want to find g given g^e=a mod p, we simply invert e under p*p-1. For n=pq, the order is a multiple of lcm(p*p-1,q*q-1), then it's trivial For factoring n we simply use yafu
函数cadd:实现两个复数的加法。
函数cmul:实现两个复数的乘法
函数cpow:实现复数的幂运算,其中k为指数,n为模数
该段实现了一个复数域上的rsa解题过程
复数中欧拉函数:
p
h
i
(
p
)
=
p
2
−
1
p
h
i
(
q
)
=
q
2
−
1
p
h
i
(
n
)
=
(
p
2
−
1
)
∗
(
q
2
−
1
)
phi(p) = p^2 - 1\\phi(q) = q^2 - 1\\phi(n) = (p^2 - 1)*(q^2 - 1)
phi(p)=p2−1phi(q)=q2−1phi(n)=(p2−1)∗(q2−1)
题1
[GeekChallenge-2023] EzComplex
题目描述:
from Crypto.Util.number import *
flag = b'FAKE{Do_You_know_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)
print(c)
print(N)
'''
122977267154486898127643454001467185956864368276013342450998567212966113302012584153291519651365278888605594000436279106907163024162771486315220072170917153855370362692990814276908399943293854077912175867886513964032241638851526276
973990451943921675425625260267293227445098713194663380695161260771362036776671793195525239267004528550439258233703798932349677698127549891815995206853756301593324349871567926792912475619794804691721625860861059975526781239293017498
'''
题目分析:
N
=
p
2
+
q
2
=
(
p
+
q
∗
i
)
∗
(
p
−
q
∗
i
)
N = p^2 + q^2 = (p + q*i)*(p - q*i)
N=p2+q2=(p+q∗i)∗(p−q∗i)
遍历所有因子得到p,q,之后常规rsa解密
from Crypto.Util.number import *
c = 122977267154486898127643454001467185956864368276013342450998567212966113302012584153291519651365278888605594000436279106907163024162771486315220072170917153855370362692990814276908399943293854077912175867886513964032241638851526276
N = 973990451943921675425625260267293227445098713194663380695161260771362036776671793195525239267004528550439258233703798932349677698127549891815995206853756301593324349871567926792912475619794804691721625860861059975526781239293017498
e = 65537
zn = ZZ[i](N)
for d in divisors(zn):
p = int(d[0])
q = int(d[1])
if isPrime(p) and isPrime(q) and p.bit_length() > 380:
break
phi = (p-1)*(q-1)
n = p*q
d = inverse(e,phi)
print(long_to_bytes(int(pow(c,d,n))))
# SYC{D0_you_like_r41n?_i_pref3r_R1_ng}
ZZ(i)[N]:创建了一个整数高斯环。这个环的元素由整数和虚数部分组成,且整数和虚数部分都是整数。其中N是一个整数,表示高斯环的模,元素中整数和虚数部分的取值范围是[-N/2, N/2]
假设复数: a + b ∗ i a + b * i a+b∗i,那么 a,b ∈[-N/2, N/2]
其中有一种特殊情况:a = N,b = 0
题2
题目描述:
import os
import hashlib
from Crypto.Util.number import *
from secret import flag, totient
# where totient is a function used to calculate phi
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 show(self):
print([self.re, self.im])
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
def pad(msg, length):
pad_length = length - len(msg) - 1
pad_data = os.urandom(pad_length)
return msg + b'\x00' + pad_data
def unpad(msg):
return msg.split(b"\x00")[0]
bits = 512
p = getPrime(bits)
q = getPrime(bits)
n = p * q
sha_flag = hashlib.sha256(flag).digest()
m1 = Complex(
int.from_bytes(sha_flag[:len(sha_flag)//2], "big"),
int.from_bytes(sha_flag[len(sha_flag)//2:], "big"),
)
m2 = Complex(
int.from_bytes(pad(flag[:len(flag)//2], bits//4-1), "big"),
int.from_bytes(pad(flag[len(flag)//2:], bits//4-1), "big"),
)
phi = totient(p, q)
e = q * inverse(p, phi)
c1 = complex_pow(m1, e, n)
c2 = complex_pow(m2, e, n)
c1.show()
c2.show()
print(f'n = {n}')
"""
[90554536599623574119664951128649936419332926063696768860765928746438458550068553748440108394673303800443215316190882880737918820592384729010491685487061658710808286341751196450604089438847354206384322610922839055308138101241906861635339635907663440043442187064090630207952625897567214431195621589834131462698, 9144096375153318849308858335764188418198064372272913164911615933938183103747900881824918069830188301084043148828961577193063557255905230182831945580084452509300200269659063051152684191139872067872645370760797859584822240361290678189844670289832298393156571913616456958845361092243648857334156534377833472900]
[62925714576233017213228404230949787334346543378320798964656732359587152905032848271156799538355748406136742979043729040728123730886381468564779041856310262770766050213464073568850702827835472680885186487027698395099598698463717279017013124488699475168052581476224742146967412904416266652605031934025266540003, 62818668456104375760667670741457826560706388018921820295286033114468271151921637926389738844622672202424650967678199715932465104135980734708459543588178208672956785650944371545080965650112025782049517299538052360417245732776384089052839997333049599655001615752078742624898059780909287845495731050387891926520]
n = 94040393367054633265453751757391098049234338193258976478647369399924701067077628840760704857546243644552533845934146003988635403227234096447871132283820920489003286967145732739404245319615714787916756200564828237043658350145929927911058782352154997346295194977765305107634012698472977467843980475009837261877
"""
题目分析:
可以看出是一道基于复数的rsa,re实部,im虚部
complex_pow(m,e,n)
函数计算的是
m
e
m
o
d
n
m^e \mod n
memodn
其中phi = totient(p,q)
,e = q * inverse(p,phi)
里面提示totient is a function used to calculate phi
,而此题又是基于复数域上的,那么可以猜测
p
h
i
=
t
o
t
i
e
n
t
(
p
,
q
)
=
(
p
2
−
1
)
∗
(
q
2
−
1
)
phi = totient(p,q) = (p^2-1)*(q^2-1)
phi=totient(p,q)=(p2−1)∗(q2−1)
从加密流程可以看出:
c1的实部是sha_flag前半部分,虚部是sha_flag后半部分
c2的实部是flag前半部分,虚部是flag后半部分
e
≡
q
∗
p
−
1
m
o
d
p
h
i
e
∗
p
≡
q
m
o
d
p
h
i
e
∗
n
≡
q
2
m
o
d
p
h
i
c
1
n
≡
m
1
e
∗
n
≡
m
1
q
2
m
o
d
n
c
1
n
≡
m
1
q
2
≡
m
1
m
o
d
q
(
回到上面
p
h
i
(
q
)
=
q
2
−
1
)
e \equiv q * p^{-1} \mod phi\\ e*p \equiv q \mod phi\\ e * n \equiv q^2 \mod phi\\ c_1^n \equiv m_1^{e*n} \equiv m_1^{q^2} \mod n\\ c_1^n \equiv m_1^{q^2} \equiv m_1\mod q\\ (回到上面phi(q) = q^2 - 1)\\
e≡q∗p−1modphie∗p≡qmodphie∗n≡q2modphic1n≡m1e∗n≡m1q2modnc1n≡m1q2≡m1modq(回到上面phi(q)=q2−1)
c
1
n
−
m
1
=
k
∗
q
c_1^n - m_1 = k * q
c1n−m1=k∗q
m
1
m_1
m1只有128比特,比q的一半位数还小的多,完全可以copper求出
m
1
m_1
m1
之后gcd(k*q,n)得到q,那么p,q就都出了
然后常规rsa解题思路得到flag
注意,这里实现pow用它定义的complex_pow()
from Crypto.Util.number import *
from gmpy2 import *
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 show(self):
print([self.re, self.im])
def get_value(self):
return self.re,self.im
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
c1 = [90554536599623574119664951128649936419332926063696768860765928746438458550068553748440108394673303800443215316190882880737918820592384729010491685487061658710808286341751196450604089438847354206384322610922839055308138101241906861635339635907663440043442187064090630207952625897567214431195621589834131462698, 9144096375153318849308858335764188418198064372272913164911615933938183103747900881824918069830188301084043148828961577193063557255905230182831945580084452509300200269659063051152684191139872067872645370760797859584822240361290678189844670289832298393156571913616456958845361092243648857334156534377833472900]
c2 = [62925714576233017213228404230949787334346543378320798964656732359587152905032848271156799538355748406136742979043729040728123730886381468564779041856310262770766050213464073568850702827835472680885186487027698395099598698463717279017013124488699475168052581476224742146967412904416266652605031934025266540003, 62818668456104375760667670741457826560706388018921820295286033114468271151921637926389738844622672202424650967678199715932465104135980734708459543588178208672956785650944371545080965650112025782049517299538052360417245732776384089052839997333049599655001615752078742624898059780909287845495731050387891926520]
n = 94040393367054633265453751757391098049234338193258976478647369399924701067077628840760704857546243644552533845934146003988635403227234096447871132283820920489003286967145732739404245319615714787916756200564828237043658350145929927911058782352154997346295194977765305107634012698472977467843980475009837261877
c1 = Complex(c1[0],c1[1])
c2 = Complex(c2[0],c2[1])
cc1 = complex_pow(c1,n,n).get_value()
P.<m> = Zmod(n)[]
f = cc1[0] - m
f = f.monic()
m1 = f.small_roots(2^128,0.4)[0]
q = gcd(cc1[0]-int(m1),n)
p = n // q
phi = (p^2 - 1) * (q ^ 2 - 1)
e = q * inverse(p, phi)
d = inverse(e,phi)
m2 = complex_pow(c2,d,n).get_value()
print(long_to_bytes(int(m2[0])))
print(long_to_bytes(int(m2[1])))
# flag{3ef6db06-b837-11ed-9825-00155dfcdef9}
感觉这道题真的很有意思
浅记一下:
关键词:
复数欧拉,ZZ(i)[N],coppersmith