blob: a862ac29f20faba8de6fe7bdb6c11a69250e60bb [file] [log] [blame]
// 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 <assert.h>
#include <string.h>
#include <utility>
#include <openssl/err.h>
#include <openssl/mem.h>
#include "../../mem_internal.h"
struct bignum_ctx {
~bignum_ctx() {
// All |BN_CTX_start| calls must be matched with |BN_CTX_end|, otherwise the
// function may use more memory than expected, potentially without bound if
// done in a loop. Assert that all |BIGNUM|s have been released.
assert(used_ == 0 || error_);
}
// bignums_ is the stack of |BIGNUM|s managed by this |BN_CTX|.
bssl::Vector<bssl::UniquePtr<BIGNUM>> bignums_;
// stack_ is the stack of |BN_CTX_start| frames. It is the value of |used_| at
// the time |BN_CTX_start| was called.
bssl::Vector<size_t> stack_;
// used_ is the number of |BIGNUM|s from |bignums_| that have been used.
size_t used_ = 0;
// error_ is whether any operation on this |BN_CTX| failed. All subsequent
// operations will fail.
bool error_ = false;
// defer_error_ is whether an operation on this |BN_CTX| has failed, but no
// error has been pushed to the queue yet. This is used to defer errors from
// |BN_CTX_start| to |BN_CTX_get|.
bool defer_error_ = false;
} /* BN_CTX */;
BN_CTX *BN_CTX_new(void) { return bssl::New<BN_CTX>(); }
void BN_CTX_free(BN_CTX *ctx) { bssl::Delete(ctx); }
void BN_CTX_start(BN_CTX *ctx) {
if (ctx->error_) {
// Once an operation has failed, |ctx->stack| no longer matches the number
// of |BN_CTX_end| calls to come. Do nothing.
return;
}
if (!ctx->stack_.Push(ctx->used_)) {
ctx->error_ = true;
// |BN_CTX_start| cannot fail, so defer the error to |BN_CTX_get|.
ctx->defer_error_ = true;
ERR_clear_error();
}
}
BIGNUM *BN_CTX_get(BN_CTX *ctx) {
// Once any operation has failed, they all do.
if (ctx->error_) {
if (ctx->defer_error_) {
OPENSSL_PUT_ERROR(BN, BN_R_TOO_MANY_TEMPORARY_VARIABLES);
ctx->defer_error_ = false;
}
return nullptr;
}
if (ctx->used_ == ctx->bignums_.size()) {
bssl::UniquePtr<BIGNUM> bn(BN_new());
if (bn == nullptr || !ctx->bignums_.Push(std::move(bn))) {
OPENSSL_PUT_ERROR(BN, BN_R_TOO_MANY_TEMPORARY_VARIABLES);
ctx->error_ = true;
return nullptr;
}
}
BIGNUM *ret = ctx->bignums_[ctx->used_].get();
BN_zero(ret);
// This is bounded by |ctx->bignums_.size()|, so it cannot overflow.
ctx->used_++;
return ret;
}
void BN_CTX_end(BN_CTX *ctx) {
if (ctx->error_) {
// Once an operation has failed, |ctx->stack_| no longer matches the number
// of |BN_CTX_end| calls to come. Do nothing.
return;
}
assert(!ctx->stack_.empty());
ctx->used_ = ctx->stack_.back();
ctx->stack_.pop_back();
}