33from . import ecdsa
44from . import der
55from . import rfc6979
6+ from . import ellipticcurve
67from .curves import NIST192p , find_curve
8+ from .numbertheory import square_root_mod_prime , SquareRootError
79from .ecdsa import RSZeroError
810from .util import string_to_number , number_to_string , randrange
911from .util import sigencode_string , sigdecode_string
@@ -23,6 +25,10 @@ class BadDigestError(Exception):
2325 pass
2426
2527
28+ class MalformedPoint (AssertionError ):
29+ pass
30+
31+
2632class VerifyingKey :
2733 def __init__ (self , _error__please_use_generate = None ):
2834 if not _error__please_use_generate :
@@ -38,9 +44,8 @@ def from_public_point(klass, point, curve=NIST192p, hashfunc=sha1):
3844 self .pubkey .order = curve .order
3945 return self
4046
41- @classmethod
42- def from_string (klass , string , curve = NIST192p , hashfunc = sha1 ,
43- validate_point = True ):
47+ @staticmethod
48+ def _from_raw_encoding (string , curve , validate_point ):
4449 order = curve .order
4550 assert (len (string ) == curve .verifying_key_length ), \
4651 (len (string ), curve .verifying_key_length )
@@ -52,8 +57,49 @@ def from_string(klass, string, curve=NIST192p, hashfunc=sha1,
5257 y = string_to_number (ys )
5358 if validate_point :
5459 assert ecdsa .point_is_valid (curve .generator , x , y )
55- from . import ellipticcurve
56- point = ellipticcurve .Point (curve .curve , x , y , order )
60+ return ellipticcurve .Point (curve .curve , x , y , order )
61+
62+ @staticmethod
63+ def _from_compressed (string , curve , validate_point ):
64+ if string [:1 ] not in (b ('\x02 ' ), b ('\x03 ' )):
65+ raise MalformedPoint ("Malformed compressed point encoding" )
66+
67+ is_even = string [:1 ] == b ('\x02 ' )
68+ x = string_to_number (string [1 :])
69+ order = curve .order
70+ p = curve .curve .p ()
71+ alpha = (pow (x , 3 , p ) + (curve .curve .a () * x ) + curve .curve .b ()) % p
72+ try :
73+ beta = square_root_mod_prime (alpha , p )
74+ except SquareRootError as e :
75+ raise MalformedPoint ("Encoding does not correspond to a point on "
76+ "curve" , e )
77+ if is_even == bool (beta & 1 ):
78+ y = p - beta
79+ else :
80+ y = beta
81+ if validate_point and not ecdsa .point_is_valid (curve .generator , x , y ):
82+ raise MalformedPoint ("Point does not lie on curve" )
83+ return ellipticcurve .Point (curve .curve , x , y , order )
84+
85+ @classmethod
86+ def from_string (klass , string , curve = NIST192p , hashfunc = sha1 ,
87+ validate_point = True ):
88+ sig_len = len (string )
89+ if sig_len == curve .verifying_key_length :
90+ point = klass ._from_raw_encoding (string , curve , validate_point )
91+ elif sig_len == curve .verifying_key_length + 1 :
92+ if string [:1 ] != b ('\x04 ' ):
93+ raise MalformedPoint ("Invalid uncompressed encoding of the "
94+ "public point" )
95+ point = klass ._from_raw_encoding (string [1 :], curve , validate_point )
96+ elif sig_len == curve .baselen + 1 :
97+ point = klass ._from_compressed (string , curve , validate_point )
98+ else :
99+ raise MalformedPoint ("Length of string does not match lengths of "
100+ "any of the supported encodings of {0} "
101+ "curve." .format (curve .name ))
102+
57103 return klass .from_public_point (point , curve , hashfunc )
58104
59105 @classmethod
@@ -110,15 +156,32 @@ def from_public_key_recovery_with_digest(klass, signature, digest, curve, hashfu
110156 verifying_keys = [klass .from_public_point (pk .point , curve , hashfunc ) for pk in pks ]
111157 return verifying_keys
112158
113- def to_string (self ):
114- # VerifyingKey.from_string(vk.to_string()) == vk as long as the
115- # curves are the same: the curve itself is not included in the
116- # serialized form
159+ def _raw_encode (self ):
117160 order = self .pubkey .order
118161 x_str = number_to_string (self .pubkey .point .x (), order )
119162 y_str = number_to_string (self .pubkey .point .y (), order )
120163 return x_str + y_str
121164
165+ def _compressed_encode (self ):
166+ order = self .pubkey .order
167+ x_str = number_to_string (self .pubkey .point .x (), order )
168+ if self .pubkey .point .y () & 1 :
169+ return b ('\x03 ' ) + x_str
170+ else :
171+ return b ('\x02 ' ) + x_str
172+
173+ def to_string (self , encoding = "raw" ):
174+ # VerifyingKey.from_string(vk.to_string()) == vk as long as the
175+ # curves are the same: the curve itself is not included in the
176+ # serialized form
177+ assert encoding in ("raw" , "uncompressed" , "compressed" )
178+ if encoding == "raw" :
179+ return self ._raw_encode ()
180+ elif encoding == "uncompressed" :
181+ return b ('\x04 ' ) + self ._raw_encode ()
182+ else :
183+ return self ._compressed_encode ()
184+
122185 def to_pem (self ):
123186 return der .topem (self .to_der (), "PUBLIC KEY" )
124187
0 commit comments