From 8243d8d1a17b700c9c48fc5660ff61245b1d14d2 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Sat, 21 Mar 2020 06:14:25 +0100 Subject: [PATCH] EVP: Add EVP_PKEY_set_type_by_keymgmt() and use it This function intialises an EVP_PKEY to contain a provider side internal key. We take the opportunity to also document the older EVP_PKEY_set_type() and EVP_PKEY_set_type_str(). Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/11375) --- CHANGES.md | 5 + crypto/evp/keymgmt_lib.c | 36 +++-- crypto/evp/p_lib.c | 240 +++++++++++++++++++++++++-------- doc/man3/EVP_PKEY_set_type.pod | 68 ++++++++++ include/openssl/evp.h | 1 + util/libcrypto.num | 1 + util/missingcrypto.txt | 2 - 7 files changed, 271 insertions(+), 82 deletions(-) create mode 100644 doc/man3/EVP_PKEY_set_type.pod diff --git a/CHANGES.md b/CHANGES.md index d2aaec9fbe..ba2569bf62 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -24,6 +24,11 @@ OpenSSL 3.0 ### Changes between 1.1.1 and 3.0 [xx XXX xxxx] ### + * Added EVP_PKEY_set_type_by_keymgmt(), to initialise an EVP_PKEY to + contain a provider side internal key. + + *Richard Levitte* + * `ASN1_verify()`, `ASN1_digest()` and `ASN1_sign()` have been deprecated. They are old functions that we don't use, and that you could disable with the macro `NO_ASN1_OLD`. This goes all the way back to OpenSSL 0.9.7. diff --git a/crypto/evp/keymgmt_lib.c b/crypto/evp/keymgmt_lib.c index 6e63c5ab2d..6c66bfa72d 100644 --- a/crypto/evp/keymgmt_lib.c +++ b/crypto/evp/keymgmt_lib.c @@ -206,17 +206,15 @@ void evp_keymgmt_util_cache_keyinfo(EVP_PKEY *pk) void *evp_keymgmt_util_fromdata(EVP_PKEY *target, EVP_KEYMGMT *keymgmt, int selection, const OSSL_PARAM params[]) { - void *keydata = evp_keymgmt_newdata(keymgmt); + void *keydata = NULL; + if ((keydata = evp_keymgmt_newdata(keymgmt)) == NULL + || !evp_keymgmt_import(keymgmt, keydata, selection, params) + || !EVP_PKEY_set_type_by_keymgmt(target, keymgmt)) { + evp_keymgmt_freedata(keymgmt, keydata); + keydata = NULL; + } if (keydata != NULL) { - if (!evp_keymgmt_import(keymgmt, keydata, selection, params) - || !EVP_KEYMGMT_up_ref(keymgmt)) { - evp_keymgmt_freedata(keymgmt, keydata); - return NULL; - } - - evp_keymgmt_util_clear_operation_cache(target); - target->keymgmt = keymgmt; target->keydata = keydata; evp_keymgmt_util_cache_keyinfo(target); } @@ -303,7 +301,7 @@ int evp_keymgmt_util_match(EVP_PKEY *pk1, EVP_PKEY *pk2, int selection) } } /* - * If we've successfully cross exported one way, there's not point + * If we've successfully cross exported one way, there's no point * doing it the other way, hence the |!ok| check. */ if (!ok @@ -387,12 +385,10 @@ int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection) } if (to->keymgmt == NULL - && !EVP_KEYMGMT_up_ref(to_keymgmt)) { + && !EVP_PKEY_set_type_by_keymgmt(to, to_keymgmt)) { evp_keymgmt_freedata(to_keymgmt, alloc_keydata); return 0; } - evp_keymgmt_util_clear_operation_cache(to); - to->keymgmt = to_keymgmt; to->keydata = to_keydata; evp_keymgmt_util_cache_keyinfo(to); @@ -402,16 +398,14 @@ int evp_keymgmt_util_copy(EVP_PKEY *to, EVP_PKEY *from, int selection) void *evp_keymgmt_util_gen(EVP_PKEY *target, EVP_KEYMGMT *keymgmt, void *genctx, OSSL_CALLBACK *cb, void *cbarg) { - void *keydata = evp_keymgmt_gen(keymgmt, genctx, cb, cbarg); + void *keydata = NULL; + if ((keydata = evp_keymgmt_gen(keymgmt, genctx, cb, cbarg)) == NULL + || !EVP_PKEY_set_type_by_keymgmt(target, keymgmt)) { + evp_keymgmt_freedata(keymgmt, keydata); + keydata = NULL; + } if (keydata != NULL) { - if (!EVP_KEYMGMT_up_ref(keymgmt)) { - evp_keymgmt_freedata(keymgmt, keydata); - return NULL; - } - - evp_keymgmt_util_clear_operation_cache(target); - target->keymgmt = keymgmt; target->keydata = keydata; evp_keymgmt_util_cache_keyinfo(target); } diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index 1603f29b1a..9c11da697e 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -35,6 +35,8 @@ #include "internal/provider.h" #include "evp_local.h" +static int pkey_set_type(EVP_PKEY *pkey, ENGINE *e, int type, const char *str, + int len, EVP_KEYMGMT *keymgmt); static void evp_pkey_free_it(EVP_PKEY *key); #ifndef FIPS_MODE @@ -294,57 +296,6 @@ int EVP_PKEY_cmp(const EVP_PKEY *a, const EVP_PKEY *b) return -2; } - -/* - * Setup a public key ASN1 method and ENGINE from a NID or a string. If pkey - * is NULL just return 1 or 0 if the algorithm exists. - */ - -static int pkey_set_type(EVP_PKEY *pkey, ENGINE *e, int type, const char *str, - int len) -{ - const EVP_PKEY_ASN1_METHOD *ameth; - ENGINE **eptr = (e == NULL) ? &e : NULL; - - if (pkey) { - if (pkey->pkey.ptr) - evp_pkey_free_it(pkey); - /* - * If key type matches and a method exists then this lookup has - * succeeded once so just indicate success. - */ - if ((type == pkey->save_type) && pkey->ameth) - return 1; -# ifndef OPENSSL_NO_ENGINE - /* If we have ENGINEs release them */ - ENGINE_finish(pkey->engine); - pkey->engine = NULL; - ENGINE_finish(pkey->pmeth_engine); - pkey->pmeth_engine = NULL; -# endif - } - if (str) - ameth = EVP_PKEY_asn1_find_str(eptr, str, len); - else - ameth = EVP_PKEY_asn1_find(eptr, type); -# ifndef OPENSSL_NO_ENGINE - if (pkey == NULL && eptr != NULL) - ENGINE_finish(e); -# endif - if (ameth == NULL) { - EVPerr(EVP_F_PKEY_SET_TYPE, EVP_R_UNSUPPORTED_ALGORITHM); - return 0; - } - if (pkey) { - pkey->ameth = ameth; - pkey->engine = e; - - pkey->type = pkey->ameth->pkey_id; - pkey->save_type = type; - } - return 1; -} - EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *e, const unsigned char *priv, size_t len) @@ -352,7 +303,7 @@ EVP_PKEY *EVP_PKEY_new_raw_private_key(int type, ENGINE *e, EVP_PKEY *ret = EVP_PKEY_new(); if (ret == NULL - || !pkey_set_type(ret, e, type, NULL, -1)) { + || !pkey_set_type(ret, e, type, NULL, -1, NULL)) { /* EVPerr already called */ goto err; } @@ -382,7 +333,7 @@ EVP_PKEY *EVP_PKEY_new_raw_public_key(int type, ENGINE *e, EVP_PKEY *ret = EVP_PKEY_new(); if (ret == NULL - || !pkey_set_type(ret, e, type, NULL, -1)) { + || !pkey_set_type(ret, e, type, NULL, -1, NULL)) { /* EVPerr already called */ goto err; } @@ -457,8 +408,8 @@ EVP_PKEY *EVP_PKEY_new_CMAC_key(ENGINE *e, const unsigned char *priv, size_t paramsn = 0; if (ret == NULL - || cmctx == NULL - || !pkey_set_type(ret, e, EVP_PKEY_CMAC, NULL, -1)) { + || cmctx == NULL + || !pkey_set_type(ret, e, EVP_PKEY_CMAC, NULL, -1, NULL)) { /* EVPerr already called */ goto err; } @@ -499,12 +450,12 @@ EVP_PKEY *EVP_PKEY_new_CMAC_key(ENGINE *e, const unsigned char *priv, int EVP_PKEY_set_type(EVP_PKEY *pkey, int type) { - return pkey_set_type(pkey, NULL, type, NULL, -1); + return pkey_set_type(pkey, NULL, type, NULL, -1, NULL); } int EVP_PKEY_set_type_str(EVP_PKEY *pkey, const char *str, int len) { - return pkey_set_type(pkey, NULL, EVP_PKEY_NONE, str, len); + return pkey_set_type(pkey, NULL, EVP_PKEY_NONE, str, len, NULL); } int EVP_PKEY_set_alias_type(EVP_PKEY *pkey, int type) @@ -999,6 +950,178 @@ EVP_PKEY *EVP_PKEY_new(void) return ret; } +/* + * Setup a public key management method. + * + * For legacy keys, either |type| or |str| is expected to have the type + * information. In this case, the setup consists of finding an ASN1 method + * and potentially an ENGINE, and setting those fields in |pkey|. + * + * For provider side keys, |keymgmt| is expected to be non-NULL. In this + * case, the setup consists of setting the |keymgmt| field in |pkey|. + * + * If pkey is NULL just return 1 or 0 if the key management method exists. + */ + +static int pkey_set_type(EVP_PKEY *pkey, ENGINE *e, int type, const char *str, + int len, EVP_KEYMGMT *keymgmt) +{ +#ifndef FIPS_MODE + const EVP_PKEY_ASN1_METHOD *ameth = NULL; + ENGINE **eptr = (e == NULL) ? &e : NULL; +#endif + + /* + * The setups can't set both legacy and provider side methods. + * It is forbidden + */ + if (!ossl_assert(type == EVP_PKEY_NONE || keymgmt == NULL) + || !ossl_assert(e == NULL || keymgmt == NULL)) { + ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); + return 0; + } + + if (pkey != NULL) { + int free_it = 0; + +#ifndef FIPS_MODE + free_it = free_it || pkey->pkey.ptr != NULL; +#endif + free_it = free_it || pkey->keydata != NULL; + if (free_it) + evp_pkey_free_it(pkey); +#ifndef FIPS_MODE + /* + * If key type matches and a method exists then this lookup has + * succeeded once so just indicate success. + */ + if (pkey->type != EVP_PKEY_NONE + && type == pkey->save_type + && pkey->ameth != NULL) + return 1; +# ifndef OPENSSL_NO_ENGINE + /* If we have ENGINEs release them */ + ENGINE_finish(pkey->engine); + pkey->engine = NULL; + ENGINE_finish(pkey->pmeth_engine); + pkey->pmeth_engine = NULL; +# endif +#endif + } +#ifndef FIPS_MODE + if (str != NULL) + ameth = EVP_PKEY_asn1_find_str(eptr, str, len); + else if (type != EVP_PKEY_NONE) + ameth = EVP_PKEY_asn1_find(eptr, type); +# ifndef OPENSSL_NO_ENGINE + if (pkey == NULL && eptr != NULL) + ENGINE_finish(e); +# endif +#endif + + + { + int check = 1; + +#ifndef FIPS_MODE + check = check && ameth == NULL; +#endif + check = check && keymgmt == NULL; + if (check) { + EVPerr(EVP_F_PKEY_SET_TYPE, EVP_R_UNSUPPORTED_ALGORITHM); + return 0; + } + } + if (pkey != NULL) { + if (keymgmt != NULL && !EVP_KEYMGMT_up_ref(keymgmt)) { + ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); + return 0; + } + + pkey->keymgmt = keymgmt; + + pkey->save_type = type; + pkey->type = type; + +#ifndef FIPS_MODE + /* + * If the internal "origin" key is provider side, don't save |ameth|. + * The main reason is that |ameth| is one factor to detect that the + * internal "origin" key is a legacy one. + */ + if (keymgmt == NULL) + pkey->ameth = ameth; + pkey->engine = e; + + /* + * The EVP_PKEY_ASN1_METHOD |pkey_id| serves different purposes, + * depending on if we're setting this key to contain a legacy or + * a provider side "origin" key. For a legacy key, we assign it + * to the |type| field, but for a provider side key, we assign it + * to the |save_type| field, because |type| is supposed to be set + * to EVP_PKEY_NONE in that case. + */ + if (keymgmt != NULL) + pkey->save_type = ameth->pkey_id; + else if (pkey->ameth != NULL) + pkey->type = ameth->pkey_id; +#endif + } + return 1; +} + +#ifndef FIPS_MODE +static void find_ameth(const char *name, void *data) +{ + const char **str = data; + + /* + * The error messages from pkey_set_type() are uninteresting here, + * and misleading. + */ + ERR_set_mark(); + + if (pkey_set_type(NULL, NULL, EVP_PKEY_NONE, name, strlen(name), + NULL)) { + if (str[0] == NULL) + str[0] = name; + else if (str[1] == NULL) + str[1] = name; + } + + ERR_pop_to_mark(); +} +#endif + +int EVP_PKEY_set_type_by_keymgmt(EVP_PKEY *pkey, EVP_KEYMGMT *keymgmt) +{ +#ifndef FIPS_MODE +# define EVP_PKEY_TYPE_STR str[0] +# define EVP_PKEY_TYPE_STRLEN (str[0] == NULL ? -1 : (int)strlen(str[0])) + /* + * Find at most two strings that have an associated EVP_PKEY_ASN1_METHOD + * Ideally, only one should be found. If two (or more) are found, the + * match is ambiguous. This should never happen, but... + */ + const char *str[2] = { NULL, NULL }; + + EVP_KEYMGMT_names_do_all(keymgmt, find_ameth, &str); + if (str[1] != NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR); + return 0; + } +#else +# define EVP_PKEY_TYPE_STR NULL +# define EVP_PKEY_TYPE_STRLEN -1 +#endif + return pkey_set_type(pkey, NULL, EVP_PKEY_NONE, + EVP_PKEY_TYPE_STR, EVP_PKEY_TYPE_STRLEN, + keymgmt); + +#undef EVP_PKEY_TYPE_STR +#undef EVP_PKEY_TYPE_STRLEN +} + int EVP_PKEY_up_ref(EVP_PKEY *pkey) { int i; @@ -1018,7 +1141,6 @@ void evp_pkey_free_legacy(EVP_PKEY *x) if (x->ameth->pkey_free != NULL) x->ameth->pkey_free(x); x->pkey.ptr = NULL; - x->ameth = NULL; } # ifndef OPENSSL_NO_ENGINE ENGINE_finish(x->engine); @@ -1026,7 +1148,7 @@ void evp_pkey_free_legacy(EVP_PKEY *x) ENGINE_finish(x->pmeth_engine); x->pmeth_engine = NULL; # endif - x->type = x->save_type = EVP_PKEY_NONE; + x->type = EVP_PKEY_NONE; } #endif /* FIPS_MODE */ diff --git a/doc/man3/EVP_PKEY_set_type.pod b/doc/man3/EVP_PKEY_set_type.pod new file mode 100644 index 0000000000..e5111a555b --- /dev/null +++ b/doc/man3/EVP_PKEY_set_type.pod @@ -0,0 +1,68 @@ +=pod + +=head1 NAME + +EVP_PKEY_set_type, EVP_PKEY_set_type_str, EVP_PKEY_set_type_by_keymgmt +- functions to change the EVP_PKEY type + +=head1 SYNOPSIS + + #include + + int EVP_PKEY_set_type(EVP_PKEY *pkey, int type); + int EVP_PKEY_set_type_str(EVP_PKEY *pkey, const char *str, int len); + int EVP_PKEY_set_type_by_keymgmt(EVP_PKEY *pkey, EVP_KEYMGMT *keymgmt); + +=head1 DESCRIPTION + +All the functions described here behave the same in so far that they +clear all the previous key data and methods from I, and reset it +to be of the type of key given by the different arguments. If +I is NULL, these functions will still return the same return +values as if it wasn't. + +EVP_PKEY_set_type() initialises I to contain an internal legacy +key. When doing this, it finds a L +corresponding to I, and associates I with the findings. +It is an error if no L could be found for +I. + +EVP_PKEY_set_type_str() initialises I to contain an internal legacy +key. When doing this, it finds a L +corresponding to I that has then length I, and associates +I with the findings. +It is an error if no L could be found for +I. + +For both EVP_PKEY_set_type() and EVP_PKEY_set_type_str(), I gets +a numeric type, which can be retrieved with L. This +numeric type is taken from the L that was +found, and is equal to or closely related to I in the case of +EVP_PKEY_set_type(), or related to I in the case of +EVP_PKEY_set_type_str(). + +EVP_PKEY_set_type_by_keymgmt() initialises I to contain an +internal provider side key. When doing this, it associates I +with I. For keys initialised like this, the numeric type +retrieved with L will always be B. + +=head1 RETURN VALUES + +All functions described here return 1 if successful, or 0 on error. + +=head1 SEE ALSO + +L, L, L, +L, L, +L + +=head1 COPYRIGHT + +Copyright 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 +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 3487b27e0a..d461f24999 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -1112,6 +1112,7 @@ int EVP_PKEY_security_bits(const EVP_PKEY *pkey); int EVP_PKEY_size(const EVP_PKEY *pkey); int EVP_PKEY_set_type(EVP_PKEY *pkey, int type); int EVP_PKEY_set_type_str(EVP_PKEY *pkey, const char *str, int len); +int EVP_PKEY_set_type_by_keymgmt(EVP_PKEY *pkey, EVP_KEYMGMT *keymgmt); int EVP_PKEY_set_alias_type(EVP_PKEY *pkey, int type); # ifndef OPENSSL_NO_ENGINE int EVP_PKEY_set1_engine(EVP_PKEY *pkey, ENGINE *e); diff --git a/util/libcrypto.num b/util/libcrypto.num index 4d57cad19e..ecc735cb94 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5000,3 +5000,4 @@ EVP_PKEY_CTX_set_rsa_keygen_primes ? 3_0_0 EXIST::FUNCTION:RSA NCONF_new_with_libctx ? 3_0_0 EXIST::FUNCTION: CONF_modules_load_file_with_libctx ? 3_0_0 EXIST::FUNCTION: OPENSSL_CTX_load_config ? 3_0_0 EXIST::FUNCTION: +EVP_PKEY_set_type_by_keymgmt ? 3_0_0 EXIST::FUNCTION: diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt index cf56fae0a6..229b33b4da 100644 --- a/util/missingcrypto.txt +++ b/util/missingcrypto.txt @@ -699,8 +699,6 @@ EVP_PKEY_get_attr_by_OBJ(3) EVP_PKEY_get_attr_count(3) EVP_PKEY_save_parameters(3) EVP_PKEY_set1_tls_encodedpoint(3) -EVP_PKEY_set_type(3) -EVP_PKEY_set_type_str(3) EVP_add_alg_module(3) EVP_add_cipher(3) EVP_add_digest(3) -- 2.25.1