#include <openssl/bn.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/mem.h>
#include "internal.h"
int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
const BIGNUM *tmp;
int a_neg = a->neg, ret;
if (a_neg ^ b->neg) {
if (a_neg) {
tmp = a;
a = b;
b = tmp;
}
if (BN_ucmp(a, b) < 0) {
if (!BN_usub_unchecked(r, b, a)) {
return 0;
}
r->neg = 1;
} else {
if (!BN_usub_unchecked(r, a, b)) {
return 0;
}
r->neg = 0;
}
return 1;
}
ret = BN_uadd(r, a, b);
r->neg = a_neg;
return ret;
}
int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
int max, min, dif;
BN_ULONG *ap, *bp, *rp, carry, t1, t2;
const BIGNUM *tmp;
if (a->top < b->top) {
tmp = a;
a = b;
b = tmp;
}
max = a->top;
min = b->top;
dif = max - min;
if (bn_wexpand(r, max + 1) == NULL) {
return 0;
}
r->top = max;
ap = a->d;
bp = b->d;
rp = r->d;
carry = bn_add_words(rp, ap, bp, min);
rp += min;
ap += min;
bp += min;
if (carry) {
while (dif) {
dif--;
t1 = *(ap++);
t2 = (t1 + 1) & BN_MASK2;
*(rp++) = t2;
if (t2) {
carry = 0;
break;
}
}
if (carry) {
*rp = 1;
r->top++;
}
}
if (dif && rp != ap) {
while (dif--) {
*(rp++) = *(ap++);
}
}
r->neg = 0;
return 1;
}
int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
int max;
int add = 0, neg = 0;
const BIGNUM *tmp;
if (a->neg) {
if (b->neg) {
tmp = a;
a = b;
b = tmp;
} else {
add = 1;
neg = 1;
}
} else {
if (b->neg) {
add = 1;
neg = 0;
}
}
if (add) {
if (!BN_uadd(r, a, b)) {
return 0;
}
r->neg = neg;
return 1;
}
max = (a->top > b->top) ? a->top : b->top;
if (bn_wexpand(r, max) == NULL) {
return 0;
}
if (BN_ucmp(a, b) < 0) {
if (!BN_usub_unchecked(r, b, a)) {
return 0;
}
r->neg = 1;
} else {
if (!BN_usub_unchecked(r, a, b)) {
return 0;
}
r->neg = 0;
}
return 1;
}
int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
assert(!BN_is_negative(a));
assert(!BN_is_negative(b));
assert(BN_cmp(a, b) >= 0);
return BN_usub_unchecked(r, a, b);
}
int BN_usub_unchecked(BIGNUM *r, const BIGNUM *a, const BIGNUM *b) {
int max, min, dif;
register BN_ULONG t1, t2, *ap, *bp, *rp;
int i, carry;
max = a->top;
min = b->top;
dif = max - min;
if (dif < 0)
{
OPENSSL_PUT_ERROR(BN, BN_R_ARG2_LT_ARG3);
return 0;
}
if (bn_wexpand(r, max) == NULL) {
return 0;
}
ap = a->d;
bp = b->d;
rp = r->d;
carry = 0;
for (i = min; i != 0; i--) {
t1 = *(ap++);
t2 = *(bp++);
if (carry) {
carry = (t1 <= t2);
t1 = (t1 - t2 - 1) & BN_MASK2;
} else {
carry = (t1 < t2);
t1 = (t1 - t2) & BN_MASK2;
}
*(rp++) = t1 & BN_MASK2;
}
if (carry)
{
if (!dif) {
return 0;
}
while (dif) {
dif--;
t1 = *(ap++);
t2 = (t1 - 1) & BN_MASK2;
*(rp++) = t2;
if (t1) {
break;
}
}
}
if (dif > 0 && rp != ap) {
memcpy(rp, ap, sizeof(*rp) * dif);
}
r->top = max;
r->neg = 0;
bn_correct_top(r);
return 1;
}