Add KEM (Key encapsulation mechanism) support to providers
authorShane Lontis <shane.lontis@oracle.com>
Sat, 19 Sep 2020 08:08:46 +0000 (18:08 +1000)
committerShane Lontis <shane.lontis@oracle.com>
Sat, 19 Sep 2020 08:08:46 +0000 (18:08 +1000)
SP800-56Br2 requires support for the RSA primitives for RSASVE generate and recover.
As these are simple KEM operations another operation type has been added that can support future extensions.

Added public functions EVP_PKEY_encapsulate_init(), EVP_PKEY_encapsulate(), EVP_PKEY_decapsulate_init() and EVP_PKEY_decapsulate()
Added EVP_KEM_* functions.
Added OSSL_FUNC_kem_* dispatch functions

Added EVP_PKEY_CTX_set_kem_op() so that different types of KEM can be added in the future. This value must currently be set to
"RSASVE" after EVP_PKEY_encapsulate_init() & EVP_PKEY_decapsulate_init() as there is no default value.
This allows the existing RSA key types, keymanagers, and encoders to be used with the encapsulation operations.

The design of the public API's resulted from contributions from @romen & @levitte.

Reviewed-by: Nicola Tuveri <nic.tuv@gmail.com>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/12750)

27 files changed:
crypto/evp/build.info
crypto/evp/evp_local.h
crypto/evp/kem.c [new file with mode: 0644]
crypto/evp/pmeth_lib.c
doc/man3/EVP_KEM_free.pod [new file with mode: 0644]
doc/man3/EVP_PKEY_CTX_ctrl.pod
doc/man3/EVP_PKEY_decapsulate.pod [new file with mode: 0644]
doc/man3/EVP_PKEY_encapsulate.pod [new file with mode: 0644]
doc/man7/EVP_KEM-RSA.pod [new file with mode: 0644]
doc/man7/OSSL_PROVIDER-FIPS.pod
doc/man7/OSSL_PROVIDER-default.pod
doc/man7/provider-kem.pod [new file with mode: 0644]
doc/man7/provider.pod
include/crypto/evp.h
include/openssl/core_dispatch.h
include/openssl/core_names.h
include/openssl/evp.h
include/openssl/types.h
providers/defltprov.c
providers/fips/fipsprov.c
providers/implementations/build.info
providers/implementations/include/prov/implementations.h
providers/implementations/kem/build.info [new file with mode: 0644]
providers/implementations/kem/rsa_kem.c [new file with mode: 0644]
providers/implementations/keymgmt/rsa_kmgmt.c
test/evp_libctx_test.c
util/libcrypto.num

index 36fac11..7f1459a 100644 (file)
@@ -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 \
index e7f7643..3268aa4 100644 (file)
@@ -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 (file)
index 0000000..6f04240
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <openssl/objects.h>
+#include <openssl/evp.h>
+#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);
+}
index 38f42ec..fcd7266 100644 (file)
@@ -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 (file)
index 0000000..0e3ca12
--- /dev/null
@@ -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 <openssl/evp.h>
+
+ 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<algorithm> from any
+provider offering it, within the criteria given by the B<properties> and in the
+scope of the given library context B<ctx> (see L<OPENSSL_CTX(3)>). The algorithm
+will be one offering functions for performing asymmetric kem related tasks such
+as key encapsulation and decapsulation.
+See L<provider(7)/Fetching algorithms> for further information.
+
+The returned value must eventually be freed with EVP_KEM_free().
+
+EVP_KEM_free() decrements the reference count for the B<EVP_KEM> 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<EVP_KEM> structure.
+
+EVP_KEM_is_a() returns 1 if I<kem> is an implementation of an
+algorithm that's identifiable with I<name>, otherwise 0.
+
+EVP_KEM_provider() returns the provider that I<kem> was fetched from.
+
+EVP_KEM_do_all_provided() traverses all EVP_KEMs implemented by all activated
+providers in the given library context I<libctx>, and for each of the
+implementations, calls the given function I<fn> with the implementation method
+and the given I<arg> as argument.
+
+EVP_KEM_number() returns the internal dynamic number assigned to I<kem>.
+
+EVP_KEM_names_do_all() traverses all names for I<kem>, and calls I<fn> with
+each name and I<data>.
+
+=head1 RETURN VALUES
+
+EVP_KEM_fetch() returns a pointer to an B<EVP_KEM> for success or B<NULL> for
+failure.
+
+EVP_KEM_up_ref() returns 1 for success or 0 otherwise.
+
+=head1 SEE ALSO
+
+L<provider(7)/Fetching algorithms>, L<OSSL_PROVIDER(3)>
+
+=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<https://www.openssl.org/source/license.html>.
+
+=cut
index 794ad20..e5c187d 100644 (file)
@@ -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 <openssl/rsa.h>
 
  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<id>. The caller should
 allocate adequate memory space for the I<id> 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<EVP_PKEY_verify(3)>,
 L<EVP_PKEY_verify_recover(3)>,
 L<EVP_PKEY_derive(3)>,
 L<EVP_PKEY_keygen(3)>
