]> www.wagner.pp.ru Git - oss/ctypescrypto.git/commitdiff
digest module covered by tests
authorVictor Wagner <wagner@atlas-card.ru>
Thu, 5 Jun 2014 10:18:33 +0000 (14:18 +0400)
committerVictor Wagner <wagner@atlas-card.ru>
Thu, 5 Jun 2014 10:18:33 +0000 (14:18 +0400)
ctypescrypto/__init__.py
ctypescrypto/digest.py
tests/testdigest.py [new file with mode: 0644]

index b6d00cacb75c7429a017d9b55997e2ba3afe3b88..d4dcf7c103374f834aea36b70f0ca0fabd3ecd72 100644 (file)
@@ -6,4 +6,4 @@
 from ctypes import CDLL
 
 libcrypto = CDLL("libcrypto.so.1.0.0")
-libcrypto.OPENSSL_config(None)
+libcrypto.OPENSSL_add_all_algorithms_conf()
index 4b0eeb5c1e7c39c30a56628cbe29593bf25387c1..4e33d92bb3e147b28a05fd22dd18ef315078dff2 100644 (file)
@@ -1,17 +1,32 @@
 """\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
@@ -22,78 +37,77 @@ class DigestType:
                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
@@ -103,15 +117,15 @@ class Digest:
                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
@@ -119,11 +133,12 @@ class Digest:
                        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
diff --git a/tests/testdigest.py b/tests/testdigest.py
new file mode 100644 (file)
index 0000000..4c4b4ed
--- /dev/null
@@ -0,0 +1,98 @@
+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()