Blame mpn/generic/sec_invert.c

Packit 5c3484
/* mpn_sec_invert
Packit 5c3484
Packit 5c3484
   Contributed to the GNU project by Niels Möller
Packit 5c3484
Packit 5c3484
Copyright 2013 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This file is part of the GNU MP Library.
Packit 5c3484
Packit 5c3484
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 5c3484
it under the terms of either:
Packit 5c3484
Packit 5c3484
  * the GNU Lesser General Public License as published by the Free
Packit 5c3484
    Software Foundation; either version 3 of the License, or (at your
Packit 5c3484
    option) any later version.
Packit 5c3484
Packit 5c3484
or
Packit 5c3484
Packit 5c3484
  * the GNU General Public License as published by the Free Software
Packit 5c3484
    Foundation; either version 2 of the License, or (at your option) any
Packit 5c3484
    later version.
Packit 5c3484
Packit 5c3484
or both in parallel, as here.
Packit 5c3484
Packit 5c3484
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 5c3484
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 5c3484
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 5c3484
for more details.
Packit 5c3484
Packit 5c3484
You should have received copies of the GNU General Public License and the
Packit 5c3484
GNU Lesser General Public License along with the GNU MP Library.  If not,
Packit 5c3484
see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
Packit 5c3484
#if 0
Packit 5c3484
/* Currently unused. Should be resurrected once mpn_cnd_neg is
Packit 5c3484
   advertised. */
Packit 5c3484
static mp_size_t
Packit 5c3484
mpn_cnd_neg_itch (mp_size_t n)
Packit 5c3484
{
Packit 5c3484
  return n;
Packit 5c3484
}
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
/* FIXME: Ought to return carry */
Packit 5c3484
static void
Packit 5c3484
mpn_cnd_neg (int cnd, mp_limb_t *rp, const mp_limb_t *ap, mp_size_t n,
Packit 5c3484
	     mp_ptr scratch)
