11from __future__ import division
22
3- from . import der , ecdsa
4- from .util import orderlen
3+ from . import der , ecdsa , ellipticcurve
4+ from .util import orderlen , number_to_string , string_to_number
5+ from ._compat import normalise_bytes
56
67
78# orderlen was defined in this module previously, so keep it in __all__,
2930 "BRAINPOOLP320r1" ,
3031 "BRAINPOOLP384r1" ,
3132 "BRAINPOOLP512r1" ,
33+ "PRIME_FIELD_OID" ,
34+ "CHARACTERISTIC_TWO_FIELD_OID" ,
3235]
3336
3437
38+ PRIME_FIELD_OID = (1 , 2 , 840 , 10045 , 1 , 1 )
39+ CHARACTERISTIC_TWO_FIELD_OID = (1 , 2 , 840 , 10045 , 1 , 2 )
40+
41+
3542class UnknownCurveError (Exception ):
3643 pass
3744
@@ -47,11 +54,142 @@ def __init__(self, name, curve, generator, oid, openssl_name=None):
4754 self .verifying_key_length = 2 * orderlen (curve .p ())
4855 self .signature_length = 2 * self .baselen
4956 self .oid = oid
50- self .encoded_oid = der .encode_oid (* oid )
57+ if oid :
58+ self .encoded_oid = der .encode_oid (* oid )
59+
60+ def __eq__ (self , other ):
61+ if isinstance (other , Curve ):
62+ return (
63+ self .curve == other .curve and self .generator == other .generator
64+ )
65+ return NotImplemented
66+
67+ def __ne__ (self , other ):
68+ return not self == other
5169
5270 def __repr__ (self ):
5371 return self .name
5472
73+ def to_der (self , encoding = None , point_encoding = "uncompressed" ):
74+ """Serialise the curve parameters to binary string.
75+
76+ :param str encoding: the format to save the curve parameters in.
77+ Default is ``named_curve``, with fallback being the ``explicit``
78+ if the OID is not set for the curve.
79+ :param str point_encoding: the point encoding of the generator when
80+ explicit curve encoding is used. Ignored for ``named_curve``
81+ format.
82+ """
83+ if encoding is None :
84+ if self .oid :
85+ encoding = "named_curve"
86+ else :
87+ encoding = "explicit"
88+
89+ if encoding == "named_curve" :
90+ if not self .oid :
91+ raise UnknownCurveError (
92+ "Can't encode curve using named_curve encoding without "
93+ "associated curve OID"
94+ )
95+ return der .encode_oid (* self .oid )
96+
97+ # encode the ECParameters sequence
98+ curve_p = self .curve .p ()
99+ version = der .encode_integer (1 )
100+ field_id = der .encode_sequence (
101+ der .encode_oid (* PRIME_FIELD_OID ), der .encode_integer (curve_p )
102+ )
103+ curve = der .encode_sequence (
104+ der .encode_octet_string (
105+ number_to_string (self .curve .a () % curve_p , curve_p )
106+ ),
107+ der .encode_octet_string (
108+ number_to_string (self .curve .b () % curve_p , curve_p )
109+ ),
110+ )
111+ base = der .encode_octet_string (self .generator .to_bytes (point_encoding ))
112+ order = der .encode_integer (self .generator .order ())
113+ seq_elements = [version , field_id , curve , base , order ]
114+ if self .curve .cofactor ():
115+ cofactor = der .encode_integer (self .curve .cofactor ())
116+ seq_elements .append (cofactor )
117+
118+ return der .encode_sequence (* seq_elements )
119+
120+ @staticmethod
121+ def from_der (data ):
122+ """Decode the curve parameters from DER file.
123+
124+ :param data: the binary string to decode the parameters from
125+ :type data: bytes-like object
126+ """
127+ data = normalise_bytes (data )
128+ if not der .is_sequence (data ):
129+ oid , empty = der .remove_object (data )
130+ if empty :
131+ raise der .UnexpectedDER ("Unexpected data after OID" )
132+ return find_curve (oid )
133+
134+ seq , empty = der .remove_sequence (data )
135+ if empty :
136+ raise der .UnexpectedDER (
137+ "Unexpected data after ECParameters structure"
138+ )
139+ # decode the ECParameters sequence
140+ version , rest = der .remove_integer (seq )
141+ if version != 1 :
142+ raise der .UnexpectedDER ("Unknown parameter encoding format" )
143+ field_id , rest = der .remove_sequence (rest )
144+ curve , rest = der .remove_sequence (rest )
145+ base_bytes , rest = der .remove_octet_string (rest )
146+ order , rest = der .remove_integer (rest )
147+ cofactor = None
148+ if rest :
149+ cofactor , rest = der .remove_integer (rest )
150+
151+ # decode the ECParameters.fieldID sequence
152+ field_type , rest = der .remove_object (field_id )
153+ if field_type == CHARACTERISTIC_TWO_FIELD_OID :
154+ raise UnknownCurveError ("Characteristic 2 curves unsupported" )
155+ if field_type != PRIME_FIELD_OID :
156+ raise UnknownCurveError (
157+ "Unknown field type: {0}" .format (field_type )
158+ )
159+ prime , empty = der .remove_integer (rest )
160+ if empty :
161+ raise der .UnexpectedDER (
162+ "Unexpected data after ECParameters.fieldID.Prime-p element"
163+ )
164+
165+ # decode the ECParameters.curve sequence
166+ curve_a_bytes , rest = der .remove_octet_string (curve )
167+ curve_b_bytes , rest = der .remove_octet_string (rest )
168+ # seed can be defined here, but we don't parse it, so ignore `rest`
169+
170+ curve_a = string_to_number (curve_a_bytes )
171+ curve_b = string_to_number (curve_b_bytes )
172+
173+ curve_fp = ellipticcurve .CurveFp (prime , curve_a , curve_b , cofactor )
174+
175+ # decode the ECParameters.base point
176+
177+ base = ellipticcurve .PointJacobi .from_bytes (
178+ curve_fp ,
179+ base_bytes ,
180+ valid_encodings = ("uncompressed" , "compressed" , "hybrid" ),
181+ order = order ,
182+ generator = True ,
183+ )
184+ tmp_curve = Curve ("unknown" , curve_fp , base , None )
185+
186+ # if the curve matches one of the well-known ones, use the well-known
187+ # one in preference, as it will have the OID and name associated
188+ for i in curves :
189+ if tmp_curve == i :
190+ return i
191+ return tmp_curve
192+
55193
56194# the SEC curves
57195SECP112r1 = Curve (
0 commit comments