~ubuntu-branches/debian/squeeze/pycryptopp/squeeze

« back to all changes in this revision

Viewing changes to pycryptopp/test/test_ecdsa.py

  • Committer: Bazaar Package Importer
  • Author(s): Zooko O'Whielacronx
  • Date: 2009-06-22 22:20:50 UTC
  • Revision ID: james.westby@ubuntu.com-20090622222050-hbqmn50dt2kvoz5o
Tags: upstream-0.5.14
ImportĀ upstreamĀ versionĀ 0.5.14

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
 
 
3
import random
 
4
import base64
 
5
 
 
6
import os
 
7
SEED = os.environ.get('REPEATABLE_RANDOMNESS_SEED', None)
 
8
 
 
9
if SEED is None:
 
10
    # Generate a seed which is fairly short (to ease cut-and-paste, writing it
 
11
    # down, etc.).  Note that Python's random module's seed() function is going
 
12
    # to take the hash() of this seed, which is a 32-bit value (currently) so
 
13
    # there is no point in making this seed larger than 32 bits.  Make it 30
 
14
    # bits, which conveniently fits into six base-32 chars.  Include a separator
 
15
    # because chunking facilitates memory (including working and short-term
 
16
    # memory) in humans.
 
17
    chars = "ybndrfg8ejkmcpqxot1uwisza345h769" # Zooko's choice, rationale in "DESIGN" doc in z-base-32 project
 
18
    SEED = ''.join([random.choice(chars) for x in range(3)] + ['-'] + [random.choice(chars) for x in range(3)])
 
19
 
 
20
import logging
 
21
logging.info("REPEATABLE_RANDOMNESS_SEED: %s\n" % SEED)
 
22
logging.info("In order to reproduce this run of the code, set the environment variable \"REPEATABLE_RANDOMNESS_SEED\" to %s before executing.\n" % SEED)
 
23
random.seed(SEED)
 
24
 
 
25
def seed_which_refuses(a):
 
26
    logging.warn("I refuse to reseed to %s -- I already seeded with %s.\n" % (a, SEED,))
 
27
    return
 
28
random.seed = seed_which_refuses
 
29
 
 
30
from random import randrange
 
31
 
 
32
import unittest
 
33
 
 
34
#from pycryptopp.publickey import ecdsa
 
35
ecdsa = None # silence pyflakes
 
36
 
 
37
def randstr(n, rr=randrange):
 
38
    return ''.join([chr(rr(0, 256)) for x in xrange(n)])
 
39
 
 
40
from base64 import b32encode
 
41
def ab(x): # debuggery
 
42
    if len(x) >= 3:
 
43
        return "%s:%s" % (len(x), b32encode(x[-3:]),)
 
44
    elif len(x) == 2:
 
45
        return "%s:%s" % (len(x), b32encode(x[-2:]),)
 
46
    elif len(x) == 1:
 
47
        return "%s:%s" % (len(x), b32encode(x[-1:]),)
 
48
    elif len(x) == 0:
 
49
        return "%s:%s" % (len(x), "--empty--",)
 
50
 
 
51
def div_ceil(n, d):
 
52
    """
 
53
    The smallest integer k such that k*d >= n.
 
54
    """
 
55
    return (n/d) + (n%d != 0)
 
56
 
 
57
KEYBITS=192
 
58
 
 
59
# The number of bytes required for a seed to have the same security level as a
 
60
# key in this elliptic curve: 2 bits of public key per bit of security.
 
61
SEEDBITS=div_ceil(192, 2)
 
62
SEEDBYTES=div_ceil(SEEDBITS, 8)
 
63
 
 
64
# The number of bytes required to encode a public key in this elliptic curve.
 
65
PUBKEYBYTES=div_ceil(KEYBITS, 8)+1 # 1 byte for the sign of the y component
 
66
 
 
67
# The number of bytes requires to encode a signature in this elliptic curve.
 
68
SIGBITS=KEYBITS*2
 
69
SIGBYTES=div_ceil(SIGBITS, 8)
 
70
 
 
71
class Signer(unittest.TestCase):
 
72
    def disabletest_construct(self):
 
73
        seed = randstr(SEEDBYTES)
 
74
        signer = ecdsa.SigningKey(seed)
 
75
 
 
76
    def disabletest_sign(self):
 
77
        seed = randstr(SEEDBYTES)
 
78
        signer = ecdsa.SigningKey(seed)
 
79
        sig = signer.sign("message")
 
80
        self.failUnlessEqual(len(sig), SIGBYTES)
 
81
 
 
82
    def disabletest_sign_and_verify(self):
 
83
        seed = randstr(SEEDBYTES)
 
84
        signer = ecdsa.SigningKey(seed)
 
85
        sig = signer.sign("message")
 
86
        v = signer.get_verifying_key()
 
87
        self.failUnless(v.verify("message", sig))
 
