]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/digest.py
Addedet length parameter to Digest.update
[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 \r
23 class DigestError(LibCryptoError):\r
24         pass\r
25 \r
26 def new(algname):\r
27         """\r
28                 Behaves just like hashlib.new. Creates digest object by\r
29                 algorithm name\r
30         """\r
31         md=DigestType(algname)\r
32         return Digest(md)\r
33 \r
34 class DigestType:\r
35         """\r
36                 \r
37                 Represents EVP_MD object - constant structure which describes\r
38                 digest algorithm\r
39 \r
40         """\r
41         def __init__(self,      digest_name):\r
42                 """\r
43                         Finds digest by its name\r
44                 """\r
45                 self.digest_name = digest_name\r
46                 self.digest = libcrypto.EVP_get_digestbyname(self.digest_name)\r
47                 if self.digest is None:\r
48                         raise DigestError, "Unknown digest: %s" % self.digest_name\r
49 \r
50         def __del__(self):\r
51                 pass\r
52         def digest_size(self):\r
53                 return libcrypto.EVP_MD_size(self.digest)\r
54         def block_size(self):\r
55                 return libcrypto.EVP_MD_block_size(self.digest)\r
56         def oid(self):\r
57                 return Oid(libcrypto.EVP_MD_type(self.digest))\r
58 \r
59 class Digest:\r
60         """\r
61                 Represents EVP_MD_CTX object which actually used to calculate\r
62                 digests.\r
63 \r
64         """\r
65         def __init__(self,digest_type):\r
66                 """\r
67                         Initializes digest using given type.\r
68                 """\r
69                 self._clean_ctx()\r
70                 self.ctx = libcrypto.EVP_MD_CTX_create()\r
71                 if self.ctx == 0:\r
72                         raise DigestError, "Unable to create digest context"\r
73                 result = libcrypto.EVP_DigestInit_ex(self.ctx, digest_type.digest, None)\r
74                 if result == 0:\r
75                         self._clean_ctx()\r
76                         raise DigestError, "Unable to initialize digest"\r
77                 self.digest_type = digest_type\r
78                 self.digest_size = self.digest_type.digest_size()\r
79                 self.block_size = self.digest_type.block_size()\r
80 \r
81         def __del__(self):\r
82                 self._clean_ctx()\r
83 \r
84         def update(self, data, length=None):\r
85                 """\r
86                         Hashes given byte string \r
87 \r
88                         @param data - string to hash\r
89                         @param length - if not specifed, entire string is hashed,\r
90                                         otherwise only first length bytes\r
91                 """\r
92                 if self.digest_finalized:\r
93                         raise DigestError, "No updates allowed"\r
94                 if type(data) != type(""):\r
95                         raise TypeError, "A string is expected"\r
96                 if length is None:\r
97                         length=len(data)\r
98                 elif length> len(data):\r
99                         raise ValueError("Specified length is greater than length of data")\r
100                 result = libcrypto.EVP_DigestUpdate(self.ctx, c_char_p(data), length)\r
101                 if result != 1:\r
102                         raise DigestError, "Unable to update digest"\r
103                 \r
104         def digest(self, data=None):\r
105                 """\r
106                         Finalizes digest operation and return digest value\r
107                         Optionally hashes more data before finalizing\r
108                 """\r
109                 if self.digest_finalized:\r
110                         return self.digest_out.raw[:self.digest_size]\r
111                 if data is not None:\r
112                         self.update(data)\r
113                 self.digest_out = create_string_buffer(256)\r
114                 length = c_long(0)\r
115                 result = libcrypto.EVP_DigestFinal_ex(self.ctx, self.digest_out, byref(length))\r
116                 if result != 1 :\r
117                         raise DigestError, "Unable to finalize digest"\r
118                 self.digest_finalized = True\r
119                 return self.digest_out.raw[:self.digest_size]\r
120         def copy(self):\r
121                 """\r
122                         Creates copy of the digest CTX to allow to compute digest\r
123                         while being able to hash more data\r
124                 """\r
125                 new_digest=Digest(self.digest_type)\r
126                 libcrypto.EVP_MD_CTX_copy(new_digest.ctx,self.ctx)\r
127                 return new_digest\r
128 \r
129         def _clean_ctx(self):\r
130                 try:\r
131                         if self.ctx is not None:\r
132                                 libcrypto.EVP_MD_CTX_destroy(self.ctx)\r
133                                 del(self.ctx)\r
134                 except AttributeError:\r
135                         pass\r
136                 self.digest_out = None\r
137                 self.digest_finalized = False\r
138 \r
139         def hexdigest(self,data=None):\r
140                 """\r
141                         Returns digest in the hexadecimal form. For compatibility\r
142                         with hashlib\r
143                 """\r
144                 from base64 import b16encode\r
145                 return b16encode(self.digest(data))\r
146 \r
147 \r
148 # Declare function result and argument types\r
149 libcrypto.EVP_get_digestbyname.restype = c_void_p\r
150 libcrypto.EVP_get_digestbyname.argtypes = (c_char_p,)\r
151 libcrypto.EVP_MD_CTX_create.restype = c_void_p\r
152 libcrypto.EVP_DigestInit_ex.argtypes = (c_void_p,c_void_p,c_void_p)\r
153 libcrypto.EVP_DigestUpdate.argtypes = (c_void_p,c_char_p,c_longlong)\r
154 libcrypto.EVP_DigestFinal_ex.argtypes = (c_void_p,c_char_p,POINTER(c_long))\r
155 libcrypto.EVP_MD_CTX_destroy.argtypes = (c_void_p,)\r
156 libcrypto.EVP_MD_CTX_copy.argtypes=(c_void_p, c_void_p)\r
157 libcrypto.EVP_MD_type.argtypes=(c_void_p,)\r
158 libcrypto.EVP_MD_size.argtypes=(c_void_p,)\r
159 libcrypto.EVP_MD_block_size.argtypes=(c_void_p,)\r