Skip to content

Commit 617b793

Browse files
committed
implement HMAC.new() interface
We eventually implement the constructor for a HMAC object, making it materializable from Python.
1 parent 5c13316 commit 617b793

File tree

2 files changed

+249
-1
lines changed

2 files changed

+249
-1
lines changed

Modules/clinic/hmacmodule.c.h

Lines changed: 72 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/hmacmodule.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,24 @@ _hacl_convert_errno(hacl_errno_t code, PyObject *algorithm)
497497
}
498498
}
499499

500+
/*
501+
* Return a new HACL* internal state or return NULL on failure.
502+
*
503+
* An appropriate exception is set if the state cannot be created.
504+
*/
505+
static HACL_HMAC_state *
506+
_hacl_hmac_state_new(HMAC_Hash_Kind kind, uint8_t *key, uint32_t len)
507+
{
508+
assert(kind != Py_hmac_kind_hash_unknown);
509+
HACL_HMAC_state *state = NULL;
510+
hacl_errno_t retcode = Hacl_Streaming_HMAC_malloc_(kind, key, len, &state);
511+
if (_hacl_convert_errno(retcode, NULL) < 0) {
512+
assert(state == NULL);
513+
return NULL;
514+
}
515+
return state;
516+
}
517+
500518
/*
501519
* Free the HACL* internal state.
502520
*/
@@ -657,6 +675,164 @@ has_uint32_t_buffer_length(const Py_buffer *buffer)
657675

658676
// --- HMAC object ------------------------------------------------------------
659677

