From c13d2ab439a9dcbbf22ef85a00603142b0a37779 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Fri, 8 Feb 2019 17:01:56 +0100 Subject: [PATCH] Add generic EVP method fetcher This is an interface between Core dispatch table fetching and EVP_{method}_fetch(). All that's needed from the diverse method fetchers are the functions to create a method structure from a dispatch table, a function that ups the method reference counter and a function to free the method (in case of failure). This routine is internal to the EVP API andis therefore only made accessible within crypto/evp, by including evp_locl.h Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/8341) --- crypto/evp/build.info | 4 + crypto/evp/evp_fetch.c | 197 ++++++++++++++++++++ crypto/evp/evp_locl.h | 12 +- doc/internal/man3/evp_generic_fetch.pod | 232 ++++++++++++++++++++++++ 4 files changed, 444 insertions(+), 1 deletion(-) create mode 100644 crypto/evp/evp_fetch.c create mode 100644 doc/internal/man3/evp_generic_fetch.pod diff --git a/crypto/evp/build.info b/crypto/evp/build.info index 862afa836a..10ba3a3c1d 100644 --- a/crypto/evp/build.info +++ b/crypto/evp/build.info @@ -16,6 +16,10 @@ SOURCE[../../libcrypto]=\ e_chacha20_poly1305.c cmeth_lib.c \ mac_lib.c c_allm.c pkey_mac.c +# New design +SOURCE[../../libcrypto]=\ + evp_fetch.c + INCLUDE[e_aes.o]=.. ../modes INCLUDE[e_aes_cbc_hmac_sha1.o]=../modes INCLUDE[e_aes_cbc_hmac_sha256.o]=../modes diff --git a/crypto/evp/evp_fetch.c b/crypto/evp/evp_fetch.c new file mode 100644 index 0000000000..e50dd592db --- /dev/null +++ b/crypto/evp/evp_fetch.c @@ -0,0 +1,197 @@ +/* + * 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/cryptlib.h" +#include "internal/thread_once.h" +#include "internal/asn1_int.h" +#include "internal/property.h" +#include "internal/core.h" +#include "internal/evp_int.h" /* evp_locl.h needs it */ +#include "evp_locl.h" + +/* The OpenSSL library context index for the default method store */ +static int default_method_store_index = -1; + +static void default_method_store_free(void *vstore) +{ + ossl_method_store_free(vstore); +} + +static void *default_method_store_new(void) +{ + return ossl_method_store_new(); +} + + +static const OPENSSL_CTX_METHOD default_method_store_method = { + default_method_store_new, + default_method_store_free, +}; + +static int default_method_store_init(void) +{ + default_method_store_index = + openssl_ctx_new_index(&default_method_store_method); + + return default_method_store_index != -1; +} + +static CRYPTO_ONCE default_method_store_init_flag = CRYPTO_ONCE_STATIC_INIT; +DEFINE_RUN_ONCE_STATIC(do_default_method_store_init) +{ + return OPENSSL_init_crypto(0, NULL) + && default_method_store_init(); +} + +/* Data to be passed through ossl_method_construct() */ +struct method_data_st { + const char *name; + int nid; + OSSL_METHOD_CONSTRUCT_METHOD *mcm; + void *(*method_from_dispatch)(int nid, const OSSL_DISPATCH *, + OSSL_PROVIDER *); + int (*refcnt_up_method)(void *method); + void (*destruct_method)(void *method); +}; + +/* + * Generic routines to fetch / create EVP methods with ossl_method_construct() + */ +static void *alloc_tmp_method_store(void) +{ + return ossl_method_store_new(); +} + + static void dealloc_tmp_method_store(void *store) +{ + if (store != NULL) + ossl_method_store_free(store); +} + +static +struct OSSL_METHOD_STORE *get_default_method_store(OPENSSL_CTX *libctx) +{ + if (!RUN_ONCE(&default_method_store_init_flag, + do_default_method_store_init)) + return NULL; + return openssl_ctx_get_data(libctx, default_method_store_index); +} + +static void *get_method_from_store(OPENSSL_CTX *libctx, void *store, + const char *propquery, void *data) +{ + struct method_data_st *methdata = data; + void *method = NULL; + + if (store == NULL + && (store = get_default_method_store(libctx)) == NULL) + return NULL; + + (void)ossl_method_store_fetch(store, methdata->nid, propquery, &method); + + if (method != NULL + && !methdata->refcnt_up_method(method)) { + method = NULL; + } + return method; +} + +static int put_method_in_store(OPENSSL_CTX *libctx, void *store, + const char *propdef, void *method, + void *data) +{ + struct method_data_st *methdata = data; + + if (store == NULL + && (store = get_default_method_store(libctx)) == NULL) + return 0; + + if (methdata->refcnt_up_method(method) + && ossl_method_store_add(store, methdata->nid, propdef, method, + methdata->destruct_method)) + return 1; + return 0; +} + +static void *construct_method(const OSSL_DISPATCH *fns, OSSL_PROVIDER *prov, + void *data) +{ + struct method_data_st *methdata = data; + void *method = NULL; + + if (methdata->nid == NID_undef) { + /* Create a new NID for that name on the fly */ + ASN1_OBJECT tmpobj; + + /* This is the same as OBJ_create() but without requiring a OID */ + tmpobj.nid = OBJ_new_nid(1); + tmpobj.sn = tmpobj.ln = methdata->name; + tmpobj.flags = ASN1_OBJECT_FLAG_DYNAMIC; + tmpobj.length = 0; + tmpobj.data = NULL; + + methdata->nid = OBJ_add_object(&tmpobj); + } + + if (methdata->nid == NID_undef) + return NULL; + + method = methdata->method_from_dispatch(methdata->nid, fns, prov); + if (method == NULL) + return NULL; + return method; +} + +static void destruct_method(void *method, void *data) +{ + struct method_data_st *methdata = data; + + methdata->destruct_method(method); +} + +void *evp_generic_fetch(OPENSSL_CTX *libctx, int operation_id, + const char *algorithm, const char *properties, + void *(*new_method)(int nid, const OSSL_DISPATCH *fns, + OSSL_PROVIDER *prov), + int (*upref_method)(void *), + void (*free_method)(void *)) +{ + int nid = OBJ_sn2nid(algorithm); + void *method = NULL; + + if (nid == NID_undef + || !ossl_method_store_cache_get(NULL, nid, properties, &method)) { + OSSL_METHOD_CONSTRUCT_METHOD mcm = { + alloc_tmp_method_store, + dealloc_tmp_method_store, + get_method_from_store, + put_method_in_store, + construct_method, + destruct_method + }; + struct method_data_st mcmdata; + + mcmdata.nid = nid; + mcmdata.mcm = &mcm; + mcmdata.method_from_dispatch = new_method; + mcmdata.destruct_method = free_method; + mcmdata.refcnt_up_method = upref_method; + mcmdata.destruct_method = free_method; + method = ossl_method_construct(libctx, operation_id, algorithm, + properties, 0 /* !force_cache */, + &mcm, &mcmdata); + ossl_method_store_cache_set(NULL, nid, properties, method); + } + + return method; +} diff --git a/crypto/evp/evp_locl.h b/crypto/evp/evp_locl.h index 95a9d5c517..82e7bf8b11 100644 --- a/crypto/evp/evp_locl.h +++ b/crypto/evp/evp_locl.h @@ -1,5 +1,5 @@ /* - * Copyright 2000-2018 The OpenSSL Project Authors. All Rights Reserved. + * Copyright 2000-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 @@ -76,3 +76,13 @@ typedef struct evp_pbe_st EVP_PBE_CTL; DEFINE_STACK_OF(EVP_PBE_CTL) int is_partially_overlapping(const void *ptr1, const void *ptr2, int len); + +#include +#include + +void *evp_generic_fetch(OPENSSL_CTX *ctx, int operation_id, + const char *algorithm, const char *properties, + void *(*new_method)(int nid, const OSSL_DISPATCH *fns, + OSSL_PROVIDER *prov), + int (*upref_method)(void *), + void (*free_method)(void *)); diff --git a/doc/internal/man3/evp_generic_fetch.pod b/doc/internal/man3/evp_generic_fetch.pod new file mode 100644 index 0000000000..b871cd1fee --- /dev/null +++ b/doc/internal/man3/evp_generic_fetch.pod @@ -0,0 +1,232 @@ +=pod + +=head1 NAME + +evp_generic_fetch - generic algorithm fetcher and method creator for EVP + +=head1 SYNOPSIS + + /* Only for EVP source */ + #include "evp_locl.h" + + void *evp_generic_fetch(OPENSSL_CTX *libctx, int operation_id, + const char *algorithm, const char *properties, + void *(*new_method)(int nid, const OSSL_DISPATCH *fns, + OSSL_PROVIDER *prov), + int (*upref_method)(void *), + void (*free_method)(void *)); + +=head1 DESCRIPTION + +evp_generic_fetch() calls ossl_method_construct() with the given +C, C, C, and C and uses +it to create an EVP method with the help of the functions +C, C, and C. + +The three functions are supposed to: + +=over 4 + +=item new_method() + +creates an internal method from function pointers found in the +dispatch table C. + +=item upref_method() + +increments the reference counter for the given method, if there is +one. + +=item free_method() + +frees the given method. + +=back + +=head1 RETURN VALUES + +evp_generic_fetch() returns a method on success, or B on error. + +=head1 EXAMPLES + +This is a short example of the fictitious EVP API and operation called +C. + +To begin with, let's assume something like this in +C: + + #define OSSL_OP_FOO 100 + + #define OSSL_OP_FOO_NEWCTX_FUNC 2001 + #define OSSL_OP_FOO_INIT 2002 + #define OSSL_OP_FOO_OPERATE 2003 + #define OSSL_OP_FOO_CLEANCTX_FUNC 2004 + #define OSSL_OP_FOO_FREECTX_FUNC 2005 + OSSL_CORE_MAKE_FUNC(void *,OP_foo_newctx,(void)) + OSSL_CORE_MAKE_FUNC(int,OP_foo_init,(void *vctx)) + OSSL_CORE_MAKE_FUNC(int,OP_foo_operate,(void *vctx, + unsigned char *out, size_t *out_l, + unsigned char *in, size_t in_l)) + OSSL_CORE_MAKE_FUNC(void,OP_foo_cleanctx,(void *vctx)) + OSSL_CORE_MAKE_FUNC(void,OP_foo_freectx,(void *vctx)) + +And here's the implementation of the FOO method fetcher: + + /* typedef struct evp_foo_st EVP_FOO */ + struct evp_foo_st { + OSSL_PROVIDER *prov; + int nid; + CRYPTO_REF_COUNT refcnt; + OSSL_OP_foo_newctx_fn *newctx; + OSSL_OP_foo_init_fn *init; + OSSL_OP_foo_operate_fn *operate; + OSSL_OP_foo_cleanctx_fn *cleanctx; + OSSL_OP_foo_freectx_fn *freectx; + }; + + /* + * In this example, we have a public method creator and destructor. + * It's not absolutely necessary, but is in the spirit of OpenSSL. + */ + EVP_FOO *EVP_FOO_meth_from_dispatch(int foo_type, const OSSL_DISPATCH *fns, + OSSL_PROVIDER *prov) + { + EVP_FOO *foo = NULL; + + if ((foo = OPENSSL_zalloc(sizeof(*foo))) == NULL) + return NULL; + + for (; fns->function_id != 0; fns++) { + switch (fns->function_id) { + case OSSL_OP_FOO_NEWCTX_FUNC: + foo->newctx = OSSL_get_OP_foo_newctx(fns); + break; + case OSSL_OP_FOO_INIT: + foo->init = OSSL_get_OP_foo_init(fns); + break; + case OSSL_OP_FOO_OPERATE: + foo->operate = OSSL_get_OP_foo_operate(fns); + break; + case OSSL_OP_FOO_CLEANCTX_FUNC: + foo->cleanctx = OSSL_get_OP_foo_cleanctx(fns); + break; + case OSSL_OP_FOO_FREECTX_FUNC: + foo->freectx = OSSL_get_OP_foo_freectx(fns); + break; + } + } + foo->nid = foo_type; + foo->prov = prov; + if (prov) + ossl_provider_upref(prov); + + return foo; + } + + EVP_FOO_meth_free(EVP_FOO *foo) + { + if (foo != NULL) { + OSSL_PROVIDER *prov = foo->prov; + + OPENSSL_free(foo); + ossl_provider_free(prov); + } + } + + static void *foo_from_dispatch(int nid, const OSSL_DISPATCH *fns, + OSSL_PROVIDER *prov) + { + return EVP_FOO_meth_from_dispatch(nid, fns, prov); + } + + static int foo_upref(void *vfoo) + { + EVP_FOO *foo = vfoo; + int ref = 0; + + CRYPTO_UP_REF(&foo->refcnt, &ref, foo_lock); + return 1; + } + + static void foo_free(void *vfoo) + { + EVP_FOO_meth_free(vfoo); + } + + EVP_FOO *EVP_FOO_fetch(OPENSSL_CTX *ctx, + const char *algorithm, + const char *properties) + { + return evp_generic_fetch(ctx, OSSL_OP_FOO, algorithm, properties, + foo_from_dispatch, foo_upref, foo_free); + } + +And finally, the library functions: + + /* typedef struct evp_foo_st EVP_FOO_CTX */ + struct evp_foo_ctx_st { + const EVP_FOO *foo; + void *provctx; /* corresponding provider context */ + }; + + int EVP_FOO_CTX_reset(EVP_FOO_CTX *c) + { + if (c == NULL) + return 1; + if (c->foo != NULL && c->foo->cleanctx != NULL) + c->foo->cleanctx(c->provctx); + return 1; + } + + EVP_FOO_CTX *EVP_FOO_CTX_new(void) + { + return OPENSSL_zalloc(sizeof(EVP_FOO_CTX)); + } + + void EVP_FOO_CTX_free(EVP_FOO_CTX *c) + { + EVP_FOO_CTX_reset(c); + c->foo->freectx(c->provctx); + OPENSSL_free(c); + } + + int EVP_FooInit(EVP_FOO_CTX *c, const EVP_FOO *foo) + { + int ok = 1; + + c->foo = foo; + if (c->provctx == NULL) + c->provctx = c->foo->newctx(); + + ok = c->foo->init(c->provctx); + + return ok; + } + + int EVP_FooOperate(EVP_FOO_CTX *c, unsigned char *out, size_t *outl, + const unsigned char *in, size_t inl) + { + int ok = 1; + + ok = c->foo->update(c->provctx, out, inl, &outl, in, inl); + return ok; + } + +=head1 SEE ALSO + +L + +=head1 HISTORY + +The functions described here were all 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 -- 2.39.2