-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathecc.py
114 lines (96 loc) · 3.49 KB
/
ecc.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
from __future__ import annotations # PEP 563: Postponed Evaluation of Annotations
from dataclasses import dataclass # https://docs.python.org/3/library/dataclasses.html I like these a lot
@dataclass
class Curve:
"""
Elliptic Curve over the field of integers modulo a prime.
Points on the curve satisfy y^2 = x^3 + a*x + b (mod p).
"""
p: int # the prime modulus of the finite field
a: int
b: int
# secp256k1 uses a = 0, b = 7, so we're dealing with the curve y^2 = x^3 + 7 (mod p)
bitcoin_curve = Curve(
p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F,
a = 0x0000000000000000000000000000000000000000000000000000000000000000, # a = 0
b = 0x0000000000000000000000000000000000000000000000000000000000000007, # b = 7
)
@dataclass
class Point:
""" An integer point (x,y) on a Curve """
curve: Curve
x: int
y: int
G = Point(
bitcoin_curve,
x = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798,
y = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
)
@dataclass
class Generator:
"""
A generator over a curve: an initial point and the (pre-computed) order
"""
G: Point # a generator point on the curve
n: int # the order of the generating point, so 0*G = n*G = INF
bitcoin_gen = Generator(
G = G,
# the order of G is known and can be mathematically derived
n = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141,
)
INF = Point(None, None, None) # special point at "infinity", kind of like a zero
def extended_euclidean_algorithm(a, b):
"""
Returns (gcd, x, y) s.t. a * x + b * y == gcd
This function implements the extended Euclidean
algorithm and runs in O(log b) in the worst case,
taken from Wikipedia.
"""
old_r, r = a, b
old_s, s = 1, 0
old_t, t = 0, 1
while r != 0:
quotient = old_r // r
old_r, r = r, old_r - quotient * r
old_s, s = s, old_s - quotient * s
old_t, t = t, old_t - quotient * t
return old_r, old_s, old_t
def inv(n, p):
""" returns modular multiplicate inverse m s.t. (n * m) % p == 1 """
gcd, x, y = extended_euclidean_algorithm(n, p) # pylint: disable=unused-variable
return x % p
def elliptic_curve_addition(self, other: Point) -> Point:
# handle special case of P + 0 = 0 + P = 0
if self == INF:
return other
if other == INF:
return self
# handle special case of P + (-P) = 0
if self.x == other.x and self.y != other.y:
return INF
# compute the "slope"
if self.x == other.x: # (self.y = other.y is guaranteed too per above check)
m = ((3 * self.x**2 + self.curve.a) * inv(2 * self.y, self.curve.p)) % self.curve.p
else:
m = ((self.y - other.y) * inv((self.x - other.x) % self.curve.p, self.curve.p)) % self.curve.p
# compute the new point
rx = (m**2 - self.x - other.x) % self.curve.p
# ry = (rx**3 + 7) % self.curve.p
ry = ((m*(self.x - rx) - self.y)) % self.curve.p
return Point(self.curve, rx, ry)
Point.__add__ = elliptic_curve_addition # monkey patch addition into the Point class
def double_and_add(self, k: int) -> Point:
assert isinstance(k, int) and k >= 0
result = INF
append = self
while k:
if k & 1:
result += append
append += append
k >>= 1
return result
# monkey patch double and add into the Point class for convenience
Point.__rmul__ = double_and_add
public_key = 3*G
secret_key = 3
key_length = 256