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