]> wagner.pp.ru Git - oss/ctypescrypto.git/blobdiff - ctypescrypto/digest.py
Python 3 support for modules pbkdf2 pkey ec x509
[oss/ctypescrypto.git] / ctypescrypto / digest.py
index 6ebb82af2a583975c2629dfc3be20c3d3bb3da69..f1c495c88ae6580c327717fa033227110a70cd97 100644 (file)
 """
 """
-    Implements interface to OpenSSL EVP_Digest* functions.
+Implements interface to OpenSSL EVP_Digest* functions.
 
 
-    Interface  made as close to hashlib as possible.
+Interface  made as close to hashlib as possible.
 
 
-    This module is really an excess effort. Hashlib allows access to
-    mostly same functionality except oids and nids of hashing
-    algortithms (which might be needed for private key operations).
+This module is really an excess effort. Hashlib allows access to
+mostly same functionality except oids and nids of hashing
+algortithms (which might be needed for private key operations).
 
 
-    hashlib even allows to use engine-provided digests if it is build
-    with dinamically linked libcrypto - so use
-    ctypescrypto.engine.set_default("gost",xFFFF) and md_gost94
-    algorithm would be available both to this module and hashlib.
+hashlib even allows to use engine-provided digests if it is build
+with dinamically linked libcrypto - so use
+ctypescrypto.engine.set_default("gost",xFFFF) and md_gost94
+algorithm would be available both to this module and hashlib.
 
 """
 
 """
-from ctypes import c_int, c_char_p, c_void_p, POINTER, c_long,c_longlong, create_string_buffer,byref
-from ctypescrypto import libcrypto
+from ctypes import c_int, c_char_p, c_void_p, POINTER, c_long, c_longlong
+from ctypes import create_string_buffer, byref
+from ctypescrypto import libcrypto,pyver, bintype
 from ctypescrypto.exception import LibCryptoError
 from ctypescrypto.oid import Oid
 DIGEST_ALGORITHMS = ("MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512")
 
 from ctypescrypto.exception import LibCryptoError
 from ctypescrypto.oid import Oid
 DIGEST_ALGORITHMS = ("MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512")
 
-__all__ = ['DigestError','Digest','DigestType','new']
+__all__ = ['DigestError', 'Digest', 'DigestType', 'new']
 
 class DigestError(LibCryptoError):
 
 class DigestError(LibCryptoError):
+    """ Exception raised if some OpenSSL function returns error """
     pass
 
 def new(algname):
     """
     pass
 
 def new(algname):
     """
-        Behaves just like hashlib.new. Creates digest object by
-        algorithm name
+    Behaves just like hashlib.new. Creates digest object by
+    algorithm name
     """
     """
-    md=DigestType(algname)
-    return Digest(md)
+
+    digest_type = DigestType(algname)
+    return Digest(digest_type)
 
 class DigestType(object):
     """
 
 class DigestType(object):
     """
-        
-        Represents EVP_MD object - constant structure which describes
-        digest algorithm
-
+    Represents EVP_MD object - constant structure which describes
+    digest algorithm
     """
     """
-    def __init__(self,  digest_name):
+    def __init__(self, digest_name):
         """
         """
-            Finds digest by its name. You can pass Oid object instead of
-            name.
+        Finds digest by its name. You can pass Oid object instead of
+        name.
 
 
-            Special case is when None is passed as name. In this case
-            unitialized digest is created, and can be initalized later
-            by setting its digest attribute to pointer to EVP_MD
+        Special case is when None is passed as name. In this case
+        unitialized digest is created, and can be initalized later
+        by setting its digest attribute to pointer to EVP_MD
         """
         if digest_name is None:
         """
         if digest_name is None:
-            return 
-        if isinstance(digest_name,Oid):
-            self.digest_name=digest_name.longname()
-            self.digest=libcrypto.EVP_get_digestbyname(self.digest_name)
+            return
+
+        if isinstance(digest_name, Oid):
+            self.digest_name = digest_name.longname()
         else:
             self.digest_name = str(digest_name)
         else:
             self.digest_name = str(digest_name)
-            self.digest = libcrypto.EVP_get_digestbyname(self.digest_name)
+        self.digest = libcrypto.EVP_get_digestbyname(self.digest_name.encode('us-ascii'))
         if self.digest is None:
             raise DigestError("Unknown digest: %s" % self.digest_name)
 
     @property
     def name(self):
         if self.digest is None:
             raise DigestError("Unknown digest: %s" % self.digest_name)
 
     @property
     def name(self):
-        if not hasattr(self,'digest_name'):
-            self.digest_name=Oid(libcrypto.EVP_MD_type(self.digest)).longname()
+        """ Returns name of the digest """
+        if not hasattr(self, 'digest_name'):
+            self.digest_name = Oid(libcrypto.EVP_MD_type(self.digest)
+                                  ).longname()
         return self.digest_name
         return self.digest_name
