Skip to content

Commit 165c453

Browse files
committed
make sure assert is not optimised out
as assert will be optimised out if the module is compiled with optimisations on, we can't use them for checking user-provided data use an exception that inherits from it, so that existing code will behave as expected
1 parent 37deb42 commit 165c453

File tree

3 files changed

+184
-13
lines changed

3 files changed

+184
-13
lines changed

.coveragerc

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,5 @@
44
include =
55
src/ecdsa/*
66
omit =
7-
src/ecdsa/six.py
87
src/ecdsa/_version.py
9-
src/ecdsa/test_ecdsa.py
10-
src/ecdsa/test_ellipticcurve.py
11-
src/ecdsa/test_numbertheory.py
12-
src/ecdsa/test_pyecdsa.py
8+
src/ecdsa/test_*

src/ecdsa/keys.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@ def from_public_point(klass, point, curve=NIST192p, hashfunc=sha1):
4848
@staticmethod
4949
def _from_raw_encoding(string, curve, validate_point):
5050
order = curve.order
51-
assert (len(string) == curve.verifying_key_length), \
52-
(len(string), curve.verifying_key_length)
51+
# real assert, from_string() should not call us with different length
52+
assert len(string) == curve.verifying_key_length
5353
xs = string[:curve.baselen]
5454
ys = string[curve.baselen:]
55-
assert len(xs) == curve.baselen, (len(xs), curve.baselen)
56-
assert len(ys) == curve.baselen, (len(ys), curve.baselen)
55+
if len(xs) != curve.baselen:
56+
raise MalformedPointError("Unexpected length of encoded x")
57+
if len(ys) != curve.baselen:
58+
raise MalformedPointError("Unexpected length of encoded y")
5759
x = string_to_number(xs)
5860
y = string_to_number(ys)
5961
if validate_point and not ecdsa.point_is_valid(curve.generator, x, y):
@@ -86,6 +88,7 @@ def _from_compressed(string, curve, validate_point):
8688

8789
@classmethod
8890
def _from_hybrid(cls, string, curve, validate_point):
91+
# real assert, from_string() should not call us with different types
8992
assert string[:1] in (b('\x06'), b('\x07'))
9093

9194
# primarily use the uncompressed as it's easiest to handle
@@ -271,7 +274,10 @@ def from_secret_exponent(klass, secexp, curve=NIST192p, hashfunc=sha1):
271274
self.default_hashfunc = hashfunc
272275
self.baselen = curve.baselen
273276
n = curve.order
274-
assert 1 <= secexp < n
277+
if not 1 <= secexp < n:
278+
raise MalformedPointError(
279+
"Invalid value for secexp, expected integer between 1 and {0}"
280+
.format(n))
275281
pubkey_point = curve.generator * secexp
276282
pubkey = ecdsa.Public_key(curve.generator, pubkey_point)
277283
pubkey.order = n
@@ -283,7 +289,10 @@ def from_secret_exponent(klass, secexp, curve=NIST192p, hashfunc=sha1):
283289

284290
@classmethod
285291
def from_string(klass, string, curve=NIST192p, hashfunc=sha1):
286-
assert len(string) == curve.baselen, (len(string), curve.baselen)
292+
if len(string) != curve.baselen:
293+
raise MalformedPointError(
294+
"Invalid length of private key, received {0}, expected {1}"
295+
.format(len(string), curve.baselen))
287296
secexp = string_to_number(string)
288297
return klass.from_secret_exponent(secexp, curve, hashfunc)
289298

src/ecdsa/test_pyecdsa.py

Lines changed: 168 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,19 @@
1414

1515
from six import b, print_, binary_type
1616
from .keys import SigningKey, VerifyingKey
17-
from .keys import BadSignatureError, MalformedPointError
17+
from .keys import BadSignatureError, MalformedPointError, BadDigestError
1818
from . import util
1919
from .util import sigencode_der, sigencode_strings
2020
from .util import sigdecode_der, sigdecode_strings
21-
from .util import number_to_string
21+
from .util import number_to_string, encoded_oid_ecPublicKey, \
22+
MalformedSignature
2223
from .curves import Curve, UnknownCurveError
2324
from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, \
2425
SECP256k1, curves
2526
from .ellipticcurve import Point
2627
from . import der
2728
from . import rfc6979
29+
from . import ecdsa
2830

2931

3032
class SubprocessError(Exception):
@@ -275,6 +277,47 @@ def order(self):
275277
pub2 = VerifyingKey.from_pem(pem)
276278
self.assertTruePubkeysEqual(pub1, pub2)
277279

280+
def test_vk_from_der_garbage_after_curve_oid(self):
281+
type_oid_der = encoded_oid_ecPublicKey
282+
curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + \
283+
b('garbage')
284+
enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
285+
point_der = der.encode_bitstring(b'\x00\xff')
286+
to_decode = der.encode_sequence(enc_type_der, point_der)
287+
288+
with self.assertRaises(der.UnexpectedDER):
289+
VerifyingKey.from_der(to_decode)
290+
291+
def test_vk_from_der_invalid_key_type(self):
292+
type_oid_der = der.encode_oid(*(1, 2, 3))
293+
curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
294+
enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
295+
point_der = der.encode_bitstring(b'\x00\xff')
296+
to_decode = der.encode_sequence(enc_type_der, point_der)
297+
298+
with self.assertRaises(der.UnexpectedDER):
299+
VerifyingKey.from_der(to_decode)
300+
301+
def test_vk_from_der_garbage_after_point_string(self):
302+
type_oid_der = encoded_oid_ecPublicKey
303+
curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
304+
enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
305+
point_der = der.encode_bitstring(b'\x00\xff') + b('garbage')
306+
to_decode = der.encode_sequence(enc_type_der, point_der)
307+
308+
with self.assertRaises(der.UnexpectedDER):
309+
VerifyingKey.from_der(to_decode)
310+
311+
def test_vk_from_der_invalid_bitstring(self):
312+
type_oid_der = encoded_oid_ecPublicKey
313+
curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
314+
enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der)
315+
point_der = der.encode_bitstring(b'\x08\xff')
316+
to_decode = der.encode_sequence(enc_type_der, point_der)
317+
318+
with self.assertRaises(der.UnexpectedDER):
319+
VerifyingKey.from_der(to_decode)
320+
278321
def test_signature_strings(self):
279322
priv1 = SigningKey.generate()
280323
pub1 = priv1.get_verifying_key()
@@ -298,6 +341,86 @@ def test_signature_strings(self):
298341
self.assertEqual(type(sig_der), binary_type)
299342
self.assertTrue(pub1.verify(sig_der, data, sigdecode=sigdecode_der))
300343

344+
def test_sig_decode_strings_with_invalid_count(self):
345+
with self.assertRaises(MalformedSignature):
346+
sigdecode_strings([b('one'), b('two'), b('three')], 0xff)
347+
348+
def test_sig_decode_strings_with_wrong_r_len(self):
349+
with self.assertRaises(MalformedSignature):
350+
sigdecode_strings([b('one'), b('two')], 0xff)
351+
352+
def test_sig_decode_strings_with_wrong_s_len(self):
353+
with self.assertRaises(MalformedSignature):
354+
sigdecode_strings([b('\xa0'), b('\xb0\xff')], 0xff)
355+
356+
def test_verify_with_too_long_input(self):
357+
sk = SigningKey.generate()
358+
vk = sk.verifying_key
359+
360+
with self.assertRaises(BadDigestError):
361+
vk.verify_digest(None, b('\x00') * 128)
362+
363+
def test_sk_from_secret_exponent_with_wrong_sec_exponent(self):
364+
with self.assertRaises(MalformedPointError):
365+
SigningKey.from_secret_exponent(0)
366+
367+
def test_sk_from_string_with_wrong_len_string(self):
368+
with self.assertRaises(MalformedPointError):
369+
SigningKey.from_string(b('\x01'))
370+
371+
def test_sk_from_der_with_junk_after_sequence(self):
372+
ver_der = der.encode_integer(1)
373+
to_decode = der.encode_sequence(ver_der) + b('garbage')
374+
375+
with self.assertRaises(der.UnexpectedDER):
376+
SigningKey.from_der(to_decode)
377+
378+
def test_sk_from_der_with_wrong_version(self):
379+
ver_der = der.encode_integer(0)
380+
to_decode = der.encode_sequence(ver_der)
381+
382+
with self.assertRaises(der.UnexpectedDER):
383+
SigningKey.from_der(to_decode)
384+
385+
def test_sk_from_der_invalid_const_tag(self):
386+
ver_der = der.encode_integer(1)
387+
privkey_der = der.encode_octet_string(b('\x00\xff'))
388+
curve_oid_der = der.encode_oid(*(1, 2, 3))
389+
const_der = der.encode_constructed(1, curve_oid_der)
390+
to_decode = der.encode_sequence(ver_der, privkey_der, const_der,
391+
curve_oid_der)
392+
393+
with self.assertRaises(der.UnexpectedDER):
394+
SigningKey.from_der(to_decode)
395+
396+
def test_sk_from_der_garbage_after_privkey_oid(self):
397+
ver_der = der.encode_integer(1)
398+
privkey_der = der.encode_octet_string(b('\x00\xff'))
399+
curve_oid_der = der.encode_oid(*(1, 2, 3)) + b('garbage')
400+
const_der = der.encode_constructed(0, curve_oid_der)
401+
to_decode = der.encode_sequence(ver_der, privkey_der, const_der,
402+
curve_oid_der)
403+
404+
with self.assertRaises(der.UnexpectedDER):
405+
SigningKey.from_der(to_decode)
406+
407+
def test_sk_from_der_with_short_privkey(self):
408+
ver_der = der.encode_integer(1)
409+
privkey_der = der.encode_octet_string(b('\x00\xff'))
410+
curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1))
411+
const_der = der.encode_constructed(0, curve_oid_der)
412+
to_decode = der.encode_sequence(ver_der, privkey_der, const_der,
413+
curve_oid_der)
414+
415+
sk = SigningKey.from_der(to_decode)
416+
self.assertEqual(sk.privkey.secret_multiplier, 255)
417+
418+
def test_sign_with_too_long_hash(self):
419+
sk = SigningKey.from_secret_exponent(12)
420+
421+
with self.assertRaises(BadDigestError):
422+
sk.sign_digest(b('\xff') * 64)
423+
301424
def test_hashfunc(self):
302425
sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256)
303426
data = b("security level is 128 bits")
@@ -448,6 +571,49 @@ def test_not_lying_on_curve(self):
448571
with self.assertRaises(MalformedPointError):
449572
VerifyingKey.from_string(b('\x02') + enc)
450573

574+
def test_decoding_with_malformed_uncompressed(self):
575+
enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3'
576+
'\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4'
577+
'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*')
578+
579+
with self.assertRaises(MalformedPointError):
580+
VerifyingKey.from_string(b('\x02') + enc)
581+
582+
def test_decoding_with_point_not_on_curve(self):
583+
enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3'
584+
'\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4'
585+
'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*')
586+
587+
with self.assertRaises(MalformedPointError):
588+
VerifyingKey.from_string(enc[:47] + b('\x00'))
589+
590+
def test_decoding_with_point_at_infinity(self):
591+
# decoding it is unsupported, as it's not necessary to encode it
592+
with self.assertRaises(MalformedPointError):
593+
VerifyingKey.from_string(b('\x00'))
594+
595+
def test_from_string_with_invalid_curve_too_short_ver_key_len(self):
596+
# both verifying_key_length and baselen are calculated internally
597+
# by the Curve constructor, but since we depend on them verify
598+
# that inconsistent values are detected
599+
curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2))
600+
curve.verifying_key_length = 16
601+
curve.baselen = 32
602+
603+
with self.assertRaises(MalformedPointError):
604+
VerifyingKey.from_string(b('\x00')*16, curve)
605+
606+
def test_from_string_with_invalid_curve_too_long_ver_key_len(self):
607+
# both verifying_key_length and baselen are calculated internally
608+
# by the Curve constructor, but since we depend on them verify
609+
# that inconsistent values are detected
610+
curve = Curve("test", ecdsa.curve_192, ecdsa.generator_192, (1, 2))
611+
curve.verifying_key_length = 16
612+
curve.baselen = 16
613+
614+
with self.assertRaises(MalformedPointError):
615+
VerifyingKey.from_string(b('\x00')*16, curve)
616+
451617

452618
@pytest.mark.parametrize("val,even",
453619
[(i, j) for i in range(256) for j in [True, False]])

0 commit comments

Comments
 (0)