Skip to content

Commit 8b4372f

Browse files
committed
implement HMAC-HASH known hashes table
This commit implements the bits needed for allocating, freeing, modifying and introspecting a hash table that maps canonical HMAC hash algorithm names to static data.
1 parent 0d39305 commit 8b4372f

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed

Modules/hmacmodule.c

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,12 +202,144 @@ static PyMethodDef hmacmodule_methods[] = {
202202
{NULL, NULL, 0, NULL} /* sentinel */
203203
};
204204

205+
// --- HMAC static information table ------------------------------------------
206+
207+
static inline Py_uhash_t
208+
py_hmac_hinfo_ht_hash(const void *name)
209+
{
210+
return Py_HashBuffer(name, strlen((const char *)name));
211+
}
212+
213+
static inline int
214+
py_hmac_hinfo_ht_comp(const void *a, const void *b)
215+
{
216+
return strcmp((const char *)a, (const char *)b) == 0;
217+
}
218+
219+
static void
220+
py_hmac_hinfo_ht_free(void *hinfo)
221+
{
222+
py_hmac_hinfo *entry = (py_hmac_hinfo *)hinfo;
223+
assert(entry->display_name != NULL);
224+
if (--(entry->refcnt) == 0) {
225+
Py_CLEAR(entry->display_name);
226+
PyMem_Free(hinfo);
227+
}
228+
}
229+
230+
/*
231+
* Equivalent to table.setdefault(key, info).
232+
*
233+
* Return 1 if a new item has been created, 0 if 'key' is NULL or
234+
* an entry 'table[key]' existed, and -1 if a memory error occurs.
235+
*
236+
* To reduce memory footprint, 'info' may be a borrowed reference,
237+
* namely, multiple keys can be associated with the same 'info'.
238+
*
239+
* In particular, resources owned by 'info' must only be released
240+
* when a single key associated with 'info' remains.
241+
*/
242+
static inline int
243+
py_hmac_hinfo_ht_add(_Py_hashtable_t *table, const void *key, void *info)
244+
{
245+
if (key == NULL || _Py_hashtable_get_entry(table, key) != NULL) {
246+
return 0;
247+
}
248+
if (_Py_hashtable_set(table, key, info) < 0) {
249+
assert(!PyErr_Occurred());
250+
PyErr_NoMemory();
251+
return -1;
252+
}
253+
return 1;
254+
}
255+
256+
/*
257+
* Create a new hashtable from the static 'py_hmac_static_hinfo' object.
258+
*/
259+
static _Py_hashtable_t *
260+
py_hmac_hinfo_ht_new(void)
261+
{
262+
_Py_hashtable_t *table = _Py_hashtable_new_full(
263+
py_hmac_hinfo_ht_hash,
264+
py_hmac_hinfo_ht_comp,
265+
NULL,
266+
py_hmac_hinfo_ht_free,
267+
NULL
268+
);
269+
270+
if (table == NULL) {
271+
assert(!PyErr_Occurred());
272+
PyErr_NoMemory();
273+
return NULL;
274+
}
275+
276+
for (const py_hmac_hinfo *e = py_hmac_static_hinfo; e->name != NULL; e++) {
277+
assert(e->kind != Py_hmac_kind_hash_unknown);
278+
py_hmac_hinfo *value = PyMem_Malloc(sizeof(py_hmac_hinfo));
279+
if (value == NULL) {
280+
PyErr_NoMemory();
281+
goto error;
282+
}
283+
284+
memcpy(value, e, sizeof(py_hmac_hinfo));
285+
assert(value->display_name == NULL);
286+
value->refcnt = 0;
287+
288+
#define Py_HMAC_HINFO_LINK(KEY) \
289+
do { \
290+
int rc = py_hmac_hinfo_ht_add(table, KEY, value); \
291+
if (rc < 0) { \
292+
assert(PyErr_Occurred()); \
293+
PyMem_Free(value); \
294+
goto error; \
295+
} \
296+
else if (rc == 1) { \
297+
value->refcnt++; \
298+
} \
299+
} while (0)
300+
Py_HMAC_HINFO_LINK(e->name);
301+
Py_HMAC_HINFO_LINK(e->hashlib_name);
302+
#undef Py_HMAC_HINFO_LINK
303+
assert(value->refcnt > 0);
304+
assert(value->display_name == NULL);
305+
value->display_name = PyUnicode_FromString(
306+
/* display name is synchronized with hashlib's name */
307+
e->hashlib_name == NULL ? e->name : e->hashlib_name
308+
);
309+
if (value->display_name == NULL) {
310+
PyMem_Free(value);
311+
goto error;
312+
}
313+
}
314+
315+
return table;
316+
317+
error:
318+
_Py_hashtable_destroy(table);
319+
return NULL;
320+
}
321+
205322
// --- HMAC module initialization and finalization functions ------------------
206323

324+
static int
325+
hmacmodule_init_hash_info_table(hmacmodule_state *state)
326+
{
327+
// py_hmac_hinfo_ht_new() sets an exception on error
328+
state->hinfo_table = py_hmac_hinfo_ht_new();
329+
if (state->hinfo_table == NULL) {
330+
assert(PyErr_Occurred());
331+
return -1;
332+
}
333+
return 0;
334+
}
335+
207336
static int
208337
hmacmodule_exec(PyObject *module)
209338
{
210339
hmacmodule_state *state = get_hmacmodule_state(module);
340+
if (hmacmodule_init_hash_info_table(state) < 0) {
341+
return -1;
342+
}
211343
return 0;
212344
}
213345

@@ -223,6 +355,10 @@ static int
223355
hmacmodule_clear(PyObject *mod)
224356
{
225357
hmacmodule_state *state = get_hmacmodule_state(mod);
358+
if (state->hinfo_table != NULL) {
359+
_Py_hashtable_destroy(state->hinfo_table);
360+
state->hinfo_table = NULL;
361+
}
226362
return 0;
227363
}
228364

0 commit comments

Comments
 (0)