+
     def __del__(self):
     def __del__(self):
+        """ Empty destructor for constant object """
         pass
         pass
+
+    @property
     def digest_size(self):
     def digest_size(self):
+        """ Returns size of digest """
         return libcrypto.EVP_MD_size(self.digest)
         return libcrypto.EVP_MD_size(self.digest)
+
+    @property
     def block_size(self):
     def block_size(self):
+        """ Returns block size of the digest """
         return libcrypto.EVP_MD_block_size(self.digest)
         return libcrypto.EVP_MD_block_size(self.digest)
+
+    @property
     def oid(self):
     def oid(self):
+        """ Returns Oid object of digest type """
         return Oid(libcrypto.EVP_MD_type(self.digest))
 
 class Digest(object):
     """
         return Oid(libcrypto.EVP_MD_type(self.digest))
 
 class Digest(object):
     """
-        Represents EVP_MD_CTX object which actually used to calculate
-        digests.
-
+    Represents EVP_MD_CTX object which actually used to calculate
+    digests.
     """
     """
-    def __init__(self,digest_type):
+
+    def __init__(self, digest_type):
         """
         """
-            Initializes digest using given type.
+        Initializes digest using given type.
         """
         """
-        self._clean_ctx()
-        self.ctx = libcrypto.EVP_MD_CTX_create()
+        self.ctx = self.newctx()
         if self.ctx is None:
             raise DigestError("Unable to create digest context")
         if self.ctx is None:
             raise DigestError("Unable to create digest context")
+        self.digest_out = None
+        self.digest_finalized = False
         result = libcrypto.EVP_DigestInit_ex(self.ctx, digest_type.digest, None)
         if result == 0:
             self._clean_ctx()
             raise DigestError("Unable to initialize digest")
         self.digest_type = digest_type
         result = libcrypto.EVP_DigestInit_ex(self.ctx, digest_type.digest, None)
         if result == 0:
             self._clean_ctx()
             raise DigestError("Unable to initialize digest")
         self.digest_type = digest_type
-        self.digest_size = self.digest_type.digest_size()
-        self.block_size = self.digest_type.block_size()
+        self.digest_size = self.digest_type.digest_size
+        self.block_size = self.digest_type.block_size
 
     def __del__(self):
 
     def __del__(self):
+        """ Uses _clean_ctx internal method """
         self._clean_ctx()
 
     def update(self, data, length=None):
         """
         self._clean_ctx()
 
     def update(self, data, length=None):
         """
-            Hashes given byte string 
+        Hashes given byte string
 
 
-            @param data - string to hash
-            @param length - if not specifed, entire string is hashed,
-                    otherwise only first length bytes
+        @param data - string to hash
+        @param length - if not specifed, entire string is hashed,
+                otherwise only first length bytes
         """
         if self.digest_finalized:
             raise DigestError("No updates allowed")
         """
         if self.digest_finalized:
             raise DigestError("No updates allowed")
-        if not isinstance(data,str):
-            raise TypeError("A string is expected")
+        if not isinstance(data, bintype):
+            raise TypeError("A byte string is expected")
         if length is None:
             length = len(data)
         elif length > len(data):
             raise ValueError("Specified length is greater than length of data")
         result = libcrypto.EVP_DigestUpdate(self.ctx, c_char_p(data), length)
         if result != 1:
         if length is None:
             length = len(data)
         elif length > len(data):
             raise ValueError("Specified length is greater than length of data")
         result = libcrypto.EVP_DigestUpdate(self.ctx, c_char_p(data), length)
         if result != 1:
-            raise DigestError, "Unable to update digest"
-        
+            raise DigestError("Unable to update digest")
+
     def digest(self, data=None):
         """
     def digest(self, data=None):
         """
-            Finalizes digest operation and return digest value
-            Optionally hashes more data before finalizing
+        Finalizes digest operation and return digest value
+        Optionally hashes more data before finalizing
         """
         if self.digest_finalized:
             return self.digest_out.raw[:self.digest_size]
         """
         if self.digest_finalized:
             return self.digest_out.raw[:self.digest_size]
@@ -129,48 +145,73 @@ class Digest(object):
             self.update(data)
         self.digest_out = create_string_buffer(256)
         length = c_long(0)
             self.update(data)
         self.digest_out = create_string_buffer(256)
         length = c_long(0)
-        result = libcrypto.EVP_DigestFinal_ex(self.ctx, self.digest_out, byref(length))
-        if result != 1 :
+        result = libcrypto.EVP_DigestFinal_ex(self.ctx, self.digest_out,
+                                              byref(length))
+        if result != 1:
             raise DigestError("Unable to finalize digest")
         self.digest_finalized = True
         return self.digest_out.raw[:self.digest_size]
     def copy(self):
         """
             raise DigestError("Unable to finalize digest")
         self.digest_finalized = True
         return self.digest_out.raw[:self.digest_size]
     def copy(self):
         """
