]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/digest.py
3589fc3fa1bc37a4d6e3d73e9a9586e1c432ab94
[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
17 from ctypes import create_string_buffer, byref
18 from ctypescrypto import libcrypto
19 from ctypescrypto.exception import LibCryptoError
20 from ctypescrypto.oid import Oid
21 DIGEST_ALGORITHMS = ("MD5", "SHA1", "SHA224", "SHA256", "SHA384", "SHA512")
22
23 __all__ = ['DigestError', 'Digest', 'DigestType', 'new']
24
25 class DigestError(LibCryptoError):
26     """ Exception raised if some OpenSSL function returns error """
27     pass
28
29 def 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
38 class 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)
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
90 class 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, str):
128             raise TypeError("A 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         return b16encode(self.digest(data))
184
185
186 # Declare function result and argument types
187 libcrypto.EVP_get_digestbyname.restype = c_void_p
188 libcrypto.EVP_get_digestbyname.argtypes = (c_char_p, )
189 # These two functions are renamed in OpenSSL 1.1.0
190 if hasattr(libcrypto,"EVP_MD_CTX_create"):
191     Digest.newctx = libcrypto.EVP_MD_CTX_create
192     Digest.freectx = libcrypto.EVP_MD_CTX_destroy
193 else:
194     Digest.newctx = libcrypto.EVP_MD_CTX_new
195     Digest.freectx = libcrypto.EVP_MD_CTX_free
196 Digest.newctx.restype = c_void_p
197 Digest.freectx.argtypes = (c_void_p, )
198 # libcrypto.EVP_MD_CTX_create has no arguments
199 libcrypto.EVP_DigestInit_ex.restype = c_int
200 libcrypto.EVP_DigestInit_ex.argtypes = (c_void_p, c_void_p, c_void_p)
201 libcrypto.EVP_DigestUpdate.restype = c_int
202 libcrypto.EVP_DigestUpdate.argtypes = (c_void_p, c_char_p, c_longlong)
203 libcrypto.EVP_DigestFinal_ex.restype = c_int
204 libcrypto.EVP_DigestFinal_ex.argtypes = (c_void_p, c_char_p, POINTER(c_long))
205 libcrypto.EVP_MD_CTX_copy.restype = c_int
206 libcrypto.EVP_MD_CTX_copy.argtypes = (c_void_p, c_void_p)
207 libcrypto.EVP_MD_type.argtypes = (c_void_p, )
208 libcrypto.EVP_MD_size.argtypes = (c_void_p, )
209 libcrypto.EVP_MD_block_size.restype = c_int
210 libcrypto.EVP_MD_block_size.argtypes = (c_void_p, )
211 libcrypto.EVP_MD_size.restype = c_int
212 libcrypto.EVP_MD_size.argtypes = (c_void_p, )
213 libcrypto.EVP_MD_type.restype = c_int
214 libcrypto.EVP_MD_type.argtypes = (c_void_p, )