Commit 06739307f1 for openssl.org

commit 06739307f135799f266b23ddabe669e45f856fe1
Author: Iva Marinova <ivamarinova06@gmail.com>
Date:   Tue Sep 29 18:01:40 2026 +0300

    Allow EVP_SKEY_get0_raw_key() to return only the key length

    There was no way to learn the length of an opaque EVP_SKEY without
    exporting the raw key, which is impossible for non-exportable keys
    (e.g. keys held in hardware).

    Let EVP_SKEY_get0_raw_key() accept a NULL key pointer, in which case
    only the length is returned. It is read from the
    OSSL_SKEY_PARAM_KEY_LENGTH key parameter, exported with
    OSSL_SKEYMGMT_SELECT_PARAMETERS, so the secret key is never exported.
    The built-in generic and AES key managements now report that parameter.

    Fixes: https://github.com/openssl/openssl/issues/32998

    Assisted-by: Claude Code:claude-opus-5-5
    Reviewed-by: Simo Sorce <simo@redhat.com>
    Reviewed-by: Dmitry Belyavskiy <beldmit@gmail.com>
    Merge-date: Mon Oct  5 10:28:09 2026
    Merged-from: https://github.com/openssl/openssl/pull/33035

diff --git a/CHANGES.md b/CHANGES.md
index 2d61b97ac2..543bb999ba 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -33,6 +33,16 @@ OpenSSL 4.2

 ### Changes between 4.1 and 4.2 [xx XXX xxxx]

+ * `EVP_SKEY_get0_raw_key()` now accepts a NULL key pointer to retrieve only
+   the key length.  The length is obtained from the `OSSL_SKEY_PARAM_KEY_LENGTH`
+   key parameter without exporting the key, so it is also available for keys
+   that cannot be exported, provided the key management reports it.  The
+   built-in key managements now include this parameter when exporting key
+   parameters.
+   <!-- https://github.com/openssl/openssl/pull/33035 -->
+
+   *Iva Marinova*
+
  * `X509_OBJECT_up_ref_count()` has been deprecated. Despite its name,
    X509_OBJECT_up_ref_count() does not reference count the X509_OBJECT itself.
    With X509_OBJECT being opaque there is nothing useful an application can do
diff --git a/crypto/evp/s_lib.c b/crypto/evp/s_lib.c
index 8342897731..093ca0aba9 100644
--- a/crypto/evp/s_lib.c
+++ b/crypto/evp/s_lib.c
@@ -160,16 +160,37 @@ static int get_secret_key(const OSSL_PARAM params[], void *arg)
     return 0;
 }

