• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2018, Google Inc.
2  *
3  * Permission to use, copy, modify, and/or distribute this software for any
4  * purpose with or without fee is hereby granted, provided that the above
5  * copyright notice and this permission notice appear in all copies.
6  *
7  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14 
15 #include <openssl/bn.h>
16 
17 #include <assert.h>
18 
19 #include <openssl/err.h>
20 
21 #include "internal.h"
22 
23 
word_is_odd_mask(BN_ULONG a)24 static BN_ULONG word_is_odd_mask(BN_ULONG a) { return (BN_ULONG)0 - (a & 1); }
25 
maybe_rshift1_words(BN_ULONG * a,BN_ULONG mask,BN_ULONG * tmp,size_t num)26 static void maybe_rshift1_words(BN_ULONG *a, BN_ULONG mask, BN_ULONG *tmp,
27                                 size_t num) {
28   bn_rshift1_words(tmp, a, num);
29   bn_select_words(a, mask, tmp, a, num);
30 }
31 
maybe_rshift1_words_carry(BN_ULONG * a,BN_ULONG carry,BN_ULONG mask,BN_ULONG * tmp,size_t num)32 static void maybe_rshift1_words_carry(BN_ULONG *a, BN_ULONG carry,
33                                       BN_ULONG mask, BN_ULONG *tmp,
34                                       size_t num) {
35   maybe_rshift1_words(a, mask, tmp, num);
36   if (num != 0) {
37     carry &= mask;
38     a[num - 1] |= carry << (BN_BITS2-1);
39   }
40 }
41 
maybe_add_words(BN_ULONG * a,BN_ULONG mask,const BN_ULONG * b,BN_ULONG * tmp,size_t num)42 static BN_ULONG maybe_add_words(BN_ULONG *a, BN_ULONG mask, const BN_ULONG *b,
43                                 BN_ULONG *tmp, size_t num) {
44   BN_ULONG carry = bn_add_words(tmp, a, b, num);
45   bn_select_words(a, mask, tmp, a, num);
46   return carry & mask;
47 }
48 
bn_gcd_consttime(BIGNUM * r,unsigned * out_shift,const BIGNUM * x,const BIGNUM * y,BN_CTX * ctx)49 static int bn_gcd_consttime(BIGNUM *r, unsigned *out_shift, const BIGNUM *x,
50                             const BIGNUM *y, BN_CTX *ctx) {
51   size_t width = x->width > y->width ? x->width : y->width;
52   if (width == 0) {
53     *out_shift = 0;
54     BN_zero(r);
55     return 1;
56   }
57 
58   // This is a constant-time implementation of Stein's algorithm (binary GCD).
59   int ret = 0;
60   BN_CTX_start(ctx);
61   BIGNUM *u = BN_CTX_get(ctx);
62   BIGNUM *v = BN_CTX_get(ctx);
63   BIGNUM *tmp = BN_CTX_get(ctx);
64   if (u == NULL || v == NULL || tmp == NULL ||
65       !BN_copy(u, x) ||
66       !BN_copy(v, y) ||
67       !bn_resize_words(u, width) ||
68       !bn_resize_words(v, width) ||
69       !bn_resize_words(tmp, width)) {
70     goto err;
71   }
72 
73   // Each loop iteration halves at least one of |u| and |v|. Thus we need at
74   // most the combined bit width of inputs for at least one value to be zero.
75   unsigned x_bits = x->width * BN_BITS2, y_bits = y->width * BN_BITS2;
76   unsigned num_iters = x_bits + y_bits;
77   if (num_iters < x_bits) {
78     OPENSSL_PUT_ERROR(BN, BN_R_BIGNUM_TOO_LONG);
79     goto err;
80   }
81 
82   unsigned shift = 0;
83   for (unsigned i = 0; i < num_iters; i++) {
84     BN_ULONG both_odd = word_is_odd_mask(u->d[0]) & word_is_odd_mask(v->d[0]);
85 
86     // If both |u| and |v| are odd, subtract the smaller from the larger.
87     BN_ULONG u_less_than_v =
88         (BN_ULONG)0 - bn_sub_words(tmp->d, u->d, v->d, width);
89     bn_select_words(u->d, both_odd & ~u_less_than_v, tmp->d, u->d, width);
90     bn_sub_words(tmp->d, v->d, u->d, width);
91     bn_select_words(v->d, both_odd & u_less_than_v, tmp->d, v->d, width);
92 
93     // At least one of |u| and |v| is now even.
94     BN_ULONG u_is_odd = word_is_odd_mask(u->d[0]);
95     BN_ULONG v_is_odd = word_is_odd_mask(v->d[0]);
96     assert(!(u_is_odd & v_is_odd));
97 
98     // If both are even, the final GCD gains a factor of two.
99     shift += 1 & (~u_is_odd & ~v_is_odd);
100 
101     // Halve any which are even.
102     maybe_rshift1_words(u->d, ~u_is_odd, tmp->d, width);
103     maybe_rshift1_words(v->d, ~v_is_odd, tmp->d, width);
104   }
105 
106   // One of |u| or |v| is zero at this point. The algorithm usually makes |u|
107   // zero, unless |y| was already zero on input. Fix this by combining the
108   // values.
109   assert(BN_is_zero(u) || BN_is_zero(v));
110   for (size_t i = 0; i < width; i++) {
111     v->d[i] |= u->d[i];
112   }
113 
114   *out_shift = shift;
115   ret = bn_set_words(r, v->d, width);
116 
117 err:
118   BN_CTX_end(ctx);
119   return ret;
120 }
121 
BN_gcd(BIGNUM * r,const BIGNUM * x,const BIGNUM * y,BN_CTX * ctx)122 int BN_gcd(BIGNUM *r, const BIGNUM *x, const BIGNUM *y, BN_CTX *ctx) {
123   unsigned shift;
124   return bn_gcd_consttime(r, &shift, x, y, ctx) &&
125          BN_lshift(r, r, shift);
126 }
127 
bn_is_relatively_prime(int * out_relatively_prime,const BIGNUM * x,const BIGNUM * y,BN_CTX * ctx)128 int bn_is_relatively_prime(int *out_relatively_prime, const BIGNUM *x,
129                            const BIGNUM *y, BN_CTX *ctx) {
130   int ret = 0;
131   BN_CTX_start(ctx);
132   unsigned shift;
133   BIGNUM *gcd = BN_CTX_get(ctx);
134   if (gcd == NULL ||
135       !bn_gcd_consttime(gcd, &shift, x, y, ctx)) {
136     goto err;
137   }
138 
139   // Check that 2^|shift| * |gcd| is one.
140   if (gcd->width == 0) {
141     *out_relatively_prime = 0;
142   } else {
143     BN_ULONG mask = shift | (gcd->d[0] ^ 1);
144     for (int i = 1; i < gcd->width; i++) {
145       mask |= gcd->d[i];
146     }
147     *out_relatively_prime = mask == 0;
148   }
149   ret = 1;
150 
151 err:
152   BN_CTX_end(ctx);
153   return ret;
154 }
155 
bn_lcm_consttime(BIGNUM * r,const BIGNUM * a,const BIGNUM * b,BN_CTX * ctx)156 int bn_lcm_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) {
157   BN_CTX_start(ctx);
158   unsigned shift;
159   BIGNUM *gcd = BN_CTX_get(ctx);
160   int ret = gcd != NULL &&
161             bn_mul_consttime(r, a, b, ctx) &&
162             bn_gcd_consttime(gcd, &shift, a, b, ctx) &&
163             bn_div_consttime(r, NULL, r, gcd, ctx) &&
164             bn_rshift_secret_shift(r, r, shift, ctx);
165   BN_CTX_end(ctx);
166   return ret;
167 }
168 
bn_mod_inverse_consttime(BIGNUM * r,int * out_no_inverse,const BIGNUM * a,const BIGNUM * n,BN_CTX * ctx)169 int bn_mod_inverse_consttime(BIGNUM *r, int *out_no_inverse, const BIGNUM *a,
170                              const BIGNUM *n, BN_CTX *ctx) {
171   *out_no_inverse = 0;
172   if (BN_is_negative(a) || BN_ucmp(a, n) >= 0) {
173     OPENSSL_PUT_ERROR(BN, BN_R_INPUT_NOT_REDUCED);
174     return 0;
175   }
176   if (BN_is_zero(a)) {
177     if (BN_is_one(n)) {
178       BN_zero(r);
179       return 1;
180     }
181     *out_no_inverse = 1;
182     OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
183     return 0;
184   }
185 
186   // This is a constant-time implementation of the extended binary GCD
187   // algorithm. It is adapted from the Handbook of Applied Cryptography, section
188   // 14.4.3, algorithm 14.51, and modified to bound coefficients and avoid
189   // negative numbers.
190   //
191   // For more details and proof of correctness, see
192   // https://github.com/mit-plv/fiat-crypto/pull/333. In particular, see |step|
193   // and |mod_inverse_consttime| for the algorithm in Gallina and see
194   // |mod_inverse_consttime_spec| for the correctness result.
195 
196   if (!BN_is_odd(a) && !BN_is_odd(n)) {
197     *out_no_inverse = 1;
198     OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
199     return 0;
200   }
201 
202   // This function exists to compute the RSA private exponent, where |a| is one
203   // word. We'll thus use |a_width| when available.
204   size_t n_width = n->width, a_width = a->width;
205   if (a_width > n_width) {
206     a_width = n_width;
207   }
208 
209   int ret = 0;
210   BN_CTX_start(ctx);
211   BIGNUM *u = BN_CTX_get(ctx);
212   BIGNUM *v = BN_CTX_get(ctx);
213   BIGNUM *A = BN_CTX_get(ctx);
214   BIGNUM *B = BN_CTX_get(ctx);
215   BIGNUM *C = BN_CTX_get(ctx);
216   BIGNUM *D = BN_CTX_get(ctx);
217   BIGNUM *tmp = BN_CTX_get(ctx);
218   BIGNUM *tmp2 = BN_CTX_get(ctx);
219   if (u == NULL || v == NULL || A == NULL || B == NULL || C == NULL ||
220       D == NULL || tmp == NULL || tmp2 == NULL ||
221       !BN_copy(u, a) ||
222       !BN_copy(v, n) ||
223       !BN_one(A) ||
224       !BN_one(D) ||
225       // For convenience, size |u| and |v| equivalently.
226       !bn_resize_words(u, n_width) ||
227       !bn_resize_words(v, n_width) ||
228       // |A| and |C| are bounded by |m|.
229       !bn_resize_words(A, n_width) ||
230       !bn_resize_words(C, n_width) ||
231       // |B| and |D| are bounded by |a|.
232       !bn_resize_words(B, a_width) ||
233       !bn_resize_words(D, a_width) ||
234       // |tmp| and |tmp2| may be used at either size.
235       !bn_resize_words(tmp, n_width) ||
236       !bn_resize_words(tmp2, n_width)) {
237     goto err;
238   }
239 
240   // Each loop iteration halves at least one of |u| and |v|. Thus we need at
241   // most the combined bit width of inputs for at least one value to be zero.
242   unsigned a_bits = a_width * BN_BITS2, n_bits = n_width * BN_BITS2;
243   unsigned num_iters = a_bits + n_bits;
244   if (num_iters < a_bits) {
245     OPENSSL_PUT_ERROR(BN, BN_R_BIGNUM_TOO_LONG);
246     goto err;
247   }
248 
249   // Before and after each loop iteration, the following hold:
250   //
251   //   u = A*a - B*n
252   //   v = D*n - C*a
253   //   0 < u <= a
254   //   0 <= v <= n
255   //   0 <= A < n
256   //   0 <= B <= a
257   //   0 <= C < n
258   //   0 <= D <= a
259   //
260   // After each loop iteration, u and v only get smaller, and at least one of
261   // them shrinks by at least a factor of two.
262   for (unsigned i = 0; i < num_iters; i++) {
263     BN_ULONG both_odd = word_is_odd_mask(u->d[0]) & word_is_odd_mask(v->d[0]);
264 
265     // If both |u| and |v| are odd, subtract the smaller from the larger.
266     BN_ULONG v_less_than_u =
267         (BN_ULONG)0 - bn_sub_words(tmp->d, v->d, u->d, n_width);
268     bn_select_words(v->d, both_odd & ~v_less_than_u, tmp->d, v->d, n_width);
269     bn_sub_words(tmp->d, u->d, v->d, n_width);
270     bn_select_words(u->d, both_odd & v_less_than_u, tmp->d, u->d, n_width);
271 
272     // If we updated one of the values, update the corresponding coefficient.
273     BN_ULONG carry = bn_add_words(tmp->d, A->d, C->d, n_width);
274     carry -= bn_sub_words(tmp2->d, tmp->d, n->d, n_width);
275     bn_select_words(tmp->d, carry, tmp->d, tmp2->d, n_width);
276     bn_select_words(A->d, both_odd & v_less_than_u, tmp->d, A->d, n_width);
277     bn_select_words(C->d, both_odd & ~v_less_than_u, tmp->d, C->d, n_width);
278 
279     bn_add_words(tmp->d, B->d, D->d, a_width);
280     bn_sub_words(tmp2->d, tmp->d, a->d, a_width);
281     bn_select_words(tmp->d, carry, tmp->d, tmp2->d, a_width);
282     bn_select_words(B->d, both_odd & v_less_than_u, tmp->d, B->d, a_width);
283     bn_select_words(D->d, both_odd & ~v_less_than_u, tmp->d, D->d, a_width);
284 
285     // Our loop invariants hold at this point. Additionally, exactly one of |u|
286     // and |v| is now even.
287     BN_ULONG u_is_even = ~word_is_odd_mask(u->d[0]);
288     BN_ULONG v_is_even = ~word_is_odd_mask(v->d[0]);
289     assert(u_is_even != v_is_even);
290 
291     // Halve the even one and adjust the corresponding coefficient.
292     maybe_rshift1_words(u->d, u_is_even, tmp->d, n_width);
293     BN_ULONG A_or_B_is_odd =
294         word_is_odd_mask(A->d[0]) | word_is_odd_mask(B->d[0]);
295     BN_ULONG A_carry =
296         maybe_add_words(A->d, A_or_B_is_odd & u_is_even, n->d, tmp->d, n_width);
297     BN_ULONG B_carry =
298         maybe_add_words(B->d, A_or_B_is_odd & u_is_even, a->d, tmp->d, a_width);
299     maybe_rshift1_words_carry(A->d, A_carry, u_is_even, tmp->d, n_width);
300     maybe_rshift1_words_carry(B->d, B_carry, u_is_even, tmp->d, a_width);
301 
302     maybe_rshift1_words(v->d, v_is_even, tmp->d, n_width);
303     BN_ULONG C_or_D_is_odd =
304         word_is_odd_mask(C->d[0]) | word_is_odd_mask(D->d[0]);
305     BN_ULONG C_carry =
306         maybe_add_words(C->d, C_or_D_is_odd & v_is_even, n->d, tmp->d, n_width);
307     BN_ULONG D_carry =
308         maybe_add_words(D->d, C_or_D_is_odd & v_is_even, a->d, tmp->d, a_width);
309     maybe_rshift1_words_carry(C->d, C_carry, v_is_even, tmp->d, n_width);
310     maybe_rshift1_words_carry(D->d, D_carry, v_is_even, tmp->d, a_width);
311   }
312 
313   assert(BN_is_zero(v));
314   if (!BN_is_one(u)) {
315     *out_no_inverse = 1;
316     OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
317     goto err;
318   }
319 
320   ret = BN_copy(r, A) != NULL;
321 
322 err:
323   BN_CTX_end(ctx);
324   return ret;
325 }
326