Skip to content

Commit 0673930

Browse files
IvaAMarinovat8m
authored andcommitted
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: openssl#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: openssl#33035
1 parent 64fa9d5 commit 0673930

7 files changed

Lines changed: 124 additions & 3 deletions

File tree

‎CHANGES.md‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@ OpenSSL 4.2
3333

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

36+
* `EVP_SKEY_get0_raw_key()` now accepts a NULL key pointer to retrieve only
37+
the key length. The length is obtained from the `OSSL_SKEY_PARAM_KEY_LENGTH`
38+
key parameter without exporting the key, so it is also available for keys
39+
that cannot be exported, provided the key management reports it. The
40+
built-in key managements now include this parameter when exporting key
41+
parameters.
42+
<!-- https://github.com/openssl/openssl/pull/33035 -->
43+
44+
*Iva Marinova*
45+
3646
* `X509_OBJECT_up_ref_count()` has been deprecated. Despite its name,
3747
X509_OBJECT_up_ref_count() does not reference count the X509_OBJECT itself.
3848
With X509_OBJECT being opaque there is nothing useful an application can do

‎crypto/evp/s_lib.c‎

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,16 +160,37 @@ static int get_secret_key(const OSSL_PARAM params[], void *arg)
160160
return 0;
161161
}
162162

