summaryrefslogtreecommitdiff
path: root/pycryptopp/test/test_sha256.py
diff options
context:
space:
mode:
Diffstat (limited to 'pycryptopp/test/test_sha256.py')
-rw-r--r--pycryptopp/test/test_sha256.py181
1 files changed, 181 insertions, 0 deletions
diff --git a/pycryptopp/test/test_sha256.py b/pycryptopp/test/test_sha256.py
new file mode 100644
index 0000000..5e982dc
--- /dev/null
+++ b/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