678+
/*
679+
* Use the HMAC information 'info' to populate the corresponding fields.
680+
*
681+
* The real 'kind' for BLAKE-2 is obtained once and depends on both static
682+
* capabilities (supported compiler flags) and runtime CPUID features.
683+
*/
684+
static void
685+
hmac_set_hinfo(hmacmodule_state *state,
686+
HMACObject *self, const py_hmac_hinfo *info)
687+
{
688+
assert(info->display_name != NULL);
689+
self->name = Py_NewRef(info->display_name);
690+
assert_is_static_hmac_hash_kind(info->kind);
691+
self->kind = narrow_hmac_hash_kind(state, info->kind);
692+
assert(info->block_size <= Py_hmac_hash_max_block_size);
693+
self->block_size = info->block_size;
694+
assert(info->digest_size <= Py_hmac_hash_max_digest_size);
695+
self->digest_size = info->digest_size;
696+
assert(info->api.compute != NULL);
697+
assert(info->api.compute_py != NULL);
698+
self->api = info->api;
699+
}
700+
701+
/*
702+
* Create initial HACL* internal state with the given key.
703+
*
704+
* This function MUST only be called by the HMAC object constructor
705+
* and after hmac_set_hinfo() has been called, lest the behaviour is
706+
* undefined.
707+
*
708+
* Return 0 on success and -1 on failure.
709+
*/
710+
static int
711+
hmac_new_initial_state(HMACObject *self, uint8_t *key, Py_ssize_t len)
712+
{
713+
assert(key != NULL);
714+
#ifdef Py_HMAC_SSIZE_LARGER_THAN_UINT32
715+
// Technically speaking, we could hash the key to make it small
716+
// but it would require to call the hash functions ourselves and
717+
// not rely on HACL* implementation anymore. As such, we explicitly
718+
// reject keys that do not fit on 32 bits until HACL* handles them.
719+
if (len > UINT32_MAX_AS_SSIZE_T) {
720+
PyErr_SetString(PyExc_OverflowError, INVALID_KEY_LENGTH);
721+
return -1;
722+
}
723+
#endif
724+
assert(self->kind != Py_hmac_kind_hash_unknown);
725+
// _hacl_hmac_state_new() may set an exception on error
726+
self->state = _hacl_hmac_state_new(self->kind, key, len);
727+
return self->state == NULL ? -1 : 0;
728+
}
729+
730+
/*
731+
* Feed initial data.
732+
*
733+
* This function MUST only be called by the HMAC object constructor
734+
* and after hmac_set_hinfo() and hmac_new_initial_state() have been
735+
* called, lest the behaviour is undefined.
736+
*
737+
* Return 0 on success and -1 on failure.
738+
*/
739+
static int
740+
hmac_feed_initial_data(HMACObject *self, uint8_t *msg, Py_ssize_t len)
741+
{
742+
assert(self->name != NULL);
743+
assert(self->state != NULL);
744+
if (len == 0) {
745+
// do nothing if the buffer is empty
746+
return 0;
747+
}
748+
749+
if (len < HASHLIB_GIL_MINSIZE) {
750+
Py_HMAC_HACL_UPDATE(self->state, msg, len, self->name, return -1);
751+
return 0;
752+
}
753+
754+
int res = 0;
755+
Py_BEGIN_ALLOW_THREADS
756+
Py_HMAC_HACL_UPDATE(self->state, msg, len, self->name, goto error);
757+
goto done;
758+
#ifndef NDEBUG
759+
error:
760+
res = -1;
761+
#else
762+
Py_UNREACHABLE();
763+
#endif
764+
done:
765+
Py_END_ALLOW_THREADS
766+
return res;
767+
}
768+
769+
/*[clinic input]
770+
_hmac.new
771+
772+
key as keyobj: object
773+
msg as msgobj: object(c_default="NULL") = None
774+
digestmod as hash_info_ref: object(c_default="NULL") = None
775+
776+
Return a new HMAC object.
777+
[clinic start generated code]*/
778+
779+
static PyObject *
780+
_hmac_new_impl(PyObject *module, PyObject *keyobj, PyObject *msgobj,
781+
PyObject *hash_info_ref)
782+
/*[clinic end generated code: output=7c7573a427d58758 input=92fc7c0a00707d42]*/
783+
{
784+
hmacmodule_state *state = get_hmacmodule_state(module);
785+
if (hash_info_ref == NULL) {
786+
PyErr_SetString(PyExc_TypeError,
787+
"new() missing 1 required argument 'digestmod'");
788+
return NULL;
789+
}
790+
791+
const py_hmac_hinfo *info = find_hash_info(state, hash_info_ref);
792+
if (info == NULL) {
793+
return NULL;
794+
}
795+
796+
HMACObject *self = PyObject_GC_New(HMACObject, state->hmac_type);
797+
if (self == NULL) {
798+
return NULL;
799+
}
800+
HASHLIB_INIT_MUTEX(self);
801+
hmac_set_hinfo(state, self, info);
802+
int rc;
803+
// Create the HACL* internal state with the given key.
804+
Py_buffer key;
805+
GET_BUFFER_VIEW_OR_ERROR(keyobj, &key, goto error_on_key);
806+
rc = hmac_new_initial_state(self, key.buf, key.len);
807+
PyBuffer_Release(&key);
808+
if (rc < 0) {
809+
goto error;
810+
}
811+
// Feed the internal state the initial message if any.
812+
if (msgobj != NULL && msgobj != Py_None) {
813+
Py_buffer msg;
814+
GET_BUFFER_VIEW_OR_ERROR(msgobj, &msg, goto error);
815+
rc = hmac_feed_initial_data(self, msg.buf, msg.len);
816+
PyBuffer_Release(&msg);
817+
#ifndef NDEBUG
818+
if (rc < 0) {
819+
goto error;
820+
}
821+
#else
822+
(void)rc;
823+
#endif
824+
}
825+
assert(rc == 0);
826+
PyObject_GC_Track(self);
827+
return (PyObject *)self;
828+
829+
error_on_key:
830+
self->state = NULL;
831+
error:
832+
Py_DECREF(self);
833+
return NULL;
834+
}
835+
660836
/*
661837
* Copy HMAC hash information from 'src' to 'out'.
662838
*/
@@ -1240,6 +1416,7 @@ _hmac_compute_blake2b_32_impl(PyObject *module, PyObject *key, PyObject *msg)
12401416
// --- HMAC module methods ----------------------------------------------------
12411417

12421418
static PyMethodDef hmacmodule_methods[] = {
1419+
_HMAC_NEW_METHODDEF
12431420
/* one-shot dispatcher */
12441421
_HMAC_COMPUTE_DIGEST_METHODDEF
12451422
/* one-shot methods */

0 commit comments

Comments
 (0)