gECC/scripts/ec.py
2025-10-15 13:44:34 +02:00

205 lines
6.4 KiB
Python

from mod_sqrt import modular_sqrt
from ec_ops import EC_ADD, EC_DBL, EC_MIXED_ADD, EC_AFF_DBL, EC_DBL_1, EC_DBL_2, AFF_DBL, AFF_DBL_1, AFF_ADD
import field
import constants
import random
import unittest
class EC:
# y^2 = x^3 + a x + b
def __init__(self, name, f, a, b, generator):
self.name = name
self.field = f
self.a = a
self.b = b
self.generator = generator
def random_element(self, difficulty=None):
if type(self.field) is field.Fp and difficulty is None:
p = self.field.p
while True:
x = random.randint(0, p - 1)
y = modular_sqrt((x * x * x + self.a * x + self.b) %
p, p)
if y != -1:
return x, random.choice([y, (p - y) % p])
r = self.zero_jacobian()
g = self.to_jacobian(self.generator)
for i in range(difficulty or 64):
if random.randint(0, 1) == 1:
r = self.add_jacobian(r, g)
g = self.add_jacobian(g, g)
return self.get_xy(r)
def is_equal(self, p, q):
(x1, y1, z1) = p
(x2, y2, z2) = q
z1z1 = self.field.mul(z1, z1)
z1z1z1 = self.field.mul(z1z1, z1)
z2z2 = self.field.mul(z2, z2)
z2z2z2 = self.field.mul(z2z2, z2)
return self.field.mul(x1, z2z2) == self.field.mul(x2, z1z1) \
and self.field.mul(y1, z2z2z2) == self.field.mul(y2, z1z1z1)
# Jac -> Aff -> Bool
def is_equal_mixed(self, p, q):
(x1, y1, z1) = p
(x2, y2) = q
z1z1 = self.field.mul(z1, z1)
z1z1z1 = self.field.mul(z1z1, z1)
return x1 == self.field.mul(x2, z1z1) \
and y1 == self.field.mul(y2, z1z1z1)
def double_jacobian(self, p):
(X1, Y1, Z1) = p
local_vars = locals()
EC_DBL_2.pyexec(globals(), local_vars)
return (local_vars['X3'], local_vars['Y3'], local_vars['Z3'])
def double_affine(self, p):
(X1, Y1) = p
local_vars = locals()
EC_AFF_DBL.pyexec(globals(), local_vars)
return (local_vars['X3'], local_vars['Y3'], local_vars['Z3'])
def affine_double(self, p):
(X1, Y1) = p
local_vars = locals()
AFF_DBL.pyexec(globals(), local_vars)
return (local_vars['X3'], local_vars['Y3'])
def add_affine(self, p, q):
if self.is_zero_affine(p):
return q
if self.is_zero_affine(q):
return p
(X1, Y1) = p
(X2, Y2) = q
if X1 == X2:
if Y1 == Y2:
return self.affine_double(p)
elif (Y1 + Y2) % self.field.p == 0:
return self.zero_affine()
local_vars = locals()
AFF_ADD.pyexec(globals(), local_vars)
return (local_vars['X3'], local_vars['Y3'])
def add_jacobian(self, p, q):
if self.is_zero_jacobian(p):
return q
if self.is_zero_jacobian(q):
return p
if self.is_equal(p, q):
return self.double_jacobian(p)
(X1, Y1, Z1) = p
(X2, Y2, Z2) = q
local_vars = locals()
EC_ADD.pyexec(globals(), local_vars)
return (local_vars['X3'], local_vars['Y3'], local_vars['Z3'])
# Jac -> Aff -> Jac
def add_mixed(self, p, q):
if self.is_zero_jacobian(p):
return self.to_jacobian(q)
if self.is_zero_affine(q):
return p
if self.is_equal_mixed(p, q):
return self.double_affine(q)
(X1, Y1, Z1) = p
(X2, Y2) = q
local_vars = locals()
EC_MIXED_ADD.pyexec(globals(), local_vars)
return (local_vars['X3'], local_vars['Y3'], local_vars['Z3'])
def to_jacobian(self, p):
if self.is_zero_affine(p):
return self.zero_jacobian()
(x, y) = p
z = self.field.random_nonzero_element()
z2 = self.field.mul(z, z)
z3 = self.field.mul(z2, z)
return (self.field.mul(x, z2), self.field.mul(y, z3), z)
def negate_jacobian(self, p):
(x, y, z) = p
return (x, self.field.neg(y), z)
def negate_affine(self, p):
(x, y) = p
return (x, self.field.neg(y))
# = [n]p
def multiply_jacobian(self, p, n):
result = self.zero_jacobian()
while n > 0:
if n % 2 == 1:
result = self.add_jacobian(result, p)
p = self.double_jacobian(p)
n >>= 1
return result
def get_xy(self, p):
(x, y, z) = p
if z == self.field.zero():
return (self.field.zero(), self.field.zero())
z_inv = self.field.inv(z)
z2_inv = self.field.mul(z_inv, z_inv)
z3_inv = self.field.mul(z2_inv, z_inv)
return (self.field.mul(x, z2_inv), self.field.mul(y, z3_inv))
def is_zero_affine(self, p):
(x, y) = p
# if self.name == 'G1MNT4753':
return y == self.field.zero()
# else:
# return x == self.field.zero()
def is_zero_jacobian(self, p):
(_, _, z) = p
return z == self.field.zero()
def zero_affine(self):
return (self.field.one(), self.field.zero())
def zero_jacobian(self):
return (self.field.one(), self.field.one(), self.field.zero())
def to_mont(self, p):
return (type(p))(map(self.field.to_mont, p))
G1_SECP256K1 = EC('G1SECP256K1', field.Fq_SECP256K1,
constants.SECP256K1_g1_a, constants.SECP256K1_g1_b, generator=constants.SECP256K1_g1_generator)
G1_ECDSA_VERIFY = EC('G1ECDSA_VERIFY', field.Fq_SECP256K1,
constants.SECP256K1_g1_a, constants.SECP256K1_g1_b, generator=constants.ECDSA_Verify_g1_generator)
def test_ec(self, ec):
affine_p = ec.random_element()
self.assertIsNotNone(affine_p)
p = ec.to_jacobian(affine_p)
self.assertIsNotNone(ec.to_mont(p))
zero = ec.zero_jacobian()
self.assertTrue(ec.is_equal(ec.add_jacobian(p, zero), p))
self.assertTrue(ec.is_equal(ec.add_jacobian(zero, p), p))
self.assertTrue(ec.is_equal(ec.add_jacobian(
p, ec.negate_jacobian(p)), zero))
_2p = ec.double_jacobian(p)
_3p = ec.add_jacobian(_2p, p)
_4p = ec.double_jacobian(_2p)
self.assertTrue(ec.is_equal(ec.add_jacobian(
_4p, ec.negate_jacobian(p)), _3p))
class ECTest(unittest.TestCase):
def test_g1(self):
test_ec(self, G1_MNT4753)
def test_g2(self):
test_ec(self, G2_MNT4753)
if __name__ == '__main__':
unittest.main()