gcd_extra.c
Go to the documentation of this file.
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 
24 static BN_ULONG word_is_odd_mask(BN_ULONG a) { return (BN_ULONG)0 - (a & 1); }
25 
26 static void maybe_rshift1_words(BN_ULONG *a, BN_ULONG mask, BN_ULONG *tmp,
27  size_t num) {
29  bn_select_words(a, mask, tmp, a, num);
30 }
31 
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 
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 
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;
61  BIGNUM *u = BN_CTX_get(ctx);
62  BIGNUM *v = 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) ||
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) {
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 
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 
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 
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 
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) {
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;
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;
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  unsigned a_bits = a_width * BN_BITS2, n_bits = n_width * BN_BITS2;
244  unsigned num_iters = a_bits + n_bits;
245  if (num_iters < a_bits) {
247  goto err;
248  }
249 
250  // Before and after each loop iteration, the following hold:
251  //
252  // u = A*a - B*n
253  // v = D*n - C*a
254  // 0 < u <= a
255  // 0 <= v <= n
256  // 0 <= A < n
257  // 0 <= B <= a
258  // 0 <= C < n
259  // 0 <= D <= a
260  //
261  // After each loop iteration, u and v only get smaller, and at least one of
262  // them shrinks by at least a factor of two.
263  for (unsigned i = 0; i < num_iters; i++) {
264  BN_ULONG both_odd = word_is_odd_mask(u->d[0]) & word_is_odd_mask(v->d[0]);
265 
266  // If both |u| and |v| are odd, subtract the smaller from the larger.
267  BN_ULONG v_less_than_u =
268  (BN_ULONG)0 - bn_sub_words(tmp->d, v->d, u->d, n_width);
269  bn_select_words(v->d, both_odd & ~v_less_than_u, tmp->d, v->d, n_width);
270  bn_sub_words(tmp->d, u->d, v->d, n_width);
271  bn_select_words(u->d, both_odd & v_less_than_u, tmp->d, u->d, n_width);
272 
273  // If we updated one of the values, update the corresponding coefficient.
274  BN_ULONG carry = bn_add_words(tmp->d, A->d, C->d, n_width);
275  carry -= bn_sub_words(tmp2->d, tmp->d, n->d, n_width);
276  bn_select_words(tmp->d, carry, tmp->d, tmp2->d, n_width);
277  bn_select_words(A->d, both_odd & v_less_than_u, tmp->d, A->d, n_width);
278  bn_select_words(C->d, both_odd & ~v_less_than_u, tmp->d, C->d, n_width);
279 
280  bn_add_words(tmp->d, B->d, D->d, a_width);
281  bn_sub_words(tmp2->d, tmp->d, a->d, a_width);
282  bn_select_words(tmp->d, carry, tmp->d, tmp2->d, a_width);
283  bn_select_words(B->d, both_odd & v_less_than_u, tmp->d, B->d, a_width);
284  bn_select_words(D->d, both_odd & ~v_less_than_u, tmp->d, D->d, a_width);
285 
286  // Our loop invariants hold at this point. Additionally, exactly one of |u|
287  // and |v| is now even.
288  BN_ULONG u_is_even = ~word_is_odd_mask(u->d[0]);
289  BN_ULONG v_is_even = ~word_is_odd_mask(v->d[0]);
290  assert(u_is_even != v_is_even);
291 
292  // Halve the even one and adjust the corresponding coefficient.
293  maybe_rshift1_words(u->d, u_is_even, tmp->d, n_width);
294  BN_ULONG A_or_B_is_odd =
295  word_is_odd_mask(A->d[0]) | word_is_odd_mask(B->d[0]);
296  BN_ULONG A_carry =
297  maybe_add_words(A->d, A_or_B_is_odd & u_is_even, n->d, tmp->d, n_width);
298  BN_ULONG B_carry =
299  maybe_add_words(B->d, A_or_B_is_odd & u_is_even, a->d, tmp->d, a_width);
300  maybe_rshift1_words_carry(A->d, A_carry, u_is_even, tmp->d, n_width);
301  maybe_rshift1_words_carry(B->d, B_carry, u_is_even, tmp->d, a_width);
302 
303  maybe_rshift1_words(v->d, v_is_even, tmp->d, n_width);
304  BN_ULONG C_or_D_is_odd =
305  word_is_odd_mask(C->d[0]) | word_is_odd_mask(D->d[0]);
306  BN_ULONG C_carry =
307  maybe_add_words(C->d, C_or_D_is_odd & v_is_even, n->d, tmp->d, n_width);
308  BN_ULONG D_carry =
309  maybe_add_words(D->d, C_or_D_is_odd & v_is_even, a->d, tmp->d, a_width);
310  maybe_rshift1_words_carry(C->d, C_carry, v_is_even, tmp->d, n_width);
311  maybe_rshift1_words_carry(D->d, D_carry, v_is_even, tmp->d, a_width);
312  }
313 
314  assert(BN_is_zero(v));
315  if (!BN_is_one(u)) {
316  *out_no_inverse = 1;
318  goto err;
319  }
320 
321  ret = BN_copy(r, A) != NULL;
322 
323 err:
324  BN_CTX_end(ctx);
325  return ret;
326 }
bn.h
width
int width
Definition: libuv/docs/code/tty-gravity/main.c:10
bn_select_words
#define bn_select_words
Definition: boringssl_prefix_symbols.h:2904
maybe_rshift1_words
static void maybe_rshift1_words(BN_ULONG *a, BN_ULONG mask, BN_ULONG *tmp, size_t num)
Definition: gcd_extra.c:26
ctx
Definition: benchmark-async.c:30
bn_resize_words
#define bn_resize_words
Definition: boringssl_prefix_symbols.h:2899
C
#define C(x)
Definition: abseil-cpp/absl/hash/internal/city_test.cc:49
gcd
unsigned gcd(unsigned a, unsigned b)
Definition: bloaty/third_party/zlib/examples/gzappend.c:102
y
const double y
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3611
OPENSSL_PUT_ERROR
#define OPENSSL_PUT_ERROR(library, reason)
Definition: err.h:423
bn_rshift_secret_shift
#define bn_rshift_secret_shift
Definition: boringssl_prefix_symbols.h:2901
word_is_odd_mask
static BN_ULONG word_is_odd_mask(BN_ULONG a)
Definition: gcd_extra.c:24
error_ref_leak.err
err
Definition: error_ref_leak.py:35
bn_lcm_consttime
int bn_lcm_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx)
Definition: gcd_extra.c:156
u
OPENSSL_EXPORT pem_password_cb void * u
Definition: pem.h:351
bn_sub_words
#define bn_sub_words
Definition: boringssl_prefix_symbols.h:2915
a
int a
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:88
bignum_ctx
Definition: ctx.c:91
BN_CTX_get
#define BN_CTX_get
Definition: boringssl_prefix_symbols.h:884
BN_gcd
int BN_gcd(BIGNUM *r, const BIGNUM *x, const BIGNUM *y, BN_CTX *ctx)
Definition: gcd_extra.c:122
bn_add_words
#define bn_add_words
Definition: boringssl_prefix_symbols.h:2851
BN_one
#define BN_one
Definition: boringssl_prefix_symbols.h:977
BN_R_INPUT_NOT_REDUCED
#define BN_R_INPUT_NOT_REDUCED
Definition: bn.h:1043
BN_lshift
#define BN_lshift
Definition: boringssl_prefix_symbols.h:942
BN_R_BIGNUM_TOO_LONG
#define BN_R_BIGNUM_TOO_LONG
Definition: bn.h:1038
bn_div_consttime
#define bn_div_consttime
Definition: boringssl_prefix_symbols.h:2853
setup.v
v
Definition: third_party/bloaty/third_party/capstone/bindings/python/setup.py:42
err.h
BN_is_odd
#define BN_is_odd
Definition: boringssl_prefix_symbols.h:934
x
int x
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3610
BN_ucmp
#define BN_ucmp
Definition: boringssl_prefix_symbols.h:1001
BN_is_zero
#define BN_is_zero
Definition: boringssl_prefix_symbols.h:940
maybe_add_words
static BN_ULONG maybe_add_words(BN_ULONG *a, BN_ULONG mask, const BN_ULONG *b, BN_ULONG *tmp, size_t num)
Definition: gcd_extra.c:42
b
uint64_t b
Definition: abseil-cpp/absl/container/internal/layout_test.cc:53
n
int n
Definition: abseil-cpp/absl/container/btree_test.cc:1080
bn_mul_consttime
#define bn_mul_consttime
Definition: boringssl_prefix_symbols.h:2887
BN_copy
#define BN_copy
Definition: boringssl_prefix_symbols.h:914
BN_zero
#define BN_zero
Definition: boringssl_prefix_symbols.h:1004
BN_CTX_start
#define BN_CTX_start
Definition: boringssl_prefix_symbols.h:886
bn_set_words
#define bn_set_words
Definition: boringssl_prefix_symbols.h:2907
bignum_st
Definition: bn.h:957
BN_is_one
#define BN_is_one
Definition: boringssl_prefix_symbols.h:935
internal.h
ret
UniquePtr< SSL_SESSION > ret
Definition: ssl_x509.cc:1029
fix_build_deps.r
r
Definition: fix_build_deps.py:491
bignum_st::d
BN_ULONG * d
Definition: bn.h:960
A
Definition: miscompile_with_no_unique_address_test.cc:23
xds_manager.num
num
Definition: xds_manager.py:56
bn_gcd_consttime
static int bn_gcd_consttime(BIGNUM *r, unsigned *out_shift, const BIGNUM *x, const BIGNUM *y, BN_CTX *ctx)
Definition: gcd_extra.c:49
BN_is_negative
#define BN_is_negative
Definition: boringssl_prefix_symbols.h:933
bn_rshift1_words
#define bn_rshift1_words
Definition: boringssl_prefix_symbols.h:2900
BN_CTX_end
#define BN_CTX_end
Definition: boringssl_prefix_symbols.h:882
bn_is_relatively_prime
int bn_is_relatively_prime(int *out_relatively_prime, const BIGNUM *x, const BIGNUM *y, BN_CTX *ctx)
Definition: gcd_extra.c:128
BN_R_NO_INVERSE
#define BN_R_NO_INVERSE
Definition: bn.h:1048
autogen_x86imm.tmp
tmp
Definition: autogen_x86imm.py:12
bn_mod_inverse_consttime
int bn_mod_inverse_consttime(BIGNUM *r, int *out_no_inverse, const BIGNUM *a, const BIGNUM *n, BN_CTX *ctx)
Definition: gcd_extra.c:170
maybe_rshift1_words_carry
static void maybe_rshift1_words_carry(BN_ULONG *a, BN_ULONG carry, BN_ULONG mask, BN_ULONG *tmp, size_t num)
Definition: gcd_extra.c:32
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230


grpc
Author(s):
autogenerated on Fri May 16 2025 02:58:25