summaryrefslogtreecommitdiff
path: root/src/pycryptopp/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/pycryptopp/test')
-rw-r--r--src/pycryptopp/test/__init__.py0
-rw-r--r--src/pycryptopp/test/test_aes.py114
-rw-r--r--src/pycryptopp/test/test_ecdsa.py263
-rw-r--r--src/pycryptopp/test/test_ed25519.py148
-rw-r--r--src/pycryptopp/test/test_ed25519_kat.py47
-rw-r--r--src/pycryptopp/test/test_from_Nikratio.py32
-rw-r--r--src/pycryptopp/test/test_rsa.py137
-rw-r--r--src/pycryptopp/test/test_sha256.py181
-rw-r--r--src/pycryptopp/test/test_xsalsa20.py136
9 files changed, 1058 insertions, 0 deletions
diff --git a/src/pycryptopp/test/__init__.py b/src/pycryptopp/test/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/pycryptopp/test/__init__.py
diff --git a/src/pycryptopp/test/test_aes.py b/src/pycryptopp/test/test_aes.py
new file mode 100644
index 0000000..16c3e20
--- /dev/null
+++ b/src/pycryptopp/test/test_aes.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+
+import random, re
+
+import unittest
+
+from binascii import a2b_hex, b2a_hex
+
+global VERBOSE
+VERBOSE=False
+
+from pycryptopp.cipher import aes
+
+from pkg_resources import resource_string, resource_listdir
+
+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 randstr(n):
+ return ''.join(map(chr, map(random.randrange, [0]*n, [256]*n)))
+
+class AES256(unittest.TestCase):
+ enc0 = "dc95c078a2408989ad48a21492842087530f8afbc74536b9a963b4f1c4cb738b"
+
+ def test_encrypt_zeroes(self):
+ cryptor = aes.AES(key="\x00"*32)
+ ct = cryptor.process("\x00"*32)
+ self.failUnlessEqual(self.enc0, b2a_hex(ct))
+
+ def test_init_type_check(self):
+ self.failUnlessRaises(TypeError, aes.AES, None)
+ self.failUnlessRaises(aes.Error, aes.AES, "a"*1) # too short
+ self.failUnlessRaises(aes.Error, aes.AES, "a"*17) # not one of the valid key sizes for AES (16, 24, 32)
+
+ def test_encrypt_zeroes_in_two_parts(self):
+ cryptor = aes.AES(key="\x00"*32)
+ ct1 = cryptor.process("\x00"*15)
+ ct2 = cryptor.process("\x00"*17)
+ self.failUnlessEqual(self.enc0, b2a_hex(ct1+ct2))
+
+class AES128(unittest.TestCase):
+ enc0 = "66e94bd4ef8a2c3b884cfa59ca342b2e"
+
+ def test_encrypt_zeroes(self):
+ cryptor = aes.AES(key="\x00"*16)
+ ct = cryptor.process("\x00"*16)
+ self.failUnlessEqual(self.enc0, b2a_hex(ct))
+
+ def test_init_type_check(self):
+ self.failUnlessRaises(TypeError, aes.AES, None)
+ self.failUnlessRaises(aes.Error, aes.AES, "a") # too short
+
+ def test_encrypt_zeroes_in_two_parts(self):
+ cryptor = aes.AES(key="\x00"*16)
+ ct1 = cryptor.process("\x00"*8)
+ ct2 = cryptor.process("\x00"*8)
+ self.failUnlessEqual(self.enc0, b2a_hex(ct1+ct2))
+
+def fake_ecb_using_ctr(k, p):
+ return aes.AES(key=k, iv=p).process('\x00'*16)
+
+NIST_KAT_VECTS_RE=re.compile("\nCOUNT = ([0-9]+)\nKEY = ([0-9a-f]+)\nPLAINTEXT = ([0-9a-f]+)\nCIPHERTEXT = ([0-9a-f]+)")
+
+class AES_from_NIST_KAT(unittest.TestCase):
+ def test_NIST_KAT(self):
+ for vectname in resource_listdir('pycryptopp', 'testvectors/KAT_AES'):
+ self._test_KAT_file(resource_string('pycryptopp', '/'.join(['testvectors/KAT_AES', vectname])))
+
+ def _test_KAT_file(self, vects_str):
+ for mo in NIST_KAT_VECTS_RE.finditer(vects_str):
+ key = a2b_hex(mo.group(2))
+ plaintext = a2b_hex(mo.group(3))
+ ciphertext = a2b_hex(mo.group(4))
+
+ computedciphertext = fake_ecb_using_ctr(key, plaintext)
+ self.failUnlessEqual(computedciphertext, ciphertext, "computedciphertext: %s, ciphertext: %s, key: %s, plaintext: %s" % (b2a_hex(computedciphertext), b2a_hex(ciphertext), b2a_hex(key), b2a_hex(plaintext)))
+
+class AES_from_Niels_Ferguson(unittest.TestCase):
+ # http://blogs.msdn.com/si_team/archive/2006/05/19/aes-test-vectors.aspx
+ def _test_from_Niels_AES(self, keysize, result):
+ E = fake_ecb_using_ctr
+ b = 16
+ k = keysize
+ S = '\x00' * (k+b)
+ for i in range(1000):
+ K = S[-k:]
+ P = S[-k-b:-k]
+ S += E(K, E(K, P))
+
+ self.failUnlessEqual(S[-b:], a2b_hex(result))
+
+ def test_from_Niels_AES128(self):
+ return self._test_from_Niels_AES(16, 'bd883f01035e58f42f9d812f2dacbcd8')
+
+ def test_from_Niels_AES256(self):
+ return self._test_from_Niels_AES(32, 'c84b0f3a2c76dd9871900b07f09bdd3e')
+
+class PartialIV(unittest.TestCase):
+ def test_partial(self):
+ k = "k"*16
+ for iv_len in range(0, 16)+range(17,70): # all are wrong, 16 is right
+ self.failUnlessRaises(aes.Error,
+ aes.AES, k, iv="i"*iv_len)
+
+if __name__ == "__main__":
+ unittest.main()
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<<randrange(0, 8))) + s[i+1:]
+ assert result != s, "Internal error -- flip_one_bit() produced the same string as its input: %s == %s" % (result, s)
+ return result
+
+def randmsg():
+ # Choose a random message size from a range probably large enough to
+ # exercise any different code paths which depend on the message length.
+ randmsglen = randrange(1, SIGBYTES*2+2)
+ return randstr(randmsglen)
+
+class SignAndVerify(unittest.TestCase):
+ def _help_test_sign_and_check_good_keys(self, signer, verifier):
+ msg = randmsg()
+
+ sig = signer.sign(msg)
+ self.failUnlessEqual(len(sig), SIGBYTES)
+ self.failUnless(verifier.verify(msg, sig))
+
+ # Now flip one bit of the signature and make sure that the signature doesn't check.
+ badsig = flip_one_bit(sig)
+ self.failIf(verifier.verify(msg, badsig))
+
+ # Now generate a random signature and make sure that the signature doesn't check.
+ badsig = randstr(len(sig))
+ assert badsig != sig, "Internal error -- randstr() produced the same string twice: %s == %s" % (badsig, sig)
+ self.failIf(verifier.verify(msg, badsig))
+
+ # Now flip one bit of the message and make sure that the original signature doesn't check.
+ badmsg = flip_one_bit(msg)
+ self.failIf(verifier.verify(badmsg, sig))
+
+ # Now generate a random message and make sure that the original signature doesn't check.
+ badmsg = randstr(len(msg))
+ assert badmsg != msg, "Internal error -- randstr() produced the same string twice: %s == %s" % (badmsg, msg)
+ self.failIf(verifier.verify(badmsg, sig))
+
+ def _help_test_sign_and_check_bad_keys(self, signer, verifier):
+ """
+ Make sure that this signer/verifier pair cannot produce and verify signatures.
+ """
+ msg = randmsg()
+
+ sig = signer.sign(msg)
+ self.failUnlessEqual(len(sig), SIGBYTES)
+ self.failIf(verifier.verify(msg, sig))
+
+ def test(self):
+ seed = randstr(SEEDBYTES)
+ signer = ecdsa.SigningKey(seed)
+ verifier = signer.get_verifying_key()
+ self._help_test_sign_and_check_good_keys(signer, verifier)
+
+ vstr = verifier.serialize()
+ self.failUnlessEqual(len(vstr), PUBKEYBYTES)
+ verifier2 = ecdsa.VerifyingKey(vstr)
+ self._help_test_sign_and_check_good_keys(signer, verifier2)
+
+ signer2 = ecdsa.SigningKey(seed)
+ self._help_test_sign_and_check_good_keys(signer2, verifier2)
+
+ verifier3 = signer2.get_verifying_key()
+ self._help_test_sign_and_check_good_keys(signer, verifier3)
+
+ # Now test various ways that the keys could be corrupted or ill-matched.
+
+ # Flip one bit of the public key.
+ badvstr = flip_one_bit(vstr)
+ try:
+ badverifier = ecdsa.VerifyingKey(badvstr)
+ except ecdsa.Error:
+ # Ok, fine, the verifying key was corrupted and Crypto++ detected this fact.
+ pass
+ else:
+ self._help_test_sign_and_check_bad_keys(signer, badverifier)
+
+ # Randomize all bits of the public key.
+ badvstr = randstr(len(vstr))
+ assert badvstr != vstr, "Internal error -- randstr() produced the same string twice: %s == %s" % (badvstr, vstr)
+ try:
+ badverifier = ecdsa.VerifyingKey(badvstr)
+ except ecdsa.Error:
+ # Ok, fine, the key was corrupted and Crypto++ detected this fact.
+ pass
+ else:
+ self._help_test_sign_and_check_bad_keys(signer, badverifier)
+
+ # Flip one bit of the private key.
+ badseed = flip_one_bit(seed)
+ badsigner = ecdsa.SigningKey(badseed)
+ self._help_test_sign_and_check_bad_keys(badsigner, verifier)
+
+ # Randomize all bits of the private key.
+ badseed = randstr(len(seed))
+ assert badseed != seed, "Internal error -- randstr() produced the same string twice: %s == %s" % (badseed, seed)
+ badsigner = ecdsa.SigningKey(badseed)
+ self._help_test_sign_and_check_bad_keys(badsigner, verifier)
+
+class Compatibility(unittest.TestCase):
+ def test_compatibility(self):
+ # Confirm that the KDF used by the SigningKey constructor doesn't
+ # change without suitable backwards-compability
+ seed = base64.b32decode('XS27TJRP3JBZKDEFBDKQ====')
+ signer = ecdsa.SigningKey(seed)
+ v1 = signer.get_verifying_key()
+ vs = v1.serialize()
+ vs32 = base64.b32encode(vs)
+ self.failUnlessEqual(vs32, "ANPNDWJWHQXYSQMD4L36D7WQEGXA42MS5JRUFIWA")
+ v2 = ecdsa.VerifyingKey(vs)
+ #print base64.b32encode(signer.sign("message"))
+ sig32 = "EA3Y7A4T62J3K6MUPJQN3WJ5S4SS53EGZXOSTQW7EQ7OXEMS6QJLYL63BLHMHZD7KFT37KEPJBAKI==="
+ sig = base64.b32decode(sig32)
+ self.failUnless(v1.verify("message", sig))
+ self.failUnless(v2.verify("message", sig))
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/pycryptopp/test/test_ed25519.py b/src/pycryptopp/test/test_ed25519.py
new file mode 100644
index 0000000..6e530ab
--- /dev/null
+++ b/src/pycryptopp/test/test_ed25519.py
@@ -0,0 +1,148 @@
+
+import unittest
+import time
+from binascii import hexlify, unhexlify
+from pycryptopp.publickey import ed25519
+from pycryptopp.publickey.ed25519 import _ed25519 as raw
+
+def flip_bit(s, bit=0, in_byte=-1):
+ as_bytes = [ord(b) for b in s]
+ as_bytes[in_byte] = as_bytes[in_byte] ^ (0x01<<bit)
+ return "".join([chr(b) for b in as_bytes])
+
+# the pure-python demonstration code (on my 2010 MacBookPro) takes 5s to
+# generate a public key, 9s to sign, 14s to verify
+
+# the SUPERCOP-ref version we use takes 2ms for keygen, 2ms to sign, and 7ms
+# to verify
+
+class Basic(unittest.TestCase):
+ timer = None
+ def log(self, msg):
+ return
+ now = time.time()
+ if self.timer is None:
+ self.timer = now
+ else:
+ elapsed = now - self.timer
+ self.timer = now
+ print " (%f elapsed)" % elapsed
+ print msg
+
+ def test_version(self):
+ # just make sure it can be retrieved
+ ver = ed25519.__version__
+ self.failUnless(isinstance(ver, type("")))
+
+ def test_constants(self):
+ # the secret key we get from raw.keypair() are 64 bytes long, and
+ # are mostly the output of a sha512 call. The first 32 bytes are the
+ # private exponent (random, with a few bits stomped).
+ self.failUnlessEqual(raw.SECRETKEYBYTES, 64)
+ # the public key is the encoded public point
+ self.failUnlessEqual(raw.PUBLICKEYBYTES, 32)
+ self.failUnlessEqual(raw.SIGNATUREKEYBYTES, 64)
+
+ def test_raw(self):
+ sk_s = "\x00" * 32 # usually urandom(32)
+ vk_s, skvk_s = raw.publickey(sk_s)
+ self.failUnlessEqual(len(vk_s), 32)
+ exp_vks = unhexlify("3b6a27bcceb6a42d62a3a8d02a6f0d73"
+ "653215771de243a63ac048a18b59da29")
+ self.failUnlessEqual(vk_s, exp_vks)
+ self.failUnlessEqual(skvk_s[:32], sk_s)
+ self.failUnlessEqual(skvk_s[32:], vk_s)
+ msg = "hello world"
+ msg_and_sig = raw.sign(msg, skvk_s)
+ sig = msg_and_sig[:-len(msg)]
+ self.failUnlessEqual(len(sig), 64)
+ exp_sig = unhexlify("b0b47780f096ae60bfff8d8e7b19c36b"
+ "321ae6e69cca972f2ff987ef30f20d29"
+ "774b53bae404485c4391ddf1b3f37aaa"
+ "8a9747f984eb0884e8aa533386e73305")
+ self.failUnlessEqual(sig, exp_sig)
+ ret = raw.open(sig+msg, vk_s) # don't raise exception
+ self.failUnlessEqual(ret, msg)
+ self.failUnlessRaises(raw.BadSignatureError,
+ raw.open,
+ sig+msg+".. NOT!", vk_s)
+ self.failUnlessRaises(raw.BadSignatureError,
+ raw.open,
+ sig+flip_bit(msg), vk_s)
+ self.failUnlessRaises(raw.BadSignatureError,
+ raw.open,
+ sig+msg, flip_bit(vk_s))
+ self.failUnlessRaises(raw.BadSignatureError,
+ raw.open,
+ sig+msg, flip_bit(vk_s, in_byte=2))
+ self.failUnlessRaises(raw.BadSignatureError,
+ raw.open,
+ flip_bit(sig)+msg, vk_s)
+ self.failUnlessRaises(raw.BadSignatureError,
+ raw.open,
+ flip_bit(sig, in_byte=33)+msg, vk_s)
+
+
+ def test_publickey(self):
+ sk_bytes = unhexlify("4ba96b0b5303328c7405220598a587c4"
+ "acb06ed9a9601d149f85400195f1ec3d")
+ sk = ed25519.SigningKey(sk_bytes)
+ self.failUnlessRaises(ValueError, ed25519.SigningKey, "wrong length")
+
+ vk_bytes = sk.get_verifying_key_bytes()
+ self.failUnlessEqual(hexlify(vk_bytes),
+ "a66d161e090652b054740748f059f92a"
+ "5b731f1c27b05571f6d942e4f8b7b264")
+
+ ed25519.VerifyingKey(vk_bytes)
+ self.failUnlessRaises(ValueError, ed25519.VerifyingKey, "wrong length")
+
+ def test_OOP(self):
+ sk_bytes = unhexlify("4ba96b0b5303328c7405220598a587c4"
+ "acb06ed9a9601d149f85400195f1ec3d")
+ sk = ed25519.SigningKey(sk_bytes)
+
+ self.failUnlessEqual(hexlify(sk.get_verifying_key_bytes()),
+ "a66d161e090652b054740748f059f92a"
+ "5b731f1c27b05571f6d942e4f8b7b264")
+ vk = ed25519.VerifyingKey(sk.get_verifying_key_bytes())
+
+ msg = "hello world"
+ sig = sk.sign(msg)
+ self.failUnlessEqual(len(sig), 64)
+ self.failUnlessEqual(hexlify(sig),
+ "6eaffe94f2972b35158b6aaa9b69c1da"
+ "97f0896aca29c41b1dd7b32e6c9e2ff6"
+ "76fc8d8b034709cdcc37d8aeb86bebfb"
+ "173ace3c319e211ea1d7e8d8884c1808")
+ self.failUnlessEqual(vk.verify(sig, msg), None) # also, don't throw
+ self.failUnlessRaises(ed25519.BadSignatureError,
+ vk.verify, sig, msg+".. NOT!")
+
+ def test_object_identity(self):
+ sk1_bytes = unhexlify("ef32972ae3f1252a5aa1395347ea008c"
+ "bd2fed0773a4ea45e2d2d06c8cf8fbd4")
+ sk2_bytes = unhexlify("3d550c158900b4c2922b6656d2f80572"
+ "89de4ee65043745179685ae7d29b944d")
+ sk1a = ed25519.SigningKey(sk1_bytes)
+ sk1b = ed25519.SigningKey(sk1_bytes)
+ sk2 = ed25519.SigningKey(sk2_bytes)
+ self.failUnlessEqual(sk1a, sk1b)
+ self.failIfEqual(sk1a, sk2)
+
+ vk1_bytes = sk1a.get_verifying_key_bytes()
+ self.failUnlessEqual(vk1_bytes, sk1b.get_verifying_key_bytes())
+ vk2_bytes = sk2.get_verifying_key_bytes()
+ vk1a = ed25519.VerifyingKey(vk1_bytes)
+ vk1b = ed25519.VerifyingKey(vk1_bytes)
+ vk2 = ed25519.VerifyingKey(vk2_bytes)
+ self.failUnlessEqual(vk1a, vk1b)
+ self.failIfEqual(vk1a, vk2)
+
+ # exercise compare-against-other-type
+ self.failIfEqual(sk2, "not a SigningKey")
+ self.failIfEqual(vk2, "not a VerifyingKey")
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/pycryptopp/test/test_ed25519_kat.py b/src/pycryptopp/test/test_ed25519_kat.py
new file mode 100644
index 0000000..83e38be
--- /dev/null
+++ b/src/pycryptopp/test/test_ed25519_kat.py
@@ -0,0 +1,47 @@
+import unittest
+from pkg_resources import resource_string
+from binascii import hexlify, unhexlify
+from pycryptopp.publickey import ed25519
+
+class KnownAnswerTests(unittest.TestCase):
+ def test_short(self):
+ # kat-ed25519.txt comes from "sign.input" on ed25519.cr.yp.to . The
+ # pure-python ed25519.py in the same distribution uses a very
+ # different key format than the one used by NaCl.
+ shortkat = resource_string('pycryptopp',
+ 'testvectors/kat-ed25519-short.txt')
+ for i,line in enumerate(shortkat.splitlines()):
+ x = line.split(":")
+ A,B,C,D = [unhexlify(i) for i in x[:4]]
+ # A[:32] is the 32 byte seed (the entropy input to H())
+ # A[32:] == B == the public point (pubkey)
+ # C is the message
+ # D is 64 bytes of signature (R+S) prepended to the message
+
+ seed = A[:32]
+ vk_s = B
+ # the NaCl signature is R+S, which happens to be the same as ours
+ msg = C
+ sig = D[:64]
+ # note that R depends only upon the second half of H(seed). S
+ # depends upon both the first half (the exponent) and the second
+ # half
+
+ #if len(msg) % 16 == 1:
+ # print "msg len = %d" % len(msg), time.time()
+
+ sk = ed25519.SigningKey(seed)
+ vkbs = sk.get_verifying_key_bytes()
+ self.failUnlessEqual(vkbs, vk_s)
+ vk = ed25519.VerifyingKey(vkbs)
+ vk2 = ed25519.VerifyingKey(vk_s)
+ self.failUnlessEqual(vk2, vk) # objects should compare equal
+ newsig = sk.sign(msg)
+ sig_R,sig_S = sig[:32],sig[32:]
+ newsig_R,newsig_S = newsig[:32],newsig[32:]
+ self.failUnlessEqual(hexlify(newsig), hexlify(sig)) # deterministic sigs
+ self.failUnlessEqual(vk.verify(sig, msg), None) # no exception
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/src/pycryptopp/test/test_from_Nikratio.py b/src/pycryptopp/test/test_from_Nikratio.py
new file mode 100644
index 0000000..a991415
--- /dev/null
+++ b/src/pycryptopp/test/test_from_Nikratio.py
@@ -0,0 +1,32 @@
+import unittest
+
+# This was reported as triggering a "Use of uninitialised value of
+# size 4" under valgrind by Nikratio in pycryptopp-0.5.17 and Crypto++
+# 5.6.0. See http://tahoe-lafs.org/trac/pycryptopp/ticket/67
+
+class T(unittest.TestCase):
+ def test_t(self):
+ import hmac
+ import pycryptopp
+ try:
+ import hashlib
+ except ImportError:
+ # Oh nevermind.
+ return
+ import struct
+
+ def encrypt(buf, passphrase, nonce):
+
+ key = hashlib.sha256(passphrase + nonce).digest()
+ cipher = pycryptopp.cipher.aes.AES(key)
+ hmac_ = hmac.new(key, digestmod=hashlib.sha256)
+
+ hmac_.update(buf)
+ buf = cipher.process(buf)
+ hash_ = cipher.process(hmac_.digest())
+
+ return ''.join(
+ (struct.pack('<B', len(nonce)),
+ nonce, hash_, buf))
+
+ encrypt('foobar', 'passphrase', 'nonce')
diff --git a/src/pycryptopp/test/test_rsa.py b/src/pycryptopp/test/test_rsa.py
new file mode 100644
index 0000000..15294bc
--- /dev/null
+++ b/src/pycryptopp/test/test_rsa.py
@@ -0,0 +1,137 @@
+#!/usr/bin/env python
+
+import random
+
+import unittest
+
+global VERBOSE
+VERBOSE=False
+
+from pycryptopp.publickey import rsa
+
+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 randstr(n):
+ return ''.join(map(chr, map(random.randrange, [0]*n, [256]*n)))
+
+KEYSIZE=522 # 522 bits is far too few for actual security -- it is used only for faster unit tests
+
+class Signer(unittest.TestCase):
+ def test_generate_bad_size(self):
+ try:
+ rsa.generate(KEYSIZE-1)
+ except rsa.Error, le:
+ self.failUnless("size in bits is required to be >=" in str(le), le)
+ else:
+ self.fail("Should have raised error from size being too small.")
+ try:
+ rsa.generate(sizeinbits=KEYSIZE-1)
+ except rsa.Error, le:
+ self.failUnless("size in bits is required to be >=" in str(le), le)
+ else:
+ self.fail("Should have raised error from size being too small.")
+
+ def test_generate(self):
+ rsa.generate(KEYSIZE)
+ # Hooray! It didn't raise an exception! We win!
+ rsa.generate(sizeinbits=KEYSIZE)
+ # Hooray! It didn't raise an exception! We win!
+
+ def test_sign(self):
+ signer = rsa.generate(KEYSIZE)
+ result = signer.sign("abc")
+ self.failUnlessEqual(len(result), ((KEYSIZE+7)/8))
+ # TODO: test against RSAInc. test vectors.
+
+ def test_create_from_string_invalid(self):
+ try:
+ rsa.create_signing_key_from_string("invalid string")
+ except rsa.Error, le:
+ self.failUnless("decode error" in str(le), le)
+ else:
+ self.fail("Should have raised error from invalid string")
+
+ try:
+ rsa.create_verifying_key_from_string("invalid string")
+ except rsa.Error, le:
+ self.failUnless("decode error" in str(le), le)
+ else:
+ self.fail("Should have raised error from invalid string")
+
+class SignAndVerify(unittest.TestCase):
+ def _help_test_sign_and_check(self, signer, verifier, msg):
+ sig = signer.sign(msg)
+ self.failUnlessEqual(len(sig), ((KEYSIZE+7)/8))
+ self.failUnless(verifier.verify(msg, sig))
+
+ def test_sign_and_check_a(self):
+ signer = rsa.generate(KEYSIZE)
+ verifier = signer.get_verifying_key()
+ return self._help_test_sign_and_check(signer, verifier, "a")
+
+ def _help_test_sign_and_check_random(self, signer, verifier):
+ for i in range(3):
+ l = random.randrange(0, 2**10)
+ msg = randstr(l)
+ self._help_test_sign_and_check(signer, verifier, msg)
+
+ def test_sign_and_check_random(self):
+ signer = rsa.generate(KEYSIZE)
+ verifier = signer.get_verifying_key()
+ return self._help_test_sign_and_check_random(signer, verifier)
+
+ def _help_test_sign_and_failcheck(self, signer, verifier, msg):
+ sig = signer.sign("a")
+ sig = sig[:-1] + chr(ord(sig[-1])^0x01)
+ self.failUnless(not verifier.verify(msg, sig))
+
+ def test_sign_and_failcheck_a(self):
+ signer = rsa.generate(KEYSIZE)
+ verifier = signer.get_verifying_key()
+ return self._help_test_sign_and_failcheck(signer, verifier, "a")
+
+ def _help_test_sign_and_failcheck_random(self, signer, verifier):
+ for i in range(3):
+ l = random.randrange(0, 2**10)
+ msg = randstr(l)
+ self._help_test_sign_and_failcheck(signer, verifier, msg)
+
+ def test_sign_and_failcheck_random(self):
+ signer = rsa.generate(KEYSIZE)
+ verifier = signer.get_verifying_key()
+ return self._help_test_sign_and_failcheck_random(signer, verifier)
+
+ def test_serialize_and_deserialize_verifying_key_and_test(self):
+ signer = rsa.generate(KEYSIZE)
+ verifier = signer.get_verifying_key()
+ serstr = verifier.serialize()
+ verifier = None
+ newverifier = rsa.create_verifying_key_from_string(serstr)
+ self._help_test_sign_and_check(signer, newverifier, "a")
+ self._help_test_sign_and_check_random(signer, newverifier)
+ self._help_test_sign_and_failcheck(signer, newverifier, "a")
+ self._help_test_sign_and_failcheck_random(signer, newverifier)
+
+ def test_serialize_and_deserialize_signing_key_and_test(self):
+ signer = rsa.generate(KEYSIZE)
+ verifier = signer.get_verifying_key()
+ serstr = signer.serialize()
+ signer = None
+ newsigner = rsa.create_signing_key_from_string(serstr)
+ self._help_test_sign_and_check(newsigner, verifier, "a")
+ self._help_test_sign_and_check_random(newsigner, verifier)
+ self._help_test_sign_and_failcheck(newsigner, verifier, "a")
+ self._help_test_sign_and_failcheck_random(newsigner, verifier)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/src/pycryptopp/test/test_sha256.py b/src/pycryptopp/test/test_sha256.py
new file mode 100644
index 0000000..5e982dc
--- /dev/null
+++ b/src/pycryptopp/test/test_sha256.py
@@ -0,0 +1,181 @@
+#!/usr/bin/env python
+
+import random, re
+
+import unittest
+
+from binascii import b2a_hex, a2b_hex
+
+global VERBOSE
+VERBOSE=False
+
+from pycryptopp.hash import sha256
+
+from pkg_resources import resource_string
+
+def resource_string_lines(pkgname, resname):
+ return split_on_newlines(resource_string(pkgname, resname))
+
+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 randstr(n):
+ return ''.join(map(chr, map(random.randrange, [0]*n, [256]*n)))
+
+h0 = a2b_hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
+h_bd = a2b_hex("68325720aabd7c82f30f554b313d0570c95accbb7dc4b5aae11204c08ffe732b")
+h_5fd4 = a2b_hex("7c4fbf484498d21b487b9d61de8914b2eadaf2698712936d47c3ada2558f6788")
+
+class SHA256(unittest.TestCase):
+ def test_digest(self):
+ empty_digest = sha256.SHA256().digest()
+ self.failUnless(isinstance(empty_digest, str))
+ self.failUnlessEqual(len(empty_digest), 32)
+ self.failUnlessEqual(empty_digest, h0)
+
+ def test_hexdigest(self):
+ empty_hexdigest = sha256.SHA256().hexdigest()
+ self.failUnlessEqual(a2b_hex(empty_hexdigest), h0)
+ test_hexdigest.todo = "Not yet implemented: SHA256.hexdigest()."
+
+ def test_onebyte_1(self):
+ d = sha256.SHA256("\xbd").digest()
+ self.failUnlessEqual(d, h_bd)
+
+ def test_onebyte_2(self):
+ s = sha256.SHA256()
+ s.update("\xbd")
+ d = s.digest()
+ self.failUnlessEqual(d, h_bd)
+
+ def test_update(self):
+ s = sha256.SHA256("\x5f")
+ s.update("\xd4")
+ d = s.digest()
+ self.failUnlessEqual(d, h_5fd4)
+
+ def test_constructor_type_check(self):
+ self.failUnlessRaises(TypeError, sha256.SHA256, None)
+
+ def test_update_type_check(self):
+ h = sha256.SHA256()
+ self.failUnlessRaises(TypeError, h.update, None)
+
+ def test_digest_twice(self):
+ h = sha256.SHA256()
+ d1 = h.digest()
+ self.failUnless(isinstance(d1, str))
+ d2 = h.digest()
+ self.failUnlessEqual(d1, d2)
+
+ def test_digest_then_update_fail(self):
+ h = sha256.SHA256()
+ h.digest()
+ try:
+ h.update("oops")
+ except sha256.Error, le:
+ self.failUnless("digest() has been called" in str(le), le)
+
+ def test_chunksize(self):
+ # hashes can be computed on arbitrarily-sized chunks
+ problems = False
+ for length in range(2, 140):
+ s = "a"*length
+ expected = sha256.SHA256(s).hexdigest()
+ for a in range(0, length):
+ h = sha256.SHA256()
+ h.update(s[:a])
+ h.update(s[a:])
+ got = h.hexdigest()
+ if got != expected:
+ problems = True
+ print len(s[:a]), len(s[a:]), len(s), got, expected
+ self.failIf(problems)
+
+ def test_recursive_different_chunksizes(self):
+ """
+ Test that updating a hasher with various sized inputs yields
+ the expected answer. This is somewhat redundant with
+ test_chunksize(), but that's okay. This one exercises some
+ slightly different situations (such as finalizing a hash after
+ different length inputs.) This one is recursive so that there
+ is a single fixed result that we expect.
+ """
+ hx = sha256.SHA256()
+ s = ''.join([ chr(c) for c in range(65) ])
+ for i in range(0, 65):
+ hy = sha256.SHA256(s[:i]).digest()
+ hx.update(hy)
+ for i in range(0, 65):
+ hx.update(chr(0xFE))
+ hx.update(s[:64])
+ self.failUnlessEqual(hx.hexdigest().lower(), '5191c7841dd4e16aa454d40af924585dffc67157ffdbfd0236acddd07901629d')
+
+
+VECTS_RE=re.compile("\nLen = ([0-9]+)\nMsg = ([0-9a-f]+)\nMD = ([0-9a-f]+)")
+
+# split_on_newlines() copied from pyutil.strutil
+def split_on_newlines(s):
+ """
+ Splits s on all of the three newline sequences: "\r\n", "\r", or "\n".
+ """
+ res = []
+ for x in s.split('\r\n'):
+ for y in x.split('\r'):
+ res.extend(y.split('\n'))
+ return res
+
+class SHSVectors(unittest.TestCase):
+ """
+ All of the SHA-256 test vectors from the NIST SHS, in the files distributed
+ by NIST. (NIST distributes them in a .zip, but we expect them to be
+ unpacked and in a subdirectory named 'testvectors').
+ """
+ def test_short(self):
+ return self._test_vect(resource_string('pycryptopp', 'testvectors/SHA256ShortMsg.txt'))
+
+ def test_long(self):
+ return self._test_vect(resource_string('pycryptopp', 'testvectors/SHA256LongMsg.txt'))
+
+ def _test_vect(self, vects_str):
+ for mo in VECTS_RE.finditer(vects_str):
+ msglenbits = int(mo.group(1))
+ assert msglenbits % 8 == 0
+ msglen = msglenbits / 8
+ msg = a2b_hex(mo.group(2))[:msglen] # The slice is necessary because NIST seems to think that "00" is a reasonable representation for the zero-length string.
+ assert len(msg) == msglen, (len(msg), msglen)
+ md = a2b_hex(mo.group(3))
+
+ computed_md = sha256.SHA256(msg).digest()
+ self.failUnlessEqual(computed_md, md)
+
+ def test_monte(self):
+ inlines = resource_string_lines('pycryptopp', 'testvectors/SHA256Monte.txt')
+ for line in inlines:
+ line = line.strip()
+ if line[:7] == 'Seed = ':
+ seed = a2b_hex(line[7:])
+ break
+
+ j = 0
+ for line in inlines:
+ line = line.strip()
+ if line[:8] == 'COUNT = ':
+ assert int(line[8:]) == j
+ elif line[:5] == 'MD = ':
+ mds = []
+ mds.append(seed);mds.append(seed);mds.append(seed);
+ for i in range(1000):
+ m = mds[-3]+mds[-2]+mds[-1]
+ mds.append(sha256.SHA256(m).digest())
+ seed = mds[-1]
+ self.failUnlessEqual(line[5:], b2a_hex(seed))
+ j += 1
diff --git a/src/pycryptopp/test/test_xsalsa20.py b/src/pycryptopp/test/test_xsalsa20.py
new file mode 100644
index 0000000..f3ee02a
--- /dev/null
+++ b/src/pycryptopp/test/test_xsalsa20.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+
+import random, re
+import unittest
+
+from binascii import a2b_hex, b2a_hex
+from pkg_resources import resource_string
+
+from pycryptopp.cipher import xsalsa20
+TEST_XSALSA_RE=re.compile("\nCOUNT=([0-9]+)\nKEY=([0-9a-f]+)\nIV=([0-9a-f]+)\nPLAINTEXT=([0-9a-f]+)\nCIPHERTEXT=([0-9a-f]+)")
+
+class XSalsa20Test(unittest.TestCase):
+
+ enc0="eea6a7251c1e72916d11c2cb214d3c252539121d8e234e652d651fa4c8cff880309e645a74e9e0a60d8243acd9177ab51a1beb8d5a2f5d700c093c5e5585579625337bd3ab619d615760d8c5b224a85b1d0efe0eb8a7ee163abb0376529fcc09bab506c618e13ce777d82c3ae9d1a6f972d4160287cbfe60bf2130fc0a6ff6049d0a5c8a82f429231f0080"
+
+ def test_zero_XSalsa20(self):
+ key="1b27556473e985d462cd51197a9a46c76009549eac6474f206c4ee0844f68389"
+ iv="69696ee955b62b73cd62bda875fc73d68219e0036b7a0b37"
+ computedcipher=xsalsa20.XSalsa20(a2b_hex(key),a2b_hex(iv)).process('\x00'*139)
+ self.failUnlessEqual(a2b_hex(self.enc0), computedcipher, "enc0: %s, computedciper: %s" % (self.enc0, b2a_hex(computedcipher)))
+
+ cryptor=xsalsa20.XSalsa20(a2b_hex(key),a2b_hex(iv))
+
+ computedcipher1=cryptor.process('\x00'*69)
+ computedcipher2=cryptor.process('\x00'*69)
+ computedcipher3=cryptor.process('\x00')
+ computedcipher12=b2a_hex(computedcipher1)+b2a_hex(computedcipher2)+b2a_hex(computedcipher3)
+ self.failUnlessEqual(self.enc0, computedcipher12)
+
+
+ def test_XSalsa(self):
+ # The test vector is from Crypto++'s TestVectors/salsa.txt, comment
+ # there is: Source: created by Wei Dai using naclcrypto-20090308 .
+ # naclcrypto being DJB's crypto library and of course DJB designed
+ # XSalsa20
+ s = resource_string("pycryptopp", "testvectors/xsalsa20.txt")
+ return self._test_XSalsa(s)
+
+ def _test_XSalsa(self, vects_str):
+ for mo in TEST_XSALSA_RE.finditer(vects_str):
+ #count = int(mo.group(1))
+ key = a2b_hex(mo.group(2))
+ iv = a2b_hex(mo.group(3))
+ #plaintext = a2b_hex(mo.group(4))
+ #ciphertext= a2b_hex(mo.group(5))
+ plaintext = mo.group(4)
+ ciphertext = mo.group(5)
+ computedcipher=xsalsa20.XSalsa20(key,iv).process(a2b_hex(plaintext))
+ #print "ciphertext", b2a_hex(computedcipher), '\n'
+ #print "computedtext", ciphertext, '\n'
+ #print count, ": \n"
+ self.failUnlessEqual(computedcipher,a2b_hex(ciphertext),"computedcipher: %s, ciphertext: %s" % (b2a_hex(computedcipher), ciphertext))
+
+ #the random decomposing
+ plaintext1 = ""
+ plaintext2 = ""
+ length = len(plaintext)
+ rccipher = ""
+ cryptor = xsalsa20.XSalsa20(key,iv)
+ if length > 2:
+ point = random.randint(0,length-3)
+ if (point%2) !=0:
+ point -= 1
+ plaintext1 += plaintext[:point+2]
+ plaintext2 += plaintext[point+2:]
+ rccipher += b2a_hex(cryptor.process(a2b_hex(plaintext1)))
+ rccipher += b2a_hex(cryptor.process(a2b_hex(plaintext2)))
+ self.failUnlessEqual(rccipher, ciphertext, "random computed cipher: %s, ciphertext: %s" % (rccipher, ciphertext))
+
+ #every byte encrypted
+ cryptor = xsalsa20.XSalsa20(key,iv)
+ eccipher=""
+ l = 0
+ while l<=(length-2):
+ eccipher += b2a_hex(cryptor.process(a2b_hex(plaintext[l:l+2])))
+ l += 2
+ self.failUnlessEqual(eccipher, ciphertext, "every byte computed cipher: %s, ciphertext: %s" % (eccipher, ciphertext))
+
+
+ def test_types_and_lengths(self):
+ # the key= argument must be a bytestring exactly 32 bytes long
+ self.failUnlessRaises(TypeError, xsalsa20.XSalsa20, None)
+ for i in range(70):
+ key = "a"*i
+ if i != 32:
+ self.failUnlessRaises(xsalsa20.Error, xsalsa20.XSalsa20, key)
+ else:
+ self.failUnless(xsalsa20.XSalsa20(key))
+
+ # likewise, iv= (if provided) must be exactly 24 bytes long. Passing
+ # None is not treated the same as not passing the argument at all.
+ key = "a"*32
+ self.failUnlessRaises(TypeError, xsalsa20.XSalsa20, key, None)
+ for i in range(70):
+ iv = "i"*i
+ if i != 24:
+ self.failUnlessRaises(xsalsa20.Error, xsalsa20.XSalsa20, key, iv)
+ else:
+ self.failUnless(xsalsa20.XSalsa20(key, iv))
+
+ def test_recursive(self):
+ # Try to use the same technique as:
+ # http://blogs.msdn.com/si_team/archive/2006/05/19/aes-test-vectors.aspx
+ # It's not exactly the same, though, because XSalsa20 is a stream
+ # cipher, whereas the Ferguson code is exercising a block cipher. But
+ # we try to do something similar.
+
+ # the XSalsa20 internal function uses a 32-byte block. We want to
+ # exercise it twice for each key, to guard against
+ # clobbering-after-key-setup errors. Just doing enc(enc(p)) could let
+ # XOR errors slip through. So to be safe, use B=64.
+ B=64
+ N=24
+ K=32
+ s = "\x00"*(B+N+K)
+ def enc(key, nonce, plaintext):
+ p = xsalsa20.XSalsa20(key=key, iv=nonce)
+ return p.process(plaintext)
+ for i in range(1000):
+ plaintext = s[-K-N-B:-K-N]
+ nonce = s[-K-N:-K]
+ key = s[-K:]
+ ciphertext = enc(key, nonce, plaintext)
+ s += ciphertext
+ s = s[-K-N-B:]
+ output = b2a_hex(s[-B:])
+ # I've compared this output against pynacl -warner
+ self.failUnlessEqual(output,
+ "77f8e2792dd4f2d44edf469c3a7ad5f7"
+ "5cb373fe0c3d9c8ee570dc91e00f1caa"
+ "25f725c202f3781869a40b8a2c856b55"
+ "8178b6af9576a15799c445c30aeced66")
+
+
+if __name__ == "__main__":
+ unittest.main()