]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Handle NULL-buffer size probe in ossl_param_build_set_bn_pad()
authorViktor Dukhovni <viktor@openssl.org>
Wed, 22 Apr 2026 12:46:18 +0000 (22:46 +1000)
committerEugene Syromiatnikov <esyr@openssl.org>
Sun, 26 Apr 2026 13:35:31 +0000 (15:35 +0200)
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)

crypto/param_build_set.c
test/evp_pkey_provided_test.c

index db49683ed97638b0624a236b32a402cdaf1e82e5..51a2678350f44e6ce3cb58201366049b3ac6fa8a 100644 (file)
@@ -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;
index f96b6e43ee90bd0c82964abdb9da4af565a37dc7..fbb418ab6ba75a4fcc30cad133b7d0beda7d9b8d 100644 (file)
@@ -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);