1
1
from __future__ import division
2
2
3
- from . import der , ecdsa
4
- from .util import orderlen
3
+ from six import PY2
4
+ from . import der , ecdsa , ellipticcurve
5
+ from .util import orderlen , number_to_string , string_to_number
6
+ from ._compat import normalise_bytes
5
7
6
8
7
9
# orderlen was defined in this module previously, so keep it in __all__,
29
31
"BRAINPOOLP320r1" ,
30
32
"BRAINPOOLP384r1" ,
31
33
"BRAINPOOLP512r1" ,
34
+ "PRIME_FIELD_OID" ,
35
+ "CHARACTERISTIC_TWO_FIELD_OID" ,
32
36
]
33
37
34
38
39
+ PRIME_FIELD_OID = (1 , 2 , 840 , 10045 , 1 , 1 )
40
+ CHARACTERISTIC_TWO_FIELD_OID = (1 , 2 , 840 , 10045 , 1 , 2 )
41
+
42
+
35
43
class UnknownCurveError (Exception ):
36
44
pass
37
45
@@ -47,11 +55,203 @@ def __init__(self, name, curve, generator, oid, openssl_name=None):
47
55
self .verifying_key_length = 2 * orderlen (curve .p ())
48
56
self .signature_length = 2 * self .baselen
49
57
self .oid = oid
50
- self .encoded_oid = der .encode_oid (* oid )
58
+ if oid :
59
+ self .encoded_oid = der .encode_oid (* oid )
60
+
61
+ def __eq__ (self , other ):
62
+ if isinstance (other , Curve ):
63
+ return (
64
+ self .curve == other .curve and self .generator == other .generator
65
+ )
66
+ return NotImplemented
67
+
68
+ def __ne__ (self , other ):
69
+ return not self == other
51
70
52
71
def __repr__ (self ):
53
72
return self .name
54
73
74
+ def to_der (self , encoding = None , point_encoding = "uncompressed" ):
75
+ """Serialise the curve parameters to binary string.
76
+
77
+ :param str encoding: the format to save the curve parameters in.
78
+ Default is ``named_curve``, with fallback being the ``explicit``
79
+ if the OID is not set for the curve.
80
+ :param str point_encoding: the point encoding of the generator when
81
+ explicit curve encoding is used. Ignored for ``named_curve``
82
+ format.
83
+
84
+ :return: DER encoded ECParameters structure
85
+ :rtype: bytes
86
+ """
87
+ if encoding is None :
88
+ if self .oid :
89
+ encoding = "named_curve"
90
+ else :
91
+ encoding = "explicit"
92
+
93
+ if encoding == "named_curve" :
94
+ if not self .oid :
95
+ raise UnknownCurveError (
96
+ "Can't encode curve using named_curve encoding without "
97
+ "associated curve OID"
98
+ )
99
+ return der .encode_oid (* self .oid )
100
+
101
+ # encode the ECParameters sequence
102
+ curve_p = self .curve .p ()
103
+ version = der .encode_integer (1 )
104
+ field_id = der .encode_sequence (
105
+ der .encode_oid (* PRIME_FIELD_OID ), der .encode_integer (curve_p )
106
+ )
107
+ curve = der .encode_sequence (
108
+ der .encode_octet_string (
109
+ number_to_string (self .curve .a () % curve_p , curve_p )
110
+ ),
111
+ der .encode_octet_string (
112
+ number_to_string (self .curve .b () % curve_p , curve_p )
113
+ ),
114
+ )
115
+ base = der .encode_octet_string (self .generator .to_bytes (point_encoding ))
116
+ order = der .encode_integer (self .generator .order ())
117
+ seq_elements = [version , field_id , curve , base , order ]
118
+ if self .curve .cofactor ():
119
+ cofactor = der .encode_integer (self .curve .cofactor ())
120
+ seq_elements .append (cofactor )
121
+
122
+ return der .encode_sequence (* seq_elements )
123
+
124
+ def to_pem (self , encoding = None , point_encoding = "uncompressed" ):
125
+ """
126
+ Serialise the curve parameters to the :term:`PEM` format.
127
+
128
+ :param str encoding: the format to save the curve parameters in.
129
+ Default is ``named_curve``, with fallback being the ``explicit``
130
+ if the OID is not set for the curve.
131
+ :param str point_encoding: the point encoding of the generator when
132
+ explicit curve encoding is used. Ignored for ``named_curve``
133
+ format.
134
+
135
+ :return: PEM encoded ECParameters structure
136
+ :rtype: str
137
+ """
138
+ return der .topem (
139
+ self .to_der (encoding , point_encoding ), "EC PARAMETERS"
140
+ )
141
+
142
+ @staticmethod
143
+ def from_der (data , valid_encodings = None ):
144
+ """Decode the curve parameters from DER file.
145
+
146
+ :param data: the binary string to decode the parameters from
147
+ :type data: :term:`bytes-like object`
148
+ :param valid_encodings: set of names of allowed encodings, by default
149
+ all (set by passing ``None``), supported ones are ``named_curve``
150
+ and ``explicit``
151
+ :type valid_encodings: :term:`set-like object`
152
+ """
153
+ if not valid_encodings :
154
+ valid_encodings = set (("named_curve" , "explicit" ))
155
+ if not all (i in ["named_curve" , "explicit" ] for i in valid_encodings ):
156
+ raise ValueError (
157
+ "Only named_curve and explicit encodings supported"
158
+ )
159
+ data = normalise_bytes (data )
160
+ if not der .is_sequence (data ):
161
+ if "named_curve" not in valid_encodings :
162
+ raise der .UnexpectedDER (
163
+ "named_curve curve parameters not allowed"
164
+ )
165
+ oid , empty = der .remove_object (data )
166
+ if empty :
167
+ raise der .UnexpectedDER ("Unexpected data after OID" )
168
+ return find_curve (oid )
169
+
170
+ if "explicit" not in valid_encodings :
171
+ raise der .UnexpectedDER ("explicit curve parameters not allowed" )
172
+
173
+ seq , empty = der .remove_sequence (data )
174
+ if empty :
175
+ raise der .UnexpectedDER (
176
+ "Unexpected data after ECParameters structure"
177
+ )
178
+ # decode the ECParameters sequence
179
+ version , rest = der .remove_integer (seq )
180
+ if version != 1 :
181
+ raise der .UnexpectedDER ("Unknown parameter encoding format" )
182
+ field_id , rest = der .remove_sequence (rest )
183
+ curve , rest = der .remove_sequence (rest )
184
+ base_bytes , rest = der .remove_octet_string (rest )
185
+ order , rest = der .remove_integer (rest )
186
+ cofactor = None
187
+ if rest :
188
+ # the ASN.1 specification of ECParameters allows for future
189
+ # extensions of the sequence, so ignore the remaining bytes
190
+ cofactor , _ = der .remove_integer (rest )
191
+
192
+ # decode the ECParameters.fieldID sequence
193
+ field_type , rest = der .remove_object (field_id )
194
+ if field_type == CHARACTERISTIC_TWO_FIELD_OID :
195
+ raise UnknownCurveError ("Characteristic 2 curves unsupported" )
196
+ if field_type != PRIME_FIELD_OID :
197
+ raise UnknownCurveError (
198
+ "Unknown field type: {0}" .format (field_type )
199
+ )
200
+ prime , empty = der .remove_integer (rest )
201
+ if empty :
202
+ raise der .UnexpectedDER (
203
+ "Unexpected data after ECParameters.fieldID.Prime-p element"
204
+ )
205
+
206
+ # decode the ECParameters.curve sequence
207
+ curve_a_bytes , rest = der .remove_octet_string (curve )
208
+ curve_b_bytes , rest = der .remove_octet_string (rest )
209
+ # seed can be defined here, but we don't parse it, so ignore `rest`
210
+
211
+ curve_a = string_to_number (curve_a_bytes )
212
+ curve_b = string_to_number (curve_b_bytes )
213
+
214
+ curve_fp = ellipticcurve .CurveFp (prime , curve_a , curve_b , cofactor )
215
+
216
+ # decode the ECParameters.base point
217
+
218
+ base = ellipticcurve .PointJacobi .from_bytes (
219
+ curve_fp ,
220
+ base_bytes ,
221
+ valid_encodings = ("uncompressed" , "compressed" , "hybrid" ),
222
+ order = order ,
223
+ generator = True ,
224
+ )
225
+ tmp_curve = Curve ("unknown" , curve_fp , base , None )
226
+
227
+ # if the curve matches one of the well-known ones, use the well-known
228
+ # one in preference, as it will have the OID and name associated
229
+ for i in curves :
230
+ if tmp_curve == i :
231
+ return i
232
+ return tmp_curve
233
+
234
+ @classmethod
235
+ def from_pem (cls , string , valid_encodings = None ):
236
+ """Decode the curve parameters from PEM file.
237
+
238
+ :param str string: the text string to decode the parameters from
239
+ :param valid_encodings: set of names of allowed encodings, by default
240
+ all (set by passing ``None``), supported ones are ``named_curve``
241
+ and ``explicit``
242
+ :type valid_encodings: :term:`set-like object`
243
+ """
244
+ if not PY2 and isinstance (string , str ): # pragma: no branch
245
+ string = string .encode ()
246
+
247
+ ec_param_index = string .find (b"-----BEGIN EC PARAMETERS-----" )
248
+ if ec_param_index == - 1 :
249
+ raise der .UnexpectedDER ("EC PARAMETERS PEM header not found" )
250
+
251
+ return cls .from_der (
252
+ der .unpem (string [ec_param_index :]), valid_encodings
253
+ )
254
+
55
255
56
256
# the SEC curves
57
257
SECP112r1 = Curve (
0 commit comments