Skip to content

Commit b574c6a

Browse files
committed
Cache legacy keys instead of downgrading them
If someone calls an EVP_PKEY_get0*() function then we create a legacy key and cache it in the EVP_PKEY - but it doesn't become an "origin" and it doesn't ever get updated. This will be documented as a restriction of the EVP_PKEY_get0*() function with provided keys. Fixes #14020 Reviewed-by: Richard Levitte <levitte@openssl.org> Reviewed-by: Shane Lontis <shane.lontis@oracle.com> Reviewed-by: Paul Dale <pauli@openssl.org> (Merged from #14319)
1 parent ec961f8 commit b574c6a

7 files changed

Lines changed: 121 additions & 166 deletions

File tree

crypto/evp/p_legacy.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,11 @@ int EVP_PKEY_set1_RSA(EVP_PKEY *pkey, RSA *key)
3333

3434
RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey)
3535
{
36-
if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
37-
ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
38-
return NULL;
39-
}
4036
if (pkey->type != EVP_PKEY_RSA && pkey->type != EVP_PKEY_RSA_PSS) {
4137
ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_AN_RSA_KEY);
4238
return NULL;
4339
}
44-
return pkey->pkey.rsa;
40+
return evp_pkey_get_legacy((EVP_PKEY *)pkey);
4541
}
4642

4743
RSA *EVP_PKEY_get1_RSA(EVP_PKEY *pkey)
@@ -65,15 +61,11 @@ int EVP_PKEY_set1_EC_KEY(EVP_PKEY *pkey, EC_KEY *key)
6561

6662
EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey)
6763
{
68-
if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
69-
ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
70-
return NULL;
71-
}
7264
if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) {
7365
EVPerr(EVP_F_EVP_PKEY_GET0_EC_KEY, EVP_R_EXPECTING_A_EC_KEY);
7466
return NULL;
7567
}
76-
return pkey->pkey.ec;
68+
return evp_pkey_get_legacy((EVP_PKEY *)pkey);
7769
}
7870

7971
EC_KEY *EVP_PKEY_get1_EC_KEY(EVP_PKEY *pkey)

crypto/evp/p_lib.c

Lines changed: 68 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
*/
1414
#include "internal/deprecated.h"
1515

16+
#include <assert.h>
1617
#include <stdio.h>
1718
#include "internal/cryptlib.h"
1819
#include "internal/refcount.h"
@@ -740,11 +741,8 @@ void *EVP_PKEY_get0(const EVP_PKEY *pkey)
740741
{
741742
if (pkey == NULL)
742743
return NULL;
743-
if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
744-
ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
745-
return NULL;
746-
}
747-
return pkey->pkey.ptr;
744+
745+
return evp_pkey_get_legacy((EVP_PKEY *)pkey);
748746
}
749747

750748
const unsigned char *EVP_PKEY_get0_hmac(const EVP_PKEY *pkey, size_t *len)
@@ -791,15 +789,11 @@ const unsigned char *EVP_PKEY_get0_siphash(const EVP_PKEY *pkey, size_t *len)
791789
# ifndef OPENSSL_NO_DSA
792790
DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey)
793791
{
794-
if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
795-
ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
796-
return NULL;
797-
}
798792
if (pkey->type != EVP_PKEY_DSA) {
799793
ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_A_DSA_KEY);
800794
return NULL;
801795
}
802-
return pkey->pkey.dsa;
796+
return evp_pkey_get_legacy((EVP_PKEY *)pkey);
803797
}
804798

805799
int EVP_PKEY_set1_DSA(EVP_PKEY *pkey, DSA *key)
@@ -823,15 +817,11 @@ DSA *EVP_PKEY_get1_DSA(EVP_PKEY *pkey)
823817
# ifndef OPENSSL_NO_EC
824818
static ECX_KEY *evp_pkey_get0_ECX_KEY(const EVP_PKEY *pkey, int type)
825819
{
826-
if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
827-
ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
828-
return NULL;
829-
}
830820
if (EVP_PKEY_base_id(pkey) != type) {
831821
ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_A_ECX_KEY);
832822
return NULL;
833823
}
834-
return pkey->pkey.ecx;
824+
return evp_pkey_get_legacy((EVP_PKEY *)pkey);
835825
}
836826

