#include "tommath_private.h"
#ifdef BN_MP_EXPTMOD_FAST_C
#ifdef MP_LOW_MEM
# define TAB_SIZE 32
#else
# define TAB_SIZE 256
#endif
int
mp_exptmod_fast(
const
mp_int *G,
const
mp_int *X,
const
mp_int *P, mp_int *Y,
int
redmode)
{
mp_int M[TAB_SIZE], res;
mp_digit buf, mp;
int
err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
int
(*redux)(mp_int *x,
const
mp_int *n, mp_digit rho);
x = mp_count_bits(X);
if
(x <= 7) {
winsize = 2;
}
else
if
(x <= 36) {
winsize = 3;
}
else
if
(x <= 140) {
winsize = 4;
}
else
if
(x <= 450) {
winsize = 5;
}
else
if
(x <= 1303) {
winsize = 6;
}
else
if
(x <= 3529) {
winsize = 7;
}
else
{
winsize = 8;
}
#ifdef MP_LOW_MEM
if
(winsize > 5) {
winsize = 5;
}
#endif
if
((err = mp_init_size(&M[1], P->alloc)) != MP_OKAY) {
return
err;
}
for
(x = 1<<(winsize-1); x < (1 << winsize); x++) {
if
((err = mp_init_size(&M[x], P->alloc)) != MP_OKAY) {
for
(y = 1<<(winsize-1); y < x; y++) {
mp_clear(&M[y]);
}
mp_clear(&M[1]);
return
err;
}
}
if
(redmode == 0) {
#ifdef BN_MP_MONTGOMERY_SETUP_C
if
((err = mp_montgomery_setup(P, &mp)) != MP_OKAY) {
goto
LBL_M;
}
#else
err = MP_VAL;
goto
LBL_M;
#endif
#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
if
((((P->used * 2) + 1) < (
int
)MP_WARRAY) &&
(P->used < (1 << ((CHAR_BIT *
sizeof
(mp_word)) - (2 * DIGIT_BIT))))) {
redux = fast_mp_montgomery_reduce;
}
else
#endif
{
#ifdef BN_MP_MONTGOMERY_REDUCE_C
redux = mp_montgomery_reduce;
#else
err = MP_VAL;
goto
LBL_M;
#endif
}
}
else
if
(redmode == 1) {
#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C)
mp_dr_setup(P, &mp);
redux = mp_dr_reduce;
#else
err = MP_VAL;
goto
LBL_M;
#endif
}
else
{
#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C)
if
((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) {
goto
LBL_M;
}
redux = mp_reduce_2k;
#else
err = MP_VAL;
goto
LBL_M;
#endif
}
if
((err = mp_init_size(&res, P->alloc)) != MP_OKAY) {
goto
LBL_M;
}
if
(redmode == 0) {
#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
if
((err = mp_montgomery_calc_normalization(&res, P)) != MP_OKAY) {
goto
LBL_RES;
}
if
((err = mp_mulmod(G, &res, P, &M[1])) != MP_OKAY) {
goto
LBL_RES;
}
#else
err = MP_VAL;
goto
LBL_RES;
#endif
}
else
{
mp_set(&res, 1uL);
if
((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
goto
LBL_RES;
}
}
if
((err = mp_copy(&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
goto
LBL_RES;
}
for
(x = 0; x < (winsize - 1); x++) {
if
((err = mp_sqr(&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) {
goto
LBL_RES;
}
if
((err = redux(&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) {
goto
LBL_RES;
}
}
for
(x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
if
((err = mp_mul(&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
goto
LBL_RES;
}
if
((err = redux(&M[x], P, mp)) != MP_OKAY) {
goto
LBL_RES;
}
}
mode = 0;
bitcnt = 1;
buf = 0;
digidx = X->used - 1;
bitcpy = 0;
bitbuf = 0;
for
(;;) {
if
(--bitcnt == 0) {
if
(digidx == -1) {
break
;
}
buf = X->dp[digidx--];
bitcnt = (
int
)DIGIT_BIT;
}
y = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
buf <<= (mp_digit)1;
if
((mode == 0) && (y == 0)) {
continue
;
}
if
((mode == 1) && (y == 0)) {
if
((err = mp_sqr(&res, &res)) != MP_OKAY) {
goto
LBL_RES;
}
if
((err = redux(&res, P, mp)) != MP_OKAY) {
goto
LBL_RES;
}
continue
;
}
bitbuf |= (y << (winsize - ++bitcpy));
mode = 2;
if
(bitcpy == winsize) {
for
(x = 0; x < winsize; x++) {
if
((err = mp_sqr(&res, &res)) != MP_OKAY) {
goto
LBL_RES;
}
if
((err = redux(&res, P, mp)) != MP_OKAY) {
goto
LBL_RES;
}
}
if
((err = mp_mul(&res, &M[bitbuf], &res)) != MP_OKAY) {
goto
LBL_RES;
}
if
((err = redux(&res, P, mp)) != MP_OKAY) {
goto
LBL_RES;
}
bitcpy = 0;
bitbuf = 0;
mode = 1;
}
}
if
((mode == 2) && (bitcpy > 0)) {
for
(x = 0; x < bitcpy; x++) {
if
((err = mp_sqr(&res, &res)) != MP_OKAY) {
goto
LBL_RES;
}
if
((err = redux(&res, P, mp)) != MP_OKAY) {
goto
LBL_RES;
}
bitbuf <<= 1;
if
((bitbuf & (1 << winsize)) != 0) {
if
((err = mp_mul(&res, &M[1], &res)) != MP_OKAY) {
goto
LBL_RES;
}
if
((err = redux(&res, P, mp)) != MP_OKAY) {
goto
LBL_RES;
}
}
}
}
if
(redmode == 0) {
if
((err = redux(&res, P, mp)) != MP_OKAY) {
goto
LBL_RES;
}
}
mp_exch(&res, Y);
err = MP_OKAY;
LBL_RES:
mp_clear(&res);
LBL_M:
mp_clear(&M[1]);
for
(x = 1<<(winsize-1); x < (1 << winsize); x++) {
mp_clear(&M[x]);
}
return
err;
}
#endif