209 lines
5.0 KiB
Python
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) |