bn/bn_lcl.h: use __int128 whenever possible, not only on MIPS.
[openssl.git] / crypto / bn / bn_add.c
1 /*
2  * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the OpenSSL license (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9
10 #include "internal/cryptlib.h"
11 #include "bn_lcl.h"
12
13 /* r can == a or b */
14 int BN_add(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
15 {
16     int a_neg = a->neg, ret;
17
18     bn_check_top(a);
19     bn_check_top(b);
20
21     /*-
22      *  a +  b      a+b
23      *  a + -b      a-b
24      * -a +  b      b-a
25      * -a + -b      -(a+b)
26      */
27     if (a_neg ^ b->neg) {
28         /* only one is negative */
29         if (a_neg) {
30             const BIGNUM *tmp;
31
32             tmp = a;
33             a = b;
34             b = tmp;
35         }
36
37         /* we are now a - b */
38
39         if (BN_ucmp(a, b) < 0) {
40             if (!BN_usub(r, b, a))
41                 return 0;
42             r->neg = 1;
43         } else {
44             if (!BN_usub(r, a, b))
45                 return 0;
46             r->neg = 0;
47         }
48         return 1;
49     }
50
51     ret = BN_uadd(r, a, b);
52     r->neg = a_neg;
53     bn_check_top(r);
54     return ret;
55 }
56
57 /* unsigned add of b to a */
58 int BN_uadd(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
59 {
60     int max, min, dif;
61     const BN_ULONG *ap, *bp;
62     BN_ULONG *rp, carry, t1, t2;
63
64     bn_check_top(a);
65     bn_check_top(b);
66
67     if (a->top < b->top) {
68         const BIGNUM *tmp;
69
70         tmp = a;
71         a = b;
72         b = tmp;
73     }
74     max = a->top;
75     min = b->top;
76     dif = max - min;
77
78     if (bn_wexpand(r, max + 1) == NULL)
79         return 0;
80
81     r->top = max;
82
83     ap = a->d;
84     bp = b->d;
85     rp = r->d;
86
87     carry = bn_add_words(rp, ap, bp, min);
88     rp += min;
89     ap += min;
90
91     while (dif) {
92         dif--;
93         t1 = *(ap++);
94         t2 = (t1 + carry) & BN_MASK2;
95         *(rp++) = t2;
96         carry &= (t2 == 0);
97     }
98     *rp = carry;
99     r->top += carry;
100
101     r->neg = 0;
102     bn_check_top(r);
103     return 1;
104 }
105
106 /* unsigned subtraction of b from a, a must be larger than b. */
107 int BN_usub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
108 {
109     int max, min, dif;
110     BN_ULONG t1, t2, borrow, *rp;
111     const BN_ULONG *ap, *bp;
112
113     bn_check_top(a);
114     bn_check_top(b);
115
116     max = a->top;
117     min = b->top;
118     dif = max - min;
119
120     if (dif < 0) {              /* hmm... should not be happening */
121         BNerr(BN_F_BN_USUB, BN_R_ARG2_LT_ARG3);
122         return 0;
123     }
124
125     if (bn_wexpand(r, max) == NULL)
126         return 0;
127
128     ap = a->d;
129     bp = b->d;
130     rp = r->d;
131
132     borrow = bn_sub_words(rp, ap, bp, min);
133     ap += min;
134     rp += min;
135
136     while (dif) {
137         dif--;
138         t1 = *(ap++);
139         t2 = (t1 - borrow) & BN_MASK2;
140         *(rp++) = t2;
141         borrow &= (t1 == 0);
142     }
143
144     r->top = max;
145     r->neg = 0;
146     bn_correct_top(r);
147     return 1;
148 }
149
150 int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
151 {
152     int max;
153     int add = 0, neg = 0;
154
155     bn_check_top(a);
156     bn_check_top(b);
157
158     /*-
159      *  a -  b      a-b
160      *  a - -b      a+b
161      * -a -  b      -(a+b)
162      * -a - -b      b-a
163      */
164     if (a->neg) {
165         if (b->neg) {
166             const BIGNUM *tmp;
167
168             tmp = a;
169             a = b;
170             b = tmp;
171         } else {
172             add = 1;
173             neg = 1;
174         }
175     } else {
176         if (b->neg) {
177             add = 1;
178             neg = 0;
179         }
180     }
181
182     if (add) {
183         if (!BN_uadd(r, a, b))
184             return 0;
185         r->neg = neg;
186         return 1;
187     }
188
189     /* We are actually doing a - b :-) */
190
191     max = (a->top > b->top) ? a->top : b->top;
192     if (bn_wexpand(r, max) == NULL)
193         return 0;
194     if (BN_ucmp(a, b) < 0) {
195         if (!BN_usub(r, b, a))
196             return 0;
197         r->neg = 1;
198     } else {
199         if (!BN_usub(r, a, b))
200             return 0;
201         r->neg = 0;
202     }
203     bn_check_top(r);
204     return 1;
205 }