summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--crypto/build.info2
-rw-r--r--crypto/ec/build.info1
-rw-r--r--crypto/ec/ec_key.c38
-rw-r--r--crypto/ec/ecx_key.c64
-rw-r--r--crypto/evp/evp_local.h2
-rw-r--r--crypto/evp/kem.c91
-rw-r--r--crypto/hpke/build.info5
-rw-r--r--crypto/hpke/hpke_util.c175
-rw-r--r--doc/build.info12
-rw-r--r--doc/man3/EVP_PKEY_decapsulate.pod26
-rw-r--r--doc/man3/EVP_PKEY_encapsulate.pod23
-rw-r--r--doc/man7/EVP_KEM-EC.pod78
-rw-r--r--doc/man7/EVP_KEM-X25519.pod77
-rw-r--r--doc/man7/EVP_PKEY-EC.pod8
-rw-r--r--doc/man7/EVP_PKEY-X25519.pod16
-rw-r--r--doc/man7/OSSL_PROVIDER-default.pod4
-rw-r--r--doc/man7/provider-kem.pod51
-rw-r--r--include/crypto/ec.h3
-rw-r--r--include/crypto/ecx.h3
-rw-r--r--include/crypto/hpke.h47
-rw-r--r--include/openssl/core_dispatch.h8
-rw-r--r--include/openssl/core_names.h5
-rw-r--r--include/openssl/evp.h5
-rw-r--r--providers/defltprov.c5
-rw-r--r--providers/implementations/exchange/ecx_exch.c64
-rw-r--r--providers/implementations/include/prov/ecx.h31
-rw-r--r--providers/implementations/include/prov/implementations.h2
-rw-r--r--providers/implementations/kem/build.info5
-rw-r--r--providers/implementations/kem/ec_kem.c841
-rw-r--r--providers/implementations/kem/eckem.h14
-rw-r--r--providers/implementations/kem/ecx_kem.c706
-rw-r--r--providers/implementations/kem/kem_util.c45
-rw-r--r--providers/implementations/keymgmt/ec_kmgmt.c21
-rw-r--r--providers/implementations/keymgmt/ecx_kmgmt.c32
-rw-r--r--test/build.info7
-rw-r--r--test/dhkem_test.inc707
-rw-r--r--test/evp_pkey_dhkem_test.c860
-rw-r--r--test/recipes/30-test_evp_pkey_dhkem.t18
-rw-r--r--util/libcrypto.num2
39 files changed, 3982 insertions, 122 deletions
diff --git a/crypto/build.info b/crypto/build.info
index 35e012d5d2..1c9ca3a809 100644
--- a/crypto/build.info
+++ b/crypto/build.info
@@ -6,7 +6,7 @@ SUBDIRS=objects buffer bio stack lhash rand evp asn1 pem x509 conf \
siphash sm3 des aes rc2 rc4 rc5 idea aria bf cast camellia \
seed sm4 chacha modes bn ec rsa dsa dh sm2 dso engine \
err comp http ocsp cms ts srp cmac ct async ess crmf cmp encode_decode \
- ffc
+ ffc hpke
LIBS=../libcrypto
diff --git a/crypto/ec/build.info b/crypto/ec/build.info
index a511e887a9..e4799ad37a 100644
--- a/crypto/ec/build.info
+++ b/crypto/ec/build.info
@@ -92,6 +92,7 @@ INCLUDE[ecp_nistz256-sparcv9.o]=..
INCLUDE[ecp_s390x_nistp.o]=..
INCLUDE[ecx_s390x.o]=..
INCLUDE[ecx_meth.o]=..
+INCLUDE[ecx_key.o]=..
GENERATE[ecp_nistz256-armv4.S]=asm/ecp_nistz256-armv4.pl
INCLUDE[ecp_nistz256-armv4.o]=..
diff --git a/crypto/ec/ec_key.c b/crypto/ec/ec_key.c
index 1bbca360e2..44bac9afa7 100644
--- a/crypto/ec/ec_key.c
+++ b/crypto/ec/ec_key.c
@@ -24,6 +24,7 @@
#endif
#include <openssl/self_test.h>
#include "prov/providercommon.h"
+#include "prov/ecx.h"
#include "crypto/bn.h"
static int ecdsa_keygen_pairwise_test(EC_KEY *eckey, OSSL_CALLBACK *cb,
@@ -350,6 +351,43 @@ err:
return ok;
}
+#ifndef FIPS_MODULE
+/*
+ * This is similar to ec_generate_key(), except it uses an ikm to
+ * derive the private key.
+ */
+int ossl_ec_generate_key_dhkem(EC_KEY *eckey,
+ const unsigned char *ikm, size_t ikmlen)
+{
+ int ok = 0;
+
+ if (eckey->priv_key == NULL) {
+ eckey->priv_key = BN_secure_new();
+ if (eckey->priv_key == NULL)
+ goto err;
+ }
+ if (ossl_ec_dhkem_derive_private(eckey, eckey->priv_key, ikm, ikmlen) <= 0)
+ goto err;
+ if (eckey->pub_key == NULL) {
+ eckey->pub_key = EC_POINT_new(eckey->group);
+ if (eckey->pub_key == NULL)
+ goto err;
+ }
+ if (!ossl_ec_key_simple_generate_public_key(eckey))
+ goto err;
+
+ ok = 1;
+err:
+ if (!ok) {
+ BN_clear_free(eckey->priv_key);
+ eckey->priv_key = NULL;
+ if (eckey->pub_key != NULL)
+ EC_POINT_set_to_infinity(eckey->group, eckey->pub_key);
+ }
+ return ok;
+}
+#endif
+
int ossl_ec_key_simple_generate_key(EC_KEY *eckey)
{
return ec_generate_key(eckey, 0);
diff --git a/crypto/ec/ecx_key.c b/crypto/ec/ecx_key.c
index dcec26c2e9..8cf7f1708c 100644
--- a/crypto/ec/ecx_key.c
+++ b/crypto/ec/ecx_key.c
@@ -9,7 +9,13 @@
#include <string.h>
#include <openssl/err.h>
+#include <openssl/proverr.h>
#include "crypto/ecx.h"
+#include "internal/common.h" /* for ossl_assert() */
+
+#ifdef S390X_EC_ASM
+# include "s390x_arch.h"
+#endif
ECX_KEY *ossl_ecx_key_new(OSSL_LIB_CTX *libctx, ECX_KEY_TYPE type, int haspubkey,
const char *propq)
@@ -96,3 +102,61 @@ unsigned char *ossl_ecx_key_allocate_privkey(ECX_KEY *key)
return key->privkey;
}
+
+int ossl_ecx_compute_key(ECX_KEY *peer, ECX_KEY *priv, size_t keylen,
+ unsigned char *secret, size_t *secretlen, size_t outlen)
+{
+ if (priv == NULL
+ || priv->privkey == NULL
+ || peer == NULL) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_MISSING_KEY);
+ return 0;
+ }
+
+ if (!ossl_assert(keylen == X25519_KEYLEN
+ || keylen == X448_KEYLEN)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KEY_LENGTH);
+ return 0;
+ }
+
+ if (secret == NULL) {
+ *secretlen = keylen;
+ return 1;
+ }
+ if (outlen < keylen) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ return 0;
+ }
+
+ if (keylen == X25519_KEYLEN) {
+#ifdef S390X_EC_ASM
+ if (OPENSSL_s390xcap_P.pcc[1]
+ & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X25519)) {
+ if (s390x_x25519_mul(secret, peer->pubkey, priv->privkey) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+ return 0;
+ }
+ } else
+#endif
+ if (ossl_x25519(secret, priv->privkey, peer->pubkey) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+ return 0;
+ }
+ } else {
+#ifdef S390X_EC_ASM
+ if (OPENSSL_s390xcap_P.pcc[1]
+ & S390X_CAPBIT(S390X_SCALAR_MULTIPLY_X448)) {
+ if (s390x_x448_mul(secret, peer->pubkey, priv->privkey) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+ return 0;
+ }
+ } else
+#endif
+ if (ossl_x448(secret, priv->privkey, peer->pubkey) == 0) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+ return 0;
+ }
+ }
+ *secretlen = keylen;
+ return 1;
+}
diff --git a/crypto/evp/evp_local.h b/crypto/evp/evp_local.h
index a853174452..8c26e8fd6d 100644
--- a/crypto/evp/evp_local.h
+++ b/crypto/evp/evp_local.h
@@ -228,6 +228,8 @@ struct evp_kem_st {
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;
+ OSSL_FUNC_kem_auth_encapsulate_init_fn *auth_encapsulate_init;
+ OSSL_FUNC_kem_auth_decapsulate_init_fn *auth_decapsulate_init;
} /* EVP_KEM */;
int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx, const char *pass,
diff --git a/crypto/evp/kem.c b/crypto/evp/kem.c
index bd28ede7ae..8c0c35b54b 100644
--- a/crypto/evp/kem.c
+++ b/crypto/evp/kem.c
@@ -18,13 +18,13 @@
#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;
const OSSL_PROVIDER *tmp_prov = NULL;
- void *provkey = NULL;
+ void *provkey = NULL, *provauthkey = NULL;
const char *supported_kem = NULL;
int iter;
@@ -40,7 +40,10 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
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;
+ }
/*
* Try to derive the supported kem from |ctx->keymgmt|.
*/
@@ -114,16 +117,26 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
* 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)
+ 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);
}
@@ -144,20 +157,28 @@ 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.algctx, 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.algctx, provkey, params);
break;
default:
ERR_raise(ERR_LIB_EVP, EVP_R_INITIALIZATION_ERROR);
@@ -178,9 +199,17 @@ static int evp_kem_init(EVP_PKEY_CTX *ctx, int operation,
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,
@@ -209,7 +238,15 @@ int EVP_PKEY_encapsulate(EVP_PKEY_CTX *ctx,
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,
@@ -288,6 +325,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;
@@ -300,6 +343,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;
@@ -348,19 +397,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;
diff --git a/crypto/hpke/build.info b/crypto/hpke/build.info
new file mode 100644
index 0000000000..f096cf170b
--- /dev/null
+++ b/crypto/hpke/build.info
@@ -0,0 +1,5 @@
+LIBS=../../libcrypto
+
+$COMMON=hpke_util.c
+
+SOURCE[../../libcrypto]=$COMMON
diff --git a/crypto/hpke/hpke_util.c b/crypto/hpke/hpke_util.c
new file mode 100644
index 0000000000..92f9892a41
--- /dev/null
+++ b/crypto/hpke/hpke_util.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2022 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 <openssl/core_names.h>
+#include <openssl/kdf.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include "crypto/hpke.h"
+#include "internal/packet.h"
+
+/*
+ * The largest value happens inside dhkem_extract_and_expand
+ * Which consists of a max dkmlen of 2*privkeylen + suiteid + small label
+ */
+#define LABELED_EXTRACT_SIZE (10 + 12 + 2 * OSSL_HPKE_MAX_PRIVATE)
+
+/*
+ * The largest value happens inside dhkem_extract_and_expand
+ * Which consists of a prklen of secretlen + contextlen of 3 encoded public keys
+ * + suiteid + small label
+ */
+#define LABELED_EXPAND_SIZE (LABELED_EXTRACT_SIZE + 3 * OSSL_HPKE_MAX_PUBLIC)
+
+/* ASCII: "HPKE-v1", in hex for EBCDIC compatibility */
+static const char LABEL_HPKEV1[] = "\x48\x50\x4B\x45\x2D\x76\x31";
+
+static int kdf_derive(EVP_KDF_CTX *kctx,
+ unsigned char *out, size_t outlen, int mode,
+ const unsigned char *salt, size_t saltlen,
+ const unsigned char *ikm, size_t ikmlen,
+ const unsigned char *info, size_t infolen)
+{
+ int ret;
+ OSSL_PARAM params[5], *p = params;
+
+ *p++ = OSSL_PARAM_construct_int(OSSL_KDF_PARAM_MODE, &mode);
+ if (salt != NULL)
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT,
+ (char *)salt, saltlen);
+ if (ikm != NULL)
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_KEY,
+ (char *)ikm, ikmlen);
+ if (info != NULL)
+ *p++ = OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_INFO,
+ (char *)info, infolen);
+ *p = OSSL_PARAM_construct_end();
+ ret = EVP_KDF_derive(kctx, out, outlen, params) > 0;
+ if (!ret)
+ ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_DURING_DERIVATION);
+ return ret;
+}
+
+int ossl_hpke_kdf_extract(EVP_KDF_CTX *kctx,
+ unsigned char *prk, size_t prklen,
+ const unsigned char *salt, size_t saltlen,
+ const unsigned char *ikm, size_t ikmlen)
+{
+ return kdf_derive(kctx, prk, prklen, EVP_KDF_HKDF_MODE_EXTRACT_ONLY,
+ salt, saltlen, ikm, ikmlen, NULL, 0);
+}
+
+/* Common code to perform a HKDF expand */
+int ossl_hpke_kdf_expand(EVP_KDF_CTX *kctx,
+ unsigned char *okm, size_t okmlen,
+ const unsigned char *prk, size_t prklen,
+ const unsigned char *info, size_t infolen)
+{
+ return kdf_derive(kctx, okm, okmlen, EVP_KDF_HKDF_MODE_EXPAND_ONLY,
+ NULL, 0, prk, prklen, info, infolen);
+}
+
+/*
+ * See RFC 9180 Section 4 LabelExtract()
+ */
+int ossl_hpke_labeled_extract(EVP_KDF_CTX *kctx,
+ unsigned char *prk, size_t prklen,
+ const unsigned char *salt, size_t saltlen,
+ const unsigned char *suiteid, size_t suiteidlen,
+ const char *label,
+ const unsigned char *ikm, size_t ikmlen)
+{
+ int ret = 0;
+ size_t labeled_ikmlen = 0;
+ unsigned char labeled_ikm[LABELED_EXTRACT_SIZE];
+ WPACKET pkt;
+
+ /* labeled_ikm = concat("HPKE-v1", suiteid, label, ikm) */
+ if (!WPACKET_init_static_len(&pkt, labeled_ikm, sizeof(labeled_ikm), 0)
+ || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, strlen(LABEL_HPKEV1))
+ || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
+ || !WPACKET_memcpy(&pkt, label, strlen(label))
+ || !WPACKET_memcpy(&pkt, ikm, ikmlen)
+ || !WPACKET_get_total_written(&pkt, &labeled_ikmlen)
+ || !WPACKET_finish(&pkt)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ goto end;
+ }
+
+ ret = ossl_hpke_kdf_extract(kctx, prk, prklen, salt, saltlen,
+ labeled_ikm, labeled_ikmlen);
+end:
+ WPACKET_cleanup(&pkt);
+ OPENSSL_cleanse(labeled_ikm, labeled_ikmlen);
+ return ret;
+}
+
+/*
+ * See RFC 9180 Section 4 LabelExpand()
+ */
+int ossl_hpke_labeled_expand(EVP_KDF_CTX *kctx,
+ unsigned char *okm, size_t okmlen,
+ const unsigned char *prk, size_t prklen,
+ const unsigned char *suiteid, size_t suiteidlen,
+ const char *label,
+ const unsigned char *info, size_t infolen)
+{
+ int ret = 0;
+ size_t labeled_infolen = 0;
+ unsigned char labeled_info[LABELED_EXPAND_SIZE];
+ WPACKET pkt;
+
+ /* labeled_info = concat(okmlen, "HPKE-v1", suiteid, label, info) */
+ if (!WPACKET_init_static_len(&pkt, labeled_info, sizeof(labeled_info), 0)
+ || !WPACKET_put_bytes_u16(&pkt, okmlen)
+ || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, strlen(LABEL_HPKEV1))
+ || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
+ || !WPACKET_memcpy(&pkt, label, strlen(label))
+ || !WPACKET_memcpy(&pkt, info, infolen)
+ || !WPACKET_get_total_written(&pkt, &labeled_infolen)
+ || !WPACKET_finish(&pkt)) {
+ ERR_raise(ERR_LIB_PROV, PROV_R_OUTPUT_BUFFER_TOO_SMALL);
+ goto end;
+ }
+
+ ret = ossl_hpke_kdf_expand(kctx, okm, okmlen,
+ prk, prklen, labeled_info, labeled_infolen);
+end:
+ WPACKET_cleanup(&pkt);
+ return ret;
+}
+
+/* Common code to create a HKDF ctx */
+EVP_KDF_CTX *ossl_kdf_ctx_create(const char *kdfname, const char *mdname,
+ OSSL_LIB_CTX *libctx, const char *propq)
+{
+ EVP_KDF *kdf;
+ EVP_KDF_CTX *kctx = NULL;
+
+ kdf = EVP_KDF_fetch(libctx, kdfname, propq);
+ kctx = EVP_KDF_CTX_new(kdf);
+ EVP_KDF_free(kdf);
+ if (kctx != NULL && mdname != NULL) {
+ OSSL_PARAM params[3], *p = params;
+
+ if (mdname != NULL)
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST,
+ (char *)mdname, 0);
+ if (propq != NULL)
+ *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_PROPERTIES,
+ (char *)propq, 0);
+ *p = OSSL_PARAM_construct_end();
+ if (EVP_KDF_CTX_set_params(kctx, params) <= 0) {
+ EVP_KDF_CTX_free(kctx);
+ return NULL;
+ }
+ }
+ return kctx;
+}
diff --git a/doc/build.info b/doc/build.info
index b90ad11eae..8b55bcdbc7 100644
--- a/doc/build.info
+++ b/doc/build.info
@@ -4209,10 +4209,18 @@ DEPEND[html/man7/EVP_KDF-X963.html]=man7/EVP_KDF-X963.pod
GENERATE[html/man7/EVP_KDF-X963.html]=man7/EVP_KDF-X963.pod
DEPEND[man/man7/EVP_KDF-X963.7]=man7/EVP_KDF-X963.pod
GENERATE[man/man7/EVP_KDF-X963.7]=man7/EVP_KDF-X963.pod
+DEPEND[html/man7/EVP_KEM-EC.html]=man7/EVP_KEM-EC.pod
+GENERATE[html/man7/EVP_KEM-EC.html]=man7/EVP_KEM-EC.pod
+DEPEND[man/man7/EVP_KEM-EC.7]=man7/EVP_KEM-EC.pod
+GENERATE[man/man7/EVP_KEM-EC.7]=man7/EVP_KEM-EC.pod
DEPEND[html/man7/EVP_KEM-RSA.html]=man7/EVP_KEM-RSA.pod
GENERATE[html/man7/EVP_KEM-RSA.html]=man7/EVP_KEM-RSA.pod
DEPEND[man/man7/EVP_KEM-RSA.7]=man7/EVP_KEM-RSA.pod
GENERATE[man/man7/EVP_KEM-RSA.7]=man7/EVP_KEM-RSA.pod
+DEPEND[html/man7/EVP_KEM-X25519.html]=man7/EVP_KEM-X25519.pod
+GENERATE[html/man7/EVP_KEM-X25519.html]=man7/EVP_KEM-X25519.pod
+DEPEND[man/man7/EVP_KEM-X25519.7]=man7/EVP_KEM-X25519.pod
+GENERATE[man/man7/EVP_KEM-X25519.7]=man7/EVP_KEM-X25519.pod
DEPEND[html/man7/EVP_KEYEXCH-DH.html]=man7/EVP_KEYEXCH-DH.pod
GENERATE[html/man7/EVP_KEYEXCH-DH.html]=man7/EVP_KEYEXCH-DH.pod
DEPEND[man/man7/EVP_KEYEXCH-DH.7]=man7/EVP_KEYEXCH-DH.pod
@@ -4630,7 +4638,9 @@ html/man7/EVP_KDF-TLS1_PRF.html \
html/man7/EVP_KDF-X942-ASN1.html \
html/man7/EVP_KDF-X942-CONCAT.html \
html/man7/EVP_KDF-X963.html \
+html/man7/EVP_KEM-EC.html \
html/man7/EVP_KEM-RSA.html \
+html/man7/EVP_KEM-X25519.html \
html/man7/EVP_KEYEXCH-DH.html \
html/man7/EVP_KEYEXCH-ECDH.html \
html/man7/EVP_KEYEXCH-X25519.html \
@@ -4755,7 +4765,9 @@ man/man7/EVP_KDF-TLS1_PRF.7 \
man/man7/EVP_KDF-X942-ASN1.7 \
man/man7/EVP_KDF-X942-CONCAT.7 \
man/man7/EVP_KDF-X963.7 \
+man/man7/EVP_KEM-EC.7 \
man/man7/EVP_KEM-RSA.7 \
+man/man7/EVP_KEM-X25519.7 \
man/man7/EVP_KEYEXCH-DH.7 \
man/man7/EVP_KEYEXCH-ECDH.7 \
man/man7/EVP_KEYEXCH-X25519.7 \
diff --git a/doc/man3/EVP_PKEY_decapsulate.pod b/doc/man3/EVP_PKEY_decapsulate.pod
index 529e318f9e..cdda54d12f 100644
--- a/doc/man3/EVP_PKEY_decapsulate.pod
+++ b/doc/man3/EVP_PKEY_decapsulate.pod
@@ -2,7 +2,7 @@
=head1 NAME
-EVP_PKEY_decapsulate_init, EVP_PKEY_decapsulate
+EVP_PKEY_decapsulate_init, EVP_PKEY_auth_decapsulate_init, EVP_PKEY_decapsulate
- Key decapsulation using a private key algorithm
=head1 SYNOPSIS
@@ -10,6 +10,8 @@ EVP_PKEY_decapsulate_init, EVP_PKEY_decapsulate
#include <openssl/evp.h>
int EVP_PKEY_decapsulate_init(EVP_PKEY_CTX *ctx, const OSSL_PARAM params[]);
+ int EVP_PKEY_auth_decapsulate_init(EVP_PKEY_CTX *ctx, EVP_PKEY *authpub,
+ const OSSL_PARAM params[]);
int EVP_PKEY_decapsulate(EVP_PKEY_CTX *ctx,
unsigned char *secret, size_t *secretlen,
const unsigned char *wrapped, size_t wrappedlen);
@@ -20,6 +22,10 @@ The EVP_PKEY_decapsulate_init() function initializes a private key algorithm
context I<ctx> for a decapsulation operation and then sets the I<params>
on the context in the same way as calling L<EVP_PKEY_CTX_set_params(3)>.
+The EVP_PKEY_auth_decapsulate_init() function is similiar to
+EVP_PKEY_decapsulate_init() but also passes an I<authpub> authentication public
+key that is used during decapsulation.
+
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.
@@ -35,9 +41,10 @@ for the operation may be set or modified using L<EVP_PKEY_CTX_set_params(3)>.
=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.
+EVP_PKEY_decapsulate_init(), EVP_PKEY_auth_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
@@ -73,7 +80,7 @@ Decapsulate data using RSA:
/* malloc failure */
/* Decapsulated secret data is secretlen bytes long */
- if (EVP_PKEY_decapsulaterctx, secret, &secretlen, in, inlen) <= 0)
+ if (EVP_PKEY_decapsulate(ctx, secret, &secretlen, in, inlen) <= 0)
/* Error */
@@ -81,15 +88,18 @@ Decapsulate data using RSA:
L<EVP_PKEY_CTX_new(3)>,
L<EVP_PKEY_encapsulate(3)>,
-L<EVP_KEM-RSA(7)>,
+L<EVP_KEM-RSA(7)>, L<EVP_KEM-X25519(7)>, L<EVP_KEM-EC(7)>
=head1 HISTORY
-These functions were added in OpenSSL 3.0.</