From: Richard Levitte Date: Mon, 18 Nov 2019 00:34:26 +0000 (+0100) Subject: SERIALIZER: add support for serializing EVP_PKEYs X-Git-Tag: openssl-3.0.0-alpha1~882 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=866234ac35e665f20c646059b1d92c5e9eb0c7ab;p=oweals%2Fopenssl.git SERIALIZER: add support for serializing EVP_PKEYs The following public functions is added: - OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() - OSSL_SERIALIZER_CTX_set_cipher() - OSSL_SERIALIZER_CTX_set_passphrase() - OSSL_SERIALIZER_CTX_set_passphrase_cb() - OSSL_SERIALIZER_CTX_set_passphrase_ui() OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() selects a suitable serializer for the given EVP_PKEY, and sets up the OSSL_SERIALIZER_CTX to function together with OSSL_SERIALIZER_to_bio() and OSSL_SERIALIZER_to_fp(). OSSL_SERIALIZER_CTX_set_cipher() indicates what cipher should be used to produce an encrypted serialization of the EVP_PKEY. This is passed directly to the provider using OSSL_SERIALIZER_CTX_set_params(). OSSL_SERIALIZER_CTX_set_passphrase() can be used to set a pass phrase to be used for the encryption. This is passed directly to the provider using OSSL_SERIALIZER_CTX_set_params(). OSSL_SERIALIZER_CTX_set_passphrase_cb() and OSSL_SERIALIZER_CTX_set_passphrase_ui() sets up a callback to be used to prompt for a passphrase. This is stored in the context, and is called via an internal intermediary at the time of serialization. Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/10394) --- diff --git a/crypto/err/err.c b/crypto/err/err.c index 0fb46bef4d..e77cfe83cf 100644 --- a/crypto/err/err.c +++ b/crypto/err/err.c @@ -113,6 +113,7 @@ static ERR_STRING_DATA ERR_str_reasons[] = { {ERR_R_INIT_FAIL, "init fail"}, {ERR_R_OPERATION_FAIL, "operation fail"}, {ERR_R_INVALID_PROVIDER_FUNCTIONS, "invalid provider functions"}, + {ERR_R_INTERRUPTED_OR_CANCELLED, "interrupted or cancelled"}, {0, NULL}, }; diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index b103f8605b..8febc5c210 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -2550,6 +2550,7 @@ OCSP_R_STATUS_TOO_OLD:127:status too old OCSP_R_UNKNOWN_MESSAGE_DIGEST:119:unknown message digest OCSP_R_UNKNOWN_NID:120:unknown nid OCSP_R_UNSUPPORTED_REQUESTORNAME_TYPE:129:unsupported requestorname type +OSSL_SERIALIZER_R_INCORRECT_PROPERTY_QUERY:100:incorrect property query OSSL_STORE_R_AMBIGUOUS_CONTENT_TYPE:107:ambiguous content type OSSL_STORE_R_BAD_PASSWORD_READ:115:bad password read OSSL_STORE_R_ERROR_VERIFYING_PKCS12_MAC:113:error verifying pkcs12 mac diff --git a/crypto/serializer/build.info b/crypto/serializer/build.info index 1a35152586..551319ed59 100644 --- a/crypto/serializer/build.info +++ b/crypto/serializer/build.info @@ -1 +1,2 @@ -SOURCE[../../libcrypto]=serializer_meth.c serializer_lib.c +SOURCE[../../libcrypto]=serializer_meth.c serializer_lib.c serializer_pkey.c \ + serializer_err.c diff --git a/crypto/serializer/serializer_err.c b/crypto/serializer/serializer_err.c new file mode 100644 index 0000000000..0f0fc5fa80 --- /dev/null +++ b/crypto/serializer/serializer_err.c @@ -0,0 +1,31 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-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 + +#ifndef OPENSSL_NO_ERR + +static const ERR_STRING_DATA OSSL_SERIALIZER_str_reasons[] = { + {ERR_PACK(ERR_LIB_OSSL_SERIALIZER, 0, OSSL_SERIALIZER_R_INCORRECT_PROPERTY_QUERY), + "incorrect property query"}, + {0, NULL} +}; + +#endif + +int ERR_load_OSSL_SERIALIZER_strings(void) +{ +#ifndef OPENSSL_NO_ERR + if (ERR_reason_error_string(OSSL_SERIALIZER_str_reasons[0].error) == NULL) + ERR_load_strings_const(OSSL_SERIALIZER_str_reasons); +#endif + return 1; +} diff --git a/crypto/serializer/serializer_local.h b/crypto/serializer/serializer_local.h index dd0eb85414..9ee0eb8244 100644 --- a/crypto/serializer/serializer_local.h +++ b/crypto/serializer/serializer_local.h @@ -38,4 +38,13 @@ struct ossl_serializer_ctx_st { */ const void *object; int (*do_output)(OSSL_SERIALIZER_CTX *ctx, BIO *out); + + /* For any function that needs a passphrase reader */ + const UI_METHOD *ui_method; + void *ui_data; + /* + * if caller used OSSL_SERIALIZER_CTX_set_passphrase_cb(), we need + * intermediary storage. + */ + UI_METHOD *allocated_ui_method; }; diff --git a/crypto/serializer/serializer_meth.c b/crypto/serializer/serializer_meth.c index 103188f93a..145d58921f 100644 --- a/crypto/serializer/serializer_meth.c +++ b/crypto/serializer/serializer_meth.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "internal/core.h" #include "internal/namemap.h" #include "internal/property.h" @@ -509,6 +510,7 @@ void OSSL_SERIALIZER_CTX_free(OSSL_SERIALIZER_CTX *ctx) if (ctx->ser != NULL && ctx->ser->freectx != NULL) ctx->ser->freectx(ctx->serctx); OSSL_SERIALIZER_free(ctx->ser); + UI_destroy_method(ctx->allocated_ui_method); OPENSSL_free(ctx); } } diff --git a/crypto/serializer/serializer_pkey.c b/crypto/serializer/serializer_pkey.c new file mode 100644 index 0000000000..d3c3362f66 --- /dev/null +++ b/crypto/serializer/serializer_pkey.c @@ -0,0 +1,386 @@ +/* + * 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 +#include "internal/provider.h" +#include "internal/property.h" +#include "crypto/evp.h" +#include "serializer_local.h" + +int OSSL_SERIALIZER_CTX_set_cipher(OSSL_SERIALIZER_CTX *ctx, + const char *cipher_name, + const char *propquery) +{ + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END, OSSL_PARAM_END }; + + params[0] = + OSSL_PARAM_construct_utf8_string(OSSL_SERIALIZER_PARAM_CIPHER, + (void *)cipher_name, 0); + params[1] = + OSSL_PARAM_construct_utf8_string(OSSL_SERIALIZER_PARAM_PROPERTIES, + (void *)propquery, 0); + + return OSSL_SERIALIZER_CTX_set_params(ctx, params); +} + +int OSSL_SERIALIZER_CTX_set_passphrase(OSSL_SERIALIZER_CTX *ctx, + const unsigned char *kstr, + size_t klen) +{ + OSSL_PARAM params[] = { OSSL_PARAM_END, OSSL_PARAM_END }; + + params[0] = OSSL_PARAM_construct_octet_string(OSSL_SERIALIZER_PARAM_PASS, + (void *)kstr, klen); + + return OSSL_SERIALIZER_CTX_set_params(ctx, params); +} + +static void serializer_ctx_reset_passphrase_ui(OSSL_SERIALIZER_CTX *ctx) +{ + UI_destroy_method(ctx->allocated_ui_method); + ctx->allocated_ui_method = NULL; + ctx->ui_method = NULL; + ctx->ui_data = NULL; +} + +int OSSL_SERIALIZER_CTX_set_passphrase_ui(OSSL_SERIALIZER_CTX *ctx, + const UI_METHOD *ui_method, + void *ui_data) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + serializer_ctx_reset_passphrase_ui(ctx); + ctx->ui_method = ui_method; + ctx->ui_data = ui_data; + return 1; +} + +int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc, + pem_password_cb *cb, void *cbarg) +{ + if (!ossl_assert(ctx != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + serializer_ctx_reset_passphrase_ui(ctx); + if (cb == NULL) + return 1; + ctx->ui_method = + ctx->allocated_ui_method = UI_UTIL_wrap_read_pem_callback(cb, enc); + ctx->ui_data = cbarg; + + return ctx->ui_method != NULL; +} + +/* + * Support for OSSL_SERIALIZER_CTX_new_by_TYPE: + * finding a suitable serializer + */ + +struct selected_serializer_st { + OPENSSL_CTX *libctx; + const OSSL_PROVIDER *desired_provider; + const char *propquery; + + /* + * When selecting serializers, we need to check the intended use. + * This is governed by the |domainparams| flag in the EVP_PKEY, + * we must just make sure to filter on 'type=domainparams' accordingly. + */ + int want_domainparams; + + /* + * Serializers offer two functions, one that handles object data in + * the form of a OSSL_PARAM array, and one that directly handles a + * provider side object. The latter requires that the serializer + * is offered by the same provider that holds that object, but is + * more desirable because it usually provides faster serialization. + * + * When looking up possible serializers, we save the first that can + * handle an OSSL_PARAM array in |first|, and the first that can + * handle a provider side object in |desired|. + */ + OSSL_SERIALIZER *first; + OSSL_SERIALIZER *desired; +}; + +static void select_serializer(const char *name, void *data) +{ + struct selected_serializer_st *d = data; + OSSL_SERIALIZER *s = NULL; + OSSL_PROPERTY_LIST *check = + d->want_domainparams + ? ossl_parse_query(d->libctx, "type=domainparams") + : NULL; + + /* No need to look further if we already have the more desirable option */ + if (d->desired != NULL) + return; + + if ((s = OSSL_SERIALIZER_fetch(d->libctx, name, d->propquery)) != NULL) { + /* + * Extra check if domain parameters are explicitly specified: + * only accept serializers that have the "type=domainparams" + * property. + * + * For data that isn't marked as domain parameters, a domain + * parameters serializer is still acceptable, because a key + * may hold domain parameters too. + */ + if (d->want_domainparams) { + OSSL_PROPERTY_LIST *current_props = + ossl_parse_property(d->libctx, OSSL_SERIALIZER_properties(s)); + int check_cnt = ossl_property_match_count(check, current_props); + + if (check_cnt == 0) { + OSSL_SERIALIZER_free(s); + return; + } + } + + if (d->first == NULL && s->serialize_data != NULL) { + d->first = s; + } else if (OSSL_SERIALIZER_provider(s) == d->desired_provider + && s->serialize_object != NULL) { + OSSL_SERIALIZER_free(d->first); + d->first = NULL; + d->desired = s; + } else { + OSSL_SERIALIZER_free(s); + } + } +} + +/* + * Support for OSSL_SERIALIZER_CTX_new_by_TYPE and OSSL_SERIALIZER_to_bio: + * Passphrase callbacks + */ + +/* + * First, we define the generic passphrase function that supports both + * outgoing (with passphrase verify) and incoming (without passphrase verify) + * passphrase reading. + */ +static int serializer_passphrase(char *pass, size_t pass_size, + size_t *pass_len, int verify, + const OSSL_PARAM params[], void *arg) +{ + OSSL_SERIALIZER_CTX *ctx = arg; + const OSSL_PARAM *p; + const char *prompt_info = NULL; + char *prompt = NULL, *vpass = NULL; + int prompt_idx = -1, verify_idx = -1; + UI *ui = NULL; + int ret = 0; + + if (!ossl_assert(ctx != NULL && pass != NULL + && pass_size != 0 && pass_len != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if ((p = OSSL_PARAM_locate_const(params, + OSSL_PASSPHRASE_PARAM_INFO)) != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + prompt_info = p->data; + } + + if ((ui = UI_new()) == NULL) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE); + return 0; + } + + UI_set_method(ui, ctx->ui_method); + UI_add_user_data(ui, ctx->ui_data); + + /* Get an application constructed prompt */ + prompt = UI_construct_prompt(ui, "pass phrase", prompt_info); + if (prompt == NULL) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE); + goto end; + } + + prompt_idx = UI_add_input_string(ui, prompt, + UI_INPUT_FLAG_DEFAULT_PWD, + pass, 0, pass_size - 1) - 1; + if (prompt_idx < 0) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB); + goto end; + } + + if (verify) { + /* Get a buffer for verification prompt */ + vpass = OPENSSL_zalloc(pass_size); + if (vpass == NULL) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_MALLOC_FAILURE); + goto end; + } + verify_idx = UI_add_verify_string(ui, prompt, + UI_INPUT_FLAG_DEFAULT_PWD, + vpass, 0, pass_size - 1, + pass) - 1; + if (verify_idx < 0) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB); + goto end; + } + } + + switch (UI_process(ui)) { + case -2: + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_INTERRUPTED_OR_CANCELLED); + break; + case -1: + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_UI_LIB); + break; + default: + *pass_len = (size_t)UI_get_result_length(ui, prompt_idx); + ret = 1; + break; + } + + end: + OPENSSL_free(vpass); + OPENSSL_free(prompt); + UI_free(ui); + return ret; +} + +/* Ensure correct function definition for outgoing passphrase reader */ +static OSSL_PASSPHRASE_CALLBACK serializer_passphrase_out_cb; +static int serializer_passphrase_out_cb(char *pass, size_t pass_size, + size_t *pass_len, + const OSSL_PARAM params[], void *arg) +{ + return serializer_passphrase(pass, pass_size, pass_len, 1, params, arg); +} + +/* + * Support for OSSL_SERIALIZER_to_bio: + * writing callback for the OSSL_PARAM (the implementation doesn't have + * intimate knowledge of the provider side object) + */ + +struct serializer_write_data_st { + OSSL_SERIALIZER_CTX *ctx; + BIO *out; +}; + +static int serializer_write_cb(const OSSL_PARAM params[], void *arg) +{ + struct serializer_write_data_st *write_data = arg; + OSSL_SERIALIZER_CTX *ctx = write_data->ctx; + BIO *out = write_data->out; + + return ctx->ser->serialize_data(ctx->serctx, params, out, + serializer_passphrase_out_cb, ctx); +} + +/* + * Support for OSSL_SERIALIZER_to_bio: + * Perform the actual output. + */ + +static int serializer_EVP_PKEY_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out) +{ + const EVP_PKEY *pkey = ctx->object; + void *provdata = pkey->pkeys[0].provdata; + int domainparams = pkey->pkeys[0].domainparams; + EVP_KEYMGMT *keymgmt = pkey->pkeys[0].keymgmt; + + /* + * OSSL_SERIALIZER_CTX_new() creates a context, even when the + * serializer it's given is NULL. Callers can detect the lack + * of serializer with OSSL_SERIALIZER_CTX_get_serializer() and + * should take precautions, possibly call a fallback instead of + * OSSL_SERIALIZER_to_bio() / OSSL_SERIALIZER_to_fp(). If it's + * come this far, we return an error. + */ + if (ctx->ser == NULL) + return 0; + + if (ctx->ser->serialize_object == NULL) { + struct serializer_write_data_st write_data; + + write_data.ctx = ctx; + write_data.out = out; + + if (domainparams) + return evp_keymgmt_exportdomparams(keymgmt, provdata, + serializer_write_cb, + &write_data); + return evp_keymgmt_exportkey(keymgmt, provdata, + serializer_write_cb, &write_data); + } + + return ctx->ser->serialize_object(ctx->serctx, provdata, out, + serializer_passphrase_out_cb, ctx); +} + +/* + * OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() returns a ctx with no serializer if + * it couldn't find a suitable serializer. This allows a caller to detect if + * a suitable serializer was found, with OSSL_SERIALIZER_CTX_get_serializer(), + * and to use fallback methods if the result is NULL. + */ +OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey, + const char *propquery) +{ + OSSL_SERIALIZER_CTX *ctx = NULL; + OSSL_SERIALIZER *ser = NULL; + EVP_KEYMGMT *keymgmt = pkey->pkeys[0].keymgmt; + + if (!ossl_assert(pkey != NULL && propquery != NULL)) { + ERR_raise(ERR_LIB_OSSL_SERIALIZER, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + if (keymgmt != NULL) { + const OSSL_PROVIDER *desired_prov = EVP_KEYMGMT_provider(keymgmt); + OPENSSL_CTX *libctx = ossl_provider_library_context(desired_prov); + struct selected_serializer_st sel_data; + + memset(&sel_data, 0, sizeof(sel_data)); + sel_data.libctx = libctx; + sel_data.desired_provider = desired_prov; + sel_data.propquery = propquery; + sel_data.want_domainparams = pkey->pkeys[0].domainparams; + EVP_KEYMGMT_names_do_all(keymgmt, select_serializer, &sel_data); + + if (sel_data.desired != NULL) { + ser = sel_data.desired; + sel_data.desired = NULL; + } else if (sel_data.first != NULL) { + ser = sel_data.first; + sel_data.first = NULL; + } + OSSL_SERIALIZER_free(sel_data.first); + OSSL_SERIALIZER_free(sel_data.desired); + } + + ctx = OSSL_SERIALIZER_CTX_new(ser); /* refcnt(ser)++ */ + OSSL_SERIALIZER_free(ser); /* refcnt(ser)-- */ + + if (ctx != NULL) { + /* Setup for OSSL_SERIALIZE_to_bio() */ + ctx->object = pkey; + ctx->do_output = serializer_EVP_PKEY_to_bio; + } + + return ctx; +} + diff --git a/doc/man3/OSSL_SERIALIZER.pod b/doc/man3/OSSL_SERIALIZER.pod index bf6ef3431c..05b889bf60 100644 --- a/doc/man3/OSSL_SERIALIZER.pod +++ b/doc/man3/OSSL_SERIALIZER.pod @@ -111,7 +111,7 @@ OSSL_SERIALIZER_number() returns an integer. =head1 SEE ALSO L, L, L, -L +L, L =head1 HISTORY diff --git a/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod b/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod new file mode 100644 index 0000000000..caa9294bcc --- /dev/null +++ b/doc/man3/OSSL_SERIALIZER_CTX_new_by_EVP_PKEY.pod @@ -0,0 +1,134 @@ +=pod + +=head1 NAME + +OSSL_SERIALIZER_CTX_new_by_EVP_PKEY, +OSSL_SERIALIZER_CTX_set_cipher, +OSSL_SERIALIZER_CTX_set_passphrase, +OSSL_SERIALIZER_CTX_set_passphrase_cb, +OSSL_SERIALIZER_CTX_set_passphrase_ui, +OSSL_SERIALIZER_PUBKEY_TO_PEM_PQ, +OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ, +OSSL_SERIALIZER_Parameters_TO_PEM_PQ, +OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ, +OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ, +OSSL_SERIALIZER_Parameters_TO_TEXT_PQ +- Serializer routines to serialize EVP_PKEYs + +=head1 SYNOPSIS + + #include + + OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey, + const char *propquery); + + int OSSL_SERIALIZER_CTX_set_cipher(OSSL_SERIALIZER_CTX *ctx, + const char *cipher_name, + const char *propquery); + int OSSL_SERIALIZER_CTX_set_passphrase(OSSL_SERIALIZER_CTX *ctx, + const unsigned char *kstr, + size_t klen); + int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc, + pem_password_cb *cb, void *cbarg); + int OSSL_SERIALIZER_CTX_set_passphrase_ui(OSSL_SERIALIZER_CTX *ctx, + const UI_METHOD *ui_method, + void *ui_data); + + #define OSSL_SERIALIZER_PUBKEY_TO_PEM_PQ "format=pem,type=public" + #define OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ "format=pem,type=private" + #define OSSL_SERIALIZER_Parameters_TO_PEM_PQ "format=pem,type=domainparams" + + #define OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ "format=text,type=public" + #define OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ "format=text,type=private" + #define OSSL_SERIALIZER_Parameters_TO_TEXT_PQ "format=text,type=domainparams" + +=head1 DESCRIPTION + +OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() creates a B +with a suitable attached output routine for Bs. It will +search for a serializer implementation that matches the algorithm of +the B and the property query given with I. It +will prefer to find a serializer from the same provider as the key +data of the B itself, but failing that, it will choose the +first serializer that supplies a generic serializing function. + +If no suitable serializer was found, OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() +still creates a B, but with no associated +serializer (L returns NULL). +This helps the caller distinguish between an error when creating +the B, and the lack the serializer support and +act accordingly. + +OSSL_SERIALIZER_CTX_set_cipher() tells the implementation what cipher +should be used to encrypt serialized keys. The cipher is given by +name I. The interpretation of that I is +implementation dependent. The implementation may implement the digest +directly itself or by other implementations, or it may choose to fetch +it. If the implementation supports fetching the cipher, then it may +use I as properties to be queried for when fetching. +I may also be NULL, which will result in unencrypted +serialization. + +OSSL_SERIALIZER_CTX_set_passphrase() gives the implementation a +pass phrase to use when encrypting the serialized private key. +Alternatively, a pass phrase callback may be specified with the +following functions. + +OSSL_SERIALIZER_CTX_set_passphrase_cb() and +OSSL_SERIALIZER_CTX_set_passphrase_ui() sets up a callback method that +the implementation can use to prompt for a pass phrase. + +=for comment Note that the callback method is called indirectly, +through an internal B function. + +The macros B, +B, +B, +B, +B, +B are convenience macros with +property queries to serialize the B as a public key, private +key or parameters to B, or to text. + +=head1 RETURN VALUES + +OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() returns a pointer to a +B, or NULL if it couldn't be created. + +OSSL_SERIALIZER_CTX_set_cipher(), +OSSL_SERIALIZER_CTX_set_passphrase(), +OSSL_SERIALIZER_CTX_set_passphrase_cb(), and +OSSL_SERIALIZER_CTX_set_passphrase_ui() all return 1 on success, or 0 +on failure. + +=head1 NOTES + +Parts of the function and macro names are made to match already +existing OpenSSL names. + +B in OSSL_SERIALIZER_CTX_new_by_EVP_PKEY() matches the type +name, thus making for the naming pattern +B>() when new types are handled. + +B, B and B in the macro names match +the B> part of of B> functions as well +as B_bio> functions. + +=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/man7/provider-serializer.pod b/doc/man7/provider-serializer.pod index e43e293d60..b23a8b64e2 100644 --- a/doc/man7/provider-serializer.pod +++ b/doc/man7/provider-serializer.pod @@ -193,6 +193,55 @@ 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. +=head2 Serializer parameters + +Parameters currently recognised by built-in serializers are as +follows: + +=over 4 + +=item "cipher" (B) + +The name of the encryption cipher to be used when generating encrypted +serialization. This is used when serializing private keys, as well as +other objects that need protection. + +If this name is invalid for the serialization implementation, the +implementation should refuse to perform the serialization, i.e. +OP_serializer_serialize_data() and OP_serializer_serialize_object() +should return an error. + +=item "properties" (B) + +The properties to be queried when trying to fetch the algorithm given +with the "cipher" parameter. +This must be given together with the "cipher" parameter to be +considered valid. + +The serialization implementation isn't obligated to use this value. +However, it is recommended that implementations that do not handle +property strings return an error on receiving this parameter unless +its value NULL or the empty string. + +=item "passphrase" (B) + +A pass phrase provided by the application. When this is given, the +built-in serializers will not attempt to use the passphrase callback. + +=back + +Parameters currently recognised by the built-in pass phrase callback: + +=over 4 + +=item "info" (B) + +A string of information that will become part of the pass phrase +prompt. This could be used to give the user information on what kind +of object it's being prompted for. + +=back + =head1 RETURN VALUES OP_serializer_newctx() returns a pointer to a context, or NULL on diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h index 1e8b764fb4..053432e0f0 100644 --- a/include/openssl/core_names.h +++ b/include/openssl/core_names.h @@ -202,6 +202,17 @@ extern "C" { #define OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL "oaep-label" #define OSSL_ASYM_CIPHER_PARAM_OAEP_LABEL_LEN "oaep-label-len" +/* + * Serializer parameters + */ +/* The passphrase may be passed as a utf8 string or an octet string */ +#define OSSL_SERIALIZER_PARAM_CIPHER OSSL_ALG_PARAM_CIPHER +#define OSSL_SERIALIZER_PARAM_PROPERTIES OSSL_ALG_PARAM_PROPERTIES +#define OSSL_SERIALIZER_PARAM_PASS "passphrase" + +/* Passphrase callback parameters */ +#define OSSL_PASSPHRASE_PARAM_INFO "info" + # ifdef __cplusplus } # endif diff --git a/include/openssl/err.h b/include/openssl/err.h index 37f3cc1d86..96b60882f0 100644 --- a/include/openssl/err.h +++ b/include/openssl/err.h @@ -233,6 +233,7 @@ struct err_state_st { # 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) +# define ERR_R_INTERRUPTED_OR_CANCELLED (10) /* * 99 is the maximum possible ERR_R_... code, higher values are reserved for diff --git a/include/openssl/serializer.h b/include/openssl/serializer.h index 78b57d225c..2629a13ccd 100644 --- a/include/openssl/serializer.h +++ b/include/openssl/serializer.h @@ -18,6 +18,7 @@ # endif # include # include +# include # include # include @@ -53,12 +54,46 @@ int OSSL_SERIALIZER_CTX_set_params(OSSL_SERIALIZER_CTX *ctx, const OSSL_PARAM params[]); void OSSL_SERIALIZER_CTX_free(OSSL_SERIALIZER_CTX *ctx); +/* Utilities that help set specific parameters */ +int OSSL_SERIALIZER_CTX_set_cipher(OSSL_SERIALIZER_CTX *ctx, + const char *cipher_name, + const char *propquery); +int OSSL_SERIALIZER_CTX_set_passphrase(OSSL_SERIALIZER_CTX *ctx, + const unsigned char *kstr, + size_t klen); +int OSSL_SERIALIZER_CTX_set_passphrase_cb(OSSL_SERIALIZER_CTX *ctx, int enc, + pem_password_cb *cb, void *cbarg); +int OSSL_SERIALIZER_CTX_set_passphrase_ui(OSSL_SERIALIZER_CTX *ctx, + const UI_METHOD *ui_method, + void *ui_data); + /* Utilities to output the object to serialize */ int OSSL_SERIALIZER_to_bio(OSSL_SERIALIZER_CTX *ctx, BIO *out); #ifndef OPENSSL_NO_STDIO int OSSL_SERIALIZER_to_fp(OSSL_SERIALIZER_CTX *ctx, FILE *fp); #endif +/* + * Create the OSSL_SERIALIZER_CTX with an associated type. This will perform + * an implicit OSSL_SERIALIZER_fetch(), suitable for the object of that type. + * This is more useful than calling OSSL_SERIALIZER_CTX_new(). + */ +OSSL_SERIALIZER_CTX *OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(const EVP_PKEY *pkey, + const char *propquery); + +/* + * These macros define the last argument to pass to + * OSSL_SERIALIZER_CTX_new_by_TYPE(). + */ +# define OSSL_SERIALIZER_PUBKEY_TO_PEM_PQ "format=pem,type=public" +# define OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ "format=pem,type=private" +# define OSSL_SERIALIZER_Parameters_TO_PEM_PQ "format=pem,type=domainparams" + +/* Corresponding macros for text output */ +# define OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ "format=text,type=public" +# define OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ "format=text,type=private" +# define OSSL_SERIALIZER_Parameters_TO_TEXT_PQ "format=text,type=domainparams" + # ifdef __cplusplus } # endif diff --git a/include/openssl/serializererr.h b/include/openssl/serializererr.h new file mode 100644 index 0000000000..4eff9deab6 --- /dev/null +++ b/include/openssl/serializererr.h @@ -0,0 +1,34 @@ +/* + * Generated by util/mkerr.pl DO NOT EDIT + * Copyright 1995-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_OSSL_SERIALIZERERR_H +# define OPENSSL_OSSL_SERIALIZERERR_H + +# include +# include + + +# ifdef __cplusplus +extern "C" +# endif +int ERR_load_OSSL_SERIALIZER_strings(void); + +/* + * OSSL_SERIALIZER function codes. + */ +# ifndef OPENSSL_NO_DEPRECATED_3_0 +# endif + +/* + * OSSL_SERIALIZER reason codes. + */ +# define OSSL_SERIALIZER_R_INCORRECT_PROPERTY_QUERY 100 + +#endif diff --git a/util/libcrypto.num b/util/libcrypto.num index cc94d580cd..d83f675f53 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -4894,3 +4894,9 @@ OSSL_SERIALIZER_CTX_free ? 3_0_0 EXIST::FUNCTION: OSSL_SERIALIZER_properties ? 3_0_0 EXIST::FUNCTION: OSSL_SERIALIZER_to_bio ? 3_0_0 EXIST::FUNCTION: OSSL_SERIALIZER_to_fp ? 3_0_0 EXIST::FUNCTION:STDIO +OSSL_SERIALIZER_CTX_new_by_EVP_PKEY ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_CTX_set_cipher ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_CTX_set_passphrase ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_CTX_set_passphrase_cb ? 3_0_0 EXIST::FUNCTION: +OSSL_SERIALIZER_CTX_set_passphrase_ui ? 3_0_0 EXIST::FUNCTION: +ERR_load_OSSL_SERIALIZER_strings ? 3_0_0 EXIST::FUNCTION: diff --git a/util/missingcrypto.txt b/util/missingcrypto.txt index 00b76abae5..6de82cfc21 100644 --- a/util/missingcrypto.txt +++ b/util/missingcrypto.txt @@ -455,6 +455,7 @@ ERR_load_PKCS12_strings ERR_load_PKCS7_strings ERR_load_RAND_strings ERR_load_RSA_strings +ERR_load_OSSL_SERIALIZER_strings ERR_load_TS_strings ERR_load_UI_strings ERR_load_X509V3_strings diff --git a/util/other.syms b/util/other.syms index 080244c54e..e07471f9ab 100644 --- a/util/other.syms +++ b/util/other.syms @@ -370,6 +370,12 @@ OSSL_PARAM_utf8_string define OSSL_PARAM_get_TYPE generic OSSL_PARAM_END define OSSL_PARAM_set_TYPE generic +OSSL_SERIALIZER_PUBKEY_TO_PEM_PQ define +OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ define +OSSL_SERIALIZER_Parameters_TO_PEM_PQ define +OSSL_SERIALIZER_PUBKEY_TO_TEXT_PQ define +OSSL_SERIALIZER_PrivateKey_TO_TEXT_PQ define +OSSL_SERIALIZER_Parameters_TO_TEXT_PQ define PEM_FLAG_EAY_COMPATIBLE define PEM_FLAG_ONLY_B64 define PEM_FLAG_SECURE define