From 4f76d62f2384d3335bd1d043706995ae64b37348 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Mon, 16 Mar 2020 18:55:32 +0100 Subject: [PATCH] EVP: add EVP_PKEY_is_a() and EVP_PKEY_can_sign() EVP_PKEY_is_a() is the provider side key checking function corresponding to checking EVP_PKEY_id() or an EVP_PKEY against macros like EVP_PKEY_EC. It also works with legacy internal keys. We also add a warning indoc/man3/EVP_PKEY_set1_RSA.pod regarding the reliability of certain functions that only understand legacy keys. Finally, we take the opportunity to clean up doc/man3/EVP_PKEY_set1_RSA.pod to better conform with man-page layout norms, see man-pages(7) on Linux. Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/11358) --- crypto/evp/p_lib.c | 77 ++++++++++++++++++++++++++++++++++ doc/man3/EVP_PKEY_is_a.pod | 72 +++++++++++++++++++++++++++++++ doc/man3/EVP_PKEY_set1_RSA.pod | 41 +++++++++++------- include/openssl/evp.h | 1 + util/libcrypto.num | 2 + 5 files changed, 177 insertions(+), 16 deletions(-) create mode 100644 doc/man3/EVP_PKEY_is_a.pod diff --git a/crypto/evp/p_lib.c b/crypto/evp/p_lib.c index b176f100e8..c3a7fbe692 100644 --- a/crypto/evp/p_lib.c +++ b/crypto/evp/p_lib.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,11 @@ #include "internal/provider.h" #include "evp_local.h" +#include "crypto/ec.h" + +/* TODO remove this when the EVP_PKEY_is_a() #legacy support hack is removed */ +#include "e_os.h" /* strcasecmp on Windows */ + 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); @@ -732,6 +738,77 @@ int EVP_PKEY_base_id(const EVP_PKEY *pkey) return EVP_PKEY_type(pkey->type); } +int EVP_PKEY_is_a(const EVP_PKEY *pkey, const char *name) +{ +#ifndef FIPS_MODE + if (pkey->keymgmt == NULL) { + /* + * These hard coded cases are pure hackery to get around the fact + * that names in crypto/objects/objects.txt are a mess. There is + * no "EC", and "RSA" leads to the NID for 2.5.8.1.1, an OID that's + * fallen out in favor of { pkcs-1 1 }, i.e. 1.2.840.113549.1.1.1, + * the NID of which is used for EVP_PKEY_RSA. Strangely enough, + * "DSA" is accurate... but still, better be safe and hard-code + * names that we know. + * TODO Clean this away along with all other #legacy support. + */ + int type; + + if (strcasecmp(name, "RSA") == 0) + type = EVP_PKEY_RSA; +#ifndef OPENSSL_NO_EC + else if (strcasecmp(name, "EC") == 0) + type = EVP_PKEY_EC; +#endif +#ifndef OPENSSL_NO_DSA + else if (strcasecmp(name, "DSA") == 0) + type = EVP_PKEY_DSA; +#endif + else + type = EVP_PKEY_type(OBJ_sn2nid(name)); + return EVP_PKEY_type(pkey->type) == type; + } +#endif + return EVP_KEYMGMT_is_a(pkey->keymgmt, name); +} + +int EVP_PKEY_can_sign(const EVP_PKEY *pkey) +{ + if (pkey->keymgmt == NULL) { + switch (EVP_PKEY_base_id(pkey)) { + case EVP_PKEY_RSA: + return 1; +#ifndef OPENSSL_NO_DSA + case EVP_PKEY_DSA: + return 1; +#endif +#ifndef OPENSSL_NO_EC + case EVP_PKEY_ED25519: + case EVP_PKEY_ED448: + return 1; + case EVP_PKEY_EC: /* Including SM2 */ + return EC_KEY_can_sign(pkey->pkey.ec); +#endif + default: + break; + } + } else { + const OSSL_PROVIDER *prov = EVP_KEYMGMT_provider(pkey->keymgmt); + OPENSSL_CTX *libctx = ossl_provider_library_context(prov); + const char *supported_sig = + pkey->keymgmt->query_operation_name != NULL + ? pkey->keymgmt->query_operation_name(OSSL_OP_SIGNATURE) + : evp_first_name(prov, pkey->keymgmt->name_id); + EVP_SIGNATURE *signature = NULL; + + signature = EVP_SIGNATURE_fetch(libctx, supported_sig, NULL); + if (signature != NULL) { + EVP_SIGNATURE_free(signature); + return 1; + } + } + return 0; +} static int print_reset_indent(BIO **out, int pop_f_prefix, long saved_indent) { diff --git a/doc/man3/EVP_PKEY_is_a.pod b/doc/man3/EVP_PKEY_is_a.pod new file mode 100644 index 0000000000..cfce3de5da --- /dev/null +++ b/doc/man3/EVP_PKEY_is_a.pod @@ -0,0 +1,72 @@ +=pod + +=head1 NAME + +EVP_PKEY_is_a, EVP_PKEY_can_sign +- key type and capabilities functions + +=head1 SYNOPSIS + + #include + + int EVP_PKEY_is_a(const EVP_PKEY *pkey, const char *name); + int EVP_PKEY_can_sign(const EVP_PKEY *pkey); + +=head1 DESCRIPTION + +EVP_PKEY_is_a() checks if the key type of I is I. + +EVP_PKEY_can_sign() checks if the functionality for the key type of +I supports signing. No other check is done, such as whether +I contains a private key. + +=head1 RETURN VALUES + +EVP_PKEY_is_a() returns 1 if I has the key type I, +otherwise 0. + +EVP_PKEY_can_sign() returns 1 if the I key type functionality +supports signing, otherwise 0. + +=head1 EXAMPLES + +=head2 EVP_PKEY_is_a() + +The loaded providers and what key types they support will ultimately +determine what I is possible to use with EVP_PKEY_is_a(). We do know +that the default provider supports RSA, DH, DSA and EC keys, so we can use +this as an crude example: + + #include + + ... + /* |pkey| is an EVP_PKEY* */ + if (EVP_PKEY_is_a(pkey, "RSA")) { + BIGNUM *modulus = NULL; + if (EVP_PKEY_get_bn_param(pkey, "n", &modulus)) + /* do whatever with the modulus */ + BN_free(modulus); + } + +=head2 EVP_PKEY_can_sign() + + #include + + ... + /* |pkey| is an EVP_PKEY* */ + if (!EVP_PKEY_can_sign(pkey)) { + fprintf(stderr, "Not a signing key!"); + exit(1); + } + /* Sign something... */ + +=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/doc/man3/EVP_PKEY_set1_RSA.pod b/doc/man3/EVP_PKEY_set1_RSA.pod index 8423a0a3b8..598cda4221 100644 --- a/doc/man3/EVP_PKEY_set1_RSA.pod +++ b/doc/man3/EVP_PKEY_set1_RSA.pod @@ -51,52 +51,61 @@ EVP_PKEY_set1_engine, EVP_PKEY_get0_engine - EVP_PKEY assignment functions =head1 DESCRIPTION EVP_PKEY_set1_RSA(), EVP_PKEY_set1_DSA(), EVP_PKEY_set1_DH() and -EVP_PKEY_set1_EC_KEY() set the key referenced by B to B. +EVP_PKEY_set1_EC_KEY() set the key referenced by I to I. EVP_PKEY_get1_RSA(), EVP_PKEY_get1_DSA(), EVP_PKEY_get1_DH() and -EVP_PKEY_get1_EC_KEY() return the referenced key in B or -B if the key is not of the correct type. +EVP_PKEY_get1_EC_KEY() return the referenced key in I or +NULL if the key is not of the correct type. EVP_PKEY_get0_hmac(), EVP_PKEY_get0_poly1305(), EVP_PKEY_get0_siphash(), EVP_PKEY_get0_RSA(), EVP_PKEY_get0_DSA(), EVP_PKEY_get0_DH() -and EVP_PKEY_get0_EC_KEY() also return the referenced key in B or B +and EVP_PKEY_get0_EC_KEY() also return the referenced key in I or NULL if the key is not of the correct type but the reference count of the returned key is B incremented and so must not be freed up after use. EVP_PKEY_assign_RSA(), EVP_PKEY_assign_DSA(), EVP_PKEY_assign_DH(), EVP_PKEY_assign_EC_KEY(), EVP_PKEY_assign_POLY1305() and -EVP_PKEY_assign_SIPHASH() also set the referenced key to B -however these use the supplied B internally and so B -will be freed when the parent B is freed. +EVP_PKEY_assign_SIPHASH() also set the referenced key to I +however these use the supplied I internally and so I +will be freed when the parent I is freed. -EVP_PKEY_base_id() returns the type of B. For example +EVP_PKEY_base_id() returns the type of I. For example an RSA key will return B. -EVP_PKEY_id() returns the actual OID associated with B. Historically keys +EVP_PKEY_id() returns the actual OID associated with I. Historically keys using the same algorithm could use different OIDs. For example an RSA key could use the OIDs corresponding to the NIDs B (equivalent to B) or B (equivalent to B). The use of alternative non-standard OIDs is now rare so B et al are not often seen in practice. -EVP_PKEY_type() returns the underlying type of the NID B. For example +EVP_PKEY_type() returns the underlying type of the NID I. For example EVP_PKEY_type(EVP_PKEY_RSA2) will return B. -EVP_PKEY_get0_engine() returns a reference to the ENGINE handling B. +EVP_PKEY_get0_engine() returns a reference to the ENGINE handling I. -EVP_PKEY_set1_engine() sets the ENGINE handling B to B. It +EVP_PKEY_set1_engine() sets the ENGINE handling I to I. It must be called after the key algorithm and components are set up. -If B does not include an B for B an +If I does not include an B for I an error occurs. EVP_PKEY_set_alias_type() allows modifying a EVP_PKEY to use a different set of algorithms than the default. +=head1 WARNINGS + +The following functions are only reliable with Bs that have +been assigned an internal key with EVP_PKEY_assign_*(): + +EVP_PKEY_id(), EVP_PKEY_base_id(), EVP_PKEY_type(), EVP_PKEY_set_alias_type() + +For EVP_PKEY key type checking purposes, L is more generic. + =head1 NOTES In accordance with the OpenSSL naming convention the key obtained -from or assigned to the B using the B<1> functions must be -freed as well as B. +from or assigned to the I using the B<1> functions must be +freed as well as I. EVP_PKEY_assign_RSA(), EVP_PKEY_assign_DSA(), EVP_PKEY_assign_DH(), EVP_PKEY_assign_EC_KEY(), EVP_PKEY_assign_POLY1305() @@ -129,7 +138,7 @@ EVP_PKEY_set1_RSA(), EVP_PKEY_set1_DSA(), EVP_PKEY_set1_DH() and EVP_PKEY_set1_EC_KEY() return 1 for success or 0 for failure. EVP_PKEY_get1_RSA(), EVP_PKEY_get1_DSA(), EVP_PKEY_get1_DH() and -EVP_PKEY_get1_EC_KEY() return the referenced key or B if +EVP_PKEY_get1_EC_KEY() return the referenced key or NULL if an error occurred. EVP_PKEY_assign_RSA(), EVP_PKEY_assign_DSA(), EVP_PKEY_assign_DH(), diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 4903fc5f42..4545bf7170 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -1104,6 +1104,7 @@ DEPRECATEDIN_3_0(int EVP_PKEY_decrypt_old(unsigned char *dec_key, DEPRECATEDIN_3_0(int EVP_PKEY_encrypt_old(unsigned char *enc_key, const unsigned char *key, int key_len, EVP_PKEY *pub_key)) +int EVP_PKEY_is_a(const EVP_PKEY *pkey, const char *name); int EVP_PKEY_type(int type); int EVP_PKEY_id(const EVP_PKEY *pkey); int EVP_PKEY_base_id(const EVP_PKEY *pkey); diff --git a/util/libcrypto.num b/util/libcrypto.num index 12761e4adc..e29234aaf9 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5037,3 +5037,5 @@ EVP_PKEY_get_size_t_param ? 3_0_0 EXIST::FUNCTION: EVP_PKEY_get_bn_param ? 3_0_0 EXIST::FUNCTION: EVP_PKEY_get_utf8_string_param ? 3_0_0 EXIST::FUNCTION: EVP_PKEY_get_octet_string_param ? 3_0_0 EXIST::FUNCTION: +EVP_PKEY_is_a ? 3_0_0 EXIST::FUNCTION: +EVP_PKEY_can_sign ? 3_0_0 EXIST::FUNCTION: -- 2.34.1