@@ -648,50 +648,18 @@ void mld_polyt0_unpack(mld_poly *r, const uint8_t a[MLDSA_POLYT0_PACKEDBYTES])
648648 (1 << (MLDSA_D - 1 )) + 1 );
649649}
650650
651- /* Reference: explicitly checks the bound B to be <= (MLDSA_Q - 1) / 8).
652- * This is unnecessary as it's always a compile-time constant.
653- * We instead model it as a precondition.
654- * Checking the bound is performed using a conditional arguing
655- * that it is okay to leak which coefficient violates the bound (while the
656- * coefficient itself must remain secret).
657- * We instead perform everything in constant-time.
658- * Also it is sufficient to check that it is smaller than
659- * MLDSA_Q - REDUCE32_RANGE_MAX > (MLDSA_Q - 1) / 8).
660- */
661- MLD_INTERNAL_API
662- uint32_t mld_poly_chknorm (const mld_poly * a , int32_t B )
651+ MLD_STATIC_TESTABLE uint32_t mld_poly_chknorm_c (const mld_poly * a , int32_t B )
652+ __contract__ (
653+ requires (memory_no_alias (a , sizeof (mld_poly )))
654+ requires (0 <= B && B <= MLDSA_Q - REDUCE32_RANGE_MAX )
655+ requires (array_bound (a - > coeffs , 0 , MLDSA_N , - REDUCE32_RANGE_MAX , REDUCE32_RANGE_MAX ))
656+ ensures (return_value == 0 || return_value == 0xFFFFFFFF )
657+ ensures ((return_value == 0 ) == array_abs_bound (a - > coeffs , 0 , MLDSA_N , B ))
658+ )
663659{
664660 unsigned int i ;
665661 uint32_t t = 0 ;
666662 mld_assert_bound (a -> coeffs , MLDSA_N , - REDUCE32_RANGE_MAX , REDUCE32_RANGE_MAX );
667- #if defined(MLD_USE_NATIVE_POLY_CHKNORM )
668- {
669- /* TODO: proof */
670- int ret ;
671- int success ;
672- /* The native backend returns 0 if all coefficients are within the bound,
673- * 1 if at least one coefficient exceeds the bound, and
674- * -1 (MLD_NATIVE_FUNC_FALLBACK) if the platform does not have the
675- * required capabilities to run the native function.
676- */
677- ret = mld_poly_chknorm_native (a -> coeffs , B );
678-
679- success = (ret != MLD_NATIVE_FUNC_FALLBACK );
680- /* Constant-time: It would be fine to leak the return value of chknorm
681- * entirely (as it is fine to leak if any coefficient exceeded the bound or
682- * not). However, it is cleaner to perform declassification in sign.c.
683- * Hence, here we only declassify if the native function returned
684- * MLD_NATIVE_FUNC_FALLBACK or not (which solely depends on system
685- * capabilities).
686- */
687- MLD_CT_TESTING_DECLASSIFY (& success , sizeof (int ));
688- if (success )
689- {
690- /* Convert 0 / 1 to 0 / 0xFFFFFFFF here */
691- return 0U - (uint32_t )ret ;
692- }
693- }
694- #endif /* MLD_USE_NATIVE_POLY_CHKNORM */
695663 for (i = 0 ; i < MLDSA_N ; ++ i )
696664 __loop__ (
697665 invariant (i <= MLDSA_N )
@@ -722,6 +690,49 @@ uint32_t mld_poly_chknorm(const mld_poly *a, int32_t B)
722690 return t ;
723691}
724692
693+ /* Reference: explicitly checks the bound B to be <= (MLDSA_Q - 1) / 8).
694+ * This is unnecessary as it's always a compile-time constant.
695+ * We instead model it as a precondition.
696+ * Checking the bound is performed using a conditional arguing
697+ * that it is okay to leak which coefficient violates the bound (while the
698+ * coefficient itself must remain secret).
699+ * We instead perform everything in constant-time.
700+ * Also it is sufficient to check that it is smaller than
701+ * MLDSA_Q - REDUCE32_RANGE_MAX > (MLDSA_Q - 1) / 8).
702+ */
703+ MLD_INTERNAL_API
704+ uint32_t mld_poly_chknorm (const mld_poly * a , int32_t B )
705+ {
706+ #if defined(MLD_USE_NATIVE_POLY_CHKNORM )
707+ /* TODO: proof */
708+ int ret ;
709+ int success ;
710+ mld_assert_bound (a -> coeffs , MLDSA_N , - REDUCE32_RANGE_MAX , REDUCE32_RANGE_MAX );
711+ /* The native backend returns 0 if all coefficients are within the bound,
712+ * 1 if at least one coefficient exceeds the bound, and
713+ * -1 (MLD_NATIVE_FUNC_FALLBACK) if the platform does not have the
714+ * required capabilities to run the native function.
715+ */
716+ ret = mld_poly_chknorm_native (a -> coeffs , B );
717+
718+ success = (ret != MLD_NATIVE_FUNC_FALLBACK );
719+ /* Constant-time: It would be fine to leak the return value of chknorm
720+ * entirely (as it is fine to leak if any coefficient exceeded the bound or
721+ * not). However, it is cleaner to perform declassification in sign.c.
722+ * Hence, here we only declassify if the native function returned
723+ * MLD_NATIVE_FUNC_FALLBACK or not (which solely depends on system
724+ * capabilities).
725+ */
726+ MLD_CT_TESTING_DECLASSIFY (& success , sizeof (int ));
727+ if (success )
728+ {
729+ /* Convert 0 / 1 to 0 / 0xFFFFFFFF here */
730+ return 0U - (uint32_t )ret ;
731+ }
732+ #endif /* MLD_USE_NATIVE_POLY_CHKNORM */
733+ return mld_poly_chknorm_c (a , B );
734+ }
735+
725736#else /* !MLD_CONFIG_MULTILEVEL_NO_SHARED */
726737MLD_EMPTY_CU (mld_poly )
727738#endif /* MLD_CONFIG_MULTILEVEL_NO_SHARED */
0 commit comments