107 lines
4.0 KiB
Python
107 lines
4.0 KiB
Python
from optimized_curve import double, add, multiply, is_on_curve, neg, twist, b, b2, b12, curve_order, G1, G2, G12, normalize
|
|
from bn128_field_elements import field_modulus, FQ
|
|
from optimized_field_elements import FQ2, FQ12
|
|
|
|
ate_loop_count = 29793968203157093288
|
|
log_ate_loop_count = 63
|
|
pseudo_binary_encoding = [0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 1, -1, 0, 0, 1, 0,
|
|
0, 1, 1, 0, -1, 0, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1,
|
|
1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 1,
|
|
1, 0, 0, -1, 0, 0, 0, 1, 1, 0, -1, 0, 0, 1, 0, 1, 1]
|
|
|
|
assert sum([e * 2**i for i, e in enumerate(pseudo_binary_encoding)]) == ate_loop_count
|
|
|
|
|
|
def normalize1(p):
|
|
x, y = normalize(p)
|
|
return x, y, x.__class__.one()
|
|
|
|
# Create a function representing the line between P1 and P2,
|
|
# and evaluate it at T. Returns a numerator and a denominator
|
|
# to avoid unneeded divisions
|
|
def linefunc(P1, P2, T):
|
|
zero = P1[0].__class__.zero()
|
|
x1, y1, z1 = P1
|
|
x2, y2, z2 = P2
|
|
xt, yt, zt = T
|
|
# points in projective coords: (x / z, y / z)
|
|
# hence, m = (y2/z2 - y1/z1) / (x2/z2 - x1/z1)
|
|
# multiply numerator and denominator by z1z2 to get values below
|
|
m_numerator = y2 * z1 - y1 * z2
|
|
m_denominator = x2 * z1 - x1 * z2
|
|
if m_denominator != zero:
|
|
# m * ((xt/zt) - (x1/z1)) - ((yt/zt) - (y1/z1))
|
|
return m_numerator * (xt * z1 - x1 * zt) - m_denominator * (yt * z1 - y1 * zt), \
|
|
m_denominator * zt * z1
|
|
elif m_numerator == zero:
|
|
# m = 3(x/z)^2 / 2(y/z), multiply num and den by z**2
|
|
m_numerator = 3 * x1 * x1
|
|
m_denominator = 2 * y1 * z1
|
|
return m_numerator * (xt * z1 - x1 * zt) - m_denominator * (yt * z1 - y1 * zt), \
|
|
m_denominator * zt * z1
|
|
else:
|
|
return xt * z1 - x1 * zt, z1 * zt
|
|
|
|
def cast_point_to_fq12(pt):
|
|
if pt is None:
|
|
return None
|
|
x, y, z = pt
|
|
return (FQ12([x.n] + [0] * 11), FQ12([y.n] + [0] * 11), FQ12([z.n] + [0] * 11))
|
|
|
|
# Check consistency of the "line function"
|
|
one, two, three = G1, double(G1), multiply(G1, 3)
|
|
negone, negtwo, negthree = multiply(G1, curve_order - 1), multiply(G1, curve_order - 2), multiply(G1, curve_order - 3)
|
|
|
|
assert linefunc(one, two, one)[0] == FQ(0)
|
|
assert linefunc(one, two, two)[0] == FQ(0)
|
|
assert linefunc(one, two, three)[0] != FQ(0)
|
|
assert linefunc(one, two, negthree)[0] == FQ(0)
|
|
assert linefunc(one, negone, one)[0] == FQ(0)
|
|
assert linefunc(one, negone, negone)[0] == FQ(0)
|
|
assert linefunc(one, negone, two)[0] != FQ(0)
|
|
assert linefunc(one, one, one)[0] == FQ(0)
|
|
assert linefunc(one, one, two)[0] != FQ(0)
|
|
assert linefunc(one, one, negtwo)[0] == FQ(0)
|
|
|
|
# Main miller loop
|
|
def miller_loop(Q, P):
|
|
if Q is None or P is None:
|
|
return FQ12.one()
|
|
R = Q
|
|
f_num, f_den = FQ12.one(), FQ12.one()
|
|
for b in pseudo_binary_encoding[63::-1]:
|
|
#for i in range(log_ate_loop_count, -1, -1):
|
|
_n, _d = linefunc(R, R, P)
|
|
f_num = f_num * f_num * _n
|
|
f_den = f_den * f_den * _d
|
|
R = double(R)
|
|
#if ate_loop_count & (2**i):
|
|
if b == 1:
|
|
_n, _d = linefunc(R, Q, P)
|
|
f_num = f_num * _n
|
|
f_den = f_den * _d
|
|
R = add(R, Q)
|
|
elif b == -1:
|
|
nQ = neg(Q)
|
|
_n, _d = linefunc(R, nQ, P)
|
|
f_num = f_num * _n
|
|
f_den = f_den * _d
|
|
R = add(R, nQ)
|
|
# assert R == multiply(Q, ate_loop_count)
|
|
Q1 = (Q[0] ** field_modulus, Q[1] ** field_modulus, Q[2] ** field_modulus)
|
|
# assert is_on_curve(Q1, b12)
|
|
nQ2 = (Q1[0] ** field_modulus, -Q1[1] ** field_modulus, Q1[2] ** field_modulus)
|
|
# assert is_on_curve(nQ2, b12)
|
|
_n1, _d1 = linefunc(R, Q1, P)
|
|
R = add(R, Q1)
|
|
_n2, _d2 = linefunc(R, nQ2, P)
|
|
f = f_num * _n1 * _n2 / (f_den * _d1 * _d2)
|
|
# R = add(R, nQ2) This line is in many specifications but it technically does nothing
|
|
return f ** ((field_modulus ** 12 - 1) // curve_order)
|
|
|
|
# Pairing computation
|
|
def pairing(Q, P):
|
|
assert is_on_curve(Q, b2)
|
|
assert is_on_curve(P, b)
|
|
return miller_loop(twist(Q), cast_point_to_fq12(P))
|