diff --git a/crypto/evp/build.info b/crypto/evp/build.info index 36fac11683..7f1459a15c 100644 --- a/crypto/evp/build.info +++ b/crypto/evp/build.info @@ -2,7 +2,7 @@ LIBS=../../libcrypto $COMMON=digest.c evp_enc.c evp_lib.c evp_fetch.c cmeth_lib.c evp_utils.c \ mac_lib.c mac_meth.c keymgmt_meth.c keymgmt_lib.c kdf_lib.c kdf_meth.c \ m_sigver.c pmeth_lib.c signature.c p_lib.c pmeth_gn.c exchange.c \ - pmeth_check.c evp_rand.c asymcipher.c + pmeth_check.c evp_rand.c asymcipher.c kem.c SOURCE[../../libcrypto]=$COMMON\ encode.c evp_key.c evp_cnf.c \ diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h index e7f7643d83..3268aa4109 100644 --- a/crypto/evp/evp_local.h +++ b/crypto/evp/evp_local.h @@ -181,6 +181,25 @@ struct evp_asym_cipher_st { OSSL_FUNC_asym_cipher_settable_ctx_params_fn *settable_ctx_params; } /* EVP_ASYM_CIPHER */; +struct evp_kem_st { + int name_id; + OSSL_PROVIDER *prov; + CRYPTO_REF_COUNT refcnt; + CRYPTO_RWLOCK *lock; + + OSSL_FUNC_kem_newctx_fn *newctx; + OSSL_FUNC_kem_encapsulate_init_fn *encapsulate_init; + OSSL_FUNC_kem_encapsulate_fn *encapsulate; + OSSL_FUNC_kem_decapsulate_init_fn *decapsulate_init; + OSSL_FUNC_kem_decapsulate_fn *decapsulate; + OSSL_FUNC_kem_freectx_fn *freectx; + OSSL_FUNC_kem_dupctx_fn *dupctx; + OSSL_FUNC_kem_get_ctx_params_fn *get_ctx_params; + OSSL_FUNC_kem_gettable_ctx_params_fn *gettable_ctx_params; + OSSL_FUNC_kem_set_ctx_params_fn *set_ctx_params; + OSSL_FUNC_kem_settable_ctx_params_fn *settable_ctx_params; +} /* EVP_KEM */; + int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass, int passlen, ASN1_TYPE *param, const EVP_CIPHER *c, const EVP_MD *md, diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c new file mode 100644 index 0000000000..6f0424075a --- /dev/null +++ b/crypto/evp/kem.c @@ -0,0 +1,359 @@ +/* + * Copyright 2020 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 "crypto/evp.h" +#include "internal/provider.h" +#include "evp_local.h" + +static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation) +{ + int ret = 0; + EVP_KEM *kem = NULL; + EVP_KEYMGMT *tmp_keymgmt = NULL; + void *provkey = NULL; + const char *supported_kem = NULL; + + if (ctx == NULL || ctx->keytype == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); + return 0; + } + + evp_pkey_ctx_free_old_ops(ctx); + ctx->operation = operation; + + /* + * Ensure that the key is provided, either natively, or as a cached export. + */ + tmp_keymgmt = ctx->keymgmt; + provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx, + &tmp_keymgmt, ctx->propquery); + if (provkey == NULL + || !EVP_KEYMGMT_up_ref(tmp_keymgmt)) { + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); + goto err; + } + EVP_KEYMGMT_free(ctx->keymgmt); + ctx->keymgmt = tmp_keymgmt; + + if (ctx->keymgmt->query_operation_name != NULL) + supported_kem = ctx->keymgmt->query_operation_name(OSSL_OP_KEM); + + /* + * If we didn't get a supported kem, assume there is one with the + * same name as the key type. + */ + if (supported_kem == NULL) + supported_kem = ctx->keytype; + + kem = EVP_KEM_fetch(ctx->libctx, supported_kem, ctx->propquery); + if (kem == NULL + || (EVP_KEYMGMT_provider(ctx->keymgmt) != EVP_KEM_provider(kem))) { + ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + ret = -2; + goto err; + } + + ctx->op.encap.kem = kem; + ctx->op.encap.kemprovctx = kem->newctx(ossl_provider_ctx(kem->prov)); + if (ctx->op.encap.kemprovctx == NULL) { + /* The provider key can stay in the cache */ + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); + goto err; + } + + switch (operation) { + case EVP_PKEY_OP_ENCAPSULATE: + if (kem->encapsulate_init == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + ret = -2; + goto err; + } + ret = kem->encapsulate_init(ctx->op.encap.kemprovctx, provkey); + break; + case EVP_PKEY_OP_DECAPSULATE: + if (kem->decapsulate_init == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + ret = -2; + goto err; + } + ret = kem->decapsulate_init(ctx->op.encap.kemprovctx, provkey); + break; + default: + ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR); + goto err; + } + + if (ret > 0) + return 1; + err: + if (ret <= 0) { + evp_pkey_ctx_free_old_ops(ctx); + ctx->operation = EVP_PKEY_OP_UNDEFINED; + } + return ret; +} + +int EVP_PKEY_encapsulate_init(EVP_PKEY_CTX *ctx) +{ + return evp_kem_init(ctx, EVP_PKEY_OP_ENCAPSULATE); +} + +int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx, + unsigned char *out, size_t *outlen, + unsigned char *secret, size_t *secretlen) +{ + if (ctx == NULL) + return 0; + + if (ctx->operation != EVP_PKEY_OP_ENCAPSULATE) { + EVPerr(0, EVP_R_OPERATON_NOT_INITIALIZED); + return -1; + } + + if (ctx->op.encap.kemprovctx == NULL) { + EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + return -2; + } + + if (out != NULL && secret == NULL) + return 0; + + return ctx->op.encap.kem->encapsulate(ctx->op.encap.kemprovctx, + out, outlen, secret, secretlen); +} + +int EVP_PKEY_decapsulate_init(EVP_PKEY_CTX *ctx) +{ + return evp_kem_init(ctx, EVP_PKEY_OP_DECAPSULATE); +} + +int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx, + unsigned char *secret, size_t *secretlen, + const unsigned char *in, size_t inlen) +{ + if (ctx == NULL + || (in == NULL || inlen == 0) + || (secret == NULL && secretlen == NULL)) + return 0; + + if (ctx->operation != EVP_PKEY_OP_DECAPSULATE) { + EVPerr(0, EVP_R_OPERATON_NOT_INITIALIZED); + return -1; + } + + if (ctx->op.encap.kemprovctx == NULL) { + EVPerr(0, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + return -2; + } + return ctx->op.encap.kem->decapsulate(ctx->op.encap.kemprovctx, + secret, secretlen, in, inlen); +} + +static EVP_KEM *evp_kem_new(OSSL_PROVIDER *prov) +{ + EVP_KEM *kem = OPENSSL_zalloc(sizeof(EVP_KEM)); + + if (kem == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE); + return NULL; + } + + kem->lock = CRYPTO_THREAD_lock_new(); + if (kem->lock == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE); + OPENSSL_free(kem); + return NULL; + } + kem->prov = prov; + ossl_provider_up_ref(prov); + kem->refcnt = 1; + + return kem; +} + +static void *evp_kem_from_dispatch(int name_id, const OSSL_DISPATCH *fns, + OSSL_PROVIDER *prov) +{ + EVP_KEM *kem = NULL; + int ctxfncnt = 0, encfncnt = 0, decfncnt = 0; + int gparamfncnt = 0, sparamfncnt = 0; + + if ((kem = evp_kem_new(prov)) == NULL) { + ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE); + goto err; + } + + kem->name_id = name_id; + + for (; fns->function_id != 0; fns++) { + switch (fns->function_id) { + case OSSL_FUNC_KEM_NEWCTX: + if (kem->newctx != NULL) + break; + kem->newctx = OSSL_FUNC_kem_newctx(fns); + ctxfncnt++; + break; + case OSSL_FUNC_KEM_ENCAPSULATE_INIT: + if (kem->encapsulate_init != NULL) + break; + kem->encapsulate_init = OSSL_FUNC_kem_encapsulate_init(fns); + encfncnt++; + break; + case OSSL_FUNC_KEM_ENCAPSULATE: + if (kem->encapsulate != NULL) + break; + kem->encapsulate = OSSL_FUNC_kem_encapsulate(fns); + encfncnt++; + break; + case OSSL_FUNC_KEM_DECAPSULATE_INIT: + if (kem->decapsulate_init != NULL) + break; + kem->decapsulate_init = OSSL_FUNC_kem_decapsulate_init(fns); + decfncnt++; + break; + case OSSL_FUNC_KEM_DECAPSULATE: + if (kem->decapsulate != NULL) + break; + kem->decapsulate = OSSL_FUNC_kem_decapsulate(fns); + decfncnt++; + break; + case OSSL_FUNC_KEM_FREECTX: + if (kem->freectx != NULL) + break; + kem->freectx = OSSL_FUNC_kem_freectx(fns); + ctxfncnt++; + break; + case OSSL_FUNC_KEM_DUPCTX: + if (kem->dupctx != NULL) + break; + kem->dupctx = OSSL_FUNC_kem_dupctx(fns); + break; + case OSSL_FUNC_KEM_GET_CTX_PARAMS: + if (kem->get_ctx_params != NULL) + break; + kem->get_ctx_params + = OSSL_FUNC_kem_get_ctx_params(fns); + gparamfncnt++; + break; + case OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS: + if (kem->gettable_ctx_params != NULL) + break; + kem->gettable_ctx_params + = OSSL_FUNC_kem_gettable_ctx_params(fns); + gparamfncnt++; + break; + case OSSL_FUNC_KEM_SET_CTX_PARAMS: + if (kem->set_ctx_params != NULL) + break; + kem->set_ctx_params + = OSSL_FUNC_kem_set_ctx_params(fns); + sparamfncnt++; + break; + case OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS: + if (kem->settable_ctx_params != NULL) + break; + kem->settable_ctx_params + = OSSL_FUNC_kem_settable_ctx_params(fns); + sparamfncnt++; + break; + } + } + if (ctxfncnt != 2 + || (encfncnt != 0 && encfncnt != 2) + || (decfncnt != 0 && decfncnt != 2) + || (encfncnt != 2 && decfncnt != 2) + || (gparamfncnt != 0 && gparamfncnt != 2) + || (sparamfncnt != 0 && sparamfncnt != 2)) { + /* + * In order to be a consistent set of functions we must have at least + * a set of context functions (newctx and freectx) as well as a pair of + * "kem" functions: (encapsulate_init, encapsulate) or + * (decapsulate_init, decapsulate). set_ctx_params and settable_ctx_params are + * optional, but if one of them is present then the other one must also + * be present. The same applies to get_ctx_params and + * gettable_ctx_params. The dupctx function is optional. + */ + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_PROVIDER_FUNCTIONS); + goto err; + } + + return kem; + err: + EVP_KEM_free(kem); + return NULL; +} + +void EVP_KEM_free(EVP_KEM *kem) +{ + if (kem != NULL) { + int i; + + CRYPTO_DOWN_REF(&kem->refcnt, &i, kem->lock); + if (i > 0) + return; + ossl_provider_free(kem->prov); + CRYPTO_THREAD_lock_free(kem->lock); + OPENSSL_free(kem); + } +} + +int EVP_KEM_up_ref(EVP_KEM *kem) +{ + int ref = 0; + + CRYPTO_UP_REF(&kem->refcnt, &ref, kem->lock); + return 1; +} + +OSSL_PROVIDER *EVP_KEM_provider(const EVP_KEM *kem) +{ + return kem->prov; +} + +EVP_KEM *EVP_KEM_fetch(OPENSSL_CTX *ctx, const char *algorithm, + const char *properties) +{ + return evp_generic_fetch(ctx, OSSL_OP_KEM, algorithm, properties, + evp_kem_from_dispatch, + (int (*)(void *))EVP_KEM_up_ref, + (void (*)(void *))EVP_KEM_free); +} + +int EVP_KEM_is_a(const EVP_KEM *kem, const char *name) +{ + return evp_is_a(kem->prov, kem->name_id, NULL, name); +} + +int EVP_KEM_number(const EVP_KEM *kem) +{ + return kem->name_id; +} + +void EVP_KEM_do_all_provided(OPENSSL_CTX *libctx, + void (*fn)(EVP_KEM *kem, void *arg), + void *arg) +{ + evp_generic_do_all(libctx, OSSL_OP_KEM, (void (*)(void *, void *))fn, arg, + evp_kem_from_dispatch, + (void (*)(void *))EVP_KEM_free); +} + + +void EVP_KEM_names_do_all(const EVP_KEM *kem, + void (*fn)(const char *name, void *data), + void *data) +{ + if (kem->prov != NULL) + evp_names_do_all(kem->prov, kem->name_id, fn, data); +} diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c index 38f42eca7d..fcd7266975 100644 --- a/crypto/evp/pmeth_lib.c +++ b/crypto/evp/pmeth_lib.c @@ -148,7 +148,9 @@ static int evp_pkey_ctx_state(EVP_PKEY_CTX *ctx) || (EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx) && ctx->op.ciph.ciphprovctx != NULL) || (EVP_PKEY_CTX_IS_GEN_OP(ctx) - && ctx->op.keymgmt.genctx != NULL)) + && ctx->op.keymgmt.genctx != NULL) + || (EVP_PKEY_CTX_IS_KEM_OP(ctx) + && ctx->op.encap.kemprovctx != NULL)) return EVP_PKEY_STATE_PROVIDER; return EVP_PKEY_STATE_LEGACY; @@ -396,7 +398,14 @@ void evp_pkey_ctx_free_old_ops(EVP_PKEY_CTX *ctx) EVP_KEYEXCH_free(ctx->op.kex.exchange); ctx->op.kex.exchprovctx = NULL; ctx->op.kex.exchange = NULL; - } else if (EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)) { + } else if (EVP_PKEY_CTX_IS_KEM_OP(ctx)) { + if (ctx->op.encap.kemprovctx != NULL && ctx->op.encap.kem != NULL) + ctx->op.encap.kem->freectx(ctx->op.encap.kemprovctx); + EVP_KEM_free(ctx->op.encap.kem); + ctx->op.encap.kemprovctx = NULL; + ctx->op.encap.kem = NULL; + } + else if (EVP_PKEY_CTX_IS_ASYM_CIPHER_OP(ctx)) { if (ctx->op.ciph.ciphprovctx != NULL && ctx->op.ciph.cipher != NULL) ctx->op.ciph.cipher->freectx(ctx->op.ciph.ciphprovctx); EVP_ASYM_CIPHER_free(ctx->op.ciph.cipher); @@ -559,6 +568,26 @@ EVP_PKEY_CTX *EVP_PKEY_CTX_dup(const EVP_PKEY_CTX *pctx) } return rctx; } + } else if (EVP_PKEY_CTX_IS_KEM_OP(pctx)) { + if (pctx->op.encap.kem != NULL) { + rctx->op.encap.kem = pctx->op.encap.kem; + if (!EVP_KEM_up_ref(rctx->op.encap.kem)) { + OPENSSL_free(rctx); + return NULL; + } + } + if (pctx->op.encap.kemprovctx != NULL) { + if (!ossl_assert(pctx->op.encap.kem != NULL)) + return NULL; + rctx->op.encap.kemprovctx + = pctx->op.encap.kem->dupctx(pctx->op.encap.kemprovctx); + if (rctx->op.encap.kemprovctx == NULL) { + EVP_KEM_free(rctx->op.encap.kem); + OPENSSL_free(rctx); + return NULL; + } + return rctx; + } } rctx->pmeth = pctx->pmeth; @@ -659,6 +688,12 @@ int EVP_PKEY_CTX_set_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params) && ctx->keymgmt->gen_set_params != NULL) return evp_keymgmt_gen_set_params(ctx->keymgmt, ctx->op.keymgmt.genctx, params); + if (EVP_PKEY_CTX_IS_KEM_OP(ctx) + && ctx->op.encap.kemprovctx != NULL + && ctx->op.encap.kem != NULL + && ctx->op.encap.kem->set_ctx_params != NULL) + return ctx->op.encap.kem->set_ctx_params(ctx->op.encap.kemprovctx, + params); return 0; } @@ -682,6 +717,12 @@ int EVP_PKEY_CTX_get_params(EVP_PKEY_CTX *ctx, OSSL_PARAM *params) && ctx->op.ciph.cipher->get_ctx_params != NULL) return ctx->op.ciph.cipher->get_ctx_params(ctx->op.ciph.ciphprovctx, params); + if (EVP_PKEY_CTX_IS_KEM_OP(ctx) + && ctx->op.encap.kemprovctx != NULL + && ctx->op.encap.kem != NULL + && ctx->op.encap.kem->get_ctx_params != NULL) + return ctx->op.encap.kem->get_ctx_params(ctx->op.encap.kemprovctx, + params); return 0; } @@ -710,6 +751,12 @@ const OSSL_PARAM *EVP_PKEY_CTX_gettable_params(EVP_PKEY_CTX *ctx) EVP_ASYM_CIPHER_provider(ctx->op.ciph.cipher)); return ctx->op.ciph.cipher->gettable_ctx_params(provctx); } + if (EVP_PKEY_CTX_IS_KEM_OP(ctx) + && ctx->op.encap.kem != NULL + && ctx->op.encap.kem->gettable_ctx_params != NULL) { + provctx = ossl_provider_ctx(EVP_KEM_provider(ctx->op.encap.kem)); + return ctx->op.encap.kem->gettable_ctx_params(provctx); + } return NULL; } @@ -740,7 +787,12 @@ const OSSL_PARAM *EVP_PKEY_CTX_settable_params(EVP_PKEY_CTX *ctx) if (EVP_PKEY_CTX_IS_GEN_OP(ctx) && ctx->keymgmt != NULL) return EVP_KEYMGMT_gen_settable_params(ctx->keymgmt); - + if (EVP_PKEY_CTX_IS_KEM_OP(ctx) + && ctx->op.encap.kem != NULL + && ctx->op.encap.kem->settable_ctx_params != NULL) { + provctx = ossl_provider_ctx(EVP_KEM_provider(ctx->op.encap.kem)); + return ctx->op.encap.kem->settable_ctx_params(provctx); + } return NULL; } @@ -1096,6 +1148,24 @@ int EVP_PKEY_CTX_set_mac_key(EVP_PKEY_CTX *ctx, const unsigned char *key, key, keylen); } +int EVP_PKEY_CTX_set_kem_op(EVP_PKEY_CTX *ctx, const char *op) +{ + OSSL_PARAM params[2], *p = params; + + if (ctx == NULL || op == NULL) { + ERR_raise(ERR_LIB_EVP, EVP_R_INVALID_VALUE); + return 0; + } + if (!EVP_PKEY_CTX_IS_KEM_OP(ctx)) { + ERR_raise(ERR_LIB_EVP, EVP_R_COMMAND_NOT_SUPPORTED); + return -2; + } + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION, + (char *)op, 0); + *p = OSSL_PARAM_construct_end(); + return EVP_PKEY_CTX_set_params(ctx, params); +} + int evp_pkey_ctx_set1_id_prov(EVP_PKEY_CTX *ctx, const void *id, int len) { OSSL_PARAM params[2], *p = params; diff --git a/doc/man3/EVP_KEM_free.pod b/doc/man3/EVP_KEM_free.pod new file mode 100644 index 0000000000..0e3ca12ae3 --- /dev/null +++ b/doc/man3/EVP_KEM_free.pod @@ -0,0 +1,82 @@ +=pod + +=head1 NAME + +EVP_KEM_fetch, EVP_KEM_free, EVP_KEM_up_ref, +EVP_KEM_number, EVP_KEM_is_a, EVP_KEM_provider, +EVP_KEM_do_all_provided, EVP_KEM_names_do_all +- Functions to manage EVP_KEM algorithm objects + +=head1 SYNOPSIS + + #include + + EVP_KEM *EVP_KEM_fetch(OPENSSL_CTX *ctx, const char *algorithm, + const char *properties); + void EVP_KEM_free(EVP_KEM *kem); + int EVP_KEM_up_ref(EVP_KEM *kem); + int EVP_KEM_number(const EVP_KEM *kem); + int EVP_KEM_is_a(const EVP_KEM *kem, const char *name); + OSSL_PROVIDER *EVP_KEM_provider(const EVP_KEM *kem); + void EVP_KEM_do_all_provided(OPENSSL_CTX *libctx, + void (*fn)(EVP_KEM *kem, void *arg), void *arg); + void EVP_KEM_names_do_all(const EVP_KEM *kem, + void (*fn)(const char *name, void *data), void *data); + +=head1 DESCRIPTION + +EVP_KEM_fetch() fetches the implementation for the given B from any +provider offering it, within the criteria given by the B and in the +scope of the given library context B (see L). The algorithm +will be one offering functions for performing asymmetric kem related tasks such +as key encapsulation and decapsulation. +See L for further information. + +The returned value must eventually be freed with EVP_KEM_free(). + +EVP_KEM_free() decrements the reference count for the B structure. +Typically this structure will have been obtained from an earlier call to +EVP_KEM_fetch(). If the reference count drops to 0 then the structure is freed. + +EVP_KEM_up_ref() increments the reference count for an B structure. + +EVP_KEM_is_a() returns 1 if I is an implementation of an +algorithm that's identifiable with I, otherwise 0. + +EVP_KEM_provider() returns the provider that I was fetched from. + +EVP_KEM_do_all_provided() traverses all EVP_KEMs implemented by all activated +providers in the given library context I, and for each of the +implementations, calls the given function I with the implementation method +and the given I as argument. + +EVP_KEM_number() returns the internal dynamic number assigned to I. + +EVP_KEM_names_do_all() traverses all names for I, and calls I with +each name and I. + +=head1 RETURN VALUES + +EVP_KEM_fetch() returns a pointer to an B for success or B for +failure. + +EVP_KEM_up_ref() returns 1 for success or 0 otherwise. + +=head1 SEE ALSO + +L, L + +=head1 HISTORY + +The functions described here were added in OpenSSL 3.0. + +=head1 COPYRIGHT + +Copyright 2020 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/EVP_PKEY_CTX_ctrl.pod b/doc/man3/EVP_PKEY_CTX_ctrl.pod index 794ad2053a..e5c187d950 100644 --- a/doc/man3/EVP_PKEY_CTX_ctrl.pod +++ b/doc/man3/EVP_PKEY_CTX_ctrl.pod @@ -67,7 +67,8 @@ EVP_PKEY_CTX_set_ecdh_kdf_outlen, EVP_PKEY_CTX_get_ecdh_kdf_outlen, EVP_PKEY_CTX_set0_ecdh_kdf_ukm, EVP_PKEY_CTX_get0_ecdh_kdf_ukm, -EVP_PKEY_CTX_set1_id, EVP_PKEY_CTX_get1_id, EVP_PKEY_CTX_get1_id_len +EVP_PKEY_CTX_set1_id, EVP_PKEY_CTX_get1_id, EVP_PKEY_CTX_get1_id_len, +EVP_PKEY_CTX_set_kem_op - algorithm specific control operations =head1 SYNOPSIS @@ -91,6 +92,8 @@ EVP_PKEY_CTX_set1_id, EVP_PKEY_CTX_get1_id, EVP_PKEY_CTX_get1_id_len int EVP_PKEY_CTX_set_group_name(EVP_PKEY_CTX *ctx, const char *name); int EVP_PKEY_CTX_get_group_name(EVP_PKEY_CTX *ctx, char *name, size_t namelen); + int EVP_PKEY_CTX_set_kem_op(EVP_PKEY_CTX *ctx, const char *op); + #include int EVP_PKEY_CTX_set_rsa_padding(EVP_PKEY_CTX *ctx, int pad); @@ -606,6 +609,12 @@ memory for further calls to EVP_PKEY_CTX_get1_id(). EVP_PKEY_CTX_get1_id() returns the previously set ID value to caller in I. The caller should allocate adequate memory space for the I before calling EVP_PKEY_CTX_get1_id(). +EVP_PKEY_CTX_set_kem_op() sets the KEM operation to run. This can be set after +EVP_PKEY_encapsulate_init() or EVP_PKEY_decapsulate_init() to select the +kem operation. RSA is the only key type that supports encapsulation currently, +and as there is no default operation for the RSA type, this function must be +called before EVP_PKEY_encapsulate() or EVP_PKEY_decapsulate(). + =head1 RETURN VALUES All other functions described on this page return a positive value for success @@ -623,6 +632,8 @@ L, L, L, L +L +L =head1 HISTORY diff --git a/doc/man3/EVP_PKEY_decapsulate.pod b/doc/man3/EVP_PKEY_decapsulate.pod new file mode 100644 index 0000000000..7dd47a1e58 --- /dev/null +++ b/doc/man3/EVP_PKEY_decapsulate.pod @@ -0,0 +1,99 @@ +=pod + +=head1 NAME + +EVP_PKEY_decapsulate_init, EVP_PKEY_decapsulate +- Key decapsulation using a private key algorithm + +=head1 SYNOPSIS + + #include + + int EVP_PKEY_decapsulate_init(EVP_PKEY_CTX *ctx); + int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx, + unsigned char *secret, size_t *secretlen, + const unsigned char *wrapped, size_t wrappedlen); + +=head1 DESCRIPTION + +The EVP_PKEY_decapsulate_init() function initializes a private key algorithm +context I for a decapsulation operation. + +The EVP_PKEY_decapsulate() function performs a private key decapsulation +operation using I. The data to be decapsulated is specified using the +I and I parameters. +If I is I then the maximum size of the output secret buffer +is written to the I<*secretlen> parameter. If I is not B and the +call is successful then the decapsulated secret data is written to I and +the amount of data written to I. + +=head1 NOTES + +After the call to EVP_PKEY_decapsulate_init() algorithm specific parameters +for the operation may be set using L. There are no +settable parameters currently. + +=head1 RETURN VALUES + +EVP_PKEY_decapsulate_init() and EVP_PKEY_decapsulate() return 1 for +success and 0 or a negative value for failure. In particular a return value of -2 +indicates the operation is not supported by the private key algorithm. + +=head1 EXAMPLES + +Decapsulate data using RSA: + + #include + + /* + * NB: assumes rsa_priv_key is an RSA private key, + * and that in, inlen are already set up to contain encapsulated data. + */ + + EVP_PKEY_CTX *ctx = NULL; + size_t secretlen = 0; + unsigned char *secret = NULL;; + + ctx = EVP_PKEY_CTX_new_from_pkey(libctx, rsa_priv_key, NULL); + if (ctx = NULL) + /* Error */ + if (EVP_PKEY_decapsulate_init(ctx) <= 0) + /* Error */ + + /* Set the mode - only 'RSASVE' is currently supported */ + if (EVP_PKEY_CTX_set_kem_op(ctx, "RSASVE") <= 0) + /* Error */ + + /* Determine buffer length */ + if (EVP_PKEY_decapsulate(ctx, NULL, &secretlen, in, inlen) <= 0) + /* Error */ + + secret = OPENSSL_malloc(secretlen); + if (secret == NULL) + /* malloc failure */ + + /* Decapsulated secret data is secretlen bytes long */ + if (EVP_PKEY_decapsulaterctx, secret, &secretlen, in, inlen) <= 0) + /* Error */ + + +=head1 SEE ALSO + +L, +L, +L, + +=head1 HISTORY + +These functions were added in OpenSSL 3.0. + +=head1 COPYRIGHT + +Copyright 2020 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/EVP_PKEY_encapsulate.pod b/doc/man3/EVP_PKEY_encapsulate.pod new file mode 100644 index 0000000000..0e911f71cf --- /dev/null +++ b/doc/man3/EVP_PKEY_encapsulate.pod @@ -0,0 +1,101 @@ +=pod + +=head1 NAME + +EVP_PKEY_encapsulate_init, EVP_PKEY_encapsulate +- Key encapsulation using a public key algorithm + +=head1 SYNOPSIS + + #include + + int EVP_PKEY_encapsulate_init(EVP_PKEY_CTX *ctx); + int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx, + unsigned char *out, size_t *outlen, + unsigned char *genkey, size_t *genkeylen); + +=head1 DESCRIPTION + +The EVP_PKEY_encapsulate_init() function initializes a public key algorithm +context I for an encapsulation operation. + +The EVP_PKEY_encapsulate() function performs a public key encapsulation +operation using I with the name I. +If I is B then the maximum size of the output buffer is written to the +I<*outlen> parameter and the maximum size of the generated key buffer is written +to I<*genkeylen>. If I is not B and the call is successful then the +internally generated key is written to I and its size is written to +I<*genkeylen>. The encapsulated version of the generated key is written to +I and its size is written to I<*outlen>. + +=head1 NOTES + +After the call to EVP_PKEY_encapsulate_init() algorithm specific parameters +for the operation may be set using L. + +=head1 RETURN VALUES + +EVP_PKEY_encapsulate_init() and EVP_PKEY_encapsulate() return 1 for +success and 0 or a negative value for failure. In particular a return value of -2 +indicates the operation is not supported by the public key algorithm. + +=head1 EXAMPLES + +Encapsulate an RSASVE key (for RSA keys). + + #include + + /* + * NB: assumes rsa_pub_key is an public key of another party. + */ + + EVP_PKEY_CTX *ctx = NULL; + size_t secretlen = 0, outlen = 0; + unsigned char *out = NULL, *secret = NULL; + + ctx = EVP_PKEY_CTX_new_from_pkey(libctx, rsa_pub_key, NULL); + if (ctx = NULL) + /* Error */ + if (EVP_PKEY_encapsulate_init(ctx) <= 0) + /* Error */ + + /* Set the mode - only 'RSASVE' is currently supported */ + if (EVP_PKEY_CTX_set_kem_op(ctx, "RSASVE") <= 0) + /* Error */ + /* Determine buffer length */ + if (EVP_PKEY_encapsulate(ctx, NULL, &outlen, NULL, &secretlen) <= 0) + /* Error */ + + out = OPENSSL_malloc(outlen); + secret = OPENSSL_malloc(secretlen); + if (out == NULL || secret == NULL) + /* malloc failure */ + + /* + * The generated 'secret' can be used as key material. + * The encapsulated 'out' can be sent to another party who can + * decapsulate it using their private key to retrieve the 'secret'. + */ + if (EVP_PKEY_encapsulate(ctx, out, &outlen, secret, &secretlen) <= 0) + /* Error */ + +=head1 SEE ALSO + +L, +L, +L, + +=head1 HISTORY + +These functions were added in OpenSSL 3.0. + +=head1 COPYRIGHT + +Copyright 2020 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/EVP_KEM-RSA.pod b/doc/man7/EVP_KEM-RSA.pod new file mode 100644 index 0000000000..21dc8ad3dd --- /dev/null +++ b/doc/man7/EVP_KEM-RSA.pod @@ -0,0 +1,66 @@ +=pod + +=head1 NAME + +EVP_KEM-RSA +- EVP_KEM RSA keytype and algorithm support + +=head1 DESCRIPTION + +The B keytype and its parameters are described in L. +See L and L for more info. + +=head2 RSA KEM parameters + +=over 4 + +=item "operation" (B) + +The OpenSSL RSA Key Encapsulation Mechanism only currently supports the +following operation + +=over 4 + +=item "RSASVE" + +The encapsulate function simply generates a secret using random bytes and then +encrypts the secret using the RSA public key (with no padding). +The decapsulate function recovers the secret using the RSA private key. + +=back + +This can be set using EVP_PKEY_CTX_set_kem_op(). + +=back + + +=head1 CONFORMING TO + +=over 4 + +=item SP800-56Br2 + +Section 7.2.1.2 RSASVE Generate Operation (RSASVE.GENERATE). +Section 7.2.1.3 RSASVE Recovery Operation (RSASVE.RECOVER). + +=back + +=head1 SEE ALSO + +L, +L, +L +L, +L, +L + +=head1 COPYRIGHT + +Copyright 2020 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/OSSL_PROVIDER-FIPS.pod b/doc/man7/OSSL_PROVIDER-FIPS.pod index 98c6079d72..b802efe215 100644 --- a/doc/man7/OSSL_PROVIDER-FIPS.pod +++ b/doc/man7/OSSL_PROVIDER-FIPS.pod @@ -136,6 +136,14 @@ This has the property "provider=fips,fips=no" =back +=head2 Asymmetric Key Encapsulation + +=over 4 + +=item RSA, see L + +=back + =head2 Asymmetric Key Management =over 4 diff --git a/doc/man7/OSSL_PROVIDER-default.pod b/doc/man7/OSSL_PROVIDER-default.pod index a88c0be6e6..848c887b29 100644 --- a/doc/man7/OSSL_PROVIDER-default.pod +++ b/doc/man7/OSSL_PROVIDER-default.pod @@ -182,6 +182,14 @@ The OpenSSL default provider supports these operations and algorithms: =back +=head2 Asymmetric Key Encapsulation + +=over 4 + +=item RSA, see L + +=back + =head2 Asymmetric Key Management =over 4 diff --git a/doc/man7/provider-kem.pod b/doc/man7/provider-kem.pod new file mode 100644 index 0000000000..4d16a3e625 --- /dev/null +++ b/doc/man7/provider-kem.pod @@ -0,0 +1,207 @@ +=pod + +=head1 NAME + +provider-kem - The kem library E-E provider functions + +=head1 SYNOPSIS + +=for openssl multiple includes + + #include + #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. + */ + + /* Context management */ + void *OSSL_FUNC_kem_newctx(void *provctx); + void OSSL_FUNC_kem_freectx(void *ctx); + void *OSSL_FUNC_kem_dupctx(void *ctx); + + /* Encapsulation */ + int OSSL_FUNC_kem_encapsulate_init(void *ctx, void *provkey, const char *name); + int OSSL_FUNC_kem_encapsulate(void *ctx, unsigned char *out, size_t *outlen, + unsigned char *secret, size_t *secretlen); + + /* Decapsulation */ + int OSSL_FUNC_kem_decapsulate_init(void *ctx, void *provkey, const char *name); + int OSSL_FUNC_kem_decapsulate(void *ctx, unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen); + + /* KEM parameters */ + int OSSL_FUNC_kem_get_ctx_params(void *ctx, OSSL_PARAM params[]); + const OSSL_PARAM *OSSL_FUNC_kem_gettable_ctx_params(void *provctx); + int OSSL_FUNC_kem_set_ctx_params(void *ctx, const OSSL_PARAM params[]); + const OSSL_PARAM *OSSL_FUNC_kem_settable_ctx_params(void *provctx); + +=head1 DESCRIPTION + +This documentation is primarily aimed at provider authors. See L +for further information. + +The asymmetric kem (OSSL_OP_KEM) operation enables providers to +implement asymmetric kem algorithms and make them available to applications +via the API functions L, +L and other related functions. + +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 an B element named +B. +For example, the "function" OSSL_FUNC_kem_newctx() has these: + + typedef void *(OSSL_FUNC_kem_newctx_fn)(void *provctx); + static ossl_inline OSSL_FUNC_kem_newctx_fn + OSSL_FUNC_kem_newctx(const OSSL_DISPATCH *opf); + +B arrays are indexed by numbers that are provided as +macros in L, as follows: + + OSSL_FUNC_kem_newctx OSSL_FUNC_KEM_NEWCTX + OSSL_FUNC_kem_freectx OSSL_FUNC_KEM_FREECTX + OSSL_FUNC_kem_dupctx OSSL_FUNC_KEM_DUPCTX + + OSSL_FUNC_kem_encapsulate_init OSSL_FUNC_KEM_ENCAPSULATE_INIT + OSSL_FUNC_kem_encapsulate OSSL_FUNC_KEM_ENCAPSULATE + + OSSL_FUNC_kem_decapsulate_init OSSL_FUNC_KEM_DECAPSULATE_INIT + OSSL_FUNC_kem_decapsulate OSSL_FUNC_KEM_DECAPSULATE + + OSSL_FUNC_kem_get_ctx_params OSSL_FUNC_KEM_GET_CTX_PARAMS + OSSL_FUNC_kem_gettable_ctx_params OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS + OSSL_FUNC_kem_set_ctx_params OSSL_FUNC_KEM_SET_CTX_PARAMS + OSSL_FUNC_kem_settable_ctx_params OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS + +An asymmetric kem algorithm implementation may not implement all of these +functions. +In order to be a consistent set of functions a provider must implement +OSSL_FUNC_kem_newctx and OSSL_FUNC_kem_freectx. +It must also implement both of OSSL_FUNC_kem_encapsulate_init and +OSSL_FUNC_kem_encapsulate, or both of OSSL_FUNC_kem_decapsulate_init and +OSSL_FUNC_kem_decapsulate. +OSSL_FUNC_kem_get_ctx_params is optional but if it is present then so must +OSSL_FUNC_kem_gettable_ctx_params. +Similarly, OSSL_FUNC_kem_set_ctx_params is optional but if it is present then +so must OSSL_FUNC_kem_settable_ctx_params. + +An asymmetric kem algorithm must also implement some mechanism for generating, +loading or importing keys via the key management (OSSL_OP_KEYMGMT) operation. +See L for further details. + +=head2 Context Management Functions + +OSSL_FUNC_kem_newctx() should create and return a pointer to a provider side +structure for holding context information during an asymmetric kem operation. +A pointer to this context will be passed back in a number of the other +asymmetric kem operation function calls. +The parameter I is the provider context generated during provider +initialisation (see L). + +OSSL_FUNC_kem_freectx() is passed a pointer to the provider side asymmetric +kem context in the I parameter. +This function should free any resources associated with that context. + +OSSL_FUNC_kem_dupctx() should duplicate the provider side asymmetric kem +context in the I parameter and return the duplicate copy. + +=head2 Asymmetric Key Encapsulation Functions + +OSSL_FUNC_kem_encapsulate_init() initialises a context for an asymmetric +encapsulation given a provider side asymmetric kem context in the I +parameter, a pointer to a provider key object in the I parameter and +the I of the algorithm. +The key object should have been previously generated, loaded or imported into +the provider using the key management (OSSL_OP_KEYMGMT) operation (see +provider-keymgmt(7)>. + +OSSL_FUNC_kem_encapsulate() performs the actual encapsulation itself. +A previously initialised asymmetric kem context is passed in the I +parameter. +Unless I is NULL, the data to be encapsulated is internally generated, +and returned into the the buffer pointed to by the I parameter and the +encapsulated data should also be written to the location pointed to by the +I parameter. The length of the encapsulated data should be written to +I<*outlen> and the length of the generated secret should be written to +I<*secretlen>. + +If I is NULL then the maximum length of the encapsulated data should be +written to I<*outlen>, and the maximum length of the generated secret should be +written to I<*secretlen>. + +=head2 Decapsulation Functions + +OSSL_FUNC_kem_decapsulate_init() initialises a context for an asymmetric +decapsulation given a provider side asymmetric kem context in the I +parameter, a pointer to a provider key object in the I parameter, and +a I of the algorithm. +The key object should have been previously generated, loaded or imported into +the provider using the key management (OSSL_OP_KEYMGMT) operation (see +provider-keymgmt(7)>. + +OSSL_FUNC_kem_decapsulate() performs the actual decapsulation itself. +A previously initialised asymmetric kem context is passed in the I +parameter. +The data to be decapsulated is pointed to by the I parameter which is I +bytes long. +Unless I is NULL, the decapsulated data should be written to the location +pointed to by the I parameter. +The length of the decapsulated data should be written to I<*outlen>. +If I is NULL then the maximum length of the decapsulated data should be +written to I<*outlen>. + +=head2 Asymmetric Key Encapsulation Parameters + +See L for further details on the parameters structure used by +the OSSL_FUNC_kem_get_ctx_params() and OSSL_FUNC_kem_set_ctx_params() +functions. + +OSSL_FUNC_kem_get_ctx_params() gets asymmetric kem parameters associated +with the given provider side asymmetric kem context I and stores them in +I. +OSSL_FUNC_kem_set_ctx_params() sets the asymmetric kem parameters associated +with the given provider side asymmetric kem context I to I. +Any parameter settings are additional to any that were previously set. + +No parameters are currently recognised by built-in asymmetric kem algorithms. + +OSSL_FUNC_kem_gettable_ctx_params() and OSSL_FUNC_kem_settable_ctx_params() +get a constant B array that describes the gettable and settable +parameters, i.e. parameters that can be used with OSSL_FUNC_kem_get_ctx_params() +and OSSL_FUNC_kem_set_ctx_params() respectively. +See L for the use of B as parameter descriptor. + +=head1 RETURN VALUES + +OSSL_FUNC_kem_newctx() and OSSL_FUNC_kem_dupctx() should return the newly +created provider side asymmetric kem context, or NULL on failure. + +All other functions should return 1 for success or 0 on error. + +=head1 SEE ALSO + +L + +=head1 HISTORY + +The provider KEM interface was introduced in OpenSSL 3.0. + +=head1 COPYRIGHT + +Copyright 2020 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 2f7f019650..ead37b5769 100644 --- a/doc/man7/provider.pod +++ b/doc/man7/provider.pod @@ -154,6 +154,12 @@ The number for this operation is B. The functions the provider can offer are described in L +=item Asymmetric Key Encapsulation + +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 + =item Encoding In the OpenSSL libraries, the corresponding method object is diff --git a/include/crypto/evp.h b/include/crypto/evp.h index 4912760230..7016606757 100644 --- a/include/crypto/evp.h +++ b/include/crypto/evp.h @@ -50,6 +50,10 @@ struct evp_pkey_ctx_st { EVP_ASYM_CIPHER *cipher; void *ciphprovctx; } ciph; + struct { + EVP_KEM *kem; + void *kemprovctx; + } encap; } op; /* @@ -665,6 +669,10 @@ struct evp_pkey_st { ((ctx)->operation == EVP_PKEY_OP_PARAMGEN \ || (ctx)->operation == EVP_PKEY_OP_KEYGEN) +#define EVP_PKEY_CTX_IS_KEM_OP(ctx) \ + ((ctx)->operation == EVP_PKEY_OP_ENCAPSULATE \ + || (ctx)->operation == EVP_PKEY_OP_DECAPSULATE) + void openssl_add_all_ciphers_int(void); void openssl_add_all_digests_int(void); void evp_cleanup_int(void); diff --git a/include/openssl/core_dispatch.h b/include/openssl/core_dispatch.h index ad1df714ea..5c6a4f4848 100644 --- a/include/openssl/core_dispatch.h +++ b/include/openssl/core_dispatch.h @@ -193,6 +193,7 @@ OSSL_CORE_MAKE_FUNC(int, provider_self_test, (void *provctx)) # define OSSL_OP_KEYEXCH 11 # define OSSL_OP_SIGNATURE 12 # define OSSL_OP_ASYM_CIPHER 13 +# define OSSL_OP_KEM 14 /* New section for non-EVP operations */ # define OSSL_OP_ENCODER 20 # define OSSL_OP_DECODER 21 @@ -717,6 +718,37 @@ OSSL_CORE_MAKE_FUNC(int, asym_cipher_set_ctx_params, OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, asym_cipher_settable_ctx_params, (void *provctx)) +/* Asymmetric Key encapsulation */ +# define OSSL_FUNC_KEM_NEWCTX 1 +# define OSSL_FUNC_KEM_ENCAPSULATE_INIT 2 +# define OSSL_FUNC_KEM_ENCAPSULATE 3 +# define OSSL_FUNC_KEM_DECAPSULATE_INIT 4 +# define OSSL_FUNC_KEM_DECAPSULATE 5 +# define OSSL_FUNC_KEM_FREECTX 6 +# define OSSL_FUNC_KEM_DUPCTX 7 +# define OSSL_FUNC_KEM_GET_CTX_PARAMS 8 +# define OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS 9 +# define OSSL_FUNC_KEM_SET_CTX_PARAMS 10 +# define OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS 11 + +OSSL_CORE_MAKE_FUNC(void *, kem_newctx, (void *provctx)) +OSSL_CORE_MAKE_FUNC(int, kem_encapsulate_init, (void *ctx, void *provkey)) +OSSL_CORE_MAKE_FUNC(int, kem_encapsulate, (void *ctx, + unsigned char *out, size_t *outlen, + unsigned char *secret, + size_t *secretlen)) +OSSL_CORE_MAKE_FUNC(int, kem_decapsulate_init, (void *ctx, void *provkey)) +OSSL_CORE_MAKE_FUNC(int, kem_decapsulate, (void *ctx, + unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen)) +OSSL_CORE_MAKE_FUNC(void, kem_freectx, (void *ctx)) +OSSL_CORE_MAKE_FUNC(void *, kem_dupctx, (void *ctx)) +OSSL_CORE_MAKE_FUNC(int, kem_get_ctx_params, (void *ctx, OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, kem_gettable_ctx_params, (void *provctx)) +OSSL_CORE_MAKE_FUNC(int, kem_set_ctx_params, + (void *ctx, const OSSL_PARAM params[])) +OSSL_CORE_MAKE_FUNC(const OSSL_PARAM *, kem_settable_ctx_params, (void *provctx)) + /* Encoders and decoders */ # define OSSL_FUNC_ENCODER_NEWCTX 1 # define OSSL_FUNC_ENCODER_FREECTX 2 diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h index 9a6cc2c03d..0fc2868d5b 100644 --- a/include/openssl/core_names.h +++ b/include/openssl/core_names.h @@ -475,9 +475,15 @@ extern "C" { #define OSSL_PKEY_PARAM_RSA_TEST_Q2 "q2" #define OSSL_SIGNATURE_PARAM_KAT "kat" +/* KEM parameters */ +#define OSSL_KEM_PARAM_OPERATION "operation" + +/* OSSL_KEM_PARAM_OPERATION values */ +#define OSSL_KEM_PARAM_OPERATION_RSASVE "RSASVE" + /* Capabilities */ -/* TLS-GROUP Capbility */ +/* TLS-GROUP Capability */ #define OSSL_CAPABILITY_TLS_GROUP_NAME "tls-group-name" #define OSSL_CAPABILITY_TLS_GROUP_NAME_INTERNAL "tls-group-name-internal" #define OSSL_CAPABILITY_TLS_GROUP_ID "tls-group-id" diff --git a/include/openssl/evp.h b/include/openssl/evp.h index ff97198542..d3892982e7 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h @@ -1496,6 +1496,8 @@ int EVP_PKEY_CTX_set1_id(EVP_PKEY_CTX *ctx, const void *id, int len); int EVP_PKEY_CTX_get1_id(EVP_PKEY_CTX *ctx, void *id); int EVP_PKEY_CTX_get1_id_len(EVP_PKEY_CTX *ctx, size_t *id_len); +int EVP_PKEY_CTX_set_kem_op(EVP_PKEY_CTX *ctx, const char *op); + const char *EVP_PKEY_get0_first_alg_name(const EVP_PKEY *key); # define EVP_PKEY_OP_UNDEFINED 0 @@ -1511,6 +1513,8 @@ const char *EVP_PKEY_get0_first_alg_name(const EVP_PKEY *key); # define EVP_PKEY_OP_ENCRYPT (1<<10) # define EVP_PKEY_OP_DECRYPT (1<<11) # define EVP_PKEY_OP_DERIVE (1<<12) +# define EVP_PKEY_OP_ENCAPSULATE (1<<13) +# define EVP_PKEY_OP_DECAPSULATE (1<<14) # define EVP_PKEY_OP_TYPE_SIG \ (EVP_PKEY_OP_SIGN | EVP_PKEY_OP_VERIFY | EVP_PKEY_OP_VERIFYRECOVER \ @@ -1689,6 +1693,18 @@ void EVP_ASYM_CIPHER_names_do_all(const EVP_ASYM_CIPHER *cipher, void (*fn)(const char *name, void *data), void *data); +void EVP_KEM_free(EVP_KEM *wrap); +int EVP_KEM_up_ref(EVP_KEM *wrap); +OSSL_PROVIDER *EVP_KEM_provider(const EVP_KEM *wrap); +EVP_KEM *EVP_KEM_fetch(OPENSSL_CTX *ctx, const char *algorithm, + const char *properties); +int EVP_KEM_is_a(const EVP_KEM *wrap, const char *name); +int EVP_KEM_number(const EVP_KEM *wrap); +void EVP_KEM_do_all_provided(OPENSSL_CTX *libctx, + void (*fn)(EVP_KEM *wrap, void *arg), void *arg); +void EVP_KEM_names_do_all(const EVP_KEM *wrap, + void (*fn)(const char *name, void *data), void *data); + int EVP_PKEY_sign_init(EVP_PKEY_CTX *ctx); int EVP_PKEY_sign(EVP_PKEY_CTX *ctx, unsigned char *sig, size_t *siglen, @@ -1714,6 +1730,15 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx); int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer); int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, size_t *keylen); +int EVP_PKEY_encapsulate_init(EVP_PKEY_CTX *ctx); +int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx, + unsigned char *wrappedkey, size_t *wrappedkeylen, + unsigned char *genkey, size_t *genkeylen); +int EVP_PKEY_decapsulate_init(EVP_PKEY_CTX *ctx); +int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx, + unsigned char *unwrapped, size_t *unwrappedlen, + const unsigned char *wrapped, size_t wrappedlen); + typedef int EVP_PKEY_gen_cb(EVP_PKEY_CTX *ctx); int EVP_PKEY_param_fromdata_init(EVP_PKEY_CTX *ctx); diff --git a/include/openssl/types.h b/include/openssl/types.h index cd0c51e8bf..ee024cef29 100644 --- a/include/openssl/types.h +++ b/include/openssl/types.h @@ -123,6 +123,8 @@ typedef struct evp_signature_st EVP_SIGNATURE; typedef struct evp_asym_cipher_st EVP_ASYM_CIPHER; +typedef struct evp_kem_st EVP_KEM; + typedef struct evp_Encode_Ctx_st EVP_ENCODE_CTX; typedef struct hmac_ctx_st HMAC_CTX; diff --git a/providers/defltprov.c b/providers/defltprov.c index d95964539b..dfb113903e 100644 --- a/providers/defltprov.c +++ b/providers/defltprov.c @@ -381,6 +381,11 @@ static const OSSL_ALGORITHM deflt_asym_cipher[] = { { NULL, NULL, NULL } }; +static const OSSL_ALGORITHM deflt_asym_kem[] = { + { "RSA", "provider=default", rsa_asym_kem_functions }, + { NULL, NULL, NULL } +}; + static const OSSL_ALGORITHM deflt_keymgmt[] = { #ifndef OPENSSL_NO_DH { "DH:dhKeyAgreement", "provider=default", dh_keymgmt_functions }, @@ -467,6 +472,8 @@ static const OSSL_ALGORITHM *deflt_query(void *provctx, int operation_id, return deflt_signature; case OSSL_OP_ASYM_CIPHER: return deflt_asym_cipher; + case OSSL_OP_KEM: + return deflt_asym_kem; case OSSL_OP_ENCODER: return deflt_encoder; case OSSL_OP_DECODER: diff --git a/providers/fips/fipsprov.c b/providers/fips/fipsprov.c index aec262654e..9cf43eb491 100644 --- a/providers/fips/fipsprov.c +++ b/providers/fips/fipsprov.c @@ -462,6 +462,11 @@ static const OSSL_ALGORITHM fips_asym_cipher[] = { { NULL, NULL, NULL } }; +static const OSSL_ALGORITHM fips_asym_kem[] = { + { "RSA", FIPS_DEFAULT_PROPERTIES, rsa_asym_kem_functions }, + { NULL, NULL, NULL } +}; + static const OSSL_ALGORITHM fips_keymgmt[] = { #ifndef OPENSSL_NO_DH { "DH:dhKeyAgreement", FIPS_DEFAULT_PROPERTIES, dh_keymgmt_functions }, @@ -517,6 +522,8 @@ static const OSSL_ALGORITHM *fips_query(void *provctx, int operation_id, return fips_signature; case OSSL_OP_ASYM_CIPHER: return fips_asym_cipher; + case OSSL_OP_KEM: + return fips_asym_kem; } return NULL; } diff --git a/providers/implementations/build.info b/providers/implementations/build.info index fe67e59401..a2f60653e2 100644 --- a/providers/implementations/build.info +++ b/providers/implementations/build.info @@ -1,2 +1,2 @@ SUBDIRS=digests ciphers rands macs kdfs exchange keymgmt signature asymciphers \ - encode_decode storemgmt + encode_decode storemgmt kem diff --git a/providers/implementations/include/prov/implementations.h b/providers/implementations/include/prov/implementations.h index 91c5df40ec..36a0d4b3d2 100644 --- a/providers/implementations/include/prov/implementations.h +++ b/providers/implementations/include/prov/implementations.h @@ -304,6 +304,9 @@ extern const OSSL_DISPATCH mac_legacy_cmac_signature_functions[]; /* Asym Cipher */ extern const OSSL_DISPATCH rsa_asym_cipher_functions[]; +/* Asym Key encapsulation */ +extern const OSSL_DISPATCH rsa_asym_kem_functions[]; + /* Encoders */ extern const OSSL_DISPATCH rsa_priv_to_text_encoder_functions[]; extern const OSSL_DISPATCH rsa_pub_to_text_encoder_functions[]; diff --git a/providers/implementations/kem/build.info b/providers/implementations/kem/build.info new file mode 100644 index 0000000000..e9f91cba43 --- /dev/null +++ b/providers/implementations/kem/build.info @@ -0,0 +1,6 @@ +# We make separate GOAL variables for each algorithm, to make it easy to +# switch each to the Legacy provider when needed. + +$RSA_KEM_GOAL=../../libimplementations.a + +SOURCE[$RSA_KEM_GOAL]=rsa_kem.c diff --git a/providers/implementations/kem/rsa_kem.c b/providers/implementations/kem/rsa_kem.c new file mode 100644 index 0000000000..7cf0e918c8 --- /dev/null +++ b/providers/implementations/kem/rsa_kem.c @@ -0,0 +1,341 @@ +/* + * Copyright 2020 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 + */ + +/* + * RSA low level APIs are deprecated for public use, but still ok for + * internal use. + */ +#include "internal/deprecated.h" + +#include "e_os.h" /* strcasecmp */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "prov/providercommonerr.h" +#include "prov/provider_ctx.h" +#include "prov/implementations.h" + +static OSSL_FUNC_kem_newctx_fn rsakem_newctx; +static OSSL_FUNC_kem_encapsulate_init_fn rsakem_init; +static OSSL_FUNC_kem_encapsulate_fn rsakem_generate; +static OSSL_FUNC_kem_decapsulate_init_fn rsakem_init; +static OSSL_FUNC_kem_decapsulate_fn rsakem_recover; +static OSSL_FUNC_kem_freectx_fn rsakem_freectx; +static OSSL_FUNC_kem_dupctx_fn rsakem_dupctx; +static OSSL_FUNC_kem_get_ctx_params_fn rsakem_get_ctx_params; +static OSSL_FUNC_kem_gettable_ctx_params_fn rsakem_gettable_ctx_params; +static OSSL_FUNC_kem_set_ctx_params_fn rsakem_set_ctx_params; +static OSSL_FUNC_kem_settable_ctx_params_fn rsakem_settable_ctx_params; + +/* + * Only the KEM for RSASVE as defined in SP800-56b r2 is implemented + * currently. + */ +#define KEM_OP_UNDEFINED -1 +#define KEM_OP_RSASVE 0 + +/* + * What's passed as an actual key is defined by the KEYMGMT interface. + * We happen to know that our KEYMGMT simply passes RSA structures, so + * we use that here too. + */ +typedef struct { + OPENSSL_CTX *libctx; + RSA *rsa; + int op; +} PROV_RSA_CTX; + +static const OSSL_ITEM rsakem_opname_id_map[] = { + { KEM_OP_RSASVE, OSSL_KEM_PARAM_OPERATION_RSASVE }, +}; + +static int name2id(const char *name, const OSSL_ITEM *map, size_t sz) +{ + size_t i; + + if (name == NULL) + return -1; + + for (i = 0; i < sz; ++i) { + if (strcasecmp(map[i].ptr, name) == 0) + return map[i].id; + } + return -1; +} + +static int rsakem_opname2id(const char *name) +{ + return name2id(name, rsakem_opname_id_map, OSSL_NELEM(rsakem_opname_id_map)); +} + +static void *rsakem_newctx(void *provctx) +{ + PROV_RSA_CTX *prsactx = OPENSSL_zalloc(sizeof(PROV_RSA_CTX)); + + if (prsactx == NULL) + return NULL; + prsactx->libctx = PROV_LIBRARY_CONTEXT_OF(provctx); + prsactx->op = KEM_OP_UNDEFINED; + + return prsactx; +} + +static void rsakem_freectx(void *vprsactx) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + RSA_free(prsactx->rsa); + OPENSSL_free(prsactx); +} + +static void *rsakem_dupctx(void *vprsactx) +{ + PROV_RSA_CTX *srcctx = (PROV_RSA_CTX *)vprsactx; + PROV_RSA_CTX *dstctx; + + dstctx = OPENSSL_zalloc(sizeof(*srcctx)); + if (dstctx == NULL) + return NULL; + + *dstctx = *srcctx; + if (dstctx->rsa != NULL && !RSA_up_ref(dstctx->rsa)) { + OPENSSL_free(dstctx); + return NULL; + } + return dstctx; +} + +static int rsakem_init(void *vprsactx, void *vrsa) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + if (prsactx == NULL || vrsa == NULL || !RSA_up_ref(vrsa)) + return 0; + RSA_free(prsactx->rsa); + prsactx->rsa = vrsa; + /* TODO(3.0) Add a RSA keylength check here for fips */ + return 1; +} + +static int rsakem_get_ctx_params(void *vprsactx, OSSL_PARAM *params) +{ + PROV_RSA_CTX *ctx = (PROV_RSA_CTX *)vprsactx; + + if (ctx == NULL || params == NULL) + return 0; + return 1; +} + +static const OSSL_PARAM known_gettable_rsakem_ctx_params[] = { + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsakem_gettable_ctx_params(ossl_unused void *provctx) +{ + return known_gettable_rsakem_ctx_params; +} + +static int rsakem_set_ctx_params(void *vprsactx, const OSSL_PARAM params[]) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + const OSSL_PARAM *p; + int op; + + if (prsactx == NULL || params == NULL) + return 0; + + p = OSSL_PARAM_locate_const(params, OSSL_KEM_PARAM_OPERATION); + if (p != NULL) { + if (p->data_type != OSSL_PARAM_UTF8_STRING) + return 0; + op = rsakem_opname2id(p->data); + if (op < 0) + return 0; + prsactx->op = op; + } + return 1; +} + +static const OSSL_PARAM known_settable_rsakem_ctx_params[] = { + OSSL_PARAM_utf8_string(OSSL_KEM_PARAM_OPERATION, NULL, 0), + OSSL_PARAM_END +}; + +static const OSSL_PARAM *rsakem_settable_ctx_params(ossl_unused void *provctx) +{ + return known_settable_rsakem_ctx_params; +} + +/* + * NIST.SP.800-56Br2 + * 7.2.1.2 RSASVE Generate Operation (RSASVE.GENERATE). + * + * Generate a random in the range 1 < z < (n – 1) + */ +static int rsasve_gen_rand_bytes(RSA *rsa_pub, + unsigned char *out, int outlen) +{ + int ret = 0; + BN_CTX *bnctx; + BIGNUM *z, *nminus3; + + bnctx = BN_CTX_secure_new_ex(rsa_get0_libctx(rsa_pub)); + if (bnctx == NULL) + return 0; + + /* + * Generate a random in the range 1 < z < (n – 1). + * Since BN_priv_rand_range_ex() returns a value in range 0 <= r < max + * We can achieve this by adding 2.. but then we need to subtract 3 from + * the upper bound i.e: 2 + (0 <= r < (n - 3)) + */ + BN_CTX_start(bnctx); + nminus3 = BN_CTX_get(bnctx); + z = BN_CTX_get(bnctx); + ret = (z != NULL + && (BN_copy(nminus3, RSA_get0_n(rsa_pub)) != NULL) + && BN_sub_word(nminus3, 3) + && BN_priv_rand_range_ex(z, nminus3, bnctx) + && BN_add_word(z, 2) + && (BN_bn2binpad(z, out, outlen) == outlen)); + BN_CTX_end(bnctx); + BN_CTX_free(bnctx); + return ret; +} + +/* + * NIST.SP.800-56Br2 + * 7.2.1.2 RSASVE Generate Operation (RSASVE.GENERATE). + */ +static int rsasve_generate(PROV_RSA_CTX *prsactx, + unsigned char *out, size_t *outlen, + unsigned char *secret, size_t *secretlen) +{ + int ret; + size_t nlen; + + /* Step (1): nlen = Ceil(len(n)/8) */ + nlen = RSA_size(prsactx->rsa); + + if (out == NULL) { + if (nlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + if (outlen == NULL && secretlen == NULL) + return 0; + if (outlen != NULL) + *outlen = nlen; + if (secretlen != NULL) + *secretlen = nlen; + return 1; + } + /* + * Step (2): Generate a random byte string z of nlen bytes where + * 1 < z < n - 1 + */ + if (!rsasve_gen_rand_bytes(prsactx->rsa, secret, nlen)) + return 0; + + /* Step(3): out = RSAEP((n,e), z) */ + ret = RSA_public_encrypt(nlen, secret, out, prsactx->rsa, RSA_NO_PADDING); + if (ret) { + ret = 1; + if (outlen != NULL) + *outlen = nlen; + if (secretlen != NULL) + *secretlen = nlen; + } else { + OPENSSL_cleanse(secret, nlen); + } + return ret; +} + +/* + * NIST.SP.800-56Br2 + * 7.2.1.3 RSASVE Recovery Operation (RSASVE.RECOVER). + */ +static int rsasve_recover(PROV_RSA_CTX *prsactx, + unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + size_t nlen; + + /* Step (1): get the byte length of n */ + nlen = RSA_size(prsactx->rsa); + + if (out == NULL) { + if (nlen == 0) { + ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY); + return 0; + } + *outlen = nlen; + return 1; + } + + /* Step (2): check the input ciphertext 'inlen' matches the nlen */ + if (inlen != nlen) { + ERR_raise(ERR_LIB_PROV, PROV_R_BAD_LENGTH); + return 0; + } + /* Step (3): out = RSADP((n,d), in) */ + return (RSA_private_decrypt(inlen, in, out, prsactx->rsa, RSA_NO_PADDING) > 0); +} + +static int rsakem_generate(void *vprsactx, unsigned char *out, size_t *outlen, + unsigned char *secret, size_t *secretlen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + switch (prsactx->op) { + case KEM_OP_RSASVE: + return rsasve_generate(prsactx, out, outlen, secret, secretlen); + default: + return -2; + } +} + +static int rsakem_recover(void *vprsactx, unsigned char *out, size_t *outlen, + const unsigned char *in, size_t inlen) +{ + PROV_RSA_CTX *prsactx = (PROV_RSA_CTX *)vprsactx; + + switch (prsactx->op) { + case KEM_OP_RSASVE: + return rsasve_recover(prsactx, out, outlen, in, inlen); + default: + return -2; + } +} + +const OSSL_DISPATCH rsa_asym_kem_functions[] = { + { OSSL_FUNC_KEM_NEWCTX, (void (*)(void))rsakem_newctx }, + { OSSL_FUNC_KEM_ENCAPSULATE_INIT, + (void (*)(void))rsakem_init }, + { OSSL_FUNC_KEM_ENCAPSULATE, (void (*)(void))rsakem_generate }, + { OSSL_FUNC_KEM_DECAPSULATE_INIT, + (void (*)(void))rsakem_init }, + { OSSL_FUNC_KEM_DECAPSULATE, (void (*)(void))rsakem_recover }, + { OSSL_FUNC_KEM_FREECTX, (void (*)(void))rsakem_freectx }, + { OSSL_FUNC_KEM_DUPCTX, (void (*)(void))rsakem_dupctx }, + { OSSL_FUNC_KEM_GET_CTX_PARAMS, + (void (*)(void))rsakem_get_ctx_params }, + { OSSL_FUNC_KEM_GETTABLE_CTX_PARAMS, + (void (*)(void))rsakem_gettable_ctx_params }, + { OSSL_FUNC_KEM_SET_CTX_PARAMS, + (void (*)(void))rsakem_set_ctx_params }, + { OSSL_FUNC_KEM_SETTABLE_CTX_PARAMS, + (void (*)(void))rsakem_settable_ctx_params }, + { 0, NULL } +}; diff --git a/providers/implementations/keymgmt/rsa_kmgmt.c b/providers/implementations/keymgmt/rsa_kmgmt.c index 5a8da35d60..659121c227 100644 --- a/providers/implementations/keymgmt/rsa_kmgmt.c +++ b/providers/implementations/keymgmt/rsa_kmgmt.c @@ -46,7 +46,7 @@ static OSSL_FUNC_keymgmt_import_fn rsa_import; static OSSL_FUNC_keymgmt_import_types_fn rsa_import_types; static OSSL_FUNC_keymgmt_export_fn rsa_export; static OSSL_FUNC_keymgmt_export_types_fn rsa_export_types; -static OSSL_FUNC_keymgmt_query_operation_name_fn rsapss_query_operation_name; +static OSSL_FUNC_keymgmt_query_operation_name_fn rsa_query_operation_name; #define RSA_DEFAULT_MD "SHA256" #define RSA_PSS_DEFAULT_MD OSSL_DIGEST_NAME_SHA1 @@ -609,7 +609,7 @@ void *rsa_load(const void *reference, size_t reference_sz) } /* For any RSA key, we use the "RSA" algorithms regardless of sub-type. */ -static const char *rsapss_query_operation_name(int operation_id) +static const char *rsa_query_operation_name(int operation_id) { return "RSA"; } @@ -657,6 +657,6 @@ const OSSL_DISPATCH rsapss_keymgmt_functions[] = { { OSSL_FUNC_KEYMGMT_EXPORT, (void (*)(void))rsa_export }, { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void (*)(void))rsa_export_types }, { OSSL_FUNC_KEYMGMT_QUERY_OPERATION_NAME, - (void (*)(void))rsapss_query_operation_name }, + (void (*)(void))rsa_query_operation_name }, { 0, NULL } }; diff --git a/test/evp_libctx_test.c b/test/evp_libctx_test.c index 823cdec8e0..e42d6683e7 100644 --- a/test/evp_libctx_test.c +++ b/test/evp_libctx_test.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "testutil.h" #include "internal/nelem.h" @@ -441,6 +442,181 @@ static void collect_cipher_names(EVP_CIPHER *cipher, void *cipher_names_list) sk_OPENSSL_CSTRING_push(names, EVP_CIPHER_name(cipher)); } +static int rsa_keygen(int bits, EVP_PKEY **pub, EVP_PKEY **priv) +{ + int ret = 0; + EVP_PKEY_CTX *keygen_ctx = NULL; + unsigned char *pub_der = NULL; + const unsigned char *pp = NULL; + long len = 0; + + if (!TEST_ptr(keygen_ctx = EVP_PKEY_CTX_new_from_name(libctx, "RSA", NULL)) + || !TEST_int_gt(EVP_PKEY_keygen_init(keygen_ctx), 0) + || !TEST_true(EVP_PKEY_CTX_set_rsa_keygen_bits(keygen_ctx, bits)) + || !TEST_int_gt(EVP_PKEY_keygen(keygen_ctx, priv), 0) + || !TEST_int_gt(len = i2d_PublicKey(*priv, &pub_der), 0)) + goto err; + pp = pub_der; + if (!TEST_ptr(d2i_PublicKey(EVP_PKEY_RSA, pub, &pp, len))) + goto err; + ret = 1; +err: + OPENSSL_free(pub_der); + EVP_PKEY_CTX_free(keygen_ctx); + return ret; +} + +static int kem_rsa_gen_recover(void) +{ + int ret = 0; + EVP_PKEY *pub = NULL; + EVP_PKEY *priv = NULL; + EVP_PKEY_CTX *sctx = NULL, *rctx = NULL; + unsigned char secret[256] = { 0, }; + unsigned char ct[256] = { 0, }; + unsigned char unwrap[256] = { 0, }; + size_t ctlen = 0, unwraplen = 0, secretlen = 0; + + ret = TEST_true(rsa_keygen(2048, &pub, &priv)) + && TEST_ptr(sctx = EVP_PKEY_CTX_new_from_pkey(libctx, pub, NULL)) + && TEST_int_eq(EVP_PKEY_encapsulate_init(sctx), 1) + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(sctx, "RSASVE"), 1) + && TEST_int_eq(EVP_PKEY_encapsulate(sctx, NULL, &ctlen, NULL, + &secretlen), 1) + && TEST_int_eq(ctlen, secretlen) + && TEST_int_eq(ctlen, 2048 / 8) + && TEST_int_eq(EVP_PKEY_encapsulate(sctx, ct, &ctlen, secret, + &secretlen), 1) + && TEST_ptr(rctx = EVP_PKEY_CTX_new_from_pkey(libctx, priv, NULL)) + && TEST_int_eq(EVP_PKEY_decapsulate_init(rctx), 1) + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(rctx, "RSASVE"), 1) + && TEST_int_eq(EVP_PKEY_decapsulate(rctx, NULL, &unwraplen, + ct, ctlen), 1) + && TEST_int_eq(EVP_PKEY_decapsulate(rctx, unwrap, &unwraplen, + ct, ctlen), 1) + && TEST_mem_eq(unwrap, unwraplen, secret, secretlen); + EVP_PKEY_free(pub); + EVP_PKEY_free(priv); + EVP_PKEY_CTX_free(rctx); + EVP_PKEY_CTX_free(sctx); + return ret; +} + +static int kem_rsa_params(void) +{ + int ret = 0; + EVP_PKEY *pub = NULL; + EVP_PKEY *priv = NULL; + EVP_PKEY_CTX *pubctx = NULL, *privctx = NULL; + unsigned char secret[256] = { 0, }; + unsigned char ct[256] = { 0, }; + size_t ctlen = 0, secretlen = 0; + + ret = TEST_true(rsa_keygen(2048, &pub, &priv)) + && TEST_ptr(pubctx = EVP_PKEY_CTX_new_from_pkey(libctx, pub, NULL)) + && TEST_ptr(privctx = EVP_PKEY_CTX_new_from_pkey(libctx, priv, NULL)) + /* Test setting kem op before the init fails */ + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(pubctx, "RSASVE"), -2) + /* Test NULL ctx passed */ + && TEST_int_eq(EVP_PKEY_encapsulate_init(NULL), 0) + && TEST_int_eq(EVP_PKEY_encapsulate(NULL, NULL, NULL, NULL, NULL), 0) + && TEST_int_eq(EVP_PKEY_decapsulate_init(NULL), 0) + && TEST_int_eq(EVP_PKEY_decapsulate(NULL, NULL, NULL, NULL, 0), 0) + /* Test Invalid operation */ + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, NULL, NULL, NULL, NULL), -1) + && TEST_int_eq(EVP_PKEY_decapsulate(privctx, NULL, NULL, NULL, 0), 0) + /* Wrong key component - no secret should be returned on failure */ + && TEST_int_eq(EVP_PKEY_decapsulate_init(pubctx), 1) + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(pubctx, "RSASVE"), 1) + && TEST_int_eq(EVP_PKEY_decapsulate(pubctx, secret, &secretlen, ct, + sizeof(ct)), 0) + && TEST_uchar_eq(secret[0], 0) + /* Test encapsulate fails if the mode is not set */ + && TEST_int_eq(EVP_PKEY_encapsulate_init(pubctx), 1) + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, ct, &ctlen, secret, &secretlen), -2) + /* Test setting a bad kem ops fail */ + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(pubctx, "RSA"), 0) + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(pubctx, NULL), 0) + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(NULL, "RSASVE"), 0) + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(NULL, NULL), 0) + /* Test secretlen is optional */ + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(pubctx, "RSASVE"), 1) + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, ct, &ctlen, secret, NULL), 1) + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, NULL, &ctlen, NULL, NULL), 1) + /* Test outlen is optional */ + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, NULL, NULL, NULL, &secretlen), 1) + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, ct, NULL, secret, &secretlen), 1) + /* test that either len must be set if out is NULL */ + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, NULL, NULL, NULL, NULL), 0) + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, NULL, &ctlen, NULL, NULL), 1) + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, NULL, NULL, NULL, &secretlen), 1) + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, NULL, &ctlen, NULL, &secretlen), 1) + /* Secret buffer should be set if there is an output buffer */ + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, ct, &ctlen, NULL, NULL), 0) + /* Test that lengths are optional if ct is not NULL */ + && TEST_int_eq(EVP_PKEY_encapsulate(pubctx, ct, NULL, secret, NULL), 1) + /* Pass if secret or secret length are not NULL */ + && TEST_int_eq(EVP_PKEY_decapsulate_init(privctx), 1) + && TEST_int_eq(EVP_PKEY_CTX_set_kem_op(privctx, "RSASVE"), 1) + && TEST_int_eq(EVP_PKEY_decapsulate(privctx, secret, NULL, ct, sizeof(ct)), 1) + && TEST_int_eq(EVP_PKEY_decapsulate(privctx, NULL, &secretlen, ct, sizeof(ct)), 1) + && TEST_int_eq(secretlen, 256) + /* Fail if passed NULL arguments */ + && TEST_int_eq(EVP_PKEY_decapsulate(privctx, NULL, NULL, ct, sizeof(ct)), 0) + && TEST_int_eq(EVP_PKEY_decapsulate(privctx, secret, &secretlen, NULL, 0), 0) + && TEST_int_eq(EVP_PKEY_decapsulate(privctx, secret, &secretlen, NULL, sizeof(ct)), 0) + && TEST_int_eq(EVP_PKEY_decapsulate(privctx, secret, &secretlen, ct, 0), 0); + + EVP_PKEY_free(pub); + EVP_PKEY_free(priv); + EVP_PKEY_CTX_free(pubctx); + EVP_PKEY_CTX_free(privctx); + return ret; +} + +#ifndef OPENSSL_NO_DH +static EVP_PKEY *gen_dh_key(void) +{ + EVP_PKEY_CTX *gctx = NULL; + EVP_PKEY *pkey = NULL; + OSSL_PARAM params[2]; + + params[0] = OSSL_PARAM_construct_utf8_string("group", "ffdhe2048", 0); + params[1] = OSSL_PARAM_construct_end(); + + if (!TEST_ptr(gctx = EVP_PKEY_CTX_new_from_name(libctx, "DH", NULL)) + || !TEST_true(EVP_PKEY_keygen_init(gctx)) + || !TEST_true(EVP_PKEY_CTX_set_params(gctx, params)) + || !TEST_true(EVP_PKEY_keygen(gctx, &pkey))) + goto err; +err: + EVP_PKEY_CTX_free(gctx); + return pkey; +} + +/* Fail if we try to use a dh key */ +static int kem_invalid_keytype(void) +{ + int ret = 0; + EVP_PKEY *key = NULL; + EVP_PKEY_CTX *sctx = NULL; + + if (!TEST_ptr(key = gen_dh_key())) + goto done; + + if (!TEST_ptr(sctx = EVP_PKEY_CTX_new_from_pkey(libctx, key, NULL))) + goto done; + if (!TEST_int_eq(EVP_PKEY_encapsulate_init(sctx), -2)) + goto done; + + ret = 1; +done: + EVP_PKEY_free(key); + EVP_PKEY_CTX_free(sctx); + return ret; +} +#endif /* OPENSSL_NO_DH */ + int setup_tests(void) { const char *prov_name = "default"; @@ -496,6 +672,11 @@ int setup_tests(void) ADD_ALL_TESTS(test_cipher_reinit, sk_OPENSSL_CSTRING_num(cipher_names)); ADD_ALL_TESTS(test_cipher_reinit_partialupdate, sk_OPENSSL_CSTRING_num(cipher_names)); + ADD_TEST(kem_rsa_gen_recover); + ADD_TEST(kem_rsa_params); +#ifndef OPENSSL_NO_DH + ADD_TEST(kem_invalid_keytype); +#endif return 1; } diff --git a/util/libcrypto.num b/util/libcrypto.num index e9f6e56690..0be0ada89d 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -5283,3 +5283,16 @@ EVP_PKEY_CTX_set_ec_param_enc ? 3_0_0 EXIST::FUNCTION:EC EVP_PKEY_get0_first_alg_name ? 3_0_0 EXIST::FUNCTION: EVP_KEYMGMT_get0_first_name ? 3_0_0 EXIST::FUNCTION: EC_KEY_decoded_from_explicit_params ? 3_0_0 EXIST::FUNCTION:EC +EVP_KEM_free ? 3_0_0 EXIST::FUNCTION: +EVP_KEM_up_ref ? 3_0_0 EXIST::FUNCTION: +EVP_KEM_provider ? 3_0_0 EXIST::FUNCTION: +EVP_KEM_fetch ? 3_0_0 EXIST::FUNCTION: +EVP_KEM_is_a ? 3_0_0 EXIST::FUNCTION: +EVP_KEM_number ? 3_0_0 EXIST::FUNCTION: +EVP_KEM_do_all_provided ? 3_0_0 EXIST::FUNCTION: +EVP_KEM_names_do_all ? 3_0_0 EXIST::FUNCTION: +EVP_PKEY_encapsulate_init ? 3_0_0 EXIST::FUNCTION: +EVP_PKEY_encapsulate ? 3_0_0 EXIST::FUNCTION: +EVP_PKEY_decapsulate_init ? 3_0_0 EXIST::FUNCTION: +EVP_PKEY_decapsulate ? 3_0_0 EXIST::FUNCTION: +EVP_PKEY_CTX_set_kem_op ? 3_0_0 EXIST::FUNCTION: