"""\r
Implmenets interface to OpenSSL EVP_Digest* functions.\r
- Interface made as close to hashlib as possible\r
+ Interface made as close to hashlib as possible.\r
+\r
+ This module is really an excess effort. Hashlib allows access to\r
+ mostly same functionality except oids and nids of hashing\r
+ algortithms (which might be needed for private key operations).\r
+\r
+ hashlib even allows to use engine-provided digests if it is build\r
+ with dinamically linked libcrypto - so use\r
+ ctypescrypto.engine.set_default("gost",xFFFF) and md_gost94\r
+ algorithm would be available both to this module and hashlib.\r
+\r
"""\r
-from ctypes import c_int, c_char_p, c_void_p, POINTER, c_long_long\r
+from ctypes import c_int, c_char_p, c_void_p, POINTER, c_long,c_longlong, create_string_buffer,byref\r
from ctypescrypto import libcrypto\r
from ctypescrypto.exception import LibCryptoError\r
+from ctypescrypto.oid import Oid\r
DIGEST_ALGORITHMS = ("MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512")\r
\r
\r
class DigestError(LibCryptoError):\r
- pass\r
+ pass\r
\r
def new(algname):\r
+ """\r
+ Behaves just like hashlib.new. Creates digest object by\r
+ algorithm name\r
+ """\r
md=DigestType(algname)\r
return Digest(md)\r
\r
digest algorithm\r
\r
"""\r
- def __init__(self, digest_name):\r
+ def __init__(self, digest_name):\r
"""\r
Finds digest by its name\r
"""\r
- self.digest_name = digest_name\r
- self.digest = libcrypto.EVP_get_digestbyname(self.digest_name)\r
- if self.digest == 0:\r
- raise DigestError, "Unknown digest: %s" % self.digest_name\r
+ self.digest_name = digest_name\r
+ self.digest = libcrypto.EVP_get_digestbyname(self.digest_name)\r
+ if self.digest is None:\r
+ raise DigestError, "Unknown digest: %s" % self.digest_name\r
\r
- def __del__(self):\r
- pass\r
+ def __del__(self):\r
+ pass\r
def digest_size(self):\r
return libcrypto.EVP_MD_size(self.digest)\r
def block_size(self):\r
return libcrypto.EVP_MD_block_size(self.digest)\r
def oid(self):\r
- pass\r
- #FIXME TBD\r
- # return Oid(nid=libcrypto.EVP_MD_type(self.digest)\r
+ return Oid(libcrypto.EVP_MD_type(self.digest))\r
+\r
class Digest:\r
"""\r
Represents EVP_MD_CTX object which actually used to calculate\r
digests.\r
\r
"""\r
- def __init__(self, digest_type):\r
+ def __init__(self,digest_type):\r
"""\r
Initializes digest using given type.\r
"""\r
- self._clean_ctx()\r
- self.ctx = libcrypto.EVP_MD_CTX_create()\r
- if self.ctx == 0:\r
- raise DigestError, "Unable to create digest context"\r
- result = libcrypto.EVP_DigestInit_ex(self.ctx, digest_type.digest, None)\r
- if result == 0:\r
- self._clean_ctx()\r
- raise DigestError, "Unable to initialize digest"\r
- self.digest_type = digest_type\r
+ self._clean_ctx()\r
+ self.ctx = libcrypto.EVP_MD_CTX_create()\r
+ if self.ctx == 0:\r
+ raise DigestError, "Unable to create digest context"\r
+ result = libcrypto.EVP_DigestInit_ex(self.ctx, digest_type.digest, None)\r
+ if result == 0:\r
+ self._clean_ctx()\r
+ raise DigestError, "Unable to initialize digest"\r
+ self.digest_type = digest_type\r
self.digest_size = self.digest_type.digest_size()\r
self.block_size = self.digest_type.block_size()\r
\r
- def __del__(self):\r
- self._clean_ctx()\r
+ def __del__(self):\r
+ self._clean_ctx()\r
\r
- def update(self, data):\r
+ def update(self, data):\r
"""\r
Hashes given byte string as data\r
"""\r
- if self.digest_finalized:\r
- raise DigestError, "No updates allowed"\r
- if type(data) != type(""):\r
- raise TypeError, "A string is expected"\r
- result = libcrypto.EVP_DigestUpdate(self.ctx, c_char_p(data), len(data))\r
- if result != 1:\r
- raise DigestError, "Unable to update digest"\r
- \r
- def digest(self, data=None):\r
+ if self.digest_finalized:\r
+ raise DigestError, "No updates allowed"\r
+ if type(data) != type(""):\r
+ raise TypeError, "A string is expected"\r
+ result = libcrypto.EVP_DigestUpdate(self.ctx, c_char_p(data), len(data))\r
+ if result != 1:\r
+ raise DigestError, "Unable to update digest"\r
+ \r
+ def digest(self, data=None):\r
"""\r
Finalizes digest operation and return digest value\r
- Optionally hashes data before finalizing\r
+ Optionally hashes more data before finalizing\r
"""\r
- if self.digest_finalized:\r
+ if self.digest_finalized:\r
return self.digest_out.raw[:self.digest_size]\r
- if data is not None:\r
- self.update(data)\r
- self.digest_out = create_string_buffer(256)\r
- length = c_long(0)\r
- result = libcrypto.EVP_DigestFinal_ex(self.ctx, self.digest_out, byref(length))\r
- if result != 1 :\r
- raise DigestError, "Unable to finalize digest"\r
- self.digest_finalized = True\r
- return self.digest_out.raw[:self.digest_size]\r
+ if data is not None:\r
+ self.update(data)\r
+ self.digest_out = create_string_buffer(256)\r
+ length = c_long(0)\r
+ result = libcrypto.EVP_DigestFinal_ex(self.ctx, self.digest_out, byref(length))\r
+ if result != 1 :\r
+ raise DigestError, "Unable to finalize digest"\r
+ self.digest_finalized = True\r
+ return self.digest_out.raw[:self.digest_size]\r
def copy(self):\r
"""\r
Creates copy of the digest CTX to allow to compute digest\r
libcrypto.EVP_MD_CTX_copy(new_digest.ctx,self.ctx)\r
return new_digest\r
\r
- def _clean_ctx(self):\r
- try:\r
- if self.ctx is not None:\r
- libcrypto.EVP_MD_CTX_destroy(self.ctx)\r
- del(self.ctx)\r
- except AttributeError:\r
- pass\r
- self.digest_out = None\r
- self.digest_finalized = False\r
+ def _clean_ctx(self):\r
+ try:\r
+ if self.ctx is not None:\r
+ libcrypto.EVP_MD_CTX_destroy(self.ctx)\r
+ del(self.ctx)\r
+ except AttributeError:\r
+ pass\r
+ self.digest_out = None\r
+ self.digest_finalized = False\r
\r
def hexdigest(self,data=None):\r
"""\r
with hashlib\r
"""\r
from base64 import b16encode\r
- return b16encode(self.digest(data)\r
+ return b16encode(self.digest(data))\r
\r
\r
# Declare function result and argument types\r
libcrypto.EVP_get_digestbyname.restype = c_void_p\r
+libcrypto.EVP_get_digestbyname.argtypes = (c_char_p,)\r
libcrypto.EVP_MD_CTX_create.restype = c_void_p\r
libcrypto.EVP_DigestInit_ex.argtypes = (c_void_p,c_void_p,c_void_p)\r
libcrypto.EVP_DigestUpdate.argtypes = (c_void_p,c_char_p,c_longlong)\r
--- /dev/null
+from ctypescrypto.oid import Oid
+from ctypescrypto import digest
+from base64 import b16decode,b16encode
+import unittest
+
+class TestDigestType(unittest.TestCase):
+ def test_md4(self):
+ d=digest.DigestType("md4")
+ self.assertEqual(d.digest_size(),16)
+ self.assertEqual(d.block_size(),64)
+ self.assertEqual(d.oid(),Oid("md4"))
+ def test_md5(self):
+ d=digest.DigestType("md5")
+ self.assertEqual(d.digest_size(),16)
+ self.assertEqual(d.block_size(),64)
+ self.assertEqual(d.oid(),Oid("md5"))
+ def test_sha1(self):
+ d=digest.DigestType("sha1")
+ self.assertEqual(d.digest_size(),20)
+ self.assertEqual(d.block_size(),64)
+ self.assertEqual(d.oid(),Oid("sha1"))
+ def test_sha256(self):
+ d=digest.DigestType("sha256")
+ self.assertEqual(d.digest_size(),32)
+ self.assertEqual(d.block_size(),64)
+ self.assertEqual(d.oid(),Oid("sha256"))
+ def test_sha384(self):
+ d=digest.DigestType("sha384")
+ self.assertEqual(d.digest_size(),48)
+ self.assertEqual(d.block_size(),128)
+ self.assertEqual(d.oid(),Oid("sha384"))
+ def test_sha512(self):
+ d=digest.DigestType("sha512")
+ self.assertEqual(d.digest_size(),64)
+ self.assertEqual(d.block_size(),128)
+ self.assertEqual(d.oid(),Oid("sha512"))
+
+
+class TestIface(unittest.TestCase):
+ """ Test all methods with one algorithms """
+ def test_cons(self):
+ md=digest.DigestType("sha1")
+ dgst=digest.Digest(md)
+ dgst.update("A quick brown fox jumps over the lazy dog.")
+ self.assertEqual(dgst.digest_size,20)
+ self.assertEqual(dgst.hexdigest(),"00CFFE7312BF9CA73584F24BDF7DF1D028340397")
+ def test_bindigest(self):
+ dgst=digest.new("sha1")
+ dgst.update("A quick brown fox jumps over the lazy dog.")
+ self.assertEqual(dgst.digest_size,20)
+ self.assertEqual(dgst.digest(),b16decode("00CFFE7312BF9CA73584F24BDF7DF1D028340397",True))
+ def test_duplicatedigest(self):
+ dgst=digest.new("sha1")
+ dgst.update("A quick brown fox jumps over the lazy dog.")
+ v1=dgst.digest()
+ v2=dgst.digest()
+ self.assertEqual(v1,v2)
+ def test_copy(self):
+ dgst=digest.new("sha1")
+ dgst.update("A quick brown fox jumps over ")
+ d2=dgst.copy()
+ dgst.update("the lazy dog.")
+ value1=dgst.hexdigest()
+ d2.update("the fat pig.")
+ value2=d2.hexdigest()
+ self.assertEqual(value1,"00CFFE7312BF9CA73584F24BDF7DF1D028340397")
+ self.assertEqual(value2,"5328F33739BEC2A15B6A30F17D3BC13CC11A7C78")
+class TestAlgo(unittest.TestCase):
+ """ Test all statdard algorithms """
+ def test_md5(self):
+ d=digest.new("md5")
+ self.assertEqual(d.digest_size,16)
+ d.update("A quick brown fox jumps over the lazy dog.")
+ self.assertEqual(d.hexdigest(),"DF756A3769FCAB0A261880957590C768")
+
+ def test_md4(self):
+ d=digest.new("md4")
+ d.update("A quick brown fox jumps over the lazy dog.")
+ self.assertEqual(d.digest_size,16)
+ self.assertEqual(d.hexdigest(),"FAAED595A3E38BBF0D9B4B98021D200F")
+ def test_sha256(self):
+ d=digest.new("sha256")
+ d.update("A quick brown fox jumps over the lazy dog.")
+ self.assertEqual(d.digest_size,32)
+ self.assertEqual(d.hexdigest(),"FFCA2587CFD4846E4CB975B503C9EB940F94566AA394E8BD571458B9DA5097D5")
+ def test_sha384(self):
+ d=digest.new("sha384")
+ d.update("A quick brown fox jumps over the lazy dog.")
+ self.assertEqual(d.digest_size,48)
+ self.assertEqual(d.hexdigest(),"C7D71B1BA81D0DD028E79C7E75CF2F83169C14BA732CA5A2AD731151584E9DE843C1A314077D62B96B03367F72E126D8")
+ def test_sha512(self):
+ d=digest.new("sha512")
+ self.assertEqual(d.digest_size,64)
+ d.update("A quick brown fox jumps over the lazy dog.")
+ self.assertEqual(d.hexdigest(),"3045575CF3B873DD656F5F3426E04A4ACD11950BB2538772EE14867002B408E21FF18EF7F7B2CAB484A3C1C0BE3F8ACC4AED536A427353C7748DC365FC1A8646")
+
+if __name__ == "__main__":
+ unittest.main()