From 5e60e0e3af85f22aa0afe8bf0ecf85619afacfeb Mon Sep 17 00:00:00 2001 From: Micah Anderson Date: Thu, 22 Aug 2013 16:39:52 -0400 Subject: Imported Upstream version 0.6.0.12 --- src/pycryptopp/test/test_ecdsa.py | 263 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 src/pycryptopp/test/test_ecdsa.py (limited to 'src/pycryptopp/test/test_ecdsa.py') diff --git a/src/pycryptopp/test/test_ecdsa.py b/src/pycryptopp/test/test_ecdsa.py new file mode 100644 index 0000000..d70a000 --- /dev/null +++ b/src/pycryptopp/test/test_ecdsa.py @@ -0,0 +1,263 @@ +#!/usr/bin/env python + +import random +import base64 + +import os +SEED = os.environ.get('REPEATABLE_RANDOMNESS_SEED', None) + +if SEED is None: + # Generate a seed which is fairly short (to ease cut-and-paste, writing it + # down, etc.). Note that Python's random module's seed() function is going + # to take the hash() of this seed, which is a 32-bit value (currently) so + # there is no point in making this seed larger than 32 bits. Make it 30 + # bits, which conveniently fits into six base-32 chars. Include a separator + # because chunking facilitates memory (including working and short-term + # memory) in humans. + chars = "ybndrfg8ejkmcpqxot1uwisza345h769" # Zooko's choice, rationale in "DESIGN" doc in z-base-32 project + SEED = ''.join([random.choice(chars) for x in range(3)] + ['-'] + [random.choice(chars) for x in range(3)]) + +import logging +logging.info("REPEATABLE_RANDOMNESS_SEED: %s\n" % SEED) +logging.info("In order to reproduce this run of the code, set the environment variable \"REPEATABLE_RANDOMNESS_SEED\" to %s before executing.\n" % SEED) +random.seed(SEED) + +def seed_which_refuses(a): + logging.warn("I refuse to reseed to %s -- I already seeded with %s.\n" % (a, SEED,)) + return +random.seed = seed_which_refuses + +from random import randrange + +import unittest + +from pycryptopp.publickey import ecdsa + +def randstr(n, rr=randrange): + return ''.join([chr(rr(0, 256)) for x in xrange(n)]) + +from base64 import b32encode +def ab(x): # debuggery + if len(x) >= 3: + return "%s:%s" % (len(x), b32encode(x[-3:]),) + elif len(x) == 2: + return "%s:%s" % (len(x), b32encode(x[-2:]),) + elif len(x) == 1: + return "%s:%s" % (len(x), b32encode(x[-1:]),) + elif len(x) == 0: + return "%s:%s" % (len(x), "--empty--",) + +def div_ceil(n, d): + """ + The smallest integer k such that k*d >= n. + """ + return (n/d) + (n%d != 0) + +KEYBITS=192 + +# The number of bytes required for a seed to have the same security level as a +# key in this elliptic curve: 2 bits of public key per bit of security. +SEEDBITS=div_ceil(192, 2) +SEEDBYTES=div_ceil(SEEDBITS, 8) + +# The number of bytes required to encode a public key in this elliptic curve. +PUBKEYBYTES=div_ceil(KEYBITS, 8)+1 # 1 byte for the sign of the y component + +# The number of bytes requires to encode a signature in this elliptic curve. +SIGBITS=KEYBITS*2 +SIGBYTES=div_ceil(SIGBITS, 8) + +class Signer(unittest.TestCase): + def test_construct(self): + seed = randstr(SEEDBYTES) + ecdsa.SigningKey(seed) + + def test_sign(self): + seed = randstr(SEEDBYTES) + signer = ecdsa.SigningKey(seed) + sig = signer.sign("message") + self.failUnlessEqual(len(sig), SIGBYTES) + + def test_sign_and_verify(self): + seed = randstr(SEEDBYTES) + signer = ecdsa.SigningKey(seed) + sig = signer.sign("message") + v = signer.get_verifying_key() + self.failUnless(v.verify("message", sig)) + + def test_sign_and_verify_emptymsg(self): + seed = randstr(SEEDBYTES) + signer = ecdsa.SigningKey(seed) + sig = signer.sign("") + v = signer.get_verifying_key() + self.failUnless(v.verify("", sig)) + + def test_construct_from_same_seed_is_reproducible(self): + seed = randstr(SEEDBYTES) + signer1 = ecdsa.SigningKey(seed) + signer2 = ecdsa.SigningKey(seed) + self.failUnlessEqual(signer1.get_verifying_key().serialize(), signer2.get_verifying_key().serialize()) + + # ... and using different seeds constructs a different private key. + seed3 = randstr(SEEDBYTES) + assert seed3 != seed, "Internal error in Python random module's PRNG (or in pycryptopp's hacks to it to facilitate testing) -- got two identical strings from randstr(%s)" % SEEDBYTES + signer3 = ecdsa.SigningKey(seed3) + self.failIfEqual(signer1.get_verifying_key().serialize(), signer3.get_verifying_key().serialize()) + + # Also try the all-zeroes string just because bugs sometimes are + # data-dependent on zero or cause bogus zeroes. + seed4 = '\x00'*SEEDBYTES + assert seed4 != seed, "Internal error in Python random module's PRNG (or in pycryptopp's hacks to it to facilitate testing) -- got the all-zeroes string from randstr(%s)" % SEEDBYTES + signer4 = ecdsa.SigningKey(seed4) + self.failIfEqual(signer4.get_verifying_key().serialize(), signer1.get_verifying_key().serialize()) + + signer5 = ecdsa.SigningKey(seed4) + self.failUnlessEqual(signer5.get_verifying_key().serialize(), signer4.get_verifying_key().serialize()) + + def test_construct_short_seed(self): + try: + ecdsa.SigningKey("\x00\x00\x00") + except ecdsa.Error, le: + self.failUnless("seed is required to be of length " in str(le), le) + else: + self.fail("Should have raised error from seed being too short.") + + def test_construct_bad_arg_type(self): + try: + ecdsa.SigningKey(1) + except TypeError, le: + self.failUnless("must be string" in str(le), le) + else: + self.fail("Should have raised error from seed being of the wrong type.") + +class Verifier(unittest.TestCase): + def test_from_signer_and_serialize_and_deserialize(self): + seed = randstr(SEEDBYTES) + signer = ecdsa.SigningKey(seed) + + verifier = signer.get_verifying_key() + s1 = verifier.serialize() + self.failUnlessEqual(len(s1), PUBKEYBYTES) + ecdsa.VerifyingKey(s1) + s2 = verifier.serialize() + self.failUnlessEqual(s1, s2) + +def flip_one_bit(s): + assert s + i = randrange(0, len(s)) + result = s[:i] + chr(ord(s[i])^(0x01<