6f564afbdd
As the EC code will ultimately want to use these in "words" form by way of EC_FELEM, and because it's much easier, I've implement these as low-level words-based functions that require all inputs have the same width. The BIGNUM versions which RSA and, for now, EC calls are implemented on top of that. Unfortunately, doing such things in constant-time and accounting for undersized inputs requires some scratch space, and these functions don't take BN_CTX. So I've added internal bn_mod_*_quick_ctx functions that take a BN_CTX and the old functions now allocate a bit unnecessarily. RSA only needs lshift (for BN_MONT_CTX) and sub (for CRT), but the generic EC code wants add as well. The generic EC code isn't even remotely constant-time, and I hope to ultimately use stack-allocated EC_FELEMs, so I've made the actual implementations here implemented in "words", which is much simpler anyway due to not having to take care of widths. I've also gone ahead and switched the EC code to these functions, largely as a test of their performance (an earlier iteration made the EC code noticeably slower). These operations are otherwise not performance-critical in RSA. The conversion from BIGNUM to BIGNUM+BN_CTX should be dropped by the static linker already, and the unused BIGNUM+BN_CTX functions will fall off when EC_FELEM happens. Update-Note: BN_mod_*_quick bounce on malloc a bit now, but they're not really used externally. The one caller I found was wpa_supplicant which bounces on malloc already. They appear to be implementing compressed coordinates by hand? We may be able to convince them to call EC_POINT_set_compressed_coordinates_GFp. Bug: 233, 236 Change-Id: I2bf361e9c089e0211b97d95523dbc06f1168e12b Reviewed-on: https://boringssl-review.googlesource.com/25261 Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org> Reviewed-by: Adam Langley <agl@google.com>
503 lines
12 KiB
C
503 lines
12 KiB
C
/* Written by Lenka Fibikova <fibikova@exp-math.uni-essen.de>
|
|
* and Bodo Moeller for the OpenSSL project. */
|
|
/* ====================================================================
|
|
* Copyright (c) 1998-2000 The OpenSSL Project. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* 3. All advertising materials mentioning features or use of this
|
|
* software must display the following acknowledgment:
|
|
* "This product includes software developed by the OpenSSL Project
|
|
* for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
|
|
*
|
|
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
|
* endorse or promote products derived from this software without
|
|
* prior written permission. For written permission, please contact
|
|
* openssl-core@openssl.org.
|
|
*
|
|
* 5. Products derived from this software may not be called "OpenSSL"
|
|
* nor may "OpenSSL" appear in their names without prior written
|
|
* permission of the OpenSSL Project.
|
|
*
|
|
* 6. Redistributions of any form whatsoever must retain the following
|
|
* acknowledgment:
|
|
* "This product includes software developed by the OpenSSL Project
|
|
* for use in the OpenSSL Toolkit (http://www.openssl.org/)"
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
|
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
* ====================================================================
|
|
*
|
|
* This product includes cryptographic software written by Eric Young
|
|
* (eay@cryptsoft.com). This product includes software written by Tim
|
|
* Hudson (tjh@cryptsoft.com). */
|
|
|
|
#include <openssl/bn.h>
|
|
|
|
#include <openssl/err.h>
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
BIGNUM *BN_mod_sqrt(BIGNUM *in, const BIGNUM *a, const BIGNUM *p, BN_CTX *ctx) {
|
|
// Compute a square root of |a| mod |p| using the Tonelli/Shanks algorithm
|
|
// (cf. Henri Cohen, "A Course in Algebraic Computational Number Theory",
|
|
// algorithm 1.5.1). |p| is assumed to be a prime.
|
|
|
|
BIGNUM *ret = in;
|
|
int err = 1;
|
|
int r;
|
|
BIGNUM *A, *b, *q, *t, *x, *y;
|
|
int e, i, j;
|
|
|
|
if (!BN_is_odd(p) || BN_abs_is_word(p, 1)) {
|
|
if (BN_abs_is_word(p, 2)) {
|
|
if (ret == NULL) {
|
|
ret = BN_new();
|
|
}
|
|
if (ret == NULL) {
|
|
goto end;
|
|
}
|
|
if (!BN_set_word(ret, BN_is_bit_set(a, 0))) {
|
|
if (ret != in) {
|
|
BN_free(ret);
|
|
}
|
|
return NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
OPENSSL_PUT_ERROR(BN, BN_R_P_IS_NOT_PRIME);
|
|
return (NULL);
|
|
}
|
|
|
|
if (BN_is_zero(a) || BN_is_one(a)) {
|
|
if (ret == NULL) {
|
|
ret = BN_new();
|
|
}
|
|
if (ret == NULL) {
|
|
goto end;
|
|
}
|
|
if (!BN_set_word(ret, BN_is_one(a))) {
|
|
if (ret != in) {
|
|
BN_free(ret);
|
|
}
|
|
return NULL;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
BN_CTX_start(ctx);
|
|
A = BN_CTX_get(ctx);
|
|
b = BN_CTX_get(ctx);
|
|
q = BN_CTX_get(ctx);
|
|
t = BN_CTX_get(ctx);
|
|
x = BN_CTX_get(ctx);
|
|
y = BN_CTX_get(ctx);
|
|
if (y == NULL) {
|
|
goto end;
|
|
}
|
|
|
|
if (ret == NULL) {
|
|
ret = BN_new();
|
|
}
|
|
if (ret == NULL) {
|
|
goto end;
|
|
}
|
|
|
|
// A = a mod p
|
|
if (!BN_nnmod(A, a, p, ctx)) {
|
|
goto end;
|
|
}
|
|
|
|
// now write |p| - 1 as 2^e*q where q is odd
|
|
e = 1;
|
|
while (!BN_is_bit_set(p, e)) {
|
|
e++;
|
|
}
|
|
// we'll set q later (if needed)
|
|
|
|
if (e == 1) {
|
|
// The easy case: (|p|-1)/2 is odd, so 2 has an inverse
|
|
// modulo (|p|-1)/2, and square roots can be computed
|
|
// directly by modular exponentiation.
|
|
// We have
|
|
// 2 * (|p|+1)/4 == 1 (mod (|p|-1)/2),
|
|
// so we can use exponent (|p|+1)/4, i.e. (|p|-3)/4 + 1.
|
|
if (!BN_rshift(q, p, 2)) {
|
|
goto end;
|
|
}
|
|
q->neg = 0;
|
|
if (!BN_add_word(q, 1) ||
|
|
!BN_mod_exp_mont(ret, A, q, p, ctx, NULL)) {
|
|
goto end;
|
|
}
|
|
err = 0;
|
|
goto vrfy;
|
|
}
|
|
|
|
if (e == 2) {
|
|
// |p| == 5 (mod 8)
|
|
//
|
|
// In this case 2 is always a non-square since
|
|
// Legendre(2,p) = (-1)^((p^2-1)/8) for any odd prime.
|
|
// So if a really is a square, then 2*a is a non-square.
|
|
// Thus for
|
|
// b := (2*a)^((|p|-5)/8),
|
|
// i := (2*a)*b^2
|
|
// we have
|
|
// i^2 = (2*a)^((1 + (|p|-5)/4)*2)
|
|
// = (2*a)^((p-1)/2)
|
|
// = -1;
|
|
// so if we set
|
|
// x := a*b*(i-1),
|
|
// then
|
|
// x^2 = a^2 * b^2 * (i^2 - 2*i + 1)
|
|
// = a^2 * b^2 * (-2*i)
|
|
// = a*(-i)*(2*a*b^2)
|
|
// = a*(-i)*i
|
|
// = a.
|
|
//
|
|
// (This is due to A.O.L. Atkin,
|
|
// <URL:
|
|
//http://listserv.nodak.edu/scripts/wa.exe?A2=ind9211&L=nmbrthry&O=T&P=562>,
|
|
// November 1992.)
|
|
|
|
// t := 2*a
|
|
if (!bn_mod_lshift1_quick_ctx(t, A, p, ctx)) {
|
|
goto end;
|
|
}
|
|
|
|
// b := (2*a)^((|p|-5)/8)
|
|
if (!BN_rshift(q, p, 3)) {
|
|
goto end;
|
|
}
|
|
q->neg = 0;
|
|
if (!BN_mod_exp_mont(b, t, q, p, ctx, NULL)) {
|
|
goto end;
|
|
}
|
|
|
|
// y := b^2
|
|
if (!BN_mod_sqr(y, b, p, ctx)) {
|
|
goto end;
|
|
}
|
|
|
|
// t := (2*a)*b^2 - 1
|
|
if (!BN_mod_mul(t, t, y, p, ctx) ||
|
|
!BN_sub_word(t, 1)) {
|
|
goto end;
|
|
}
|
|
|
|
// x = a*b*t
|
|
if (!BN_mod_mul(x, A, b, p, ctx) ||
|
|
!BN_mod_mul(x, x, t, p, ctx)) {
|
|
goto end;
|
|
}
|
|
|
|
if (!BN_copy(ret, x)) {
|
|
goto end;
|
|
}
|
|
err = 0;
|
|
goto vrfy;
|
|
}
|
|
|
|
// e > 2, so we really have to use the Tonelli/Shanks algorithm.
|
|
// First, find some y that is not a square.
|
|
if (!BN_copy(q, p)) {
|
|
goto end; // use 'q' as temp
|
|
}
|
|
q->neg = 0;
|
|
i = 2;
|
|
do {
|
|
// For efficiency, try small numbers first;
|
|
// if this fails, try random numbers.
|
|
if (i < 22) {
|
|
if (!BN_set_word(y, i)) {
|
|
goto end;
|
|
}
|
|
} else {
|
|
if (!BN_pseudo_rand(y, BN_num_bits(p), 0, 0)) {
|
|
goto end;
|
|
}
|
|
if (BN_ucmp(y, p) >= 0) {
|
|
if (!(p->neg ? BN_add : BN_sub)(y, y, p)) {
|
|
goto end;
|
|
}
|
|
}
|
|
// now 0 <= y < |p|
|
|
if (BN_is_zero(y)) {
|
|
if (!BN_set_word(y, i)) {
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
|
|
r = bn_jacobi(y, q, ctx); // here 'q' is |p|
|
|
if (r < -1) {
|
|
goto end;
|
|
}
|
|
if (r == 0) {
|
|
// m divides p
|
|
OPENSSL_PUT_ERROR(BN, BN_R_P_IS_NOT_PRIME);
|
|
goto end;
|
|
}
|
|
} while (r == 1 && ++i < 82);
|
|
|
|
if (r != -1) {
|
|
// Many rounds and still no non-square -- this is more likely
|
|
// a bug than just bad luck.
|
|
// Even if p is not prime, we should have found some y
|
|
// such that r == -1.
|
|
OPENSSL_PUT_ERROR(BN, BN_R_TOO_MANY_ITERATIONS);
|
|
goto end;
|
|
}
|
|
|
|
// Here's our actual 'q':
|
|
if (!BN_rshift(q, q, e)) {
|
|
goto end;
|
|
}
|
|
|
|
// Now that we have some non-square, we can find an element
|
|
// of order 2^e by computing its q'th power.
|
|
if (!BN_mod_exp_mont(y, y, q, p, ctx, NULL)) {
|
|
goto end;
|
|
}
|
|
if (BN_is_one(y)) {
|
|
OPENSSL_PUT_ERROR(BN, BN_R_P_IS_NOT_PRIME);
|
|
goto end;
|
|
}
|
|
|
|
// Now we know that (if p is indeed prime) there is an integer
|
|
// k, 0 <= k < 2^e, such that
|
|
//
|
|
// a^q * y^k == 1 (mod p).
|
|
//
|
|
// As a^q is a square and y is not, k must be even.
|
|
// q+1 is even, too, so there is an element
|
|
//
|
|
// X := a^((q+1)/2) * y^(k/2),
|
|
//
|
|
// and it satisfies
|
|
//
|
|
// X^2 = a^q * a * y^k
|
|
// = a,
|
|
//
|
|
// so it is the square root that we are looking for.
|
|
|
|
// t := (q-1)/2 (note that q is odd)
|
|
if (!BN_rshift1(t, q)) {
|
|
goto end;
|
|
}
|
|
|
|
// x := a^((q-1)/2)
|
|
if (BN_is_zero(t)) // special case: p = 2^e + 1
|
|
{
|
|
if (!BN_nnmod(t, A, p, ctx)) {
|
|
goto end;
|
|
}
|
|
if (BN_is_zero(t)) {
|
|
// special case: a == 0 (mod p)
|
|
BN_zero(ret);
|
|
err = 0;
|
|
goto end;
|
|
} else if (!BN_one(x)) {
|
|
goto end;
|
|
}
|
|
} else {
|
|
if (!BN_mod_exp_mont(x, A, t, p, ctx, NULL)) {
|
|
goto end;
|
|
}
|
|
if (BN_is_zero(x)) {
|
|
// special case: a == 0 (mod p)
|
|
BN_zero(ret);
|
|
err = 0;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
// b := a*x^2 (= a^q)
|
|
if (!BN_mod_sqr(b, x, p, ctx) ||
|
|
!BN_mod_mul(b, b, A, p, ctx)) {
|
|
goto end;
|
|
}
|
|
|
|
// x := a*x (= a^((q+1)/2))
|
|
if (!BN_mod_mul(x, x, A, p, ctx)) {
|
|
goto end;
|
|
}
|
|
|
|
while (1) {
|
|
// Now b is a^q * y^k for some even k (0 <= k < 2^E
|
|
// where E refers to the original value of e, which we
|
|
// don't keep in a variable), and x is a^((q+1)/2) * y^(k/2).
|
|
//
|
|
// We have a*b = x^2,
|
|
// y^2^(e-1) = -1,
|
|
// b^2^(e-1) = 1.
|
|
|
|
if (BN_is_one(b)) {
|
|
if (!BN_copy(ret, x)) {
|
|
goto end;
|
|
}
|
|
err = 0;
|
|
goto vrfy;
|
|
}
|
|
|
|
|
|
// find smallest i such that b^(2^i) = 1
|
|
i = 1;
|
|
if (!BN_mod_sqr(t, b, p, ctx)) {
|
|
goto end;
|
|
}
|
|
while (!BN_is_one(t)) {
|
|
i++;
|
|
if (i == e) {
|
|
OPENSSL_PUT_ERROR(BN, BN_R_NOT_A_SQUARE);
|
|
goto end;
|
|
}
|
|
if (!BN_mod_mul(t, t, t, p, ctx)) {
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
|
|
// t := y^2^(e - i - 1)
|
|
if (!BN_copy(t, y)) {
|
|
goto end;
|
|
}
|
|
for (j = e - i - 1; j > 0; j--) {
|
|
if (!BN_mod_sqr(t, t, p, ctx)) {
|
|
goto end;
|
|
}
|
|
}
|
|
if (!BN_mod_mul(y, t, t, p, ctx) ||
|
|
!BN_mod_mul(x, x, t, p, ctx) ||
|
|
!BN_mod_mul(b, b, y, p, ctx)) {
|
|
goto end;
|
|
}
|
|
e = i;
|
|
}
|
|
|
|
vrfy:
|
|
if (!err) {
|
|
// verify the result -- the input might have been not a square
|
|
// (test added in 0.9.8)
|
|
|
|
if (!BN_mod_sqr(x, ret, p, ctx)) {
|
|
err = 1;
|
|
}
|
|
|
|
if (!err && 0 != BN_cmp(x, A)) {
|
|
OPENSSL_PUT_ERROR(BN, BN_R_NOT_A_SQUARE);
|
|
err = 1;
|
|
}
|
|
}
|
|
|
|
end:
|
|
if (err) {
|
|
if (ret != in) {
|
|
BN_clear_free(ret);
|
|
}
|
|
ret = NULL;
|
|
}
|
|
BN_CTX_end(ctx);
|
|
return ret;
|
|
}
|
|
|
|
int BN_sqrt(BIGNUM *out_sqrt, const BIGNUM *in, BN_CTX *ctx) {
|
|
BIGNUM *estimate, *tmp, *delta, *last_delta, *tmp2;
|
|
int ok = 0, last_delta_valid = 0;
|
|
|
|
if (in->neg) {
|
|
OPENSSL_PUT_ERROR(BN, BN_R_NEGATIVE_NUMBER);
|
|
return 0;
|
|
}
|
|
if (BN_is_zero(in)) {
|
|
BN_zero(out_sqrt);
|
|
return 1;
|
|
}
|
|
|
|
BN_CTX_start(ctx);
|
|
if (out_sqrt == in) {
|
|
estimate = BN_CTX_get(ctx);
|
|
} else {
|
|
estimate = out_sqrt;
|
|
}
|
|
tmp = BN_CTX_get(ctx);
|
|
last_delta = BN_CTX_get(ctx);
|
|
delta = BN_CTX_get(ctx);
|
|
if (estimate == NULL || tmp == NULL || last_delta == NULL || delta == NULL) {
|
|
OPENSSL_PUT_ERROR(BN, ERR_R_MALLOC_FAILURE);
|
|
goto err;
|
|
}
|
|
|
|
// We estimate that the square root of an n-bit number is 2^{n/2}.
|
|
if (!BN_lshift(estimate, BN_value_one(), BN_num_bits(in)/2)) {
|
|
goto err;
|
|
}
|
|
|
|
// This is Newton's method for finding a root of the equation |estimate|^2 -
|
|
// |in| = 0.
|
|
for (;;) {
|
|
// |estimate| = 1/2 * (|estimate| + |in|/|estimate|)
|
|
if (!BN_div(tmp, NULL, in, estimate, ctx) ||
|
|
!BN_add(tmp, tmp, estimate) ||
|
|
!BN_rshift1(estimate, tmp) ||
|
|
// |tmp| = |estimate|^2
|
|
!BN_sqr(tmp, estimate, ctx) ||
|
|
// |delta| = |in| - |tmp|
|
|
!BN_sub(delta, in, tmp)) {
|
|
OPENSSL_PUT_ERROR(BN, ERR_R_BN_LIB);
|
|
goto err;
|
|
}
|
|
|
|
delta->neg = 0;
|
|
// The difference between |in| and |estimate| squared is required to always
|
|
// decrease. This ensures that the loop always terminates, but I don't have
|
|
// a proof that it always finds the square root for a given square.
|
|
if (last_delta_valid && BN_cmp(delta, last_delta) >= 0) {
|
|
break;
|
|
}
|
|
|
|
last_delta_valid = 1;
|
|
|
|
tmp2 = last_delta;
|
|
last_delta = delta;
|
|
delta = tmp2;
|
|
}
|
|
|
|
if (BN_cmp(tmp, in) != 0) {
|
|
OPENSSL_PUT_ERROR(BN, BN_R_NOT_A_SQUARE);
|
|
goto err;
|
|
}
|
|
|
|
ok = 1;
|
|
|
|
err:
|
|
if (ok && out_sqrt == in && !BN_copy(out_sqrt, estimate)) {
|
|
ok = 0;
|
|
}
|
|
BN_CTX_end(ctx);
|
|
return ok;
|
|
}
|