From: Viktor Dukhovni Date: Wed, 22 Apr 2026 12:46:18 +0000 (+1000) Subject: Handle NULL-buffer size probe in ossl_param_build_set_bn_pad() X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7e57092596c97683cea16ab2f712ac48138fece9;p=thirdparty%2Fopenssl.git 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 Reviewed-by: Frederik Wedel-Heinen 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 db49683ed97..51a2678350f 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 f96b6e43ee9..fbb418ab6ba 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);