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 #ifndef Py_BUILD_CORE_BUILTIN
19 # define Py_BUILD_CORE_MODULE 1
20 #endif
21
22 #include "Python.h"
23 #include "hashlib.h"
24 #include "pycore_strhex.h" // _Py_strhex()
25 #include "pycore_typeobject.h" // _PyType_GetModuleState()
26
27 /*[clinic input]
28 module _md5
29 class MD5Type "MD5object *" "&PyType_Type"
30 [clinic start generated code]*/
31 /*[clinic end generated code: output=da39a3ee5e6b4b0d input=6e5261719957a912]*/
32
33 /* Some useful types */
34
35 #if SIZEOF_INT == 4
36 typedef unsigned int MD5_INT32; /* 32-bit integer */
37 typedef long long MD5_INT64; /* 64-bit integer */
38 #else
39 /* not defined. compilation will die. */
40 #endif
41
42 /* The MD5 block size and message digest sizes, in bytes */
43
44 #define MD5_BLOCKSIZE 64
45 #define MD5_DIGESTSIZE 16
46
47 #include "_hacl/Hacl_Hash_MD5.h"
48
49
50 typedef struct {
51 PyObject_HEAD
52 // Prevents undefined behavior via multiple threads entering the C API.
53 // The lock will be NULL before threaded access has been enabled.
54 PyThread_type_lock lock;
55 Hacl_Streaming_MD5_state *hash_state;
56 } MD5object;
57
58 #include "clinic/md5module.c.h"
59
60
61 typedef struct {
62 PyTypeObject* md5_type;
63 } MD5State;
64
65 static inline MD5State*
66 md5_get_state(PyObject *module)
67 {
68 void *state = PyModule_GetState(module);
69 assert(state != NULL);
70 return (MD5State *)state;
71 }
72
73 static MD5object *
74 newMD5object(MD5State * st)
75 {
76 MD5object *md5 = (MD5object *)PyObject_GC_New(MD5object, st->md5_type);
77 md5->lock = NULL;
78 PyObject_GC_Track(md5);
79 return md5;
80 }
81
82 /* Internal methods for a hash object */
83 static int
84 MD5_traverse(PyObject *ptr, visitproc visit, void *arg)
85 {
86 Py_VISIT(Py_TYPE(ptr));
87 return 0;
88 }
89
90 static void
91 MD5_dealloc(MD5object *ptr)
92 {
93 Hacl_Streaming_MD5_legacy_free(ptr->hash_state);
94 if (ptr->lock != NULL) {
95 PyThread_free_lock(ptr->lock);
96 }
97 PyTypeObject *tp = Py_TYPE(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 *
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_Streaming_MD5_legacy_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 *
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_Streaming_MD5_legacy_finish(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 *
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_Streaming_MD5_legacy_finish(self->hash_state, digest);
160 LEAVE_HASHLIB(self);
161 return _Py_strhex((const char*)digest, MD5_DIGESTSIZE);
162 }
163
164 static void update(Hacl_Streaming_MD5_state *state, uint8_t *buf, Py_ssize_t len) {
165 #if PY_SSIZE_T_MAX > UINT32_MAX
166 while (len > UINT32_MAX) {
167 Hacl_Streaming_MD5_legacy_update(state, buf, UINT32_MAX);
168 len -= UINT32_MAX;
169 buf += UINT32_MAX;
170 }
171 #endif
172 Hacl_Streaming_MD5_legacy_update(state, buf, (uint32_t) len);
173 }
174
175 /*[clinic input]
176 MD5Type.update
177
178 obj: object
179 /
180
181 Update this hash object's state with the provided string.
182 [clinic start generated code]*/
183
184 static PyObject *
185 MD5Type_update(MD5object *self, PyObject *obj)
186 /*[clinic end generated code: output=f6ad168416338423 input=6e1efcd9ecf17032]*/
187 {
188 Py_buffer buf;
189
190 GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
191
192 if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
193 self->lock = PyThread_allocate_lock();
194 }
195 if (self->lock != NULL) {
196 Py_BEGIN_ALLOW_THREADS
197 PyThread_acquire_lock(self->lock, 1);
198 update(self->hash_state, buf.buf, buf.len);
199 PyThread_release_lock(self->lock);
200 Py_END_ALLOW_THREADS
201 } else {
202 update(self->hash_state, buf.buf, buf.len);
203 }
204
205 PyBuffer_Release(&buf);
206 Py_RETURN_NONE;
207 }
208
209 static PyMethodDef MD5_methods[] = {
210 MD5TYPE_COPY_METHODDEF
211 MD5TYPE_DIGEST_METHODDEF
212 MD5TYPE_HEXDIGEST_METHODDEF
213 MD5TYPE_UPDATE_METHODDEF
214 {NULL, NULL} /* sentinel */
215 };
216
217 static PyObject *
218 MD5_get_block_size(PyObject *self, void *closure)
219 {
220 return PyLong_FromLong(MD5_BLOCKSIZE);
221 }
222
223 static PyObject *
224 MD5_get_name(PyObject *self, void *closure)
225 {
226 return PyUnicode_FromStringAndSize("md5", 3);
227 }
228
229 static PyObject *
230 md5_get_digest_size(PyObject *self, void *closure)
231 {
232 return PyLong_FromLong(MD5_DIGESTSIZE);
233 }
234
235 static PyGetSetDef MD5_getseters[] = {
236 {"block_size",
237 (getter)MD5_get_block_size, NULL,
238 NULL,
239 NULL},
240 {"name",
241 (getter)MD5_get_name, NULL,
242 NULL,
243 NULL},
244 {"digest_size",
245 (getter)md5_get_digest_size, NULL,
246 NULL,
247 NULL},
248 {NULL} /* Sentinel */
249 };
250
251 static PyType_Slot md5_type_slots[] = {
252 {Py_tp_dealloc, MD5_dealloc},
253 {Py_tp_methods, MD5_methods},
254 {Py_tp_getset, MD5_getseters},
255 {Py_tp_traverse, MD5_traverse},
256 {0,0}
257 };
258
259 static PyType_Spec md5_type_spec = {
260 .name = "_md5.md5",
261 .basicsize = sizeof(MD5object),
262 .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION |
263 Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC),
264 .slots = md5_type_slots
265 };
266
267 /* The single module-level function: new() */
268
269 /*[clinic input]
270 _md5.md5
271
272 string: object(c_default="NULL") = b''
273 *
274 usedforsecurity: bool = True
275
276 Return a new MD5 hash object; optionally initialized with a string.
277 [clinic start generated code]*/
278
279 static PyObject *
280 _md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity)
281 /*[clinic end generated code: output=587071f76254a4ac input=7a144a1905636985]*/
282 {
283 MD5object *new;
284 Py_buffer buf;
285
286 if (string)
287 GET_BUFFER_VIEW_OR_ERROUT(string, &buf);
288
289 MD5State *st = md5_get_state(module);
290 if ((new = newMD5object(st)) == NULL) {
291 if (string)
292 PyBuffer_Release(&buf);
293 return NULL;
294 }
295
296 new->hash_state = Hacl_Streaming_MD5_legacy_create_in();
297
298 if (PyErr_Occurred()) {
299 Py_DECREF(new);
300 if (string)
301 PyBuffer_Release(&buf);
302 return NULL;
303 }
304 if (string) {
305 if (buf.len >= HASHLIB_GIL_MINSIZE) {
306 /* We do not initialize self->lock here as this is the constructor
307 * where it is not yet possible to have concurrent access. */
308 Py_BEGIN_ALLOW_THREADS
309 update(new->hash_state, buf.buf, buf.len);
310 Py_END_ALLOW_THREADS
311 } else {
312 update(new->hash_state, buf.buf, buf.len);
313 }
314 PyBuffer_Release(&buf);
315 }
316
317 return (PyObject *)new;
318 }
319
320
321 /* List of functions exported by this module */
322
323 static struct PyMethodDef MD5_functions[] = {
324 _MD5_MD5_METHODDEF
325 {NULL, NULL} /* Sentinel */
326 };
327
328 static int
329 _md5_traverse(PyObject *module, visitproc visit, void *arg)
330 {
331 MD5State *state = md5_get_state(module);
332 Py_VISIT(state->md5_type);
333 return 0;
334 }
335
336 static int
337 _md5_clear(PyObject *module)
338 {
339 MD5State *state = md5_get_state(module);
340 Py_CLEAR(state->md5_type);
341 return 0;
342 }
343
344 static void
345 _md5_free(void *module)
346 {
347 _md5_clear((PyObject *)module);
348 }
349
350 /* Initialize this module. */
351 static int
352 md5_exec(PyObject *m)
353 {
354 MD5State *st = md5_get_state(m);
355
356 st->md5_type = (PyTypeObject *)PyType_FromModuleAndSpec(
357 m, &md5_type_spec, NULL);
358
359 if (st->md5_type == NULL) {
360 return -1;
361 }
362
363 Py_INCREF((PyObject *)st->md5_type);
364 if (PyModule_AddObject(m, "MD5Type", (PyObject *)st->md5_type) < 0) {
365 Py_DECREF(st->md5_type);
366 return -1;
367 }
368
369 return 0;
370 }
371
372 static PyModuleDef_Slot _md5_slots[] = {
373 {Py_mod_exec, md5_exec},
374 {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
375 {0, NULL}
376 };
377
378
379 static struct PyModuleDef _md5module = {
380 PyModuleDef_HEAD_INIT,
381 .m_name = "_md5",
382 .m_size = sizeof(MD5State),
383 .m_methods = MD5_functions,
384 .m_slots = _md5_slots,
385 .m_traverse = _md5_traverse,
386 .m_clear = _md5_clear,
387 .m_free = _md5_free,
388 };
389
390 PyMODINIT_FUNC
391 PyInit__md5(void)
392 {
393 return PyModuleDef_Init(&_md5module);
394 }