88
 
 
89
    def disabletest_sign_and_verify_emptymsg(self):
 
90
        seed = randstr(SEEDBYTES)
 
91
        signer = ecdsa.SigningKey(seed)
 
92
        sig = signer.sign("")
 
93
        v = signer.get_verifying_key()
 
94
        self.failUnless(v.verify("", sig))
 
95
 
 
96
    def disabletest_construct_from_same_seed_is_reproducible(self):
 
97
        seed = randstr(SEEDBYTES)
 
98
        signer1 = ecdsa.SigningKey(seed)
 
99
        signer2 = ecdsa.SigningKey(seed)
 
100
        self.failUnlessEqual(signer1.get_verifying_key().serialize(), signer2.get_verifying_key().serialize())
 
101
 
 
102
        # ... and using different seeds constructs a different private key.
 
103
        seed3 = randstr(SEEDBYTES)
 
104
        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
 
105
        signer3 = ecdsa.SigningKey(seed3)
 
106
        self.failIfEqual(signer1.get_verifying_key().serialize(), signer3.get_verifying_key().serialize())
 
107
 
 
108
        # Also try the all-zeroes string just because bugs sometimes are
 
109
        # data-dependent on zero or cause bogus zeroes.
 
110
        seed4 = '\x00'*SEEDBYTES
 
111
        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
 
112
        signer4 = ecdsa.SigningKey(seed4)
 
113
        self.failIfEqual(signer4.get_verifying_key().serialize(), signer1.get_verifying_key().serialize())
 
114
 
 
115
        signer5 = ecdsa.SigningKey(seed4)
 
116
        self.failUnlessEqual(signer5.get_verifying_key().serialize(), signer4.get_verifying_key().serialize())
 
117
 
 
118
    def disabletest_construct_short_seed(self):
 
119
        try:
 
120
            signer = ecdsa.SigningKey("\x00\x00\x00")
 
121
        except ecdsa.Error, le:
 
122
            self.failUnless("seed is required to be of length " in str(le), le)
 
123
        else:
 
124
           self.fail("Should have raised error from seed being too short.")
 
125
 
 
126
    def disabletest_construct_bad_arg_type(self):
 
127
        try:
 
128
            signer = ecdsa.SigningKey(1)
 
129
        except TypeError, le:
 
130
            self.failUnless("must be string" in str(le), le)
 
131
        else:
 
132
           self.fail("Should have raised error from seed being of the wrong type.")
 
133
 
 
134
class Verifier(unittest.TestCase):
 
135
    def disabletest_from_signer_and_serialize_and_deserialize(self):
 
136
        seed = randstr(SEEDBYTES)
 
137
        signer = ecdsa.SigningKey(seed)
 
138
 
 
139
        verifier = signer.get_verifying_key()
 
140
        s1 = verifier.serialize()
 
141
        self.failUnlessEqual(len(s1), PUBKEYBYTES)
 
142
        verifier2 = ecdsa.VerifyingKey(s1)
 
143
        s2 = verifier.serialize()
 
144
        self.failUnlessEqual(s1, s2)
 
145
 
 
146
def flip_one_bit(s):
 
147
    assert s
 
148
    i = randrange(0, len(s))
 
149
    result = s[:i] + chr(ord(s[i])^(0x01<<randrange(0, 8))) + s[i+1:]
 
150
    assert result != s, "Internal error -- flip_one_bit() produced the same string as its input: %s == %s" % (result, s)
 
151
    return result
 
152
 
 
153
def randmsg():
 
154
    # Choose a random message size from a range probably large enough to
 
155
    # exercise any different code paths which depend on the message length.
 
156
    randmsglen = randrange(1, SIGBYTES*2+2)
 
157
    return randstr(randmsglen)
 
158
 
 
159
class SignAndVerify(unittest.TestCase):
 
160
    def _help_test_sign_and_check_good_keys(self, signer, verifier):
 
161
        msg = randmsg()
 
162
 
 
163
        sig = signer.sign(msg)
 
164
        self.failUnlessEqual(len(sig), SIGBYTES)
 
165
        self.failUnless(verifier.verify(msg, sig))
 
166
 
 
167
        # Now flip one bit of the signature and make sure that the signature doesn't check.
 
168
        badsig = flip_one_bit(sig)
 
169
        self.failIf(verifier.verify(msg, badsig))
 
170
 
 
171
        # Now generate a random signature and make sure that the signature doesn't check.
 
172
        badsig = randstr(len(sig))
 
173
        assert badsig != sig, "Internal error -- randstr() produced the same string twice: %s == %s" % (badsig, sig)
 
174
        self.failIf(verifier.verify(msg, badsig))
 
175
 
 
176
        # Now flip one bit of the message and make sure that the original signature doesn't check.
 
177
        badmsg = flip_one_bit(msg)
 
178
        self.failIf(verifier.verify(badmsg, sig))
 
