X509: Add d2i_PUBKEY_ex(), which take a libctx and propq

Just like d2i_PrivateKey() / d2i_PrivateKey_ex(), there's a need to
associate an EVP_PKEY extracted from a PUBKEY to a library context and
a property query string.  Without it, a provider-native EVP_PKEY can
only fetch necessary internal algorithms from the default library
context, even though an application specific context should be used.

Reviewed-by: Paul Dale <paul.dale@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/12671)
This commit is contained in:
Richard Levitte 2020-08-18 20:39:45 +02:00
parent 3b1fd0b003
commit 22b814443e
7 changed files with 73 additions and 16 deletions

View File

@ -22,6 +22,7 @@
#include <openssl/asn1t.h>
#include "crypto/asn1.h"
#include "crypto/evp.h"
#include "crypto/x509.h"
#include <openssl/core_names.h>
#include "openssl/param_build.h"
#include "ec_local.h"
@ -161,12 +162,15 @@ static int eckey_pub_decode(EVP_PKEY *pkey, const X509_PUBKEY *pubkey)
int ptype, pklen;
EC_KEY *eckey = NULL;
X509_ALGOR *palg;
OPENSSL_CTX *libctx = NULL;
const char *propq = NULL;
if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
if (!X509_PUBKEY_get0_libctx(&libctx, &propq, pubkey)
|| !X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey))
return 0;
X509_ALGOR_get0(NULL, &ptype, &pval, palg);
eckey = eckey_type2param(ptype, pval, NULL, NULL);
eckey = eckey_type2param(ptype, pval, libctx, propq);
if (!eckey) {
ECerr(EC_F_ECKEY_PUB_DECODE, ERR_R_EC_LIB);

View File

@ -28,6 +28,10 @@ struct X509_pubkey_st {
X509_ALGOR *algor;
ASN1_BIT_STRING *public_key;
EVP_PKEY *pkey;
/* extra data for the callback, used by d2i_PUBKEY_ex */
OPENSSL_CTX *libctx;
const char *propq;
};
static int x509_pubkey_decode(EVP_PKEY **pk, const X509_PUBKEY *key);
@ -228,32 +232,57 @@ EVP_PKEY *X509_PUBKEY_get(const X509_PUBKEY *key)
}
/*
* Now two pseudo ASN1 routines that take an EVP_PKEY structure and encode or
* decode as X509_PUBKEY
* Now three pseudo ASN1 routines that take an EVP_PKEY structure and encode
* or decode as X509_PUBKEY
*/
EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length)
EVP_PKEY *d2i_PUBKEY_ex(EVP_PKEY **a, const unsigned char **pp, long length,
OPENSSL_CTX *libctx, const char *propq)
{
X509_PUBKEY *xpk;
EVP_PKEY *pktmp;
X509_PUBKEY *xpk, *xpk2 = NULL, **pxpk = NULL;
EVP_PKEY *pktmp = NULL;
const unsigned char *q;
q = *pp;
xpk = d2i_X509_PUBKEY(NULL, &q, length);
/*
* If libctx or propq are non-NULL, we take advantage of the reuse
* feature. It's not generally recommended, but is safe enough for
* newly created structures.
*/
if (libctx != NULL || propq != NULL) {
xpk2 = OPENSSL_zalloc(sizeof(*xpk2));
if (xpk2 == NULL) {
ERR_raise(ERR_LIB_X509, ERR_R_MALLOC_FAILURE);
return NULL;
}
xpk2->libctx = libctx;
xpk2->propq = propq;
pxpk = &xpk2;
}
xpk = d2i_X509_PUBKEY(pxpk, &q, length);
if (xpk == NULL)
return NULL;
goto end;
pktmp = X509_PUBKEY_get(xpk);
X509_PUBKEY_free(xpk);
xpk2 = NULL; /* We know that xpk == xpk2 */
if (pktmp == NULL)
return NULL;
goto end;
*pp = q;
if (a != NULL) {
EVP_PKEY_free(*a);
*a = pktmp;
}
end:
X509_PUBKEY_free(xpk2);
return pktmp;
}
EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length)
{
return d2i_PUBKEY_ex(a, pp, length, NULL, NULL);
}
int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp)
{
int ret = -1;
@ -493,3 +522,13 @@ int X509_PUBKEY_eq(const X509_PUBKEY *a, const X509_PUBKEY *b)
return -2;
return EVP_PKEY_eq(pA, pB);
}
int X509_PUBKEY_get0_libctx(OPENSSL_CTX **plibctx, const char **ppropq,
const X509_PUBKEY *key)
{
if (plibctx)
*plibctx = key->libctx;
if (ppropq)
*ppropq = key->propq;
return 1;
}

View File