837827
static ECX_KEY *evp_pkey_get1_ECX_KEY(EVP_PKEY *pkey, int type)
@@ -868,15 +858,11 @@ int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key)
868858

869859
DH *EVP_PKEY_get0_DH(const EVP_PKEY *pkey)
870860
{
871-
if (!evp_pkey_downgrade((EVP_PKEY *)pkey)) {
872-
ERR_raise(ERR_LIB_EVP, EVP_R_INACCESSIBLE_KEY);
873-
return NULL;
874-
}
875861
if (pkey->type != EVP_PKEY_DH && pkey->type != EVP_PKEY_DHX) {
876862
ERR_raise(ERR_LIB_EVP, EVP_R_EXPECTING_A_DH_KEY);
877863
return NULL;
878864
}
879-
return pkey->pkey.dh;
865+
return evp_pkey_get_legacy((EVP_PKEY *)pkey);
880866
}
881867

882868
DH *EVP_PKEY_get1_DH(EVP_PKEY *pkey)
@@ -1310,36 +1296,6 @@ size_t EVP_PKEY_get1_encoded_public_key(EVP_PKEY *pkey, unsigned char **ppub)
13101296

13111297
/*- All methods below can also be used in FIPS_MODULE */
13121298

1313-
/*
1314-
* This reset function must be used very carefully, as it literally throws
1315-
* away everything in an EVP_PKEY without freeing them, and may cause leaks
1316-
* of memory, what have you.
1317-
* The only reason we have this is to have the same code for EVP_PKEY_new()
1318-
* and evp_pkey_downgrade().
1319-
*/
1320-
static int evp_pkey_reset_unlocked(EVP_PKEY *pk)
1321-
{
1322-
if (pk == NULL)
1323-
return 0;
1324-
1325-
if (pk->lock != NULL) {
1326-
const size_t offset = (unsigned char *)&pk->lock - (unsigned char *)pk;
1327-
1328-
memset(pk, 0, offset);
1329-
memset((unsigned char *)pk + offset + sizeof(pk->lock),
1330-
0,
1331-
sizeof(*pk) - offset - sizeof(pk->lock));
1332-
}
1333-
/* EVP_PKEY_new uses zalloc so no need to call memset if pk->lock is NULL */
1334-
1335-
pk->type = EVP_PKEY_NONE;
1336-
pk->save_type = EVP_PKEY_NONE;
1337-
pk->references = 1;
1338-
pk->save_parameters = 1;
1339-
1340-
return 1;
1341-
}
1342-
13431299
EVP_PKEY *EVP_PKEY_new(void)
13441300
{
13451301
EVP_PKEY *ret = OPENSSL_zalloc(sizeof(*ret));
@@ -1349,8 +1305,10 @@ EVP_PKEY *EVP_PKEY_new(void)
13491305
return NULL;
13501306
}
13511307

1352-
if (!evp_pkey_reset_unlocked(ret))
1353-
goto err;
1308+
ret->type = EVP_PKEY_NONE;
1309+
ret->save_type = EVP_PKEY_NONE;
1310+
ret->references = 1;
1311+
ret->save_parameters = 1;
13541312

