Split BN_from_montgomery_word into a non-BIGNUM core.
bn_from_montgomery_in_place is actually constant-time. It is, of course,
only used by non-constant-time BIGNUM callers, but that will soon be
fixed.
Change-Id: I2b2c9943dc3b8d6a4b5b19a5bc4fa9ebad532bac
Reviewed-on: https://boringssl-review.googlesource.com/23069
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/bn/montgomery.c b/crypto/fipsmodule/bn/montgomery.c
index 15dfcde..f09ada8 100644
--- a/crypto/fipsmodule/bn/montgomery.c
+++ b/crypto/fipsmodule/bn/montgomery.c
@@ -260,72 +260,75 @@
return BN_mod_mul_montgomery(ret, a, &mont->RR, mont, ctx);
}
-static int BN_from_montgomery_word(BIGNUM *ret, BIGNUM *r,
- const BN_MONT_CTX *mont) {
- const BIGNUM *n = &mont->N;
- int nl = n->top;
- if (nl == 0) {
- ret->top = 0;
- return 1;
- }
-
- int max = (2 * nl); // carry is stored separately
- if (!bn_wexpand(r, max)) {
+static int bn_from_montgomery_in_place(BN_ULONG *r, size_t num_r, BN_ULONG *a,
+ size_t num_a, const BN_MONT_CTX *mont) {
+ const BN_ULONG *n = mont->N.d;
+ size_t num_n = mont->N.top;
+ if (num_r != num_n || num_a != 2 * num_n) {
+ OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
- r->neg ^= n->neg;
- BN_ULONG *np = n->d;
- BN_ULONG *rp = r->d;
-
- // Clear the top words of T.
- if (max > r->top) {
- OPENSSL_memset(&rp[r->top], 0, (max - r->top) * sizeof(BN_ULONG));
- }
-
- r->top = max;
- BN_ULONG n0 = mont->n0[0];
-
// Add multiples of |n| to |r| until R = 2^(nl * BN_BITS2) divides it. On
// input, we had |r| < |n| * R, so now |r| < 2 * |n| * R. Note that |r|
// includes |carry| which is stored separately.
+ BN_ULONG n0 = mont->n0[0];
BN_ULONG carry = 0;
- for (int i = 0; i < nl; i++, rp++) {
- BN_ULONG v = bn_mul_add_words(rp, np, nl, rp[0] * n0);
- v += carry + rp[nl];
- carry |= (v != rp[nl]);
- carry &= (v <= rp[nl]);
- rp[nl] = v;
+ for (size_t i = 0; i < num_n; i++) {
+ BN_ULONG v = bn_mul_add_words(a + i, n, num_n, a[i] * n0);
+ v += carry + a[i + num_n];
+ carry |= (v != a[i + num_n]);
+ carry &= (v <= a[i + num_n]);
+ a[i + num_n] = v;
}
- if (!bn_wexpand(ret, nl)) {
- return 0;
- }
- ret->top = nl;
- ret->neg = r->neg;
- rp = ret->d;
-
- // Shift |nl| words to divide by R. We have |ap| < 2 * |n|. Note that |ap|
+ // Shift |num_n| words to divide by R. We have |a| < 2 * |n|. Note that |a|
// includes |carry| which is stored separately.
- BN_ULONG *ap = &(r->d[nl]);
+ a += num_n;
- // |ap| thus requires at most one additional subtraction |n| to be reduced.
+ // |a| thus requires at most one additional subtraction |n| to be reduced.
// Subtract |n| and select the answer in constant time.
OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
crypto_word_t_too_small);
- BN_ULONG v = bn_sub_words(rp, ap, np, nl) - carry;
- // |v| is one if |ap| - |np| underflowed or zero if it did not. Note |v|
- // cannot be -1. That would imply the subtraction did not fit in |nl| words,
- // and we know at most one subtraction is needed.
+ BN_ULONG v = bn_sub_words(r, a, n, num_n) - carry;
+ // |v| is one if |a| - |n| underflowed or zero if it did not. Note |v| cannot
+ // be -1. That would imply the subtraction did not fit in |num_n| words, and
+ // we know at most one subtraction is needed.
v = 0u - v;
- for (int i = 0; i < nl; i++) {
- rp[i] = constant_time_select_w(v, ap[i], rp[i]);
- ap[i] = 0;
+ for (size_t i = 0; i < num_n; i++) {
+ r[i] = constant_time_select_w(v, a[i], r[i]);
+ a[i] = 0;
}
+ return 1;
+}
+
+static int BN_from_montgomery_word(BIGNUM *ret, BIGNUM *r,
+ const BN_MONT_CTX *mont) {
+ const BIGNUM *n = &mont->N;
+ if (n->top == 0) {
+ ret->top = 0;
+ return 1;
+ }
+
+ int max = (2 * n->top); // carry is stored separately
+ if (!bn_wexpand(r, max) ||
+ !bn_wexpand(ret, n->top)) {
+ return 0;
+ }
+ // Clear the top words of |r|.
+ if (max > r->top) {
+ OPENSSL_memset(r->d + r->top, 0, (max - r->top) * sizeof(BN_ULONG));
+ }
+ r->top = max;
+ ret->top = n->top;
+
+ if (!bn_from_montgomery_in_place(ret->d, ret->top, r->d, r->top, mont)) {
+ return 0;
+ }
+ ret->neg = r->neg;
bn_correct_top(r);
bn_correct_top(ret);
-
return 1;
}