X-Git-Url: https://git.openssl.org/?p=openssl.git;a=blobdiff_plain;f=crypto%2Fx509%2Fx_pubkey.c;h=d3e6af25527638d2f69bcffa5cb510cd7a609264;hp=158d1d26aff3c01f5989ffb88713b08778862827;hb=e9e7b5df865c0bcd0a99d8146ec05378892a36e1;hpb=91829e456c998eb9c2e565307b8f1022481049ce diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c index 158d1d26af..d3e6af2552 100644 --- a/crypto/x509/x_pubkey.c +++ b/crypto/x509/x_pubkey.c @@ -1,83 +1,58 @@ -/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. - * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. +/* + * Copyright 1995-2020 The OpenSSL Project Authors. All Rights Reserved. * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] + * 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 + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html */ +/* + * DSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + #include #include "internal/cryptlib.h" #include #include -#include "internal/asn1_int.h" -#include "internal/evp_int.h" +#include "crypto/asn1.h" +#include "crypto/evp.h" +#include "crypto/x509.h" #include #include +#include + +struct X509_pubkey_st { + X509_ALGOR *algor; + ASN1_BIT_STRING *public_key; + EVP_PKEY *pkey; +}; + +static int x509_pubkey_decode(EVP_PKEY **pk, 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) { - if (operation == ASN1_OP_NEW_POST) { - X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; - pubkey->lock = CRYPTO_THREAD_lock_new(); - if (pubkey->lock == NULL) - return 0; - } if (operation == ASN1_OP_FREE_POST) { X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval; - CRYPTO_THREAD_lock_free(pubkey->lock); 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; + /* + * Opportunistically decode the key but remove any non fatal errors + * from the queue. Subsequent explicit attempts to decode/use the key + * will return an appropriate error. + */ + ERR_set_mark(); + if (x509_pubkey_decode(&pubkey->pkey, pubkey) == -1) + return 0; + ERR_pop_to_mark(); } return 1; } @@ -88,19 +63,25 @@ ASN1_SEQUENCE_cb(X509_PUBKEY, pubkey_cb) = { } ASN1_SEQUENCE_END_cb(X509_PUBKEY, X509_PUBKEY) IMPLEMENT_ASN1_FUNCTIONS(X509_PUBKEY) +IMPLEMENT_ASN1_DUP_FUNCTION(X509_PUBKEY) +/* TODO should better be called X509_PUBKEY_set1 */ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) { X509_PUBKEY *pk = NULL; if (x == NULL) - return (0); + return 0; - if ((pk = X509_PUBKEY_new()) == NULL) - goto error; + if (pkey == NULL) + goto unsupported; - if (pkey->ameth) { - if (pkey->ameth->pub_encode) { + if (pkey->ameth != NULL) { + if ((pk = X509_PUBKEY_new()) == NULL) { + X509err(X509_F_X509_PUBKEY_SET, ERR_R_MALLOC_FAILURE); + goto error; + } + if (pkey->ameth->pub_encode != NULL) { if (!pkey->ameth->pub_encode(pk, pkey)) { X509err(X509_F_X509_PUBKEY_SET, X509_R_PUBLIC_KEY_ENCODE_ERROR); @@ -110,76 +91,139 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) X509err(X509_F_X509_PUBKEY_SET, X509_R_METHOD_NOT_SUPPORTED); goto error; } - } else { - X509err(X509_F_X509_PUBKEY_SET, X509_R_UNSUPPORTED_ALGORITHM); - goto error; + } else if (pkey->keymgmt != NULL) { + BIO *bmem = BIO_new(BIO_s_mem()); + const char *serprop = OSSL_SERIALIZER_PUBKEY_TO_DER_PQ; + 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); + } + + OSSL_SERIALIZER_CTX_free(sctx); + BIO_free(bmem); } + if (pk == NULL) + goto unsupported; + X509_PUBKEY_free(*x); + if (!EVP_PKEY_up_ref(pkey)) { + X509err(X509_F_X509_PUBKEY_SET, ERR_R_INTERNAL_ERROR); + goto error; + } *x = pk; + + /* + * 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 + * 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 + * application might very well depend on the passed |pkey| being used + * and none other, so we spend a few more cycles throwing away the newly + * created |pk->pkey| and replace it with |pkey|. + * TODO(3.0) Investigate if it's safe to change to simply return here + * if |pk->pkey != NULL|. + */ + if (pk->pkey != NULL) + EVP_PKEY_free(pk->pkey); + + pk->pkey = pkey; return 1; + unsupported: + X509err(X509_F_X509_PUBKEY_SET, X509_R_UNSUPPORTED_ALGORITHM); + error: X509_PUBKEY_free(pk); return 0; } -EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key) -{ - EVP_PKEY *ret = NULL; - - if (key == NULL) - goto error; +/* + * Attempt to decode a public key. + * Returns 1 on success, 0 for a decode failure and -1 for a fatal + * error e.g. malloc failure. + */ - if (key->pkey != NULL) - return key->pkey; - if (key->public_key == NULL) - goto error; +static int x509_pubkey_decode(EVP_PKEY **ppkey, X509_PUBKEY *key) +{ + EVP_PKEY *pkey = EVP_PKEY_new(); - if ((ret = EVP_PKEY_new()) == NULL) { - X509err(X509_F_X509_PUBKEY_GET0, ERR_R_MALLOC_FAILURE); - goto error; + if (pkey == NULL) { + X509err(X509_F_X509_PUBKEY_DECODE, ERR_R_MALLOC_FAILURE); + return -1; } - if (!EVP_PKEY_set_type(ret, OBJ_obj2nid(key->algor->algorithm))) { - X509err(X509_F_X509_PUBKEY_GET0, X509_R_UNSUPPORTED_ALGORITHM); + if (!EVP_PKEY_set_type(pkey, OBJ_obj2nid(key->algor->algorithm))) { + X509err(X509_F_X509_PUBKEY_DECODE, X509_R_UNSUPPORTED_ALGORITHM); goto error; } - if (ret->ameth->pub_decode) { - if (!ret->ameth->pub_decode(ret, key)) { - X509err(X509_F_X509_PUBKEY_GET0, X509_R_PUBLIC_KEY_DECODE_ERROR); + if (pkey->ameth->pub_decode) { + /* + * Treat any failure of pub_decode as a decode error. In + * 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); goto error; } } else { - X509err(X509_F_X509_PUBKEY_GET0, X509_R_METHOD_NOT_SUPPORTED); + X509err(X509_F_X509_PUBKEY_DECODE, X509_R_METHOD_NOT_SUPPORTED); goto error; } - /* Check to see if another thread set key->pkey first */ - CRYPTO_THREAD_write_lock(key->lock); - if (key->pkey) { - CRYPTO_THREAD_unlock(key->lock); + *ppkey = pkey; + return 1; + + error: + EVP_PKEY_free(pkey); + return 0; +} + +EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key) +{ + EVP_PKEY *ret = NULL; + + if (key == NULL || key->public_key == NULL) + return NULL; + + if (key->pkey != NULL) + return key->pkey; + + /* + * When the key ASN.1 is initially parsed an attempt is made to + * decode the public key and cache the EVP_PKEY structure. If this + * operation fails the cached value will be NULL. Parsing continues + * to allow parsing of unknown key types or unsupported forms. + * We repeat the decode operation so the appropriate errors are left + * in the queue. + */ + x509_pubkey_decode(&ret, key); + /* If decode doesn't fail something bad happened */ + if (ret != NULL) { + X509err(X509_F_X509_PUBKEY_GET0, ERR_R_INTERNAL_ERROR); EVP_PKEY_free(ret); - ret = key->pkey; - } else { - key->pkey = ret; - CRYPTO_THREAD_unlock(key->lock); } - return ret; - - error: - EVP_PKEY_free(ret); - return (NULL); + return NULL; } EVP_PKEY *X509_PUBKEY_get(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; } @@ -193,32 +237,71 @@ EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length) X509_PUBKEY *xpk; EVP_PKEY *pktmp; const unsigned char *q; + q = *pp; xpk = d2i_X509_PUBKEY(NULL, &q, length); - if (!xpk) + if (xpk == NULL) return NULL; pktmp = X509_PUBKEY_get(xpk); X509_PUBKEY_free(xpk); - if (!pktmp) + if (pktmp == NULL) return NULL; *pp = q; - if (a) { + if (a != NULL) { EVP_PKEY_free(*a); *a = pktmp; } return pktmp; } -int i2d_PUBKEY(EVP_PKEY *a, unsigned char **pp) +int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp) { - X509_PUBKEY *xpk = NULL; - int ret; - if (!a) - return 0; - if (!X509_PUBKEY_set(&xpk, a)) + int ret = -1; + + if (a == NULL) return 0; - ret = i2d_X509_PUBKEY(xpk, pp); - 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 char *serprop = OSSL_SERIALIZER_PUBKEY_TO_DER_PQ; + OSSL_SERIALIZER_CTX *ctx = + OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(a, serprop); + BIO *out = BIO_new(BIO_s_mem()); + BUF_MEM *buf = NULL; + + if (ctx != NULL + && out != NULL + && OSSL_SERIALIZER_CTX_get_serializer(ctx) != NULL + && OSSL_SERIALIZER_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_SERIALIZER_CTX_free(ctx); + } + return ret; } @@ -231,23 +314,24 @@ RSA *d2i_RSA_PUBKEY(RSA **a, const unsigned char **pp, long length) EVP_PKEY *pkey; RSA *key; const unsigned char *q; + q = *pp; pkey = d2i_PUBKEY(NULL, &q, length); - if (!pkey) + if (pkey == NULL) return NULL; key = EVP_PKEY_get1_RSA(pkey); EVP_PKEY_free(pkey); - if (!key) + if (key == NULL) return NULL; *pp = q; - if (a) { + if (a != NULL) { RSA_free(*a); *a = key; } return key; } -int i2d_RSA_PUBKEY(RSA *a, unsigned char **pp) +int i2d_RSA_PUBKEY(const RSA *a, unsigned char **pp) { EVP_PKEY *pktmp; int ret; @@ -256,10 +340,11 @@ int i2d_RSA_PUBKEY(RSA *a, unsigned char **pp) pktmp = EVP_PKEY_new(); if (pktmp == NULL) { ASN1err(ASN1_F_I2D_RSA_PUBKEY, ERR_R_MALLOC_FAILURE); - return 0; + return -1; } - EVP_PKEY_set1_RSA(pktmp, a); + (void)EVP_PKEY_assign_RSA(pktmp, (RSA *)a); ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; EVP_PKEY_free(pktmp); return ret; } @@ -271,23 +356,24 @@ DSA *d2i_DSA_PUBKEY(DSA **a, const unsigned char **pp, long length) EVP_PKEY *pkey; DSA *key; const unsigned char *q; + q = *pp; pkey = d2i_PUBKEY(NULL, &q, length); - if (!pkey) + if (pkey == NULL) return NULL; key = EVP_PKEY_get1_DSA(pkey); EVP_PKEY_free(pkey); - if (!key) + if (key == NULL) return NULL; *pp = q; - if (a) { + if (a != NULL) { DSA_free(*a); *a = key; } return key; } -int i2d_DSA_PUBKEY(DSA *a, unsigned char **pp) +int i2d_DSA_PUBKEY(const DSA *a, unsigned char **pp) { EVP_PKEY *pktmp; int ret; @@ -296,10 +382,11 @@ int i2d_DSA_PUBKEY(DSA *a, unsigned char **pp) pktmp = EVP_PKEY_new(); if (pktmp == NULL) { ASN1err(ASN1_F_I2D_DSA_PUBKEY, ERR_R_MALLOC_FAILURE); - return 0; + return -1; } - EVP_PKEY_set1_DSA(pktmp, a); + (void)EVP_PKEY_assign_DSA(pktmp, (DSA *)a); ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; EVP_PKEY_free(pktmp); return ret; } @@ -311,36 +398,39 @@ EC_KEY *d2i_EC_PUBKEY(EC_KEY **a, const unsigned char **pp, long length) EVP_PKEY *pkey; EC_KEY *key; const unsigned char *q; + q = *pp; pkey = d2i_PUBKEY(NULL, &q, length); - if (!pkey) - return (NULL); + if (pkey == NULL) + return NULL; key = EVP_PKEY_get1_EC_KEY(pkey); EVP_PKEY_free(pkey); - if (!key) - return (NULL); + if (key == NULL) + return NULL; *pp = q; - if (a) { + if (a != NULL) { EC_KEY_free(*a); *a = key; } - return (key); + return key; } -int i2d_EC_PUBKEY(EC_KEY *a, unsigned char **pp) +int i2d_EC_PUBKEY(const EC_KEY *a, unsigned char **pp) { EVP_PKEY *pktmp; int ret; - if (!a) - return (0); + + if (a == NULL) + return 0; if ((pktmp = EVP_PKEY_new()) == NULL) { ASN1err(ASN1_F_I2D_EC_PUBKEY, ERR_R_MALLOC_FAILURE); - return (0); + return -1; } - EVP_PKEY_set1_EC_KEY(pktmp, a); + (void)EVP_PKEY_assign_EC_KEY(pktmp, (EC_KEY *)a); ret = i2d_PUBKEY(pktmp, pp); + pktmp->pkey.ptr = NULL; EVP_PKEY_free(pktmp); - return (ret); + return ret; } #endif @@ -375,3 +465,10 @@ int X509_PUBKEY_get0_param(ASN1_OBJECT **ppkalg, *pa = pub->algor; return 1; } + +ASN1_BIT_STRING *X509_get0_pubkey_bitstr(const X509 *x) +{ + if (x == NULL) + return NULL; + return x->cert_info.key->public_key; +}