X-Git-Url: https://git.openssl.org/gitweb/?p=openssl.git;a=blobdiff_plain;f=crypto%2Fx509%2Fx_pubkey.c;h=7d39254685df65a7c0919845bf335be854c38528;hp=c4d2806ea17aeb5d30039c250c8d0b810dd1a56b;hb=a829b735b645516041b55746e013692babd8cd31;hpb=f41ac0eeab9d2889d44e3acf6ff1e8274d03d73e diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c index c4d2806ea1..7d39254685 100644 --- a/crypto/x509/x_pubkey.c +++ b/crypto/x509/x_pubkey.c @@ -1,5 +1,5 @@ /* - * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the Apache License 2.0 (the "License"). You may not use * this file except in compliance with the License. You can obtain a copy @@ -22,26 +22,31 @@ #include "crypto/x509.h" #include #include -#include +#include +#include "internal/provider.h" 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 */ + OSSL_LIB_CTX *libctx; + const char *propq; }; -static int x509_pubkey_decode(EVP_PKEY **pk, X509_PUBKEY *key); +static int x509_pubkey_decode(EVP_PKEY **pk, const X509_PUBKEY *key); /* Minor tweak to operation: free up EVP_PKEY */ static int pubkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, void *exarg) { + X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; + if (operation == ASN1_OP_FREE_POST) { - X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; EVP_PKEY_free(pubkey->pkey); } else if (operation == ASN1_OP_D2I_POST) { /* Attempt to decode public key and cache in pubkey structure. */ - X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; EVP_PKEY_free(pubkey->pkey); pubkey->pkey = NULL; /* @@ -50,8 +55,10 @@ static int pubkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it, * will return an appropriate error. */ ERR_set_mark(); - if (x509_pubkey_decode(&pubkey->pkey, pubkey) == -1) + if (x509_pubkey_decode(&pubkey->pkey, pubkey) == -1) { + ERR_clear_last_mark(); return 0; + } ERR_pop_to_mark(); } return 1; @@ -91,21 +98,25 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) X509err(X509_F_X509_PUBKEY_SET, X509_R_METHOD_NOT_SUPPORTED); goto error; } - } else if (pkey->pkeys[0].keymgmt != NULL) { - BIO *bmem = BIO_new(BIO_s_mem()); - const char *serprop = "format=der,type=public"; - OSSL_SERIALIZER_CTX *sctx = - OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(pkey, serprop); - - if (OSSL_SERIALIZER_to_bio(sctx, bmem)) { - const unsigned char *der = NULL; - long derlen = BIO_get_mem_data(bmem, (char **)&der); - - pk = d2i_X509_PUBKEY(NULL, &der, derlen); + } else if (evp_pkey_is_provided(pkey)) { + const OSSL_PROVIDER *pkprov = EVP_KEYMGMT_provider(pkey->keymgmt); + OSSL_LIB_CTX *libctx = ossl_provider_libctx(pkprov); + unsigned char *der = NULL; + size_t derlen = 0; + int selection = (OSSL_KEYMGMT_SELECT_PUBLIC_KEY + | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS); + OSSL_ENCODER_CTX *ectx = + OSSL_ENCODER_CTX_new_by_EVP_PKEY(pkey, "DER", selection, + libctx, NULL); + + if (OSSL_ENCODER_to_data(ectx, &der, &derlen)) { + const unsigned char *pder = der; + + pk = d2i_X509_PUBKEY(NULL, &pder, (long)derlen); } - OSSL_SERIALIZER_CTX_free(sctx); - BIO_free(bmem); + OSSL_ENCODER_CTX_free(ectx); + OPENSSL_free(der); } if (pk == NULL) @@ -120,7 +131,7 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) /* * pk->pkey is NULL when using the legacy routine, but is non-NULL when - * going through the serializer, and for all intents and purposes, it's + * going through the encoder, and for all intents and purposes, it's * a perfect copy of |pkey|, just not the same instance. In that case, * we could simply return early, right here. * However, in the interest of being cautious leaning on paranoia, some @@ -151,7 +162,7 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) */ -static int x509_pubkey_decode(EVP_PKEY **ppkey, X509_PUBKEY *key) +static int x509_pubkey_decode(EVP_PKEY **ppkey, const X509_PUBKEY *key) { EVP_PKEY *pkey = EVP_PKEY_new(); @@ -171,10 +182,8 @@ static int x509_pubkey_decode(EVP_PKEY **ppkey, X509_PUBKEY *key) * future we could have different return codes for decode * errors and fatal errors such as malloc failure. */ - if (!pkey->ameth->pub_decode(pkey, key)) { - X509err(X509_F_X509_PUBKEY_DECODE, X509_R_PUBLIC_KEY_DECODE_ERROR); + if (!pkey->ameth->pub_decode(pkey, key)) goto error; - } } else { X509err(X509_F_X509_PUBKEY_DECODE, X509_R_METHOD_NOT_SUPPORTED); goto error; @@ -188,7 +197,7 @@ static int x509_pubkey_decode(EVP_PKEY **ppkey, X509_PUBKEY *key) return 0; } -EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key) +EVP_PKEY *X509_PUBKEY_get0(const X509_PUBKEY *key) { EVP_PKEY *ret = NULL; @@ -216,58 +225,119 @@ EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key) return NULL; } -EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) +EVP_PKEY *X509_PUBKEY_get(const X509_PUBKEY *key) { EVP_PKEY *ret = X509_PUBKEY_get0(key); - if (ret != NULL) - EVP_PKEY_up_ref(ret); + + if (ret != NULL && !EVP_PKEY_up_ref(ret)) { + X509err(X509_F_X509_PUBKEY_GET, ERR_R_INTERNAL_ERROR); + ret = NULL; + } return ret; } /* - * 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, + OSSL_LIB_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) { - X509_PUBKEY *xpk = NULL; int ret = -1; if (a == NULL) return 0; - if ((xpk = X509_PUBKEY_new()) == NULL) - return -1; - if (a->ameth != NULL && a->ameth->pub_encode != NULL - && !a->ameth->pub_encode(xpk, a)) - goto error; - xpk->pkey = (EVP_PKEY *)a; - ret = i2d_X509_PUBKEY(xpk, pp); - xpk->pkey = NULL; - error: - X509_PUBKEY_free(xpk); + if (a->ameth != NULL) { + X509_PUBKEY *xpk = NULL; + + if ((xpk = X509_PUBKEY_new()) == NULL) + return -1; + + /* pub_encode() only encode parameters, not the key itself */ + if (a->ameth->pub_encode != NULL && a->ameth->pub_encode(xpk, a)) { + xpk->pkey = (EVP_PKEY *)a; + ret = i2d_X509_PUBKEY(xpk, pp); + xpk->pkey = NULL; + } + X509_PUBKEY_free(xpk); + } else if (a->keymgmt != NULL) { + const OSSL_PROVIDER *pkprov = EVP_KEYMGMT_provider(a->keymgmt); + OSSL_LIB_CTX *libctx = ossl_provider_libctx(pkprov); + int selection = (OSSL_KEYMGMT_SELECT_PUBLIC_KEY + | OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS); + OSSL_ENCODER_CTX *ctx = + OSSL_ENCODER_CTX_new_by_EVP_PKEY(a, "DER", selection, libctx, NULL); + BIO *out = BIO_new(BIO_s_mem()); + BUF_MEM *buf = NULL; + + if (OSSL_ENCODER_CTX_get_num_encoders(ctx) != 0 + && out != NULL + && OSSL_ENCODER_to_bio(ctx, out) + && BIO_get_mem_ptr(out, &buf) > 0) { + ret = buf->length; + + if (pp != NULL) { + if (*pp == NULL) { + *pp = (unsigned char *)buf->data; + buf->length = 0; + buf->data = NULL; + } else { + memcpy(*pp, buf->data, ret); + *pp += ret; + } + } + } + BIO_free(out); + OSSL_ENCODER_CTX_free(ctx); + } + return ret; } @@ -419,7 +489,7 @@ int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *aobj, int X509_PUBKEY_get0_param(ASN1_OBJECT **ppkalg, const unsigned char **pk, int *ppklen, - X509_ALGOR **pa, X509_PUBKEY *pub) + X509_ALGOR **pa, const X509_PUBKEY *pub) { if (ppkalg) *ppkalg = pub->algor->algorithm; @@ -438,3 +508,34 @@ ASN1_BIT_STRING *X509_get0_pubkey_bitstr(const X509 *x) return NULL; return x->cert_info.key->public_key; } + +/* Returns 1 for equal, 0, for non-equal, < 0 on error */ +int X509_PUBKEY_eq(const X509_PUBKEY *a, const X509_PUBKEY *b) +{ + X509_ALGOR *algA, *algB; + EVP_PKEY *pA, *pB; + + if (a == b) + return 1; + if (a == NULL || b == NULL) + return 0; + if (!X509_PUBKEY_get0_param(NULL, NULL, NULL, &algA, a) || algA == NULL + || !X509_PUBKEY_get0_param(NULL, NULL, NULL, &algB, b) || algB == NULL) + return -2; + if (X509_ALGOR_cmp(algA, algB) != 0) + return 0; + if ((pA = X509_PUBKEY_get0(a)) == NULL + || (pB = X509_PUBKEY_get0(b)) == NULL) + return -2; + return EVP_PKEY_eq(pA, pB); +} + +int X509_PUBKEY_get0_libctx(OSSL_LIB_CTX **plibctx, const char **ppropq, + const X509_PUBKEY *key) +{ + if (plibctx) + *plibctx = key->libctx; + if (ppropq) + *ppropq = key->propq; + return 1; +}