#include <stdlib.h>
#include <string.h>
#include "bn_local.h"
#include "constant_time.h"
#include "err_local.h"
#define TABLE_SIZE 32
int
BN_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx)
{
BIGNUM *rr, *v;
int i;
int ret = 0;
if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
BNerror(ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return -1;
}
BN_CTX_start(ctx);
if ((v = BN_CTX_get(ctx)) == NULL)
goto err;
rr = r;
if (r == a || r == p)
rr = BN_CTX_get(ctx);
if (rr == NULL)
goto err;
if (!BN_one(rr))
goto err;
if (BN_is_odd(p)) {
if (!bn_copy(rr, a))
goto err;
}
if (!bn_copy(v, a))
goto err;
for (i = 1; i < BN_num_bits(p); i++) {
if (!BN_sqr(v, v, ctx))
goto err;
if (!BN_is_bit_set(p, i))
continue;
if (!BN_mul(rr, rr, v, ctx))
goto err;
}
if (!bn_copy(r, rr))
goto err;
ret = 1;
err:
BN_CTX_end(ctx);
return ret;
}
LCRYPTO_ALIAS(BN_exp);
int
BN_mod_exp_simple(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx)
{
int i, j, bits, wstart, wend, window, wvalue;
int start = 1;
BIGNUM *d, *q;
BIGNUM *val[TABLE_SIZE];
int ret = 0;
if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
BNerror(ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return -1;
}
if (r == m) {
BNerror(BN_R_INVALID_ARGUMENT);
return 0;
}
bits = BN_num_bits(p);
if (bits == 0) {
if (BN_abs_is_word(m, 1)) {
ret = 1;
BN_zero(r);
} else
ret = BN_one(r);
return ret;
}
BN_CTX_start(ctx);
if ((d = BN_CTX_get(ctx)) == NULL)
goto err;
if ((q = BN_CTX_get(ctx)) == NULL)
goto err;
if ((val[0] = BN_CTX_get(ctx)) == NULL)
goto err;
if (!BN_nnmod(val[0], a, m, ctx))
goto err;
if (BN_is_zero(val[0])) {
BN_zero(r);
goto done;
}
if (!bn_copy(q, p))
goto err;
window = BN_window_bits_for_exponent_size(bits);
if (window > 1) {
if (!BN_mod_mul(d, val[0], val[0], m, ctx))
goto err;
j = 1 << (window - 1);
for (i = 1; i < j; i++) {
if (((val[i] = BN_CTX_get(ctx)) == NULL) ||
!BN_mod_mul(val[i], val[i - 1], d,m, ctx))
goto err;
}
}
start = 1;
wvalue = 0;
wstart = bits - 1;
wend = 0;
if (!BN_one(r))
goto err;
for (;;) {
if (BN_is_bit_set(q, wstart) == 0) {
if (!start)
if (!BN_mod_mul(r, r, r, m, ctx))
goto err;
if (wstart == 0)
break;
wstart--;
continue;
}
j = wstart;
wvalue = 1;
wend = 0;
for (i = 1; i < window; i++) {
if (wstart - i < 0)
break;
if (BN_is_bit_set(q, wstart - i)) {
wvalue <<= (i - wend);
wvalue |= 1;
wend = i;
}
}
j = wend + 1;
if (!start)
for (i = 0; i < j; i++) {
if (!BN_mod_mul(r, r, r, m, ctx))
goto err;
}
if (!BN_mod_mul(r, r, val[wvalue >> 1], m, ctx))
goto err;
wstart -= wend + 1;
wvalue = 0;
start = 0;
if (wstart < 0)
break;
}
done:
ret = 1;
err:
BN_CTX_end(ctx);
return ret;
}
static int
MOD_EXP_CTIME_COPY_TO_PREBUF(const BIGNUM *b, int top, unsigned char *buf,
int idx, int window)
{
int i, j;
int width = 1 << window;
BN_ULONG *table = (BN_ULONG *)buf;
if (top > b->top)
top = b->top;
for (i = 0, j = idx; i < top; i++, j += width) {
table[j] = b->d[i];
}
return 1;
}
static int
MOD_EXP_CTIME_COPY_FROM_PREBUF(BIGNUM *b, int top, unsigned char *buf, int idx,
int window)
{
int i, j;
int width = 1 << window;
volatile BN_ULONG *table = (volatile BN_ULONG *)buf;
if (!bn_wexpand(b, top))
return 0;
if (window <= 3) {
for (i = 0; i < top; i++, table += width) {
BN_ULONG acc = 0;
for (j = 0; j < width; j++) {
acc |= table[j] &
((BN_ULONG)0 - (constant_time_eq_int(j,idx)&1));
}
b->d[i] = acc;
}
} else {
int xstride = 1 << (window - 2);
BN_ULONG y0, y1, y2, y3;
i = idx >> (window - 2);
idx &= xstride - 1;
y0 = (BN_ULONG)0 - (constant_time_eq_int(i,0)&1);
y1 = (BN_ULONG)0 - (constant_time_eq_int(i,1)&1);
y2 = (BN_ULONG)0 - (constant_time_eq_int(i,2)&1);
y3 = (BN_ULONG)0 - (constant_time_eq_int(i,3)&1);
for (i = 0; i < top; i++, table += width) {
BN_ULONG acc = 0;
for (j = 0; j < xstride; j++) {
acc |= ( (table[j + 0 * xstride] & y0) |
(table[j + 1 * xstride] & y1) |
(table[j + 2 * xstride] & y2) |
(table[j + 3 * xstride] & y3) )
& ((BN_ULONG)0 - (constant_time_eq_int(j,idx)&1));
}
b->d[i] = acc;
}
}
b->top = top;
bn_correct_top(b);
return 1;
}
#define MOD_EXP_CTIME_ALIGN(x_) \
((unsigned char*)(x_) + (MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH - (((size_t)(x_)) & (MOD_EXP_CTIME_MIN_CACHE_LINE_MASK))))
int
BN_mod_exp_mont_consttime(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p,
const BIGNUM *m, BN_CTX *ctx, BN_MONT_CTX *in_mont)
{
int i, bits, ret = 0, window, wvalue;
int top;
BN_MONT_CTX *mont = NULL;
int numPowers;
unsigned char *powerbufFree = NULL;
int powerbufLen = 0;
unsigned char *powerbuf = NULL;
BIGNUM tmp, am;
if (!BN_is_odd(m)) {
BNerror(BN_R_CALLED_WITH_EVEN_MODULUS);
return (0);
}
top = m->top;
bits = BN_num_bits(p);
if (bits == 0) {
if (BN_abs_is_word(m, 1)) {
ret = 1;
BN_zero(rr);
} else
ret = BN_one(rr);
return ret;
}
BN_CTX_start(ctx);
if ((mont = in_mont) == NULL)
mont = BN_MONT_CTX_create(m, ctx);
if (mont == NULL)
goto err;
window = BN_window_bits_for_ctime_exponent_size(bits);
#if defined(OPENSSL_BN_ASM_MONT5)
if (window == 6 && bits <= 1024)
window = 5;
#endif
numPowers = 1 << window;
powerbufLen = sizeof(m->d[0]) * (top * numPowers +
((2*top) > numPowers ? (2*top) : numPowers));
if ((powerbufFree = calloc(powerbufLen +
MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH, 1)) == NULL)
goto err;
powerbuf = MOD_EXP_CTIME_ALIGN(powerbufFree);
tmp.d = (BN_ULONG *)(powerbuf + sizeof(m->d[0]) * top * numPowers);
am.d = tmp.d + top;
tmp.top = am.top = 0;
tmp.dmax = am.dmax = top;
tmp.neg = am.neg = 0;
tmp.flags = am.flags = BN_FLG_STATIC_DATA;
#if 1
if (!BN_to_montgomery(&tmp, BN_value_one(), mont, ctx))
goto err;
#else
tmp.d[0] = (0 - m - >d[0]) & BN_MASK2;
for (i = 1; i < top; i++)
tmp.d[i] = (~m->d[i]) & BN_MASK2;
tmp.top = top;
#endif
if (!BN_nnmod(&am, a, m, ctx))
goto err;
if (!BN_to_montgomery(&am, &am, mont, ctx))
goto err;
#if defined(OPENSSL_BN_ASM_MONT5)
if (window == 5 && top > 1) {
void bn_mul_mont_gather5(BN_ULONG *rp, const BN_ULONG *ap,
const void *table, const BN_ULONG *np,
const BN_ULONG *n0, int num, int power);
void bn_scatter5(const BN_ULONG *inp, size_t num,
void *table, size_t power);
void bn_gather5(BN_ULONG *out, size_t num,
void *table, size_t power);
BN_ULONG *np = mont->N.d, *n0 = mont->n0;
for (i = am.top; i < top; i++)
am.d[i] = 0;
for (i = tmp.top; i < top; i++)
tmp.d[i] = 0;
bn_scatter5(tmp.d, top, powerbuf, 0);
bn_scatter5(am.d, am.top, powerbuf, 1);
bn_mul_mont(tmp.d, am.d, am.d, np, n0, top);
bn_scatter5(tmp.d, top, powerbuf, 2);
#if 0
for (i = 3; i < 32; i++) {
bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np,
n0, top, i - 1);
bn_scatter5(tmp.d, top, powerbuf, i);
}
#else
for (i = 4; i < 32; i*=2) {
bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
bn_scatter5(tmp.d, top, powerbuf, i);
}
for (i = 3; i < 8; i += 2) {
int j;
bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np,
n0, top, i - 1);
bn_scatter5(tmp.d, top, powerbuf, i);
for (j = 2 * i; j < 32; j *= 2) {
bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
bn_scatter5(tmp.d, top, powerbuf, j);
}
}
for (; i < 16; i += 2) {
bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np,
n0, top, i - 1);
bn_scatter5(tmp.d, top, powerbuf, i);
bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
bn_scatter5(tmp.d, top, powerbuf, 2*i);
}
for (; i < 32; i += 2) {
bn_mul_mont_gather5(tmp.d, am.d, powerbuf, np,
n0, top, i - 1);
bn_scatter5(tmp.d, top, powerbuf, i);
}
#endif
bits--;
for (wvalue = 0, i = bits % 5; i >= 0; i--, bits--)
wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
bn_gather5(tmp.d, top, powerbuf, wvalue);
while (bits >= 0) {
for (wvalue = 0, i = 0; i < 5; i++, bits--)
wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
bn_mul_mont(tmp.d, tmp.d, tmp.d, np, n0, top);
bn_mul_mont_gather5(tmp.d, tmp.d, powerbuf, np, n0, top, wvalue);
}
tmp.top = top;
bn_correct_top(&tmp);
} else
#endif
{
if (!MOD_EXP_CTIME_COPY_TO_PREBUF(&tmp, top, powerbuf, 0,
window))
goto err;
if (!MOD_EXP_CTIME_COPY_TO_PREBUF(&am, top, powerbuf, 1,
window))
goto err;
if (window > 1) {
if (!BN_mod_mul_montgomery(&tmp, &am, &am, mont, ctx))
goto err;
if (!MOD_EXP_CTIME_COPY_TO_PREBUF(&tmp, top, powerbuf,
2, window))
goto err;
for (i = 3; i < numPowers; i++) {
if (!BN_mod_mul_montgomery(&tmp, &am, &tmp,
mont, ctx))
goto err;
if (!MOD_EXP_CTIME_COPY_TO_PREBUF(&tmp, top,
powerbuf, i, window))
goto err;
}
}
bits--;
for (wvalue = 0, i = bits % window; i >= 0; i--, bits--)
wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
if (!MOD_EXP_CTIME_COPY_FROM_PREBUF(&tmp, top, powerbuf,
wvalue, window))
goto err;
while (bits >= 0) {
wvalue = 0;
for (i = 0; i < window; i++, bits--) {
if (!BN_mod_mul_montgomery(&tmp, &tmp, &tmp,
mont, ctx))
goto err;
wvalue = (wvalue << 1) + BN_is_bit_set(p, bits);
}
if (!MOD_EXP_CTIME_COPY_FROM_PREBUF(&am, top, powerbuf,
wvalue, window))
goto err;
if (!BN_mod_mul_montgomery(&tmp, &tmp, &am, mont, ctx))
goto err;
}
}
if (!BN_from_montgomery(rr, &tmp, mont, ctx))
goto err;
ret = 1;
err:
if (mont != in_mont)
BN_MONT_CTX_free(mont);
BN_CTX_end(ctx);
freezero(powerbufFree, powerbufLen + MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH);
return ret;
}
LCRYPTO_ALIAS(BN_mod_exp_mont_consttime);
static int
BN_mod_exp_mont_internal(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx, BN_MONT_CTX *in_mont, int ct)
{
int i, j, bits, ret = 0, wstart, wend, window, wvalue;
int start = 1;
BIGNUM *d, *r;
const BIGNUM *aa;
BIGNUM *val[TABLE_SIZE];
BN_MONT_CTX *mont = NULL;
if (ct) {
return BN_mod_exp_mont_consttime(rr, a, p, m, ctx, in_mont);
}
if (!BN_is_odd(m)) {
BNerror(BN_R_CALLED_WITH_EVEN_MODULUS);
return (0);
}
bits = BN_num_bits(p);
if (bits == 0) {
if (BN_abs_is_word(m, 1)) {
ret = 1;
BN_zero(rr);
} else
ret = BN_one(rr);
return ret;
}
BN_CTX_start(ctx);
if ((d = BN_CTX_get(ctx)) == NULL)
goto err;
if ((r = BN_CTX_get(ctx)) == NULL)
goto err;
if ((val[0] = BN_CTX_get(ctx)) == NULL)
goto err;
if ((mont = in_mont) == NULL)
mont = BN_MONT_CTX_create(m, ctx);
if (mont == NULL)
goto err;
if (!BN_nnmod(val[0], a,m, ctx))
goto err;
aa = val[0];
if (BN_is_zero(aa)) {
BN_zero(rr);
ret = 1;
goto err;
}
if (!BN_to_montgomery(val[0], aa, mont, ctx))
goto err;
window = BN_window_bits_for_exponent_size(bits);
if (window > 1) {
if (!BN_mod_mul_montgomery(d, val[0], val[0], mont, ctx))
goto err;
j = 1 << (window - 1);
for (i = 1; i < j; i++) {
if (((val[i] = BN_CTX_get(ctx)) == NULL) ||
!BN_mod_mul_montgomery(val[i], val[i - 1],
d, mont, ctx))
goto err;
}
}
start = 1;
wvalue = 0;
wstart = bits - 1;
wend = 0;
if (!BN_to_montgomery(r, BN_value_one(), mont, ctx))
goto err;
for (;;) {
if (BN_is_bit_set(p, wstart) == 0) {
if (!start) {
if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
goto err;
}
if (wstart == 0)
break;
wstart--;
continue;
}
j = wstart;
wvalue = 1;
wend = 0;
for (i = 1; i < window; i++) {
if (wstart - i < 0)
break;
if (BN_is_bit_set(p, wstart - i)) {
wvalue <<= (i - wend);
wvalue |= 1;
wend = i;
}
}
j = wend + 1;
if (!start)
for (i = 0; i < j; i++) {
if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
goto err;
}
if (!BN_mod_mul_montgomery(r, r, val[wvalue >> 1], mont, ctx))
goto err;
wstart -= wend + 1;
wvalue = 0;
start = 0;
if (wstart < 0)
break;
}
if (!BN_from_montgomery(rr, r,mont, ctx))
goto err;
ret = 1;
err:
if (mont != in_mont)
BN_MONT_CTX_free(mont);
BN_CTX_end(ctx);
return ret;
}
int
BN_mod_exp_mont(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx, BN_MONT_CTX *in_mont)
{
return BN_mod_exp_mont_internal(rr, a, p, m, ctx, in_mont,
(BN_get_flags(p, BN_FLG_CONSTTIME) != 0));
}
LCRYPTO_ALIAS(BN_mod_exp_mont);
int
BN_mod_exp_mont_ct(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx, BN_MONT_CTX *in_mont)
{
return BN_mod_exp_mont_internal(rr, a, p, m, ctx, in_mont, 1);
}
int
BN_mod_exp_mont_nonct(BIGNUM *rr, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx, BN_MONT_CTX *in_mont)
{
return BN_mod_exp_mont_internal(rr, a, p, m, ctx, in_mont, 0);
}
int
BN_mod_exp_mont_word(BIGNUM *rr, BN_ULONG a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx, BN_MONT_CTX *in_mont)
{
BN_MONT_CTX *mont = NULL;
int b, bits, ret = 0;
int r_is_one;
BN_ULONG w, next_w;
BIGNUM *d, *r, *t;
BIGNUM *swap_tmp;
#define BN_MOD_MUL_WORD(r, w, m) \
(BN_mul_word(r, (w)) && \
( \
(BN_mod_ct(t, r, m, ctx) && (swap_tmp = r, r = t, t = swap_tmp, 1))))
#define BN_TO_MONTGOMERY_WORD(r, w, mont) \
(BN_set_word(r, (w)) && BN_to_montgomery(r, r, (mont), ctx))
if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
BNerror(ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return -1;
}
if (!BN_is_odd(m)) {
BNerror(BN_R_CALLED_WITH_EVEN_MODULUS);
return (0);
}
if (m->top == 1)
a %= m->d[0];
bits = BN_num_bits(p);
if (bits == 0) {
if (BN_abs_is_word(m, 1)) {
ret = 1;
BN_zero(rr);
} else
ret = BN_one(rr);
return ret;
}
if (a == 0) {
BN_zero(rr);
ret = 1;
return ret;
}
BN_CTX_start(ctx);
if ((d = BN_CTX_get(ctx)) == NULL)
goto err;
if ((r = BN_CTX_get(ctx)) == NULL)
goto err;
if ((t = BN_CTX_get(ctx)) == NULL)
goto err;
if ((mont = in_mont) == NULL)
mont = BN_MONT_CTX_create(m, ctx);
if (mont == NULL)
goto err;
r_is_one = 1;
w = a;
for (b = bits - 2; b >= 0; b--) {
next_w = w * w;
if ((next_w / w) != w)
{
if (r_is_one) {
if (!BN_TO_MONTGOMERY_WORD(r, w, mont))
goto err;
r_is_one = 0;
} else {
if (!BN_MOD_MUL_WORD(r, w, m))
goto err;
}
next_w = 1;
}
w = next_w;
if (!r_is_one) {
if (!BN_mod_mul_montgomery(r, r, r, mont, ctx))
goto err;
}
if (BN_is_bit_set(p, b)) {
next_w = w * a;
if ((next_w / a) != w)
{
if (r_is_one) {
if (!BN_TO_MONTGOMERY_WORD(r, w, mont))
goto err;
r_is_one = 0;
} else {
if (!BN_MOD_MUL_WORD(r, w, m))
goto err;
}
next_w = a;
}
w = next_w;
}
}
if (w != 1) {
if (r_is_one) {
if (!BN_TO_MONTGOMERY_WORD(r, w, mont))
goto err;
r_is_one = 0;
} else {
if (!BN_MOD_MUL_WORD(r, w, m))
goto err;
}
}
if (r_is_one)
{
if (!BN_one(rr))
goto err;
} else {
if (!BN_from_montgomery(rr, r, mont, ctx))
goto err;
}
ret = 1;
err:
if (mont != in_mont)
BN_MONT_CTX_free(mont);
BN_CTX_end(ctx);
return ret;
}
int
BN_mod_exp_reciprocal(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx)
{
int i, j, bits, wstart, wend, window, wvalue;
int start = 1;
BIGNUM *aa, *q;
BIGNUM *val[TABLE_SIZE];
BN_RECP_CTX *recp = NULL;
int ret = 0;
if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
BNerror(ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return -1;
}
bits = BN_num_bits(p);
if (bits == 0) {
if (BN_abs_is_word(m, 1)) {
ret = 1;
BN_zero(r);
} else
ret = BN_one(r);
return ret;
}
BN_CTX_start(ctx);
if ((aa = BN_CTX_get(ctx)) == NULL)
goto err;
if ((q = BN_CTX_get(ctx)) == NULL)
goto err;
if ((val[0] = BN_CTX_get(ctx)) == NULL)
goto err;
if ((recp = BN_RECP_CTX_create(m)) == NULL)
goto err;
if (!BN_nnmod(val[0], a, m, ctx))
goto err;
if (BN_is_zero(val[0])) {
BN_zero(r);
goto done;
}
if (!bn_copy(q, p))
goto err;
window = BN_window_bits_for_exponent_size(bits);
if (window > 1) {
if (!BN_mod_sqr_reciprocal(aa, val[0], recp, ctx))
goto err;
j = 1 << (window - 1);
for (i = 1; i < j; i++) {
if (((val[i] = BN_CTX_get(ctx)) == NULL) ||
!BN_mod_mul_reciprocal(val[i], val[i - 1],
aa, recp, ctx))
goto err;
}
}
start = 1;
wvalue = 0;
wstart = bits - 1;
wend = 0;
if (!BN_one(r))
goto err;
for (;;) {
if (BN_is_bit_set(q, wstart) == 0) {
if (!start)
if (!BN_mod_sqr_reciprocal(r, r, recp, ctx))
goto err;
if (wstart == 0)
break;
wstart--;
continue;
}
j = wstart;
wvalue = 1;
wend = 0;
for (i = 1; i < window; i++) {
if (wstart - i < 0)
break;
if (BN_is_bit_set(q, wstart - i)) {
wvalue <<= (i - wend);
wvalue |= 1;
wend = i;
}
}
j = wend + 1;
if (!start)
for (i = 0; i < j; i++) {
if (!BN_mod_sqr_reciprocal(r, r, recp, ctx))
goto err;
}
if (!BN_mod_mul_reciprocal(r, r, val[wvalue >> 1], recp, ctx))
goto err;
wstart -= wend + 1;
wvalue = 0;
start = 0;
if (wstart < 0)
break;
}
done:
ret = 1;
err:
BN_CTX_end(ctx);
BN_RECP_CTX_free(recp);
return ret;
}
static int
BN_mod_exp_internal(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx, int ct)
{
int ret;
if (BN_is_odd(m)) {
if (a->top == 1 && !a->neg && !ct) {
BN_ULONG A = a->d[0];
ret = BN_mod_exp_mont_word(r, A,p, m,ctx, NULL);
} else
ret = BN_mod_exp_mont_ct(r, a,p, m,ctx, NULL);
} else {
ret = BN_mod_exp_reciprocal(r, a,p, m, ctx);
}
return (ret);
}
int
BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx)
{
return BN_mod_exp_internal(r, a, p, m, ctx,
(BN_get_flags(p, BN_FLG_CONSTTIME) != 0));
}
LCRYPTO_ALIAS(BN_mod_exp);
int
BN_mod_exp_ct(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx)
{
return BN_mod_exp_internal(r, a, p, m, ctx, 1);
}
int
BN_mod_exp_nonct(BIGNUM *r, const BIGNUM *a, const BIGNUM *p, const BIGNUM *m,
BN_CTX *ctx)
{
return BN_mod_exp_internal(r, a, p, m, ctx, 0);
}
int
BN_mod_exp2_mont(BIGNUM *rr, const BIGNUM *a1, const BIGNUM *p1,
const BIGNUM *a2, const BIGNUM *p2, const BIGNUM *m, BN_CTX *ctx,
BN_MONT_CTX *in_mont)
{
int i, j, bits, b, bits1, bits2, ret = 0, wpos1, wpos2, window1, window2, wvalue1, wvalue2;
int r_is_one = 1;
BIGNUM *d, *r;
const BIGNUM *a_mod_m;
BIGNUM *val1[TABLE_SIZE], *val2[TABLE_SIZE];
BN_MONT_CTX *mont = NULL;
if (!BN_is_odd(m)) {
BNerror(BN_R_CALLED_WITH_EVEN_MODULUS);
return (0);
}
bits1 = BN_num_bits(p1);
bits2 = BN_num_bits(p2);
if ((bits1 == 0) && (bits2 == 0)) {
ret = BN_one(rr);
return ret;
}
bits = (bits1 > bits2) ? bits1 : bits2;
BN_CTX_start(ctx);
if ((d = BN_CTX_get(ctx)) == NULL)
goto err;
if ((r = BN_CTX_get(ctx)) == NULL)
goto err;
if ((val1[0] = BN_CTX_get(ctx)) == NULL)
goto err;
if ((val2[0] = BN_CTX_get(ctx)) == NULL)
goto err;
if ((mont = in_mont) == NULL)
mont = BN_MONT_CTX_create(m, ctx);
if (mont == NULL)
goto err;
window1 = BN_window_bits_for_exponent_size(bits1);
window2 = BN_window_bits_for_exponent_size(bits2);
if (!BN_nnmod(val1[0], a1, m, ctx))
goto err;
a_mod_m = val1[0];
if (BN_is_zero(a_mod_m)) {
BN_zero(rr);
ret = 1;
goto err;
}
if (!BN_to_montgomery(val1[0], a_mod_m, mont, ctx))
goto err;
if (window1 > 1) {
if (!BN_mod_mul_montgomery(d, val1[0], val1[0], mont, ctx))
goto err;
j = 1 << (window1 - 1);
for (i = 1; i < j; i++) {
if (((val1[i] = BN_CTX_get(ctx)) == NULL) ||
!BN_mod_mul_montgomery(val1[i], val1[i - 1],
d, mont, ctx))
goto err;
}
}
if (!BN_nnmod(val2[0], a2, m, ctx))
goto err;
a_mod_m = val2[0];
if (BN_is_zero(a_mod_m)) {
BN_zero(rr);
ret = 1;
goto err;
}
if (!BN_to_montgomery(val2[0], a_mod_m, mont, ctx))
goto err;
if (window2 > 1) {
if (!BN_mod_mul_montgomery(d, val2[0], val2[0], mont, ctx))
goto err;
j = 1 << (window2 - 1);
for (i = 1; i < j; i++) {
if (((val2[i] = BN_CTX_get(ctx)) == NULL) ||
!BN_mod_mul_montgomery(val2[i], val2[i - 1],
d, mont, ctx))
goto err;
}
}
r_is_one = 1;
wvalue1 = 0;
wvalue2 = 0;
wpos1 = 0;
wpos2 = 0;
if (!BN_to_montgomery(r, BN_value_one(), mont, ctx))
goto err;
for (b = bits - 1; b >= 0; b--) {
if (!r_is_one) {
if (!BN_mod_mul_montgomery(r, r,r, mont, ctx))
goto err;
}
if (!wvalue1)
if (BN_is_bit_set(p1, b)) {
i = b - window1 + 1;
while (!BN_is_bit_set(p1, i))
i++;
wpos1 = i;
wvalue1 = 1;
for (i = b - 1; i >= wpos1; i--) {
wvalue1 <<= 1;
if (BN_is_bit_set(p1, i))
wvalue1++;
}
}
if (!wvalue2)
if (BN_is_bit_set(p2, b)) {
i = b - window2 + 1;
while (!BN_is_bit_set(p2, i))
i++;
wpos2 = i;
wvalue2 = 1;
for (i = b - 1; i >= wpos2; i--) {
wvalue2 <<= 1;
if (BN_is_bit_set(p2, i))
wvalue2++;
}
}
if (wvalue1 && b == wpos1) {
if (!BN_mod_mul_montgomery(r, r, val1[wvalue1 >> 1],
mont, ctx))
goto err;
wvalue1 = 0;
r_is_one = 0;
}
if (wvalue2 && b == wpos2) {
if (!BN_mod_mul_montgomery(r, r, val2[wvalue2 >> 1],
mont, ctx))
goto err;
wvalue2 = 0;
r_is_one = 0;
}
}
if (!BN_from_montgomery(rr, r,mont, ctx))
goto err;
ret = 1;
err:
if (mont != in_mont)
BN_MONT_CTX_free(mont);
BN_CTX_end(ctx);
return ret;
}