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