Skip to content

Commit 2574d21

Browse files
committed
support for limiting acceptable curve encodings
as some standards, like PKIX in X.509 certificates, don't allow for explicit curve paramters, provide an API that limits the supported formats
1 parent 5ddcd9b commit 2574d21

File tree

2 files changed

+62
-5
lines changed

2 files changed

+62
-5
lines changed

src/ecdsa/curves.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,19 +140,36 @@ def to_pem(self, encoding=None, point_encoding="uncompressed"):
140140
)
141141

142142
@staticmethod
143-
def from_der(data):
143+
def from_der(data, valid_encodings=None):
144144
"""Decode the curve parameters from DER file.
145145
146146
:param data: the binary string to decode the parameters from
147-
:type data: bytes-like object
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`
148152
"""
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+
)
149159
data = normalise_bytes(data)
150160
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+
)
151165
oid, empty = der.remove_object(data)
152166
if empty:
153167
raise der.UnexpectedDER("Unexpected data after OID")
154168
return find_curve(oid)
155169

170+
if "explicit" not in valid_encodings:
171+
raise der.UnexpectedDER("explicit curve parameters not allowed")
172+
156173
seq, empty = der.remove_sequence(data)
157174
if empty:
158175
raise der.UnexpectedDER(
@@ -168,7 +185,9 @@ def from_der(data):
168185
order, rest = der.remove_integer(rest)
169186
cofactor = None
170187
if rest:
171-
cofactor, rest = der.remove_integer(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)
172191

173192
# decode the ECParameters.fieldID sequence
174193
field_type, rest = der.remove_object(field_id)
@@ -213,10 +232,14 @@ def from_der(data):
213232
return tmp_curve
214233

215234
@classmethod
216-
def from_pem(cls, string):
235+
def from_pem(cls, string, valid_encodings=None):
217236
"""Decode the curve parameters from PEM file.
218237
219238
: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`
220243
"""
221244
if not PY2 and isinstance(string, str): # pragma: no branch
222245
string = string.encode()
@@ -225,7 +248,9 @@ def from_pem(cls, string):
225248
if ec_param_index == -1:
226249
raise der.UnexpectedDER("EC PARAMETERS PEM header not found")
227250

228-
return cls.from_der(der.unpem(string[ec_param_index:]))
251+
return cls.from_der(
252+
der.unpem(string[ec_param_index:]), valid_encodings
253+
)
229254

230255

231256
# the SEC curves

src/ecdsa/test_curves.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,32 @@ def test_from_pem(self):
3838

3939
self.assertIs(curve, NIST256p)
4040

41+
def test_from_pem_with_explicit_when_explicit_disabled(self):
42+
pem_params = (
43+
"-----BEGIN EC PARAMETERS-----\n"
44+
"MIHgAgEBMCwGByqGSM49AQECIQD/////AAAAAQAAAAAAAAAAAAAAAP/////////\n"
45+
"//////zBEBCD/////AAAAAQAAAAAAAAAAAAAAAP///////////////AQgWsY12K\n"
46+
"o6k+ez671VdpiGvGUdBrDMU7D2O848PifSYEsEQQRrF9Hy4SxCR/i85uVjpEDyd\n"
47+
"wN9gS3rM6D0oTlF2JjClk/jQuL+Gn+bjufrSnwPnhYrzjNXazFezsu2QGg3v1H1\n"
48+
"AiEA/////wAAAAD//////////7zm+q2nF56E87nKwvxjJVECAQE=\n"
49+
"-----END EC PARAMETERS-----\n"
50+
)
51+
with self.assertRaises(der.UnexpectedDER) as e:
52+
Curve.from_pem(pem_params, ["named_curve"])
53+
54+
self.assertIn("explicit curve parameters not", str(e.exception))
55+
56+
def test_from_pem_with_named_curve_with_named_curve_disabled(self):
57+
pem_params = (
58+
"-----BEGIN EC PARAMETERS-----\n"
59+
"BggqhkjOPQMBBw==\n"
60+
"-----END EC PARAMETERS-----\n"
61+
)
62+
with self.assertRaises(der.UnexpectedDER) as e:
63+
Curve.from_pem(pem_params, ["explicit"])
64+
65+
self.assertIn("named_curve curve parameters not", str(e.exception))
66+
4167
def test_from_pem_with_wrong_header(self):
4268
pem_params = (
4369
"-----BEGIN PARAMETERS-----\n"
@@ -119,6 +145,12 @@ def test_decoding_well_known_from_explicit_params(self):
119145

120146
self.assertIs(curve, NIST256p)
121147

148+
def test_decoding_with_incorrect_valid_encodings(self):
149+
with self.assertRaises(ValueError) as e:
150+
Curve.from_der(b"", ["explicitCA"])
151+
152+
self.assertIn("Only named_curve", str(e.exception))
153+
122154
def test_compare_curves_with_different_generators(self):
123155
curve_fp = CurveFp(23, 1, 7)
124156
base_a = PointJacobi(curve_fp, 13, 3, 1, 9, generator=True)

0 commit comments

Comments
 (0)