Packit 5c3484
{
Packit 5c3484
  mpn_lshift (scratch, ap, n, 1);
Packit 5c3484
  mpn_cnd_sub_n (cnd, rp, ap, scratch, n);
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
static int
Packit 5c3484
mpn_sec_eq_ui (mp_srcptr ap, mp_size_t n, mp_limb_t b)
Packit 5c3484
{
Packit 5c3484
  mp_limb_t d;
Packit 5c3484
  ASSERT (n > 0);
Packit 5c3484
Packit 5c3484
  d = ap[0] ^ b;
Packit 5c3484
Packit 5c3484
  while (--n > 0)
Packit 5c3484
    d |= ap[n];
Packit 5c3484
Packit 5c3484
  return d == 0;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
mp_size_t
Packit 5c3484
mpn_sec_invert_itch (mp_size_t n)
Packit 5c3484
{
Packit 5c3484
  return 4*n;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Compute V <-- A^{-1} (mod M), in data-independent time. M must be
Packit 5c3484
   odd. Returns 1 on success, and 0 on failure (i.e., if gcd (A, m) !=
Packit 5c3484
   1). Inputs and outputs of size n, and no overlap allowed. The {ap,
Packit 5c3484
   n} area is destroyed. For arbitrary inputs, bit_size should be
Packit 5c3484
   2*n*GMP_NUMB_BITS, but if A or M are known to be smaller, e.g., if
Packit 5c3484
   M = 2^521 - 1 and A < M, bit_size can be any bound on the sum of
Packit 5c3484
   the bit sizes of A and M. */
Packit 5c3484
int
Packit 5c3484
mpn_sec_invert (mp_ptr vp, mp_ptr ap, mp_srcptr mp,
Packit 5c3484
		mp_size_t n, mp_bitcnt_t bit_size,
Packit 5c3484
		mp_ptr scratch)
Packit 5c3484
{
Packit 5c3484
  ASSERT (n > 0);
Packit 5c3484
  ASSERT (bit_size > 0);
Packit 5c3484
  ASSERT (mp[0] & 1);
Packit 5c3484
  ASSERT (! MPN_OVERLAP_P (ap, n, vp, n));
Packit 5c3484
#define bp (scratch + n)
Packit 5c3484
#define up (scratch + 2*n)
Packit 5c3484
#define m1hp (scratch + 3*n)
Packit 5c3484
Packit 5c3484
  /* Maintain
Packit 5c3484
Packit 5c3484
       a = u * orig_a (mod m)
Packit 5c3484
       b = v * orig_a (mod m)
Packit 5c3484
Packit 5c3484
     and b odd at all times. Initially,
Packit 5c3484
Packit 5c3484
       a = a_orig, u = 1
Packit 5c3484
       b = m,      v = 0
Packit 5c3484
     */
Packit 5c3484
Packit 5c3484
Packit 5c3484
  up[0] = 1;
Packit 5c3484
  mpn_zero (up+1, n - 1);
Packit 5c3484
  mpn_copyi (bp, mp, n);
Packit 5c3484
  mpn_zero (vp, n);
Packit 5c3484
Packit 5c3484
  ASSERT_CARRY (mpn_rshift (m1hp, mp, n, 1));
Packit 5c3484
  ASSERT_NOCARRY (mpn_sec_add_1 (m1hp, m1hp, n, 1, scratch));
Packit 5c3484
Packit 5c3484
  while (bit_size-- > 0)
Packit 5c3484
    {
Packit 5c3484
      mp_limb_t odd, swap, cy;
Packit 5c3484
Packit 5c3484
      /* Always maintain b odd. The logic of the iteration is as
Packit 5c3484
	 follows. For a, b:
Packit 5c3484
Packit 5c3484
	   odd = a & 1
Packit 5c3484
	   a -= odd * b
Packit 5c3484
	   if (underflow from a-b)
Packit 5c3484
	     {
Packit 5c3484
	       b += a, assigns old a
Packit 5c3484
	       a = B^n-a
Packit 5c3484
	     }
Packit 5c3484
Packit 5c3484
	   a /= 2
Packit 5c3484
Packit 5c3484
	 For u, v:
Packit 5c3484
Packit 5c3484
	   if (underflow from a - b)
Packit 5c3484
	     swap u, v
Packit 5c3484
	   u -= odd * v
Packit 5c3484
	   if (underflow from u - v)
Packit 5c3484
	     u += m
Packit 5c3484
Packit 5c3484
	   u /= 2
Packit 5c3484
	   if (a one bit was shifted out)
Packit 5c3484
	     u += (m+1)/2
Packit 5c3484
Packit 5c3484
	 As long as a > 0, the quantity
Packit 5c3484
Packit 5c3484
	   (bitsize of a) + (bitsize of b)
Packit 5c3484
Packit 5c3484
	 is reduced by at least one bit per iteration, hence after (bit_size of
Packit 5c3484
	 orig_a) + (bit_size of m) - 1 iterations we surely have a = 0. Then b
Packit 5c3484
	 = gcd(orig_a, m) and if b = 1 then also v = orig_a^{-1} (mod m).
Packit 5c3484
      */
Packit 5c3484
Packit 5c3484
      ASSERT (bp[0] & 1);
Packit 5c3484
      odd = ap[0] & 1;
Packit 5c3484
Packit 5c3484
      swap = mpn_cnd_sub_n (odd, ap, ap, bp, n);
Packit 5c3484
      mpn_cnd_add_n (swap, bp, bp, ap, n);
Packit 5c3484
      mpn_cnd_neg (swap, ap, ap, n, scratch);
Packit 5c3484
Packit 5c3484
      mpn_cnd_swap (swap, up, vp, n);
Packit 5c3484
      cy = mpn_cnd_sub_n (odd, up, up, vp, n);
Packit 5c3484
      cy -= mpn_cnd_add_n (cy, up, up, mp, n);
Packit 5c3484
      ASSERT (cy == 0);
Packit 5c3484
Packit 5c3484
      cy = mpn_rshift (ap, ap, n, 1);
Packit 5c3484
      ASSERT (cy == 0);
Packit 5c3484
      cy = mpn_rshift (up, up, n, 1);
Packit 5c3484
      cy = mpn_cnd_add_n (cy, up, up, m1hp, n);
Packit 5c3484
      ASSERT (cy == 0);
Packit 5c3484
    }
Packit 5c3484
  /* Should be all zeros, but check only extreme limbs */
Packit 5c3484
  ASSERT ( (ap[0] | ap[n-1]) == 0);
Packit 5c3484
  /* Check if indeed gcd == 1. */
Packit 5c3484
  return mpn_sec_eq_ui (bp, n, 1);
Packit 5c3484
#undef bp
Packit 5c3484
#undef up
Packit 5c3484
#undef m1hp
Packit 5c3484
}