13551313
ret->lock = CRYPTO_THREAD_lock_new();
13561314
if (ret->lock == NULL) {
@@ -1559,12 +1517,32 @@ int EVP_PKEY_up_ref(EVP_PKEY *pkey)
15591517
#ifndef FIPS_MODULE
15601518
void evp_pkey_free_legacy(EVP_PKEY *x)
15611519
{
1562-
if (x->ameth != NULL) {
1563-
if (x->ameth->pkey_free != NULL)
1564-
x->ameth->pkey_free(x);
1520+
const EVP_PKEY_ASN1_METHOD *ameth = x->ameth;
1521+
ENGINE *tmpe = NULL;
1522+
1523+
if (ameth == NULL && x->legacy_cache_pkey.ptr != NULL)
1524+
ameth = EVP_PKEY_asn1_find(&tmpe, x->type);
1525+
1526+
if (ameth != NULL) {
1527+
if (x->legacy_cache_pkey.ptr != NULL) {
1528+
/*
1529+
* We should never have both a legacy origin key, and a key in the
1530+
* legacy cache.
1531+
*/
1532+
assert(x->pkey.ptr == NULL);
1533+
/*
1534+
* For the purposes of freeing we make the legacy cache look like
1535+
* a legacy origin key.
1536+
*/
1537+
x->pkey = x->legacy_cache_pkey;
1538+
x->legacy_cache_pkey.ptr = NULL;
1539+
}
1540+
if (ameth->pkey_free != NULL)
1541+
ameth->pkey_free(x);
15651542
x->pkey.ptr = NULL;
15661543
}
15671544
# ifndef OPENSSL_NO_ENGINE
1545+
ENGINE_finish(tmpe);
15681546
ENGINE_finish(x->engine);
15691547
x->engine = NULL;
15701548
ENGINE_finish(x->pmeth_engine);
@@ -1877,78 +1855,57 @@ int evp_pkey_copy_downgraded(EVP_PKEY **dest, const EVP_PKEY *src)
18771855
return 0;
18781856
}
18791857

1880-
int evp_pkey_downgrade(EVP_PKEY *pk)
1858+
void *evp_pkey_get_legacy(EVP_PKEY *pk)
18811859
{
1882-
EVP_PKEY tmp_copy; /* Stack allocated! */
1883-
int rv = 0;
1860+
EVP_PKEY *tmp_copy = NULL;
1861+
void *ret = NULL;
18841862

18851863
if (!ossl_assert(pk != NULL))
1886-
return 0;
1864+
return NULL;
18871865

18881866
/*
1889-
* Throughout this whole function, we must ensure that we lock / unlock
1890-
* the exact same lock. Note that we do pass it around a bit.
1867+
* If this isn't an assigned provider side key, we just use any existing
1868+
* origin legacy key.
18911869
*/
1892-
if (!CRYPTO_THREAD_write_lock(pk->lock))
1893-
return 0;
1870+
if (!evp_pkey_is_assigned(pk))
1871+
return NULL;
1872+
if (!evp_pkey_is_provided(pk))
1873+
return pk->pkey.ptr;
18941874

1895-
/* If this isn't an assigned provider side key, we're done */
1896-
if (!evp_pkey_is_assigned(pk) || !evp_pkey_is_provided(pk)) {
1897-
rv = 1;
1898-
goto end;
1899-
}
1875+
if (!CRYPTO_THREAD_read_lock(pk->lock))
1876+
return NULL;
19001877

1901-
/*
1902-
* To be able to downgrade, we steal the contents of |pk|, then reset
1903-
* it, and finally try to make it a downgraded copy. If any of that
1904-
* fails, we restore the copied contents into |pk|.
1905-
*/
1906-
tmp_copy = *pk; /* |tmp_copy| now owns THE lock */
1878+
ret = pk->legacy_cache_pkey.ptr;
19071879

1908-
if (evp_pkey_reset_unlocked(pk)
1909-
&& evp_pkey_copy_downgraded(&pk, &tmp_copy)) {
1880+
if (!CRYPTO_THREAD_unlock(pk->lock))
1881+
return NULL;
19101882

1911-
/* Restore the common attributes, then empty |tmp_copy| */
1912-
pk->references = tmp_copy.references;
1913-
pk->attributes = tmp_copy.attributes;
1914-
pk->save_parameters = tmp_copy.save_parameters;
1915-
pk->ex_data = tmp_copy.ex_data;
1883+
if (ret != NULL)
1884+
return ret;
19161885

1917-
/* Ensure that stuff we've copied won't be freed */
1918-
tmp_copy.lock = NULL;
1919-
tmp_copy.attributes = NULL;
1920-
memset(&tmp_copy.ex_data, 0, sizeof(tmp_copy.ex_data));
1886+
if (!evp_pkey_copy_downgraded(&tmp_copy, pk))
1887+
return NULL;
19211888

1922-
/*
1923-
* Save the provider side data in the operation cache, so they'll
1924-
* find it again. |pk| is new, so it's safe to assume slot zero
1925-
* is free.
1926-
* Note that evp_keymgmt_util_cache_keydata() increments keymgmt's
1927-
* reference count, so we need to decrement it, or there will be a
1928-
* leak.
1929-
*/
1930-
evp_keymgmt_util_cache_keydata(pk, tmp_copy.keymgmt,
1931-
tmp_copy.keydata);
1932-
EVP_KEYMGMT_free(tmp_copy.keymgmt);
1889+
if (!CRYPTO_THREAD_write_lock(pk->lock))
1890+
goto err;
19331891

1934-
/*
1935-
* Clear keymgmt and keydata from |tmp_copy|, or they'll get
1936-
* inadvertently freed.
1937-
*/
1938-
tmp_copy.keymgmt = NULL;
1939-
tmp_copy.keydata = NULL;
1892+
/* Check again in case some other thread has updated it in the meantime */
1893+
ret = pk->legacy_cache_pkey.ptr;
1894+
if (ret == NULL) {
1895+
/* Steal the legacy key reference from the temporary copy */
1896+
ret = pk->legacy_cache_pkey.ptr = tmp_copy->pkey.ptr;
1897+
tmp_copy->pkey.ptr = NULL;
1898+
}
19401899

1941-
evp_pkey_free_it(&tmp_copy);
1942-
rv = 1;
1943-
} else {
1944-
/* Restore the original key */
1945-
*pk = tmp_copy;
1900+
if (!CRYPTO_THREAD_unlock(pk->lock)) {
1901+
ret = NULL;
1902+
goto err;
19461903
}
19471904

1948-
end:
1949-
if (!CRYPTO_THREAD_unlock(pk->lock))
1950-
return 0;
1951-
return rv;
1905+
err:
1906+
EVP_PKEY_free(tmp_copy);
1907+
1908+
return ret;
19521909
}
19531910
#endif /* FIPS_MODULE */
19541911

crypto/evp/pmeth_gn.c

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ int EVP_PKEY_gen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
197197
#endif
198198

199199
/*
200-
* Because we still have legacy keys, and evp_pkey_downgrade()
200+
* Because we still have legacy keys
201201
* TODO remove this #legacy internal keys are gone
202202
*/
203203
(*ppkey)->type = ctx->legacy_keytype;
@@ -208,8 +208,17 @@ int EVP_PKEY_gen(EVP_PKEY_CTX *ctx, EVP_PKEY **ppkey)
208208
#ifdef FIPS_MODULE
209209
goto not_supported;
210210
#else
211-
if (ctx->pkey && !evp_pkey_downgrade(ctx->pkey))
211+
/*
212+
* If we get here then we're using legacy paramgen/keygen. In that case
213+
* the pkey in ctx (if there is one) had better not be provided (because the
214+
* legacy methods may not know how to handle it). However we can only get
215+
* here if ctx->op.keymgmt.genctx == NULL, but that should never be the case
216+
* if ctx->pkey is provided because we don't allow this when we initialise
217+
* the ctx.
218+
*/
219+
if (ctx->pkey != NULL && !ossl_assert(!evp_pkey_is_provided(ctx->pkey)))
212220
goto not_accessible;
221+
213222
switch (ctx->operation) {
214223
case EVP_PKEY_OP_PARAMGEN:
215224
ret = ctx->pmeth->paramgen(ctx, *ppkey);

crypto/evp/pmeth_lib.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,7 @@ static EVP_PKEY_CTX *int_ctx_new(OSSL_LIB_CTX *libctx,
266266
/*
267267
* Chase down the legacy NID, as that might be needed for diverse
268268
* purposes, such as ensure that EVP_PKEY_type() can return sensible
269-
* values, or that there's a better chance to "downgrade" a key when
270-
* needed. We go through all keymgmt names, because the keytype
269+
* values. We go through all keymgmt names, because the keytype
271270
* that's passed to this function doesn't necessarily translate
272271
* directly.
273272
* TODO: Remove this when #legacy keys are gone.

doc/internal/man3/evp_pkey_export_to_provider.pod

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,9 @@ evp_pkey_downgrade() returns 1 on success or 0 on error.
5353

5454
=head1 NOTES
5555

56-
Some functions calling evp_pkey_export_to_provider() or evp_pkey_downgrade()
57-
may have received a const key, and may therefore have to cast the key to
58-
non-const form to call this function. Since B<EVP_PKEY> is always dynamically
59-
allocated, this is OK.
56+
Some functions calling evp_pkey_export_to_provider() may have received a const
57+
key, and may therefore have to cast the key to non-const form to call this
58+
function. Since B<EVP_PKEY> is always dynamically allocated, this is OK.
6059

6160
=head1 SEE ALSO
6261

0 commit comments

Comments
 (0)