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