#include <sys/types.h>
#include <sys/param.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "bn.h"
#define MP_PREC 32
#define DIGIT_BIT 28
#define MP_MASK ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1))
#define MP_WARRAY (1U << (((sizeof(mp_word) * CHAR_BIT) - (2 * DIGIT_BIT) + 1)))
#define MP_NO 0
#define MP_YES 1
#ifndef USE_ARG
#define USE_ARG(x) (void)&(x)
#endif
#ifndef __arraycount
#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0]))
#endif
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#define MP_ISZERO(a) (((a)->used == 0) ? MP_YES : MP_NO)
typedef int mp_err;
static int signed_multiply(mp_int * a, mp_int * b, mp_int * c);
static int square(mp_int * a, mp_int * b);
static int signed_subtract_word(mp_int *a, mp_digit b, mp_int *c);
static inline void *
allocate(size_t n, size_t m)
{
return calloc(n, m);
}
static inline void
deallocate(void *v, size_t sz)
{
USE_ARG(sz);
free(v);
}
static inline void
mp_zero(mp_int *a)
{
a->sign = MP_ZPOS;
a->used = 0;
memset(a->dp, 0x0, a->alloc * sizeof(*a->dp));
}
static int
mp_grow(mp_int *a, int size)
{
mp_digit *tmp;
if (a->alloc < size) {
size += (MP_PREC * 2) - (size % MP_PREC);
tmp = realloc(a->dp, sizeof(*tmp) * size);
if (tmp == NULL) {
return MP_MEM;
}
a->dp = tmp;
memset(&a->dp[a->alloc], 0x0, (size - a->alloc) * sizeof(*a->dp));
a->alloc = size;
}
return MP_OKAY;
}
static int
lshift_digits(mp_int * a, int b)
{
mp_digit *top, *bottom;
int x, res;
if (b <= 0) {
return MP_OKAY;
}
if (a->alloc < a->used + b) {
if ((res = mp_grow(a, a->used + b)) != MP_OKAY) {
return res;
}
}
a->used += b;
top = a->dp + a->used - 1;
bottom = a->dp + a->used - 1 - b;
for (x = a->used - 1; x >= b; x--) {
*top-- = *bottom--;
}
memset(a->dp, 0x0, b * sizeof(*a->dp));
return MP_OKAY;
}
static void
trim_unused_digits(mp_int * a)
{
while (a->used > 0 && a->dp[a->used - 1] == 0) {
a->used -= 1;
}
if (a->used == 0) {
a->sign = MP_ZPOS;
}
}
static int
mp_copy(BIGNUM *a, BIGNUM *b)
{
int res;
if (a == b) {
return MP_OKAY;
}
if (a == NULL || b == NULL) {
return MP_VAL;
}
if (b->alloc < a->used) {
if ((res = mp_grow(b, a->used)) != MP_OKAY) {
return res;
}
}
memcpy(b->dp, a->dp, a->used * sizeof(*b->dp));
if (b->used > a->used) {
memset(&b->dp[a->used], 0x0, (b->used - a->used) * sizeof(*b->dp));
}
b->used = a->used;
b->sign = a->sign;
return MP_OKAY;
}
static int
lshift_bits(mp_int *a, int b, mp_int *c)
{
mp_digit d;
int res;
if (a != c) {
if ((res = mp_copy(a, c)) != MP_OKAY) {
return res;
}
}
if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) {
if ((res = mp_grow(c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) {
return res;
}
}
if (b >= (int)DIGIT_BIT) {
if ((res = lshift_digits(c, b / DIGIT_BIT)) != MP_OKAY) {
return res;
}
}
d = (mp_digit) (b % DIGIT_BIT);
if (d != 0) {
mp_digit *tmpc, shift, mask, carry, rr;
int x;
mask = (((mp_digit)1) << d) - 1;
shift = DIGIT_BIT - d;
tmpc = c->dp;
carry = 0;
for (x = 0; x < c->used; x++) {
rr = (*tmpc >> shift) & mask;
*tmpc = ((*tmpc << d) | carry) & MP_MASK;
++tmpc;
carry = rr;
}
if (carry != 0) {
c->dp[c->used++] = carry;
}
}
trim_unused_digits(c);
return MP_OKAY;
}
static int
mp_read_unsigned_bin(mp_int *a, const uint8_t *b, int c)
{
int res;
if (a->alloc < 2) {
if ((res = mp_grow(a, 2)) != MP_OKAY) {
return res;
}
}
mp_zero(a);
while (c-- > 0) {
if ((res = lshift_bits(a, 8, a)) != MP_OKAY) {
return res;
}
a->dp[0] |= *b++;
a->used += 1;
}
trim_unused_digits(a);
return MP_OKAY;
}
static int
mp_count_bits(const mp_int *a)
{
int r;
mp_digit q;
if (a->used == 0) {
return 0;
}
r = (a->used - 1) * DIGIT_BIT;
for (q = a->dp[a->used - 1]; q > ((mp_digit) 0) ; r++) {
q >>= ((mp_digit) 1);
}
return r;
}
static int
compare_magnitude(mp_int * a, mp_int * b)
{
int n;
mp_digit *tmpa, *tmpb;
if (a->used > b->used) {
return MP_GT;
}
if (a->used < b->used) {
return MP_LT;
}
tmpa = a->dp + (a->used - 1);
tmpb = b->dp + (a->used - 1);
for (n = 0; n < a->used; ++n, --tmpa, --tmpb) {
if (*tmpa > *tmpb) {
return MP_GT;
}
if (*tmpa < *tmpb) {
return MP_LT;
}
}
return MP_EQ;
}
static int
signed_compare(mp_int * a, mp_int * b)
{
if (a->sign != b->sign) {
return (a->sign == MP_NEG) ? MP_LT : MP_GT;
}
return (a->sign == MP_NEG) ? compare_magnitude(b, a) : compare_magnitude(a, b);
}
static int
mp_unsigned_bin_size(mp_int * a)
{
int size = mp_count_bits(a);
return (size / 8 + ((size & 7) != 0 ? 1 : 0));
}
static int
mp_init(mp_int * a)
{
a->dp = allocate(1, sizeof(*a->dp) * MP_PREC);
if (a->dp == NULL) {
return MP_MEM;
}
memset(a->dp, 0x0, MP_PREC * sizeof(*a->dp));
a->used = 0;
a->alloc = MP_PREC;
a->sign = MP_ZPOS;
return MP_OKAY;
}
static void
mp_clear(mp_int * a)
{
if (a->dp != NULL) {
memset(a->dp, 0x0, a->used * sizeof(*a->dp));
deallocate(a->dp, (size_t)a->alloc);
a->dp = NULL;
a->alloc = a->used = 0;
a->sign = MP_ZPOS;
}
}
static int
mp_init_multi(mp_int *mp, ...)
{
mp_err res = MP_OKAY;
int n = 0;
mp_int* cur_arg = mp;
va_list args;
va_start(args, mp);
while (cur_arg != NULL) {
if (mp_init(cur_arg) != MP_OKAY) {
va_list clean_args;
va_end(args);
cur_arg = mp;
va_start(clean_args, mp);
while (n--) {
mp_clear(cur_arg);
cur_arg = va_arg(clean_args, mp_int*);
}
va_end(clean_args);
res = MP_MEM;
break;
}
n++;
cur_arg = va_arg(args, mp_int*);
}
va_end(args);
return res;
}
static int
mp_init_size(mp_int * a, int size)
{
size += (MP_PREC * 2) - (size % MP_PREC);
a->dp = allocate(1, sizeof(*a->dp) * size);
if (a->dp == NULL) {
return MP_MEM;
}
a->used = 0;
a->alloc = size;
a->sign = MP_ZPOS;
memset(a->dp, 0x0, size * sizeof(*a->dp));
return MP_OKAY;
}
static int
mp_init_copy(mp_int * a, mp_int * b)
{
int res;
if ((res = mp_init(a)) != MP_OKAY) {
return res;
}
return mp_copy(b, a);
}
static int
basic_add(mp_int * a, mp_int * b, mp_int * c)
{
mp_int *x;
int olduse, res, min, max;
if (a->used > b->used) {
min = b->used;
max = a->used;
x = a;
} else {
min = a->used;
max = b->used;
x = b;
}
if (c->alloc < max + 1) {
if ((res = mp_grow(c, max + 1)) != MP_OKAY) {
return res;
}
}
olduse = c->used;
c->used = max + 1;
{
mp_digit carry, *tmpa, *tmpb, *tmpc;
int i;
tmpa = a->dp;
tmpb = b->dp;
tmpc = c->dp;
carry = 0;
for (i = 0; i < min; i++) {
*tmpc = *tmpa++ + *tmpb++ + carry;
carry = *tmpc >> ((mp_digit)DIGIT_BIT);
*tmpc++ &= MP_MASK;
}
if (min != max) {
for (; i < max; i++) {
*tmpc = x->dp[i] + carry;
carry = *tmpc >> ((mp_digit)DIGIT_BIT);
*tmpc++ &= MP_MASK;
}
}
*tmpc++ = carry;
if (olduse > c->used) {
memset(tmpc, 0x0, (olduse - c->used) * sizeof(*c->dp));
}
}
trim_unused_digits(c);
return MP_OKAY;
}
static int
basic_subtract(mp_int * a, mp_int * b, mp_int * c)
{
int olduse, res, min, max;
min = b->used;
max = a->used;
if (c->alloc < max) {
if ((res = mp_grow(c, max)) != MP_OKAY) {
return res;
}
}
olduse = c->used;
c->used = max;
{
mp_digit carry, *tmpa, *tmpb, *tmpc;
int i;
tmpa = a->dp;
tmpb = b->dp;
tmpc = c->dp;
carry = 0;
for (i = 0; i < min; i++) {
*tmpc = *tmpa++ - *tmpb++ - carry;
carry = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof(mp_digit) - 1));
*tmpc++ &= MP_MASK;
}
for (; i < max; i++) {
*tmpc = *tmpa++ - carry;
carry = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof(mp_digit) - 1));
*tmpc++ &= MP_MASK;
}
if (olduse > c->used) {
memset(tmpc, 0x0, (olduse - c->used) * sizeof(*a->dp));
}
}
trim_unused_digits(c);
return MP_OKAY;
}
static int
signed_subtract(mp_int * a, mp_int * b, mp_int * c)
{
int sa, sb, res;
sa = a->sign;
sb = b->sign;
if (sa != sb) {
c->sign = sa;
res = basic_add(a, b, c);
} else {
if (compare_magnitude(a, b) != MP_LT) {
c->sign = sa;
res = basic_subtract(a, b, c);
} else {
c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS;
res = basic_subtract(b, a, c);
}
}
return res;
}
static int
rshift_digits(mp_int * a, int b)
{
if (b <= 0) {
return 0;
}
if (a->used <= b) {
mp_zero(a);
return 0;
}
memmove(a->dp, &a->dp[b], (a->used - b) * sizeof(*a->dp));
memset(&a->dp[a->used - b], 0x0, b * sizeof(*a->dp));
a->used -= b;
return 1;
}
static int
multiply_digit(mp_int * a, mp_digit b, mp_int * c)
{
mp_digit carry, *tmpa, *tmpc;
mp_word r;
int ix, res, olduse;
if (c->alloc < a->used + 1) {
if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) {
return res;
}
}
olduse = c->used;
c->sign = a->sign;
tmpa = a->dp;
tmpc = c->dp;
carry = 0;
for (ix = 0; ix < a->used; ix++) {
r = ((mp_word) carry) + ((mp_word)*tmpa++) * ((mp_word)b);
*tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK));
carry = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
}
*tmpc++ = carry;
++ix;
if (olduse > ix) {
memset(tmpc, 0x0, (olduse - ix) * sizeof(*tmpc));
}
c->used = a->used + 1;
trim_unused_digits(c);
return MP_OKAY;
}
static int
signed_add(mp_int * a, mp_int * b, mp_int * c)
{
int asign, bsign, res;
asign = a->sign;
bsign = b->sign;
if (asign == bsign) {
c->sign = asign;
res = basic_add(a, b, c);
} else {
if (compare_magnitude(a, b) == MP_LT) {
c->sign = bsign;
res = basic_subtract(b, a, c);
} else {
c->sign = asign;
res = basic_subtract(a, b, c);
}
}
return res;
}
static void
mp_exch(mp_int *a, mp_int *b)
{
mp_int t;
t = *a;
*a = *b;
*b = t;
}
static int
modulo_2_to_power(mp_int * a, int b, mp_int * c)
{
int x, res;
if (b <= 0) {
mp_zero(c);
return MP_OKAY;
}
if (b >= (int) (a->used * DIGIT_BIT)) {
res = mp_copy(a, c);
return res;
}
if ((res = mp_copy(a, c)) != MP_OKAY) {
return res;
}
for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) {
c->dp[x] = 0;
}
c->dp[b / DIGIT_BIT] &=
(mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1));
trim_unused_digits(c);
return MP_OKAY;
}
static int
rshift_bits(mp_int * a, int b, mp_int * c, mp_int * d)
{
mp_digit D, r, rr;
int x, res;
mp_int t;
if (b <= 0) {
res = mp_copy(a, c);
if (d != NULL) {
mp_zero(d);
}
return res;
}
if ((res = mp_init(&t)) != MP_OKAY) {
return res;
}
if (d != NULL) {
if ((res = modulo_2_to_power(a, b, &t)) != MP_OKAY) {
mp_clear(&t);
return res;
}
}
if ((res = mp_copy(a, c)) != MP_OKAY) {
mp_clear(&t);
return res;
}
if (b >= (int)DIGIT_BIT) {
rshift_digits(c, b / DIGIT_BIT);
}
D = (mp_digit) (b % DIGIT_BIT);
if (D != 0) {
mp_digit *tmpc, mask, shift;
mask = (((mp_digit)1) << D) - 1;
shift = DIGIT_BIT - D;
tmpc = c->dp + (c->used - 1);
r = 0;
for (x = c->used - 1; x >= 0; x--) {
rr = *tmpc & mask;
*tmpc = (*tmpc >> D) | (r << shift);
--tmpc;
r = rr;
}
}
trim_unused_digits(c);
if (d != NULL) {
mp_exch(&t, d);
}
mp_clear(&t);
return MP_OKAY;
}
static int
signed_divide(mp_int *c, mp_int *d, mp_int *a, mp_int *b)
{
mp_int q, x, y, t1, t2;
int res, n, t, i, norm, neg;
if (MP_ISZERO(b) == MP_YES) {
return MP_VAL;
}
if (compare_magnitude(a, b) == MP_LT) {
if (d != NULL) {
res = mp_copy(a, d);
} else {
res = MP_OKAY;
}
if (c != NULL) {
mp_zero(c);
}
return res;
}
if ((res = mp_init_size(&q, a->used + 2)) != MP_OKAY) {
return res;
}
q.used = a->used + 2;
if ((res = mp_init(&t1)) != MP_OKAY) {
goto LBL_Q;
}
if ((res = mp_init(&t2)) != MP_OKAY) {
goto LBL_T1;
}
if ((res = mp_init_copy(&x, a)) != MP_OKAY) {
goto LBL_T2;
}
if ((res = mp_init_copy(&y, b)) != MP_OKAY) {
goto LBL_X;
}
neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
x.sign = y.sign = MP_ZPOS;
norm = mp_count_bits(&y) % DIGIT_BIT;
if (norm < (int)(DIGIT_BIT-1)) {
norm = (DIGIT_BIT-1) - norm;
if ((res = lshift_bits(&x, norm, &x)) != MP_OKAY) {
goto LBL_Y;
}
if ((res = lshift_bits(&y, norm, &y)) != MP_OKAY) {
goto LBL_Y;
}
} else {
norm = 0;
}
n = x.used - 1;
t = y.used - 1;
if ((res = lshift_digits(&y, n - t)) != MP_OKAY) {
goto LBL_Y;
}
while (signed_compare(&x, &y) != MP_LT) {
++(q.dp[n - t]);
if ((res = signed_subtract(&x, &y, &x)) != MP_OKAY) {
goto LBL_Y;
}
}
rshift_digits(&y, n - t);
for (i = n; i >= (t + 1); i--) {
if (i > x.used) {
continue;
}
if (x.dp[i] == y.dp[t]) {
q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1);
} else {
mp_word tmp;
tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT);
tmp |= ((mp_word) x.dp[i - 1]);
tmp /= ((mp_word) y.dp[t]);
if (tmp > (mp_word) MP_MASK) {
tmp = MP_MASK;
}
q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK));
}
q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK;
do {
q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK;
mp_zero(&t1);
t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1];
t1.dp[1] = y.dp[t];
t1.used = 2;
if ((res = multiply_digit(&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) {
goto LBL_Y;
}
t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2];
t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1];
t2.dp[2] = x.dp[i];
t2.used = 3;
} while (compare_magnitude(&t1, &t2) == MP_GT);
if ((res = multiply_digit(&y, q.dp[i - t - 1], &t1)) != MP_OKAY) {
goto LBL_Y;
}
if ((res = lshift_digits(&t1, i - t - 1)) != MP_OKAY) {
goto LBL_Y;
}
if ((res = signed_subtract(&x, &t1, &x)) != MP_OKAY) {
goto LBL_Y;
}
if (x.sign == MP_NEG) {
if ((res = mp_copy(&y, &t1)) != MP_OKAY) {
goto LBL_Y;
}
if ((res = lshift_digits(&t1, i - t - 1)) != MP_OKAY) {
goto LBL_Y;
}
if ((res = signed_add(&x, &t1, &x)) != MP_OKAY) {
goto LBL_Y;
}
q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK;
}
}
x.sign = x.used == 0 ? MP_ZPOS : a->sign;
if (c != NULL) {
trim_unused_digits(&q);
mp_exch(&q, c);
c->sign = neg;
}
if (d != NULL) {
rshift_bits(&x, norm, &x, NULL);
mp_exch(&x, d);
}
res = MP_OKAY;
LBL_Y:
mp_clear(&y);
LBL_X:
mp_clear(&x);
LBL_T2:
mp_clear(&t2);
LBL_T1:
mp_clear(&t1);
LBL_Q:
mp_clear(&q);
return res;
}
static int
modulo(mp_int * a, mp_int * b, mp_int * c)
{
mp_int t;
int res;
if ((res = mp_init(&t)) != MP_OKAY) {
return res;
}
if ((res = signed_divide(NULL, &t, a, b)) != MP_OKAY) {
mp_clear(&t);
return res;
}
if (t.sign != b->sign) {
res = signed_add(b, &t, c);
} else {
res = MP_OKAY;
mp_exch(&t, c);
}
mp_clear(&t);
return res;
}
static void
set_word(mp_int * a, mp_digit b)
{
mp_zero(a);
a->dp[0] = b & MP_MASK;
a->used = (a->dp[0] != 0) ? 1 : 0;
}
static int
half(mp_int * a, mp_int * b)
{
int x, res, oldused;
if (b->alloc < a->used) {
if ((res = mp_grow(b, a->used)) != MP_OKAY) {
return res;
}
}
oldused = b->used;
b->used = a->used;
{
mp_digit r, rr, *tmpa, *tmpb;
tmpa = a->dp + b->used - 1;
tmpb = b->dp + b->used - 1;
r = 0;
for (x = b->used - 1; x >= 0; x--) {
rr = *tmpa & 1;
*tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1));
r = rr;
}
tmpb = b->dp + b->used;
for (x = b->used; x < oldused; x++) {
*tmpb++ = 0;
}
}
b->sign = a->sign;
trim_unused_digits(b);
return MP_OKAY;
}
static int
compare_digit(mp_int * a, mp_digit b)
{
if (a->sign == MP_NEG) {
return MP_LT;
}
if (a->used > 1) {
return MP_GT;
}
if (a->dp[0] > b) {
return MP_GT;
} else if (a->dp[0] < b) {
return MP_LT;
} else {
return MP_EQ;
}
}
static void
mp_clear_multi(mp_int *mp, ...)
{
mp_int* next_mp = mp;
va_list args;
va_start(args, mp);
while (next_mp != NULL) {
mp_clear(next_mp);
next_mp = va_arg(args, mp_int*);
}
va_end(args);
}
static int
fast_modular_inverse(mp_int * a, mp_int * b, mp_int * c)
{
mp_int x, y, u, v, B, D;
int res, neg;
if (MP_ISZERO(b) == MP_YES) {
return MP_VAL;
}
if ((res = mp_init_multi(&x, &y, &u, &v, &B, &D, NULL)) != MP_OKAY) {
return res;
}
if ((res = mp_copy(b, &x)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = modulo(a, b, &y)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = mp_copy(&x, &u)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = mp_copy(&y, &v)) != MP_OKAY) {
goto LBL_ERR;
}
set_word(&D, 1);
top:
while (BN_is_even(&u) == 1) {
if ((res = half(&u, &u)) != MP_OKAY) {
goto LBL_ERR;
}
if (BN_is_odd(&B) == 1) {
if ((res = signed_subtract(&B, &x, &B)) != MP_OKAY) {
goto LBL_ERR;
}
}
if ((res = half(&B, &B)) != MP_OKAY) {
goto LBL_ERR;
}
}
while (BN_is_even(&v) == 1) {
if ((res = half(&v, &v)) != MP_OKAY) {
goto LBL_ERR;
}
if (BN_is_odd(&D) == 1) {
if ((res = signed_subtract(&D, &x, &D)) != MP_OKAY) {
goto LBL_ERR;
}
}
if ((res = half(&D, &D)) != MP_OKAY) {
goto LBL_ERR;
}
}
if (signed_compare(&u, &v) != MP_LT) {
if ((res = signed_subtract(&u, &v, &u)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = signed_subtract(&B, &D, &B)) != MP_OKAY) {
goto LBL_ERR;
}
} else {
if ((res = signed_subtract(&v, &u, &v)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = signed_subtract(&D, &B, &D)) != MP_OKAY) {
goto LBL_ERR;
}
}
if (MP_ISZERO(&u) == MP_NO) {
goto top;
}
if (compare_digit(&v, 1) != MP_EQ) {
res = MP_VAL;
goto LBL_ERR;
}
neg = a->sign;
while (D.sign == MP_NEG) {
if ((res = signed_add(&D, b, &D)) != MP_OKAY) {
goto LBL_ERR;
}
}
mp_exch(&D, c);
c->sign = neg;
res = MP_OKAY;
LBL_ERR:
mp_clear_multi (&x, &y, &u, &v, &B, &D, NULL);
return res;
}
static int
slow_modular_inverse(mp_int * a, mp_int * b, mp_int * c)
{
mp_int x, y, u, v, A, B, C, D;
int res;
if (b->sign == MP_NEG || MP_ISZERO(b) == MP_YES) {
return MP_VAL;
}
if ((res = mp_init_multi(&x, &y, &u, &v,
&A, &B, &C, &D, NULL)) != MP_OKAY) {
return res;
}
if ((res = modulo(a, b, &x)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = mp_copy(b, &y)) != MP_OKAY) {
goto LBL_ERR;
}
if (BN_is_even(&x) == 1 && BN_is_even(&y) == 1) {
res = MP_VAL;
goto LBL_ERR;
}
if ((res = mp_copy(&x, &u)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = mp_copy(&y, &v)) != MP_OKAY) {
goto LBL_ERR;
}
set_word(&A, 1);
set_word(&D, 1);
top:
while (BN_is_even(&u) == 1) {
if ((res = half(&u, &u)) != MP_OKAY) {
goto LBL_ERR;
}
if (BN_is_odd(&A) == 1 || BN_is_odd(&B) == 1) {
if ((res = signed_add(&A, &y, &A)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = signed_subtract(&B, &x, &B)) != MP_OKAY) {
goto LBL_ERR;
}
}
if ((res = half(&A, &A)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = half(&B, &B)) != MP_OKAY) {
goto LBL_ERR;
}
}
while (BN_is_even(&v) == 1) {
if ((res = half(&v, &v)) != MP_OKAY) {
goto LBL_ERR;
}
if (BN_is_odd(&C) == 1 || BN_is_odd(&D) == 1) {
if ((res = signed_add(&C, &y, &C)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = signed_subtract(&D, &x, &D)) != MP_OKAY) {
goto LBL_ERR;
}
}
if ((res = half(&C, &C)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = half(&D, &D)) != MP_OKAY) {
goto LBL_ERR;
}
}
if (signed_compare(&u, &v) != MP_LT) {
if ((res = signed_subtract(&u, &v, &u)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = signed_subtract(&A, &C, &A)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = signed_subtract(&B, &D, &B)) != MP_OKAY) {
goto LBL_ERR;
}
} else {
if ((res = signed_subtract(&v, &u, &v)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = signed_subtract(&C, &A, &C)) != MP_OKAY) {
goto LBL_ERR;
}
if ((res = signed_subtract(&D, &B, &D)) != MP_OKAY) {
goto LBL_ERR;
}
}
if (BN_is_zero(&u) == 0) {
goto top;
}
if (compare_digit(&v, 1) != MP_EQ) {
res = MP_VAL;
goto LBL_ERR;
}
while (compare_digit(&C, 0) == MP_LT) {
if ((res = signed_add(&C, b, &C)) != MP_OKAY) {
goto LBL_ERR;
}
}
while (compare_magnitude(&C, b) != MP_LT) {
if ((res = signed_subtract(&C, b, &C)) != MP_OKAY) {
goto LBL_ERR;
}
}
mp_exch(&C, c);
res = MP_OKAY;
LBL_ERR:
mp_clear_multi(&x, &y, &u, &v, &A, &B, &C, &D, NULL);
return res;
}
static int
modular_inverse(mp_int *c, mp_int *a, mp_int *b)
{
if (b->sign == MP_NEG || MP_ISZERO(b) == MP_YES) {
return MP_VAL;
}
if (BN_is_odd(b) == 1) {
return fast_modular_inverse(a, b, c);
}
return slow_modular_inverse(a, b, c);
}
static int
absolute(mp_int * a, mp_int * b)
{
int res;
if (a != b) {
if ((res = mp_copy(a, b)) != MP_OKAY) {
return res;
}
}
b->sign = MP_ZPOS;
return MP_OKAY;
}
static int
mp_reduce_is_2k_l(mp_int *a)
{
int ix, iy;
if (a->used == 0) {
return MP_NO;
} else if (a->used == 1) {
return MP_YES;
} else if (a->used > 1) {
for (iy = ix = 0; ix < a->used; ix++) {
if (a->dp[ix] == MP_MASK) {
++iy;
}
}
return (iy >= (a->used/2)) ? MP_YES : MP_NO;
}
return MP_NO;
}
static int
mp_2expt(mp_int * a, int b)
{
int res;
mp_zero(a);
if ((res = mp_grow(a, b / DIGIT_BIT + 1)) != MP_OKAY) {
return res;
}
a->used = b / DIGIT_BIT + 1;
a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT);
return MP_OKAY;
}
static int
mp_reduce_setup(mp_int * a, mp_int * b)
{
int res;
if ((res = mp_2expt(a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
return res;
}
return signed_divide(a, NULL, a, b);
}
static int
doubled(mp_int * a, mp_int * b)
{
int x, res, oldused;
if (b->alloc < a->used + 1) {
if ((res = mp_grow(b, a->used + 1)) != MP_OKAY) {
return res;
}
}
oldused = b->used;
b->used = a->used;
{
mp_digit r, rr, *tmpa, *tmpb;
tmpa = a->dp;
tmpb = b->dp;
r = 0;
for (x = 0; x < a->used; x++) {
rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1));
*tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK;
r = rr;
}
if (r != 0) {
*tmpb = 1;
++(b->used);
}
tmpb = b->dp + b->used;
for (x = b->used; x < oldused; x++) {
*tmpb++ = 0;
}
}
b->sign = a->sign;
return MP_OKAY;
}
static int
third(mp_int * a, mp_int *c, mp_digit * d)
{
mp_int q;
mp_word w, t;
mp_digit b;
int res, ix;
b = (((mp_word)1) << ((mp_word)DIGIT_BIT)) / ((mp_word)3);
if ((res = mp_init_size(&q, a->used)) != MP_OKAY) {
return res;
}
q.used = a->used;
q.sign = a->sign;
w = 0;
for (ix = a->used - 1; ix >= 0; ix--) {
w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]);
if (w >= 3) {
t = (w * ((mp_word)b)) >> ((mp_word)DIGIT_BIT);
w -= t+t+t;
while (w >= 3) {
t += 1;
w -= 3;
}
} else {
t = 0;
}
q.dp[ix] = (mp_digit)t;
}
if (d != NULL) {
*d = (mp_digit)w;
}
if (c != NULL) {
trim_unused_digits(&q);
mp_exch(&q, c);
}
mp_clear(&q);
return res;
}
static int
toom_cook_multiply(mp_int *a, mp_int *b, mp_int *c)
{
mp_int w0, w1, w2, w3, w4, tmp1, tmp2, a0, a1, a2, b0, b1, b2;
int res, B;
if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4,
&a0, &a1, &a2, &b0, &b1,
&b2, &tmp1, &tmp2, NULL)) != MP_OKAY) {
return res;
}
B = MIN(a->used, b->used) / 3;
if ((res = modulo_2_to_power(a, DIGIT_BIT * B, &a0)) != MP_OKAY) {
goto ERR;
}
if ((res = mp_copy(a, &a1)) != MP_OKAY) {
goto ERR;
}
rshift_digits(&a1, B);
modulo_2_to_power(&a1, DIGIT_BIT * B, &a1);
if ((res = mp_copy(a, &a2)) != MP_OKAY) {
goto ERR;
}
rshift_digits(&a2, B*2);
if ((res = modulo_2_to_power(b, DIGIT_BIT * B, &b0)) != MP_OKAY) {
goto ERR;
}
if ((res = mp_copy(b, &b1)) != MP_OKAY) {
goto ERR;
}
rshift_digits(&b1, B);
modulo_2_to_power(&b1, DIGIT_BIT * B, &b1);
if ((res = mp_copy(b, &b2)) != MP_OKAY) {
goto ERR;
}
rshift_digits(&b2, B*2);
if ((res = signed_multiply(&a0, &b0, &w0)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_multiply(&a2, &b2, &w4)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&a0, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&tmp1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a2, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&b0, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp2, &b1, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&tmp2, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp2, &b2, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_multiply(&tmp1, &tmp2, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&a2, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&tmp1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&b2, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp2, &b1, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&tmp2, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp2, &b0, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_multiply(&tmp1, &tmp2, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&a2, &a1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&b2, &b1, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp2, &b0, &tmp2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_multiply(&tmp1, &tmp2, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w1, &w4, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w3, &w0, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = half(&w1, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = half(&w3, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w2, &w0, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w2, &w4, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w1, &w2, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w3, &w2, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_bits(&w0, 3, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w1, &tmp1, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_bits(&w4, 3, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w3, &tmp1, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = multiply_digit(&w2, 3, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w2, &w1, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w2, &w3, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w1, &w2, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w3, &w2, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = third(&w1, &w1, NULL)) != MP_OKAY) {
goto ERR;
}
if ((res = third(&w3, &w3, NULL)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_digits(&w1, 1*B)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_digits(&w2, 2*B)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_digits(&w3, 3*B)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_digits(&w4, 4*B)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&w0, &w1, c)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&w2, &w3, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&w4, &tmp1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, c, c)) != MP_OKAY) {
goto ERR;
}
ERR:
mp_clear_multi(&w0, &w1, &w2, &w3, &w4,
&a0, &a1, &a2, &b0, &b1,
&b2, &tmp1, &tmp2, NULL);
return res;
}
#define TOOM_MUL_CUTOFF 350
#define KARATSUBA_MUL_CUTOFF 80
static int
karatsuba_multiply(mp_int * a, mp_int * b, mp_int * c)
{
mp_int x0, x1, y0, y1, t1, x0y0, x1y1;
int B;
int err;
err = MP_MEM;
B = MIN(a->used, b->used);
B = (int)((unsigned)B >> 1);
if (mp_init_size(&x0, B) != MP_OKAY) {
goto ERR;
}
if (mp_init_size(&x1, a->used - B) != MP_OKAY) {
goto X0;
}
if (mp_init_size(&y0, B) != MP_OKAY) {
goto X1;
}
if (mp_init_size(&y1, b->used - B) != MP_OKAY) {
goto Y0;
}
if (mp_init_size(&t1, B * 2) != MP_OKAY) {
goto Y1;
}
if (mp_init_size(&x0y0, B * 2) != MP_OKAY) {
goto T1;
}
if (mp_init_size(&x1y1, B * 2) != MP_OKAY) {
goto X0Y0;
}
x0.used = y0.used = B;
x1.used = a->used - B;
y1.used = b->used - B;
{
int x;
mp_digit *tmpa, *tmpb, *tmpx, *tmpy;
tmpa = a->dp;
tmpb = b->dp;
tmpx = x0.dp;
tmpy = y0.dp;
for (x = 0; x < B; x++) {
*tmpx++ = *tmpa++;
*tmpy++ = *tmpb++;
}
tmpx = x1.dp;
for (x = B; x < a->used; x++) {
*tmpx++ = *tmpa++;
}
tmpy = y1.dp;
for (x = B; x < b->used; x++) {
*tmpy++ = *tmpb++;
}
}
trim_unused_digits(&x0);
trim_unused_digits(&y0);
if (signed_multiply(&x0, &y0, &x0y0) != MP_OKAY) {
goto X1Y1;
}
if (signed_multiply(&x1, &y1, &x1y1) != MP_OKAY) {
goto X1Y1;
}
if (basic_add(&x1, &x0, &t1) != MP_OKAY) {
goto X1Y1;
}
if (basic_add(&y1, &y0, &x0) != MP_OKAY) {
goto X1Y1;
}
if (signed_multiply(&t1, &x0, &t1) != MP_OKAY) {
goto X1Y1;
}
if (signed_add(&x0y0, &x1y1, &x0) != MP_OKAY) {
goto X1Y1;
}
if (basic_subtract(&t1, &x0, &t1) != MP_OKAY) {
goto X1Y1;
}
if (lshift_digits(&t1, B) != MP_OKAY) {
goto X1Y1;
}
if (lshift_digits(&x1y1, B * 2) != MP_OKAY) {
goto X1Y1;
}
if (signed_add(&x0y0, &t1, &t1) != MP_OKAY) {
goto X1Y1;
}
if (signed_add(&t1, &x1y1, c) != MP_OKAY) {
goto X1Y1;
}
err = MP_OKAY;
X1Y1:
mp_clear(&x1y1);
X0Y0:
mp_clear(&x0y0);
T1:
mp_clear(&t1);
Y1:
mp_clear(&y1);
Y0:
mp_clear(&y0);
X1:
mp_clear(&x1);
X0:
mp_clear(&x0);
ERR:
return err;
}
static int
fast_col_array_multiply(mp_int * a, mp_int * b, mp_int * c, int digs)
{
int olduse, res, pa, ix, iz;
mp_digit W[MP_WARRAY];
mp_word _W;
if (c->alloc < digs) {
if ((res = mp_grow(c, digs)) != MP_OKAY) {
return res;
}
}
pa = MIN(digs, a->used + b->used);
_W = 0;
for (ix = 0; ix < pa; ix++) {
int tx, ty;
int iy;
mp_digit *tmpx, *tmpy;
ty = MIN(b->used-1, ix);
tx = ix - ty;
tmpx = a->dp + tx;
tmpy = b->dp + ty;
iy = MIN(a->used-tx, ty+1);
for (iz = 0; iz < iy; ++iz) {
_W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
}
W[ix] = ((mp_digit)_W) & MP_MASK;
_W = _W >> ((mp_word)DIGIT_BIT);
}
olduse = c->used;
c->used = pa;
{
mp_digit *tmpc;
tmpc = c->dp;
for (ix = 0; ix < pa+1; ix++) {
*tmpc++ = (ix < pa) ? W[ix] : 0;
}
for (; ix < olduse; ix++) {
*tmpc++ = 0;
}
}
trim_unused_digits(c);
return MP_OKAY;
}
static inline int
can_use_fast_column_array(int ndigits, int used)
{
return (((unsigned)ndigits < MP_WARRAY) &&
used < (1 << (unsigned)((CHAR_BIT * sizeof(mp_word)) - (2 * DIGIT_BIT))));
}
static int
basic_multiply_partial_lower(mp_int * a, mp_int * b, mp_int * c, int digs)
{
mp_int t;
int res, pa, pb, ix, iy;
mp_digit u;
mp_word r;
mp_digit tmpx, *tmpt, *tmpy;
if (can_use_fast_column_array(digs, MIN(a->used, b->used))) {
return fast_col_array_multiply(a, b, c, digs);
}
if ((res = mp_init_size(&t, digs)) != MP_OKAY) {
return res;
}
t.used = digs;
pa = a->used;
for (ix = 0; ix < pa; ix++) {
u = 0;
pb = MIN(b->used, digs - ix);
tmpx = a->dp[ix];
tmpt = t.dp + ix;
tmpy = b->dp;
for (iy = 0; iy < pb; iy++) {
r = ((mp_word)*tmpt) +
((mp_word)tmpx) * ((mp_word)*tmpy++) +
((mp_word) u);
*tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
u = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
}
if (ix + iy < digs) {
*tmpt = u;
}
}
trim_unused_digits(&t);
mp_exch(&t, c);
mp_clear(&t);
return MP_OKAY;
}
static int
signed_multiply(mp_int * a, mp_int * b, mp_int * c)
{
int res, neg;
neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
if (MIN(a->used, b->used) >= TOOM_MUL_CUTOFF) {
res = toom_cook_multiply(a, b, c);
} else if (MIN(a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
res = karatsuba_multiply(a, b, c);
} else {
int digs = a->used + b->used + 1;
if (can_use_fast_column_array(digs, MIN(a->used, b->used))) {
res = fast_col_array_multiply(a, b, c, digs);
} else {
res = basic_multiply_partial_lower(a, b, c, (a)->used + (b)->used + 1);
}
}
c->sign = (c->used > 0) ? neg : MP_ZPOS;
return res;
}
static int
fast_basic_multiply_partial_upper(mp_int * a, mp_int * b, mp_int * c, int digs)
{
int olduse, res, pa, ix, iz;
mp_digit W[MP_WARRAY];
mp_word _W;
pa = a->used + b->used;
if (c->alloc < pa) {
if ((res = mp_grow(c, pa)) != MP_OKAY) {
return res;
}
}
pa = a->used + b->used;
_W = 0;
for (ix = digs; ix < pa; ix++) {
int tx, ty, iy;
mp_digit *tmpx, *tmpy;
ty = MIN(b->used-1, ix);
tx = ix - ty;
tmpx = a->dp + tx;
tmpy = b->dp + ty;
iy = MIN(a->used-tx, ty+1);
for (iz = 0; iz < iy; iz++) {
_W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
}
W[ix] = ((mp_digit)_W) & MP_MASK;
_W = _W >> ((mp_word)DIGIT_BIT);
}
olduse = c->used;
c->used = pa;
{
mp_digit *tmpc;
tmpc = c->dp + digs;
for (ix = digs; ix < pa; ix++) {
*tmpc++ = W[ix];
}
for (; ix < olduse; ix++) {
*tmpc++ = 0;
}
}
trim_unused_digits(c);
return MP_OKAY;
}
static int
basic_multiply_partial_upper(mp_int * a, mp_int * b, mp_int * c, int digs)
{
mp_int t;
int res, pa, pb, ix, iy;
mp_digit carry;
mp_word r;
mp_digit tmpx, *tmpt, *tmpy;
if (can_use_fast_column_array(a->used + b->used + 1, MIN(a->used, b->used))) {
return fast_basic_multiply_partial_upper(a, b, c, digs);
}
if ((res = mp_init_size(&t, a->used + b->used + 1)) != MP_OKAY) {
return res;
}
t.used = a->used + b->used + 1;
pa = a->used;
pb = b->used;
for (ix = 0; ix < pa; ix++) {
carry = 0;
tmpx = a->dp[ix];
tmpt = &(t.dp[digs]);
tmpy = b->dp + (digs - ix);
for (iy = digs - ix; iy < pb; iy++) {
r = ((mp_word)*tmpt) +
((mp_word)tmpx) * ((mp_word)*tmpy++) +
((mp_word) carry);
*tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
carry = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
}
*tmpt = carry;
}
trim_unused_digits(&t);
mp_exch(&t, c);
mp_clear(&t);
return MP_OKAY;
}
static int
mp_reduce(mp_int * x, mp_int * m, mp_int * mu)
{
mp_int q;
int res, um = m->used;
if ((res = mp_init_copy(&q, x)) != MP_OKAY) {
return res;
}
rshift_digits(&q, um - 1);
if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) {
if ((res = signed_multiply(&q, mu, &q)) != MP_OKAY) {
goto CLEANUP;
}
} else {
if ((res = basic_multiply_partial_upper(&q, mu, &q, um)) != MP_OKAY) {
goto CLEANUP;
}
}
rshift_digits(&q, um + 1);
if ((res = modulo_2_to_power(x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
goto CLEANUP;
}
if ((res = basic_multiply_partial_lower(&q, m, &q, um + 1)) != MP_OKAY) {
goto CLEANUP;
}
if ((res = signed_subtract(x, &q, x)) != MP_OKAY) {
goto CLEANUP;
}
if (compare_digit(x, 0) == MP_LT) {
set_word(&q, 1);
if ((res = lshift_digits(&q, um + 1)) != MP_OKAY) {
goto CLEANUP;
}
if ((res = signed_add(x, &q, x)) != MP_OKAY) {
goto CLEANUP;
}
}
while (signed_compare(x, m) != MP_LT) {
if ((res = basic_subtract(x, m, x)) != MP_OKAY) {
goto CLEANUP;
}
}
CLEANUP:
mp_clear(&q);
return res;
}
static int
mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
{
int res;
mp_int tmp;
if ((res = mp_init(&tmp)) != MP_OKAY) {
return res;
}
if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
goto ERR;
}
if ((res = basic_subtract(&tmp, a, d)) != MP_OKAY) {
goto ERR;
}
ERR:
mp_clear(&tmp);
return res;
}
static int
mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
{
mp_int q;
int p, res;
if ((res = mp_init(&q)) != MP_OKAY) {
return res;
}
p = mp_count_bits(n);
top:
if ((res = rshift_bits(a, p, &q, a)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_multiply(&q, d, &q)) != MP_OKAY) {
goto ERR;
}
if ((res = basic_add(a, &q, a)) != MP_OKAY) {
goto ERR;
}
if (compare_magnitude(a, n) != MP_LT) {
basic_subtract(a, n, a);
goto top;
}
ERR:
mp_clear(&q);
return res;
}
static int
toom_cook_square(mp_int *a, mp_int *b)
{
mp_int w0, w1, w2, w3, w4, tmp1, a0, a1, a2;
int res, B;
if ((res = mp_init_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL)) != MP_OKAY) {
return res;
}
B = a->used / 3;
if ((res = modulo_2_to_power(a, DIGIT_BIT * B, &a0)) != MP_OKAY) {
goto ERR;
}
if ((res = mp_copy(a, &a1)) != MP_OKAY) {
goto ERR;
}
rshift_digits(&a1, B);
modulo_2_to_power(&a1, DIGIT_BIT * B, &a1);
if ((res = mp_copy(a, &a2)) != MP_OKAY) {
goto ERR;
}
rshift_digits(&a2, B*2);
if ((res = square(&a0, &w0)) != MP_OKAY) {
goto ERR;
}
if ((res = square(&a2, &w4)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&a0, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&tmp1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a2, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = square(&tmp1, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&a2, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = doubled(&tmp1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = square(&tmp1, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&a2, &a1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, &a0, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = square(&tmp1, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w1, &w4, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w3, &w0, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = half(&w1, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = half(&w3, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w2, &w0, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w2, &w4, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w1, &w2, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w3, &w2, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_bits(&w0, 3, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w1, &tmp1, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_bits(&w4, 3, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w3, &tmp1, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = multiply_digit(&w2, 3, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w2, &w1, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w2, &w3, &w2)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w1, &w2, &w1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_subtract(&w3, &w2, &w3)) != MP_OKAY) {
goto ERR;
}
if ((res = third(&w1, &w1, NULL)) != MP_OKAY) {
goto ERR;
}
if ((res = third(&w3, &w3, NULL)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_digits(&w1, 1*B)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_digits(&w2, 2*B)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_digits(&w3, 3*B)) != MP_OKAY) {
goto ERR;
}
if ((res = lshift_digits(&w4, 4*B)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&w0, &w1, b)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&w2, &w3, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&w4, &tmp1, &tmp1)) != MP_OKAY) {
goto ERR;
}
if ((res = signed_add(&tmp1, b, b)) != MP_OKAY) {
goto ERR;
}
ERR:
mp_clear_multi(&w0, &w1, &w2, &w3, &w4, &a0, &a1, &a2, &tmp1, NULL);
return res;
}
static int
karatsuba_square(mp_int * a, mp_int * b)
{
mp_int x0, x1, t1, t2, x0x0, x1x1;
int B, err;
err = MP_MEM;
B = a->used;
B = (unsigned)B >> 1;
if (mp_init_size(&x0, B) != MP_OKAY) {
goto ERR;
}
if (mp_init_size(&x1, a->used - B) != MP_OKAY) {
goto X0;
}
if (mp_init_size(&t1, a->used * 2) != MP_OKAY) {
goto X1;
}
if (mp_init_size(&t2, a->used * 2) != MP_OKAY) {
goto T1;
}
if (mp_init_size(&x0x0, B * 2) != MP_OKAY) {
goto T2;
}
if (mp_init_size(&x1x1, (a->used - B) * 2) != MP_OKAY) {
goto X0X0;
}
memcpy(x0.dp, a->dp, B * sizeof(*x0.dp));
memcpy(x1.dp, &a->dp[B], (a->used - B) * sizeof(*x1.dp));
x0.used = B;
x1.used = a->used - B;
trim_unused_digits(&x0);
if (square(&x0, &x0x0) != MP_OKAY) {
goto X1X1;
}
if (square(&x1, &x1x1) != MP_OKAY) {
goto X1X1;
}
if (basic_add(&x1, &x0, &t1) != MP_OKAY) {
goto X1X1;
}
if (square(&t1, &t1) != MP_OKAY) {
goto X1X1;
}
if (basic_add(&x0x0, &x1x1, &t2) != MP_OKAY) {
goto X1X1;
}
if (basic_subtract(&t1, &t2, &t1) != MP_OKAY) {
goto X1X1;
}
if (lshift_digits(&t1, B) != MP_OKAY) {
goto X1X1;
}
if (lshift_digits(&x1x1, B * 2) != MP_OKAY) {
goto X1X1;
}
if (signed_add(&x0x0, &t1, &t1) != MP_OKAY) {
goto X1X1;
}
if (signed_add(&t1, &x1x1, b) != MP_OKAY) {
goto X1X1;
}
err = MP_OKAY;
X1X1:
mp_clear(&x1x1);
X0X0:
mp_clear(&x0x0);
T2:
mp_clear(&t2);
T1:
mp_clear(&t1);
X1:
mp_clear(&x1);
X0:
mp_clear(&x0);
ERR:
return err;
}
static int
fast_basic_square(mp_int * a, mp_int * b)
{
int olduse, res, pa, ix, iz;
mp_digit W[MP_WARRAY], *tmpx;
mp_word W1;
pa = a->used + a->used;
if (b->alloc < pa) {
if ((res = mp_grow(b, pa)) != MP_OKAY) {
return res;
}
}
W1 = 0;
for (ix = 0; ix < pa; ix++) {
int tx, ty, iy;
mp_word _W;
mp_digit *tmpy;
_W = 0;
ty = MIN(a->used-1, ix);
tx = ix - ty;
tmpx = a->dp + tx;
tmpy = a->dp + ty;
iy = MIN(a->used-tx, ty+1);
iy = MIN(iy, (int)((unsigned)(ty-tx+1)>>1));
for (iz = 0; iz < iy; iz++) {
_W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
}
_W = _W + _W + W1;
if ((ix&1) == 0) {
_W += ((mp_word)a->dp[(unsigned)ix>>1])*((mp_word)a->dp[(unsigned)ix>>1]);
}
W[ix] = (mp_digit)(_W & MP_MASK);
W1 = _W >> ((mp_word)DIGIT_BIT);
}
olduse = b->used;
b->used = a->used+a->used;
{
mp_digit *tmpb;
tmpb = b->dp;
for (ix = 0; ix < pa; ix++) {
*tmpb++ = W[ix] & MP_MASK;
}
for (; ix < olduse; ix++) {
*tmpb++ = 0;
}
}
trim_unused_digits(b);
return MP_OKAY;
}
static int
basic_square(mp_int * a, mp_int * b)
{
mp_int t;
int res, ix, iy, pa;
mp_word r;
mp_digit carry, tmpx, *tmpt;
pa = a->used;
if ((res = mp_init_size(&t, 2*pa + 1)) != MP_OKAY) {
return res;
}
t.used = 2*pa + 1;
for (ix = 0; ix < pa; ix++) {
r = ((mp_word) t.dp[2*ix]) +
((mp_word)a->dp[ix])*((mp_word)a->dp[ix]);
t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK));
carry = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
tmpx = a->dp[ix];
tmpt = t.dp + (2*ix + 1);
for (iy = ix + 1; iy < pa; iy++) {
r = ((mp_word)tmpx) * ((mp_word)a->dp[iy]);
r = ((mp_word) *tmpt) + r + r + ((mp_word) carry);
*tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
carry = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
}
while (carry != ((mp_digit) 0)) {
r = ((mp_word) *tmpt) + ((mp_word) carry);
*tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
carry = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
}
}
trim_unused_digits(&t);
mp_exch(&t, b);
mp_clear(&t);
return MP_OKAY;
}
#define TOOM_SQR_CUTOFF 400
#define KARATSUBA_SQR_CUTOFF 120
static int
square(mp_int * a, mp_int * b)
{
int res;
if (a->used >= TOOM_SQR_CUTOFF) {
res = toom_cook_square(a, b);
} else if (a->used >= KARATSUBA_SQR_CUTOFF) {
res = karatsuba_square(a, b);
} else {
if (can_use_fast_column_array(a->used + a->used + 1, a->used)) {
res = fast_basic_square(a, b);
} else {
res = basic_square(a, b);
}
}
b->sign = MP_ZPOS;
return res;
}
static inline int
find_window_size(mp_int *X)
{
int x;
x = mp_count_bits(X);
return (x <= 7) ? 2 : (x <= 36) ? 3 : (x <= 140) ? 4 : (x <= 450) ? 5 : (x <= 1303) ? 6 : (x <= 3529) ? 7 : 8;
}
#define TAB_SIZE 256
static int
basic_exponent_mod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
{
mp_digit buf;
mp_int M[TAB_SIZE], res, mu;
int err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
int (*redux)(mp_int*,mp_int*,mp_int*);
winsize = find_window_size(X);
if ((err = mp_init(&M[1])) != MP_OKAY) {
return err;
}
for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
if ((err = mp_init(&M[x])) != MP_OKAY) {
for (y = 1<<(winsize-1); y < x; y++) {
mp_clear(&M[y]);
}
mp_clear(&M[1]);
return err;
}
}
if ((err = mp_init(&mu)) != MP_OKAY) {
goto LBL_M;
}
if (redmode == 0) {
if ((err = mp_reduce_setup(&mu, P)) != MP_OKAY) {
goto LBL_MU;
}
redux = mp_reduce;
} else {
if ((err = mp_reduce_2k_setup_l(P, &mu)) != MP_OKAY) {
goto LBL_MU;
}
redux = mp_reduce_2k_l;
}
if ((err = modulo(G, P, &M[1])) != MP_OKAY) {
goto LBL_MU;
}
if ((err = mp_copy( &M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
goto LBL_MU;
}
for (x = 0; x < (winsize - 1); x++) {
if ((err = square(&M[1 << (winsize - 1)],
&M[1 << (winsize - 1)])) != MP_OKAY) {
goto LBL_MU;
}
if ((err = redux(&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) {
goto LBL_MU;
}
}
for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
if ((err = signed_multiply(&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
goto LBL_MU;
}
if ((err = redux(&M[x], P, &mu)) != MP_OKAY) {
goto LBL_MU;
}
}
if ((err = mp_init(&res)) != MP_OKAY) {
goto LBL_MU;
}
set_word(&res, 1);
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 = (unsigned)(buf >> (mp_digit)(DIGIT_BIT - 1)) & 1;
buf <<= (mp_digit)1;
if (mode == 0 && y == 0) {
continue;
}
if (mode == 1 && y == 0) {
if ((err = square(&res, &res)) != MP_OKAY) {
goto LBL_RES;
}
if ((err = redux(&res, P, &mu)) != MP_OKAY) {
goto LBL_RES;
}
continue;
}
bitbuf |= (y << (winsize - ++bitcpy));
mode = 2;
if (bitcpy == winsize) {
for (x = 0; x < winsize; x++) {
if ((err = square(&res, &res)) != MP_OKAY) {
goto LBL_RES;
}
if ((err = redux(&res, P, &mu)) != MP_OKAY) {
goto LBL_RES;
}
}
if ((err = signed_multiply(&res, &M[bitbuf], &res)) != MP_OKAY) {
goto LBL_RES;
}
if ((err = redux(&res, P, &mu)) != MP_OKAY) {
goto LBL_RES;
}
bitcpy = 0;
bitbuf = 0;
mode = 1;
}
}
if (mode == 2 && bitcpy > 0) {
for (x = 0; x < bitcpy; x++) {
if ((err = square(&res, &res)) != MP_OKAY) {
goto LBL_RES;
}
if ((err = redux(&res, P, &mu)) != MP_OKAY) {
goto LBL_RES;
}
bitbuf <<= 1;
if ((bitbuf & (1 << winsize)) != 0) {
if ((err = signed_multiply(&res, &M[1], &res)) != MP_OKAY) {
goto LBL_RES;
}
if ((err = redux(&res, P, &mu)) != MP_OKAY) {
goto LBL_RES;
}
}
}
}
mp_exch(&res, Y);
err = MP_OKAY;
LBL_RES:
mp_clear(&res);
LBL_MU:
mp_clear(&mu);
LBL_M:
mp_clear(&M[1]);
for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
mp_clear(&M[x]);
}
return err;
}
static int
is_diminished_radix_modulus(mp_int *a)
{
int ix;
if (a->used < 2) {
return 0;
}
for (ix = 1; ix < a->used; ix++) {
if (a->dp[ix] != MP_MASK) {
return 0;
}
}
return 1;
}
static int
mp_reduce_is_2k(mp_int *a)
{
int ix, iy, iw;
mp_digit iz;
if (a->used == 0) {
return MP_NO;
}
if (a->used == 1) {
return MP_YES;
}
if (a->used > 1) {
iy = mp_count_bits(a);
iz = 1;
iw = 1;
for (ix = DIGIT_BIT; ix < iy; ix++) {
if ((a->dp[iw] & iz) == 0) {
return MP_NO;
}
iz <<= 1;
if (iz > (mp_digit)MP_MASK) {
++iw;
iz = 1;
}
}
}
return MP_YES;
}
static int
multiply_modulo(mp_int *d, mp_int * a, mp_int * b, mp_int * c)
{
mp_int t;
int res;
if ((res = mp_init(&t)) != MP_OKAY) {
return res;
}
if ((res = signed_multiply(a, b, &t)) != MP_OKAY) {
mp_clear(&t);
return res;
}
res = modulo(&t, c, d);
mp_clear(&t);
return res;
}
static int
mp_montgomery_setup(mp_int * n, mp_digit * rho)
{
mp_digit x, b;
b = n->dp[0];
if ((b & 1) == 0) {
return MP_VAL;
}
x = (((b + 2) & 4) << 1) + b;
x *= 2 - b * x;
x *= 2 - b * x;
x *= 2 - b * x;
if (sizeof(mp_digit) == 8) {
x *= 2 - b * x;
}
*rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK;
return MP_OKAY;
}
static int
fast_mp_montgomery_reduce(mp_int * x, mp_int * n, mp_digit rho)
{
int ix, res, olduse;
mp_word W[MP_WARRAY];
olduse = x->used;
if (x->alloc < n->used + 1) {
if ((res = mp_grow(x, n->used + 1)) != MP_OKAY) {
return res;
}
}
{
mp_word *_W;
mp_digit *tmpx;
_W = W;
tmpx = x->dp;
for (ix = 0; ix < x->used; ix++) {
*_W++ = *tmpx++;
}
for (; ix < n->used * 2 + 1; ix++) {
*_W++ = 0;
}
}
for (ix = 0; ix < n->used; ix++) {
mp_digit mu;
mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK);
{
int iy;
mp_digit *tmpn;
mp_word *_W;
tmpn = n->dp;
_W = W + ix;
for (iy = 0; iy < n->used; iy++) {
*_W++ += ((mp_word)mu) * ((mp_word)*tmpn++);
}
}
W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT);
}
{
mp_digit *tmpx;
mp_word *_W, *_W1;
_W1 = W + ix;
_W = W + ++ix;
for (; ix <= n->used * 2 + 1; ix++) {
*_W++ += *_W1++ >> ((mp_word) DIGIT_BIT);
}
tmpx = x->dp;
_W = W + n->used;
for (ix = 0; ix < n->used + 1; ix++) {
*tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK));
}
for (; ix < olduse; ix++) {
*tmpx++ = 0;
}
}
x->used = n->used + 1;
trim_unused_digits(x);
if (compare_magnitude(x, n) != MP_LT) {
return basic_subtract(x, n, x);
}
return MP_OKAY;
}
static int
mp_montgomery_reduce(mp_int * x, mp_int * n, mp_digit rho)
{
int ix, res, digs;
mp_digit mu;
digs = n->used * 2 + 1;
if (can_use_fast_column_array(digs, n->used)) {
return fast_mp_montgomery_reduce(x, n, rho);
}
if (x->alloc < digs) {
if ((res = mp_grow(x, digs)) != MP_OKAY) {
return res;
}
}
x->used = digs;
for (ix = 0; ix < n->used; ix++) {
mu = (mp_digit) (((mp_word)x->dp[ix]) * ((mp_word)rho) & MP_MASK);
{
int iy;
mp_digit *tmpn, *tmpx, carry;
mp_word r;
tmpn = n->dp;
tmpx = x->dp + ix;
carry = 0;
for (iy = 0; iy < n->used; iy++) {
r = ((mp_word)mu) * ((mp_word)*tmpn++) +
((mp_word) carry) + ((mp_word) * tmpx);
carry = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
*tmpx++ = (mp_digit)(r & ((mp_word) MP_MASK));
}
while (carry) {
*tmpx += carry;
carry = *tmpx >> DIGIT_BIT;
*tmpx++ &= MP_MASK;
}
}
}
trim_unused_digits(x);
rshift_digits(x, n->used);
if (compare_magnitude(x, n) != MP_LT) {
return basic_subtract(x, n, x);
}
return MP_OKAY;
}
static void
diminished_radix_setup(mp_int *a, mp_digit *d)
{
*d = (mp_digit)((((mp_word)1) << ((mp_word)DIGIT_BIT)) -
((mp_word)a->dp[0]));
}
static int
diminished_radix_reduce(mp_int * x, mp_int * n, mp_digit k)
{
int err, i, m;
mp_word r;
mp_digit mu, *tmpx1, *tmpx2;
m = n->used;
if (x->alloc < m + m) {
if ((err = mp_grow(x, m + m)) != MP_OKAY) {
return err;
}
}
top:
tmpx1 = x->dp;
tmpx2 = x->dp + m;
mu = 0;
for (i = 0; i < m; i++) {
r = ((mp_word)*tmpx2++) * ((mp_word)k) + *tmpx1 + mu;
*tmpx1++ = (mp_digit)(r & MP_MASK);
mu = (mp_digit)(r >> ((mp_word)DIGIT_BIT));
}
*tmpx1++ = mu;
for (i = m + 1; i < x->used; i++) {
*tmpx1++ = 0;
}
trim_unused_digits(x);
if (compare_magnitude(x, n) != MP_LT) {
basic_subtract(x, n, x);
goto top;
}
return MP_OKAY;
}
static int
mp_reduce_2k_setup(mp_int *a, mp_digit *d)
{
int res, p;
mp_int tmp;
if ((res = mp_init(&tmp)) != MP_OKAY) {
return res;
}
p = mp_count_bits(a);
if ((res = mp_2expt(&tmp, p)) != MP_OKAY) {
mp_clear(&tmp);
return res;
}
if ((res = basic_subtract(&tmp, a, &tmp)) != MP_OKAY) {
mp_clear(&tmp);
return res;
}
*d = tmp.dp[0];
mp_clear(&tmp);
return MP_OKAY;
}
static int
mp_reduce_2k(mp_int *a, mp_int *n, mp_digit d)
{
mp_int q;
int p, res;
if ((res = mp_init(&q)) != MP_OKAY) {
return res;
}
p = mp_count_bits(n);
top:
if ((res = rshift_bits(a, p, &q, a)) != MP_OKAY) {
goto ERR;
}
if (d != 1) {
if ((res = multiply_digit(&q, d, &q)) != MP_OKAY) {
goto ERR;
}
}
if ((res = basic_add(a, &q, a)) != MP_OKAY) {
goto ERR;
}
if (compare_magnitude(a, n) != MP_LT) {
basic_subtract(a, n, a);
goto top;
}
ERR:
mp_clear(&q);
return res;
}
static int
mp_montgomery_calc_normalization(mp_int * a, mp_int * b)
{
int x, bits, res;
bits = mp_count_bits(b) % DIGIT_BIT;
if (b->used > 1) {
if ((res = mp_2expt(a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) {
return res;
}
} else {
set_word(a, 1);
bits = 1;
}
for (x = bits - 1; x < (int)DIGIT_BIT; x++) {
if ((res = doubled(a, a)) != MP_OKAY) {
return res;
}
if (compare_magnitude(a, b) != MP_LT) {
if ((res = basic_subtract(a, b, a)) != MP_OKAY) {
return res;
}
}
}
return MP_OKAY;
}
#define TAB_SIZE 256
static int
fast_exponent_modulo(mp_int * G, mp_int * X, 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*,mp_int*,mp_digit);
winsize = find_window_size(X);
if ((err = mp_init(&M[1])) != MP_OKAY) {
return err;
}
for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
if ((err = mp_init(&M[x])) != MP_OKAY) {
for (y = 1<<(winsize-1); y < x; y++) {
mp_clear(&M[y]);
}
mp_clear(&M[1]);
return err;
}
}
if (redmode == 0) {
if ((err = mp_montgomery_setup(P, &mp)) != MP_OKAY) {
goto LBL_M;
}
if (can_use_fast_column_array(P->used + P->used + 1, P->used)) {
redux = fast_mp_montgomery_reduce;
} else {
redux = mp_montgomery_reduce;
}
} else if (redmode == 1) {
diminished_radix_setup(P, &mp);
redux = diminished_radix_reduce;
} else {
if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) {
goto LBL_M;
}
redux = mp_reduce_2k;
}
if ((err = mp_init(&res)) != MP_OKAY) {
goto LBL_M;
}
if (redmode == 0) {
if ((err = mp_montgomery_calc_normalization(&res, P)) != MP_OKAY) {
goto LBL_RES;
}
if ((err = multiply_modulo(&M[1], G, &res, P)) != MP_OKAY) {
goto LBL_RES;
}
} else {
set_word(&res, 1);
if ((err = modulo(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 = square(&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 = signed_multiply(&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 = (int)(mp_digit)((mp_digit)buf >> (unsigned)(DIGIT_BIT - 1)) & 1;
buf <<= (mp_digit)1;
if (mode == 0 && y == 0) {
continue;
}
if (mode == 1 && y == 0) {
if ((err = square(&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 = square(&res, &res)) != MP_OKAY) {
goto LBL_RES;
}
if ((err = (*redux)(&res, P, mp)) != MP_OKAY) {
goto LBL_RES;
}
}
if ((err = signed_multiply(&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 = square(&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 = signed_multiply(&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;
}
static int
exponent_modulo(mp_int * G, mp_int * X, mp_int * P, mp_int *Y)
{
int diminished_radix;
if (P->sign == MP_NEG) {
return MP_VAL;
}
if (X->sign == MP_NEG) {
mp_int tmpG, tmpX;
int err;
if ((err = mp_init(&tmpG)) != MP_OKAY) {
return err;
}
if ((err = modular_inverse(&tmpG, G, P)) != MP_OKAY) {
mp_clear(&tmpG);
return err;
}
if ((err = mp_init(&tmpX)) != MP_OKAY) {
mp_clear(&tmpG);
return err;
}
if ((err = absolute(X, &tmpX)) != MP_OKAY) {
mp_clear_multi(&tmpG, &tmpX, NULL);
return err;
}
err = exponent_modulo(&tmpG, &tmpX, P, Y);
mp_clear_multi(&tmpG, &tmpX, NULL);
return err;
}
if (mp_reduce_is_2k_l(P) == MP_YES) {
return basic_exponent_mod(G, X, P, Y, 1);
}
diminished_radix = is_diminished_radix_modulus(P);
if (!diminished_radix) {
diminished_radix = mp_reduce_is_2k(P) << 1;
}
if (BN_is_odd(P) == 1 || diminished_radix) {
return fast_exponent_modulo(G, X, P, Y, diminished_radix);
}
return basic_exponent_mod(G, X, P, Y, 0);
}
static void
bn_reverse(unsigned char *s, int len)
{
int ix, iy;
uint8_t t;
for (ix = 0, iy = len - 1; ix < iy ; ix++, --iy) {
t = s[ix];
s[ix] = s[iy];
s[iy] = t;
}
}
static inline int
is_power_of_two(mp_digit b, int *p)
{
int x;
if ((b==0) || (b & (b-1))) {
return 0;
}
for (x = 0; x < DIGIT_BIT; x++) {
if (b == (((mp_digit)1)<<x)) {
*p = x;
return 1;
}
}
return 0;
}
static int
signed_divide_word(mp_int *a, mp_digit b, mp_int *c, mp_digit *d)
{
mp_int q;
mp_word w;
mp_digit t;
int res, ix;
if (b == 0) {
return MP_VAL;
}
if (b == 1 || MP_ISZERO(a) == 1) {
if (d != NULL) {
*d = 0;
}
if (c != NULL) {
return mp_copy(a, c);
}
return MP_OKAY;
}
if (is_power_of_two(b, &ix) == 1) {
if (d != NULL) {
*d = a->dp[0] & ((((mp_digit)1)<<ix) - 1);
}
if (c != NULL) {
return rshift_bits(a, ix, c, NULL);
}
return MP_OKAY;
}
if (b == 3) {
return third(a, c, d);
}
if ((res = mp_init_size(&q, a->used)) != MP_OKAY) {
return res;
}
q.used = a->used;
q.sign = a->sign;
w = 0;
for (ix = a->used - 1; ix >= 0; ix--) {
w = (w << ((mp_word)DIGIT_BIT)) | ((mp_word)a->dp[ix]);
if (w >= b) {
t = (mp_digit)(w / b);
w -= ((mp_word)t) * ((mp_word)b);
} else {
t = 0;
}
q.dp[ix] = (mp_digit)t;
}
if (d != NULL) {
*d = (mp_digit)w;
}
if (c != NULL) {
trim_unused_digits(&q);
mp_exch(&q, c);
}
mp_clear(&q);
return res;
}
static const mp_digit ltm_prime_tab[] = {
0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013,
0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035,
0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059,
0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F,
#ifndef MP_8BIT
0x0083,
0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD,
0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF,
0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107,
0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137,
0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167,
0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199,
0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9,
0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7,
0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239,
0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265,
0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293,
0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF,
0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301,
0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B,
0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371,
0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD,
0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5,
0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419,
0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449,
0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B,
0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7,
0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503,
0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529,
0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F,
0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3,
0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7,
0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623,
0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653
#endif
};
#define PRIME_SIZE __arraycount(ltm_prime_tab)
static inline int
mp_prime_is_divisible(mp_int *a, int *result)
{
int err, ix;
mp_digit res;
*result = MP_NO;
for (ix = 0; ix < (int)PRIME_SIZE; ix++) {
if ((err = signed_divide_word(a, ltm_prime_tab[ix], NULL, &res)) != MP_OKAY) {
return err;
}
if (res == 0) {
*result = MP_YES;
return MP_OKAY;
}
}
return MP_OKAY;
}
static int
add_single_digit(mp_int *a, mp_digit b, mp_int *c)
{
int res, ix, oldused;
mp_digit *tmpa, *tmpc, mu;
if (c->alloc < a->used + 1) {
if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) {
return res;
}
}
if (a->sign == MP_NEG && (a->used > 1 || a->dp[0] >= b)) {
a->sign = MP_ZPOS;
res = signed_subtract_word(a, b, c);
a->sign = c->sign = MP_NEG;
trim_unused_digits(c);
return res;
}
oldused = c->used;
c->sign = MP_ZPOS;
tmpa = a->dp;
tmpc = c->dp;
if (a->sign == MP_ZPOS) {
*tmpc = *tmpa++ + b;
mu = *tmpc >> DIGIT_BIT;
*tmpc++ &= MP_MASK;
for (ix = 1; ix < a->used; ix++) {
*tmpc = *tmpa++ + mu;
mu = *tmpc >> DIGIT_BIT;
*tmpc++ &= MP_MASK;
}
ix++;
*tmpc++ = mu;
c->used = a->used + 1;
} else {
c->used = 1;
if (a->used == 1) {
*tmpc++ = b - a->dp[0];
} else {
*tmpc++ = b;
}
ix = 1;
}
while (ix++ < oldused) {
*tmpc++ = 0;
}
trim_unused_digits(c);
return MP_OKAY;
}
static int
signed_subtract_word(mp_int *a, mp_digit b, mp_int *c)
{
mp_digit *tmpa, *tmpc, mu;
int res, ix, oldused;
if (c->alloc < a->used + 1) {
if ((res = mp_grow(c, a->used + 1)) != MP_OKAY) {
return res;
}
}
if (a->sign == MP_NEG) {
a->sign = MP_ZPOS;
res = add_single_digit(a, b, c);
a->sign = c->sign = MP_NEG;
trim_unused_digits(c);
return res;
}
oldused = c->used;
tmpa = a->dp;
tmpc = c->dp;
if ((a->used == 1 && a->dp[0] <= b) || a->used == 0) {
if (a->used == 1) {
*tmpc++ = b - *tmpa;
} else {
*tmpc++ = b;
}
ix = 1;
c->sign = MP_NEG;
c->used = 1;
} else {
c->sign = MP_ZPOS;
c->used = a->used;
*tmpc = *tmpa++ - b;
mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1);
*tmpc++ &= MP_MASK;
for (ix = 1; ix < a->used; ix++) {
*tmpc = *tmpa++ - mu;
mu = *tmpc >> (sizeof(mp_digit) * CHAR_BIT - 1);
*tmpc++ &= MP_MASK;
}
}
while (ix++ < oldused) {
*tmpc++ = 0;
}
trim_unused_digits(c);
return MP_OKAY;
}
static const int lnz[16] = {
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};
static int
mp_cnt_lsb(mp_int *a)
{
int x;
mp_digit q, qq;
if (MP_ISZERO(a) == 1) {
return 0;
}
for (x = 0; x < a->used && a->dp[x] == 0; x++) {
}
q = a->dp[x];
x *= DIGIT_BIT;
if ((q & 1) == 0) {
do {
qq = q & 15;
x += lnz[qq];
q >>= 4;
} while (qq == 0);
}
return x;
}
static int
square_modulo(mp_int *a, mp_int *b, mp_int *c)
{
int res;
mp_int t;
if ((res = mp_init(&t)) != MP_OKAY) {
return res;
}
if ((res = square(a, &t)) != MP_OKAY) {
mp_clear(&t);
return res;
}
res = modulo(&t, b, c);
mp_clear(&t);
return res;
}
static int
mp_prime_miller_rabin(mp_int *a, mp_int *b, int *result)
{
mp_int n1, y, r;
int s, j, err;
*result = MP_NO;
if (compare_digit(b, 1) != MP_GT) {
return MP_VAL;
}
if ((err = mp_init_copy(&n1, a)) != MP_OKAY) {
return err;
}
if ((err = signed_subtract_word(&n1, 1, &n1)) != MP_OKAY) {
goto LBL_N1;
}
if ((err = mp_init_copy(&r, &n1)) != MP_OKAY) {
goto LBL_N1;
}
s = mp_cnt_lsb(&r);
if ((err = rshift_bits(&r, s, &r, NULL)) != MP_OKAY) {
goto LBL_R;
}
if ((err = mp_init(&y)) != MP_OKAY) {
goto LBL_R;
}
if ((err = exponent_modulo(b, &r, a, &y)) != MP_OKAY) {
goto LBL_Y;
}
if (compare_digit(&y, 1) != MP_EQ && signed_compare(&y, &n1) != MP_EQ) {
j = 1;
while ((j <= (s - 1)) && signed_compare(&y, &n1) != MP_EQ) {
if ((err = square_modulo(&y, a, &y)) != MP_OKAY) {
goto LBL_Y;
}
if (compare_digit(&y, 1) == MP_EQ) {
goto LBL_Y;
}
++j;
}
if (signed_compare(&y, &n1) != MP_EQ) {
goto LBL_Y;
}
}
*result = MP_YES;
LBL_Y:
mp_clear(&y);
LBL_R:
mp_clear(&r);
LBL_N1:
mp_clear(&n1);
return err;
}
static int
mp_prime_is_prime(mp_int *a, int t, int *result)
{
mp_int b;
int ix, err, res;
*result = MP_NO;
if (t <= 0 || t > (int)PRIME_SIZE) {
return MP_VAL;
}
for (ix = 0; ix < (int)PRIME_SIZE; ix++) {
if (compare_digit(a, ltm_prime_tab[ix]) == MP_EQ) {
*result = 1;
return MP_OKAY;
}
}
if ((err = mp_prime_is_divisible(a, &res)) != MP_OKAY) {
return err;
}
if (res == MP_YES) {
return MP_OKAY;
}
if ((err = mp_init(&b)) != MP_OKAY) {
return err;
}
for (ix = 0; ix < t; ix++) {
set_word(&b, ltm_prime_tab[ix]);
if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
goto LBL_B;
}
if (res == MP_NO) {
goto LBL_B;
}
}
*result = MP_YES;
LBL_B:
mp_clear(&b);
return err;
}
static int
mp_radix_size(mp_int *a, int radix, int *size)
{
int res, digs;
mp_int t;
mp_digit d;
*size = 0;
if (radix == 2) {
*size = mp_count_bits(a) + (a->sign == MP_NEG ? 1 : 0) + 1;
return MP_OKAY;
}
if (radix < 2 || radix > 64) {
return MP_VAL;
}
if (MP_ISZERO(a) == MP_YES) {
*size = 2;
return MP_OKAY;
}
digs = 0;
if (a->sign == MP_NEG) {
++digs;
}
if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
return res;
}
t.sign = MP_ZPOS;
while (MP_ISZERO(&t) == MP_NO) {
if ((res = signed_divide_word(&t, (mp_digit) radix, &t, &d)) != MP_OKAY) {
mp_clear(&t);
return res;
}
++digs;
}
mp_clear(&t);
*size = digs + 1;
return MP_OKAY;
}
static const char *mp_s_rmap = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+/";
static int
mp_toradix_n(mp_int * a, char *str, int radix, int maxlen)
{
int res, digs;
mp_int t;
mp_digit d;
char *_s = str;
if (maxlen < 2 || radix < 2 || radix > 64) {
return MP_VAL;
}
if (MP_ISZERO(a) == MP_YES) {
*str++ = '0';
*str = '\0';
return MP_OKAY;
}
if ((res = mp_init_copy(&t, a)) != MP_OKAY) {
return res;
}
if (t.sign == MP_NEG) {
++_s;
*str++ = '-';
t.sign = MP_ZPOS;
--maxlen;
}
digs = 0;
while (MP_ISZERO(&t) == 0) {
if (--maxlen < 1) {
break;
}
if ((res = signed_divide_word(&t, (mp_digit) radix, &t, &d)) != MP_OKAY) {
mp_clear(&t);
return res;
}
*str++ = mp_s_rmap[d];
++digs;
}
bn_reverse((unsigned char *)_s, digs);
*str = '\0';
mp_clear(&t);
return MP_OKAY;
}
static char *
formatbn(const BIGNUM *a, const int radix)
{
char *s;
int len;
if (mp_radix_size(__UNCONST(a), radix, &len) != MP_OKAY) {
return NULL;
}
if ((s = allocate(1, (size_t)len)) != NULL) {
if (mp_toradix_n(__UNCONST(a), s, radix, len) != MP_OKAY) {
deallocate(s, (size_t)len);
return NULL;
}
}
return s;
}
static int
mp_getradix_num(mp_int *a, int radix, char *s)
{
int err, ch, neg, y;
mp_zero(a);
if ((ch = *s++) == '-') {
neg = MP_NEG;
ch = *s++;
} else {
neg = MP_ZPOS;
}
for (;;) {
for (y = 0; y < radix; y++) {
if (mp_s_rmap[y] == ch) {
break;
}
}
if (y == radix) {
break;
}
if ((err = multiply_digit(a, radix, a)) != MP_OKAY) {
return err;
}
if ((err = add_single_digit(a, y, a)) != MP_OKAY) {
return err;
}
ch = *s++;
}
if (compare_digit(a, 0) != MP_EQ) {
a->sign = neg;
}
return MP_OKAY;
}
static int
getbn(BIGNUM **a, const char *str, int radix)
{
int len;
if (a == NULL || str == NULL || (*a = BN_new()) == NULL) {
return 0;
}
if (mp_getradix_num(*a, radix, __UNCONST(str)) != MP_OKAY) {
return 0;
}
mp_radix_size(__UNCONST(*a), radix, &len);
return len - 1;
}
static int
subtract_modulo(mp_int *a, mp_int *b, mp_int *c, mp_int *d)
{
int res;
mp_int t;
if ((res = mp_init(&t)) != MP_OKAY) {
return res;
}
if ((res = signed_subtract(a, b, &t)) != MP_OKAY) {
mp_clear(&t);
return res;
}
res = modulo(&t, c, d);
mp_clear(&t);
return res;
}
static int
mp_gcd(mp_int *a, mp_int *b, mp_int *c)
{
mp_int u, v;
int k, u_lsb, v_lsb, res;
if (BN_is_zero(a) == MP_YES) {
return absolute(b, c);
}
if (BN_is_zero(b) == MP_YES) {
return absolute(a, c);
}
if ((res = mp_init_copy(&u, a)) != MP_OKAY) {
return res;
}
if ((res = mp_init_copy(&v, b)) != MP_OKAY) {
goto LBL_U;
}
u.sign = v.sign = MP_ZPOS;
u_lsb = mp_cnt_lsb(&u);
v_lsb = mp_cnt_lsb(&v);
k = MIN(u_lsb, v_lsb);
if (k > 0) {
if ((res = rshift_bits(&u, k, &u, NULL)) != MP_OKAY) {
goto LBL_V;
}
if ((res = rshift_bits(&v, k, &v, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
if (u_lsb != k) {
if ((res = rshift_bits(&u, u_lsb - k, &u, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
if (v_lsb != k) {
if ((res = rshift_bits(&v, v_lsb - k, &v, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
while (BN_is_zero(&v) == 0) {
if (compare_magnitude(&u, &v) == MP_GT) {
mp_exch(&u, &v);
}
if ((res = signed_subtract(&v, &u, &v)) != MP_OKAY) {
goto LBL_V;
}
if ((res = rshift_bits(&v, mp_cnt_lsb(&v), &v, NULL)) != MP_OKAY) {
goto LBL_V;
}
}
if ((res = lshift_bits(&u, k, c)) != MP_OKAY) {
goto LBL_V;
}
c->sign = MP_ZPOS;
res = MP_OKAY;
LBL_V:
mp_clear (&u);
LBL_U:
mp_clear (&v);
return res;
}
BIGNUM *
BN_bin2bn(const uint8_t *data, int len, BIGNUM *ret)
{
if (data == NULL) {
return BN_new();
}
if (ret == NULL) {
ret = BN_new();
}
return (mp_read_unsigned_bin(ret, data, len) == MP_OKAY) ? ret : NULL;
}
int
BN_bn2bin(const BIGNUM *a, unsigned char *b)
{
BIGNUM t;
int x;
if (a == NULL || b == NULL) {
return -1;
}
if (mp_init_copy (&t, __UNCONST(a)) != MP_OKAY) {
return -1;
}
for (x = 0; !BN_is_zero(&t) ; ) {
b[x++] = (unsigned char) (t.dp[0] & 0xff);
if (rshift_bits(&t, 8, &t, NULL) != MP_OKAY) {
mp_clear(&t);
return -1;
}
}
bn_reverse(b, x);
mp_clear(&t);
return x;
}
void
BN_init(BIGNUM *a)
{
if (a != NULL) {
mp_init(a);
}
}
BIGNUM *
BN_new(void)
{
BIGNUM *a;
if ((a = allocate(1, sizeof(*a))) != NULL) {
mp_init(a);
}
return a;
}
int
BN_copy(BIGNUM *b, const BIGNUM *a)
{
if (a == NULL || b == NULL) {
return MP_VAL;
}
return mp_copy(__UNCONST(a), b);
}
BIGNUM *
BN_dup(const BIGNUM *a)
{
BIGNUM *ret;
if (a == NULL) {
return NULL;
}
if ((ret = BN_new()) != NULL) {
BN_copy(ret, a);
}
return ret;
}
void
BN_swap(BIGNUM *a, BIGNUM *b)
{
if (a && b) {
mp_exch(a, b);
}
}
int
BN_lshift(BIGNUM *r, const BIGNUM *a, int n)
{
if (r == NULL || a == NULL || n < 0) {
return 0;
}
BN_copy(r, a);
return lshift_digits(r, n) == MP_OKAY;
}
int
BN_lshift1(BIGNUM *r, BIGNUM *a)
{
if (r == NULL || a == NULL) {
return 0;
}
BN_copy(r, a);
return lshift_digits(r, 1) == MP_OKAY;
}
int
BN_rshift(BIGNUM *r, const BIGNUM *a, int n)
{
if (r == NULL || a == NULL || n < 0) {
return MP_VAL;
}
BN_copy(r, a);
return rshift_digits(r, n) == MP_OKAY;
}
int
BN_rshift1(BIGNUM *r, BIGNUM *a)
{
if (r == NULL || a == NULL) {
return 0;
}
BN_copy(r, a);
return rshift_digits(r, 1) == MP_OKAY;
}
int
BN_set_word(BIGNUM *a, BN_ULONG w)
{
if (a == NULL) {
return 0;
}
set_word(a, w);
return 1;
}
int
BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
{
if (a == NULL || b == NULL || r == NULL) {
return 0;
}
return signed_add(__UNCONST(a), __UNCONST(b), r) == MP_OKAY;
}
int
BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
{
if (a == NULL || b == NULL || r == NULL) {
return 0;
}
return signed_subtract(__UNCONST(a), __UNCONST(b), r) == MP_OKAY;
}
int
BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
{
if (a == NULL || b == NULL || r == NULL) {
return 0;
}
USE_ARG(ctx);
return signed_multiply(__UNCONST(a), __UNCONST(b), r) == MP_OKAY;
}
int
BN_div(BIGNUM *dv, BIGNUM *rem, const BIGNUM *a, const BIGNUM *d, BN_CTX *ctx)
{
if ((dv == NULL && rem == NULL) || a == NULL || d == NULL) {
return 0;
}
USE_ARG(ctx);
return signed_divide(dv, rem, __UNCONST(a), __UNCONST(d)) == MP_OKAY;
}
int
BN_bitop(BIGNUM *r, const BIGNUM *a, char op, const BIGNUM *b)
{
unsigned ndigits;
mp_digit ad;
mp_digit bd;
int i;
if (a == NULL || b == NULL || r == NULL) {
return 0;
}
if (BN_cmp(__UNCONST(a), __UNCONST(b)) >= 0) {
BN_copy(r, a);
ndigits = a->used;
} else {
BN_copy(r, b);
ndigits = b->used;
}
for (i = 0 ; i < (int)ndigits ; i++) {
ad = (i > a->used) ? 0 : a->dp[i];
bd = (i > b->used) ? 0 : b->dp[i];
switch(op) {
case '&':
r->dp[i] = (ad & bd);
break;
case '|':
r->dp[i] = (ad | bd);
break;
case '^':
r->dp[i] = (ad ^ bd);
break;
default:
break;
}
}
return 1;
}
void
BN_free(BIGNUM *a)
{
if (a) {
mp_clear(a);
free(a);
}
}
void
BN_clear(BIGNUM *a)
{
if (a) {
mp_clear(a);
}
}
void
BN_clear_free(BIGNUM *a)
{
BN_clear(a);
free(a);
}
int
BN_num_bytes(const BIGNUM *a)
{
if (a == NULL) {
return MP_VAL;
}
return mp_unsigned_bin_size(__UNCONST(a));
}
int
BN_num_bits(const BIGNUM *a)
{
if (a == NULL) {
return 0;
}
return mp_count_bits(a);
}
void
BN_set_negative(BIGNUM *a, int n)
{
if (a) {
a->sign = (n) ? MP_NEG : 0;
}
}
int
BN_cmp(BIGNUM *a, BIGNUM *b)
{
if (a == NULL || b == NULL) {
return MP_VAL;
}
switch(signed_compare(a, b)) {
case MP_LT:
return -1;
case MP_GT:
return 1;
case MP_EQ:
default:
return 0;
}
}
int
BN_mod_exp(BIGNUM *Y, BIGNUM *G, BIGNUM *X, BIGNUM *P, BN_CTX *ctx)
{
if (Y == NULL || G == NULL || X == NULL || P == NULL) {
return MP_VAL;
}
USE_ARG(ctx);
return exponent_modulo(G, X, P, Y) == MP_OKAY;
}
BIGNUM *
BN_mod_inverse(BIGNUM *r, BIGNUM *a, const BIGNUM *n, BN_CTX *ctx)
{
USE_ARG(ctx);
if (r == NULL || a == NULL || n == NULL) {
return NULL;
}
return (modular_inverse(r, a, __UNCONST(n)) == MP_OKAY) ? r : NULL;
}
int
BN_mod_mul(BIGNUM *ret, BIGNUM *a, BIGNUM *b, const BIGNUM *m, BN_CTX *ctx)
{
USE_ARG(ctx);
if (ret == NULL || a == NULL || b == NULL || m == NULL) {
return 0;
}
return multiply_modulo(ret, a, b, __UNCONST(m)) == MP_OKAY;
}
BN_CTX *
BN_CTX_new(void)
{
return allocate(1, sizeof(BN_CTX));
}
void
BN_CTX_init(BN_CTX *c)
{
if (c != NULL) {
c->arraysize = 15;
if ((c->v = allocate(sizeof(*c->v), c->arraysize)) == NULL) {
c->arraysize = 0;
}
}
}
BIGNUM *
BN_CTX_get(BN_CTX *ctx)
{
if (ctx == NULL || ctx->v == NULL || ctx->arraysize == 0 || ctx->count == ctx->arraysize - 1) {
return NULL;
}
return ctx->v[ctx->count++] = BN_new();
}
void
BN_CTX_start(BN_CTX *ctx)
{
BN_CTX_init(ctx);
}
void
BN_CTX_free(BN_CTX *c)
{
unsigned i;
if (c != NULL && c->v != NULL) {
for (i = 0 ; i < c->count ; i++) {
BN_clear_free(c->v[i]);
}
deallocate(c->v, sizeof(*c->v) * c->arraysize);
}
}
void
BN_CTX_end(BN_CTX *ctx)
{
BN_CTX_free(ctx);
}
char *
BN_bn2hex(const BIGNUM *a)
{
return (a == NULL) ? NULL : formatbn(a, 16);
}
char *
BN_bn2dec(const BIGNUM *a)
{
return (a == NULL) ? NULL : formatbn(a, 10);
}
char *
BN_bn2radix(const BIGNUM *a, unsigned radix)
{
return (a == NULL) ? NULL : formatbn(a, (int)radix);
}
int
BN_print_fp(FILE *fp, const BIGNUM *a)
{
char *s;
int ret;
if (fp == NULL || a == NULL) {
return 0;
}
s = BN_bn2hex(a);
ret = fprintf(fp, "%s", s);
deallocate(s, strlen(s) + 1);
return ret;
}
#ifdef BN_RAND_NEEDED
int
BN_rand(BIGNUM *rnd, int bits, int top, int bottom)
{
uint64_t r;
int digits;
int i;
if (rnd == NULL) {
return 0;
}
mp_init_size(rnd, digits = howmany(bits, DIGIT_BIT));
for (i = 0 ; i < digits ; i++) {
r = (uint64_t)arc4random();
r <<= 32;
r |= arc4random();
rnd->dp[i] = (r & MP_MASK);
rnd->used += 1;
}
if (top == 0) {
rnd->dp[rnd->used - 1] |= (((mp_digit)1)<<((mp_digit)DIGIT_BIT));
}
if (top == 1) {
rnd->dp[rnd->used - 1] |= (((mp_digit)1)<<((mp_digit)DIGIT_BIT));
rnd->dp[rnd->used - 1] |= (((mp_digit)1)<<((mp_digit)(DIGIT_BIT - 1)));
}
if (bottom) {
rnd->dp[0] |= 0x1;
}
return 1;
}
int
BN_rand_range(BIGNUM *rnd, BIGNUM *range)
{
if (rnd == NULL || range == NULL || BN_is_zero(range)) {
return 0;
}
BN_rand(rnd, BN_num_bits(range), 1, 0);
return modulo(rnd, range, rnd) == MP_OKAY;
}
#endif
int
BN_is_prime(const BIGNUM *a, int checks, void (*callback)(int, int, void *), BN_CTX *ctx, void *cb_arg)
{
int primality;
if (a == NULL) {
return 0;
}
USE_ARG(ctx);
USE_ARG(cb_arg);
USE_ARG(callback);
return (mp_prime_is_prime(__UNCONST(a), checks, &primality) == MP_OKAY) ? primality : 0;
}
const BIGNUM *
BN_value_one(void)
{
static mp_digit digit = 1UL;
static const BIGNUM one = { &digit, 1, 1, 0 };
return &one;
}
int
BN_hex2bn(BIGNUM **a, const char *str)
{
return getbn(a, str, 16);
}
int
BN_dec2bn(BIGNUM **a, const char *str)
{
return getbn(a, str, 10);
}
int
BN_radix2bn(BIGNUM **a, const char *str, unsigned radix)
{
return getbn(a, str, (int)radix);
}
int
BN_mod_sub(BIGNUM *r, BIGNUM *a, BIGNUM *b, const BIGNUM *m, BN_CTX *ctx)
{
USE_ARG(ctx);
if (r == NULL || a == NULL || b == NULL || m == NULL) {
return 0;
}
return subtract_modulo(a, b, __UNCONST(m), r) == MP_OKAY;
}
int
BN_is_bit_set(const BIGNUM *a, int n)
{
if (a == NULL || n < 0 || n >= a->used * DIGIT_BIT) {
return 0;
}
return (a->dp[n / DIGIT_BIT] & (1 << (n % DIGIT_BIT))) ? 1 : 0;
}
int
BN_raise(BIGNUM *res, BIGNUM *a, BIGNUM *b)
{
uint64_t exponent;
BIGNUM *power;
BIGNUM *temp;
char *t;
t = BN_bn2dec(b);
exponent = (uint64_t)strtoull(t, NULL, 10);
free(t);
if (exponent == 0) {
BN_copy(res, BN_value_one());
} else {
power = BN_dup(a);
for ( ; (exponent & 1) == 0 ; exponent >>= 1) {
BN_mul(power, power, power, NULL);
}
temp = BN_dup(power);
for (exponent >>= 1 ; exponent > 0 ; exponent >>= 1) {
BN_mul(power, power, power, NULL);
if (exponent & 1) {
BN_mul(temp, power, temp, NULL);
}
}
BN_copy(res, temp);
BN_free(power);
BN_free(temp);
}
return 1;
}
int
BN_factorial(BIGNUM *res, BIGNUM *f)
{
BIGNUM *one;
BIGNUM *i;
i = BN_dup(f);
one = __UNCONST(BN_value_one());
BN_sub(i, i, one);
BN_copy(res, f);
while (BN_cmp(i, one) > 0) {
BN_mul(res, res, i, NULL);
BN_sub(i, i, one);
}
BN_free(i);
return 1;
}
int
BN_gcd(BIGNUM *r, BIGNUM *a, BIGNUM *b, BN_CTX *ctx)
{
return mp_gcd(a, b, r);
}