crypto-algorithms/elas.py

209 lines
5.0 KiB
Python

from dataclasses import dataclass
from random import randint
@dataclass
class PrimeGaloisField:
prime: int
def __contains__(self, field_value: "FieldElement") -> bool:
return 0 <= field_value.value < self.prime
@dataclass
class FieldElement:
value: int
field: PrimeGaloisField
def __repr__(self):
return "0x" + f"{self.value:x}".zfill(64)
@property
def P(self) -> int:
return self.field.prime
def __add__(self, other: "FieldElement") -> "FieldElement":
return FieldElement(
value=(self.value + other.value) % self.P,
field=self.field
)
def __sub__(self, other: "FieldElement") -> "FieldElement":
return FieldElement(
value=(self.value - other.value) % self.P,
field=self.field
)
def __rmul__(self, scalar: int) -> "FieldValue":
return FieldElement(
value=(self.value * scalar) % self.P,
field=self.field
)
def __mul__(self, other: "FieldElement") -> "FieldElement":
return FieldElement(
value=(self.value * other.value) % self.P,
field=self.field
)
def __pow__(self, exponent: int) -> "FieldElement":
return FieldElement(
value=pow(self.value, exponent, self.P),
field=self.field
)
def __truediv__(self, other: "FieldElement") -> "FieldElement":
other_inv = other ** -1
return self * other_inv
@dataclass
class EllipticCurve:
a: int
b: int
field: PrimeGaloisField
def __contains__(self, point: "Point") -> bool:
x, y = point.x, point.y
return y ** 2 == x ** 3 + self.a * x + self.b
def __post_init__(self):
self.a = FieldElement(self.a, self.field)
self.b = FieldElement(self.b, self.field)
if self.a not in self.field or self.b not in self.field:
raise ValueError
@dataclass
class Point:
x: int
y: int
curve: EllipticCurve
def __post_init__(self):
if self.x is None and self.y is None:
return
self.x = FieldElement(self.x, self.curve.field)
self.y = FieldElement(self.y, self.curve.field)
if self not in self.curve:
raise ValueError
def __add__(self, other):
if self == I:
return other
if other == I:
return self
if self.x == other.x and self.y == (-1 * other.y):
return I
if self.x != other.x:
x1, x2 = self.x, other.x
y1, y2 = self.y, other.y
s = (y2 - y1) / (x2 - x1)
x3 = s ** 2 - x1 - x2
y3 = s * (x1 - x3) - y1
return self.__class__(
x=x3.value,
y=y3.value,
curve=secp256k1
)
if self == other and self.y == inf:
return I
if self == other:
x1, y1, a = self.x, self.y, self.curve.a
s = (3 * x1 ** 2 + a) / (2 * y1)
x3 = s ** 2 - 2 * x1
y3 = s * (x1 - x3) - y1
return self.__class__(
x=x3.value,
y=y3.value,
curve=secp256k1
)
def __rmul__(self, scalar: int) -> "Point":
current = self
result = I
while scalar:
if scalar & 1:
result = result + current
current = current + current
scalar >>= 1
return result
@dataclass
class Signature:
r: int
s: int
def verify(self, z: int, pub_key: Point) -> bool:
s_inv = pow(self.s, -1, N)
u = (z * s_inv) % N
v = (self.r * s_inv) % N
return (u*G + v*pub_key).x.value == self.r
@dataclass
class PrivateKey:
secret: int
def sign(self, z: int) -> Signature:
e = self.secret
k = randint(0, N)
R = k * G
r = R.x.value
k_inv = pow(k, -1, N)
s = ((z + r*e) * k_inv) % N
return Signature(r, s)
if __name__ == "__main__":
P = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
A = 0
B = 7
field = PrimeGaloisField(prime=P)
secp256k1 = EllipticCurve(a=A, b=B, field=field)
G = Point(
x=0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
y=0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8,
curve=secp256k1)
N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
I = Point(x=None, y=None, curve=secp256k1)
inf = float("inf")
priv: int = 0xea11d6ada978a0b491aa5cbbe4df17a65c2fecc24448e95d1ccd854b43991bec
e = PrivateKey(priv)
pub = e.secret * G
print(pub)
z = 0x7e240de74fb1ed08fa08d38063f6a6a91462a815
signature: Signature = e.sign(z)
print(e.sign(z))
assert signature.verify(z, pub)