[PATCH 05/10] sntrup761: reduce freeze helpers without division
Jussi Kivilinna <[email protected]> Sun, 2 Aug 2026 12:55:10 +0300
| Newsgroups | gmane.comp.encryption.gpg.libgcrypt.devel |
|---|---|
| Message-ID | <[email protected]> |
* cipher/sntrup761.c (int32_divmod_uint14, int32_mod_uint14): Remove.
(F3_freeze, Fq_freeze): Bias input to non-negative range and compute
quotient with multiply-shift.
--
'F3_freeze' and 'Fq_freeze' reduced through 'int32_mod_uint14', which
handles run-time modulus and executes two hardware divisions per call.
Modulus is compile-time constant in both, so biasing input by multiple
of modulus allows open coding reduction as multiply-shift.
Benchmark on AMD Ryzen 9 9950X3D, SNTRUP761 usec/operation, quick
random disabled ('base' being state before this patch series):
| base before after speedup total
keygen | 37971.9 37971.9 9480.2 4.01x 4.01x
encap | 7355.8 7355.8 4468.2 1.65x 1.65x
decap | 12207.1 12207.1 3325.4 3.67x 3.67x
Signed-off-by: Jussi Kivilinna <[email protected]>
---
cipher/sntrup761.c | 53 ++++++++++++++++++----------------------------
1 file changed, 21 insertions(+), 32 deletions(-)
diff --git a/cipher/sntrup761.c b/cipher/sntrup761.c
index e9c7d707..11366f8f 100644
--- a/cipher/sntrup761.c
+++ b/cipher/sntrup761.c
@@ -208,36 +208,6 @@ uint32_mod_uint14 (uint32_t x, uint16_t m)
return r;
}
-/* from supercop-20201130/crypto_kem/sntrup761/ref/int32.c */
-
-static void
-int32_divmod_uint14 (int32_t * q, uint16_t * r, int32_t x, uint16_t m)
-{
- uint32_t uq, uq2;
- uint16_t ur, ur2;
- uint32_t mask;
-
- uint32_divmod_uint14 (&uq, &ur, 0x80000000 + (uint32_t) x, m);
- uint32_divmod_uint14 (&uq2, &ur2, 0x80000000, m);
- ur -= ur2;
- uq -= uq2;
- mask = ct_ulong_gen_mask(ur >> 15);
- ur += mask & m;
- uq += mask;
- *r = ur;
- *q = uq;
-}
-
-
-static uint16_t
-int32_mod_uint14 (int32_t x, uint16_t m)
-{
- int32_t q;
- uint16_t r;
- int32_divmod_uint14 (&q, &r, x, m);
- return r;
-}
-
/* from supercop-20201130/crypto_kem/sntrup761/ref/paramsmenu.h */
#define p 761
#define q 4591
@@ -402,7 +372,16 @@ typedef int8_t small;
static small
F3_freeze (int16_t x)
{
- return int32_mod_uint14 (x + 1, 3) - 1;
+ /* Bias by multiple of three so that reduction is done on non-negative
+ value. Multiply-shift quotient is exact for values below 2^17. */
+ static const u32 max_s16 = 0x7fff;
+ static const u32 max_s16_round_up_3 = (max_s16 + (3 - 1)) / 3 * 3;
+ static const u32 mod3_shift = 17;
+ static const u32 mod3_mul = (1U << mod3_shift) / 3 + 1;
+ u32 biased = x + 1 + max_s16_round_up_3;
+ u32 quot = (biased * mod3_mul) >> mod3_shift;
+
+ return (small)(biased - quot * 3) - 1;
}
/* ----- arithmetic mod q */
@@ -416,7 +395,17 @@ typedef int16_t Fq;
static Fq
Fq_freeze (int32_t x)
{
- return int32_mod_uint14 (x + q12, q) - q12;
+ /* Bias by multiple of q so that reduction is done on non-negative value.
+ Callers stay within +-2*q12*q12, where multiply-shift quotient is
+ exact. */
+ static const u32 max_fq = 2 * q12 * q12;
+ static const u32 max_fq_round_up_q = (max_fq + (q - 1)) / q * q;
+ static const u32 modq_shift = 36;
+ static const u32 modq_mul = (u32)(((u64)1 << modq_shift) / q + 1);
+ u32 biased = x + q12 + max_fq_round_up_q;
+ u32 quot = (u32)(((u64)biased * modq_mul) >> modq_shift);
+
+ return (Fq)(biased - quot * q) - q12;
}
static Fq
--
2.53.0