diff --git a/crypto/build.info b/crypto/build.info index 5f90a2eda2..5beaf528be 100644 --- a/crypto/build.info +++ b/crypto/build.info @@ -5,7 +5,7 @@ SUBDIRS=objects buffer bio stack lhash rand evp asn1 pem x509 conf \ md2 md4 md5 sha mdc2 hmac ripemd whrlpool poly1305 \ siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \ seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \ - err comp ocsp cms ts srp cmac ct async ess crmf cmp + err comp ocsp cms ts srp cmac ct async ess crmf cmp serializer LIBS=../libcrypto diff --git a/crypto/err/err.c b/crypto/err/err.c index bc33a06b7e..0fb46bef4d 100644 --- a/crypto/err/err.c +++ b/crypto/err/err.c @@ -75,6 +75,7 @@ static ERR_STRING_DATA ERR_str_libraries[] = { {ERR_PACK(ERR_LIB_SM2, 0, 0), "SM2 routines"}, {ERR_PACK(ERR_LIB_ESS, 0, 0), "ESS routines"}, {ERR_PACK(ERR_LIB_PROV, 0, 0), "Provider routines"}, + {ERR_PACK(ERR_LIB_OSSL_SERIALIZER, 0, 0), "SERIALIZER routines"}, {0, NULL}, }; @@ -111,6 +112,7 @@ static ERR_STRING_DATA ERR_str_reasons[] = { {ERR_R_DISABLED, "called a function that was disabled at compile-time"}, {ERR_R_INIT_FAIL, "init fail"}, {ERR_R_OPERATION_FAIL, "operation fail"}, + {ERR_R_INVALID_PROVIDER_FUNCTIONS, "invalid provider functions"}, {0, NULL}, }; diff --git a/crypto/err/openssl.ec b/crypto/err/openssl.ec index 211edd42f3..485c0c89ce 100644 --- a/crypto/err/openssl.ec +++ b/crypto/err/openssl.ec @@ -40,6 +40,7 @@ L OSSL_STORE include/openssl/store.h crypto/store/store_err.c L ESS include/openssl/ess.h crypto/ess/ess_err.c L PROP include/internal/property.h crypto/property/property_err.c L PROV providers/common/include/prov/providercommon.h providers/common/provider_err.c +L OSSL_SERIALIZER include/openssl/serializer.h crypto/serializer/serializer_err.c # additional header files to be scanned for function names L NONE include/openssl/x509_vfy.h NONE diff --git a/crypto/property/property_parse.c b/crypto/property/property_parse.c index a16bcd6dba..f6377e7714 100644 --- a/crypto/property/property_parse.c +++ b/crypto/property/property_parse.c @@ -568,6 +568,8 @@ int ossl_property_parse_init(OPENSSL_CTX *ctx) "version", /* Version number of this provider */ "fips", /* FIPS supporting provider */ "engine", /* An old style engine masquerading as a provider */ + "format", /* output format for serializers */ + "type", /* output type for serializers */ }; size_t i; diff --git a/crypto/serializer/build.info b/crypto/serializer/build.info new file mode 100644 index 0000000000..7d69df931c --- /dev/null +++ b/crypto/serializer/build.info @@ -0,0 +1 @@ +SOURCE[../../libcrypto]=serializer_meth.c diff --git a/crypto/serializer/serializer_local.h b/crypto/serializer/serializer_local.h new file mode 100644 index 0000000000..979ba83e78 --- /dev/null +++ b/crypto/serializer/serializer_local.h @@ -0,0 +1,34 @@ +/* + * 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 "internal/cryptlib.h" +#include "internal/refcount.h" + +struct ossl_serializer_st { + OSSL_PROVIDER *prov; + int id; + const char *propdef; + + CRYPTO_REF_COUNT refcnt; + CRYPTO_RWLOCK *lock; + + OSSL_OP_serializer_newctx_fn *newctx; + OSSL_OP_serializer_freectx_fn *freectx; + OSSL_OP_serializer_set_ctx_params_fn *set_ctx_params; + OSSL_OP_serializer_settable_ctx_params_fn *settable_ctx_params; + OSSL_OP_serializer_serialize_data_fn *serialize_data; + OSSL_OP_serializer_serialize_object_fn *serialize_object; +}; + +struct ossl_serializer_ctx_st { + OSSL_SERIALIZER *ser; + void *serctx; +}; diff --git a/crypto/serializer/serializer_meth.c b/crypto/serializer/serializer_meth.c new file mode 100644 index 0000000000..103188f93a --- /dev/null +++ b/crypto/serializer/serializer_meth.c @@ -0,0 +1,514 @@ +/* + * 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 "internal/core.h" +#include "internal/namemap.h" +#include "internal/property.h" +#include "internal/provider.h" +#include "crypto/serializer.h" +#include "serializer_local.h" + +/* + * Serializer can have multiple names, separated with colons in a name string + */ +#define NAME_SEPARATOR ':' + +/* Simple method structure constructor and destructor */ +static OSSL_SERIALIZER *ossl_serializer_new(void) +{ + OSSL_SERIALIZER *ser = NULL; + + if ((ser = OPENSSL_zalloc(sizeof(*ser))) == NULL + || (ser->lock = CRYPTO_THREAD_lock_new()) == NULL) { + OSSL_SERIALIZER_free(ser); + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE); + return NULL; + } + + ser->refcnt = 1; + + return ser; +} + +int OSSL_SERIALIZER_up_ref(OSSL_SERIALIZER *ser) +{ + int ref = 0; + + CRYPTO_UP_REF(&ser->refcnt, &ref, ser->lock); + return 1; +} + +void OSSL_SERIALIZER_free(OSSL_SERIALIZER *ser) +{ + int ref = 0; + + if (ser == NULL) + return; + + CRYPTO_DOWN_REF(&ser->refcnt, &ref, ser->lock); + if (ref > 0) + return; + ossl_provider_free(ser->prov); + CRYPTO_THREAD_lock_free(ser->lock); + OPENSSL_free(ser); +} + +/* Permanent serializer method store, constructor and destructor */ +static void serializer_store_free(void *vstore) +{ + ossl_method_store_free(vstore); +} + +static void *serializer_store_new(OPENSSL_CTX *ctx) +{ + return ossl_method_store_new(ctx); +} + + +static const OPENSSL_CTX_METHOD serializer_store_method = { + serializer_store_new, + serializer_store_free, +}; + +/* Data to be passed through ossl_method_construct() */ +struct serializer_data_st { + OPENSSL_CTX *libctx; + OSSL_METHOD_CONSTRUCT_METHOD *mcm; + int id; /* For get_serializer_from_store() */ + const char *names; /* For get_serializer_from_store() */ + const char *propquery; /* For get_serializer_from_store() */ +}; + +/* + * Generic routines to fetch / create SERIALIZER methods with + * ossl_method_construct() + */ + +/* Temporary serializer method store, constructor and destructor */ +static void *alloc_tmp_serializer_store(OPENSSL_CTX *ctx) +{ + return ossl_method_store_new(ctx); +} + + static void dealloc_tmp_serializer_store(void *store) +{ + if (store != NULL) + ossl_method_store_free(store); +} + +/* Get the permanent serializer store */ +static OSSL_METHOD_STORE *get_serializer_store(OPENSSL_CTX *libctx) +{ + return openssl_ctx_get_data(libctx, OPENSSL_CTX_SERIALIZER_STORE_INDEX, + &serializer_store_method); +} + +/* Get serializer methods from a store, or put one in */ +static void *get_serializer_from_store(OPENSSL_CTX *libctx, void *store, + void *data) +{ + struct serializer_data_st *methdata = data; + void *method = NULL; + int id; + + if ((id = methdata->id) == 0) { + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + + id = ossl_namemap_name2num(namemap, methdata->names); + } + + if (store == NULL + && (store = get_serializer_store(libctx)) == NULL) + return NULL; + + if (!ossl_method_store_fetch(store, id, methdata->propquery, &method)) + return NULL; + return method; +} + +static int put_serializer_in_store(OPENSSL_CTX *libctx, void *store, + void *method, const OSSL_PROVIDER *prov, + int operation_id, const char *names, + const char *propdef, void *unused) +{ + OSSL_NAMEMAP *namemap; + int id; + + if ((namemap = ossl_namemap_stored(libctx)) == NULL + || (id = ossl_namemap_name2num(namemap, names)) == 0) + return 0; + + if (store == NULL && (store = get_serializer_store(libctx)) == NULL) + return 0; + + return ossl_method_store_add(store, prov, id, propdef, method, + (int (*)(void *))OSSL_SERIALIZER_up_ref, + (void (*)(void *))OSSL_SERIALIZER_free); +} + +/* Create and populate a serializer method */ +static void *serializer_from_dispatch(int id, const OSSL_ALGORITHM *algodef, + OSSL_PROVIDER *prov) +{ + OSSL_SERIALIZER *ser = NULL; + const OSSL_DISPATCH *fns = algodef->implementation; + + if ((ser = ossl_serializer_new()) == NULL) + return NULL; + ser->id = id; + ser->propdef = algodef->property_definition; + + for (; fns->function_id != 0; fns++) { + switch (fns->function_id) { + case OSSL_FUNC_SERIALIZER_NEWCTX: + if (ser->newctx == NULL) + ser->newctx = + OSSL_get_OP_serializer_newctx(fns); + break; + case OSSL_FUNC_SERIALIZER_FREECTX: + if (ser->freectx == NULL) + ser->freectx = + OSSL_get_OP_serializer_freectx(fns); + break; + case OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS: + if (ser->set_ctx_params == NULL) + ser->set_ctx_params = + OSSL_get_OP_serializer_set_ctx_params(fns); + break; + case OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS: + if (ser->settable_ctx_params == NULL) + ser->settable_ctx_params = + OSSL_get_OP_serializer_settable_ctx_params(fns); + break; + case OSSL_FUNC_SERIALIZER_SERIALIZE_DATA: + if (ser->serialize_data == NULL) + ser->serialize_data = + OSSL_get_OP_serializer_serialize_data(fns); + break; + case OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT: + if (ser->serialize_object == NULL) + ser->serialize_object = + OSSL_get_OP_serializer_serialize_object(fns); + break; + } + } + /* + * Try to check that the method is sensible. + * If you have a constructor, you must have a destructor and vice versa. + * You must have at least one of the serializing driver functions. + */ + if (!((ser->newctx == NULL && ser->freectx == NULL) + || (ser->newctx != NULL && ser->freectx != NULL)) + || (ser->serialize_data == NULL && ser->serialize_object == NULL)) { + OSSL_SERIALIZER_free(ser); + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_INVALID_PROVIDER_FUNCTIONS); + return NULL; + } + + if (prov != NULL && !ossl_provider_up_ref(prov)) { + OSSL_SERIALIZER_free(ser); + return NULL; + } + + ser->prov = prov; + return ser; +} + + +/* + * The core fetching functionality passes the names of the implementation. + * This function is responsible to getting an identity number for them, + * then call serializer_from_dispatch() with that identity number. + */ +static void *construct_serializer(const OSSL_ALGORITHM *algodef, + OSSL_PROVIDER *prov, void *unused) +{ + /* + * This function is only called if get_serializer_from_store() returned + * NULL, so it's safe to say that of all the spots to create a new + * namemap entry, this is it. Should the name already exist there, we + * know that ossl_namemap_add() will return its corresponding number. + */ + OPENSSL_CTX *libctx = ossl_provider_library_context(prov); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + const char *names = algodef->algorithm_names; + int id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR); + void *method = NULL; + + if (id != 0) + method = serializer_from_dispatch(id, algodef, prov); + + return method; +} + +/* Intermediary function to avoid ugly casts, used below */ +static void destruct_serializer(void *method, void *data) +{ + OSSL_SERIALIZER_free(method); +} + +static int up_ref_serializer(void *method) +{ + return OSSL_SERIALIZER_up_ref(method); +} + +static void free_serializer(void *method) +{ + OSSL_SERIALIZER_free(method); +} + +/* Fetching support. Can fetch by numeric identity or by name */ +static OSSL_SERIALIZER *inner_ossl_serializer_fetch(OPENSSL_CTX *libctx, + int id, const char *name, + const char *properties) +{ + OSSL_METHOD_STORE *store = get_serializer_store(libctx); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + void *method = NULL; + + if (store == NULL || namemap == NULL) + return NULL; + + /* + * If we have been passed neither a name_id or a name, we have an + * internal programming error. + */ + if (!ossl_assert(id != 0 || name != NULL)) + return NULL; + + if (id == 0) + id = ossl_namemap_name2num(namemap, name); + + if (id == 0 + || !ossl_method_store_cache_get(store, id, properties, &method)) { + OSSL_METHOD_CONSTRUCT_METHOD mcm = { + alloc_tmp_serializer_store, + dealloc_tmp_serializer_store, + get_serializer_from_store, + put_serializer_in_store, + construct_serializer, + destruct_serializer + }; + struct serializer_data_st mcmdata; + + mcmdata.libctx = libctx; + mcmdata.mcm = &mcm; + mcmdata.id = id; + mcmdata.names = name; + mcmdata.propquery = properties; + if ((method = ossl_method_construct(libctx, OSSL_OP_SERIALIZER, + 0 /* !force_cache */, + &mcm, &mcmdata)) != NULL) { + /* + * If construction did create a method for us, we know that + * there is a correct name_id and meth_id, since those have + * already been calculated in get_serializer_from_store() and + * put_serializer_in_store() above. + */ + if (id == 0) + id = ossl_namemap_name2num(namemap, name); + ossl_method_store_cache_set(store, id, properties, method, + up_ref_serializer, free_serializer); + } + } + + return method; +} + +OSSL_SERIALIZER *OSSL_SERIALIZER_fetch(OPENSSL_CTX *libctx, const char *name, + const char *properties) +{ + return inner_ossl_serializer_fetch(libctx, 0, name, properties); +} + +OSSL_SERIALIZER *ossl_serializer_fetch_by_number(OPENSSL_CTX *libctx, int id, + const char *properties) +{ + return inner_ossl_serializer_fetch(libctx, id, NULL, properties); +} + +/* + * Library of basic method functions + */ + +const OSSL_PROVIDER *OSSL_SERIALIZER_provider(const OSSL_SERIALIZER *ser) +{ + if (!ossl_assert(ser != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return ser->prov; +} + +const char *OSSL_SERIALIZER_properties(const OSSL_SERIALIZER *ser) +{ + if (!ossl_assert(ser != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return ser->propdef; +} + +int OSSL_SERIALIZER_number(const OSSL_SERIALIZER *ser) +{ + if (!ossl_assert(ser != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return ser->id; +} + +int OSSL_SERIALIZER_is_a(const OSSL_SERIALIZER *ser, const char *name) +{ + if (ser->prov != NULL) { + OPENSSL_CTX *libctx = ossl_provider_library_context(ser->prov); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + + return ossl_namemap_name2num(namemap, name) == ser->id; + } + return 0; +} + +struct serializer_do_all_data_st { + void (*user_fn)(void *method, void *arg); + void *user_arg; +}; + +static void serializer_do_one(OSSL_PROVIDER *provider, + const OSSL_ALGORITHM *algodef, + int no_store, void *vdata) +{ + struct serializer_do_all_data_st *data = vdata; + OPENSSL_CTX *libctx = ossl_provider_library_context(provider); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + const char *names = algodef->algorithm_names; + int id = ossl_namemap_add_names(namemap, 0, names, NAME_SEPARATOR); + void *method = NULL; + + if (id != 0) + method = + serializer_from_dispatch(id, algodef, provider); + + if (method != NULL) { + data->user_fn(method, data->user_arg); + OSSL_SERIALIZER_free(method); + } +} + +void OSSL_SERIALIZER_do_all_provided(OPENSSL_CTX *libctx, + void (*fn)(OSSL_SERIALIZER *ser, + void *arg), + void *arg) +{ + struct serializer_do_all_data_st data; + + data.user_fn = (void (*)(void *, void *))fn; + data.user_arg = arg; + ossl_algorithm_do_all(libctx, OSSL_OP_SERIALIZER, NULL, + serializer_do_one, &data); +} + +void OSSL_SERIALIZER_names_do_all(const OSSL_SERIALIZER *ser, + void (*fn)(const char *name, void *data), + void *data) +{ + if (ser == NULL) + return; + + if (ser->prov != NULL) { + OPENSSL_CTX *libctx = ossl_provider_library_context(ser->prov); + OSSL_NAMEMAP *namemap = ossl_namemap_stored(libctx); + + ossl_namemap_doall_names(namemap, ser->id, fn, data); + } +} + +const OSSL_PARAM *OSSL_SERIALIZER_settable_ctx_params(OSSL_SERIALIZER *ser) +{ + if (ser != NULL && ser->settable_ctx_params != NULL) + return ser->settable_ctx_params(); + return NULL; +} + +/* + * Serializer context support + */ + +/* + * |ser| value NULL is valid, and signifies that there is no serializer. + * This is useful to provide fallback mechanisms. + * Funtions that want to verify if there is a serializer can do so with + * OSSL_SERIALIZER_CTX_get_serializer() + */ +OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new(OSSL_SERIALIZER *ser) +{ + OSSL_SERIALIZER_CTX *ctx; + + if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE); + return NULL; + } + + ctx->ser = ser; + if (ser != NULL && ser->newctx != NULL) { + const OSSL_PROVIDER *prov = OSSL_SERIALIZER_provider(ser); + void *provctx = ossl_provider_ctx(prov); + + if (OSSL_SERIALIZER_up_ref(ser)) { + ctx->serctx = ser->newctx(provctx); + } else { + OSSL_SERIALIZER_free(ser); + OPENSSL_free(ctx); + ctx = NULL; + } + } + + return ctx; +} + +const OSSL_SERIALIZER * +OSSL_SERIALIZER_CTX_get_serializer(OSSL_SERIALIZER_CTX *ctx) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + return ctx->ser; +} + + +int OSSL_SERIALIZER_CTX_set_params(OSSL_SERIALIZER_CTX *ctx, + const OSSL_PARAM params[]) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (ctx->ser != NULL && ctx->ser->set_ctx_params != NULL) + return ctx->ser->set_ctx_params(ctx->serctx, params); + return 0; +} + +void OSSL_SERIALIZER_CTX_free(OSSL_SERIALIZER_CTX *ctx) +{ + if (ctx != NULL) { + if (ctx->ser != NULL && ctx->ser->freectx != NULL) + ctx->ser->freectx(ctx->serctx); + OSSL_SERIALIZER_free(ctx->ser); + OPENSSL_free(ctx); + } +} diff --git a/doc/man3/OSSL_SERIALIZER.pod b/doc/man3/OSSL_SERIALIZER.pod new file mode 100644 index 0000000000..34767734f7 --- /dev/null +++ b/doc/man3/OSSL_SERIALIZER.pod @@ -0,0 +1,128 @@ +=pod + +=head1 NAME + +OSSL_SERIALIZER, +OSSL_SERIALIZER_fetch, +OSSL_SERIALIZER_up_ref, +OSSL_SERIALIZER_free, +OSSL_SERIALIZER_provider, +OSSL_SERIALIZER_properties, +OSSL_SERIALIZER_is_a, +OSSL_SERIALIZER_number, +OSSL_SERIALIZER_do_all_provided, +OSSL_SERIALIZER_names_do_all +- Serializer method routines + +=head1 SYNOPSIS + + #include + + typedef struct ossl_serializer_st OSSL_SERIALIZER; + + OSSL_SERIALIZER *OSSL_SERIALIZER_fetch(OPENSSL_CTX *ctx, const char *name, + const char *properties); + int OSSL_SERIALIZER_up_ref(OSSL_SERIALIZER *serializer); + void OSSL_SERIALIZER_free(OSSL_SERIALIZER *serializer); + const OSSL_PROVIDER *OSSL_SERIALIZER_provider(const OSSL_SERIALIZER + *serializer); + const char *OSSL_SERIALIZER_properties(const OSSL_SERIALIZER *ser); + int OSSL_SERIALIZER_is_a(const OSSL_SERIALIZER *serializer, + const char *name); + int OSSL_SERIALIZER_number(const OSSL_SERIALIZER *serializer); + void OSSL_SERIALIZER_do_all_provided(OPENSSL_CTX *libctx, + void (*fn)(OSSL_SERIALIZER *serializer, + void *arg), + void *arg); + void OSSL_SERIALIZER_names_do_all(const OSSL_SERIALIZER *serializer, + void (*fn)(const char *name, void *data), + void *data); + +=head1 DESCRIPTION + +=for comment Future development should also talk about deserialization + +B is a method for serializers, which know how to +serialize an object of some kind to a serialized form, such as PEM, +DER, or even human readable text. + +OSSL_SERIALIZER_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. +The I determines what type of object the fetched serializer +method is expected to be able to serialize, and the properties are +used to determine the expected output type. +For known properties and the values they may have, please have a look +in L. + +OSSL_SERIALIZER_up_ref() increments the reference count for the given +I. + +OSSL_SERIALIZER_free() decrements the reference count for the given +I, and when the count reaches zero, frees it. + +OSSL_SERIALIZER_provider() returns the provider of the given +I. + +OSSL_SERIALIZER_provider() returns the property definition associated +with the given I. + +OSSL_SERIALIZER_is_a() checks if I is an implementation of an +algorithm that's identifiable with I. + +OSSL_SERIALIZER_number() returns the internal dynamic number assigned to +the given I. + +OSSL_SERIALIZER_names_do_all() traverses all names for the given +I, and calls I with each name and I. + +OSSL_SERIALIZER_do_all_provided() traverses all serializer +implementations by all activated providers in the library context +I, and for each of the implementations, calls I with the +implementation method and I as arguments. + +=head1 NOTES + +OSSL_SERIALIZER_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 + +OSSL_SERIALIZER_fetch() returns a pointer to the key management +implementation represented by an OSSL_SERIALIZER object, or NULL on +error. + +OSSL_SERIALIZER_up_ref() returns 1 on success, or 0 on error. + +OSSL_SERIALIZER_free() doesn't return any value. + +OSSL_SERIALIZER_provider() returns a pointer to a provider object, or +NULL on error. + +OSSL_SERIALIZER_properties() returns a pointer to a property +definition string, or NULL on error. + +OSSL_SERIALIZER_is_a() returns 1 of I was identifiable, +otherwise 0. + +OSSL_SERIALIZER_number() returns an integer. + +=head1 SEE ALSO + +L, 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/doc/man3/OSSL_SERIALIZER_CTX.pod b/doc/man3/OSSL_SERIALIZER_CTX.pod new file mode 100644 index 0000000000..d446b842d0 --- /dev/null +++ b/doc/man3/OSSL_SERIALIZER_CTX.pod @@ -0,0 +1,94 @@ +=pod + +=head1 NAME + +OSSL_SERIALIZER_CTX, +OSSL_SERIALIZER_CTX_new, +OSSL_SERIALIZER_CTX_get_serializer, +OSSL_SERIALIZER_settable_ctx_params, +OSSL_SERIALIZER_CTX_set_params, +OSSL_SERIALIZER_CTX_free +- Serializer context routines + +=head1 SYNOPSIS + + #include + + typedef struct ossl_serializer_ctx_st OSSL_SERIALIZER_CTX; + + OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new(OSSL_SERIALIZER *ser); + const OSSL_SERIALIZER * + OSSL_SERIALIZER_CTX_get_serializer(OSSL_SERIALIZER_CTX *ctx); + const OSSL_PARAM *OSSL_SERIALIZER_settable_ctx_params(OSSL_SERIALIZER *ser); + int OSSL_SERIALIZER_CTX_set_params(OSSL_SERIALIZER_CTX *ctx, + const OSSL_PARAM params[]); + void OSSL_SERIALIZER_CTX_free(OSSL_SERIALIZER_CTX *ctx); + +=head1 DESCRIPTION + +B is a context with which B +operations are performed. The context typically holds values, both +internal and supplied by the application, which are useful for the +implementations supplied by providers. + +OSSL_SERIALIZER_CTX_new() creates a B associated +with the serializer I. NULL is a valid I, the context will +be created anyway, it's just not very useful. This is intentional, to +distinguish between errors in allocating the context or assigning it +values on one hand, and the lack of serializer support on the other. + +=begin comment + +The above distinction makes it possible for other routines to sense if +they need to report an error or fall back on other methods to +serialize. + +=end comment + +OSSL_SERIALIZER_CTX_get_serializer() gets the serializer method +currently associated with the context I. + +OSSL_SERIALIZER_settable_ctx_params() returns an L +array of parameter descriptors. + +OSSL_SERIALIZER_CTX_set_params() attempts to set parameters specified +with an L array I. Parameters that the +implementation doesn't recognise should be ignored. + +OSSL_SERIALIZER_CTX_free() frees the given context I. + +=head1 RETURN VALUES + +OSSL_SERIALIZER_CTX_new() returns a pointer to a +B, or NULL if the context structure couldn't be +allocated. + +OSSL_SERIALIZER_CTX_get_serializer() returns a pointer to the +serializer method associated with I. NULL is a valid return +value and signifies that there is no associated serializer method. + +OSSL_SERIALIZER_settable_ctx_params() returns an L +array, or NULL if none is available. + +OSSL_SERIALIZER_CTX_set_params() returns 1 if all recognised +parameters were valid, or 0 if one of them was invalid or caused some +other failure in the implementation. + +=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/doc/man7/openssl-core.h.pod b/doc/man7/openssl-core.h.pod index b5323e9d21..28307a97d4 100644 --- a/doc/man7/openssl-core.h.pod +++ b/doc/man7/openssl-core.h.pod @@ -76,7 +76,7 @@ B is further described in L =item B -This is a function type for a generic callback function: +This is a function type for a generic feedback callback function: typedef int (OSSL_CALLBACK)(const OSSL_PARAM params[], void *arg); @@ -86,6 +86,27 @@ expected to build an B array of data it wants or is expected to pass back, and pass that as I, as well as the caller data pointer it received, as I. +=item B + +This is a function type for a generic pass phrase callback function: + + typedef int (OSSL_PASSPHRASE_CALLBACK)(char *pass, size_t pass_size, + size_t *pass_len, + const OSSL_PARAM params[], + void *arg); + +This callback can be used to prompt the user for a passphrase. When +calling it, a buffer to store the pass phrase needs to be given with +I, and its size with I. The length of the prompted +pass phrase will be given back in I<*pass_len>. + +Additional parameters can be passed with the B array +I. + +A function that takes a pointer of this type should also take a +pointer to caller data, which should be passed as I to this +callback. + =back =head1 SEE ALSO diff --git a/doc/man7/provider-serializer.pod b/doc/man7/provider-serializer.pod new file mode 100644 index 0000000000..e43e293d60 --- /dev/null +++ b/doc/man7/provider-serializer.pod @@ -0,0 +1,227 @@ +=pod + +=head1 NAME + +provider-serializer - The SERIALIZER library E-E provider functions + +=head1 SYNOPSIS + +=begin comment + +Future development will also include deserializing functions. + +=end comment + + #include + + /* + * None of these are actual functions, but are displayed like this for + * the function signatures for functions that are offered as function + * pointers in OSSL_DISPATCH arrays. + */ + + /* Functions to construct / destruct / manipulate the serializer context */ + void *OP_serializer_newctx(void *provctx); + void OP_serializer_freectx(void *ctx); + int OP_serializer_set_ctx_params(void *ctx, const OSSL_PARAM params[]); + const OSSL_PARAM *OP_serializer_settable_ctx_params(void) + + /* Functions to serialize object data */ + int OP_serializer_serialize_data(void *ctx, const OSSL_PARAM *data, + BIO *out, + OSSL_PASSPHRASE_CALLBACK *cb, + void *cbarg); + int OP_serializer_serialize_object(void *ctx, void *obj, BIO *out, + OSSL_PASSPHRASE_CALLBACK *cb, + void *cbarg); + +=head1 DESCRIPTION + +The SERIALIZER is a generic method to serialize any set of object data +in L array form, or any provider side object into +serialized form, and write it to the given BIO. If the caller wants +to get the serialized stream to memory, it should provide a +L. + +The serializer doesn't need to know more about the B pointer than +being able to pass it to the appropriate BIO upcalls (see +L). + +The serialization using the L array form allows a +serializer to be used for data that's been exported from another +provider, and thereby allow them to exist independently of each +other. + +The serialization using a provider side object can only be safely used +with provider data coming from the same provider, for example keys +with the L provider. + +All "functions" mentioned here are passed as function pointers between +F and the provider in B arrays via +B arrays that are returned by the provider's +provider_query_operation() function +(see L). + +All these "functions" have a corresponding function type definition +named B, and a helper function to retrieve the +function pointer from a B element named +B. +For example, the "function" OP_serializer_serialize_data() has these: + + typedef int + (OSSL_OP_serializer_serialize_data_fn)(void *provctx, + const OSSL_PARAM params[], + BIO *out); + static ossl_inline OSSL_OP_serializer_serialize_data_fn + OSSL_get_OP_serializer_serialize_data(const OSSL_DISPATCH *opf); + +B arrays are indexed by numbers that are provided as +macros in L, as follows: + + OP_serializer_newctx OSSL_FUNC_SERIALIZER_NEWCTX + OP_serializer_freectx OSSL_FUNC_SERIALIZER_FREECTX + OP_serializer_set_ctx_params OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS + OP_serializer_settable_ctx_params OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS + + OP_serializer_serialize_data OSSL_FUNC_SERIALIZER_SERIALIZE_DATA + OP_serializer_serialize_object OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT + +=head2 Names and properties + +The name of an implementation should match the type of object it +handles. For example, an implementation that serializes an RSA key +should be named accordingly. + +To be able to specify exactly what serialization format and what type +of data a serializer implementation is expected to handle, two +additional properties may be given: + +=over 4 + +=item format + +This property is used to specify what kind of output format the +implementation produces. Currently known formats are: + +=over 4 + +=item text + +An implementation with that format property value outputs human +readable text, making that implementation suitable for C<-text> output +in diverse L commands. + +=item pem + +An implementation with that format property value outputs PEM +formatted data. + +=item der + +An implementation with that format property value outputs DER +formatted data. + +=back + +=item type + +With objects that have multiple purposes, this can be used to specify +the purpose type. The currently known use cases are asymmetric keys +and domain parameters, where the type can be one of: + +=over 4 + +=item private + +An implementation with that format property value outputs a private +key. + +=item public + +An implementation with that format property value outputs a public +key. + +=item domainparams + +An implementation with that format property value outputs domain +parameters. + +=back + +=back + +The possible values of both these properties is open ended. A +provider may very well specify other formats that libcrypto doesn't +know anything about. + +=head2 Context functions + +OP_serializer_newctx() returns a context to be used with the rest of +the functions. + +OP_serializer_freectx() frees the given I, if it was created by +OP_serializer_newctx(). + +OP_serializer_set_ctx_params() sets context data according to +parameters from I that it recognises. Unrecognised parameters +should be ignored. + +OP_serializer_settable_ctx_params() returns a constant B +array describing the parameters that OP_serializer_set_ctx_params() +can handle. + +See L for further details on the parameters structure used +by OP_serializer_set_ctx_params() and OP_serializer_settable_ctx_params(). + +=head2 Serializing functions + +=for comment There will be a "Deserializing functions" title as well + +OP_serializer_serialize_data() should take an array of B, +I, and if it contains the data necessary for the object type +that the implementation handles, it should output the object in +serialized form to the B. + +OP_serializer_serialize_object() should take a pointer to an object +that it knows intimately, and output that object in serialized form to +the B. The caller I ensure that this function is called +with a pointer that the provider of this function is familiar with. +It is not suitable to use with object pointers coming from other +providers. + +Both serialization functions also take an B +function pointer along with a pointer to application data I, +which should be used when a pass phrase prompt is needed. + +=head1 RETURN VALUES + +OP_serializer_newctx() returns a pointer to a context, or NULL on +failure. + +OP_serializer_set_ctx_params() returns 1, unless a recognised +parameters was invalid or caused an error, for which 0 is returned. + +OP_serializer_settable_ctx_params() returns a pointer to an array of +constant B elements. + +OP_serializer_serialize_data() and OP_serializer_serialize_object() +return 1 on success, or 0 on failure. + +=head1 SEE ALSO + +L + +=head1 HISTORY + +The SERIALIZER interface was introduced 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/doc/man7/provider.pod b/doc/man7/provider.pod index c3405b6f22..b6c5e49f50 100644 --- a/doc/man7/provider.pod +++ b/doc/man7/provider.pod @@ -147,6 +147,14 @@ The number for this operation is B. The functions the provider can offer are described in L +=item Serialization + +In the OpenSSL libraries, the corresponding method object is +B. +The number for this operation is B. +The functions the provider can offer are described in +L + =back =head2 Fetching algorithms diff --git a/include/crypto/serializer.h b/include/crypto/serializer.h new file mode 100644 index 0000000000..c40788f78b --- /dev/null +++ b/include/crypto/serializer.h @@ -0,0 +1,13 @@ +/* + * 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 + +OSSL_SERIALIZER *ossl_serializer_fetch_by_number(OPENSSL_CTX *libctx, int id, + const char *properties); diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h index 7428453a35..8be3861d4f 100644 --- a/include/internal/cryptlib.h +++ b/include/internal/cryptlib.h @@ -154,7 +154,8 @@ typedef struct ossl_ex_data_global_st { # define OPENSSL_CTX_RAND_CRNGT_INDEX 7 # define OPENSSL_CTX_THREAD_EVENT_HANDLER_INDEX 8 # define OPENSSL_CTX_FIPS_PROV_INDEX 9 -# define OPENSSL_CTX_MAX_INDEXES 10 +# define OPENSSL_CTX_SERIALIZER_STORE_INDEX 10 +# define OPENSSL_CTX_MAX_INDEXES 11 typedef struct openssl_ctx_method { void *(*new_func)(OPENSSL_CTX *ctx); diff --git a/include/openssl/core.h b/include/openssl/core.h index bed580c811..5959a31880 100644 --- a/include/openssl/core.h +++ b/include/openssl/core.h @@ -202,6 +202,16 @@ extern OSSL_provider_init_fn OSSL_provider_init; */ typedef int (OSSL_CALLBACK)(const OSSL_PARAM params[], void *arg); +/* + * Passphrase callback function signature + * + * This is similar to the generic callback function above, but adds a + * result parameter. + */ +typedef int (OSSL_PASSPHRASE_CALLBACK)(char *pass, size_t pass_size, + size_t *pass_len, + const OSSL_PARAM params[], void *arg); + # ifdef __cplusplus } # endif diff --git a/include/openssl/core_numbers.h b/include/openssl/core_numbers.h index d07ef556d9..889362bc37 100644 --- a/include/openssl/core_numbers.h +++ b/include/openssl/core_numbers.h @@ -160,8 +160,10 @@ OSSL_CORE_MAKE_FUNC(const OSSL_ITEM *,provider_get_reason_strings, # define OSSL_OP_KEYEXCH 11 # define OSSL_OP_SIGNATURE 12 # define OSSL_OP_ASYM_CIPHER 13 +/* New section for non-EVP operations */ +# define OSSL_OP_SERIALIZER 20 /* Highest known operation number */ -# define OSSL_OP__HIGHEST 13 +# define OSSL_OP__HIGHEST 20 /* Digests */ @@ -534,6 +536,27 @@ OSSL_CORE_MAKE_FUNC(int, OP_asym_cipher_set_ctx_params, OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_asym_cipher_settable_ctx_params, (void)) +/* Serializers */ +# define OSSL_FUNC_SERIALIZER_NEWCTX 1 +# define OSSL_FUNC_SERIALIZER_FREECTX 2 +# define OSSL_FUNC_SERIALIZER_SET_CTX_PARAMS 3 +# define OSSL_FUNC_SERIALIZER_SETTABLE_CTX_PARAMS 4 +# define OSSL_FUNC_SERIALIZER_SERIALIZE_DATA 10 +# define OSSL_FUNC_SERIALIZER_SERIALIZE_OBJECT 11 +OSSL_CORE_MAKE_FUNC(void *, OP_serializer_newctx, (void *provctx)) +OSSL_CORE_MAKE_FUNC(void, OP_serializer_freectx, (void *ctx)) +OSSL_CORE_MAKE_FUNC(int, OP_serializer_set_ctx_params, + (void *ctx, const OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, OP_serializer_settable_ctx_params, + (void)) + +OSSL_CORE_MAKE_FUNC(int, OP_serializer_serialize_data, + (void *ctx, const OSSL_PARAM[], BIO *out, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)) +OSSL_CORE_MAKE_FUNC(int, OP_serializer_serialize_object, + (void *ctx, void *obj, BIO *out, + OSSL_PASSPHRASE_CALLBACK *cb, void *cbarg)) + # ifdef __cplusplus } # endif diff --git a/include/openssl/err.h b/include/openssl/err.h index 9244bb84b1..37f3cc1d86 100644 --- a/include/openssl/err.h +++ b/include/openssl/err.h @@ -112,6 +112,7 @@ struct err_state_st { # define ERR_LIB_CRMF 56 # define ERR_LIB_PROV 57 # define ERR_LIB_CMP 58 +# define ERR_LIB_OSSL_SERIALIZER 59 # define ERR_LIB_USER 128 @@ -231,6 +232,7 @@ struct err_state_st { # define ERR_R_INIT_FAIL (6|ERR_R_FATAL) # define ERR_R_PASSED_INVALID_ARGUMENT (7) # define ERR_R_OPERATION_FAIL (8|ERR_R_FATAL) +# define ERR_R_INVALID_PROVIDER_FUNCTIONS (9|ERR_R_FATAL) /* * 99 is the maximum possible ERR_R_... code, higher values are reserved for diff --git a/include/openssl/pem.h b/include/openssl/pem.h index ef79c1a46a..35d01544ba 100644 --- a/include/openssl/pem.h +++ b/include/openssl/pem.h @@ -261,7 +261,6 @@ extern "C" { # define DECLARE_PEM_rw_cb(name, type) \ DECLARE_PEM_read(name, type) \ DECLARE_PEM_write_cb(name, type) -typedef int pem_password_cb (char *buf, int size, int rwflag, void *userdata); int PEM_get_EVP_CIPHER_INFO(char *header, EVP_CIPHER_INFO *cipher); int PEM_do_header(EVP_CIPHER_INFO *cipher, unsigned char *data, long *len, diff --git a/include/openssl/serializer.h b/include/openssl/serializer.h new file mode 100644 index 0000000000..79f8abecb5 --- /dev/null +++ b/include/openssl/serializer.h @@ -0,0 +1,59 @@ +/* + * 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 + */ + +#ifndef OPENSSL_SERIALIZER_H +# define OPENSSL_SERIALIZER_H +# pragma once + +# include + +# ifndef OPENSSL_NO_STDIO +# include +# endif +# include +# include +# include +# include + +# ifdef __cplusplus +extern "C" { +# endif + +OSSL_SERIALIZER *OSSL_SERIALIZER_fetch(OPENSSL_CTX *libctx, + const char *name, + const char *properties); +int OSSL_SERIALIZER_up_ref(OSSL_SERIALIZER *ser); +void OSSL_SERIALIZER_free(OSSL_SERIALIZER *ser); + +const OSSL_PROVIDER *OSSL_SERIALIZER_provider(const OSSL_SERIALIZER *ser); +const char *OSSL_SERIALIZER_properties(const OSSL_SERIALIZER *ser); +int OSSL_SERIALIZER_number(const OSSL_SERIALIZER *ser); +int OSSL_SERIALIZER_is_a(const OSSL_SERIALIZER *ser, + const char *name); + +void OSSL_SERIALIZER_do_all_provided(OPENSSL_CTX *libctx, + void (*fn)(OSSL_SERIALIZER *ser, + void *arg), + void *arg); +void OSSL_SERIALIZER_names_do_all(const OSSL_SERIALIZER *ser, + void (*fn)(const char *name, void *data), + void *data); + +const OSSL_PARAM *OSSL_SERIALIZER_settable_ctx_params(OSSL_SERIALIZER *ser); +OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new(OSSL_SERIALIZER *ser); +const OSSL_SERIALIZER * +OSSL_SERIALIZER_CTX_get_serializer(OSSL_SERIALIZER_CTX *ctx); +int OSSL_SERIALIZER_CTX_set_params(OSSL_SERIALIZER_CTX *ctx, + const OSSL_PARAM params[]); +void OSSL_SERIALIZER_CTX_free(OSSL_SERIALIZER_CTX *ctx); + +# ifdef __cplusplus +} +# endif +#endif diff --git a/include/openssl/types.h b/include/openssl/types.h index 151e3f1713..b8450ce709 100644 --- a/include/openssl/types.h +++ b/include/openssl/types.h @@ -203,6 +203,11 @@ typedef struct ossl_item_st OSSL_ITEM; typedef struct ossl_algorithm_st OSSL_ALGORITHM; typedef struct ossl_param_st OSSL_PARAM; +typedef int pem_password_cb (char *buf, int size, int rwflag, void *userdata); + +typedef struct ossl_serializer_st OSSL_SERIALIZER; +typedef struct ossl_serializer_ctx_st OSSL_SERIALIZER_CTX; + #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L && \ defined(INTMAX_MAX) && defined(UINTMAX_MAX) typedef intmax_t ossl_intmax_t; diff --git a/util/libcrypto.num b/util/libcrypto.num index 0c2dc2c40b..5f246571c4 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -4878,3 +4878,17 @@ EVP_PKEY_meth_set_digestsign ? 3_0_0 EXIST::FUNCTION: EVP_PKEY_meth_set_digestverify ? 3_0_0 EXIST::FUNCTION: EVP_PKEY_meth_get_digestsign ? 3_0_0 EXIST::FUNCTION: EVP_PKEY_meth_get_digestverify ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_up_ref ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_free ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_fetch ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_number ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_is_a ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_provider ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_do_all_provided ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_names_do_all ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_settable_ctx_params ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_CTX_new ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_CTX_get_serializer ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_CTX_set_params ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_CTX_free ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_properties ? 3_0_0 EXIST::FUNCTION: diff --git a/util/other.syms b/util/other.syms index c5575ac151..080244c54e 100644 --- a/util/other.syms +++ b/util/other.syms @@ -36,6 +36,8 @@ OPENSSL_CTX datatype NAMING_AUTHORITY datatype OSSL_PARAM datatype OSSL_PROVIDER datatype +OSSL_SERIALIZER datatype +OSSL_SERIALIZER_CTX datatype OSSL_STORE_CTX datatype OSSL_STORE_INFO datatype OSSL_STORE_LOADER datatype