#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mpi-internal.h"
#include "longlong.h"
#if defined(USE_ALGORITHM_SIMPLE_EXPONENTIATION)
void
_gcry_mpi_powm (gcry_mpi_t res,
gcry_mpi_t base, gcry_mpi_t expo, gcry_mpi_t mod)
{
mpi_ptr_t rp, ep, mp, bp;
mpi_size_t esize, msize, bsize, rsize;
int msign, bsign, rsign;
int esec, msec, bsec;
mpi_size_t size;
int mod_shift_cnt;
int negative_result;
mpi_ptr_t mp_marker = NULL;
mpi_ptr_t bp_marker = NULL;
mpi_ptr_t ep_marker = NULL;
mpi_ptr_t xp_marker = NULL;
unsigned int mp_nlimbs = 0;
unsigned int bp_nlimbs = 0;
unsigned int ep_nlimbs = 0;
unsigned int xp_nlimbs = 0;
mpi_ptr_t tspace = NULL;
mpi_size_t tsize = 0;
esize = expo->nlimbs;
msize = mod->nlimbs;
size = 2 * msize;
msign = mod->sign;
esec = mpi_is_secure(expo);
msec = mpi_is_secure(mod);
bsec = mpi_is_secure(base);
rp = res->d;
ep = expo->d;
MPN_NORMALIZE(ep, esize);
if (!msize)
_gcry_divide_by_zero();
if (!esize)
{
res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
if (res->nlimbs)
{
RESIZE_IF_NEEDED (res, 1);
rp = res->d;
rp[0] = 1;
}
res->sign = 0;
goto leave;
}
mp_nlimbs = msec? msize:0;
mp = mp_marker = mpi_alloc_limb_space(msize, msec);
count_leading_zeros (mod_shift_cnt, mod->d[msize-1]);
if (mod_shift_cnt)
_gcry_mpih_lshift (mp, mod->d, msize, mod_shift_cnt);
else
MPN_COPY( mp, mod->d, msize );
bsize = base->nlimbs;
bsign = base->sign;
if (bsize > msize)
{
bp_nlimbs = bsec ? (bsize + 1):0;
bp = bp_marker = mpi_alloc_limb_space( bsize + 1, bsec );
MPN_COPY ( bp, base->d, bsize );
_gcry_mpih_divrem( bp + msize, 0, bp, bsize, mp, msize );
bsize = msize;
MPN_NORMALIZE( bp, bsize );
}
else
bp = base->d;
if (!bsize)
{
res->nlimbs = 0;
res->sign = 0;
goto leave;
}
if ( rp == bp )
{
gcry_assert (!bp_marker);
bp_nlimbs = bsec? bsize:0;
bp = bp_marker = mpi_alloc_limb_space( bsize, bsec );
MPN_COPY(bp, rp, bsize);
}
if ( rp == ep )
{
ep_nlimbs = esec? esize:0;
ep = ep_marker = mpi_alloc_limb_space( esize, esec );
MPN_COPY(ep, rp, esize);
}
if ( rp == mp )
{
gcry_assert (!mp_marker);
mp_nlimbs = msec?msize:0;
mp = mp_marker = mpi_alloc_limb_space( msize, msec );
MPN_COPY(mp, rp, msize);
}
if (res->alloced < size)
{
mpi_resize (res, size);
rp = res->d;
}
MPN_COPY ( rp, bp, bsize );
rsize = bsize;
rsign = 0;
{
mpi_size_t i;
mpi_ptr_t xp;
int c;
mpi_limb_t e;
mpi_limb_t carry_limb;
struct karatsuba_ctx karactx;
struct gcry_mpi w, u;
xp_nlimbs = msec? size:0;
xp = xp_marker = mpi_alloc_limb_space( size, msec );
w.sign = u.sign = 0;
w.flags = u.flags = 0;
w.alloced = w.nlimbs = size;
u.alloced = u.nlimbs = size;
memset( &karactx, 0, sizeof karactx );
negative_result = (ep[0] & 1) && bsign;
i = esize - 1;
e = ep[i];
count_leading_zeros (c, e);
e = (e << c) << 1;
c = BITS_PER_MPI_LIMB - 1 - c;
for (;;)
{
while (c)
{
mpi_ptr_t tp;
mpi_size_t xsize;
if ( rsize < KARATSUBA_THRESHOLD )
_gcry_mpih_sqr_n_basecase( xp, rp, rsize );
else
{
if ( !tspace )
{
tsize = 2 * rsize;
tspace = mpi_alloc_limb_space( tsize, 0 );
}
else if ( tsize < (2*rsize) )
{
_gcry_mpi_free_limb_space (tspace, 0);
tsize = 2 * rsize;
tspace = mpi_alloc_limb_space (tsize, 0 );
}
_gcry_mpih_sqr_n (xp, rp, rsize, tspace);
}
xsize = 2 * rsize;
if ( xsize > msize )
{
_gcry_mpih_divrem(xp + msize, 0, xp, xsize, mp, msize);
xsize = msize;
}
tp = rp; rp = xp; xp = tp;
rsize = xsize;
if (esec || (mpi_limb_signed_t)e < 0)
{
if( bsize < KARATSUBA_THRESHOLD )
_gcry_mpih_mul ( xp, rp, rsize, bp, bsize );
else
_gcry_mpih_mul_karatsuba_case (xp, rp, rsize, bp, bsize,
&karactx);
xsize = rsize + bsize;
if ( xsize > msize )
{
_gcry_mpih_divrem(xp + msize, 0, xp, xsize, mp, msize);
xsize = msize;
}
}
w.d = rp;
u.d = xp;
mpi_set_cond (&w, &u, ((mpi_limb_signed_t)e < 0));
e <<= 1;
c--;
}
i--;
if ( i < 0 )
break;
e = ep[i];
c = BITS_PER_MPI_LIMB;
}
if ( mod_shift_cnt )
{
carry_limb = _gcry_mpih_lshift( res->d, rp, rsize, mod_shift_cnt);
rp = res->d;
if ( carry_limb )
{
rp[rsize] = carry_limb;
rsize++;
}
}
else if (res->d != rp)
{
MPN_COPY (res->d, rp, rsize);
rp = res->d;
}
if ( rsize >= msize )
{
_gcry_mpih_divrem(rp + msize, 0, rp, rsize, mp, msize);
rsize = msize;
}
if ( mod_shift_cnt )
_gcry_mpih_rshift( rp, rp, rsize, mod_shift_cnt);
MPN_NORMALIZE (rp, rsize);
_gcry_mpih_release_karatsuba_ctx (&karactx );
}
if ( negative_result && rsize )
{
if ( mod_shift_cnt )
_gcry_mpih_rshift( mp, mp, msize, mod_shift_cnt);
_gcry_mpih_sub( rp, mp, msize, rp, rsize);
rsize = msize;
rsign = msign;
MPN_NORMALIZE(rp, rsize);
}
gcry_assert (res->d == rp);
res->nlimbs = rsize;
res->sign = rsign;
leave:
if (mp_marker)
_gcry_mpi_free_limb_space( mp_marker, mp_nlimbs );
if (bp_marker)
_gcry_mpi_free_limb_space( bp_marker, bp_nlimbs );
if (ep_marker)
_gcry_mpi_free_limb_space( ep_marker, ep_nlimbs );
if (xp_marker)
_gcry_mpi_free_limb_space( xp_marker, xp_nlimbs );
if (tspace)
_gcry_mpi_free_limb_space( tspace, 0 );
}
#else
static void
mul_mod (mpi_ptr_t xp, mpi_size_t *xsize_p,
mpi_ptr_t rp, mpi_size_t rsize,
mpi_ptr_t sp, mpi_size_t ssize,
mpi_ptr_t mp, mpi_size_t msize,
struct karatsuba_ctx *karactx_p)
{
if( ssize < KARATSUBA_THRESHOLD )
_gcry_mpih_mul ( xp, rp, rsize, sp, ssize );
else
_gcry_mpih_mul_karatsuba_case (xp, rp, rsize, sp, ssize, karactx_p);
if (rsize + ssize > msize)
{
_gcry_mpih_divrem (xp + msize, 0, xp, rsize + ssize, mp, msize);
*xsize_p = msize;
}
else
*xsize_p = rsize + ssize;
}
#define SIZE_PRECOMP ((1 << (5 - 1)))
void
_gcry_mpi_powm (gcry_mpi_t res,
gcry_mpi_t base, gcry_mpi_t expo, gcry_mpi_t mod)
{
mpi_ptr_t rp, ep, mp, bp;
mpi_size_t esize, msize, bsize, rsize;
int msign, bsign, rsign;
int esec, msec, bsec;
mpi_size_t size;
int mod_shift_cnt;
int negative_result;
mpi_ptr_t mp_marker = NULL;
mpi_ptr_t bp_marker = NULL;
mpi_ptr_t ep_marker = NULL;
mpi_ptr_t xp_marker = NULL;
unsigned int mp_nlimbs = 0;
unsigned int bp_nlimbs = 0;
unsigned int ep_nlimbs = 0;
unsigned int xp_nlimbs = 0;
mpi_ptr_t precomp[SIZE_PRECOMP];
mpi_size_t precomp_size[SIZE_PRECOMP];
mpi_size_t W;
mpi_ptr_t base_u;
mpi_size_t base_u_size;
mpi_size_t max_u_size;
esize = expo->nlimbs;
msize = mod->nlimbs;
size = 2 * msize;
msign = mod->sign;
ep = expo->d;
MPN_NORMALIZE(ep, esize);
if (esize * BITS_PER_MPI_LIMB > 512)
W = 5;
else if (esize * BITS_PER_MPI_LIMB > 256)
W = 4;
else if (esize * BITS_PER_MPI_LIMB > 128)
W = 3;
else if (esize * BITS_PER_MPI_LIMB > 64)
W = 2;
else
W = 1;
esec = mpi_is_secure(expo);
msec = mpi_is_secure(mod);
bsec = mpi_is_secure(base);
rp = res->d;
if (!msize)
_gcry_divide_by_zero();
if (!esize)
{
res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
if (res->nlimbs)
{
RESIZE_IF_NEEDED (res, 1);
rp = res->d;
rp[0] = 1;
}
res->sign = 0;
goto leave;
}
mp_nlimbs = msec? msize:0;
mp = mp_marker = mpi_alloc_limb_space(msize, msec);
count_leading_zeros (mod_shift_cnt, mod->d[msize-1]);
if (mod_shift_cnt)
_gcry_mpih_lshift (mp, mod->d, msize, mod_shift_cnt);
else
MPN_COPY( mp, mod->d, msize );
bsize = base->nlimbs;
bsign = base->sign;
if (bsize > msize)
{
bp_nlimbs = bsec ? (bsize + 1):0;
bp = bp_marker = mpi_alloc_limb_space( bsize + 1, bsec );
MPN_COPY ( bp, base->d, bsize );
_gcry_mpih_divrem( bp + msize, 0, bp, bsize, mp, msize );
bsize = msize;
MPN_NORMALIZE( bp, bsize );
}
else
bp = base->d;
if (!bsize)
{
res->nlimbs = 0;
res->sign = 0;
goto leave;
}
if ( rp == bp )
{
gcry_assert (!bp_marker);
bp_nlimbs = bsec? bsize:0;
bp = bp_marker = mpi_alloc_limb_space( bsize, bsec );
MPN_COPY(bp, rp, bsize);
}
if ( rp == ep )
{
ep_nlimbs = esec? esize:0;
ep = ep_marker = mpi_alloc_limb_space( esize, esec );
MPN_COPY(ep, rp, esize);
}
if (res->alloced < size)
{
mpi_resize (res, size);
rp = res->d;
}
{
mpi_size_t i, j, k;
mpi_ptr_t xp;
mpi_size_t xsize;
int c;
mpi_limb_t e;
mpi_limb_t carry_limb;
struct karatsuba_ctx karactx;
mpi_ptr_t tp;
xp_nlimbs = msec? size:0;
xp = xp_marker = mpi_alloc_limb_space( size, msec );
memset( &karactx, 0, sizeof karactx );
negative_result = (ep[0] & 1) && bsign;
if (W > 1)
mul_mod (xp, &xsize, bp, bsize, bp, bsize, mp, msize, &karactx);
base_u = precomp[0] = mpi_alloc_limb_space (bsize, esec);
base_u_size = max_u_size = precomp_size[0] = bsize;
MPN_COPY (precomp[0], bp, bsize);
for (i = 1; i < (1 << (W - 1)); i++)
{
if (xsize >= base_u_size)
mul_mod (rp, &rsize, xp, xsize, base_u, base_u_size,
mp, msize, &karactx);
else
mul_mod (rp, &rsize, base_u, base_u_size, xp, xsize,
mp, msize, &karactx);
base_u = precomp[i] = mpi_alloc_limb_space (rsize, esec);
base_u_size = precomp_size[i] = rsize;
if (max_u_size < base_u_size)
max_u_size = base_u_size;
MPN_COPY (precomp[i], rp, rsize);
}
if (msize > max_u_size)
max_u_size = msize;
base_u = mpi_alloc_limb_space (max_u_size, esec);
MPN_ZERO (base_u, max_u_size);
i = esize - 1;
rsign = 0;
if (W == 1)
{
rsize = bsize;
}
else
{
rsize = msize;
MPN_ZERO (rp, rsize);
}
MPN_COPY ( rp, bp, bsize );
e = ep[i];
count_leading_zeros (c, e);
e = (e << c) << 1;
c = BITS_PER_MPI_LIMB - 1 - c;
j = 0;
for (;;)
if (e == 0)
{
j += c;
if ( --i < 0 )
break;
e = ep[i];
c = BITS_PER_MPI_LIMB;
}
else
{
int c0;
mpi_limb_t e0;
struct gcry_mpi w, u;
w.sign = u.sign = 0;
w.flags = u.flags = 0;
w.d = base_u;
count_leading_zeros (c0, e);
e = (e << c0);
c -= c0;
j += c0;
e0 = (e >> (BITS_PER_MPI_LIMB - W));
if (c >= W)
c0 = 0;
else
{
if ( --i < 0 )
{
e0 = (e >> (BITS_PER_MPI_LIMB - c));
j += c - W;
goto last_step;
}
else
{
c0 = c;
e = ep[i];
c = BITS_PER_MPI_LIMB;
e0 |= (e >> (BITS_PER_MPI_LIMB - (W - c0)));
}
}
e = e << (W - c0);
c -= (W - c0);
last_step:
count_trailing_zeros (c0, e0);
e0 = (e0 >> c0) >> 1;
for (j += W - c0; j >= 0; j--)
{
base_u_size = 0;
for (k = 0; k < (1<< (W - 1)); k++)
{
w.alloced = w.nlimbs = precomp_size[k];
u.alloced = u.nlimbs = precomp_size[k];
u.d = precomp[k];
mpi_set_cond (&w, &u, k == e0);
base_u_size |= ( precomp_size[k] & (0UL - (k == e0)) );
}
w.alloced = w.nlimbs = rsize;
u.alloced = u.nlimbs = rsize;
u.d = rp;
mpi_set_cond (&w, &u, j != 0);
base_u_size ^= ((base_u_size ^ rsize) & (0UL - (j != 0)));
mul_mod (xp, &xsize, rp, rsize, base_u, base_u_size,
mp, msize, &karactx);
tp = rp; rp = xp; xp = tp;
rsize = xsize;
}
j = c0;
if ( i < 0 )
break;
}
while (j--)
{
mul_mod (xp, &xsize, rp, rsize, rp, rsize, mp, msize, &karactx);
tp = rp; rp = xp; xp = tp;
rsize = xsize;
}
if ( mod_shift_cnt )
{
carry_limb = _gcry_mpih_lshift( res->d, rp, rsize, mod_shift_cnt);
rp = res->d;
if ( carry_limb )
{
rp[rsize] = carry_limb;
rsize++;
}
}
else if (res->d != rp)
{
MPN_COPY (res->d, rp, rsize);
rp = res->d;
}
if ( rsize >= msize )
{
_gcry_mpih_divrem(rp + msize, 0, rp, rsize, mp, msize);
rsize = msize;
}
if ( mod_shift_cnt )
_gcry_mpih_rshift( rp, rp, rsize, mod_shift_cnt);
MPN_NORMALIZE (rp, rsize);
_gcry_mpih_release_karatsuba_ctx (&karactx );
for (i = 0; i < (1 << (W - 1)); i++)
_gcry_mpi_free_limb_space( precomp[i], esec ? precomp_size[i] : 0 );
_gcry_mpi_free_limb_space (base_u, esec ? max_u_size : 0);
}
if ( negative_result && rsize )
{
if ( mod_shift_cnt )
_gcry_mpih_rshift( mp, mp, msize, mod_shift_cnt);
_gcry_mpih_sub( rp, mp, msize, rp, rsize);
rsize = msize;
rsign = msign;
MPN_NORMALIZE(rp, rsize);
}
gcry_assert (res->d == rp);
res->nlimbs = rsize;
res->sign = rsign;
leave:
if (mp_marker)
_gcry_mpi_free_limb_space( mp_marker, mp_nlimbs );
if (bp_marker)
_gcry_mpi_free_limb_space( bp_marker, bp_nlimbs );
if (ep_marker)
_gcry_mpi_free_limb_space( ep_marker, ep_nlimbs );
if (xp_marker)
_gcry_mpi_free_limb_space( xp_marker, xp_nlimbs );
}
#endif