From c56ad2a158304c48a5c23c5ff27c3dcd9fae2760 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C5=8Dan?= Date: Fri, 3 Apr 2026 05:34:12 -0600 Subject: [PATCH] perf: cache is_private_key flag in rsaData to avoid per-call BIGNUM alloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On OpenSSL 3.x, _is_private() called EVP_PKEY_get_bn_param() which heap-allocates a full BIGNUM just to test non-nullness, then frees it. This happened on every sign/decrypt/private_encrypt/check_key call. Add is_private_key field to rsaData, detected once in make_rsa_obj() via a new _detect_private_key() helper. _is_private() now returns the cached flag directly — zero heap allocations on the hot path. Fixes https://github.com/cpan-authors/Crypt-OpenSSL-RSA/issues/152 Co-Authored-By: Claude Sonnet 4.6 --- RSA.xs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/RSA.xs b/RSA.xs index c6e80af..23def5f 100644 --- a/RSA.xs +++ b/RSA.xs @@ -58,6 +58,7 @@ typedef struct EVP_PKEY* rsa; int padding; int hashMode; + int is_private_key; /* cached once at construction; avoids per-call BIGNUM alloc on 3.x */ } rsaData; /* Key names for the rsa hash structure */ @@ -100,24 +101,28 @@ void croakSsl(char* p_file, int p_line) char _is_private(rsaData* p_rsa) { - char ret = 0; + return p_rsa->is_private_key; +} + +static int _detect_private_key(EVP_PKEY* p_rsa) +{ #if OLD_CRUFTY_SSL_VERSION - const BIGNUM* d; - d = p_rsa->rsa->d; - ret = (d != NULL); + return (p_rsa->d != NULL); #else #if OPENSSL_VERSION_NUMBER >= 0x30000000L BIGNUM* d = NULL; - EVP_PKEY_get_bn_param(p_rsa->rsa, OSSL_PKEY_PARAM_RSA_D, &d); - ret = (d != NULL); - BN_clear_free(d); + EVP_PKEY_get_bn_param(p_rsa, OSSL_PKEY_PARAM_RSA_D, &d); + if (d) { + BN_clear_free(d); + return 1; + } + return 0; #else const BIGNUM* d = NULL; - RSA_get0_key(p_rsa->rsa, NULL, NULL, &d); - ret = (d != NULL); + RSA_get0_key(p_rsa, NULL, NULL, &d); + return (d != NULL); #endif #endif - return ret; } SV* make_rsa_obj(SV* p_proto, EVP_PKEY* p_rsa) @@ -132,6 +137,7 @@ SV* make_rsa_obj(SV* p_proto, EVP_PKEY* p_rsa) rsa->hashMode = NID_sha1; #endif rsa->padding = RSA_PKCS1_OAEP_PADDING; + rsa->is_private_key = _detect_private_key(p_rsa); return sv_bless( newRV_noinc(newSViv((IV) rsa)), (SvROK(p_proto) ? SvSTASH(SvRV(p_proto)) : gv_stashsv(p_proto, 1)));