7
SEED = os.environ.get('REPEATABLE_RANDOMNESS_SEED', 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
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)])
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)
25
def seed_which_refuses(a):
26
logging.warn("I refuse to reseed to %s -- I already seeded with %s.\n" % (a, SEED,))
28
random.seed = seed_which_refuses
30
from random import randrange
34
#from pycryptopp.publickey import ecdsa
35
ecdsa = None # silence pyflakes
37
def randstr(n, rr=randrange):
38
return ''.join([chr(rr(0, 256)) for x in xrange(n)])
40
from base64 import b32encode
41
def ab(x): # debuggery
43
return "%s:%s" % (len(x), b32encode(x[-3:]),)
45
return "%s:%s" % (len(x), b32encode(x[-2:]),)
47
return "%s:%s" % (len(x), b32encode(x[-1:]),)
49
return "%s:%s" % (len(x), "--empty--",)
53
The smallest integer k such that k*d >= n.
55
return (n/d) + (n%d != 0)
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)
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
67
# The number of bytes requires to encode a signature in this elliptic curve.
69
SIGBYTES=div_ceil(SIGBITS, 8)
71
class Signer(unittest.TestCase):
72
def disabletest_construct(self):
73
seed = randstr(SEEDBYTES)
74
signer = ecdsa.SigningKey(seed)
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)
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))
89
def disabletest_sign_and_verify_emptymsg(self):
90
seed = randstr(SEEDBYTES)
91
signer = ecdsa.SigningKey(seed)
93
v = signer.get_verifying_key()
94
self.failUnless(v.verify("", sig))
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())
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())
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())
115
signer5 = ecdsa.SigningKey(seed4)
116
self.failUnlessEqual(signer5.get_verifying_key().serialize(), signer4.get_verifying_key().serialize())
118
def disabletest_construct_short_seed(self):
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)
124
self.fail("Should have raised error from seed being too short.")
126
def disabletest_construct_bad_arg_type(self):
128
signer = ecdsa.SigningKey(1)
129
except TypeError, le:
130
self.failUnless("must be string" in str(le), le)
132
self.fail("Should have raised error from seed being of the wrong type.")
134
class Verifier(unittest.TestCase):
135
def disabletest_from_signer_and_serialize_and_deserialize(self):
136
seed = randstr(SEEDBYTES)
137
signer = ecdsa.SigningKey(seed)
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)
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)
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)
159
class SignAndVerify(unittest.TestCase):
160
def _help_test_sign_and_check_good_keys(self, signer, verifier):
163
sig = signer.sign(msg)
164
self.failUnlessEqual(len(sig), SIGBYTES)
165
self.failUnless(verifier.verify(msg, sig))
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))
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))
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))
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))
185
def _help_test_sign_and_check_bad_keys(self, signer, verifier):
187
Make sure that this signer/verifier pair cannot produce and verify signatures.
191
sig = signer.sign(msg)
192
self.failUnlessEqual(len(sig), SIGBYTES)
193
self.failIf(verifier.verify(msg, sig))
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)
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)
206
signer2 = ecdsa.SigningKey(seed)
207
self._help_test_sign_and_check_good_keys(signer2, verifier2)
209
verifier3 = signer2.get_verifying_key()
210
self._help_test_sign_and_check_good_keys(signer, verifier3)
212
# Now test various ways that the keys could be corrupted or ill-matched.
214
# Flip one bit of the public key.
215
badvstr = flip_one_bit(vstr)
217
badverifier = ecdsa.VerifyingKey(badvstr)
218
except ecdsa.Error, le:
219
# Ok, fine, the verifying key was corrupted and Crypto++ detected this fact.
222
self._help_test_sign_and_check_bad_keys(signer, badverifier)
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)
228
badverifier = ecdsa.VerifyingKey(badvstr)
229
except ecdsa.Error, le:
230
# Ok, fine, the key was corrupted and Crypto++ detected this fact.
233
self._help_test_sign_and_check_bad_keys(signer, badverifier)
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)
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)
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()
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))
263
if __name__ == "__main__":