Skip to content

Commit

Permalink
Merge 3547485 into 4c92d31
Browse files Browse the repository at this point in the history
  • Loading branch information
tomato42 committed Oct 27, 2019
2 parents 4c92d31 + 3547485 commit a79a2f4
Show file tree
Hide file tree
Showing 10 changed files with 395 additions and 148 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ coverage-html
.tox
nosetests.xml
t/
.hypothesis/

# Translations
*.mo
Expand Down
17 changes: 7 additions & 10 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ before_install:
TRAVIS_COMMIT_RANGE=$PR_FIRST^..$TRAVIS_COMMIT
fi
# sanity check current commit
- git rev-parse HEAD
- BRANCH=$(git rev-parse HEAD)
- echo "TRAVIS_COMMIT_RANGE=$TRAVIS_COMMIT_RANGE"
- git fetch origin master:refs/remotes/origin/master

Expand All @@ -78,31 +78,28 @@ install:
script:
- if [[ $TOX_ENV ]]; then tox -e $TOX_ENV; fi
- tox -e speed
- cp diff-instrumental.py diff-instrumental-2.py
- |
if [[ $INSTRUMENTAL && $TRAVIS_PULL_REQUEST != "false" ]]; then
git checkout $PR_FIRST^
# exclude the super slow test_malformed_sigs.py, until #127 is merged
files="$(ls src/ecdsa/test*.py | grep -v test_malformed_sigs.py)"
instrumental -t ecdsa -i 'test.*|.*_version' `which pytest` $files
instrumental -f .instrumental.cov -s
instrumental -f .instrumental.cov -s | python diff-instrumental-2.py --save .diff-instrumental
git checkout $TRAVIS_COMMIT
instrumental -t ecdsa -i 'test.*|.*_version' `which pytest` $files
instrumental -f .instrumental.cov -s | python diff-instrumental.py --save .diff-instrumental
git checkout $BRANCH
instrumental -t ecdsa -i 'test.*|.*_version' `which pytest` src/ecdsa
instrumental -f .instrumental.cov -sr
fi
- |
if [[ $INSTRUMENTAL && $TRAVIS_PULL_REQUEST == "false" ]]; then
# exclude the super slow test_malformed_sigs.py, until #127 is merged
files="$(ls src/ecdsa/test*.py | grep -v test_malformed_sigs.py)"
instrumental -t ecdsa -i 'test.*|.*_version' `which pytest` $files
instrumental -t ecdsa -i 'test.*|.*_version' `which pytest` src/ecdsa
instrumental -f .instrumental.cov -s
# just log the values when merging
instrumental -f .instrumental.cov -s | python diff-instrumental-2.py
instrumental -f .instrumental.cov -s | python diff-instrumental.py
fi
- |
if [[ $INSTRUMENTAL && $TRAVIS_PULL_REQUEST != "false" ]]; then
instrumental -f .instrumental.cov -s | python diff-instrumental-2.py --read .diff-instrumental --fail-under 70 --max-difference -0.1
instrumental -f .instrumental.cov -s | python diff-instrumental.py --read .diff-instrumental --fail-under 70 --max-difference -0.1
fi
after_success:
- if [[ -z $INSTRUMENTAL ]]; then coveralls; fi
Expand Down
1 change: 1 addition & 0 deletions build-requirements-2.6.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ tox
coveralls<1.3.0
idna<2.8
unittest2
hypothesis<3
2 changes: 2 additions & 0 deletions build-requirements-3.3.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ pluggy<0.6
tox<3
wheel<0.30
virtualenv==15.2.0
enum34
hypothesis<3.44
2 changes: 2 additions & 0 deletions build-requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
tox
python-coveralls
hypothesis
pytest>=4.6.0
19 changes: 17 additions & 2 deletions src/ecdsa/curves.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@
# will need to mark it as deprecated later
__all__ = ["UnknownCurveError", "orderlen", "Curve", "NIST192p",
"NIST224p", "NIST256p", "NIST384p", "NIST521p", "curves",
"find_curve"]
"find_curve", "SECP256k1"]


class UnknownCurveError(Exception):
pass


# the NIST curves
class Curve:
def __init__(self, name, curve, generator, oid, openssl_name=None):
self.name = name
Expand All @@ -29,25 +28,41 @@ def __init__(self, name, curve, generator, oid, openssl_name=None):
self.oid = oid
self.encoded_oid = der.encode_oid(*oid)

def __repr__(self):
return self.name


# the NIST curves
NIST192p = Curve("NIST192p", ecdsa.curve_192,
ecdsa.generator_192,
(1, 2, 840, 10045, 3, 1, 1), "prime192v1")


NIST224p = Curve("NIST224p", ecdsa.curve_224,
ecdsa.generator_224,
(1, 3, 132, 0, 33), "secp224r1")


