-
Notifications
You must be signed in to change notification settings - Fork 46
/
ecdsa_base.py
161 lines (136 loc) · 5.92 KB
/
ecdsa_base.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
from collections import namedtuple
import hmac
import random
Point = namedtuple('Point', ['x', 'y'])
class EllipticCurveBase(object):
""" A generic class for elliptic curves and operations on them.
The curves must be of the form: y^2 = x^3 + a*x + b.
"""
def __init__(self, hash_function):
self.hash_function = hash_function
def is_on_curve(self, p):
""" Checks whether a point is on the curve.
Args:
p (ECPointAffine): Point to be checked
Returns:
bool: True if p is on the curve, False otherwise.
"""
raise NotImplementedError
def y_from_x(self, x):
""" Computes the y component corresponding to x.
Since elliptic curves are symmetric about the x-axis,
the x component (and sign) is all that is required to determine
a point on the curve.
Args:
x (int): x component of the point.
Returns:
tuple: both possible y components of the point.
"""
raise NotImplementedError
def gen_key_pair(self, random_generator=random.SystemRandom()):
""" Generates a public/private key pair.
Args:
random_generator (generator): The random generator to use.
Returns:
tuple: A private key in the range of 1 to `self.n - 1`
and an ECPointAffine containing the public key point.
"""
raise NotImplementedError
def public_key(self, private_key):
""" Returns the public (verifying) key for a given private key.
Args:
private_key (int): the private key to derive the public key for.
Returns:
ECPointAffine: The point representing the public key.
"""
raise NotImplementedError
def recover_public_key(self, message, signature, recovery_id=None):
""" Recovers possibilities for the public key associated with the
private key used to sign message and generate signature.
Since there are multiple possibilities (two for curves with
co-factor = 1), each possibility that successfully verifies the
signature is returned.
Args:
message (bytes): The message that was signed.
signature (ECPointAffine): The point representing the signature.
recovery_id (int) (Optional): If provided, limits the valid x and y
point to only that described by the recovery_id.
Returns:
list(ECPointAffine): List of points representing valid public
keys that verify signature.
"""
raise NotImplementedError
def _sign(self, message, private_key, do_hash=True, secret=None):
raise NotImplementedError
def sign(self, message, private_key, do_hash=True):
""" Signs a message with the given private key.
Args:
message (bytes): The message to be signed
private_key (int): Integer that is the private key
do_hash (bool): True if the message should be hashed prior
to signing, False if not. This should always be left as
True except in special situations which require doing
the hash outside (e.g. handling Bitcoin bugs).
Returns:
(Point, int): The point (r, s) representing the signature
and the ID representing which public key possibility
is associated with the private key being used to sign.
"""
return self._sign(message, private_key, do_hash)
def verify(self, message, signature, public_key, do_hash=True):
""" Verifies that signature was generated with a private key corresponding
to public key, operating on message.
Args:
message (bytes): The message to be signed
signature (Point): (r, s) representing the signature
public_key (ECPointAffine): ECPointAffine of the public key
do_hash (bool): True if the message should be hashed prior
to signing, False if not. This should always be left as
True except in special situations which require doing
the hash outside (e.g. handling Bitcoin bugs).
Returns:
bool: True if the signature is verified, False otherwise.
"""
raise NotImplementedError
def _nonce_random(self):
return random.SystemRandom().randrange(1, self.n - 1)
def _nonce_rfc6979(self, private_key, message):
""" Computes a deterministic nonce (k) for use when signing
according to RFC6979 (https://tools.ietf.org/html/rfc6979),
Section 3.2.
Args:
private_key (int): The private key.
message (bytes): A hash of the input message.
Returns:
int: A deterministic nonce.
"""
hash_bytes = 32
x = private_key.to_bytes(hash_bytes, 'big')
# Message should already be hashed by the time it gets here,
# so don't bother doing another hash.
x_msg = x + message
# Step b
V = bytes([0x1] * hash_bytes)
# Step c
K = bytes([0x0] * hash_bytes)
# Step d
K = hmac.new(K, V + bytes([0]) + x_msg, self.hash_function).digest()
# Step e
V = hmac.new(K, V, self.hash_function).digest()
# Step f
K = hmac.new(K, V + bytes([0x1]) + x_msg, self.hash_function).digest()
# Step g
V = hmac.new(K, V, self.hash_function).digest()
while True:
# Step h.1
T = bytes()
# Step h.2
while 8 * len(T) < self.nlen:
V = hmac.new(K, V, self.hash_function).digest()
T += V
# Step h.3
k = int.from_bytes(T, 'big')
if k >= 1 and k < (self.n - 1):
return k
K = hmac.new(K, V + bytes([0]), self.hash_function).digest()
V = hmac.new(K, V, self.hash_function).digest()