Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/ecdsa/curves.py: 39%
147 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:16 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:16 +0000
1from __future__ import division
3from six import PY2
4from . import der, ecdsa, ellipticcurve, eddsa
5from .util import orderlen, number_to_string, string_to_number
6from ._compat import normalise_bytes, bit_length
9# orderlen was defined in this module previously, so keep it in __all__,
10# will need to mark it as deprecated later
11__all__ = [
12 "UnknownCurveError",
13 "orderlen",
14 "Curve",
15 "SECP112r1",
16 "SECP112r2",
17 "SECP128r1",
18 "SECP160r1",
19 "NIST192p",
20 "NIST224p",
21 "NIST256p",
22 "NIST384p",
23 "NIST521p",
24 "curves",
25 "find_curve",
26 "curve_by_name",
27 "SECP256k1",
28 "BRAINPOOLP160r1",
29 "BRAINPOOLP192r1",
30 "BRAINPOOLP224r1",
31 "BRAINPOOLP256r1",
32 "BRAINPOOLP320r1",
33 "BRAINPOOLP384r1",
34 "BRAINPOOLP512r1",
35 "PRIME_FIELD_OID",
36 "CHARACTERISTIC_TWO_FIELD_OID",
37 "Ed25519",
38 "Ed448",
39]
42PRIME_FIELD_OID = (1, 2, 840, 10045, 1, 1)
43CHARACTERISTIC_TWO_FIELD_OID = (1, 2, 840, 10045, 1, 2)
46class UnknownCurveError(Exception):
47 pass
50class Curve:
51 def __init__(self, name, curve, generator, oid, openssl_name=None):
52 self.name = name
53 self.openssl_name = openssl_name # maybe None
54 self.curve = curve
55 self.generator = generator
56 self.order = generator.order()
57 if isinstance(curve, ellipticcurve.CurveEdTw):
58 # EdDSA keys are special in that both private and public
59 # are the same size (as it's defined only with compressed points)
61 # +1 for the sign bit and then round up
62 self.baselen = (bit_length(curve.p()) + 1 + 7) // 8
63 self.verifying_key_length = self.baselen
64 else:
65 self.baselen = orderlen(self.order)
66 self.verifying_key_length = 2 * orderlen(curve.p())
67 self.signature_length = 2 * self.baselen
68 self.oid = oid
69 if oid:
70 self.encoded_oid = der.encode_oid(*oid)
72 def __eq__(self, other):
73 if isinstance(other, Curve):
74 return (
75 self.curve == other.curve and self.generator == other.generator
76 )
77 return NotImplemented
79 def __ne__(self, other):
80 return not self == other
82 def __repr__(self):
83 return self.name
85 def to_der(self, encoding=None, point_encoding="uncompressed"):
86 """Serialise the curve parameters to binary string.
88 :param str encoding: the format to save the curve parameters in.
89 Default is ``named_curve``, with fallback being the ``explicit``
90 if the OID is not set for the curve.
91 :param str point_encoding: the point encoding of the generator when
92 explicit curve encoding is used. Ignored for ``named_curve``
93 format.
95 :return: DER encoded ECParameters structure
96 :rtype: bytes
97 """
98 if encoding is None:
99 if self.oid:
100 encoding = "named_curve"
101 else:
102 encoding = "explicit"
104 if encoding not in ("named_curve", "explicit"):
105 raise ValueError(
106 "Only 'named_curve' and 'explicit' encodings supported"
107 )
109 if encoding == "named_curve":
110 if not self.oid:
111 raise UnknownCurveError(
112 "Can't encode curve using named_curve encoding without "
113 "associated curve OID"
114 )
115 return der.encode_oid(*self.oid)
116 elif isinstance(self.curve, ellipticcurve.CurveEdTw):
117 assert encoding == "explicit"
118 raise UnknownCurveError(
119 "Twisted Edwards curves don't support explicit encoding"
120 )
122 # encode the ECParameters sequence
123 curve_p = self.curve.p()
124 version = der.encode_integer(1)
125 field_id = der.encode_sequence(
126 der.encode_oid(*PRIME_FIELD_OID), der.encode_integer(curve_p)
127 )
128 curve = der.encode_sequence(
129 der.encode_octet_string(
130 number_to_string(self.curve.a() % curve_p, curve_p)
131 ),
132 der.encode_octet_string(
133 number_to_string(self.curve.b() % curve_p, curve_p)
134 ),
135 )
136 base = der.encode_octet_string(self.generator.to_bytes(point_encoding))
137 order = der.encode_integer(self.generator.order())
138 seq_elements = [version, field_id, curve, base, order]
139 if self.curve.cofactor():
140 cofactor = der.encode_integer(self.curve.cofactor())
141 seq_elements.append(cofactor)
143 return der.encode_sequence(*seq_elements)
145 def to_pem(self, encoding=None, point_encoding="uncompressed"):
146 """
147 Serialise the curve parameters to the :term:`PEM` format.
149 :param str encoding: the format to save the curve parameters in.
150 Default is ``named_curve``, with fallback being the ``explicit``
151 if the OID is not set for the curve.
152 :param str point_encoding: the point encoding of the generator when
153 explicit curve encoding is used. Ignored for ``named_curve``
154 format.
156 :return: PEM encoded ECParameters structure
157 :rtype: str
158 """
159 return der.topem(
160 self.to_der(encoding, point_encoding), "EC PARAMETERS"
161 )
163 @staticmethod
164 def from_der(data, valid_encodings=None):
165 """Decode the curve parameters from DER file.
167 :param data: the binary string to decode the parameters from
168 :type data: :term:`bytes-like object`
169 :param valid_encodings: set of names of allowed encodings, by default
170 all (set by passing ``None``), supported ones are ``named_curve``
171 and ``explicit``
172 :type valid_encodings: :term:`set-like object`
173 """
174 if not valid_encodings:
175 valid_encodings = set(("named_curve", "explicit"))
176 if not all(i in ["named_curve", "explicit"] for i in valid_encodings):
177 raise ValueError(
178 "Only named_curve and explicit encodings supported"
179 )
180 data = normalise_bytes(data)
181 if not der.is_sequence(data):
182 if "named_curve" not in valid_encodings:
183 raise der.UnexpectedDER(
184 "named_curve curve parameters not allowed"
185 )
186 oid, empty = der.remove_object(data)
187 if empty:
188 raise der.UnexpectedDER("Unexpected data after OID")
189 return find_curve(oid)
191 if "explicit" not in valid_encodings:
192 raise der.UnexpectedDER("explicit curve parameters not allowed")
194 seq, empty = der.remove_sequence(data)
195 if empty:
196 raise der.UnexpectedDER(
197 "Unexpected data after ECParameters structure"
198 )
199 # decode the ECParameters sequence
200 version, rest = der.remove_integer(seq)
201 if version != 1:
202 raise der.UnexpectedDER("Unknown parameter encoding format")
203 field_id, rest = der.remove_sequence(rest)
204 curve, rest = der.remove_sequence(rest)
205 base_bytes, rest = der.remove_octet_string(rest)
206 order, rest = der.remove_integer(rest)
207 cofactor = None
208 if rest:
209 # the ASN.1 specification of ECParameters allows for future
210 # extensions of the sequence, so ignore the remaining bytes
211 cofactor, _ = der.remove_integer(rest)
213 # decode the ECParameters.fieldID sequence
214 field_type, rest = der.remove_object(field_id)
215 if field_type == CHARACTERISTIC_TWO_FIELD_OID:
216 raise UnknownCurveError("Characteristic 2 curves unsupported")
217 if field_type != PRIME_FIELD_OID:
218 raise UnknownCurveError(
219 "Unknown field type: {0}".format(field_type)
220 )
221 prime, empty = der.remove_integer(rest)
222 if empty:
223 raise der.UnexpectedDER(
224 "Unexpected data after ECParameters.fieldID.Prime-p element"
225 )
227 # decode the ECParameters.curve sequence
228 curve_a_bytes, rest = der.remove_octet_string(curve)
229 curve_b_bytes, rest = der.remove_octet_string(rest)
230 # seed can be defined here, but we don't parse it, so ignore `rest`
232 curve_a = string_to_number(curve_a_bytes)
233 curve_b = string_to_number(curve_b_bytes)
235 curve_fp = ellipticcurve.CurveFp(prime, curve_a, curve_b, cofactor)
237 # decode the ECParameters.base point
239 base = ellipticcurve.PointJacobi.from_bytes(
240 curve_fp,
241 base_bytes,
242 valid_encodings=("uncompressed", "compressed", "hybrid"),
243 order=order,
244 generator=True,
245 )
246 tmp_curve = Curve("unknown", curve_fp, base, None)
248 # if the curve matches one of the well-known ones, use the well-known
249 # one in preference, as it will have the OID and name associated
250 for i in curves:
251 if tmp_curve == i:
252 return i
253 return tmp_curve
255 @classmethod
256 def from_pem(cls, string, valid_encodings=None):
257 """Decode the curve parameters from PEM file.
259 :param str string: the text string to decode the parameters from
260 :param valid_encodings: set of names of allowed encodings, by default
261 all (set by passing ``None``), supported ones are ``named_curve``
262 and ``explicit``
263 :type valid_encodings: :term:`set-like object`
264 """
265 if not PY2 and isinstance(string, str): # pragma: no branch
266 string = string.encode()
268 ec_param_index = string.find(b"-----BEGIN EC PARAMETERS-----")
269 if ec_param_index == -1:
270 raise der.UnexpectedDER("EC PARAMETERS PEM header not found")
272 return cls.from_der(
273 der.unpem(string[ec_param_index:]), valid_encodings
274 )
277# the SEC curves
278SECP112r1 = Curve(
279 "SECP112r1",
280 ecdsa.curve_112r1,
281 ecdsa.generator_112r1,
282 (1, 3, 132, 0, 6),
283 "secp112r1",
284)
287SECP112r2 = Curve(
288 "SECP112r2",
289 ecdsa.curve_112r2,
290 ecdsa.generator_112r2,
291 (1, 3, 132, 0, 7),
292 "secp112r2",
293)
296SECP128r1 = Curve(
297 "SECP128r1",
298 ecdsa.curve_128r1,
299 ecdsa.generator_128r1,
300 (1, 3, 132, 0, 28),
301 "secp128r1",
302)
305SECP160r1 = Curve(
306 "SECP160r1",
307 ecdsa.curve_160r1,
308 ecdsa.generator_160r1,
309 (1, 3, 132, 0, 8),
310 "secp160r1",
311)
314# the NIST curves
315NIST192p = Curve(
316 "NIST192p",
317 ecdsa.curve_192,
318 ecdsa.generator_192,
319 (1, 2, 840, 10045, 3, 1, 1),
320 "prime192v1",
321)
324NIST224p = Curve(
325 "NIST224p",
326 ecdsa.curve_224,
327 ecdsa.generator_224,
328 (1, 3, 132, 0, 33),
329 "secp224r1",
330)
333NIST256p = Curve(
334 "NIST256p",
335 ecdsa.curve_256,
336 ecdsa.generator_256,
337 (1, 2, 840, 10045, 3, 1, 7),
338 "prime256v1",
339)
342NIST384p = Curve(
343 "NIST384p",
344 ecdsa.curve_384,
345 ecdsa.generator_384,
346 (1, 3, 132, 0, 34),
347 "secp384r1",
348)
351NIST521p = Curve(
352 "NIST521p",
353 ecdsa.curve_521,
354 ecdsa.generator_521,
355 (1, 3, 132, 0, 35),
356 "secp521r1",
357)
360SECP256k1 = Curve(
361 "SECP256k1",
362 ecdsa.curve_secp256k1,
363 ecdsa.generator_secp256k1,
364 (1, 3, 132, 0, 10),
365 "secp256k1",
366)
369BRAINPOOLP160r1 = Curve(
370 "BRAINPOOLP160r1",
371 ecdsa.curve_brainpoolp160r1,
372 ecdsa.generator_brainpoolp160r1,
373 (1, 3, 36, 3, 3, 2, 8, 1, 1, 1),
374 "brainpoolP160r1",
375)
378BRAINPOOLP192r1 = Curve(
379 "BRAINPOOLP192r1",
380 ecdsa.curve_brainpoolp192r1,
381 ecdsa.generator_brainpoolp192r1,
382 (1, 3, 36, 3, 3, 2, 8, 1, 1, 3),
383 "brainpoolP192r1",
384)
387BRAINPOOLP224r1 = Curve(
388 "BRAINPOOLP224r1",
389 ecdsa.curve_brainpoolp224r1,
390 ecdsa.generator_brainpoolp224r1,
391 (1, 3, 36, 3, 3, 2, 8, 1, 1, 5),
392 "brainpoolP224r1",
393)
396BRAINPOOLP256r1 = Curve(
397 "BRAINPOOLP256r1",
398 ecdsa.curve_brainpoolp256r1,
399 ecdsa.generator_brainpoolp256r1,
400 (1, 3, 36, 3, 3, 2, 8, 1, 1, 7),
401 "brainpoolP256r1",
402)
405BRAINPOOLP320r1 = Curve(
406 "BRAINPOOLP320r1",
407 ecdsa.curve_brainpoolp320r1,
408 ecdsa.generator_brainpoolp320r1,
409 (1, 3, 36, 3, 3, 2, 8, 1, 1, 9),
410 "brainpoolP320r1",
411)
414BRAINPOOLP384r1 = Curve(
415 "BRAINPOOLP384r1",
416 ecdsa.curve_brainpoolp384r1,
417 ecdsa.generator_brainpoolp384r1,
418 (1, 3, 36, 3, 3, 2, 8, 1, 1, 11),
419 "brainpoolP384r1",
420)
423BRAINPOOLP512r1 = Curve(
424 "BRAINPOOLP512r1",
425 ecdsa.curve_brainpoolp512r1,
426 ecdsa.generator_brainpoolp512r1,
427 (1, 3, 36, 3, 3, 2, 8, 1, 1, 13),
428 "brainpoolP512r1",
429)
432Ed25519 = Curve(
433 "Ed25519",
434 eddsa.curve_ed25519,
435 eddsa.generator_ed25519,
436 (1, 3, 101, 112),
437)
440Ed448 = Curve(
441 "Ed448",
442 eddsa.curve_ed448,
443 eddsa.generator_ed448,
444 (1, 3, 101, 113),
445)
448# no order in particular, but keep previously added curves first
449curves = [
450 NIST192p,
451 NIST224p,
452 NIST256p,
453 NIST384p,
454 NIST521p,
455 SECP256k1,
456 BRAINPOOLP160r1,
457 BRAINPOOLP192r1,
458 BRAINPOOLP224r1,
459 BRAINPOOLP256r1,
460 BRAINPOOLP320r1,
461 BRAINPOOLP384r1,
462 BRAINPOOLP512r1,
463 SECP112r1,
464 SECP112r2,
465 SECP128r1,
466 SECP160r1,
467 Ed25519,
468 Ed448,
469]
472def find_curve(oid_curve):
473 """Select a curve based on its OID
475 :param tuple[int,...] oid_curve: ASN.1 Object Identifier of the
476 curve to return, like ``(1, 2, 840, 10045, 3, 1, 7)`` for ``NIST256p``.
478 :raises UnknownCurveError: When the oid doesn't match any of the supported
479 curves
481 :rtype: ~ecdsa.curves.Curve
482 """
483 for c in curves:
484 if c.oid == oid_curve:
485 return c
486 raise UnknownCurveError(
487 "I don't know about the curve with oid %s."
488 "I only know about these: %s" % (oid_curve, [c.name for c in curves])
489 )
492def curve_by_name(name):
493 """Select a curve based on its name.
495 Returns a :py:class:`~ecdsa.curves.Curve` object with a ``name`` name.
496 Note that ``name`` is case-sensitve.
498 :param str name: Name of the curve to return, like ``NIST256p`` or
499 ``prime256v1``
501 :raises UnknownCurveError: When the name doesn't match any of the supported
502 curves
504 :rtype: ~ecdsa.curves.Curve
505 """
506 for c in curves:
507 if name == c.name or (c.openssl_name and name == c.openssl_name):
508 return c
509 raise UnknownCurveError(
510 "Curve with name {0!r} unknown, only curves supported: {1}".format(
511 name, [c.name for c in curves]
512 )
513 )