# sagemath
from Crypto.Util.number import *
from os import urandom
from Flag import FLAG, MAXLEN
New_Year = 2024
FLAG = FLAG + urandom(MAXLEN - len(FLAG))
# Verify the Goldbach's Conjecture
Goldbach_christian = [(p, New_Year - p) for p in sieve_base if p <= New_Year // 2 and isPrime(New_Year - p)]
# 2024 is a Leap Year!
Happy_2024, Haqqy_2024 = getPrime(New_Year // 4), getPrime(New_Year // 4)
while (Happy_2024 % 3 == Haqqy_2024 % 3):
Happy_2024 = getPrime(New_Year // 4)
Hanny_2024 = Happy_2024 * Haqqy_2024
# The exuberant new year of 2024 will bring us joy and excitement!
p, q = choice(Goldbach_christian)
Exuberant_p = EllipticCurve(GF(Happy_2024), [0, p])
Exuberant_q = EllipticCurve(GF(Haqqy_2024), [0, q])
# Happy New Year!
m = bytes_to_long(FLAG)
with open(r"./output.txt", "w") as f:
gift1 = bytes_to_long(str(Exuberant_p.order() + q).encode()) * bytes_to_long(str(Haqqy_2024 + p).encode())
gift2 = bytes_to_long(str(Happy_2024 + q).encode()) * bytes_to_long(str(Exuberant_q.order() + p).encode())
print(f"I'll give the gifts to U: {Hanny_2024, gift1, gift2}", file = f)
red_envelope1 = pow(Happy_2024 * m + Exuberant_p.order(), Haqqy_2024, Hanny_2024)
red_envelope2 = pow(Haqqy_2024 * m + Exuberant_q.order(), Happy_2024, Hanny_2024)
print(f"OKOK ~ I'll give U both my red envelopes: {(red_envelope1 + red_envelope2) % Hanny_2024}", file = f)
关注到代码片段:
Happy_2024, Haqqy_2024 = getPrime(New_Year // 4), getPrime(New_Year // 4)
while (Happy_2024 % 3 == Haqqy_2024 % 3):
Happy_2024 = getPrime(New_Year // 4)
p, q = choice(Goldbach_christian)
Exuberant_p = EllipticCurve(GF(Happy_2024), [0, p])
Exuberant_q = EllipticCurve(GF(Haqqy_2024), [0, q])
此处素数模3是一个十分重要的提示,根据有限域上椭圆曲线点群的相关结论,如果素数 $p \equiv 2 \pmod{3}$ ,那么 $ \mathbb{F}_p $ 上形如 $ y^2 = x^3 + C $ 的椭圆曲线的阶一定满足 $ \lvert E(\mathbb{F}_p)\rvert = p + 1 $ 。因此上述代码一定满足下列条件:
assert (Exuberant_p.order() == Happy_2024 + 1 and Exuberant_q.order() != Haqqy_2024 + 1) or (Exuberant_p.order() != Happy_2024 + 1 and Exuberant_q.order() == Haqqy_2024 + 1)
证明 $p \equiv 2 \pmod{3}$ 的情况:
根据熟知的有限域上椭圆曲线点的计算公式(公式依据就是遍历每个 $ x $ ,计算 $ y $ 可以开平方的数量):
注:
考虑 $ p \equiv 2\pmod{3} $,不考虑$k = 0$ ,由于 $ k_1 ^ 3 + c = k_2 ^ 3 + c $ $ \Leftrightarrow $ $ k_1 ^ 3 = k_2 ^ 3 $ ,而 $ p - 1 \equiv 1\pmod{3} $ ,根据费马小定理 $ k_1 ^ {p-1} = k_2^{p-1} = 1 $ ,那么 $ k_1 ^ 3 + c = k_2 ^ 3 + c $ $ \Leftrightarrow $ $ k_1^{3\cdot \frac{p-2}{3}} = k_2^{3\cdot \frac{p-2}{3}} $ $ \Leftrightarrow $ $ k_1 = k_2 $ 。因此 $ k^3+c $ 恰好构成 $ p $ 的一个完全剩余系。(听起来高级一点的说法就是 $ x \mapsto x^3 + c $ 为 $ \mathbb{F}_p^{*} $ 上的一个内自同构)
二次剩余和二次非剩余的数量相等;$0$ 的勒让德符号值为 $0$。
记 $p_H$ 和 $q_H$ 分别表示 Happy_2024
和 Haqqy_2024
,$n_H$ 表示 Hanny_2024
,另外记 $a$ 和 $b$ 分别表示 p
和 q
$p_H, q_H$ 在十进制下可以被写成如下形式:
记 f = lambda x: bytes_to_long(str(x).encode())
为 $f$ 因此 $f(x)$ 就可以写成:
根据上一条对于有限域上椭圆曲线点群阶的讨论,不妨设 Exuberant_p.order() == Happy_2024 + 1
,记 $ n_g $ 表示这里的 gift1 = f(Exuberant_p.order() + q) * f(Haqqy_2024 + p)
,那么就有:
考虑 $ n_H \equiv (\sum\limits_{j=0}^{i-1} p_j 10^j) \cdot (\sum\limits_{j=0}^{i-1} q_j 10^j) \pmod{10^i} $ 以及 $ n_g \equiv (\sum\limits_{j=0}^{i-1} (48+p_j’) 256^j) \cdot (\sum\limits_{j=0}^{i-1} (48+q_j’) 256^j) \pmod{256^i} $ 从低到高逐数位恢复即可。
这里可以使用递推或者递归实现,但是递归实现小心爆栈。
New_Year = 2024
Goldbach_christian = [(p, New_Year - p) for p in sieve_base if p <= New_Year // 2 and isPrime(New_Year - p)]
根据上述代码可知,列表 Goldbach_christian
空间是很小的,因此枚举即可
至此已经可以求出两个素数了,下面先给出一部分代码。
from Crypto.Util.number import *
New_Year = 2024
Goldbach_christian = [(p, New_Year - p) for p in sieve_base if p <= New_Year // 2 and isPrime(New_Year - p)]
def factor(nh, ng, a, b, f = lambda x: bytes_to_long(str(x).encode())):
nh_p = None
def test_digits(ps, qs):
nonlocal nh_p
if nh_p is not None:
return False
p = sum([pi * 10 ** i for i, pi in enumerate(ps)])
p_with_a = p + a
new_ps = [int(i) for i in str(p_with_a).zfill(len(ps))[::-1]][:len(ps)]
pp = sum([(48 + pi) << (i * 8) for i, pi in enumerate(new_ps)])
q = sum([pi * 10 ** i for i, pi in enumerate(qs)])
q_with_b = q + b
new_qs = [int(i) for i in str(q_with_b).zfill(len(qs))[::-1]][:len(qs)]
qq = sum([(48 + qi) << (i * 8) for i, qi in enumerate(new_qs)])
if p != 0 and p != 1 and nh % p == 0:
nh_p = p
return False
m1 = 10 ** len(ps)
m2 = 1 << (len(qs) * 8)
return (p * q) % m1 == nh % m1 and (pp * qq) % m2 == ng % m2
stack = [([], [])]
while stack:
ps, qs = stack.pop()
stack += [(ps + [i], qs + [j]) for i in range(10) for j in range(10) if test_digits(ps + [i], qs + [j])]
ng_p = f(nh_p + a)
assert ng % ng_p == 0
return nh_p, nh // nh_p
New_Year = 2024
Goldbach_christian = [(p, New_Year - p) for p in sieve_base if p <= New_Year and isPrime(New_Year - p)]
def get_factors(n, nn, ABlist):
Hp, Hq, p, q = None, None, None, None
for a, b in ABlist:
try:
Hp, Hq = factor(n, nn, a + 1, b)
print(Hp, Hq, a, b)
p, q = a, b
break
except: pass
return Hp, Hq, p, q
pH, qH, p, q = get_factors(n, g1, Goldbach_christian)
if pH is None or qH is None:
pH, qH, p, q = get_factors(n, g2, Goldbach_christian)
这一部分是一个简单的数论变换。记 $p_H$ 和 $q_H$ 分别表示 Happy_2024
和 Haqqy_2024
,$n_H$ 表示 Hanny_2024
,另外记 $\omega_p$ 和 $\omega_q$ 分别表示 Exuberant_p
和 Exuberant_q
,这些变量现在都已经是已知的了
那么根据代码片段:
red_envelope1 = pow(Happy_2024 * m + Exuberant_p.order(), Haqqy_2024, Hanny_2024)
red_envelope2 = pow(Haqqy_2024 * m + Exuberant_q.order(), Happy_2024, Hanny_2024)
print(f"OKOK ~ I'll give U both my red_envelopes: {(red_envelope1 + red_envelope2) % Hanny_2024}", file = f)
可以整理得到如下表达式:
\[c \equiv (p_H \cdot m + \omega_p)^{q_H} + (q_H \cdot m + \omega_q)^{p_H} \pmod{n_H}\]注意到 $n_H = p_H \cdot q_H$ ,那么根据费马小定理可以得到如下方程组:
\[\begin{cases} c \equiv \omega_p^{q_H} + q_H \cdot m + \omega_q & \pmod{p_H} \\ c \equiv p_H \cdot m + \omega_p + \omega_q^{p_H} & \pmod{q_H} \\ \end{cases}\]从而有:
\[\begin{cases} m_p \equiv q_H ^ {-1} \cdot(c - \omega_p^{q_H} - \omega_q) & \pmod{p_H} \\ m_q \equiv p_H ^ {-1} \cdot(c - \omega_p - \omega_q^{p_H}) & \pmod{q_H} \\ \end{cases}\]最后利用中国剩余定理即可恢复 $m$ 。
这里主要注意一点是前面解出来的小 p
和小 q
可能顺序不对应,调整一下就好了:
# sagemath
Ep = EllipticCurve(GF(pH), [0, p])
Eq = EllipticCurve(GF(qH), [0, q])
omega_p = Ep.order()
omega_q = Eq.order()
mp = inverse(int(qH), int(pH)) * (int(c) - int(pow(omega_p, qH, pH)) - int(omega_q)) % pH
mq = inverse(int(pH), int(qH)) * (int(c) - int(pow(omega_q, pH, qH)) - int(omega_p)) % qH
m = crt([mq, mp], [pH, qH])
print(long_to_bytes(int(m)))
Ep = EllipticCurve(GF(pH), [0, q])
Eq = EllipticCurve(GF(qH), [0, p])
omega_p = Ep.order()
omega_q = Eq.order()
mp = inverse(int(qH), int(pH)) * (int(c) - int(pow(omega_p, qH, pH)) - int(omega_q)) % pH
mq = inverse(int(pH), int(qH)) * (int(c) - int(pow(omega_q, pH, qH)) - int(omega_p)) % qH
m = crt([mp, mq], [pH, qH])
print(long_to_bytes(int(m)))
# sagemath
n, g1, g2 =
c =
from Crypto.Util.number import *
New_Year = 2024
Goldbach_christian = [(p, New_Year - p) for p in sieve_base if p <= New_Year // 2 and isPrime(New_Year - p)]
def factor(nh, ng, a, b, f = lambda x: bytes_to_long(str(x).encode())):
nh_p = None
def test_digits(ps, qs):
nonlocal nh_p
if nh_p is not None:
return False
p = sum([pi * 10 ** i for i, pi in enumerate(ps)])
p_with_a = p + a
new_ps = [int(i) for i in str(p_with_a).zfill(len(ps))[::-1]][:len(ps)]
pp = sum([(48 + pi) << (i * 8) for i, pi in enumerate(new_ps)])
q = sum([pi * 10 ** i for i, pi in enumerate(qs)])
q_with_b = q + b
new_qs = [int(i) for i in str(q_with_b).zfill(len(qs))[::-1]][:len(qs)]
qq = sum([(48 + qi) << (i * 8) for i, qi in enumerate(new_qs)])
if p != 0 and p != 1 and nh % p == 0:
nh_p = p
return False
m1 = 10 ** len(ps)
m2 = 1 << (len(qs) * 8)
return (p * q) % m1 == nh % m1 and (pp * qq) % m2 == ng % m2
stack = [([], [])]
while stack:
ps, qs = stack.pop()
stack += [(ps + [i], qs + [j]) for i in range(10) for j in range(10) if test_digits(ps + [i], qs + [j])]
ng_p = f(nh_p + a)
assert ng % ng_p == 0
return nh_p, nh // nh_p
New_Year = 2024
Goldbach_christian = [(p, New_Year - p) for p in sieve_base if p <= New_Year and isPrime(New_Year - p)]
def get_factors(n, nn, ABlist):
Hp, Hq, p, q = None, None, None, None
for a, b in ABlist:
try:
Hp, Hq = factor(n, nn, a + 1, b)
print(Hp, Hq, a, b)
p, q = a, b
break
except: pass
return Hp, Hq, p, q
pH, qH, p, q = get_factors(n, g1, Goldbach_christian)
if pH is None or qH is None:
pH, qH, p, q = get_factors(n, g2, Goldbach_christian)
Ep = EllipticCurve(GF(pH), [0, p])
Eq = EllipticCurve(GF(qH), [0, q])
omega_p = Ep.order()
omega_q = Eq.order()
mp = inverse(int(qH), int(pH)) * (int(c) - int(pow(omega_p, qH, pH)) - int(omega_q)) % pH
mq = inverse(int(pH), int(qH)) * (int(c) - int(pow(omega_q, pH, qH)) - int(omega_p)) % qH
m = crt([mq, mp], [pH, qH])
print(long_to_bytes(int(m)))
Ep = EllipticCurve(GF(pH), [0, q])
Eq = EllipticCurve(GF(qH), [0, p])
omega_p = Ep.order()
omega_q = Eq.order()
mp = inverse(int(qH), int(pH)) * (int(c) - int(pow(omega_p, qH, pH)) - int(omega_q)) % pH
mq = inverse(int(pH), int(qH)) * (int(c) - int(pow(omega_q, pH, qH)) - int(omega_p)) % qH
m = crt([mp, mq], [pH, qH])
print(long_to_bytes(int(m)))