Commit 7e57092596 for openssl.org
commit 7e57092596c97683cea16ab2f712ac48138fece9
Author: Viktor Dukhovni <viktor@openssl.org>
Date: Wed Apr 22 22:46:18 2026 +1000
Handle NULL-buffer size probe in ossl_param_build_set_bn_pad()
ossl_param_build_set_bn_pad() is reached by two distinct caller
populations. When an OSSL_PARAM_BLD template is supplied
(bld != NULL), the template allocates backing storage internally and
no caller-side sizing is required. When an explicit OSSL_PARAM[]
array is supplied (bld == NULL), the caller follows the standard
OSSL_PARAM size-probe contract: invoke the primitive once with
p->data == NULL to learn the required size via p->return_size, then
allocate a buffer of that size and invoke again with the real
storage.
The bld == NULL branch did not honour the size-probe contract: with
p->data == NULL and a non-zero sz it fell through to
OSSL_PARAM_set_BN() and raised CRYPTO_R_TOO_SMALL_BUFFER, so callers
could never discover the required size.
The defect has been latent across several releases. This primitive
is the *padded* BN setter: it emits a fixed-width encoding regardless
of the BN's actual magnitude, which is needed for the private key --
a minimal encoding would leak its bit-length through timing or
allocation side channels. In practice the private key is the only
provider parameter that reaches this primitive. Callers that want
private-key material have historically done so through
EVP_PKEY_todata() and its OSSL_PARAM_BLD template path, where the
bug is invisible. EVP_PKEY_get_params() callers exist but have not
previously needed the private-key BN. Any caller that does request
it on the explicit-params path -- whether by name or as part of
iterating a provider's full gettable list -- now sees the probe
behave as it does elsewhere.
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
Reviewed-by: Frederik Wedel-Heinen <fwh.openssl@gmail.com>
MergeDate: Sun Apr 26 13:35:32 2026
(Merged from https://github.com/openssl/openssl/pull/30942)
diff --git a/crypto/param_build_set.c b/crypto/param_build_set.c
index db49683ed9..51a2678350 100644
--- a/crypto/param_build_set.c
+++ b/crypto/param_build_set.c
@@ -73,6 +73,11 @@ int ossl_param_build_set_bn_pad(OSSL_PARAM_BLD *bld, OSSL_PARAM *p,
return OSSL_PARAM_BLD_push_BN_pad(bld, key, bn, sz);
p = OSSL_PARAM_locate(p, key);
if (p != NULL) {
+ /* Size probe: NULL data means "report the required size". */
+ if (p->data == NULL) {
+ p->return_size = sz;
+ return 1;
+ }
if (sz > p->data_size) {
ERR_raise(ERR_LIB_CRYPTO, CRYPTO_R_TOO_SMALL_BUFFER);
return 0;
diff --git a/test/evp_pkey_provided_test.c b/test/evp_pkey_provided_test.c
index f96b6e43ee..fbb418ab6b 100644
--- a/test/evp_pkey_provided_test.c
+++ b/test/evp_pkey_provided_test.c
@@ -1654,6 +1654,11 @@ static int test_fromdata_ec(void)
BIGNUM *a = NULL;
BIGNUM *b = NULL;
BIGNUM *p = NULL;
+ OSSL_PARAM probe[2] = {
+ OSSL_PARAM_DEFN(OSSL_PKEY_PARAM_PRIV_KEY, OSSL_PARAM_UNSIGNED_INTEGER,
+ NULL, 0),
+ OSSL_PARAM_END
+ };
if (!TEST_ptr(bld = OSSL_PARAM_BLD_new()))
goto err;
@@ -1742,6 +1747,18 @@ static int test_fromdata_ec(void)
|| !TEST_BN_eq(group_b, b))
goto err;
+ /*
+ * Probe the EC private-key BN length via the explicit-params
+ * path; with NULL data, return_size receives the required
+ * (padded) buffer size, which equals the byte length of the
+ * group order.
+ */
+ probe[0].return_size = OSSL_PARAM_UNMODIFIED;
+ if (!TEST_true(EVP_PKEY_get_params(pk, probe))
+ || !TEST_size_t_eq(probe[0].return_size,
+ BN_num_bytes(EC_GROUP_get0_order(group))))
+ goto err;
+
EC_GROUP_free(group);
group = NULL;
BN_free(group_p);