]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/digest.py
Begin to implement python3 support. Now tests for oid, bio, cipher, digest, mac and...
[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,pyver, bintype
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.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
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, 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
190 libcrypto.EVP_get_digestbyname.restype = c_void_p
191 libcrypto.EVP_get_digestbyname.argtypes = (c_char_p, )
192 # These two functions are renamed in OpenSSL 1.1.0
193 if hasattr(libcrypto,"EVP_MD_CTX_create"):
194     Digest.newctx = libcrypto.EVP_MD_CTX_create
195     Digest.freectx = libcrypto.EVP_MD_CTX_destroy
196 else:
197     Digest.newctx = libcrypto.EVP_MD_CTX_new
198     Digest.freectx = libcrypto.EVP_MD_CTX_free
199 Digest.newctx.restype = c_void_p
200 Digest.freectx.argtypes = (c_void_p, )
201 # libcrypto.EVP_MD_CTX_create has no arguments
202 libcrypto.EVP_DigestInit_ex.restype = c_int
203 libcrypto.EVP_DigestInit_ex.argtypes = (c_void_p, c_void_p, c_void_p)
204 libcrypto.EVP_DigestUpdate.restype = c_int
205 libcrypto.EVP_DigestUpdate.argtypes = (c_void_p, c_char_p, c_longlong)
206 libcrypto.EVP_DigestFinal_ex.restype = c_int
207 libcrypto.EVP_DigestFinal_ex.argtypes = (c_void_p, c_char_p, POINTER(c_long))
208 libcrypto.EVP_MD_CTX_copy.restype = c_int
209 libcrypto.EVP_MD_CTX_copy.argtypes = (c_void_p, c_void_p)
210 libcrypto.EVP_MD_type.argtypes = (c_void_p, )
211 libcrypto.EVP_MD_size.argtypes = (c_void_p, )
212 libcrypto.EVP_MD_block_size.restype = c_int
213 libcrypto.EVP_MD_block_size.argtypes = (c_void_p, )
214 libcrypto.EVP_MD_size.restype = c_int
215 libcrypto.EVP_MD_size.argtypes = (c_void_p, )
216 libcrypto.EVP_MD_type.restype = c_int
217 libcrypto.EVP_MD_type.argtypes = (c_void_p, )