NIST256p = Curve("NIST256p", ecdsa.curve_256,
ecdsa.generator_256,
(1, 2, 840, 10045, 3, 1, 7), "prime256v1")


NIST384p = Curve("NIST384p", ecdsa.curve_384,
ecdsa.generator_384,
(1, 3, 132, 0, 34), "secp384r1")


NIST521p = Curve("NIST521p", ecdsa.curve_521,
ecdsa.generator_521,
(1, 3, 132, 0, 35), "secp521r1")


SECP256k1 = Curve("SECP256k1", ecdsa.curve_secp256k1,
ecdsa.generator_secp256k1,
(1, 3, 132, 0, 10), "secp256k1")


curves = [NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1]


Expand Down
5 changes: 5 additions & 0 deletions src/ecdsa/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ def __init__(self, _error__please_use_generate=None):
self.default_hashfunc = None
self.pubkey = None

def __repr__(self):
pub_key = self.to_string("compressed")
return "VerifyingKey.from_string({0!r}, {1!r}, {2})".format(
pub_key, self.curve, self.default_hashfunc().name)

@classmethod
def from_public_point(cls, point, curve=NIST192p, hashfunc=sha1):
"""
Expand Down
128 changes: 73 additions & 55 deletions src/ecdsa/test_ecdsa.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
from .ecdsa import (Private_key, Public_key, Signature,
curve_192, generator_192,
digest_integer, ellipticcurve, point_is_valid)
from six import print_
import random
from __future__ import print_function
import sys
import hypothesis.strategies as st
from hypothesis import given, settings, note
from .ecdsa import Private_key, Public_key, Signature, \
curve_192, generator_192, digest_integer, ellipticcurve, point_is_valid, \
generator_224, generator_256, generator_384, generator_521, \
generator_secp256k1


HYP_SETTINGS = {}
# old hypothesis doesn't have the "deadline" setting
if sys.version_info > (2, 7):
# SEC521p is slow, allow long execution for it
HYP_SETTINGS["deadline"] = 2000


def test_ecdsa():
class TestFailure(Exception):
Expand All @@ -12,7 +23,7 @@ def test_point_validity(generator, x, y, expected):
"""generator defines the curve; is (x,y) a point on
this curve? "expected" is True if the right answer is Yes."""
if point_is_valid(generator, x, y) == expected:
print_("Point validity tested as expected.")
print("Point validity tested as expected.")
else:
raise TestFailure("*** Point validity test gave wrong result.")

Expand All @@ -24,8 +35,8 @@ def test_signature_validity(Msg, Qx, Qy, R, S, expected):
ellipticcurve.Point(curve_192, Qx, Qy))
got = pubk.verifies(digest_integer(Msg), Signature(R, S))
if got == expected:
print_("Signature tested as expected: got %s, expected %s." % \
(got, expected))
print("Signature tested as expected: got %s, expected %s." % \
(got, expected))
else:
raise TestFailure("*** Signature test failed: got %s, expected %s." % \
(got, expected))
Expand All @@ -35,29 +46,29 @@ def test_pk_recovery(Msg, R, S, Qx, Qy):
sign = Signature(R,S)
pks = sign.recover_public_keys(digest_integer(Msg), generator_192)

print_("Test pk recover")
print("Test pk recover")

if pks:

# Test if the signature is valid for all found public keys
for pk in pks:
q = pk.point
print_("Recovered q: %s" % q)
print("Recovered q: %s" % q)
test_signature_validity(Msg, q.x(), q.y(), R, S, True)

# Test if the original public key is in the set of found keys
original_q = ellipticcurve.Point(curve_192, Qx, Qy)
points = [pk.point for pk in pks]
if original_q in points:
print_("Original q was found")
print("Original q was found")
else:
raise TestFailure("Original q is not in the list of recovered public keys")

else:
raise TestFailure("*** NO valid public key returned")


print_("NIST Curve P-192:")
print("NIST Curve P-192:")

p192 = generator_192

Expand All @@ -68,15 +79,15 @@ def test_pk_recovery(Msg, R, S, Qx, Qy):
if Q.x() != 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5:
raise TestFailure("*** p192 * d came out wrong.")
else:
print_("p192 * d came out right.")
print("p192 * d came out right.")

k = 6140507067065001063065065565667405560006161556565665656654
R = k * p192
if R.x() != 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD \
or R.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835:
raise TestFailure("*** k * p192 came out wrong.")
else:
print_("k * p192 came out right.")
print("k * p192 came out right.")

u1 = 2563697409189434185194736134579731015366492496392189760599
u2 = 6266643813348617967186477710235785849136406323338782220568
Expand All @@ -85,7 +96,7 @@ def test_pk_recovery(Msg, R, S, Qx, Qy):
or temp.y() != 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835:
raise TestFailure("*** u1 * p192 + u2 * Q came out wrong.")
else:
print_("u1 * p192 + u2 * Q came out right.")
print("u1 * p192 + u2 * Q came out right.")

e = 968236873715988614170569073515315707566766479517
pubk = Public_key(generator_192, generator_192 * d)
Expand All @@ -96,21 +107,21 @@ def test_pk_recovery(Msg, R, S, Qx, Qy):
or s != 5735822328888155254683894997897571951568553642892029982342:
raise TestFailure("*** r or s came out wrong.")
else:
print_("r and s came out right.")
print("r and s came out right.")

valid = pubk.verifies(e, sig)
if valid:
print_("Signature verified OK.")
print("Signature verified OK.")
else:
raise TestFailure("*** Signature failed verification.")

valid = pubk.verifies(e - 1, sig)
if not valid:
print_("Forgery was correctly rejected.")
print("Forgery was correctly rejected.")
else:
raise TestFailure("*** Forgery was erroneously accepted.")

print_("Testing point validity, as per ECDSAVS.pdf B.2.2:")
print("Testing point validity, as per ECDSAVS.pdf B.2.2:")

test_point_validity( \
p192, \
Expand Down Expand Up @@ -184,8 +195,8 @@ def test_pk_recovery(Msg, R, S, Qx, Qy):
0x509014c0c4d6b536e3ca750ec09066af39b4c8616a53a923, \
False)

print_("Trying signature-verification tests from ECDSAVS.pdf B.2.4:")
print_("P-192:")
print("Trying signature-verification tests from ECDSAVS.pdf B.2.4:")
print("P-192:")
Msg = 0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff798cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d158
Qx = 0xd9dbfb332aa8e5ff091e8ce535857c37c73f6250ffb2e7ac
Qy = 0x282102e364feded3ad15ddf968f88d8321aa268dd483ebc4
Expand Down Expand Up @@ -293,38 +304,45 @@ def test_pk_recovery(Msg, R, S, Qx, Qy):
S = 0x984c2db99827576c0a41a5da41e07d8cc768bc82f18c9da9
test_signature_validity(Msg, Qx, Qy, R, S, False)

print_("Testing the example code:")

# Building a public/private key pair from the NIST Curve P-192:

g = generator_192
n = g.order()

# (random.SystemRandom is supposed to provide
# crypto-quality random numbers, but as Debian recently
# illustrated, a systems programmer can accidentally
# demolish this security, so in serious applications
# further precautions are appropriate.)

randrange = random.SystemRandom().randrange

secret = randrange(1, n)
pubkey = Public_key(g, g * secret)
privkey = Private_key(pubkey, secret)

# Signing a hash value:

hash = randrange(1, n)
signature = privkey.sign(hash, randrange(1, n))

# Verifying a signature for a hash value:

if pubkey.verifies(hash, signature):
print_("Demo verification succeeded.")
else:
raise TestFailure("*** Demo verification failed.")

if pubkey.verifies(hash - 1, signature):
raise TestFailure("**** Demo verification failed to reject tampered hash.")
else:
print_("Demo verification correctly rejected tampered hash.")
@st.composite
def st_random_gen_key_msg_nonce(draw):
"""Hypothesis strategy for test_sig_verify()."""
name_gen = {
"generator_192": generator_192,
"generator_224": generator_224,
"generator_256": generator_256,
"generator_secp256k1": generator_secp256k1,
"generator_384": generator_384,
"generator_521": generator_521}
name = draw(st.sampled_from(sorted(name_gen.keys())))
note("Generator used: {0}".format(name))
generator = name_gen[name]
order = generator.order()

key = draw(st.integers(min_value=1, max_value=order))
msg = draw(st.integers(min_value=1, max_value=order))
nonce = draw(st.integers(min_value=1, max_value=order+1) |
st.integers(min_value=order>>1, max_value=order))
return generator, key, msg, nonce


SIG_VER_SETTINGS = dict(HYP_SETTINGS)
SIG_VER_SETTINGS["max_examples"] = 10
@settings(**SIG_VER_SETTINGS)
@given(st_random_gen_key_msg_nonce())
def test_sig_verify(args):
"""
Check if signing and verification works for arbitrary messages and
that signatures for other messages are rejected.
"""
generator, sec_mult, msg, nonce = args

pubkey = Public_key(generator, generator * sec_mult)
privkey = Private_key(pubkey, sec_mult)

signature = privkey.sign(msg, nonce)

assert pubkey.verifies(msg, signature)

assert not pubkey.verifies(msg - 1, signature)
Loading

0 comments on commit a79a2f4

Please sign in to comment.