163+
static int get_key_length(const OSSL_PARAM params[], void *arg)
164+
{
165+
const OSSL_PARAM *p = NULL;
166+
size_t *len = arg;
167+
168+
if ((p = OSSL_PARAM_locate_const(params, OSSL_SKEY_PARAM_KEY_LENGTH)) != NULL)
169+
return OSSL_PARAM_get_size_t(p, len);
170+
171+
return 0;
172+
}
173+
163174
int EVP_SKEY_get0_raw_key(const EVP_SKEY *skey, const unsigned char **key,
164175
size_t *len)
165176
{
166177
struct raw_key_details_st raw_key;
167178

168-
if (skey == NULL || key == NULL || len == NULL) {
179+
if (skey == NULL || len == NULL) {
169180
ERR_raise(ERR_LIB_EVP, ERR_R_PASSED_NULL_PARAMETER);
170181
return 0;
171182
}
172183

184+
/*
185+
* Only the length is requested, so get it from the key parameters
186+
* instead of exporting the secret key. This way the length is also
187+
* available for non-exportable keys if the key management reports it.
188+
*/
189+
if (key == NULL)
190+
return evp_skeymgmt_export(skey->skeymgmt, skey->keydata,
191+
OSSL_SKEYMGMT_SELECT_PARAMETERS,
192+
get_key_length, len);
193+
173194
raw_key.key = (const void **)key;
174195
raw_key.len = len;
175196

‎doc/man3/EVP_SKEY.pod‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,11 @@ The EVP_SKEY_get0_raw_key() returns a pointer to a raw key bytes to the passed
8383
address and sets the key len. The returned address is managed by the internal
8484
key management and shouldn't be freed explicitly. The operation can fail when
8585
the underlying key management doesn't support export of the secret key.
86+
If I<key> is NULL, only the key length is returned in I<*len>. In that case
87+
the secret key is not exported; the length is obtained from the "key-length"
88+
(B<OSSL_SKEY_PARAM_KEY_LENGTH>) key parameter, so it is also available for keys
89+
that cannot be exported. The operation fails if the underlying key management
90+
doesn't provide that parameter.
8691

8792
The EVP_SKEY_get0_key_id() returns a NUL-terminated string providing some
8893
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.
208213
The EVP_SKEY_get0_local_keyid() and EVP_SKEY_get0_algorithm_id()
209214
functions were added in OpenSSL 4.1.
210215

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

213221
Copyright 2025-2026 The OpenSSL Project Authors. All Rights Reserved.

‎doc/man7/provider-skeymgmt.pod‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,10 @@ The value represents symmetric key as a byte array.
187187

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

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

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

‎include/openssl/evp.h.in‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1951,6 +1951,10 @@ EVP_SKEY *EVP_SKEY_import_raw_key(OSSL_LIB_CTX *libctx, const char *skeymgmtname
19511951
const char *propquery);
19521952
EVP_SKEY *EVP_SKEY_import_SKEYMGMT(OSSL_LIB_CTX *libctx, EVP_SKEYMGMT *skeymgmt,
19531953
int selection, const OSSL_PARAM *params);
1954+
/**
1955+
* @brief Get the raw bytes and/or the length of a symmetric key.
1956+
* @see EVP_SKEY_get0_raw_key(3)
1957+
*/
19541958
int EVP_SKEY_get0_raw_key(const EVP_SKEY *skey, const unsigned char **key,
19551959
size_t *len);
19561960
const char *EVP_SKEY_get0_key_id(const EVP_SKEY *skey);

‎providers/implementations/skeymgmt/generic.c‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ int generic_export(void *keydata, int selection,
137137
OSSL_CALLBACK *param_callback, void *cbarg)
138138
{
139139
PROV_SKEY *gen = keydata;
140-
OSSL_PARAM params[6];
140+
OSSL_PARAM params[7];
141141
int idx = 0;
142142

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

151151
if ((selection & OSSL_SKEYMGMT_SELECT_PARAMETERS) != 0) {
152+
params[idx++] = OSSL_PARAM_construct_size_t(OSSL_SKEY_PARAM_KEY_LENGTH,
153+
&gen->length);
154+
152155
if (gen->alias != NULL)
153156
params[idx++] = OSSL_PARAM_construct_utf8_string(
154157
OSSL_SKEY_PARAM_ALIAS, gen->alias, 0);

‎test/evp_skey_test.c‎

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,77 @@ static int test_skey_to_diff_provider(void)
622622
return ret;
623623
}
624624

625+
static int test_skey_get0_raw_key_len(int tst)
626+
{
627+
OSSL_PROVIDER *fake_prov = NULL;
628+
EVP_SKEY *key = NULL;
629+
OSSL_PARAM params[2];
630+
unsigned char import_key[32] = {
631+
0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B,
632+
0x45, 0x59, 0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B, 0x45, 0x59,
633+
0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B, 0x45, 0x59, 0x53, 0x4B,
634+
0x45, 0x59
635+
};
636+
const unsigned char *export_key = NULL;
637+
size_t len = 0;
638+
int ret = 0;
639+
640+
deflprov = OSSL_PROVIDER_load(libctx, "default");
641+
if (!TEST_ptr(deflprov))
642+
return 0;
643+
644+
switch (tst) {
645+
case 0:
646+
/* Generic key, only the length is requested */
647+
if (!TEST_ptr(key = EVP_SKEY_import_raw_key(libctx,
648+
OSSL_SKEY_TYPE_GENERIC, import_key, 5, NULL))
649+
|| !TEST_int_eq(EVP_SKEY_get0_raw_key(key, NULL, &len), 1)
650+
|| !TEST_size_t_eq(len, 5))
651+
goto end;
652+
break;
653+
case 1:
654+
/* AES key, only the length is requested */
655+
if (!TEST_ptr(key = EVP_SKEY_import_raw_key(libctx, OSSL_SKEY_TYPE_AES,
656+
import_key, sizeof(import_key), NULL))
657+
|| !TEST_int_eq(EVP_SKEY_get0_raw_key(key, NULL, &len), 1)
658+
|| !TEST_size_t_eq(len, sizeof(import_key)))
659+
goto end;
660+
break;
661+
case 2:
662+
/*
663+
* The fake provider doesn't report OSSL_SKEY_PARAM_KEY_LENGTH, so
664+
* requesting only the length fails even though the key is exportable.
665+
*/
666+
if (!TEST_ptr(fake_prov = fake_cipher_start(libctx)))
667+
goto end;
668+
params[0] = OSSL_PARAM_construct_octet_string(OSSL_SKEY_PARAM_RAW_BYTES,
669+
import_key, KEY_SIZE);
670+
params[1] = OSSL_PARAM_construct_end();
671+
if (!TEST_ptr(key = EVP_SKEY_import(libctx, "fake_cipher",
672+
FAKE_CIPHER_FETCH_PROPS,
673+
OSSL_SKEYMGMT_SELECT_ALL, params))
674+
|| !TEST_int_eq(EVP_SKEY_get0_raw_key(key, NULL, &len), 0))
675+
goto end;
676+
break;
677+
case 3:
678+
/* The length pointer is mandatory */
679+
if (!TEST_ptr(key = EVP_SKEY_import_raw_key(libctx,
680+
OSSL_SKEY_TYPE_GENERIC, import_key, 5, NULL))
681+
|| !TEST_int_eq(EVP_SKEY_get0_raw_key(key, &export_key, NULL), 0))
682+
goto end;
683+
break;
684+
default:
685+
goto end;
686+
}
687+
688+
ret = 1;
689+
end:
690+
EVP_SKEY_free(key);
691+
fake_cipher_finish(fake_prov);
692+
OSSL_PROVIDER_unload(deflprov);
693+
return ret;
694+
}
695+
625696
int setup_tests(void)
626697
{
627698
libctx = OSSL_LIB_CTX_new();
@@ -635,6 +706,7 @@ int setup_tests(void)
635706
ADD_ALL_TESTS(test_skey_metadata_export, 3);
636707
ADD_TEST(test_skey_to_same_provider);
637708
ADD_TEST(test_skey_to_diff_provider);
709+
ADD_ALL_TESTS(test_skey_get0_raw_key_len, 4);
638710
ADD_TEST(test_aes_raw_skey);
639711
#ifndef OPENSSL_NO_DES
640712
ADD_TEST(test_des_raw_skey);

0 commit comments

Comments
 (0)