Commit 6d9a698615 for openssl.org

commit 6d9a69861562176d3c2dbdd115aa71357de89e51
Author: Timo Keller <tkeller@linux.ibm.com>
Date:   Fri Jun 26 11:29:26 2026 +0200

    s390x: Fix montgomery_multiplication_vectorized

    Introduce `reduce_twice_signed` that reduces from `(-2q,q)` to `[0,q)`.
    Fix `montgomery_multiplication_vectorized` in `ml_dsa_ntt_vec128.c`
    by calling `reduce_twice_signed` at the end of the computation ensuring
    that the result is in `[0,q)` and not only in `(-2q,q)` or `(-q,q)`.
    Do not call `reduce_once_signed` in `ossl_poly_ntt_mult_scalar_vec128`
    and at the end of `ossl_ml_dsa_poly_ntt_inverse_vec128` anymore as it is
    not necessary anymore after `reduce_twice_signed`.

    Without this fix, keygen, sign or verify might fail or produce wrong
    results.

    Signed-off-by: Timo Keller <tkeller@linux.ibm.com>

    Reviewed-by: Neil Horman <nhorman@openssl.org>
    Reviewed-by: Viktor Dukhovni <viktor@openssl.org>
    MergeDate: Mon Jul 13 14:45:11 2026
    (Merged from https://github.com/openssl/openssl/pull/31744)

diff --git a/crypto/ml_dsa/ml_dsa_ntt_vec128.c b/crypto/ml_dsa/ml_dsa_ntt_vec128.c
index 8176cbcdfb..54d59a9a08 100644
--- a/crypto/ml_dsa/ml_dsa_ntt_vec128.c
+++ b/crypto/ml_dsa/ml_dsa_ntt_vec128.c
@@ -309,15 +309,47 @@ static const int32_t neg_zetas_montgomery_twisted[256] = {
 static const vec_int32_t vec_q = { ML_DSA_Q, ML_DSA_Q, ML_DSA_Q, ML_DSA_Q };
 static const vec_int32_t vec_q_inv = { ML_DSA_Q_INV, ML_DSA_Q_INV, ML_DSA_Q_INV, ML_DSA_Q_INV };

+/*
+ * @brief Reduce a in (-q, q) to a mod q in [0, q).
+ *
+ * @param a in (-q, q)
+ * @returns a mod q in [0, q)
+ */
+static ossl_inline
+    vec_int32_t
+    reduce_once_signed(vec_int32_t a)
+{
+    /* mask is 11..11 when a is negative, else 0 */
+    vec_uint32_t mask = -(((vec_uint32_t)a) >> 31);
+    return a + (vec_int32_t)(mask & (vec_uint32_t)vec_q);
+}
+
+/*
+ * @brief Reduce a in (-2q, q) to a mod q in [0, q).
+ *
+ * @param a in (-2q, q)
+ * @returns a mod q in [0, q)
+ */
+static ossl_inline
+    vec_int32_t
+    reduce_twice_signed(vec_int32_t a)
+{
+    /* mask is 11..11 when a is negative, else 0 */
+    vec_uint32_t mask = -(((vec_uint32_t)a) >> 31);
+    /* b is in (-q, q) */
+    vec_int32_t b = a + (vec_int32_t)(mask & (vec_uint32_t)vec_q);
+    return reduce_once_signed(b);
+}
+
 /*
  * @brief Computes the Montgomery product of a and b.
  *        See [Seiler 2018, Algorithm 3].
  *
- * @param a is the first factor, assumed to be non-negative.
+ * @param a is the first factor, assumed to be in [0, q).
  * @param a_twist is (int32)((uint32)a * ML_DSA_Q_INV).
  * @param b is the second factor.
  * @returns The Montgomery product of a and b in the range
- *          -q+1..q-1.
+ *          [0, q).
  */

 static ossl_inline
@@ -329,22 +361,7 @@ static ossl_inline
     vec_int32_t c = (vec_int32_t)c_u;
     vec_int32_t z_high = vec_mulh((vec_int32_alias_t)a, (vec_int32_alias_t)b);
     vec_int32_t r = z_high - c;
-    return r;
-}
-
-/*
- * @brief Reduce a in (-q, q) to a mod q in [0, q-1].
- *
- * @param a in (-q, q)
- * @returns a mod q in [0, q-1]
- */
-static ossl_inline
-    vec_int32_t
-    reduce_once_signed(vec_int32_t a)
-{
-    /* mask is 11..11 when a is negative, else 0 */
-    vec_uint32_t mask = -(((vec_uint32_t)a) >> 31);
-    return a + (vec_int32_t)(mask & (vec_uint32_t)vec_q);
+    return reduce_twice_signed(r);
 }

 /*
@@ -376,9 +393,8 @@ void ossl_poly_ntt_mult_scalar_vec128(const POLY *lhs, const POLY *rhs, POLY *ou

     for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS / NUM_INT32_IN_VECTOR; i++) {
         vec_int32_t twist_vec = (vec_int32_t)((vec_uint32_t)lhs_vec_ptr[i] * (vec_uint32_t)vec_q_inv);
-        vec_int32_t result = montgomery_multiplication_vectorized(
+        out_vec_ptr[i] = montgomery_multiplication_vectorized(
             lhs_vec_ptr[i], twist_vec, rhs_vec_ptr[i]);
-        out_vec_ptr[i] = reduce_once_signed(result);
     }
 }

@@ -682,11 +698,10 @@ void ossl_ml_dsa_poly_ntt_inverse_vec128(POLY *p)
     }

     for (i = 0; i < ML_DSA_NUM_POLY_COEFFICIENTS / NUM_INT32_IN_VECTOR; i += 1) {
-        vec_int32_t coeff_i_vec = montgomery_multiplication_vectorized(
+        p_vec[i] = montgomery_multiplication_vectorized(
             vec_inverse_degree_montgomery,
             vec_inverse_degree_montgomery_twisted,
             p_vec[i]);
-        p_vec[i] = reduce_once_signed(coeff_i_vec);
     }
 }