7cdefa77a17c57be6892ea650745b63f916e0dec
[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     while (max && *--rp == 0)
145         max--;
146
147     r->top = max;
148     r->neg = 0;
149     bn_pollute(r);
150
151     return 1;
152 }
153
154 int BN_sub(BIGNUM *r, const BIGNUM *a, const BIGNUM *b)
155 {
156     int max;
157     int add = 0, neg = 0;
158
159     bn_check_top(a);
160     bn_check_top(b);
161
162     /*-
163      *  a -  b      a-b
164      *  a - -b      a+b
165      * -a -  b      -(a+b)
166      * -a - -b      b-a
167      */
168     if (a->neg) {
169         if (b->neg) {
170             const BIGNUM *tmp;
171
172             tmp = a;
173             a = b;
174             b = tmp;
175         } else {
176             add = 1;
177             neg = 1;
178         }
179     } else {
180         if (b->neg) {
181             add = 1;
182             neg = 0;
183         }
184     }
185
186     if (add) {
187         if (!BN_uadd(r, a, b))
188             return 0;
189         r->neg = neg;
190         return 1;
191     }
192
193     /* We are actually doing a - b :-) */
194
195     max = (a->top > b->top) ? a->top : b->top;
196     if (bn_wexpand(r, max) == NULL)
197         return 0;
198     if (BN_ucmp(a, b) < 0) {
199         if (!BN_usub(r, b, a))
200             return 0;
201         r->neg = 1;
202     } else {
203         if (!BN_usub(r, a, b))
204             return 0;
205         r->neg = 0;
206     }
207     bn_check_top(r);
208     return 1;
209 }