xref: /aosp_15_r20/external/boringssl/src/crypto/fipsmodule/bn/gcd_extra.c (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
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     declassify_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   declassify_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             // |gcd| has a secret bit width.
164             bn_div_consttime(r, NULL, r, gcd, /*divisor_min_bits=*/0, ctx) &&
165             bn_rshift_secret_shift(r, r, shift, ctx);
166   BN_CTX_end(ctx);
167   return ret;
168 }
169 
bn_mod_inverse_consttime(BIGNUM * r,int * out_no_inverse,const BIGNUM * a,const BIGNUM * n,BN_CTX * ctx)170 int bn_mod_inverse_consttime(BIGNUM *r, int *out_no_inverse, const BIGNUM *a,
171                              const BIGNUM *n, BN_CTX *ctx) {
172   *out_no_inverse = 0;
173   if (BN_is_negative(a) || BN_ucmp(a, n) >= 0) {
174     OPENSSL_PUT_ERROR(BN, BN_R_INPUT_NOT_REDUCED);
175     return 0;
176   }
177   if (BN_is_zero(a)) {
178     if (BN_is_one(n)) {
179       BN_zero(r);
180       return 1;
181     }
182     *out_no_inverse = 1;
183     OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
184     return 0;
185   }
186 
187   // This is a constant-time implementation of the extended binary GCD
188   // algorithm. It is adapted from the Handbook of Applied Cryptography, section
189   // 14.4.3, algorithm 14.51, and modified to bound coefficients and avoid
190   // negative numbers.
191   //
192   // For more details and proof of correctness, see
193   // https://github.com/mit-plv/fiat-crypto/pull/333. In particular, see |step|
194   // and |mod_inverse_consttime| for the algorithm in Gallina and see
195   // |mod_inverse_consttime_spec| for the correctness result.
196 
197   if (!BN_is_odd(a) && !BN_is_odd(n)) {
198     *out_no_inverse = 1;
199     OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
200     return 0;
201   }
202 
203   // This function exists to compute the RSA private exponent, where |a| is one
204   // word. We'll thus use |a_width| when available.
205   size_t n_width = n->width, a_width = a->width;
206   if (a_width > n_width) {
207     a_width = n_width;
208   }
209 
210   int ret = 0;
211   BN_CTX_start(ctx);
212   BIGNUM *u = BN_CTX_get(ctx);
213   BIGNUM *v = BN_CTX_get(ctx);
214   BIGNUM *A = BN_CTX_get(ctx);
215   BIGNUM *B = BN_CTX_get(ctx);
216   BIGNUM *C = BN_CTX_get(ctx);
217   BIGNUM *D = BN_CTX_get(ctx);
218   BIGNUM *tmp = BN_CTX_get(ctx);
219   BIGNUM *tmp2 = BN_CTX_get(ctx);
220   if (u == NULL || v == NULL || A == NULL || B == NULL || C == NULL ||
221       D == NULL || tmp == NULL || tmp2 == NULL ||
222       !BN_copy(u, a) ||
223       !BN_copy(v, n) ||
224       !BN_one(A) ||
225       !BN_one(D) ||
226       // For convenience, size |u| and |v| equivalently.
227       !bn_resize_words(u, n_width) ||
228       !bn_resize_words(v, n_width) ||
229       // |A| and |C| are bounded by |m|.
230       !bn_resize_words(A, n_width) ||
231       !bn_resize_words(C, n_width) ||
232       // |B| and |D| are bounded by |a|.
233       !bn_resize_words(B, a_width) ||
234       !bn_resize_words(D, a_width) ||
235       // |tmp| and |tmp2| may be used at either size.
236       !bn_resize_words(tmp, n_width) ||
237       !bn_resize_words(tmp2, n_width)) {
238     goto err;
239   }
240 
241   // Each loop iteration halves at least one of |u| and |v|. Thus we need at
242   // most the combined bit width of inputs for at least one value to be zero.
243   // |a_bits| and |n_bits| cannot overflow because |bn_wexpand| ensures bit
244   // counts fit in even |int|.
245   size_t a_bits = a_width * BN_BITS2, n_bits = n_width * BN_BITS2;
246   size_t num_iters = a_bits + n_bits;
247   if (num_iters < a_bits) {
248     OPENSSL_PUT_ERROR(BN, BN_R_BIGNUM_TOO_LONG);
249     goto err;
250   }
251 
252   // Before and after each loop iteration, the following hold:
253   //
254   //   u = A*a - B*n
255   //   v = D*n - C*a
256   //   0 < u <= a
257   //   0 <= v <= n
258   //   0 <= A < n
259   //   0 <= B <= a
260   //   0 <= C < n
261   //   0 <= D <= a
262   //
263   // After each loop iteration, u and v only get smaller, and at least one of
264   // them shrinks by at least a factor of two.
265   for (size_t i = 0; i < num_iters; i++) {
266     BN_ULONG both_odd = word_is_odd_mask(u->d[0]) & word_is_odd_mask(v->d[0]);
267 
268     // If both |u| and |v| are odd, subtract the smaller from the larger.
269     BN_ULONG v_less_than_u =
270         (BN_ULONG)0 - bn_sub_words(tmp->d, v->d, u->d, n_width);
271     bn_select_words(v->d, both_odd & ~v_less_than_u, tmp->d, v->d, n_width);
272     bn_sub_words(tmp->d, u->d, v->d, n_width);
273     bn_select_words(u->d, both_odd & v_less_than_u, tmp->d, u->d, n_width);
274 
275     // If we updated one of the values, update the corresponding coefficient.
276     BN_ULONG carry = bn_add_words(tmp->d, A->d, C->d, n_width);
277     carry -= bn_sub_words(tmp2->d, tmp->d, n->d, n_width);
278     bn_select_words(tmp->d, carry, tmp->d, tmp2->d, n_width);
279     bn_select_words(A->d, both_odd & v_less_than_u, tmp->d, A->d, n_width);
280     bn_select_words(C->d, both_odd & ~v_less_than_u, tmp->d, C->d, n_width);
281 
282     bn_add_words(tmp->d, B->d, D->d, a_width);
283     bn_sub_words(tmp2->d, tmp->d, a->d, a_width);
284     bn_select_words(tmp->d, carry, tmp->d, tmp2->d, a_width);
285     bn_select_words(B->d, both_odd & v_less_than_u, tmp->d, B->d, a_width);
286     bn_select_words(D->d, both_odd & ~v_less_than_u, tmp->d, D->d, a_width);
287 
288     // Our loop invariants hold at this point. Additionally, exactly one of |u|
289     // and |v| is now even.
290     BN_ULONG u_is_even = ~word_is_odd_mask(u->d[0]);
291     BN_ULONG v_is_even = ~word_is_odd_mask(v->d[0]);
292     declassify_assert(u_is_even != v_is_even);
293 
294     // Halve the even one and adjust the corresponding coefficient.
295     maybe_rshift1_words(u->d, u_is_even, tmp->d, n_width);
296     BN_ULONG A_or_B_is_odd =
297         word_is_odd_mask(A->d[0]) | word_is_odd_mask(B->d[0]);
298     BN_ULONG A_carry =
299         maybe_add_words(A->d, A_or_B_is_odd & u_is_even, n->d, tmp->d, n_width);
300     BN_ULONG B_carry =
301         maybe_add_words(B->d, A_or_B_is_odd & u_is_even, a->d, tmp->d, a_width);
302     maybe_rshift1_words_carry(A->d, A_carry, u_is_even, tmp->d, n_width);
303     maybe_rshift1_words_carry(B->d, B_carry, u_is_even, tmp->d, a_width);
304 
305     maybe_rshift1_words(v->d, v_is_even, tmp->d, n_width);
306     BN_ULONG C_or_D_is_odd =
307         word_is_odd_mask(C->d[0]) | word_is_odd_mask(D->d[0]);
308     BN_ULONG C_carry =
309         maybe_add_words(C->d, C_or_D_is_odd & v_is_even, n->d, tmp->d, n_width);
310     BN_ULONG D_carry =
311         maybe_add_words(D->d, C_or_D_is_odd & v_is_even, a->d, tmp->d, a_width);
312     maybe_rshift1_words_carry(C->d, C_carry, v_is_even, tmp->d, n_width);
313     maybe_rshift1_words_carry(D->d, D_carry, v_is_even, tmp->d, a_width);
314   }
315 
316   declassify_assert(BN_is_zero(v));
317   // While the inputs and output are secret, this function considers whether the
318   // input was invertible to be public. It is used as part of RSA key
319   // generation, where inputs are chosen to already be invertible.
320   if (constant_time_declassify_int(!BN_is_one(u))) {
321     *out_no_inverse = 1;
322     OPENSSL_PUT_ERROR(BN, BN_R_NO_INVERSE);
323     goto err;
324   }
325 
326   ret = BN_copy(r, A) != NULL;
327 
328 err:
329   BN_CTX_end(ctx);
330   return ret;
331 }
332