Add OSSL_FN_CTX integration with BN_CTX

Add an OSSL_FN_CTX * pointer to struct bignum_ctx, allowing a BN_CTX
to cache an OSSL_FN_CTX for use by BIGNUM wrapper functions.  The
pointer is freed automatically when BN_CTX_free() is called.

Also add bn_ctx_acquire_ossl_fn_ctx() and bn_ctx_release_ossl_fn_ctx():
- acquire creates (or reuses if large enough) an OSSL_FN_CTX inside
  the BN_CTX, sizing it according to the caller's needs.
- release checks that no frames remain in the cached OSSL_FN_CTX.

The acquire function respects BN_FLG_SECURE: if the BN_CTX was created
with BN_CTX_secure_new(), the OSSL_FN_CTX is also allocated in secure
memory.

Assisted-by: Pi:moonshotai/kimi-k2.6
Signed-off-by: Richard Levitte <levitte@openssl.foundation>

Reviewed-by: Igor Ustinov <igus@openssl.foundation>
Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
MergeDate: Sun Jun 14 08:40:44 2026
(Merged from https://github.com/openssl/openssl/pull/31431)
This commit is contained in:
Richard Levitte 2026-06-09 15:12:18 +02:00 committed by Tomas Mraz
parent 485e79dc66
commit e3213b0c03
3 changed files with 143 additions and 0 deletions

View file

@ -75,6 +75,8 @@ struct bignum_ctx {
int flags;
/* The library context */
OSSL_LIB_CTX *libctx;
/* OSSL_FN_CTX for OSSL_FN wrapper functions */
OSSL_FN_CTX *fn_ctx;
};
#ifndef FIPS_MODULE
@ -179,6 +181,7 @@ void BN_CTX_free(BN_CTX *ctx)
#endif
BN_STACK_finish(&ctx->stack);
BN_POOL_finish(&ctx->pool);
OSSL_FN_CTX_free(ctx->fn_ctx);
OPENSSL_free(ctx);
}
@ -240,6 +243,49 @@ BIGNUM *BN_CTX_get(BN_CTX *ctx)
return ret;
}
OSSL_FN_CTX *bn_ctx_acquire_ossl_fn_ctx(BN_CTX *ctx, size_t max_n_frames,
size_t max_n_numbers, size_t max_n_limbs)
{
size_t needed;
if (ctx == NULL)
return NULL;
needed = ossl_fn_ctx_calculate_arena_size(max_n_frames, max_n_numbers,
max_n_limbs);
if (ctx->fn_ctx != NULL) {
if (ctx->fn_ctx->msize >= needed) {
/*
* Existing context is large enough. Ensure no frames are
* outstanding (callers are expected to have ended them).
*/
assert(ctx->fn_ctx->last_frame == NULL);
return ctx->fn_ctx;
}
/* Too small, free and recreate */
OSSL_FN_CTX_free(ctx->fn_ctx);
ctx->fn_ctx = NULL;
}
if (ctx->flags & BN_FLG_SECURE)
ctx->fn_ctx = OSSL_FN_CTX_secure_new(ctx->libctx, max_n_frames,
max_n_numbers, max_n_limbs);
else
ctx->fn_ctx = OSSL_FN_CTX_new(ctx->libctx, max_n_frames,
max_n_numbers, max_n_limbs);
return ctx->fn_ctx;
}
void bn_ctx_release_ossl_fn_ctx(BN_CTX *ctx)
{
if (ctx == NULL || ctx->fn_ctx == NULL)
return;
assert(ctx->fn_ctx->last_frame == NULL);
}
OSSL_LIB_CTX *ossl_bn_get_libctx(BN_CTX *ctx)
{
if (ctx == NULL)

View file

@ -126,6 +126,15 @@ int ossl_bn_rsa_fips186_5_derive_prime(BIGNUM *Y, BIGNUM *X, const BIGNUM *Xin,
OSSL_LIB_CTX *ossl_bn_get_libctx(BN_CTX *ctx);
/*
* bn_ctx_acquire_ossl_fn_ctx() and bn_ctx_release_ossl_fn_ctx() work
* in tandem. They manage an OSSL_FN_CTX that is cached inside a BN_CTX
* for use by BIGNUM wrapper functions that delegate to OSSL_FN.
*/
OSSL_FN_CTX *bn_ctx_acquire_ossl_fn_ctx(BN_CTX *ctx, size_t max_n_frames,
size_t max_n_numbers, size_t max_n_limbs);
void bn_ctx_release_ossl_fn_ctx(BN_CTX *ctx);
extern const BIGNUM ossl_bn_inv_sqrt_2;
#if defined(OPENSSL_SYS_LINUX) && !defined(FIPS_MODULE) && defined(__s390x__) \

View file

@ -21,6 +21,8 @@
#include "testutil.h"
#include "bn_prime.h"
#include "crypto/bn.h"
#include "crypto/fn.h"
#include "crypto/fn_intern.h"
static BN_CTX *ctx;
@ -86,6 +88,91 @@ err:
return ret;
}
static int test_bn_ctx_fn_ctx(void)
{
int ret = 1;
BN_CTX *bnctx = NULL;
OSSL_FN_CTX *fnctx = NULL;
OSSL_FN *fn = NULL;
const void *token = NULL;
/* Test non-secure BN_CTX */
if (!TEST_ptr(bnctx = BN_CTX_new()))
return 0;
/* Acquire should create a new OSSL_FN_CTX */
if (!TEST_ptr(fnctx = bn_ctx_acquire_ossl_fn_ctx(bnctx, 1, 1, 32)))
ret = 0;
/* The returned pointer should be cached inside BN_CTX */
if (ret && !TEST_ptr_eq(fnctx, bn_ctx_acquire_ossl_fn_ctx(bnctx, 1, 1, 32)))
ret = 0;
/* Re-acquire with same size should return the same cached context */
if (ret && !TEST_ptr_eq(fnctx, bn_ctx_acquire_ossl_fn_ctx(bnctx, 1, 1, 32)))
ret = 0;
/* Use the OSSL_FN_CTX */
if (ret) {
if (!TEST_ptr(token = OSSL_FN_CTX_start(fnctx))
|| !TEST_ptr(fn = OSSL_FN_CTX_get_limbs(fnctx, 4))
|| !TEST_true(OSSL_FN_CTX_end(fnctx, token)))
ret = 0;
}
/*
* Release does NOT free the cached OSSL_FN_CTX; it just asserts
* no frames are outstanding. Re-acquire should return the same
* cached pointer.
*/
bn_ctx_release_ossl_fn_ctx(bnctx);
if (ret && !TEST_ptr_eq(fnctx, bn_ctx_acquire_ossl_fn_ctx(bnctx, 1, 1, 32)))
ret = 0;
/*
* Re-acquire with a larger size should replace the cached context
* because the old one is too small. (Free + alloc may reuse the
* same address, so only verify the new context satisfies the larger
* request.)
*/
if (ret) {
OSSL_FN_CTX *small = bn_ctx_acquire_ossl_fn_ctx(bnctx, 1, 1, 8);
if (!TEST_ptr(small))
ret = 0;
else {
OSSL_FN_CTX *large = bn_ctx_acquire_ossl_fn_ctx(bnctx, 1, 1, 64);
if (!TEST_ptr(large)
|| !TEST_ptr(token = OSSL_FN_CTX_start(large))
|| !TEST_ptr(fn = OSSL_FN_CTX_get_limbs(large, 64))
|| !TEST_true(OSSL_FN_CTX_end(large, token)))
ret = 0;
}
}
BN_CTX_free(bnctx);
/* Test secure BN_CTX */
if (ret) {
if (!TEST_ptr(bnctx = BN_CTX_secure_new()))
ret = 0;
else {
fn = NULL;
token = NULL;
fnctx = bn_ctx_acquire_ossl_fn_ctx(bnctx, 1, 1, 8);
if (!TEST_ptr(fnctx)
|| !TEST_ptr(token = OSSL_FN_CTX_start(fnctx))
|| !TEST_ptr(fn = OSSL_FN_CTX_get_limbs(fnctx, 1))
|| !TEST_true(ossl_fn_is_securely_allocated(fn))
|| !TEST_true(OSSL_FN_CTX_end(fnctx, token)))
ret = 0;
BN_CTX_free(bnctx);
}
}
return ret;
}
int setup_tests(void)
{
if (!TEST_ptr(ctx = BN_CTX_new()))
@ -94,6 +181,7 @@ int setup_tests(void)
ADD_TEST(test_is_prime_enhanced);
ADD_ALL_TESTS(test_is_composite_enhanced, (int)OSSL_NELEM(composites));
ADD_TEST(test_bn_small_factors);
ADD_TEST(test_bn_ctx_fn_ctx);
return 1;
}