179
 
 
180
        # Now generate a random message and make sure that the original signature doesn't check.
 
181
        badmsg = randstr(len(msg))
 
182
        assert badmsg != msg, "Internal error -- randstr() produced the same string twice: %s == %s" % (badmsg, msg)
 
183
        self.failIf(verifier.verify(badmsg, sig))
 
184
 
 
185
    def _help_test_sign_and_check_bad_keys(self, signer, verifier):
 
186
        """
 
187
        Make sure that this signer/verifier pair cannot produce and verify signatures.
 
188
        """
 
189
        msg = randmsg()
 
190
 
 
191
        sig = signer.sign(msg)
 
192
        self.failUnlessEqual(len(sig), SIGBYTES)
 
193
        self.failIf(verifier.verify(msg, sig))
 
194
 
 
195
    def disabletest(self):
 
196
        seed = randstr(SEEDBYTES)
 
197
        signer = ecdsa.SigningKey(seed)
 
198
        verifier = signer.get_verifying_key()
 
199
        self._help_test_sign_and_check_good_keys(signer, verifier)
 
200
 
 
201
        vstr = verifier.serialize()
 
202
        self.failUnlessEqual(len(vstr), PUBKEYBYTES)
 
203
        verifier2 = ecdsa.VerifyingKey(vstr)
 
204
        self._help_test_sign_and_check_good_keys(signer, verifier2)
 
205
       
 
206
        signer2 = ecdsa.SigningKey(seed)
 
207
        self._help_test_sign_and_check_good_keys(signer2, verifier2)
 
208
         
 
209
        verifier3 = signer2.get_verifying_key()
 
210
        self._help_test_sign_and_check_good_keys(signer, verifier3)
 
211
 
 
212
        # Now test various ways that the keys could be corrupted or ill-matched.
 
213
 
 
214
        # Flip one bit of the public key.
 
215
        badvstr = flip_one_bit(vstr)
 
216
        try:
 
217
            badverifier = ecdsa.VerifyingKey(badvstr)
 
218
        except ecdsa.Error, le:
 
219
            # Ok, fine, the verifying key was corrupted and Crypto++ detected this fact.
 
220
            pass
 
221
        else:
 
222
            self._help_test_sign_and_check_bad_keys(signer, badverifier)
 
223
 
 
224
        # Randomize all bits of the public key.
 
225
        badvstr = randstr(len(vstr))
 
226
        assert badvstr != vstr, "Internal error -- randstr() produced the same string twice: %s == %s" % (badvstr, vstr)
 
227
        try:
 
228
            badverifier = ecdsa.VerifyingKey(badvstr)
 
229
        except ecdsa.Error, le:
 
230
            # Ok, fine, the key was corrupted and Crypto++ detected this fact.
 
231
            pass
 
232
        else:
 
233
            self._help_test_sign_and_check_bad_keys(signer, badverifier)
 
234
        
 
235
        # Flip one bit of the private key.
 
236
        badseed = flip_one_bit(seed)
 
237
        badsigner = ecdsa.SigningKey(badseed)
 
238
        self._help_test_sign_and_check_bad_keys(badsigner, verifier)
 
239
 
 
240
        # Randomize all bits of the private key.
 
241
        badseed = randstr(len(seed))
 
242
        assert badseed != seed, "Internal error -- randstr() produced the same string twice: %s == %s" % (badseed, seed)
 
243
        badsigner = ecdsa.SigningKey(badseed)
 
244
        self._help_test_sign_and_check_bad_keys(badsigner, verifier)
 
245
 
 
246
class Compatibility(unittest.TestCase):
 
247
    def disabletest_compatibility(self):
 
248
        # Confirm that the KDF used by the SigningKey constructor doesn't
 
249
        # change without suitable backwards-compability
 
250
        seed = base64.b32decode('XS27TJRP3JBZKDEFBDKQ====')
 
251
        signer = ecdsa.SigningKey(seed)
 
252
        v1 = signer.get_verifying_key()
 
253
        vs = v1.serialize()
 
254
        vs32 = base64.b32encode(vs)
 
255
        self.failUnlessEqual(vs32, "ANPNDWJWHQXYSQMD4L36D7WQEGXA42MS5JRUFIWA")
 
256
        v2 = ecdsa.VerifyingKey(vs)
 
257
        #print base64.b32encode(signer.sign("message"))
 
258
        sig32 = "EA3Y7A4T62J3K6MUPJQN3WJ5S4SS53EGZXOSTQW7EQ7OXEMS6QJLYL63BLHMHZD7KFT37KEPJBAKI==="
 
259
        sig = base64.b32decode(sig32)
 
260
        self.failUnless(v1.verify("message", sig))
 
261
        self.failUnless(v2.verify("message", sig))
 
262
 
 
263
if __name__ == "__main__":
 
264
    unittest.main()