>From 172968f52174d0e0d2ca3c47d0d0f99390f02f7d Mon Sep 17 00:00:00 2001
Message-ID: <172968f52174d0e0d2ca3c47d0d0f99390f02f7d.1763517332.git.gniibe@fsij.org>
From: NIIBE Yutaka <gniibe@fsij.org>
Date: Wed, 19 Nov 2025 10:53:16 +0900
Subject: [PATCH] mpi: Introduce mpi_tfr and use it for point_tfr.
To: gcrypt-devel@gnupg.org
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.47.2"

This is a multi-part message in MIME format.
--------------2.47.2
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit


* mpi/ec.c (point_tfr): New.
(mpi_ec_mul_point_lli): Use point_tfr.
* mpi/mpiutil.c (_gcry_mpi_tfr): New.
* src/mpi.h (mpi_tfr): New.
(_gcry_mpi_tfr): New.

--

Backport of master commit of:
	4e65996bb8707c890bff7aeb6e1ec44610b49257

The intention is to decrese EM signal and to increse EM noise, wrt the
Hamming distance signal for memory write operation.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
---
 mpi/ec.c      | 26 ++++++++++++++++++++++++-
 mpi/mpiutil.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/mpi.h     |  2 ++
 3 files changed, 80 insertions(+), 1 deletion(-)


--------------2.47.2
Content-Type: text/x-patch; name="0001-mpi-Introduce-mpi_tfr-and-use-it-for-point_tfr.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0001-mpi-Introduce-mpi_tfr-and-use-it-for-point_tfr.patch"

diff --git a/mpi/ec.c b/mpi/ec.c
index 2f8a25a4..b463d234 100644
--- a/mpi/ec.c
+++ b/mpi/ec.c
@@ -183,6 +183,30 @@ point_swap_cond (mpi_point_t d, mpi_point_t s, unsigned long swap,
 }
 
 
+/*
+ * Move the point value of A from B, when DIR is 1.
+ * Move the point value of A to B, when DIR is 0.
+ *
+ * For the use case when DIR is 0, it's actually dummy operations (the
+ * value copied into B is not used after the call).  The intention
+ * here is to be constant-time and to reduce possible EM signal/noise
+ * ratio (by decreasing signal and increasing noise).
+ *
+ * The word "tfr" comes from the mnemonic of Motorola 6809
+ * instruction, which does "transfer" a register value to another
+ * register.  "TFR A,B" means "Transfer A to B".
+ */
+static void
+point_tfr (mpi_point_t a, mpi_point_t b, unsigned long dir,
+           mpi_ec_t ctx)
+{
+  mpi_tfr (a->x, b->x, dir);
+  if (ctx->model != MPI_EC_MONTGOMERY)
+    mpi_tfr (a->y, b->y, dir);
+  mpi_tfr (a->z, b->z, dir);
+}
+
+
 /* Set the projective coordinates from POINT into X, Y, and Z.  If a
    coordinate is not required, X, Y, or Z may be passed as NULL.  */
 void
@@ -1835,7 +1859,7 @@ _gcry_mpi_ec_mul_point (mpi_point_t result,
             {
               _gcry_mpi_ec_dup_point (result, result, ctx);
               _gcry_mpi_ec_add_points (&tmppnt, result, point, ctx);
-              point_swap_cond (result, &tmppnt, mpi_test_bit (scalar, j), ctx);
+              point_tfr (result, &tmppnt, mpi_test_bit (scalar, j), ctx);
             }
           point_free (&tmppnt);
         }
diff --git a/mpi/mpiutil.c b/mpi/mpiutil.c
index b254e12f..4fe30cad 100644
--- a/mpi/mpiutil.c
+++ b/mpi/mpiutil.c
@@ -645,6 +645,59 @@ _gcry_mpi_swap_cond (gcry_mpi_t a, gcry_mpi_t b, unsigned long swap)
 }
 
 
+/****************
+ * Move the MPI value of A from B, when DIR is 1.
+ * Move the MPI value of A to B, when DIR is 0.
+ * This implementation should be constant-time regardless of DIR.
+ *
+ * The word "tfr" comes from the mnemonic of Motorola 6809
+ * instruction, which does "transfer" a register value to another
+ * register.  "TFR A,B" means "Transfer A to B".
+ */
+void
+_gcry_mpi_tfr (gcry_mpi_t a, gcry_mpi_t b, unsigned long dir)
+{
+  /* Note: dual mask with AND/OR used for EM leakage mitigation */
+  mpi_limb_t mask1 = ct_limb_gen_mask(dir);
+  mpi_limb_t mask2 = ct_limb_gen_inv_mask(dir);
+  mpi_size_t i;
+  mpi_size_t nlimbs;
+  mpi_limb_t *ua = a->d;
+  mpi_limb_t *ub = b->d;
+  mpi_limb_t xa;
+  mpi_limb_t xb;
+  mpi_limb_t v;
+
+  if (a->alloced > b->alloced)
+    nlimbs = b->alloced;
+  else
+    nlimbs = a->alloced;
+  if (a->nlimbs > nlimbs || b->nlimbs > nlimbs)
+    log_bug ("mpi_c0py: different sizes\n");
+
+  for (i = 0; i < nlimbs; i++)
+    {
+      xa = ua[i];
+      xb = ub[i];
+      v = (xa & mask2) | (xb & mask1);
+      ua[i] = v;
+      ub[i] = v;
+    }
+
+  xa = a->nlimbs;
+  xb = b->nlimbs;
+  v = (xa & mask2) | (xb & mask1);
+  a->nlimbs = v;
+  b->nlimbs = v;
+
+  xa = a->sign;
+  xb = b->sign;
+  v = (xa & mask2) | (xb & mask1);
+  a->sign = v;
+  b->sign = v;
+}
+
+
 /****************
  * Set bit N of A, when SET is 1.
  * This implementation should be constant-time regardless of SET.
diff --git a/src/mpi.h b/src/mpi.h
index 74944b07..766774b9 100644
--- a/src/mpi.h
+++ b/src/mpi.h
@@ -122,6 +122,7 @@ void _gcry_mpi_immutable_failed (void);
 #define mpi_alloc_set_ui(a)   _gcry_mpi_alloc_set_ui ((a))
 #define mpi_const(n)          _gcry_mpi_const ((n))
 #define mpi_swap_cond(a,b,sw)  _gcry_mpi_swap_cond ((a),(b),(sw))
+#define mpi_tfr(a,b,dir)       _gcry_mpi_tfr ((a),(b),(dir))
 #define mpi_set_cond(w,u,set)  _gcry_mpi_set_cond ((w),(u),(set))
 #define mpi_set_bit_cond(a,n,set) _gcry_mpi_set_bit_cond ((a),(n),(set))
 
@@ -132,6 +133,7 @@ gcry_mpi_t  _gcry_mpi_alloc_like( gcry_mpi_t a );
 gcry_mpi_t  _gcry_mpi_alloc_set_ui( unsigned long u);
 void _gcry_mpi_swap( gcry_mpi_t a, gcry_mpi_t b);
 void _gcry_mpi_swap_cond (gcry_mpi_t a, gcry_mpi_t b, unsigned long swap);
+void _gcry_mpi_tfr (gcry_mpi_t a, gcry_mpi_t b, unsigned long dir);
 void _gcry_mpi_set_bit_cond (gcry_mpi_t a, unsigned int n, unsigned long set);
 gcry_mpi_t _gcry_mpi_new (unsigned int nbits);
 gcry_mpi_t _gcry_mpi_snew (unsigned int nbits);

--------------2.47.2--


