#!/usr/bin/env python3
from random import randrange
from Crypto.Cipher import AES
p = 193387944202565886198256260591909756041
i = lambda x: pow(x, p-2, p)
def add(A, B):
(x1, y1), (x2, y2) = A, B
assert x1 != x2 or y1 == y2
if x1 == x2: m = (3*x1*x2 + 4*x1 + 1) * i(y1+y2)
else: m = (y2-y1) * i(x2-x1)
y = m*m - x1 - x2 - 2
z = m*(x1-y) - y1
return y % p, z % p
def mul(t, A, B=0):
if not t: return B
return mul(t//2, add(A,A), B if not t&1 else add(B,A) if B else A)
x = randrange(p)
aes = AES.new(x.to_bytes(16, 'big'), AES.MODE_CBC, bytes(16))
flag = open('flag.txt').read().strip()
cipher = aes.encrypt(flag.ljust((len(flag)+15)//16*16).encode())
print(*mul(x, (4, 10)), cipher.hex(), file=open('flag.enc', 'w'))
'''
65639504587209705872811542111125696405 125330437930804525313353306745824609665 b3669dc657cef9dc17db4de5287cd1a1e8a48184ed9746f4c52d3b9f8186ec046d6fb1b8ed1b45111c35b546204b68e0
'''
hxp2018原题基础上有小改动。关键要求出AES的密钥$x$,也就是提供的点关于点$(4,10)$的离散对数(ECDLP)。主要思路首先是由曲线通项公式和add()
函数判断出曲线表达式:
一般定义的椭圆曲线方程$y^2=x^3+ax+b$要求系数满足$4a^3+27b^2\not = 0$。这样的椭圆曲线存在渐近线。当$4a^3+27b^2 = 0$,此时椭圆曲线是一种退化后的形式,称为奇异曲线(singular curve),分两种情况讨论:
- $a=b=0$,两段曲线交于$(0,0)$一点,且在交点处的两条切线重合,这样的点称为尖点(cusp)
- $a,b\not =0$,两段曲线交于某一点,且在交点处的两条切线不重合,这样的点称为结点(node)
$F_p$上的奇异曲线与$p-1$阶乘法群同构,可以更快解决DLP。
注意到该曲线是一条singlar curve,将其映射到乘法群$F_p$上:
from random import randrange
from Crypto.Cipher import AES
from Crypto.Util.number import *
i = lambda x: pow(x, p-2, p)
def add(A, B):
(x1, y1), (x2, y2) = A, B
assert x1 != x2 or y1 == y2
if x1 == x2: m = (3*x1*x2 + 4*x1 + 1) * i(y1+y2)
else: m = (y2-y1) * i(x2-x1)
y = m*m - x1 - x2 - 2
z = m*(x1-y) - y1
return y % p, z % p
def mul(t, A, B=0):
if not t: return B
return mul(t//2, add(A,A), B if not t&1 else add(B,A) if B else A)
p = 193387944202565886198256260591909756041
P.<x> = GF(p)[]
f = x^3 + 2*x^2 + x
P = (4, 10)
Q = (65639504587209705872811542111125696405, 125330437930804525313353306745824609665)
f_ = f.subs(x=x-1)
t = GF(p)(-1).square_root()
u = (P[1] - t * (P[0] + 1))/(P[1] + t * (P[0] + 1)) % p
v = (Q[1] - t * (Q[0] + 1))/(Q[1] + t * (Q[0] + 1)) % p
k = v.log(u)
x = k + u.multiplicative_order()
print(*mul(x, (4, 10)))
aes = AES.new(x.to_bytes(16, 'big'), AES.MODE_CBC, bytes(16))
flag = long_to_bytes(0xb3669dc657cef9dc17db4de5287cd1a1e8a48184ed9746f4c52d3b9f8186ec046d6fb1b8ed1b45111c35b546204b68e0)
cipher = aes.decrypt(flag)
print(cipher)
#!/usr/bin/env python3
import signal
import socketserver
import string
import os
from secret import flag
from py_ecc import bn128
lib = bn128
FQ, FQ2, FQ12, field_modulus = lib.FQ, lib.FQ2, lib.FQ12, lib.field_modulus
G1, G2, G12, b, b2, b12, is_inf, is_on_curve, eq, add, double, curve_order, multiply = \
lib.G1, lib.G2, lib.G12, lib.b, lib.b2, lib.b12, lib.is_inf, lib.is_on_curve, lib.eq, lib.add, lib.double, lib.curve_order, lib.multiply
pairing, neg = lib.pairing, lib.neg
LENGTH = 7
def Cx(x,length=LENGTH):
res = []
for i in range(length):
res.append(pow(x,i,curve_order) % curve_order)
return res
def C(x,y,length=LENGTH):
assert len(x) == len(y) == length
res = multiply(G1, curve_order)
for i in range(length):
res = add(multiply(x[i],y[i]),res)
return res
def Z(x):
return (x-1)*(x-2)*(x-3)*(x-4) % curve_order
def genK(curve_order,length=LENGTH):
t = int(os.urandom(8).hex(),16) % curve_order
a = int(os.urandom(8).hex(),16) % curve_order
Ct = Cx(t)
PKC = []
for ct in Ct:
PKC.append(multiply(G1, ct))
PKCa = []
for ct in Ct:
PKCa.append(multiply(multiply(G1, ct), a))
PK = (PKC,PKCa)
VKa = multiply(G2, a)
VKz = multiply(G2, Z(t))
VK = (VKa,VKz)
return PK,VK
def verify(proof,VK):
VKa,VKz = VK
PiC,PiCa,PiH = proof
l = pairing(VKa, PiC)
r = pairing(G2, PiCa)
if l !=r:
return False
l = pairing(G2,PiC)
r = pairing(VKz,PiH)
if l !=r:
return False
return True
class Task(socketserver.BaseRequestHandler):
def __init__(self, *args, **kargs):
super().__init__(*args, **kargs)
def OKPROOF(self,proof,VK):
return verify(proof,VK)
def dosend(self, msg):
try:
self.request.sendall(msg.encode('latin-1') + b'\n')
except:
pass
def timeout_handler(self, signum, frame):
raise TimeoutError
def handle(self):
try:
signal.signal(signal.SIGALRM, self.timeout_handler)
self.dosend('===========================')
self.dosend('=WELCOME TO 0KPR00F SYSTEM=')
self.dosend('===========================')
PK,VK = genK(curve_order)
self.dosend(str(PK))
self.dosend('now give me your proof')
msg = self.request.recv(1024).strip()
msg = msg.decode('utf-8')
tmp = msg.replace('(','').replace(')','').replace(',','')
tmp = tmp.split(' ')
assert len(tmp) == 6
PiC = (FQ(int(tmp[0].strip())),FQ(int(tmp[1].strip())))
PiCa = (FQ(int(tmp[2].strip())),FQ(int(tmp[3].strip())))
PiH = (FQ(int(tmp[4].strip())),FQ(int(tmp[5].strip())))
proof = (PiC,PiCa,PiH)
if self.OKPROOF(proof,VK):
self.dosend("Congratulations!Here is flag:"+flag)
else:
self.dosend("sorry")
except TimeoutError:
self.dosend('Timeout!')
self.request.close()
except:
self.dosend('Wtf?')
self.request.close()
class ThreadedServer(socketserver.ForkingMixIn, socketserver.TCPServer):
pass
if __name__ == "__main__":
HOST, PORT = '0.0.0.0', 13337
server = ThreadedServer((HOST, PORT), Task)
server.allow_reuse_address = True
server.serve_forever()
题目中给出了要用到的bn128曲线的库py_ecc
,提供的公钥$PK$(实际上是向量承诺),所有元素都在$G_1$上,$a$是$\mathbb{Z}_p$上的随机数:
验证密钥$VK$,所有元素都在$G_2$上:
成功条件是使得双线性配对$e:G_1\times G_2\rightarrow G_T$验证通过:
虽然不知道$t$和$a$的值,但可以利用返回的$PK$来构造$(PiC, PiCa,PiH)$。由双线性对的性质:
sage代码:
# GF(p)
p = 21888242871839275222246405745257275088696311157297823662689037894645226208583
F1 = GF(p)
G1 = EllipticCurve(F1,[0,3])
P1 = G1(1,2)
rev = ([G1(1, 2), G1(12634587734867345779923022940292181443558285587050818763065229149202894039283, 10694431093643956247616156239776406618083705658003165544249763851117830634777), G1(19966160771227176410398522389691772605718481584431049081277968564299713462607, 4675694108236602701470610220829271602207702697669072707005068465960783999029), G1(5295595387360986098717588647519286285494204232232229640163321068642463197494, 5468404437624818015883608347372142659092450509816884367689767172835376362835), G1(12976516847948090440613452943665766191145343826830984063037221571706352413122, 21881532903998970231834756191425446971145510248658365275503409396400778709409), G1(20737086716279207343439321031990505834011873934955301190324476515781185779177, 5173539055331640470887541930947239331810487512515483313761449160512937425069), G1(8433948793372369642092002088663055366803076152745412986154550745360539646492, 3042518052342448279890913775197983226384042755194717968574699700013187454643)], [G1(21298034894188468253863936122397844807940040392978095681402771832942943111677, 4097298912181484378640845756789133350561837868426974714402807402569558743979), G1(18302718821486572691257585886518112531499674422732738024775374663775501512673, 597181006271960418091320972557859750483383974730836970677708879919553000193), G1(7006808936778869952427923784953840537499239953493031067400500956190062808388, 19427857033202464435310039030180575265354530861695818591549784617800916724611), G1(3418774925111105430768808332329589724534326657054333081710712814871406993772, 18435402000607857201600558020957994996270491122552058132591092200285832883030), G1(16891960009772082695253315359430973505206354034499655384028848566531817379191, 997421905222517397998456679072431794616100175877489093847111227566078909809), G1(17474948273843330972012911666456412407279451369959221598751487945085040098780, 17111745637366152502061819500253402626710495776069611237627548700747255267566), G1(9904286627956461874829859092452324772869766648459603411100874950699004998060, 7077337925663027667774728985408447182260312450030540489641964124790773462458)])
PKC,PKCa = rev[0], rev[1]
PiC = PKC[4] - 10 * PKC[3] + 35 * PKC[2] - 50 * PKC[1] + 24 * PKC[0]
PiCa = PKCa[4] - 10 * PKCa[3] + 35 * PKCa[2] - 50 * PKCa[1] + 24 * PKCa[0]
PiH = G1
print(str(PiC.xy()[0])+' '+ str(PiC.xy()[1]) + ' '+ str(PiCa.xy()[0]) + ' ' + str(PiCa.xy()[1]) + ' 1 2')