• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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