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