1""" 2Implements interface to OpenSSL EVP_Digest* functions. 3 4Interface made as close to hashlib as possible. 5 6This module is really an excess effort. Hashlib allows access to 7mostly same functionality except oids and nids of hashing 8algortithms (which might be needed for private key operations). 9 10hashlib even allows to use engine-provided digests if it is build 11with dinamically linked libcrypto - so use 12ctypescrypto.engine.set_default("gost",xFFFF) and md_gost94 13algorithm would be available both to this module and hashlib. 14 15""" 16from ctypes import c_int, c_char_p, c_void_p, POINTER, c_long, c_longlong 17from ctypes import create_string_buffer, byref 18from ctypescrypto import libcrypto,pyver, bintype 19from ctypescrypto.exception import LibCryptoError 20from ctypescrypto.oid import Oid 21DIGEST_ALGORITHMS = ("MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512") 22 23__all__ = ['DigestError', 'Digest', 'DigestType', 'new'] 24 25class DigestError(LibCryptoError): 26 """ Exception raised if some OpenSSL function returns error """ 27 pass 28 29def new(algname): 30 """ 31 Behaves just like hashlib.new. Creates digest object by 32 algorithm name 33 """ 34 35 digest_type = DigestType(algname) 36 return Digest(digest_type) 37 38class DigestType(object): 39 """ 40 Represents EVP_MD object - constant structure which describes 41 digest algorithm 42 """ 43 def __init__(self, digest_name): 44 """ 45 Finds digest by its name. You can pass Oid object instead of 46 name. 47 48 Special case is when None is passed as name. In this case 49 unitialized digest is created, and can be initalized later 50 by setting its digest attribute to pointer to EVP_MD 51 """ 52 if digest_name is None: 53 return 54 55 if isinstance(digest_name, Oid): 56 self.digest_name = digest_name.longname() 57 else: 58 self.digest_name = str(digest_name) 59 self.digest = libcrypto.EVP_get_digestbyname(self.digest_name.encode('us-ascii')) 60 if self.digest is None: 61 raise DigestError("Unknown digest: %s" % self.digest_name) 62 63 @property 64 def name(self): 65 """ Returns name of the digest """ 66 if not hasattr(self, 'digest_name'): 67 self.digest_name = Oid(libcrypto.EVP_MD_type(self.digest) 68 ).longname() 69 return self.digest_name 70 71 def __del__(self): 72 """ Empty destructor for constant object """ 73 pass 74 75 @property 76 def digest_size(self): 77 """ Returns size of digest """ 78 return libcrypto.EVP_MD_size(self.digest) 79 80 @property 81 def block_size(self): 82 """ Returns block size of the digest """ 83 return libcrypto.EVP_MD_block_size(self.digest) 84 85 @property 86 def oid(self): 87 """ Returns Oid object of digest type """ 88 return Oid(libcrypto.EVP_MD_type(self.digest)) 89 90class Digest(object): 91 """ 92 Represents EVP_MD_CTX object which actually used to calculate 93 digests. 94 """ 95 96 def __init__(self, digest_type): 97 """ 98 Initializes digest using given type. 99 """ 100 self.ctx = self.newctx() 101 if self.ctx is None: 102 raise DigestError("Unable to create digest context") 103 self.digest_out = None 104 self.digest_finalized = False 105 result = libcrypto.EVP_DigestInit_ex(self.ctx, digest_type.digest, None) 106 if result == 0: 107 self._clean_ctx() 108 raise DigestError("Unable to initialize digest") 109 self.digest_type = digest_type 110 self.digest_size = self.digest_type.digest_size 111 self.block_size = self.digest_type.block_size 112 113 def __del__(self): 114 """ Uses _clean_ctx internal method """ 115 self._clean_ctx() 116 117 def update(self, data, length=None): 118 """ 119 Hashes given byte string 120 121 @param data - string to hash 122 @param length - if not specifed, entire string is hashed, 123 otherwise only first length bytes 124 """ 125 if self.digest_finalized: 126 raise DigestError("No updates allowed") 127 if not isinstance(data, bintype): 128 raise TypeError("A byte string is expected") 129 if length is None: 130 length = len(data) 131 elif length > len(data): 132 raise ValueError("Specified length is greater than length of data") 133 result = libcrypto.EVP_DigestUpdate(self.ctx, c_char_p(data), length) 134 if result != 1: 135 raise DigestError("Unable to update digest") 136 137 def digest(self, data=None): 138 """ 139 Finalizes digest operation and return digest value 140 Optionally hashes more data before finalizing 141 """ 142 if self.digest_finalized: 143 return self.digest_out.raw[:self.digest_size] 144 if data is not None: 145 self.update(data) 146 self.digest_out = create_string_buffer(256) 147 length = c_long(0) 148 result = libcrypto.EVP_DigestFinal_ex(self.ctx, self.digest_out, 149 byref(length)) 150 if result != 1: 151 raise DigestError("Unable to finalize digest") 152 self.digest_finalized = True 153 return self.digest_out.raw[:self.digest_size] 154 def copy(self): 155 """ 156 Creates copy of the digest CTX to allow to compute digest 157 while being able to hash more data 158 """ 159 160 new_digest = Digest(self.digest_type) 161 libcrypto.EVP_MD_CTX_copy(new_digest.ctx, self.ctx) 162 return new_digest 163 164 def _clean_ctx(self): 165 """ 166 Clears and deallocates context 167 """ 168 try: 169 if self.ctx is not None: 170 libcrypto.EVP_MD_CTX_free(self.ctx) 171 del self.ctx 172 except AttributeError: 173 pass 174 self.digest_out = None 175 self.digest_finalized = False 176 177 def hexdigest(self, data=None): 178 """ 179 Returns digest in the hexadecimal form. For compatibility 180 with hashlib 181 """ 182 from base64 import b16encode 183 if pyver == 2: 184 return b16encode(self.digest(data)) 185 else: 186 return b16encode(self.digest(data)).decode('us-ascii') 187 188 189# Declare function result and argument types 190libcrypto.EVP_get_digestbyname.restype = c_void_p 191libcrypto.EVP_get_digestbyname.argtypes = (c_char_p, ) 192# These two functions are renamed in OpenSSL 1.1.0 193if hasattr(libcrypto,"EVP_MD_CTX_create"): 194 Digest.newctx = libcrypto.EVP_MD_CTX_create 195 Digest.freectx = libcrypto.EVP_MD_CTX_destroy 196else: 197 Digest.newctx = libcrypto.EVP_MD_CTX_new 198 Digest.freectx = libcrypto.EVP_MD_CTX_free 199Digest.newctx.restype = c_void_p 200Digest.freectx.argtypes = (c_void_p, ) 201# libcrypto.EVP_MD_CTX_create has no arguments 202libcrypto.EVP_DigestInit_ex.restype = c_int 203libcrypto.EVP_DigestInit_ex.argtypes = (c_void_p, c_void_p, c_void_p) 204libcrypto.EVP_DigestUpdate.restype = c_int 205libcrypto.EVP_DigestUpdate.argtypes = (c_void_p, c_char_p, c_longlong) 206libcrypto.EVP_DigestFinal_ex.restype = c_int 207libcrypto.EVP_DigestFinal_ex.argtypes = (c_void_p, c_char_p, POINTER(c_long)) 208libcrypto.EVP_MD_CTX_copy.restype = c_int 209libcrypto.EVP_MD_CTX_copy.argtypes = (c_void_p, c_void_p) 210libcrypto.EVP_MD_type.argtypes = (c_void_p, ) 211libcrypto.EVP_MD_size.argtypes = (c_void_p, ) 212libcrypto.EVP_MD_block_size.restype = c_int 213libcrypto.EVP_MD_block_size.argtypes = (c_void_p, ) 214libcrypto.EVP_MD_size.restype = c_int 215libcrypto.EVP_MD_size.argtypes = (c_void_p, ) 216libcrypto.EVP_MD_type.restype = c_int 217libcrypto.EVP_MD_type.argtypes = (c_void_p, ) 218