+static int get_key_length(const OSSL_PARAM params[], void *arg)
+{
+    const OSSL_PARAM *p = NULL;
+    size_t *len = arg;
+
+    if ((p = OSSL_PARAM_locate_const(params, OSSL_SKEY_PARAM_KEY_LENGTH)) != NULL)
+        return OSSL_PARAM_get_size_t(p, len);
+
+    return 0;
+}
+
 int EVP_SKEY_get0_raw_key(const EVP_SKEY *skey, const unsigned char **key,
     size_t *len)
 {
     struct raw_key_details_st raw_key;

-    if (skey == NULL || key == NULL || len == NULL) {
+    if (skey == NULL || len == NULL) {
         ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER);
         return 0;
     }

+    /*
+     * Only the length is requested, so get it from the key parameters
+     * instead of exporting the secret key.  This way the length is also
+     * available for non-exportable keys if the key management reports it.
+     */
+    if (key == NULL)
+        return evp_skeymgmt_export(skey->skeymgmt, skey->keydata,
+            OSSL_SKEYMGMT_SELECT_PARAMETERS,
+            get_key_length, len);
+
     raw_key.key = (const void **)key;
     raw_key.len = len;

diff --git a/doc/man3/EVP_SKEY.pod b/doc/man3/EVP_SKEY.pod
index b9ee17d1da..106679a7db 100644
--- a/doc/man3/EVP_SKEY.pod
+++ b/doc/man3/EVP_SKEY.pod
@@ -83,6 +83,11 @@ The EVP_SKEY_get0_raw_key() returns a pointer to a raw key bytes to the passed
 address and sets the key len. The returned address is managed by the internal
 key management and shouldn't be freed explicitly.  The operation can fail when
 the underlying key management doesn't support export of the secret key.
+If I<key> is NULL, only the key length is returned in I<*len>.  In that case
+the secret key is not exported; the length is obtained from the "key-length"
+(B<OSSL_SKEY_PARAM_KEY_LENGTH>) key parameter, so it is also available for keys
+that cannot be exported.  The operation fails if the underlying key management
+doesn't provide that parameter.

 The EVP_SKEY_get0_key_id() returns a NUL-terminated string providing some
 human-readable identifier of the key if provided by the underlying key
@@ -208,6 +213,9 @@ The EVP_SKEY_import_SKEYMGMT() function was introduced in OpenSSL 4.0.
 The EVP_SKEY_get0_local_keyid() and EVP_SKEY_get0_algorithm_id()
 functions were added in OpenSSL 4.1.

+Passing NULL as I<key> to EVP_SKEY_get0_raw_key() to retrieve only the key
+length was added in OpenSSL 4.2.
+
 =head1 COPYRIGHT

 Copyright 2025-2026 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/doc/man7/provider-skeymgmt.pod b/doc/man7/provider-skeymgmt.pod
index f6d894a78e..d796e1a61d 100644
--- a/doc/man7/provider-skeymgmt.pod
+++ b/doc/man7/provider-skeymgmt.pod
@@ -187,7 +187,10 @@ The value represents symmetric key as a byte array.

 =item "key-length" (B<OSSL_SKEY_PARAM_KEY_LENGTH>) <integer>

-The value is the byte length of the given key.
+The value is the byte length of the given key.  Key managements should
+provide it when exporting with B<OSSL_SKEYMGMT_SELECT_PARAMETERS>, including for
+keys whose raw bytes cannot be exported, so that the key length is available
+through L<EVP_SKEY_get0_raw_key(3)> without exporting the key.

 =item "skey-alias" (B<OSSL_SKEY_PARAM_ALIAS>) <UTF8 string>

diff --git a/include/openssl/evp.h.in b/include/openssl/evp.h.in
index 7bad41ba79..d6f309b539 100644
--- a/include/openssl/evp.h.in
+++ b/include/openssl/evp.h.in
@@ -1951,6 +1951,10 @@ EVP_SKEY *EVP_SKEY_import_raw_key(OSSL_LIB_CTX *libctx, const char *skeymgmtname
     const char *propquery);
 EVP_SKEY *EVP_SKEY_import_SKEYMGMT(OSSL_LIB_CTX *libctx, EVP_SKEYMGMT *skeymgmt,
     int selection, const OSSL_PARAM *params);
+/**
+ * @brief Get the raw bytes and/or the length of a symmetric key.
+ * @see EVP_SKEY_get0_raw_key(3)
+ */
 int EVP_SKEY_get0_raw_key(const EVP_SKEY *skey, const unsigned char **key,
     size_t *len);
 const char *EVP_SKEY_get0_key_id(const EVP_SKEY *skey);
diff --git a/providers/implementations/skeymgmt/generic.c b/providers/implementations/skeymgmt/generic.c
index a4f443f307..b1936f666d 100644
--- a/providers/implementations/skeymgmt/generic.c
+++ b/providers/implementations/skeymgmt/generic.c
@@ -137,7 +137,7 @@ int generic_export(void *keydata, int selection,
     OSSL_CALLBACK *param_callback, void *cbarg)
 {
     PROV_SKEY *gen = keydata;
-    OSSL_PARAM params[6];
+    OSSL_PARAM params[7];
     int idx = 0;

     if (!ossl_prov_is_running() || gen == NULL || selection == 0)
@@ -149,6 +149,9 @@ int generic_export(void *keydata, int selection,
             gen->data, gen->length);

     if ((selection & OSSL_SKEYMGMT_SELECT_PARAMETERS) != 0) {
+        params[idx++] = OSSL_PARAM_construct_size_t(OSSL_SKEY_PARAM_KEY_LENGTH,
+            &gen->length);
+
         if (gen->alias != NULL)
             params[idx++] = OSSL_PARAM_construct_utf8_string(
                 OSSL_SKEY_PARAM_ALIAS, gen->alias, 0);
diff --git a/test/evp_skey_test.c b/test/evp_skey_test.c
index 3ff4d93676..5e32330e4e 100644
--- a/test/evp_skey_test.c
+++ b/test/evp_skey_test.c
@@ -622,6 +622,77 @@ end:
     return ret;
 }

+static int test_skey_get0_raw_key_len(int tst)
+{
+    OSSL_PROVIDER *fake_prov = NULL;
+    EVP_SKEY *key = NULL;
+    OSSL_PARAM params[2];
+    unsigned char import_key[32] = {
+        0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B,
+        0x45, 0x59, 0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B, 0x45, 0x59,
+        0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B,
+        0x45, 0x59
+    };
+    const unsigned char *export_key = NULL;
+    size_t len = 0;
+    int ret = 0;
+
+    deflprov = OSSL_PROVIDER_load(libctx, "default");
+    if (!TEST_ptr(deflprov))
+        return 0;
+
+    switch (tst) {
+    case 0:
+        /* Generic key, only the length is requested */
+        if (!TEST_ptr(key = EVP_SKEY_import_raw_key(libctx,
+                          OSSL_SKEY_TYPE_GENERIC, import_key, 5, NULL))
+            || !TEST_int_eq(EVP_SKEY_get0_raw_key(key, NULL, &len), 1)
+            || !TEST_size_t_eq(len, 5))
+            goto end;
+        break;
+    case 1:
+        /* AES key, only the length is requested */
+        if (!TEST_ptr(key = EVP_SKEY_import_raw_key(libctx, OSSL_SKEY_TYPE_AES,
+                          import_key, sizeof(import_key), NULL))
+            || !TEST_int_eq(EVP_SKEY_get0_raw_key(key, NULL, &len), 1)
+            || !TEST_size_t_eq(len, sizeof(import_key)))
+            goto end;
+        break;
+    case 2:
+        /*
+         * The fake provider doesn't report OSSL_SKEY_PARAM_KEY_LENGTH, so
+         * requesting only the length fails even though the key is exportable.
+         */
+        if (!TEST_ptr(fake_prov = fake_cipher_start(libctx)))
+            goto end;
+        params[0] = OSSL_PARAM_construct_octet_string(OSSL_SKEY_PARAM_RAW_BYTES,
+            import_key, KEY_SIZE);
+        params[1] = OSSL_PARAM_construct_end();
+        if (!TEST_ptr(key = EVP_SKEY_import(libctx, "fake_cipher",
+                          FAKE_CIPHER_FETCH_PROPS,
+                          OSSL_SKEYMGMT_SELECT_ALL, params))
+            || !TEST_int_eq(EVP_SKEY_get0_raw_key(key, NULL, &len), 0))
+            goto end;
+        break;
+    case 3:
+        /* The length pointer is mandatory */
+        if (!TEST_ptr(key = EVP_SKEY_import_raw_key(libctx,
+                          OSSL_SKEY_TYPE_GENERIC, import_key, 5, NULL))
+            || !TEST_int_eq(EVP_SKEY_get0_raw_key(key, &export_key, NULL), 0))
+            goto end;
+        break;
+    default:
+        goto end;
+    }
+
+    ret = 1;
+end:
+    EVP_SKEY_free(key);
+    fake_cipher_finish(fake_prov);
+    OSSL_PROVIDER_unload(deflprov);
+    return ret;
+}
+
 int setup_tests(void)
 {
     libctx = OSSL_LIB_CTX_new();
@@ -635,6 +706,7 @@ int setup_tests(void)
     ADD_ALL_TESTS(test_skey_metadata_export, 3);
     ADD_TEST(test_skey_to_same_provider);
     ADD_TEST(test_skey_to_diff_provider);
+    ADD_ALL_TESTS(test_skey_get0_raw_key_len, 4);
     ADD_TEST(test_aes_raw_skey);
 #ifndef OPENSSL_NO_DES
     ADD_TEST(test_des_raw_skey);