#include <tfm_private.h>
#ifdef TFM_TIMING_RESISTANT
static int _fp_exptmod(fp_int * G, fp_int * X, fp_int * P, fp_int * Y)
{
fp_int R[2];
fp_digit buf, mp;
int err, bitcnt, digidx, y;
if ((err = fp_montgomery_setup (P, &mp)) != FP_OKAY) {
return err;
}
fp_init(&R[0]);
fp_init(&R[1]);
fp_montgomery_calc_normalization (&R[0], P);
if (fp_cmp_mag(P, G) != FP_GT) {
fp_mod(G, P, &R[1]);
} else {
fp_copy(G, &R[1]);
}
fp_mulmod (&R[1], &R[0], P, &R[1]);
bitcnt = 1;
buf = 0;
digidx = X->used - 1;
for (;;) {
if (--bitcnt == 0) {
if (digidx == -1) {
break;
}
buf = X->dp[digidx--];
bitcnt = (int)DIGIT_BIT;
}
y = (fp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
buf <<= (fp_digit)1;
fp_mul(&R[0], &R[1], &R[y^1]); fp_montgomery_reduce(&R[y^1], P, mp);
fp_sqr(&R[y], &R[y]); fp_montgomery_reduce(&R[y], P, mp);
}
fp_montgomery_reduce(&R[0], P, mp);
fp_copy(&R[0], Y);
return FP_OKAY;
}
#else
static int _fp_exptmod(fp_int * G, fp_int * X, fp_int * P, fp_int * Y)
{
fp_int M[64], res;
fp_digit buf, mp;
int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
x = fp_count_bits (X);
if (x <= 21) {
winsize = 1;
} else if (x <= 36) {
winsize = 3;
} else if (x <= 140) {
winsize = 4;
} else if (x <= 450) {
winsize = 5;
} else {
winsize = 6;
}
memset(M, 0, sizeof(M));
if ((err = fp_montgomery_setup (P, &mp)) != FP_OKAY) {
return err;
}
fp_init(&res);
fp_montgomery_calc_normalization (&res, P);
if (fp_cmp_mag(P, G) != FP_GT) {
fp_mod(G, P, &M[1]);
} else {
fp_copy(G, &M[1]);
}
fp_mulmod (&M[1], &res, P, &M[1]);
fp_copy (&M[1], &M[1 << (winsize - 1)]);
for (x = 0; x < (winsize - 1); x++) {
fp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)]);
fp_montgomery_reduce (&M[1 << (winsize - 1)], P, mp);
}
for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
fp_mul(&M[x - 1], &M[1], &M[x]);
fp_montgomery_reduce(&M[x], P, mp);
}
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 = (fp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
buf <<= (fp_digit)1;
if (mode == 0 && y == 0) {
continue;
}
if (mode == 1 && y == 0) {
fp_sqr(&res, &res);
fp_montgomery_reduce(&res, P, mp);
continue;
}
bitbuf |= (y << (winsize - ++bitcpy));
mode = 2;
if (bitcpy == winsize) {
for (x = 0; x < winsize; x++) {
fp_sqr(&res, &res);
fp_montgomery_reduce(&res, P, mp);
}
fp_mul(&res, &M[bitbuf], &res);
fp_montgomery_reduce(&res, P, mp);
bitcpy = 0;
bitbuf = 0;
mode = 1;
}
}
if (mode == 2 && bitcpy > 0) {
for (x = 0; x < bitcpy; x++) {
fp_sqr(&res, &res);
fp_montgomery_reduce(&res, P, mp);
bitbuf <<= 1;
if ((bitbuf & (1 << winsize)) != 0) {
fp_mul(&res, &M[1], &res);
fp_montgomery_reduce(&res, P, mp);
}
}
}
fp_montgomery_reduce(&res, P, mp);
fp_copy (&res, Y);
return FP_OKAY;
}
#endif
int fp_exptmod(fp_int * G, fp_int * X, fp_int * P, fp_int * Y)
{
fp_int tmp;
int err;
#ifdef TFM_CHECK
if (P->used > (FP_SIZE/2)) {
return FP_VAL;
}
#endif
if (X->sign == FP_NEG) {
fp_copy(G, &tmp);
if ((err = fp_invmod(&tmp, P, &tmp)) != FP_OKAY) {
return err;
}
X->sign = FP_ZPOS;
err = _fp_exptmod(&tmp, X, P, Y);
if (X != Y) {
X->sign = FP_NEG;
}
return err;
} else {
return _fp_exptmod(G, X, P, Y);
}
}