]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/digest.py
Converted tabs to spaces to make pylint happy
[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,)