From a94a3e0d91378b5c478f687a0dbc51914d4ed497 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Fri, 5 Jul 2019 00:31:42 +0200 Subject: [PATCH] Add basic EVP_KEYMGMT API and libcrypto <-> provider interface The idea with the key management "operation" is to support the following set of functionality: - Key domain parameter generation - Key domain parameter import - Key domain parameter export - Key generation - Key import - Key export - Key loading (HSM / hidden key support) With that set of function, we can support handling domain parameters on one provider, key handling on another, and key usage on a third, with transparent export / import of applicable data. Of course, if a provider doesn't offer export / import functionality, then all operations surrounding a key must be performed with the same provider. This method also avoids having to do anything special with legacy assignment of libcrypto key structures, i.e. EVP_PKEY_assign_RSA(). They will simply be used as keys to be exported from whenever they are used with provider based operations. This change only adds the EVP_KEYMGMT API and the libcrypto <-> provider interface. Further changes will integrate them into existing libcrypto functionality. Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/9312) --- crypto/evp/build.info | 3 +- crypto/evp/evp_locl.h | 27 ++++- crypto/evp/keymgmt_meth.c | 189 ++++++++++++++++++++++++++++++ crypto/include/internal/evp_int.h | 22 +++- doc/man3/EVP_KEYMGMT.pod | 84 +++++++++++++ include/openssl/core_numbers.h | 73 +++++++++++- include/openssl/evp.h | 6 + include/openssl/ossl_typ.h | 2 + util/libcrypto.num | 4 + util/private.num | 1 + 10 files changed, 405 insertions(+), 6 deletions(-) create mode 100644 crypto/evp/keymgmt_meth.c create mode 100644 doc/man3/EVP_KEYMGMT.pod diff --git a/crypto/evp/build.info b/crypto/evp/build.info index 5030f3f68d..d889897b18 100644 --- a/crypto/evp/build.info +++ b/crypto/evp/build.info @@ -1,5 +1,6 @@ LIBS=../../libcrypto -$COMMON=digest.c evp_enc.c evp_lib.c evp_fetch.c cmeth_lib.c evp_utils.c +$COMMON=digest.c evp_enc.c evp_lib.c evp_fetch.c cmeth_lib.c keymgmt_meth.c \ + evp_utils.c SOURCE[../../libcrypto]=$COMMON\ encode.c evp_key.c evp_cnf.c \ e_des.c e_bf.c e_idea.c e_des3.c e_camellia.c\ diff --git a/crypto/evp/evp_locl.h b/crypto/evp/evp_locl.h index 8aeb5d4003..740c159f05 100644 --- a/crypto/evp/evp_locl.h +++ b/crypto/evp/evp_locl.h @@ -62,6 +62,32 @@ struct evp_kdf_ctx_st { EVP_KDF_IMPL *impl; /* Algorithm-specific data */ } /* EVP_KDF_CTX */ ; +struct evp_keymgmt_st { + int id; /* libcrypto internal */ + + const char *name; + OSSL_PROVIDER *prov; + CRYPTO_REF_COUNT refcnt; + CRYPTO_RWLOCK *lock; + + /* Domain parameter routines */ + OSSL_OP_keymgmt_importdomparams_fn *importdomparams; + OSSL_OP_keymgmt_gendomparams_fn *gendomparams; + OSSL_OP_keymgmt_freedomparams_fn *freedomparams; + OSSL_OP_keymgmt_exportdomparams_fn *exportdomparams; + OSSL_OP_keymgmt_importdomparam_types_fn *importdomparam_types; + OSSL_OP_keymgmt_exportdomparam_types_fn *exportdomparam_types; + + /* Key routines */ + OSSL_OP_keymgmt_importkey_fn *importkey; + OSSL_OP_keymgmt_genkey_fn *genkey; + OSSL_OP_keymgmt_loadkey_fn *loadkey; + OSSL_OP_keymgmt_freekey_fn *freekey; + OSSL_OP_keymgmt_exportkey_fn *exportkey; + OSSL_OP_keymgmt_importkey_types_fn *importkey_types; + OSSL_OP_keymgmt_exportkey_types_fn *exportkey_types; +} /* EVP_KEYMGMT */ ; + struct evp_keyexch_st { OSSL_PROVIDER *prov; CRYPTO_REF_COUNT refcnt; @@ -76,7 +102,6 @@ struct evp_keyexch_st { OSSL_OP_keyexch_set_params_fn *set_params; } /* EVP_KEYEXCH */; - int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen, ASN1_TYPE *param, const EVP_CIPHER *c, const EVP_MD *md, diff --git a/crypto/evp/keymgmt_meth.c b/crypto/evp/keymgmt_meth.c new file mode 100644 index 0000000000..9723820203 --- /dev/null +++ b/crypto/evp/keymgmt_meth.c @@ -0,0 +1,189 @@ +/* + * Copyright 2019 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 + * https://www.openssl.org/source/license.html + */ + +#include +#include +#include +#include +#include "internal/provider.h" +#include "internal/refcount.h" +#include "internal/evp_int.h" +#include "evp_locl.h" + + +static void *keymgmt_new(void) +{ + EVP_KEYMGMT *keymgmt = NULL; + + if ((keymgmt = OPENSSL_zalloc(sizeof(*keymgmt))) == NULL + || (keymgmt->lock = CRYPTO_THREAD_lock_new()) == NULL) { + EVP_KEYMGMT_free(keymgmt); + return NULL; + } + + keymgmt->refcnt = 1; + + return keymgmt; +} + +static void *keymgmt_from_dispatch(const OSSL_DISPATCH *fns, + OSSL_PROVIDER *prov) +{ + EVP_KEYMGMT *keymgmt = NULL; + + if ((keymgmt = keymgmt_new()) == NULL) + return NULL; + + for (; fns->function_id != 0; fns++) { + switch (fns->function_id) { + case OSSL_FUNC_KEYMGMT_IMPORTDOMPARAMS: + if (keymgmt->importdomparams != NULL) + break; + keymgmt->importdomparams = + OSSL_get_OP_keymgmt_importdomparams(fns); + break; + case OSSL_FUNC_KEYMGMT_GENDOMPARAMS: + if (keymgmt->gendomparams != NULL) + break; + keymgmt->gendomparams = OSSL_get_OP_keymgmt_gendomparams(fns); + break; + case OSSL_FUNC_KEYMGMT_FREEDOMPARAMS: + if (keymgmt->freedomparams != NULL) + break; + keymgmt->freedomparams = OSSL_get_OP_keymgmt_freedomparams(fns); + break; + case OSSL_FUNC_KEYMGMT_EXPORTDOMPARAMS: + if (keymgmt->exportdomparams != NULL) + break; + keymgmt->exportdomparams = + OSSL_get_OP_keymgmt_exportdomparams(fns); + break; + case OSSL_FUNC_KEYMGMT_IMPORTDOMPARAM_TYPES: + if (keymgmt->importdomparam_types != NULL) + break; + keymgmt->importdomparam_types = + OSSL_get_OP_keymgmt_importdomparam_types(fns); + break; + case OSSL_FUNC_KEYMGMT_EXPORTDOMPARAM_TYPES: + if (keymgmt->exportdomparam_types != NULL) + break; + keymgmt->exportdomparam_types = + OSSL_get_OP_keymgmt_exportdomparam_types(fns); + break; + case OSSL_FUNC_KEYMGMT_IMPORTKEY: + if (keymgmt->importkey != NULL) + break; + keymgmt->importkey = OSSL_get_OP_keymgmt_importkey(fns); + break; + case OSSL_FUNC_KEYMGMT_GENKEY: + if (keymgmt->genkey != NULL) + break; + keymgmt->genkey = OSSL_get_OP_keymgmt_genkey(fns); + break; + case OSSL_FUNC_KEYMGMT_LOADKEY: + if (keymgmt->loadkey != NULL) + break; + keymgmt->loadkey = OSSL_get_OP_keymgmt_loadkey(fns); + break; + case OSSL_FUNC_KEYMGMT_FREEKEY: + if (keymgmt->freekey != NULL) + break; + keymgmt->freekey = OSSL_get_OP_keymgmt_freekey(fns); + break; + case OSSL_FUNC_KEYMGMT_EXPORTKEY: + if (keymgmt->exportkey != NULL) + break; + keymgmt->exportkey = OSSL_get_OP_keymgmt_exportkey(fns); + break; + case OSSL_FUNC_KEYMGMT_IMPORTKEY_TYPES: + if (keymgmt->importkey_types != NULL) + break; + keymgmt->importkey_types = + OSSL_get_OP_keymgmt_importkey_types(fns); + break; + case OSSL_FUNC_KEYMGMT_EXPORTKEY_TYPES: + if (keymgmt->exportkey_types != NULL) + break; + keymgmt->exportkey_types = + OSSL_get_OP_keymgmt_exportkey_types(fns); + break; + } + } + /* + * Try to check that the method is sensible. + * It makes no sense being able to free stuff if you can't create it. + * It makes no sense providing OSSL_PARAM descriptors for import and + * export if you can't import or export. + */ + if ((keymgmt->freedomparams != NULL + && (keymgmt->importdomparams == NULL + && keymgmt->gendomparams == NULL)) + || (keymgmt->freekey != NULL + && (keymgmt->importkey == NULL + && keymgmt->genkey == NULL + && keymgmt->loadkey == NULL)) + || (keymgmt->importdomparam_types != NULL + && keymgmt->importdomparams == NULL) + || (keymgmt->exportdomparam_types != NULL + && keymgmt->exportdomparams == NULL) + || (keymgmt->importkey_types != NULL + && keymgmt->importkey == NULL) + || (keymgmt->exportkey_types != NULL + && keymgmt->exportkey == NULL)) { + EVP_KEYMGMT_free(keymgmt); + EVPerr(0, EVP_R_INVALID_PROVIDER_FUNCTIONS); + return NULL; + } + keymgmt->prov = prov; + if (prov != NULL) + ossl_provider_up_ref(prov); + + return keymgmt; +} + +EVP_KEYMGMT *EVP_KEYMGMT_fetch(OPENSSL_CTX *ctx, const char *algorithm, + const char *properties) +{ + EVP_KEYMGMT *keymgmt = + evp_generic_fetch(ctx, OSSL_OP_KEYMGMT, algorithm, properties, + keymgmt_from_dispatch, + (int (*)(void *))EVP_KEYMGMT_up_ref, + (void (*)(void *))EVP_KEYMGMT_free); + + return keymgmt; +} + +int EVP_KEYMGMT_up_ref(EVP_KEYMGMT *keymgmt) +{ + int ref = 0; + + CRYPTO_UP_REF(&keymgmt->refcnt, &ref, keymgmt->lock); + return 1; +} + +void EVP_KEYMGMT_free(EVP_KEYMGMT *keymgmt) +{ + int ref = 0; + + if (keymgmt == NULL) + return; + + CRYPTO_DOWN_REF(&keymgmt->refcnt, &ref, keymgmt->lock); + if (ref > 0) + return; + ossl_provider_free(keymgmt->prov); + CRYPTO_THREAD_lock_free(keymgmt->lock); + OPENSSL_free(keymgmt); +} + +const OSSL_PROVIDER *EVP_KEYMGMT_provider(const EVP_KEYMGMT *keymgmt) +{ + return keymgmt->prov; +} + diff --git a/crypto/include/internal/evp_int.h b/crypto/include/internal/evp_int.h index 71833fa49e..359d561342 100644 --- a/crypto/include/internal/evp_int.h +++ b/crypto/include/internal/evp_int.h @@ -504,9 +504,9 @@ typedef struct { * method, as in, can it do arbitrary encryption.... */ struct evp_pkey_st { + /* == Legacy attributes == */ int type; int save_type; - CRYPTO_REF_COUNT references; const EVP_PKEY_ASN1_METHOD *ameth; ENGINE *engine; ENGINE *pmeth_engine; /* If not NULL public key ENGINE to use */ @@ -526,9 +526,25 @@ struct evp_pkey_st { ECX_KEY *ecx; /* X25519, X448, Ed25519, Ed448 */ # endif } pkey; - int save_parameters; - STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */ + + /* == Common attributes == */ + CRYPTO_REF_COUNT references; CRYPTO_RWLOCK *lock; + STACK_OF(X509_ATTRIBUTE) *attributes; /* [ 0 ] */ + int save_parameters; + + /* == Provider attributes == */ + /* + * To support transparent export/import between providers that + * support the methods for it, and still not having to do the + * export/import every time a key is used, we maintain a cache + * of imported key, indexed by provider address. + * pkeys[0] is *always* the "original" key. + */ + struct { + EVP_KEYMGMT *keymgmt; + void *provkey; + } pkeys[10]; } /* EVP_PKEY */ ; diff --git a/doc/man3/EVP_KEYMGMT.pod b/doc/man3/EVP_KEYMGMT.pod new file mode 100644 index 0000000000..ab209da768 --- /dev/null +++ b/doc/man3/EVP_KEYMGMT.pod @@ -0,0 +1,84 @@ +=pod + +=head1 NAME + +EVP_KEYMGMT, +EVP_KEYMGMT_fetch, +EVP_KEYMGMT_up_ref, +EVP_KEYMGMT_free, +EVP_KEYMGMT_provider +- EVP key management routines + +=head1 SYNOPSIS + + #include + + typedef struct evp_keymgmt_st EVP_KEYMGMT; + + EVP_KEYMGMT *EVP_KEYMGMT_fetch(OPENSSL_CTX *ctx, const char *algorithm, + const char *properties); + int EVP_KEYMGMT_up_ref(EVP_KEYMGMT *keymgmt); + void EVP_KEYMGMT_free(EVP_KEYMGMT *keymgmt); + const OSSL_PROVIDER *EVP_KEYMGMT_provider(const EVP_KEYMGMT *keymgmt); + +=head1 DESCRIPTION + +B is a method object that represents key management +implementations for different cryptographic algorithms. +This method object provides functionality to have providers import key +material from the outside, as well as export key material to the +outside. +Most of the functionality can only be used internally and has no +public interface, this object is simply passed into other functions +when needed. + +EVP_KEYMGMT_fetch() looks for an algorithm within the provider that +has been loaded into the B given by I, having the +name given by I and the properties given by I. + +EVP_KEYMGMT_up_ref() increments the reference count for the given +B I. + +EVP_KEYMGMT_free() decrements the reference count for the given +B I, and when the count reaches zero, frees it. + +EVP_KEYMGMT_provider() returns the provider that has this particular +implementation. + +=head1 NOTES + +EVP_KEYMGMT_fetch() may be called implicitly by other fetching +functions, using the same library context and properties. +Any other API that uses keys will typically do this. + +=head1 RETURN VALUES + +EVP_KEYMGMT_fetch() returns a pointer to the key management +implementation represented by an EVP_KEYMGMT object, or NULL on +error. + +EVP_KEYMGMT_up_ref() returns 1 on success, or 0 on error. + +EVP_KEYMGMT_free() doesn't return any value. + +EVP_KEYMGMT_provider() returns a pointer to a provider object, or NULL +on error. + +=head1 SEE ALSO + +L, L + +=head1 HISTORY + +The functions described here were added in OpenSSL 3.0. + +=head1 COPYRIGHT + +Copyright 2019 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/core_numbers.h b/include/openssl/core_numbers.h index c5892431e4..f45b8f1084 100644 --- a/include/openssl/core_numbers.h +++ b/include/openssl/core_numbers.h @@ -229,9 +229,80 @@ OSSL_CORE_MAKE_FUNC(int, OP_cipher_ctx_get_params, (void *cctx, OSSL_CORE_MAKE_FUNC(int, OP_cipher_ctx_set_params, (void *cctx, const OSSL_PARAM params[])) +/*- + * Key management + * + * Key domain parameter references can be created in several manners: + * - by importing the domain parameter material via an OSSL_PARAM array. + * - by generating key domain parameters, given input via an OSSL_PARAM + * array. + * + * Key references can be created in several manners: + * - by importing the key material via an OSSL_PARAM array. + * - by generating a key, given optional domain parameters and + * additional keygen parameters. + * If domain parameters are given, they must have been generated using + * the domain parameter generator functions. + * If the domain parameters comes from a different provider, results + * are undefined. + * THE CALLER MUST ENSURE THAT CORRECT DOMAIN PARAMETERS ARE USED. + * - by loading an internal key, given a binary blob that forms an identity. + * THE CALLER MUST ENSURE THAT A CORRECT IDENTITY IS USED. + */ + +# define OSSL_OP_KEYMGMT 10 + +/* Key domain parameter creation and destruction */ +# define OSSL_FUNC_KEYMGMT_IMPORTDOMPARAMS 1 +# define OSSL_FUNC_KEYMGMT_GENDOMPARAMS 2 +# define OSSL_FUNC_KEYMGMT_FREEDOMPARAMS 3 +OSSL_CORE_MAKE_FUNC(void *, OP_keymgmt_importdomparams, + (void *provctx, const OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(void *, OP_keymgmt_gendomparams, + (void *provctx, const OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(void, OP_keymgmt_freedomparams, (void *domparams)) + +/* Key domain parameter export */ +# define OSSL_FUNC_KEYMGMT_EXPORTDOMPARAMS 4 +OSSL_CORE_MAKE_FUNC(int, OP_keymgmt_exportdomparams, + (void *domparams, OSSL_PARAM params[])) + +/* Key domain parameter discovery */ +# define OSSL_FUNC_KEYMGMT_IMPORTDOMPARAM_TYPES 5 +# define OSSL_FUNC_KEYMGMT_EXPORTDOMPARAM_TYPES 6 +OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keymgmt_importdomparam_types, + (void)) +OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keymgmt_exportdomparam_types, + (void)) + +/* Key creation and destruction */ +# define OSSL_FUNC_KEYMGMT_IMPORTKEY 10 +# define OSSL_FUNC_KEYMGMT_GENKEY 11 +# define OSSL_FUNC_KEYMGMT_LOADKEY 12 +# define OSSL_FUNC_KEYMGMT_FREEKEY 13 +OSSL_CORE_MAKE_FUNC(void *, OP_keymgmt_importkey, + (void *provctx, const OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(void *, OP_keymgmt_genkey, + (void *provctx, + void *domparams, const OSSL_PARAM genkeyparams[])) +OSSL_CORE_MAKE_FUNC(void *, OP_keymgmt_loadkey, + (void *provctx, void *id, size_t idlen)) +OSSL_CORE_MAKE_FUNC(void, OP_keymgmt_freekey, (void *key)) + +/* Key export */ +# define OSSL_FUNC_KEYMGMT_EXPORTKEY 14 +OSSL_CORE_MAKE_FUNC(int, OP_keymgmt_exportkey, + (void *key, OSSL_PARAM params[])) + +/* Key discovery */ +# define OSSL_FUNC_KEYMGMT_IMPORTKEY_TYPES 15 +# define OSSL_FUNC_KEYMGMT_EXPORTKEY_TYPES 16 +OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keymgmt_importkey_types, (void)) +OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_keymgmt_exportkey_types, (void)) + /* Key Exchange */ -# define OSSL_OP_KEYEXCH 3 +# define OSSL_OP_KEYEXCH 11 # define OSSL_FUNC_KEYEXCH_NEWCTX 1 # define OSSL_FUNC_KEYEXCH_INIT 2 diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 377b4b16c0..d014a2e3cf 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -1411,6 +1411,12 @@ int EVP_PKEY_meth_remove(const EVP_PKEY_METHOD *pmeth); size_t EVP_PKEY_meth_get_count(void); const EVP_PKEY_METHOD *EVP_PKEY_meth_get0(size_t idx); +EVP_KEYMGMT *EVP_KEYMGMT_fetch(OPENSSL_CTX *ctx, const char *algorithm, + const char *properties); +int EVP_KEYMGMT_up_ref(EVP_KEYMGMT *keymgmt); +void EVP_KEYMGMT_free(EVP_KEYMGMT *keymgmt); +const OSSL_PROVIDER *EVP_KEYMGMT_provider(const EVP_KEYMGMT *keymgmt); + EVP_PKEY_CTX *EVP_PKEY_CTX_new(EVP_PKEY *pkey, ENGINE *e); EVP_PKEY_CTX *EVP_PKEY_CTX_new_id(int id, ENGINE *e); EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *ctx); diff --git a/include/openssl/ossl_typ.h b/include/openssl/ossl_typ.h index 76a9bee78d..7eec053bee 100644 --- a/include/openssl/ossl_typ.h +++ b/include/openssl/ossl_typ.h @@ -101,6 +101,8 @@ typedef struct evp_pkey_asn1_method_st EVP_PKEY_ASN1_METHOD; typedef struct evp_pkey_method_st EVP_PKEY_METHOD; typedef struct evp_pkey_ctx_st EVP_PKEY_CTX; +typedef struct evp_keymgmt_st EVP_KEYMGMT; + typedef struct evp_kdf_st EVP_KDF; typedef struct evp_kdf_ctx_st EVP_KDF_CTX; diff --git a/util/libcrypto.num b/util/libcrypto.num index 648aed9d85..1992504c91 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -4685,3 +4685,7 @@ EVP_KEYEXCH_up_ref 4790 3_0_0 EXIST::FUNCTION: EVP_KEYEXCH_fetch 4791 3_0_0 EXIST::FUNCTION: EVP_PKEY_CTX_set_dh_pad 4792 3_0_0 EXIST::FUNCTION:DH EVP_PKEY_CTX_set_params 4793 3_0_0 EXIST::FUNCTION: +EVP_KEYMGMT_fetch 4794 3_0_0 EXIST::FUNCTION: +EVP_KEYMGMT_up_ref 4795 3_0_0 EXIST::FUNCTION: +EVP_KEYMGMT_free 4796 3_0_0 EXIST::FUNCTION: +EVP_KEYMGMT_provider 4797 3_0_0 EXIST::FUNCTION: diff --git a/util/private.num b/util/private.num index f63319dd96..3307e3e239 100644 --- a/util/private.num +++ b/util/private.num @@ -24,6 +24,7 @@ CRYPTO_EX_new datatype DTLS_timer_cb datatype EVP_KDF datatype EVP_KDF_CTX datatype +EVP_KEYMGMT datatype EVP_MAC datatype EVP_MAC_CTX datatype EVP_PKEY_gen_cb datatype -- 2.25.1