-            Creates copy of the digest CTX to allow to compute digest
-            while being able to hash more data
+        Creates copy of the digest CTX to allow to compute digest
+        while being able to hash more data
         """
         """
-        new_digest=Digest(self.digest_type)
-        libcrypto.EVP_MD_CTX_copy(new_digest.ctx,self.ctx)
+
+        new_digest = Digest(self.digest_type)
+        libcrypto.EVP_MD_CTX_copy(new_digest.ctx, self.ctx)
         return new_digest
 
     def _clean_ctx(self):
         return new_digest
 
     def _clean_ctx(self):
+        """
+        Clears and deallocates context
+        """
         try:
             if self.ctx is not None:
         try:
             if self.ctx is not None:
-                libcrypto.EVP_MD_CTX_destroy(self.ctx)
-                del(self.ctx)
+                libcrypto.EVP_MD_CTX_free(self.ctx)
+                del self.ctx
         except AttributeError:
             pass
         self.digest_out = None
         self.digest_finalized = False
 
         except AttributeError:
             pass
         self.digest_out = None
         self.digest_finalized = False
 
-    def hexdigest(self,data=None):
+    def hexdigest(self, data=None):
         """
             Returns digest in the hexadecimal form. For compatibility
             with hashlib
         """
         from base64 import b16encode
         """
             Returns digest in the hexadecimal form. For compatibility
             with hashlib
         """
         from base64 import b16encode
-        return b16encode(self.digest(data))
+        if pyver == 2:
+            return b16encode(self.digest(data))
+        else:
+            return b16encode(self.digest(data)).decode('us-ascii')
 
 
 # Declare function result and argument types
 libcrypto.EVP_get_digestbyname.restype = c_void_p
 
 
 # Declare function result and argument types
 libcrypto.EVP_get_digestbyname.restype = c_void_p
-libcrypto.EVP_get_digestbyname.argtypes = (c_char_p,)
-libcrypto.EVP_MD_CTX_create.restype = c_void_p
-libcrypto.EVP_DigestInit_ex.argtypes = (c_void_p,c_void_p,c_void_p)
-libcrypto.EVP_DigestUpdate.argtypes = (c_void_p,c_char_p,c_longlong)
-libcrypto.EVP_DigestFinal_ex.argtypes = (c_void_p,c_char_p,POINTER(c_long))
-libcrypto.EVP_MD_CTX_destroy.argtypes = (c_void_p,)
-libcrypto.EVP_MD_CTX_copy.argtypes=(c_void_p, c_void_p)
-libcrypto.EVP_MD_type.argtypes=(c_void_p,)
-libcrypto.EVP_MD_size.argtypes=(c_void_p,)
-libcrypto.EVP_MD_block_size.argtypes=(c_void_p,)
+libcrypto.EVP_get_digestbyname.argtypes = (c_char_p, )
+# These two functions are renamed in OpenSSL 1.1.0
+if hasattr(libcrypto,"EVP_MD_CTX_create"):
+    Digest.newctx = libcrypto.EVP_MD_CTX_create
+    Digest.freectx = libcrypto.EVP_MD_CTX_destroy
+else:
+    Digest.newctx = libcrypto.EVP_MD_CTX_new
+    Digest.freectx = libcrypto.EVP_MD_CTX_free
+Digest.newctx.restype = c_void_p
+Digest.freectx.argtypes = (c_void_p, )
+# libcrypto.EVP_MD_CTX_create has no arguments
+libcrypto.EVP_DigestInit_ex.restype = c_int
+libcrypto.EVP_DigestInit_ex.argtypes = (c_void_p, c_void_p, c_void_p)
+libcrypto.EVP_DigestUpdate.restype = c_int
+libcrypto.EVP_DigestUpdate.argtypes = (c_void_p, c_char_p, c_longlong)
+libcrypto.EVP_DigestFinal_ex.restype = c_int
+libcrypto.EVP_DigestFinal_ex.argtypes = (c_void_p, c_char_p, POINTER(c_long))
+libcrypto.EVP_MD_CTX_copy.restype = c_int
+libcrypto.EVP_MD_CTX_copy.argtypes = (c_void_p, c_void_p)
+libcrypto.EVP_MD_type.argtypes = (c_void_p, )
+libcrypto.EVP_MD_size.argtypes = (c_void_p, )
+libcrypto.EVP_MD_block_size.restype = c_int
+libcrypto.EVP_MD_block_size.argtypes = (c_void_p, )
+libcrypto.EVP_MD_size.restype = c_int
+libcrypto.EVP_MD_size.argtypes = (c_void_p, )
+libcrypto.EVP_MD_type.restype = c_int
+libcrypto.EVP_MD_type.argtypes = (c_void_p, )