| // Copyright 2000-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 <openssl/bn.h> | |
| #include <openssl/err.h> | |
| 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; | |
| } | |
| bssl::BN_CTXScope scope(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 == nullptr || tmp == nullptr || last_delta == nullptr || | |
| delta == nullptr) { | |
| 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, nullptr, 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; | |
| } | |
| return ok; | |
| } |