@ -4,7 +4,7 @@
X509_PUBKEY_new, X509_PUBKEY_free, X509_PUBKEY_dup,
X509_PUBKEY_set, X509_PUBKEY_get0, X509_PUBKEY_get,
d2i_PUBKEY, i2d_PUBKEY, d2i_PUBKEY_bio, d2i_PUBKEY_fp,
d2i_PUBKEY_ex, d2i_PUBKEY, i2d_PUBKEY, d2i_PUBKEY_bio, d2i_PUBKEY_fp,
i2d_PUBKEY_fp, i2d_PUBKEY_bio, X509_PUBKEY_set0_param, X509_PUBKEY_get0_param,
X509_PUBKEY_eq - SubjectPublicKeyInfo public key functions
@ -20,6 +20,8 @@ X509_PUBKEY_eq - SubjectPublicKeyInfo public key functions
EVP_PKEY *X509_PUBKEY_get0(const X509_PUBKEY *key);
EVP_PKEY *X509_PUBKEY_get(const X509_PUBKEY *key);
EVP_PKEY *d2i_PUBKEY_ex(EVP_PKEY **a, const unsigned char **pp, long length,
OPENSSL_CTX *libctx, const char *propq);
EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length);
int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp);
@ -58,9 +60,15 @@ X509_PUBKEY_get() is similar to X509_PUBKEY_get0() except the reference
count on the returned key is incremented so it B<MUST> be freed using
EVP_PKEY_free() after use.
d2i_PUBKEY() and i2d_PUBKEY() decode and encode an B<EVP_PKEY> structure
using B<SubjectPublicKeyInfo> format. They otherwise follow the conventions of
other ASN.1 functions such as d2i_X509().
d2i_PUBKEY_ex() decodes an B<EVP_PKEY> structure using B<SubjectPublicKeyInfo>
format. Some public key decoding implementations may use cryptographic
algorithms. In this case the supplied library context I<libctx> and property
query string I<propq> are used.
d2i_PUBKEY() does the same as d2i_PUBKEY_ex() except that the default
library context and property query string are used.
i2d_PUBKEY() encodes an B<EVP_PKEY> structure using B<SubjectPublicKeyInfo>
format.
d2i_PUBKEY_bio(), d2i_PUBKEY_fp(), i2d_PUBKEY_bio() and i2d_PUBKEY_fp() are
similar to d2i_PUBKEY() and i2d_PUBKEY() except they decode or encode using a

View File

@ -306,3 +306,6 @@ int asn1_item_digest_with_libctx(const ASN1_ITEM *it, const EVP_MD *type,
unsigned int *len, OPENSSL_CTX *libctx,
const char *propq);
int X509_add_cert_new(STACK_OF(X509) **sk, X509 *cert, int flags);
int X509_PUBKEY_get0_libctx(OPENSSL_CTX **plibctx, const char **ppropq,
const X509_PUBKEY *key);

View File

@ -522,6 +522,8 @@ EVP_PKEY *X509_PUBKEY_get(const X509_PUBKEY *key);
int X509_get_pubkey_parameters(EVP_PKEY *pkey, STACK_OF(X509) *chain);
long X509_get_pathlen(X509 *x);
DECLARE_ASN1_ENCODE_FUNCTIONS_only(EVP_PKEY, PUBKEY)
EVP_PKEY *d2i_PUBKEY_ex(EVP_PKEY **a, const unsigned char **pp, long length,
OPENSSL_CTX *libctx, const char *propq);
# ifndef OPENSSL_NO_RSA
DECLARE_ASN1_ENCODE_FUNCTIONS_only(RSA, RSA_PUBKEY)
# endif

View File

@ -127,7 +127,7 @@ static int der2key_deserialize(void *vctx, OSSL_CORE_BIO *cin,
libctx, NULL);
if (pkey == NULL) {
derp = der;
pkey = d2i_PUBKEY(NULL, &derp, der_len);
pkey = d2i_PUBKEY_ex(NULL, &derp, der_len, libctx, NULL);
}
if (pkey == NULL) {
@ -142,7 +142,7 @@ static int der2key_deserialize(void *vctx, OSSL_CORE_BIO *cin,
*
* TODO(3.0): The check should be done with EVP_PKEY_is_a(), but
* as long as we still have #legacy internal keys, it's safer to
* use the type numbers in side the provider.
* use the type numbers inside the provider.
*/
if (EVP_PKEY_id(pkey) == ctx->desc->type)
key = ctx->desc->extract_key(pkey);

View File

@ -5262,3 +5262,4 @@ EVP_SIGNATURE_gettable_ctx_params ? 3_0_0 EXIST::FUNCTION:
EVP_SIGNATURE_settable_ctx_params ? 3_0_0 EXIST::FUNCTION:
EVP_KEYEXCH_gettable_ctx_params ? 3_0_0 EXIST::FUNCTION:
EVP_KEYEXCH_settable_ctx_params ? 3_0_0 EXIST::FUNCTION:
d2i_PUBKEY_ex ? 3_0_0 EXIST::FUNCTION: