11import hashlib
22import hmac
33import re
4+ from typing import Optional , Tuple
45
5- import ecdsa
66from cryptography .hazmat .primitives .asymmetric .ed25519 import (
77 Ed25519PrivateKey ,
88 Ed25519PublicKey ,
@@ -30,11 +30,29 @@ class SLIP10DerivationError(Exception):
3030 pass
3131
3232
33+ Point = Optional [Tuple [int , int ]]
34+
35+
3336class WeierstrassCurve :
34- def __init__ (self , name , modifier , curve ):
37+ def __init__ (
38+ self ,
39+ name : str ,
40+ modifier : bytes ,
41+ * ,
42+ p : int ,
43+ a : int ,
44+ b : int ,
45+ generator : Tuple [int , int ],
46+ order : int ,
47+ ):
3548 self .name = name
3649 self .modifier = modifier
37- self .curve = curve
50+ self .p = p
51+ self .a = a
52+ self .b = b
53+ self .generator = generator
54+ self .order = order
55+ self .coordinate_size = (p .bit_length () + 7 ) // 8
3856
3957 def generate_master (self , seed ):
4058 """Master key generation in SLIP-0010
@@ -59,7 +77,6 @@ def derive_private_child(self, privkey, chaincode, index):
5977 :return: (child_privatekey, child_chaincode)
6078 """
6179 assert isinstance (privkey , bytes ) and isinstance (chaincode , bytes )
62- # payload is the I from the SLIP. Index is 32 bits unsigned int, BE.
6380 if index & HARDENED_INDEX != 0 :
6481 payload = hmac .new (
6582 chaincode , b"\x00 " + privkey + index .to_bytes (4 , "big" ), hashlib .sha512
@@ -72,8 +89,8 @@ def derive_private_child(self, privkey, chaincode, index):
7289
7390 while True :
7491 tweak = int .from_bytes (payload [:32 ], "big" )
75- child_private = (tweak + int .from_bytes (privkey , "big" )) % self .curve . order
76- if tweak <= self .curve . order and child_private != 0 :
92+ child_private = (tweak + int .from_bytes (privkey , "big" )) % self .order
93+ if tweak <= self .order and child_private != 0 :
7794 break
7895 payload = hmac .new (
7996 chaincode ,
@@ -92,43 +109,136 @@ def derive_public_child(self, pubkey, chaincode, index):
92109
93110 :return: (child_pubkey, child_chaincode)
94111 """
95- from ecdsa .ellipticcurve import INFINITY
96-
97112 assert isinstance (pubkey , bytes ) and isinstance (chaincode , bytes )
98113 if index & HARDENED_INDEX != 0 :
99114 raise SLIP10DerivationError ("Hardened derivation is not possible." )
100115
101- # payload is the I from the SLIP. Index is 32 bits unsigned int, BE.
102116 payload = hmac .new (
103117 chaincode , pubkey + index .to_bytes (4 , "big" ), hashlib .sha512
104118 ).digest ()
119+ base_point = self ._bytes_to_point (pubkey )
105120 while True :
106121 tweak = int .from_bytes (payload [:32 ], "big" )
107- point = ecdsa . VerifyingKey . from_string ( pubkey , self .curve ). pubkey . point
108- point + = self .curve . generator * tweak
109- if tweak <= self .curve . order and point != INFINITY :
122+ tweak_point = self . _scalar_mult ( tweak , self .generator )
123+ child_point = self ._point_add ( base_point , tweak_point )
124+ if tweak <= self .order and child_point is not None :
110125 break
111126 payload = hmac .new (
112127 chaincode ,
113128 b"\x01 " + payload [32 :] + index .to_bytes (4 , "big" ),
114129 hashlib .sha512 ,
115130 ).digest ()
116- return point . to_bytes ( "compressed" ), payload [32 :]
131+ return self . _point_to_bytes ( child_point ), payload [32 :]
117132
118133 def privkey_is_valid (self , privkey ):
119134 key = int .from_bytes (privkey , "big" )
120- return 0 < key < self .curve . order
135+ return 0 < key < self .order
121136
122137 def pubkey_is_valid (self , pubkey ):
123138 try :
124- ecdsa .VerifyingKey .from_string (pubkey , self .curve )
125- return True
126- except ecdsa .errors .MalformedPointError :
139+ point = self ._bytes_to_point (pubkey )
140+ except ValueError :
127141 return False
142+ return point is not None and self ._is_on_curve (point )
128143
129144 def privkey_to_pubkey (self , privkey ):
130- sk = ecdsa .SigningKey .from_string (privkey , self .curve )
131- return sk .get_verifying_key ().to_string ("compressed" )
145+ if not self .privkey_is_valid (privkey ):
146+ raise ValueError ("Invalid private key" )
147+ scalar = int .from_bytes (privkey , "big" )
148+ point = self ._scalar_mult (scalar , self .generator )
149+ if point is None :
150+ raise ValueError ("Point at infinity" )
151+ return self ._point_to_bytes (point )
152+
153+ def _is_on_curve (self , point : Point ) -> bool :
154+ if point is None :
155+ return True
156+ x , y = point
157+ return (y * y - (x * x * x + self .a * x + self .b )) % self .p == 0
158+
159+ def _point_add (self , p1 : Point , p2 : Point ) -> Point :
160+ if p1 is None :
161+ return p2
162+ if p2 is None :
163+ return p1
164+ if p1 == p2 :
165+ return self ._point_double (p1 )
166+
167+ x1 , y1 = p1
168+ x2 , y2 = p2
169+ if x1 == x2 :
170+ return None
171+
172+ slope = ((y2 - y1 ) * self ._inverse_mod ((x2 - x1 ) % self .p )) % self .p
173+ x3 = (slope * slope - x1 - x2 ) % self .p
174+ y3 = (slope * (x1 - x3 ) - y1 ) % self .p
175+ return x3 , y3
176+
177+ def _point_double (self , point : Point ) -> Point :
178+ if point is None :
179+ return None
180+ x , y = point
181+ if y == 0 :
182+ return None
183+
184+ slope = ((3 * x * x + self .a ) * self ._inverse_mod ((2 * y ) % self .p )) % self .p
185+ x3 = (slope * slope - 2 * x ) % self .p
186+ y3 = (slope * (x - x3 ) - y ) % self .p
187+ return x3 , y3
188+
189+ def _scalar_mult (self , scalar : int , point : Tuple [int , int ]) -> Point :
190+ scalar %= self .order
191+ if scalar == 0 or point is None :
192+ return None
193+
194+ result : Point = None
195+ addend : Point = point
196+ while scalar :
197+ if scalar & 1 :
198+ result = self ._point_add (result , addend )
199+ addend = self ._point_double (addend )
200+ scalar >>= 1
201+ return result
202+
203+ def _bytes_to_point (self , data : bytes ) -> Point :
204+ if len (data ) == self .coordinate_size * 2 + 1 and data [0 ] == 4 :
205+ x = int .from_bytes (data [1 : 1 + self .coordinate_size ], "big" )
206+ y = int .from_bytes (data [1 + self .coordinate_size :], "big" )
207+ point = (x , y )
208+ if not self ._is_on_curve (point ):
209+ raise ValueError ("Point is not on curve" )
210+ return point
211+
212+ if len (data ) != self .coordinate_size + 1 or data [0 ] not in (2 , 3 ):
213+ raise ValueError ("Invalid public key encoding" )
214+
215+ x = int .from_bytes (data [1 :], "big" )
216+ y = self ._recover_y (x , data [0 ] == 3 )
217+ point = (x , y )
218+ if not self ._is_on_curve (point ):
219+ raise ValueError ("Point is not on curve" )
220+ return point
221+
222+ def _point_to_bytes (self , point : Tuple [int , int ]) -> bytes :
223+ x , y = point
224+ prefix = 0x03 if y & 1 else 0x02
225+ return bytes ([prefix ]) + x .to_bytes (self .coordinate_size , "big" )
226+
227+ def _recover_y (self , x : int , is_odd : bool ) -> int :
228+ if x >= self .p :
229+ raise ValueError ("Invalid point" )
230+ rhs = (pow (x , 3 , self .p ) + self .a * x + self .b ) % self .p
231+ y = pow (rhs , (self .p + 1 ) // 4 , self .p )
232+ if (y * y ) % self .p != rhs :
233+ raise ValueError ("Invalid point" )
234+ if bool (y & 1 ) != is_odd :
235+ y = (- y ) % self .p
236+ if bool (y & 1 ) != is_odd :
237+ raise ValueError ("Invalid point" )
238+ return y
239+
240+ def _inverse_mod (self , value : int ) -> int :
241+ return pow (value % self .p , - 1 , self .p )
132242
133243
134244class EdwardsCurve :
@@ -197,8 +307,30 @@ def privkey_to_pubkey(self, privkey):
197307 return b"\x00 " + sk .public_key ().public_bytes (key_encoding , key_format )
198308
199309
200- SECP256K1 = WeierstrassCurve ("secp256k1" , b"Bitcoin seed" , ecdsa .SECP256k1 )
201- SECP256R1 = WeierstrassCurve ("secp256r1" , b"Nist256p1 seed" , ecdsa .NIST256p )
310+ SECP256K1 = WeierstrassCurve (
311+ "secp256k1" ,
312+ b"Bitcoin seed" ,
313+ p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F ,
314+ a = 0 ,
315+ b = 7 ,
316+ generator = (
317+ 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 ,
318+ 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 ,
319+ ),
320+ order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 ,
321+ )
322+ SECP256R1 = WeierstrassCurve (
323+ "secp256r1" ,
324+ b"Nist256p1 seed" ,
325+ p = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF ,
326+ a = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC ,
327+ b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B ,
328+ generator = (
329+ 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296 ,
330+ 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5 ,
331+ ),
332+ order = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 ,
333+ )
202334ED25519 = EdwardsCurve ("ed25519" , b"ed25519 seed" , Ed25519PrivateKey , Ed25519PublicKey )
203335X25519 = EdwardsCurve (
204336 "curve25519" , b"curve25519 seed" , X25519PrivateKey , X25519PublicKey
0 commit comments