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 <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/8341)
This commit is contained in:
Richard Levitte 2019-02-08 17:01:56 +01:00
parent a383083194
commit c13d2ab439
4 changed files with 444 additions and 1 deletions

View file

@ -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

197
crypto/evp/evp_fetch.c Normal file
View file

@ -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 <stddef.h>
#include <openssl/ossl_typ.h>
#include <openssl/evp.h>
#include <openssl/core.h>
#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;
}

View file

@ -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 <openssl/ossl_typ.h>
#include <openssl/core.h>
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 *));

View file

@ -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<libctx>, C<operation_id>, C<algorithm>, and C<properties> and uses
it to create an EVP method with the help of the functions
C<new_method>, C<upref_method>, and C<free_method>.
The three functions are supposed to:
=over 4
=item new_method()
creates an internal method from function pointers found in the
dispatch table C<fns>.
=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<NULL> on error.
=head1 EXAMPLES
This is a short example of the fictitious EVP API and operation called
C<EVP_FOO>.
To begin with, let's assume something like this in
C<include/openssl/core_numbers.h>:
#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<ossl_method_construct>
=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<https://www.openssl.org/source/license.html>.
=cut