Skip to content

Commit 807eb3c

Browse files
committed
implement HMAC.new() interface
We eventually implement the constructor for a HMAC object, making it materializable from Python.
1 parent 9b29015 commit 807eb3c

File tree

2 files changed

+250
-1
lines changed

2 files changed

+250
-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: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,24 @@ _hacl_convert_errno(hacl_errno_t code, PyObject *algorithm)
498498
}
499499
}
500500

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

661679
// --- HMAC object ------------------------------------------------------------
662680

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

12461423
static PyMethodDef hmacmodule_methods[] = {
1424+
_HMAC_NEW_METHODDEF
12471425
/* one-shot dispatcher */
12481426
_HMAC_COMPUTE_DIGEST_METHODDEF
12491427
/* one-shot methods */

0 commit comments

Comments
 (0)