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