]> git.ipfire.org Git - thirdparty/openssl.git/blobdiff - crypto/evp/kem.c
Copyright year updates
[thirdparty/openssl.git] / crypto / evp / kem.c
index cd8924ef395bca3150b4c716a77c480db0f4604f..f96012ccf01edc966fa900264431aa5515bb28a8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2020-2023 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
 #include <openssl/objects.h>
 #include <openssl/evp.h>
 #include "internal/cryptlib.h"
-#include "crypto/evp.h"
 #include "internal/provider.h"
+#include "internal/core.h"
+#include "crypto/evp.h"
 #include "evp_local.h"
 
 static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
-                        const OSSL_PARAM params[])
+                        const OSSL_PARAM params[], EVP_PKEY *authkey)
 {
     int ret = 0;
     EVP_KEM *kem = NULL;
     EVP_KEYMGMT *tmp_keymgmt = NULL;
-    void *provkey = NULL;
+    const OSSL_PROVIDER *tmp_prov = NULL;
+    void *provkey = NULL, *provauthkey = NULL;
     const char *supported_kem = NULL;
+    int iter;
 
     if (ctx == NULL || ctx->keytype == NULL) {
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
@@ -33,41 +36,120 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
     evp_pkey_ctx_free_old_ops(ctx);
     ctx->operation = operation;
 
+    if (ctx->pkey == NULL) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_NO_KEY_SET);
+        goto err;
+    }
+    if (authkey != NULL && authkey->type != ctx->pkey->type) {
+        ERR_raise(ERR_LIB_EVP, EVP_R_DIFFERENT_KEY_TYPES);
+        return 0;
+    }
     /*
-     * Ensure that the key is provided, either natively, or as a cached export.
+     * Try to derive the supported kem from |ctx->keymgmt|.
      */