+L<EVP_PKEY_encapsulate(3)>
+L<EVP_PKEY_decapsulate(3)>
 
 =head1 HISTORY
 
diff --git a/doc/man3/EVP_PKEY_decapsulate.pod b/doc/man3/EVP_PKEY_decapsulate.pod
new file mode 100644 (file)
index 0000000..7dd47a1
--- /dev/null
@@ -0,0 +1,99 @@
+=pod
+
+=head1 NAME
+
+EVP_PKEY_decapsulate_init, EVP_PKEY_decapsulate
+- Key decapsulation using a private key algorithm
+
+=head1 SYNOPSIS
+
+ #include <openssl/evp.h>
+
+ 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<ctx> for a decapsulation operation.
+
+The EVP_PKEY_decapsulate() function performs a private key decapsulation
+operation using I<ctx>. The data to be decapsulated is specified using the
+I<wrapped> and I<wrappedlen> parameters.
+If I<secret> is I<NULL> then the maximum size of the output secret buffer
+is written to the I<*secretlen> parameter. If I<secret> is not B<NULL> and the
+call is successful then the decapsulated secret data is written to I<secret> and
+the amount of data written to I<secretlen>.
+
+=head1 NOTES
+
+After the call to EVP_PKEY_decapsulate_init() algorithm specific parameters
+for the operation may be set using L<EVP_PKEY_CTX_set_params(3)>. 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 <openssl/evp.h>
+
+ /*
+  * 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<EVP_PKEY_CTX_new(3)>,
+L<EVP_PKEY_encapsulate(3)>,
+L<EVP_KEM-RSA(7)>,
+
+=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<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man3/EVP_PKEY_encapsulate.pod b/doc/man3/EVP_PKEY_encapsulate.pod
new file mode 100644 (file)
index 0000000..0e911f7
--- /dev/null
@@ -0,0 +1,101 @@
+=pod
+
+=head1 NAME
+
+EVP_PKEY_encapsulate_init, EVP_PKEY_encapsulate
+- Key encapsulation using a public key algorithm
+
+=head1 SYNOPSIS
+
+ #include <openssl/evp.h>
+
+ 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<ctx> for an encapsulation operation.
+
+The EVP_PKEY_encapsulate() function performs a public key encapsulation
+operation using I<ctx> with the name I<name>.
+If I<out> is B<NULL> 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<out> is not B<NULL> and the call is successful then the
+internally generated key is written to I<genkey> and its size is written to
+I<*genkeylen>. The encapsulated version of the generated key is written to
+I<out> 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<EVP_PKEY_CTX_set_params(3)>.
+
+=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 <openssl/evp.h>
+
+ /*
+  * 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<EVP_PKEY_CTX_new(3)>,
+L<EVP_PKEY_decapsulate(3)>,
+L<EVP_KEM-RSA(7)>,
+
+=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<https://www.openssl.org/source/license.html>.
+
+=cut
diff --git a/doc/man7/EVP_KEM-RSA.pod b/doc/man7/EVP_KEM-RSA.pod
new file mode 100644 (file)
index 0000000..21dc8ad
--- /dev/null
@@ -0,0 +1,66 @@
+=pod
+
+=head1 NAME
+
+EVP_KEM-RSA
+- EVP_KEM RSA keytype and algorithm support
+
+=head1 DESCRIPTION
+
+The B<RSA> keytype and its parameters are described in L<EVP_PKEY-RSA(7)>.
+See L<EVP_PKEY_encapsulate(3)> and L<EVP_PKEY_decapsulate(3)> for more info.
+
+=head2 RSA KEM parameters
+
+=over 4
+
+=item "operation" (B<OSSL_KEM_PARAM_OPERATION>) <UTF8 string>
+
+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<EVP_PKEY_CTX_set_kem_op(3)>,
+L<EVP_PKEY_encapsulate(3)>,
+L<EVP_PKEY_decapsulate(3)>
+L<EVP_KEYMGMT(3)>,
+L<EVP_PKEY(3)>,
+L<provider-keymgmt(7)>
+
+=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<https://www.openssl.org/source/license.html>.
+
+=cut
index 98c6079..b802efe 100644 (file)
@@ -136,6 +136,14 @@ This has the property "provider=fips,fips=no"
 
 =back
 
+=head2 Asymmetric Key Encapsulation
+
+=over 4
+
+=item RSA, see L<EVP_KEM-RSA(7)>
+
+=back
+
 =head2 Asymmetric Key Management
 
 =over 4
index a88c0be..848c887 100644 (file)
@@ -182,6 +182,14 @@ The OpenSSL default provider supports these operations and algorithms:
 
 =back
 
+=head2 Asymmetric Key Encapsulation
+
+=over 4
+
+=item RSA, see L<EVP_KEM-RSA(7)>
+
+=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 (file)
index 0000000..4d16a3e
--- /dev/null
@@ -0,0 +1,207 @@
+=pod
+
+=head1 NAME
+
+provider-kem - The kem library E<lt>-E<gt> provider functions
+
+=head1 SYNOPSIS
+
+=for openssl multiple includes
+
+ #include <openssl/core_dispatch.h>
+ #include <openssl/core_names.h>
+
+ /*
+  * 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<provider(7)>
+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<EVP_PKEY_encapsulate(3)>,
+L<EVP_PKEY_decapsulate(3)> and other related functions.
+
+All "functions" mentioned here are passed as function pointers between
+F<libcrypto> and the provider in B<OSSL_DISPATCH> arrays via
+B<OSSL_ALGORITHM> arrays that are returned by the provider's
+provider_query_operation() function
+(see L<provider-base(7)/Provider Functions>).
+
+All these "functions" have a corresponding function type definition
+named B<OSSL_{name}_fn>, and a helper function to retrieve the
+function pointer from an B<OSSL_DISPATCH> element named
+B<OSSL_FUNC_{name}>.
+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<OSSL_DISPATCH> arrays are indexed by numbers that are provided as
+macros in L<openssl-core_dispatch.h(7)>, 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<provider-keymgmt(7)> 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<provctx> is the provider context generated during provider
+initialisation (see L<provider(7)>).
+
+OSSL_FUNC_kem_freectx() is passed a pointer to the provider side asymmetric
+kem context in the I<ctx> 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<ctx> 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<ctx>
+parameter, a pointer to a provider key object in the I<provkey> parameter and
+the I<name> 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<ctx>
+parameter.
+Unless I<out> is NULL, the data to be encapsulated is internally generated,
+and returned into the the buffer pointed to by the I<secret> parameter and the
+encapsulated data should also be written to the location pointed to by the
+I<out> 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<out> 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<ctx>
+parameter, a pointer to a provider key object in the I<provkey> parameter, and
+a I<name> 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<ctx>
+parameter.
+The data to be decapsulated is pointed to by the I<in> parameter which is I<inlen>
+bytes long.
+Unless I<out> is NULL, the decapsulated data should be written to the location
+pointed to by the I<out> parameter.
+The length of the decapsulated data should be written to I<*outlen>.
+If I<out> is NULL then the maximum length of the decapsulated data should be
+written to I<*outlen>.
+
+=head2 Asymmetric Key Encapsulation Parameters
+
+See L<OSSL_PARAM(3)> 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<ctx> and stores them in
+I<params>.
+OSSL_FUNC_kem_set_ctx_params() sets the asymmetric kem parameters associated
+with the given provider side asymmetric kem context I<ctx> to I<params>.
+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<OSSL_PARAM> 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<OSSL_PARAM(3)> for the use of B<OSSL_PARAM> 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<provider(7)>
+
+=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<https://www.openssl.org/source/license.html>.
+
+=cut
index 2f7f019..ead37b5 100644 (file)
@@ -154,6 +154,12 @@ The number for this operation is B<OSSL_OP_ASYM_CIPHER>.
 The functions the provider can offer are described in
 L<provider-asym_cipher(7)>
 
+=item Asymmetric Key Encapsulation
+
+In the OpenSSL libraries, the corresponding method object is B<EVP_KEM>.
+The number for this operation is B<OSSL_OP_KEM>.
+The functions the provider can offer are described in L<provider-kem(7)>
+
 =item Encoding
 
 In the OpenSSL libraries, the corresponding method object is
index 4912760..7016606 100644 (file)
@@ -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);
index ad1df71..5c6a4f4 100644 (file)
@@ -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
index 9a6cc2c..0fc2868 100644 (file)
@@ -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"
index ff97198..d389298 100644 (file)
@@ -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);
index cd0c51e..ee024ce 100644 (file)
@@ -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;
index d959645..dfb1139 100644 (file)
@@ -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:
index aec2626..9cf43eb 100644 (file)
@@ -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;
 }
index fe67e59..a2f6065 100644 (file)
@@ -1,2 +1,2 @@
 SUBDIRS=digests ciphers rands macs kdfs exchange keymgmt signature asymciphers \
-        encode_decode storemgmt
+        encode_decode storemgmt kem
index 91c5df4..36a0d4b 100644 (file)
@@ -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 (file)
index 0000000..e9f91cb
--- /dev/null
@@ -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 (file)
index 0000000..7cf0e91
--- /dev/null
@@ -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 <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/core_dispatch.h>
+#include <openssl/core_names.h>
+#include <openssl/rsa.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <crypto/rsa.h>
+#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 }
+};
index 5a8da35..659121c 100644 (file)
@@ -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 }
 };
index 823cdec..e42d668 100644 (file)
@@ -25,6 +25,7 @@
 #include <openssl/dsa.h>
 #include <openssl/dh.h>
 #include <openssl/safestack.h>
+#include <openssl/core_names.h>
 #include <openssl/x509.h>
 #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;
 }
 
index e9f6e56..0be0ada 100644 (file)
@@ -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: