@@ -384,6 +384,10 @@ class _hmac.HMAC "HMACObject *" "clinic_state()->hmac_type"
384384//
385385// - Helpers with the "_hacl" prefix are thin wrappers around HACL* functions.
386386// Buffer lengths given as inputs should fit on 32-bit integers.
387+ //
388+ // - Helpers with the "hmac_" prefix act on HMAC objects and accept buffers
389+ // whose length fits on 32-bit or 64-bit integers (depending on the host
390+ // machine).
387391
388392/*
389393 * Handle the HACL* exit code.
@@ -588,6 +592,99 @@ has_uint32_t_buffer_length(const Py_buffer *buffer)
588592
589593// --- HMAC object ------------------------------------------------------------
590594
595+ /*
596+ * Update the HMAC object with the given buffer.
597+ *
598+ * This unconditionally acquires the lock on the HMAC object.
599+ *
600+ * On DEBUG builds, each update() call is verified.
601+ * On other builds, only the last update() call is verified.
602+ *
603+ * Return 0 on success and -1 on failure.
604+ */
605+ static int
606+ hmac_update_state_with_lock (HMACObject * self , uint8_t * buf , Py_ssize_t len )
607+ {
608+ int res = 0 ;
609+ Py_BEGIN_ALLOW_THREADS
610+ PyMutex_Lock (& self -> mutex ); // unconditionally acquire a lock
611+ Py_HMAC_HACL_UPDATE (self -> state , buf , len , self -> name , goto error );
612+ goto done ;
613+ #ifndef NDEBUG
614+ error :
615+ res = -1 ;
616+ #else
617+ Py_UNREACHABLE ();
618+ #endif
619+ done :
620+ PyMutex_Unlock (& self -> mutex );
621+ Py_END_ALLOW_THREADS
622+ return res ;
623+ }
624+
625+ /*
626+ * Update the HMAC object with the given buffer.
627+ *
628+ * This conditionally acquires the lock on the HMAC object.
629+ *
630+ * On DEBUG builds, each update() call is verified.
631+ * On other builds, only the last update() call is verified.
632+ *
633+ * Return 0 on success and -1 on failure.
634+ */
635+ static int
636+ hmac_update_state_cond_lock (HMACObject * self , uint8_t * buf , Py_ssize_t len )
637+ {
638+ ENTER_HASHLIB (self ); // conditionally acquire a lock
639+ Py_HMAC_HACL_UPDATE (self -> state , buf , len , self -> name , goto error );
640+ LEAVE_HASHLIB (self );
641+ return 0 ;
642+
643+ #ifndef NDEBUG
644+ error :
645+ LEAVE_HASHLIB (self );
646+ return -1 ;
647+ #else
648+ Py_UNREACHABLE ();
649+ #endif
650+ }
651+
652+ /*
653+ * Update the internal HMAC state with the given buffer.
654+ *
655+ * Return 0 on success and -1 on failure.
656+ */
657+ static inline int
658+ hmac_update_state (HMACObject * self , uint8_t * buf , Py_ssize_t len )
659+ {
660+ assert (buf != 0 );
661+ assert (len >= 0 );
662+ return len == 0
663+ ? 0 /* nothing to do */
664+ : len < HASHLIB_GIL_MINSIZE
665+ ? hmac_update_state_cond_lock (self , buf , len )
666+ : hmac_update_state_with_lock (self , buf , len );
667+ }
668+
669+ /*[clinic input]
670+ _hmac.HMAC.update
671+
672+ msg as msgobj: object
673+
674+ Update the HMAC object with the given message.
675+ [clinic start generated code]*/
676+
677+ static PyObject *
678+ _hmac_HMAC_update_impl (HMACObject * self , PyObject * msgobj )
679+ /*[clinic end generated code: output=962134ada5e55985 input=7c0ea830efb03367]*/
680+ {
681+ Py_buffer msg ;
682+ GET_BUFFER_VIEW_OR_ERROUT (msgobj , & msg );
683+ int rc = hmac_update_state (self , msg .buf , msg .len );
684+ PyBuffer_Release (& msg );
685+ return rc < 0 ? NULL : Py_None ;
686+ }
687+
591688/*[clinic input]
592689@getter
593690_hmac.HMAC.name
@@ -661,6 +758,7 @@ HMACObject_traverse(PyObject *self, visitproc visit, void *arg)
661758}
662759
663760static PyMethodDef HMACObject_methods [] = {
761+ _HMAC_HMAC_UPDATE_METHODDEF
664762 {NULL , NULL , 0 , NULL } /* sentinel */
665763};
666764
0 commit comments