-    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)) {
+    if (!ossl_assert(ctx->pkey->keymgmt == NULL
+                     || ctx->pkey->keymgmt == ctx->keymgmt)) {
+        ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    supported_kem = evp_keymgmt_util_query_operation_name(ctx->keymgmt,
+                                                          OSSL_OP_KEM);
+    if (supported_kem == NULL) {
         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.
+     * Because we cleared out old ops, we shouldn't need to worry about
+     * checking if kem is already there.
+     * We perform two iterations:
+     *
+     * 1.  Do the normal kem fetch, using the fetching data given by
+     *     the EVP_PKEY_CTX.
+     * 2.  Do the provider specific kem fetch, from the same provider
+     *     as |ctx->keymgmt|
+     *
+     * We then try to fetch the keymgmt from the same provider as the
+     * kem, and try to export |ctx->pkey| to that keymgmt (when this
+     * keymgmt happens to be the same as |ctx->keymgmt|, the export is
+     * a no-op, but we call it anyway to not complicate the code even
+     * more).
+     * If the export call succeeds (returns a non-NULL provider key pointer),
+     * we're done and can perform the operation itself.  If not, we perform
+     * the second iteration, or jump to legacy.
      */
-    if (supported_kem == NULL)
-        supported_kem = ctx->keytype;
+    for (iter = 1, provkey = NULL; iter < 3 && provkey == NULL; iter++) {
+        EVP_KEYMGMT *tmp_keymgmt_tofree = NULL;
 
-    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;
+        /*
+         * If we're on the second iteration, free the results from the first.
+         * They are NULL on the first iteration, so no need to check what
+         * iteration we're on.
+         */
+        EVP_KEM_free(kem);
+        EVP_KEYMGMT_free(tmp_keymgmt);
+
+        switch (iter) {
+        case 1:
+            kem = EVP_KEM_fetch(ctx->libctx, supported_kem, ctx->propquery);
+            if (kem != NULL)
+                tmp_prov = EVP_KEM_get0_provider(kem);
+            break;
+        case 2:
+            tmp_prov = EVP_KEYMGMT_get0_provider(ctx->keymgmt);
+            kem = evp_kem_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                          supported_kem, ctx->propquery);
+
+            if (kem == NULL) {
+                ERR_raise(ERR_LIB_EVP,
+                          EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+                ret = -2;
+                goto err;
+            }
+        }
+        if (kem == NULL)
+            continue;
+
+        /*
+         * Ensure that the key is provided, either natively, or as a cached
+         * export.  We start by fetching the keymgmt with the same name as
+         * |ctx->pkey|, but from the provider of the kem method, using the
+         * same property query as when fetching the kem method.
+         * With the keymgmt we found (if we did), we try to export |ctx->pkey|
+         * to it (evp_pkey_export_to_provider() is smart enough to only actually
+         * export it if |tmp_keymgmt| is different from |ctx->pkey|'s keymgmt)
+         */
+        tmp_keymgmt_tofree = tmp_keymgmt =
+            evp_keymgmt_fetch_from_prov((OSSL_PROVIDER *)tmp_prov,
+                                        EVP_KEYMGMT_get0_name(ctx->keymgmt),
+                                        ctx->propquery);
+        if (tmp_keymgmt != NULL) {
+            provkey = evp_pkey_export_to_provider(ctx->pkey, ctx->libctx,
+                                                  &tmp_keymgmt, ctx->propquery);
+            if (provkey != NULL && authkey != NULL) {
+                provauthkey = evp_pkey_export_to_provider(authkey, ctx->libctx,
+                                                          &tmp_keymgmt,
+                                                          ctx->propquery);
+                if (provauthkey == NULL) {
+                    EVP_KEM_free(kem);
+                    ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
+                    goto err;
+                }
+            }
+        }
+        if (tmp_keymgmt == NULL)
+            EVP_KEYMGMT_free(tmp_keymgmt_tofree);
+    }
+
+    if (provkey == NULL) {
+        EVP_KEM_free(kem);
+        ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
 
     ctx->op.encap.kem = kem;
-    ctx->op.encap.kemprovctx = kem->newctx(ossl_provider_ctx(kem->prov));
-    if (ctx->op.encap.kemprovctx == NULL) {
+    ctx->op.encap.algctx = kem->newctx(ossl_provider_ctx(kem->prov));
+    if (ctx->op.encap.algctx == NULL) {
         /* The provider key can stay in the cache */
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
@@ -75,26 +157,37 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
 
     switch (operation) {
     case EVP_PKEY_OP_ENCAPSULATE:
-        if (kem->encapsulate_init == NULL) {
+        if (provauthkey != NULL && kem->auth_encapsulate_init != NULL) {
+            ret = kem->auth_encapsulate_init(ctx->op.encap.algctx, provkey,
+                                             provauthkey, params);
+        } else if (provauthkey == NULL && kem->encapsulate_init != NULL) {
+            ret = kem->encapsulate_init(ctx->op.encap.algctx, provkey, params);
+        } else {
             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, params);
         break;
     case EVP_PKEY_OP_DECAPSULATE:
-        if (kem->decapsulate_init == NULL) {
+        if (provauthkey != NULL && kem->auth_decapsulate_init != NULL) {
+            ret = kem->auth_decapsulate_init(ctx->op.encap.algctx, provkey,
+                                             provauthkey, params);
+        } else if (provauthkey == NULL && kem->encapsulate_init != NULL) {
+            ret = kem->decapsulate_init(ctx->op.encap.algctx, provkey, params);
+        } else {
             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, params);
         break;
     default:
         ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
         goto err;
     }
 
+    EVP_KEYMGMT_free(tmp_keymgmt);
+    tmp_keymgmt = NULL;
+
     if (ret > 0)
         return 1;
  err:
@@ -102,12 +195,21 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
         evp_pkey_ctx_free_old_ops(ctx);
         ctx->operation = EVP_PKEY_OP_UNDEFINED;
     }
+    EVP_KEYMGMT_free(tmp_keymgmt);
     return ret;
 }
 
+int EVP_PKEY_auth_encapsulate_init(EVP_PKEY_CTX *ctx, EVP_PKEY *authpriv,
+                                   const OSSL_PARAM params[])
+{
+    if (authpriv == NULL)
+        return 0;
+    return evp_kem_init(ctx, EVP_PKEY_OP_ENCAPSULATE, params, authpriv);
+}
+
 int EVP_PKEY_encapsulate_init(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
 {
-    return evp_kem_init(ctx, EVP_PKEY_OP_ENCAPSULATE, params);
+    return evp_kem_init(ctx, EVP_PKEY_OP_ENCAPSULATE, params, NULL);
 }
 
 int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
@@ -122,7 +224,7 @@ int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
         return -1;
     }
 
-    if (ctx->op.encap.kemprovctx == NULL) {
+    if (ctx->op.encap.algctx == NULL) {
         ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
         return -2;
     }
@@ -130,13 +232,21 @@ int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
     if (out != NULL && secret == NULL)
         return 0;
 
-    return ctx->op.encap.kem->encapsulate(ctx->op.encap.kemprovctx,
+    return ctx->op.encap.kem->encapsulate(ctx->op.encap.algctx,
                                           out, outlen, secret, secretlen);
 }
 
 int EVP_PKEY_decapsulate_init(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[])
 {
-    return evp_kem_init(ctx, EVP_PKEY_OP_DECAPSULATE, params);
+    return evp_kem_init(ctx, EVP_PKEY_OP_DECAPSULATE, params, NULL);
+}
+
+int EVP_PKEY_auth_decapsulate_init(EVP_PKEY_CTX *ctx, EVP_PKEY *authpub,
+                                   const OSSL_PARAM params[])
+{
+    if (authpub == NULL)
+        return 0;
+    return evp_kem_init(ctx, EVP_PKEY_OP_DECAPSULATE, params, authpub);
 }
 
 int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx,
@@ -153,11 +263,11 @@ int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx,
         return -1;
     }
 
-    if (ctx->op.encap.kemprovctx == NULL) {
+    if (ctx->op.encap.algctx == NULL) {
         ERR_raise(ERR_LIB_EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
         return -2;
     }
-    return ctx->op.encap.kem->decapsulate(ctx->op.encap.kemprovctx,
+    return ctx->op.encap.kem->decapsulate(ctx->op.encap.algctx,
                                           secret, secretlen, in, inlen);
 }
 
@@ -165,20 +275,15 @@ 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);
+    if (kem == NULL)
         return NULL;
-    }
 
-    kem->lock = CRYPTO_THREAD_lock_new();
-    if (kem->lock == NULL) {
-        ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
+    if (!CRYPTO_NEW_REF(&kem->refcnt, 1)) {
         OPENSSL_free(kem);
         return NULL;
     }
     kem->prov = prov;
     ossl_provider_up_ref(prov);
-    kem->refcnt = 1;
 
     return kem;
 }
@@ -192,11 +297,13 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef,
     int gparamfncnt = 0, sparamfncnt = 0;
 
     if ((kem = evp_kem_new(prov)) == NULL) {
-        ERR_raise(ERR_LIB_EVP, ERR_R_MALLOC_FAILURE);
+        ERR_raise(ERR_LIB_EVP, ERR_R_EVP_LIB);
         goto err;
     }
 
     kem->name_id = name_id;
+    if ((kem->type_name = ossl_algorithm_get1_first_name(algodef)) == NULL)
+        goto err;
     kem->description = algodef->algorithm_description;
 
     for (; fns->function_id != 0; fns++) {
@@ -213,6 +320,12 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef,
             kem->encapsulate_init = OSSL_FUNC_kem_encapsulate_init(fns);
             encfncnt++;
             break;
+        case OSSL_FUNC_KEM_AUTH_ENCAPSULATE_INIT:
+            if (kem->auth_encapsulate_init != NULL)
+                break;
+            kem->auth_encapsulate_init = OSSL_FUNC_kem_auth_encapsulate_init(fns);
+            encfncnt++;
+            break;
         case OSSL_FUNC_KEM_ENCAPSULATE:
             if (kem->encapsulate != NULL)
                 break;
@@ -225,6 +338,12 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef,
             kem->decapsulate_init = OSSL_FUNC_kem_decapsulate_init(fns);
             decfncnt++;
             break;
+        case OSSL_FUNC_KEM_AUTH_DECAPSULATE_INIT:
+            if (kem->auth_decapsulate_init != NULL)
+                break;
+            kem->auth_decapsulate_init = OSSL_FUNC_kem_auth_decapsulate_init(fns);
+            decfncnt++;
+            break;
         case OSSL_FUNC_KEM_DECAPSULATE:
             if (kem->decapsulate != NULL)
                 break;
@@ -273,19 +392,21 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef,
         }
     }
     if (ctxfncnt != 2
-        || (encfncnt != 0 && encfncnt != 2)
-        || (decfncnt != 0 && decfncnt != 2)
-        || (encfncnt != 2 && decfncnt != 2)
+        || (encfncnt != 0 && encfncnt != 2 && encfncnt != 3)
+        || (decfncnt != 0 && decfncnt != 2 && decfncnt != 3)
+        || (encfncnt != decfncnt)
         || (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.
+         * a set of context functions (newctx and freectx) as well as a pair
+         * (or triplet) of "kem" functions:
+         * (encapsulate_init, (and/or auth_encapsulate_init), encapsulate) or
+         * (decapsulate_init, (and/or auth_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;
@@ -299,27 +420,29 @@ static void *evp_kem_from_algorithm(int name_id, const OSSL_ALGORITHM *algodef,
 
 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 i;
+
+    if (kem == NULL)
+        return;
+
+    CRYPTO_DOWN_REF(&kem->refcnt, &i);
+    if (i > 0)
+        return;
+    OPENSSL_free(kem->type_name);
+    ossl_provider_free(kem->prov);
+    CRYPTO_FREE_REF(&kem->refcnt);
+    OPENSSL_free(kem);
 }
 
 int EVP_KEM_up_ref(EVP_KEM *kem)
 {
     int ref = 0;
 
-    CRYPTO_UP_REF(&kem->refcnt, &ref, kem->lock);
+    CRYPTO_UP_REF(&kem->refcnt, &ref);
     return 1;
 }
 
-OSSL_PROVIDER *EVP_KEM_provider(const EVP_KEM *kem)
+OSSL_PROVIDER *EVP_KEM_get0_provider(const EVP_KEM *kem)
 {
     return kem->prov;
 }
@@ -333,17 +456,31 @@ EVP_KEM *EVP_KEM_fetch(OSSL_LIB_CTX *ctx, const char *algorithm,
                              (void (*)(void *))EVP_KEM_free);
 }
 
+EVP_KEM *evp_kem_fetch_from_prov(OSSL_PROVIDER *prov, const char *algorithm,
+                                 const char *properties)
+{
+    return evp_generic_fetch_from_prov(prov, OSSL_OP_KEM, algorithm, properties,
+                                       evp_kem_from_algorithm,
+                                       (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);
+    return kem != NULL && evp_is_a(kem->prov, kem->name_id, NULL, name);
 }
 
-int EVP_KEM_number(const EVP_KEM *kem)
+int evp_kem_get_number(const EVP_KEM *kem)
 {
     return kem->name_id;
 }
 
-const char *EVP_KEM_description(const EVP_KEM *kem)
+const char *EVP_KEM_get0_name(const EVP_KEM *kem)
+{
+    return kem->type_name;
+}
+
+const char *EVP_KEM_get0_description(const EVP_KEM *kem)
 {
     return kem->description;
 }
@@ -354,6 +491,7 @@ void EVP_KEM_do_all_provided(OSSL_LIB_CTX *libctx,
 {
     evp_generic_do_all(libctx, OSSL_OP_KEM, (void (*)(void *, void *))fn, arg,
                        evp_kem_from_algorithm,
+                       (int (*)(void *))EVP_KEM_up_ref,
                        (void (*)(void *))EVP_KEM_free);
 }
 
@@ -374,7 +512,7 @@ const OSSL_PARAM *EVP_KEM_gettable_ctx_params(const EVP_KEM *kem)
     if (kem == NULL || kem->gettable_ctx_params == NULL)
         return NULL;
 
-    provctx = ossl_provider_ctx(EVP_KEM_provider(kem));
+    provctx = ossl_provider_ctx(EVP_KEM_get0_provider(kem));
     return kem->gettable_ctx_params(NULL, provctx);
 }
 
@@ -385,6 +523,6 @@ const OSSL_PARAM *EVP_KEM_settable_ctx_params(const EVP_KEM *kem)
     if (kem == NULL || kem->settable_ctx_params == NULL)
         return NULL;
 
-    provctx = ossl_provider_ctx(EVP_KEM_provider(kem));
+    provctx = ossl_provider_ctx(EVP_KEM_get0_provider(kem));
     return kem->settable_ctx_params(NULL, provctx);
 }