// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include #include "../../internal.h" #include "internal.h" #define BN_SQR_STACK_WORDS 16 static void bn_mul_normal(BN_ULONG *r, const BN_ULONG *a, size_t na, const BN_ULONG *b, size_t nb) { if (na < nb) { size_t itmp = na; na = nb; nb = itmp; const BN_ULONG *ltmp = a; a = b; b = ltmp; } BN_ULONG *rr = &(r[na]); if (nb == 0) { OPENSSL_memset(r, 0, na * sizeof(BN_ULONG)); return; } rr[0] = bn_mul_words(r, a, na, b[0]); for (;;) { if (--nb == 0) { return; } rr[1] = bn_mul_add_words(&(r[1]), a, na, b[1]); if (--nb == 0) { return; } rr[2] = bn_mul_add_words(&(r[2]), a, na, b[2]); if (--nb == 0) { return; } rr[3] = bn_mul_add_words(&(r[3]), a, na, b[3]); if (--nb == 0) { return; } rr[4] = bn_mul_add_words(&(r[4]), a, na, b[4]); rr += 4; r += 4; b += 4; } } // bn_sub_part_words sets |r| to |a| - |b|. It returns the borrow bit, which is // one if the operation underflowed and zero otherwise. |cl| is the common // length, that is, the shorter of len(a) or len(b). |dl| is the delta length, // that is, len(a) - len(b). |r|'s length matches the larger of |a| and |b|, or // cl + abs(dl). // // TODO(davidben): Make this take |size_t|. The |cl| + |dl| calling convention // is confusing. static BN_ULONG bn_sub_part_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b, int cl, int dl) { assert(cl >= 0); BN_ULONG borrow = bn_sub_words(r, a, b, cl); if (dl == 0) { return borrow; } r += cl; a += cl; b += cl; if (dl < 0) { // |a| is shorter than |b|. Complete the subtraction as if the excess words // in |a| were zeros. dl = -dl; for (int i = 0; i < dl; i++) { r[i] = CRYPTO_subc_w(0, b[i], borrow, &borrow); } } else { // |b| is shorter than |a|. Complete the subtraction as if the excess words // in |b| were zeros. for (int i = 0; i < dl; i++) { r[i] = CRYPTO_subc_w(a[i], 0, borrow, &borrow); } } return borrow; } // bn_abs_sub_part_words computes |r| = |a| - |b|, storing the absolute value // and returning a mask of all ones if the result was negative and all zeros if // the result was positive. |cl| and |dl| follow the |bn_sub_part_words| calling // convention. // // TODO(davidben): Make this take |size_t|. The |cl| + |dl| calling convention // is confusing. // // TODO(davidben): This function used to be used as part of a general Karatsuba // multiplication implementation, which had to account for differently-sized // inputs. Now it is only used as part of RSA key generation, which does not // need all this. static BN_ULONG bn_abs_sub_part_words(BN_ULONG *r, const BN_ULONG *a, const BN_ULONG *b, int cl, int dl, BN_ULONG *tmp) { BN_ULONG borrow = bn_sub_part_words(tmp, a, b, cl, dl); bn_sub_part_words(r, b, a, cl, -dl); int r_len = cl + (dl < 0 ? -dl : dl); borrow = 0 - borrow; bn_select_words(r, borrow, r /* tmp < 0 */, tmp /* tmp >= 0 */, r_len); return borrow; } int bn_abs_sub_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) { int cl = a->width < b->width ? a->width : b->width; int dl = a->width - b->width; int r_len = a->width < b->width ? b->width : a->width; BN_CTX_start(ctx); BIGNUM *tmp = BN_CTX_get(ctx); int ok = tmp != NULL && bn_wexpand(r, r_len) && bn_wexpand(tmp, r_len); if (ok) { bn_abs_sub_part_words(r->d, a->d, b->d, cl, dl, tmp->d); r->width = r_len; } BN_CTX_end(ctx); return ok; } // bn_mul_impl implements |BN_mul| and |bn_mul_consttime|. Note this function // breaks |BIGNUM| invariants and may return a negative zero. This is handled by // the callers. static int bn_mul_impl(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) { int al = a->width; int bl = b->width; if (al == 0 || bl == 0) { BN_zero(r); return 1; } int ret = 0, i, top; BIGNUM *rr; BN_CTX_start(ctx); if (r == a || r == b) { rr = BN_CTX_get(ctx); if (rr == NULL) { goto err; } } else { rr = r; } rr->neg = a->neg ^ b->neg; i = al - bl; if (i == 0) { if (al == 8) { if (!bn_wexpand(rr, 16)) { goto err; } rr->width = 16; bn_mul_comba8(rr->d, a->d, b->d); goto end; } } top = al + bl; if (!bn_wexpand(rr, top)) { goto err; } rr->width = top; bn_mul_normal(rr->d, a->d, al, b->d, bl); end: if (r != rr && !BN_copy(r, rr)) { goto err; } ret = 1; err: BN_CTX_end(ctx); return ret; } int BN_mul(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) { if (!bn_mul_impl(r, a, b, ctx)) { return 0; } // This additionally fixes any negative zeros created by |bn_mul_impl|. bn_set_minimal_width(r); return 1; } int bn_mul_consttime(BIGNUM *r, const BIGNUM *a, const BIGNUM *b, BN_CTX *ctx) { // Prevent negative zeros. if (a->neg || b->neg) { OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER); return 0; } return bn_mul_impl(r, a, b, ctx); } void bn_mul_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a, size_t num_a, const BN_ULONG *b, size_t num_b) { if (num_r != num_a + num_b) { abort(); } // TODO(davidben): Should this call |bn_mul_comba4| too? |BN_mul| does not // hit that code. if (num_a == 8 && num_b == 8) { bn_mul_comba8(r, a, b); } else { bn_mul_normal(r, a, num_a, b, num_b); } } // tmp must have 2*n words static void bn_sqr_normal(BN_ULONG *r, const BN_ULONG *a, size_t n, BN_ULONG *tmp) { if (n == 0) { return; } size_t max = n * 2; const BN_ULONG *ap = a; BN_ULONG *rp = r; rp[0] = rp[max - 1] = 0; rp++; // Compute the contribution of a[i] * a[j] for all i < j. if (n > 1) { ap++; rp[n - 1] = bn_mul_words(rp, ap, n - 1, ap[-1]); rp += 2; } if (n > 2) { for (size_t i = n - 2; i > 0; i--) { ap++; rp[i] = bn_mul_add_words(rp, ap, i, ap[-1]); rp += 2; } } // The final result fits in |max| words, so none of the following operations // will overflow. // Double |r|, giving the contribution of a[i] * a[j] for all i != j. bn_add_words(r, r, r, max); // Add in the contribution of a[i] * a[i] for all i. bn_sqr_words(tmp, a, n); bn_add_words(r, r, tmp, max); } int BN_mul_word(BIGNUM *bn, BN_ULONG w) { if (!bn->width) { return 1; } if (w == 0) { BN_zero(bn); return 1; } BN_ULONG ll = bn_mul_words(bn->d, bn->d, bn->width, w); if (ll) { if (!bn_wexpand(bn, bn->width + 1)) { return 0; } bn->d[bn->width++] = ll; } return 1; } int bn_sqr_consttime(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx) { int al = a->width; if (al <= 0) { r->width = 0; r->neg = 0; return 1; } int ret = 0, max; BN_CTX_start(ctx); BIGNUM *rr = (a != r) ? r : BN_CTX_get(ctx); BIGNUM *tmp = BN_CTX_get(ctx); if (!rr || !tmp) { goto err; } max = 2 * al; // Non-zero (from above) if (!bn_wexpand(rr, max)) { goto err; } if (al == 4) { bn_sqr_comba4(rr->d, a->d); } else if (al == 8) { bn_sqr_comba8(rr->d, a->d); } else { if (al < BN_SQR_STACK_WORDS) { BN_ULONG t[BN_SQR_STACK_WORDS * 2]; bn_sqr_normal(rr->d, a->d, al, t); } else { if (!bn_wexpand(tmp, max)) { goto err; } bn_sqr_normal(rr->d, a->d, al, tmp->d); } } rr->neg = 0; rr->width = max; if (rr != r && !BN_copy(r, rr)) { goto err; } ret = 1; err: BN_CTX_end(ctx); return ret; } int BN_sqr(BIGNUM *r, const BIGNUM *a, BN_CTX *ctx) { if (!bn_sqr_consttime(r, a, ctx)) { return 0; } bn_set_minimal_width(r); return 1; } void bn_sqr_small(BN_ULONG *r, size_t num_r, const BN_ULONG *a, size_t num_a) { if (num_r != 2 * num_a || num_a > BN_SMALL_MAX_WORDS) { abort(); } if (num_a == 4) { bn_sqr_comba4(r, a); } else if (num_a == 8) { bn_sqr_comba8(r, a); } else { BN_ULONG tmp[2 * BN_SMALL_MAX_WORDS]; bn_sqr_normal(r, a, num_a, tmp); OPENSSL_cleanse(tmp, 2 * num_a * sizeof(BN_ULONG)); } }