Skip to content

Commit ca73f3c

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 99efd4a commit ca73f3c

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

Modules/hmacmodule.c

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

204+
// --- HMAC static information table ------------------------------------------
205+
206+
static inline Py_uhash_t
207+
py_hmac_hinfo_ht_hash(const void *name)
208+
{
209+
return Py_HashBuffer(name, strlen((const char *)name));
210+
}
211+
212+
static inline int
213+
py_hmac_hinfo_ht_comp(const void *a, const void *b)
214+
{
215+
return strcmp((const char *)a, (const char *)b) == 0;
216+
}
217+
218+
static void
219+
py_hmac_hinfo_ht_free(void *hinfo)
220+
{
221+
py_hmac_hinfo *entry = (py_hmac_hinfo *)hinfo;
222+
assert(entry->display_name != NULL);
223+
if (--(entry->refcnt) == 0) {
224+
Py_CLEAR(entry->display_name);
225+
PyMem_Free(hinfo);
226+
}
227+
}
228+
229+
/*
230+
* Equivalent to table.setdefault(key, info).
231+
*
232+
* Return 1 if a new item has been created, 0 if 'key' is NULL or
233+
* an entry 'table[key]' existed, and -1 if a memory error occurs.
234+
*
235+
* To reduce memory footprint, 'info' may be a borrowed reference,
236+
* namely, multiple keys can be associated with the same 'info'.
237+
*
238+
* In particular, resources owned by 'info' must only be released
239+
* when a single key associated with 'info' remains.
240+
*/
241+
static int
242+
py_hmac_hinfo_ht_add(_Py_hashtable_t *table, const void *key, void *info)
243+
{
244+
if (key == NULL || _Py_hashtable_get_entry(table, key) != NULL) {
245+
return 0;
246+
}
247+
if (_Py_hashtable_set(table, key, info) < 0) {
248+
assert(!PyErr_Occurred());
249+
PyErr_NoMemory();
250+
return -1;
251+
}
252+
return 1;
253+
}
254+
255+
/*
256+
* Create a new hashtable from the static 'py_hmac_static_hinfo' object,
257+
* or set an exception and return NULL if an error occurs.
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+
PyMem_Free(value); \
293+
goto error; \
294+
} \
295+
else if (rc == 1) { \
296+
value->refcnt++; \
297+
} \
298+
} while (0)
299+
Py_HMAC_HINFO_LINK(e->name);
300+
Py_HMAC_HINFO_LINK(e->hashlib_name);
301+
#undef Py_HMAC_HINFO_LINK
302+
assert(value->refcnt > 0);
303+
assert(value->display_name == NULL);
304+
value->display_name = PyUnicode_FromString(
305+
/* display name is synchronized with hashlib's name */
306+
e->hashlib_name == NULL ? e->name : e->hashlib_name
307+
);
308+
if (value->display_name == NULL) {
309+
PyMem_Free(value);
310+
goto error;
311+
}
312+
}
313+
314+
return table;
315+
316+
error:
317+
_Py_hashtable_destroy(table);
318+
return NULL;
319+
}
320+
204321
// --- HMAC module initialization and finalization functions ------------------
205322

323+
static int
324+
hmacmodule_init_hash_info_table(hmacmodule_state *state)
325+
{
326+
// py_hmac_hinfo_ht_new() sets an exception on error
327+
state->hinfo_table = py_hmac_hinfo_ht_new();
328+
return state->hinfo_table == NULL ? -1 : 0;
329+
}
330+
206331
static int
207332
hmacmodule_exec(PyObject *module)
208333
{
209334
hmacmodule_state *state = get_hmacmodule_state(module);
335+
if (hmacmodule_init_hash_info_table(state) < 0) {
336+
return -1;
337+
}
210338
return 0;
211339
}
212340

@@ -222,6 +350,10 @@ static int
222350
hmacmodule_clear(PyObject *mod)
223351
{
224352
hmacmodule_state *state = get_hmacmodule_state(mod);
353+
if (state->hinfo_table != NULL) {
354+
_Py_hashtable_destroy(state->hinfo_table);
355+
state->hinfo_table = NULL;
356+
}
225357
return 0;
226358
}
227359

0 commit comments

Comments
 (0)