@@ -170,6 +170,119 @@ typedef struct py_hmac_hacl_api {
170170#define Py_HMAC_SSIZE_LARGER_THAN_UINT32
171171#endif
172172
173+ /*
174+ * Assert that 'LEN' can be safely casted to uint32_t.
175+ *
176+ * The 'LEN' parameter should be convertible to Py_ssize_t.
177+ */
178+ #ifdef Py_HMAC_SSIZE_LARGER_THAN_UINT32
179+ #define Py_CHECK_HACL_UINT32_T_LENGTH (LEN ) \
180+ do { \
181+ assert((Py_ssize_t)(LEN) <= UINT32_MAX_AS_SSIZE_T); \
182+ } while (0)
183+ #else
184+ #define Py_CHECK_HACL_UINT32_T_LENGTH (LEN )
185+ #endif
186+
187+ /*
188+ * Call the HACL* HMAC-HASH update function on the given data.
189+ *
190+ * The magnitude of 'LEN' is not checked and thus 'LEN' must be
191+ * safely convertible to a uint32_t value.
192+ */
193+ #define Py_HMAC_HACL_UPDATE_CALL (HACL_STATE , BUF , LEN ) \
194+ Hacl_Streaming_HMAC_update(HACL_STATE, BUF, (uint32_t)(LEN))
195+
196+ /*
197+ * Call the HACL* HMAC-HASH update function on the given data.
198+ *
199+ * On DEBUG builds, the 'ERRACTION' statements are executed if
200+ * the update() call returned a non-successful HACL* exit code.
201+ *
202+ * The buffer 'BUF' and its length 'LEN' are left untouched.
203+ *
204+ * The formal signature of this macro is:
205+ *
206+ * (HACL_HMAC_state *, uint8_t *, uint32_t, PyObject *, (C statements))
207+ */
208+ #ifndef NDEBUG
209+ #define Py_HMAC_HACL_UPDATE_ONCE ( \
210+ HACL_STATE , BUF , LEN , \
211+ ALGORITHM , ERRACTION \
212+ ) \
213+ do { \
214+ Py_CHECK_HACL_UINT32_T_LENGTH(LEN); \
215+ hacl_errno_t code = Py_HMAC_HACL_UPDATE_CALL(HACL_STATE, BUF, LEN); \
216+ if (_hacl_convert_errno(code, (ALGORITHM)) < 0) { \
217+ ERRACTION; \
218+ } \
219+ } while (0)
220+ #else
221+ #define Py_HMAC_HACL_UPDATE_ONCE ( \
222+ HACL_STATE , BUF , LEN , \
223+ _ALGORITHM , _ERRACTION \
224+ ) \
225+ do { \
226+ (void)Py_HMAC_HACL_UPDATE_CALL(HACL_STATE, BUF, (LEN)); \
227+ } while (0)
228+ #endif
229+
230+ /*
231+ * Repetivively call the HACL* HMAC-HASH update function on the given
232+ * data until the buffer length 'LEN' is strictly less than UINT32_MAX.
233+ *
234+ * On builds with PY_SSIZE_T_MAX <= UINT32_MAX, this is a no-op.
235+ *
236+ * The buffer 'BUF' (resp. 'LEN') is advanced (resp. decremented)
237+ * by UINT32_MAX after each update. On DEBUG builds, each update()
238+ * call is verified and the 'ERRACTION' statements are executed if
239+ * a non-successful HACL* exit code is being returned.
240+ *
241+ * In particular, 'BUF' and 'LEN' must be variable names and not
242+ * expressions on their own.
243+ *
244+ * The formal signature of this macro is:
245+ *
246+ * (HACL_HMAC_state *, uint8_t *, C integer, PyObject *, (C statements))
247+ */
248+ #ifdef Py_HMAC_SSIZE_LARGER_THAN_UINT32
249+ #define Py_HMAC_HACL_UPDATE_LOOP ( \
250+ HACL_STATE , BUF , LEN , \
251+ ALGORITHM , ERRACTION \
252+ ) \
253+ do { \
254+ while ((Py_ssize_t)LEN > UINT32_MAX_AS_SSIZE_T) { \
255+ Py_HMAC_HACL_UPDATE_ONCE(HACL_STATE, BUF, UINT32_MAX, \
256+ ALGORITHM, ERRACTION); \
257+ BUF += UINT32_MAX; \
258+ LEN -= UINT32_MAX; \
259+ } \
260+ } while (0)
261+ #else
262+ #define Py_HMAC_HACL_UPDATE_LOOP ( \
263+ HACL_STATE , BUF , LEN , \
264+ _ALGORITHM , _ERRACTION \
265+ )
266+ #endif
267+
268+ /*
269+ * Perform the HMAC-HASH update() operation in a streaming fashion.
270+ *
271+ * The formal signature of this macro is:
272+ *
273+ * (HACL_HMAC_state *, uint8_t *, C integer, PyObject *, (C statements))
274+ */
275+ #define Py_HMAC_HACL_UPDATE ( \
276+ HACL_STATE , BUF , LEN , \
277+ ALGORITHM , ERRACTION \
278+ ) \
279+ do { \
280+ Py_HMAC_HACL_UPDATE_LOOP(HACL_STATE, BUF, LEN, \
281+ ALGORITHM, ERRACTION); \
282+ Py_HMAC_HACL_UPDATE_ONCE(HACL_STATE, BUF, LEN, \
283+ ALGORITHM, ERRACTION); \
284+ } while (0)
285+
173286/*
174287 * HMAC underlying hash function static information.
175288 */
@@ -266,6 +379,52 @@ class _hmac.HMAC "HMACObject *" "clinic_state()->hmac_type"
266379#undef clinic_state
267380
268381// --- Helpers ----------------------------------------------------------------
382+ //
383+ // The helpers have the following naming conventions:
384+ //
385+ // - Helpers with the "_hacl" prefix are thin wrappers around HACL* functions.
386+ // Buffer lengths given as inputs should fit on 32-bit integers.
387+
388+ /*
389+ * Handle the HACL* exit code.
390+ *
391+ * If 'code' represents a successful operation, this returns 0.
392+ * Otherwise, this sets an appropriate exception and returns -1.
393+ */
394+ static int
395+ _hacl_convert_errno (hacl_errno_t code , PyObject * algorithm )
396+ {
397+ switch (code ) {
398+ case Hacl_Streaming_Types_Success : {
399+ return 0 ;
400+ }
401+ case Hacl_Streaming_Types_InvalidAlgorithm : {
402+ // only makes sense if an algorithm is known at call time
403+ assert (algorithm != NULL );
404+ assert (PyUnicode_CheckExact (algorithm ));
405+ PyErr_Format (PyExc_ValueError , "invalid algorithm: %U" , algorithm );
406+ return -1 ;
407+ }
408+ case Hacl_Streaming_Types_InvalidLength : {
409+ PyErr_SetString (PyExc_ValueError , "invalid length" );
410+ return -1 ;
411+ }
412+ case Hacl_Streaming_Types_MaximumLengthExceeded : {
413+ PyErr_SetString (PyExc_OverflowError , "maximum length exceeded" );
414+ return -1 ;
415+ }
416+ case Hacl_Streaming_Types_OutOfMemory : {
417+ PyErr_NoMemory ();
418+ return -1 ;
419+ }
420+ default : {
421+ PyErr_Format (PyExc_RuntimeError ,
422+ "HACL* internal routine failed with error code: %d" ,
423+ code );
424+ return -1 ;
425+ }
426+ }
427+ }
269428
270429/*
271430 * Free the HACL* internal state.
0 commit comments