1 /* MD5 module */
2
3 /* This module provides an interface to the MD5 algorithm */
4
5 /* See below for information about the original code this module was
6 based upon. Additional work performed by:
7
8 Andrew Kuchling (amk@amk.ca)
9 Greg Stein (gstein@lyra.org)
10 Trevor Perrin (trevp@trevp.net)
11
12 Copyright (C) 2005-2007 Gregory P. Smith (greg@krypto.org)
13 Licensed to PSF under a Contributor Agreement.
14
15 */
16
17 /* MD5 objects */
18
19 #ifndef Py_BUILD_CORE_BUILTIN
20 # define Py_BUILD_CORE_MODULE 1
21 #endif
22
23 #include "Python.h"
24 #include "hashlib.h"
25
26 /*[clinic input]
27 module _md5
28 class MD5Type "MD5object *" "&PyType_Type"
29 [clinic start generated code]*/
30 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=6e5261719957a912]*/
31
32 /* Some useful types */
33
34 #if SIZEOF_INT == 4
35 typedef unsigned int MD5_INT32; /* 32-bit integer */
36 typedef long long MD5_INT64; /* 64-bit integer */
37 #else
38 /* not defined. compilation will die. */
39 #endif
40
41 /* The MD5 block size and message digest sizes, in bytes */
42
43 #define MD5_BLOCKSIZE 64
44 #define MD5_DIGESTSIZE 16
45
46 #include "_hacl/Hacl_Hash_MD5.h"
47
48
49 typedef struct {
50 PyObject_HEAD
51 // Prevents undefined behavior via multiple threads entering the C API.
52 bool use_mutex;
53 PyMutex mutex;
54 Hacl_Hash_MD5_state_t *hash_state;
55 } MD5object;
56
57 #include "clinic/md5module.c.h"
58
59
60 typedef struct {
61 PyTypeObject* md5_type;
62 } MD5State;
63
64 static inline MD5State*
md5_get_state(PyObject * module)65 md5_get_state(PyObject *module)
66 {
67 void *state = PyModule_GetState(module);
68 assert(state != NULL);
69 return (MD5State *)state;
70 }
71
72 static MD5object *
newMD5object(MD5State * st)73 newMD5object(MD5State * st)
74 {
75 MD5object *md5 = (MD5object *)PyObject_GC_New(MD5object, st->md5_type);
76 if (!md5) {
77 return NULL;
78 }
79 HASHLIB_INIT_MUTEX(md5);
80
81 PyObject_GC_Track(md5);
82 return md5;
83 }
84
85 /* Internal methods for a hash object */
86 static int
MD5_traverse(PyObject * ptr,visitproc visit,void * arg)87 MD5_traverse(PyObject *ptr, visitproc visit, void *arg)
88 {
89 Py_VISIT(Py_TYPE(ptr));
90 return 0;
91 }
92
93 static void
MD5_dealloc(MD5object * ptr)94 MD5_dealloc(MD5object *ptr)
95 {
96 Hacl_Hash_MD5_free(ptr->hash_state);
97 PyTypeObject *tp = Py_TYPE((PyObject*)ptr);
98 PyObject_GC_UnTrack(ptr);
99 PyObject_GC_Del(ptr);
100 Py_DECREF(tp);
101 }
102
103
104 /* External methods for a hash object */
105
106 /*[clinic input]
107 MD5Type.copy
108
109 cls: defining_class
110
111 Return a copy of the hash object.
112 [clinic start generated code]*/
113
114 static PyObject *
MD5Type_copy_impl(MD5object * self,PyTypeObject * cls)115 MD5Type_copy_impl(MD5object *self, PyTypeObject *cls)
116 /*[clinic end generated code: output=bf055e08244bf5ee input=d89087dcfb2a8620]*/
117 {
118 MD5State *st = PyType_GetModuleState(cls);
119
120 MD5object *newobj;
121 if ((newobj = newMD5object(st))==NULL)
122 return NULL;
123
124 ENTER_HASHLIB(self);
125 newobj->hash_state = Hacl_Hash_MD5_copy(self->hash_state);
126 LEAVE_HASHLIB(self);
127 return (PyObject *)newobj;
128 }
129
130 /*[clinic input]
131 MD5Type.digest
132
133 Return the digest value as a bytes object.
134 [clinic start generated code]*/
135
136 static PyObject *
MD5Type_digest_impl(MD5object * self)137 MD5Type_digest_impl(MD5object *self)
138 /*[clinic end generated code: output=eb691dc4190a07ec input=bc0c4397c2994be6]*/
139 {
140 unsigned char digest[MD5_DIGESTSIZE];
141 ENTER_HASHLIB(self);
142 Hacl_Hash_MD5_digest(self->hash_state, digest);
143 LEAVE_HASHLIB(self);
144 return PyBytes_FromStringAndSize((const char *)digest, MD5_DIGESTSIZE);
145 }
146
147 /*[clinic input]
148 MD5Type.hexdigest
149
150 Return the digest value as a string of hexadecimal digits.
151 [clinic start generated code]*/
152
153 static PyObject *
MD5Type_hexdigest_impl(MD5object * self)154 MD5Type_hexdigest_impl(MD5object *self)
155 /*[clinic end generated code: output=17badced1f3ac932 input=b60b19de644798dd]*/
156 {
157 unsigned char digest[MD5_DIGESTSIZE];
158 ENTER_HASHLIB(self);
159 Hacl_Hash_MD5_digest(self->hash_state, digest);
160 LEAVE_HASHLIB(self);
161
162 const char *hexdigits = "0123456789abcdef";
163 char digest_hex[MD5_DIGESTSIZE * 2];
164 char *str = digest_hex;
165 for (size_t i=0; i < MD5_DIGESTSIZE; i++) {
166 unsigned char byte = digest[i];
167 *str++ = hexdigits[byte >> 4];
168 *str++ = hexdigits[byte & 0x0f];
169 }
170 return PyUnicode_FromStringAndSize(digest_hex, sizeof(digest_hex));
171 }
172
update(Hacl_Hash_MD5_state_t * state,uint8_t * buf,Py_ssize_t len)173 static void update(Hacl_Hash_MD5_state_t *state, uint8_t *buf, Py_ssize_t len) {
174 #if PY_SSIZE_T_MAX > UINT32_MAX
175 while (len > UINT32_MAX) {
176 Hacl_Hash_MD5_update(state, buf, UINT32_MAX);
177 len -= UINT32_MAX;
178 buf += UINT32_MAX;
179 }
180 #endif
181 Hacl_Hash_MD5_update(state, buf, (uint32_t) len);
182 }
183
184 /*[clinic input]
185 MD5Type.update
186
187 obj: object
188 /
189
190 Update this hash object's state with the provided string.
191 [clinic start generated code]*/
192
193 static PyObject *
MD5Type_update(MD5object * self,PyObject * obj)194 MD5Type_update(MD5object *self, PyObject *obj)
195 /*[clinic end generated code: output=f6ad168416338423 input=6e1efcd9ecf17032]*/
196 {
197 Py_buffer buf;
198
199 GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
200
201 if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
202 self->use_mutex = true;
203 }
204 if (self->use_mutex) {
205 Py_BEGIN_ALLOW_THREADS
206 PyMutex_Lock(&self->mutex);
207 update(self->hash_state, buf.buf, buf.len);
208 PyMutex_Unlock(&self->mutex);
209 Py_END_ALLOW_THREADS
210 } else {
211 update(self->hash_state, buf.buf, buf.len);
212 }
213
214 PyBuffer_Release(&buf);
215 Py_RETURN_NONE;
216 }
217
218 static PyMethodDef MD5_methods[] = {
219 MD5TYPE_COPY_METHODDEF
220 MD5TYPE_DIGEST_METHODDEF
221 MD5TYPE_HEXDIGEST_METHODDEF
222 MD5TYPE_UPDATE_METHODDEF
223 {NULL, NULL} /* sentinel */
224 };
225
226 static PyObject *
MD5_get_block_size(PyObject * self,void * closure)227 MD5_get_block_size(PyObject *self, void *closure)
228 {
229 return PyLong_FromLong(MD5_BLOCKSIZE);
230 }
231
232 static PyObject *
MD5_get_name(PyObject * self,void * closure)233 MD5_get_name(PyObject *self, void *closure)
234 {
235 return PyUnicode_FromStringAndSize("md5", 3);
236 }
237
238 static PyObject *
md5_get_digest_size(PyObject * self,void * closure)239 md5_get_digest_size(PyObject *self, void *closure)
240 {
241 return PyLong_FromLong(MD5_DIGESTSIZE);
242 }
243
244 static PyGetSetDef MD5_getseters[] = {
245 {"block_size",
246 (getter)MD5_get_block_size, NULL,
247 NULL,
248 NULL},
249 {"name",
250 (getter)MD5_get_name, NULL,
251 NULL,
252 NULL},
253 {"digest_size",
254 (getter)md5_get_digest_size, NULL,
255 NULL,
256 NULL},
257 {NULL} /* Sentinel */
258 };
259
260 static PyType_Slot md5_type_slots[] = {
261 {Py_tp_dealloc, MD5_dealloc},
262 {Py_tp_methods, MD5_methods},
263 {Py_tp_getset, MD5_getseters},
264 {Py_tp_traverse, MD5_traverse},
265 {0,0}
266 };
267
268 static PyType_Spec md5_type_spec = {
269 .name = "_md5.md5",
270 .basicsize = sizeof(MD5object),
271 .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION |
272 Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC),
273 .slots = md5_type_slots
274 };
275
276 /* The single module-level function: new() */
277
278 /*[clinic input]
279 _md5.md5
280
281 string: object(c_default="NULL") = b''
282 *
283 usedforsecurity: bool = True
284
285 Return a new MD5 hash object; optionally initialized with a string.
286 [clinic start generated code]*/
287
288 static PyObject *
_md5_md5_impl(PyObject * module,PyObject * string,int usedforsecurity)289 _md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity)
290 /*[clinic end generated code: output=587071f76254a4ac input=7a144a1905636985]*/
291 {
292 MD5object *new;
293 Py_buffer buf;
294
295 if (string)
296 GET_BUFFER_VIEW_OR_ERROUT(string, &buf);
297
298 MD5State *st = md5_get_state(module);
299 if ((new = newMD5object(st)) == NULL) {
300 if (string)
301 PyBuffer_Release(&buf);
302 return NULL;
303 }
304
305 new->hash_state = Hacl_Hash_MD5_malloc();
306
307 if (PyErr_Occurred()) {
308 Py_DECREF(new);
309 if (string)
310 PyBuffer_Release(&buf);
311 return NULL;
312 }
313 if (string) {
314 if (buf.len >= HASHLIB_GIL_MINSIZE) {
315 /* We do not initialize self->lock here as this is the constructor
316 * where it is not yet possible to have concurrent access. */
317 Py_BEGIN_ALLOW_THREADS
318 update(new->hash_state, buf.buf, buf.len);
319 Py_END_ALLOW_THREADS
320 } else {
321 update(new->hash_state, buf.buf, buf.len);
322 }
323 PyBuffer_Release(&buf);
324 }
325
326 return (PyObject *)new;
327 }
328
329
330 /* List of functions exported by this module */
331
332 static struct PyMethodDef MD5_functions[] = {
333 _MD5_MD5_METHODDEF
334 {NULL, NULL} /* Sentinel */
335 };
336
337 static int
_md5_traverse(PyObject * module,visitproc visit,void * arg)338 _md5_traverse(PyObject *module, visitproc visit, void *arg)
339 {
340 MD5State *state = md5_get_state(module);
341 Py_VISIT(state->md5_type);
342 return 0;
343 }
344
345 static int
_md5_clear(PyObject * module)346 _md5_clear(PyObject *module)
347 {
348 MD5State *state = md5_get_state(module);
349 Py_CLEAR(state->md5_type);
350 return 0;
351 }
352
353 static void
_md5_free(void * module)354 _md5_free(void *module)
355 {
356 _md5_clear((PyObject *)module);
357 }
358
359 /* Initialize this module. */
360 static int
md5_exec(PyObject * m)361 md5_exec(PyObject *m)
362 {
363 MD5State *st = md5_get_state(m);
364
365 st->md5_type = (PyTypeObject *)PyType_FromModuleAndSpec(
366 m, &md5_type_spec, NULL);
367
368 if (PyModule_AddObjectRef(m, "MD5Type", (PyObject *)st->md5_type) < 0) {
369 return -1;
370 }
371
372 return 0;
373 }
374
375 static PyModuleDef_Slot _md5_slots[] = {
376 {Py_mod_exec, md5_exec},
377 {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
378 {Py_mod_gil, Py_MOD_GIL_NOT_USED},
379 {0, NULL}
380 };
381
382
383 static struct PyModuleDef _md5module = {
384 PyModuleDef_HEAD_INIT,
385 .m_name = "_md5",
386 .m_size = sizeof(MD5State),
387 .m_methods = MD5_functions,
388 .m_slots = _md5_slots,
389 .m_traverse = _md5_traverse,
390 .m_clear = _md5_clear,
391 .m_free = _md5_free,
392 };
393
394 PyMODINIT_FUNC
PyInit__md5(void)395 PyInit__md5(void)
396 {
397 return PyModuleDef_Init(&_md5module);
398 }
399