from Crypto.Util.number import getPrime,bytes_to_long,getRandomNBitInteger
from secret import flag
from gmpy2 import gcd
def gmc(a, p):
if pow(a, (p-1)//2, p) == 1:
return 1
else:
return -1
def gen_key():
[gp,gq] = [getPrime(512) for i in range(2)]
gN = gp * gq
return gN, gq, gp
def gen_x(gq,gp):
while True:
x = getRandomNBitInteger(512)
if gmc(x,gp) ^ gmc(x,gq) == -2:
return x
def gen_y(gN):
gy_list = []
while len(gy_list) != F_LEN:
ty = getRandomNBitInteger(768)
if gcd(ty,gN) == 1:
gy_list.append(ty)
return gy_list
if __name__ == '__main__':
flag = bin(bytes_to_long(flag))[2:]
F_LEN = len(flag)
N, q, p = gen_key()
x = gen_x(q, p)
y_list = gen_y(N)
ciphertext = []
for i in range(F_LEN):
tc = pow(y_list[i],2) * pow(x,int(flag[i])) % N
ciphertext.append(tc)
with open('./output.txt','w') as f:
f.write(str(N) + '\n')
for i in range(F_LEN):
f.write(str(ciphertext[i]) + '\n')
分析代码,发现x 的生成涉及到平方剩余。利用雅可比符号可表示为
(
x
p
)
(
x
q
)
=
−
1
(\dfrac{x}{p})(\dfrac{x}{q})=-1
(px)(qx)=−1。观察加密部分,根据flag[i]的值有以下两种情况:
{
t
c
=
y
i
2
m
o
d
n
,
f
l
a
g
[
i
]
=
0
t
c
=
y
i
2
∗
x
m
o
d
n
,
f
l
a
g
[
i
]
=
1
\begin{cases} tc = y_i^2\quad mod\ n\ ,\quad flag[i] = 0 \\ tc = y_i^2*x\quad mod\ n\ ,\quad flag[i] = 1 \end{cases}
{tc=yi2mod n ,flag[i]=0tc=yi2∗xmod n ,flag[i]=1
y
i
y_i
yi是模 n 的平方剩余,于是利用雅可比符号的可乘性,上面两个式子是否平方剩余可表示为
{
(
t
c
n
)
=
(
y
i
2
n
)
=
1
,
f
l
a
g
[
i
]
=
0
(
t
c
n
)
=
(
y
i
2
n
)
(
x
n
)
=
(
x
p
)
(
x
q
)
=
−
1
,
f
l
a
g
[
i
]
=
1
\begin{cases} (\dfrac{tc}{n}) = (\dfrac{y_i^2}{n})=1\ ,\quad flag[i] = 0 \\ (\dfrac{tc}{n}) = (\dfrac{y_i^2}{n})(\dfrac{x}{n}) = (\dfrac{x}{p})(\dfrac{x}{q})=-1 \ ,\quad flag[i] = 1 \end{cases}
⎩
⎨
⎧(ntc)=(nyi2)=1 ,flag[i]=0(ntc)=(nyi2)(nx)=(px)(qx)=−1 ,flag[i]=1
根据雅可比的值的不同,推出flag。
from Crypto.Util.number import *
from gmpy2 import jacobi
f = open(r'output.txt','r')
n = int(f.readline())
cipher = f.readlines()
flag = ''
for each in cipher:
tc = int(each)
if jacobi(tc,n) == -1:
flag += '1'
else:
flag += '0'
print(long_to_bytes(int(flag,2)))
参考: