Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More test coverage #126

Merged
merged 4 commits into from Oct 10, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 1 addition & 5 deletions .coveragerc
Expand Up @@ -4,9 +4,5 @@
include =
src/ecdsa/*
omit =
src/ecdsa/six.py
src/ecdsa/_version.py
src/ecdsa/test_ecdsa.py
src/ecdsa/test_ellipticcurve.py
src/ecdsa/test_numbertheory.py
src/ecdsa/test_pyecdsa.py
src/ecdsa/test_*
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -123,8 +123,8 @@ is to call `s=sk.to_string()`, and then re-create it with
`SigningKey.from_string(s, curve)` . This short form does not record the
curve, so you must be sure to tell from_string() the same curve you used for
the original key. The short form of a NIST192p-based signing key is just 24
bytes long. If the point encoding is invalid or it does not lie on the
specified curve, `from_string()` will raise MalformedPointError.
bytes long. If a point encoding is invalid or it does not lie on the specified
curve, `from_string()` will raise MalformedPointError.

```python
from ecdsa import SigningKey, NIST384p
Expand Down
5 changes: 4 additions & 1 deletion src/ecdsa/__init__.py
@@ -1,5 +1,7 @@
from .keys import SigningKey, VerifyingKey, BadSignatureError, BadDigestError
from .keys import SigningKey, VerifyingKey, BadSignatureError, BadDigestError,\
MalformedPointError
from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1
from .der import UnexpectedDER

# This code comes from http://github.com/warner/python-ecdsa
from ._version import get_versions
Expand All @@ -10,5 +12,6 @@
"test_pyecdsa", "util", "six"]

_hush_pyflakes = [SigningKey, VerifyingKey, BadSignatureError, BadDigestError,
MalformedPointError, UnexpectedDER,
NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1]
del _hush_pyflakes
24 changes: 15 additions & 9 deletions src/ecdsa/keys.py
Expand Up @@ -48,12 +48,14 @@ def from_public_point(klass, point, curve=NIST192p, hashfunc=sha1):
@staticmethod
def _from_raw_encoding(string, curve, validate_point):
order = curve.order
assert (len(string) == curve.verifying_key_length), \
(len(string), curve.verifying_key_length)
# real assert, from_string() should not call us with different length
assert len(string) == curve.verifying_key_length
xs = string[:curve.baselen]
ys = string[curve.baselen:]
assert len(xs) == curve.baselen, (len(xs), curve.baselen)
assert len(ys) == curve.baselen, (len(ys), curve.baselen)
if len(xs) != curve.baselen:
raise MalformedPointError("Unexpected length of encoded x")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wouldn't it be a bit friendly that the actual length is embedded in the message?

Copy link
Member Author

@tomato42 tomato42 Oct 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the problem is that we support 3 different lengths: for raw encoding, uncompressed X9.62 and compressed X9.62 so the message would be very confusing if we provided so much data.

And those two checks will get triggered only when the Curve we use is misconstructed, so they are more asserts than real checks

if len(ys) != curve.baselen:
raise MalformedPointError("Unexpected length of encoded y")
x = string_to_number(xs)
y = string_to_number(ys)
if validate_point and not ecdsa.point_is_valid(curve.generator, x, y):
Expand Down Expand Up @@ -86,6 +88,7 @@ def _from_compressed(string, curve, validate_point):

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

# primarily use the uncompressed as it's easiest to handle
Expand Down Expand Up @@ -223,9 +226,6 @@ def to_pem(self):
return der.topem(self.to_der(), "PUBLIC KEY")

def to_der(self, point_encoding="uncompressed"):
order = self.pubkey.order
x_str = number_to_string(self.pubkey.point.x(), order)
y_str = number_to_string(self.pubkey.point.y(), order)
point_str = b("\x00") + self.to_string(point_encoding)
return der.encode_sequence(der.encode_sequence(encoded_oid_ecPublicKey,
self.curve.encoded_oid),
Expand Down Expand Up @@ -274,7 +274,10 @@ def from_secret_exponent(klass, secexp, curve=NIST192p, hashfunc=sha1):
self.default_hashfunc = hashfunc
self.baselen = curve.baselen
n = curve.order
assert 1 <= secexp < n
if not 1 <= secexp < n:
raise MalformedPointError(
"Invalid value for secexp, expected integer between 1 and {0}"
.format(n))
pubkey_point = curve.generator * secexp
pubkey = ecdsa.Public_key(curve.generator, pubkey_point)
pubkey.order = n
Expand All @@ -286,7 +289,10 @@ def from_secret_exponent(klass, secexp, curve=NIST192p, hashfunc=sha1):

@classmethod
def from_string(klass, string, curve=NIST192p, hashfunc=sha1):
assert len(string) == curve.baselen, (len(string), curve.baselen)
if len(string) != curve.baselen:
raise MalformedPointError(
"Invalid length of private key, received {0}, expected {1}"
.format(len(string), curve.baselen))
secexp = string_to_number(string)
return klass.from_secret_exponent(secexp, curve, hashfunc)

Expand Down
50 changes: 43 additions & 7 deletions src/ecdsa/numbertheory.py
Expand Up @@ -19,6 +19,7 @@
xrange = range

import math
import warnings


class Error(Exception):
Expand Down Expand Up @@ -326,8 +327,13 @@ def factorization(n):
return result


def phi(n):
def phi(n): # pragma: no cover
"""Return the Euler totient function of n."""
# deprecated in 0.14
warnings.warn("Function is unused by library code. If you use this code, "
"please open an issue in "
"https://github.com/warner/python-ecdsa",
DeprecationWarning)

assert isinstance(n, integer_types)

Expand All @@ -345,20 +351,30 @@ def phi(n):
return result


def carmichael(n):
def carmichael(n): # pragma: no cover
"""Return Carmichael function of n.

Carmichael(n) is the smallest integer x such that
m**x = 1 mod n for all m relatively prime to n.
"""
# deprecated in 0.14
warnings.warn("Function is unused by library code. If you use this code, "
"please open an issue in "
"https://github.com/warner/python-ecdsa",
DeprecationWarning)

return carmichael_of_factorized(factorization(n))


def carmichael_of_factorized(f_list):
def carmichael_of_factorized(f_list): # pragma: no cover
"""Return the Carmichael function of a number that is
represented as a list of (prime,exponent) pairs.
"""
# deprecated in 0.14
warnings.warn("Function is unused by library code. If you use this code, "
"please open an issue in "
"https://github.com/warner/python-ecdsa",
DeprecationWarning)

if len(f_list) < 1:
return 1
Expand All @@ -370,9 +386,14 @@ def carmichael_of_factorized(f_list):
return result


def carmichael_of_ppower(pp):
def carmichael_of_ppower(pp): # pragma: no cover
"""Carmichael function of the given power of the given prime.
"""
# deprecated in 0.14
warnings.warn("Function is unused by library code. If you use this code, "
"please open an issue in "
"https://github.com/warner/python-ecdsa",
DeprecationWarning)

p, a = pp
if p == 2 and a > 2:
Expand All @@ -381,9 +402,14 @@ def carmichael_of_ppower(pp):
return (p - 1) * p**(a - 1)


def order_mod(x, m):
def order_mod(x, m): # pragma: no cover
"""Return the order of x in the multiplicative group mod m.
"""
# deprecated in 0.14
warnings.warn("Function is unused by library code. If you use this code, "
"please open an issue in "
"https://github.com/warner/python-ecdsa",
DeprecationWarning)

# Warning: this implementation is not very clever, and will
# take a long time if m is very large.
Expand All @@ -401,9 +427,14 @@ def order_mod(x, m):
return result


def largest_factor_relatively_prime(a, b):
def largest_factor_relatively_prime(a, b): # pragma: no cover
"""Return the largest factor of a relatively prime to b.
"""
# deprecated in 0.14
warnings.warn("Function is unused by library code. If you use this code, "
"please open an issue in "
"https://github.com/warner/python-ecdsa",
DeprecationWarning)

while 1:
d = gcd(a, b)
Expand All @@ -418,10 +449,15 @@ def largest_factor_relatively_prime(a, b):
return a


def kinda_order_mod(x, m):
def kinda_order_mod(x, m): # pragma: no cover
"""Return the order of x in the multiplicative group mod m',
where m' is the largest factor of m relatively prime to x.
"""
# deprecated in 0.14
warnings.warn("Function is unused by library code. If you use this code, "
"please open an issue in "
"https://github.com/warner/python-ecdsa",
DeprecationWarning)

return order_mod(x, largest_factor_relatively_prime(m, x))

Expand Down