Index: /trunk/src/libs/openssl-3.3.1/crypto/hpke/Makefile.kmk
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/hpke/Makefile.kmk	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/hpke/Makefile.kmk	(revision 105970)
@@ -0,0 +1,39 @@
+# $Id$
+## @file
+# OpenSSL Sub-Makefile.
+#
+
+#
+# Copyright (C) 2022-2024 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+LIBRARIES += crypto_hpke
+crypto_hpke_TEMPLATE = SubLibCrypto
+crypto_hpke_DEFS = OPENSSL_NO_DEPRECATED
+crypto_hpke_SOURCES = \
+	hpke.c \
+	hpke_util.c
+$(evalcall VBOX_OPENSSL_X86,crypto_hpke)
+
+include $(FILE_KBUILD_SUB_FOOTER)
Index: /trunk/src/libs/openssl-3.3.1/crypto/hpke/build.info
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/hpke/build.info	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/hpke/build.info	(revision 105970)
@@ -0,0 +1,5 @@
+LIBS=../../libcrypto
+
+$COMMON=hpke_util.c hpke.c
+
+SOURCE[../../libcrypto]=$COMMON
Index: /trunk/src/libs/openssl-3.3.1/crypto/hpke/hpke.c
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/hpke/hpke.c	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/hpke/hpke.c	(revision 105970)
@@ -0,0 +1,1463 @@
+/*
+ * Copyright 2022-2024 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
+ */
+
+/* An OpenSSL-based HPKE implementation of RFC9180 */
+
+#include <string.h>
+#include <openssl/rand.h>
+#include <openssl/kdf.h>
+#include <openssl/core_names.h>
+#include <openssl/hpke.h>
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include "internal/hpke_util.h"
+#include "internal/nelem.h"
+#include "internal/common.h"
+
+/* default buffer size for keys and internal buffers we use */
+#define OSSL_HPKE_MAXSIZE 512
+
+/* Define HPKE labels from RFC9180 in hex for EBCDIC compatibility */
+/* "HPKE" - "suite_id" label for section 5.1 */
+static const char OSSL_HPKE_SEC51LABEL[] = "\x48\x50\x4b\x45";
+/* "psk_id_hash" - in key_schedule_context */
+static const char OSSL_HPKE_PSKIDHASH_LABEL[] = "\x70\x73\x6b\x5f\x69\x64\x5f\x68\x61\x73\x68";
+/*  "info_hash" - in key_schedule_context */
+static const char OSSL_HPKE_INFOHASH_LABEL[] = "\x69\x6e\x66\x6f\x5f\x68\x61\x73\x68";
+/*  "base_nonce" - base nonce calc label */
+static const char OSSL_HPKE_NONCE_LABEL[] = "\x62\x61\x73\x65\x5f\x6e\x6f\x6e\x63\x65";
+/*  "exp" - internal exporter secret generation label */
+static const char OSSL_HPKE_EXP_LABEL[] = "\x65\x78\x70";
+/*  "sec" - external label for exporting secret */
+static const char OSSL_HPKE_EXP_SEC_LABEL[] = "\x73\x65\x63";
+/*  "key" - label for use when generating key from shared secret */
+static const char OSSL_HPKE_KEY_LABEL[] = "\x6b\x65\x79";
+/*  "secret" - for generating shared secret */
+static const char OSSL_HPKE_SECRET_LABEL[] = "\x73\x65\x63\x72\x65\x74";
+
+/**
+ * @brief sender or receiver context
+ */
+struct ossl_hpke_ctx_st
+{
+    OSSL_LIB_CTX *libctx; /* library context */
+    char *propq; /* properties */
+    int mode; /* HPKE mode */
+    OSSL_HPKE_SUITE suite; /* suite */
+    const OSSL_HPKE_KEM_INFO *kem_info;
+    const OSSL_HPKE_KDF_INFO *kdf_info;
+    const OSSL_HPKE_AEAD_INFO *aead_info;
+    EVP_CIPHER *aead_ciph;
+    int role; /* sender(0) or receiver(1) */
+    uint64_t seq; /* aead sequence number */
+    unsigned char *shared_secret; /* KEM output, zz */
+    size_t shared_secretlen;
+    unsigned char *key; /* final aead key */
+    size_t keylen;
+    unsigned char *nonce; /* aead base nonce */
+    size_t noncelen;
+    unsigned char *exportersec; /* exporter secret */
+    size_t exporterseclen;
+    char *pskid; /* PSK stuff */
+    unsigned char *psk;
+    size_t psklen;
+    EVP_PKEY *authpriv; /* sender's authentication private key */
+    unsigned char *authpub; /* auth public key */
+    size_t authpublen;
+    unsigned char *ikme; /* IKM for sender deterministic key gen */
+    size_t ikmelen;
+};
+
+/**
+ * @brief check if KEM uses NIST curve or not
+ * @param kem_id is the externally supplied kem_id
+ * @return 1 for NIST curves, 0 for other
+ */
+static int hpke_kem_id_nist_curve(uint16_t kem_id)
+{
+    const OSSL_HPKE_KEM_INFO *kem_info;
+
+    kem_info = ossl_HPKE_KEM_INFO_find_id(kem_id);
+    return kem_info != NULL && kem_info->groupname != NULL;
+}
+
+/**
+ * @brief wrapper to import NIST curve public key as easily as x25519/x448
+ * @param libctx is the context to use
+ * @param propq is a properties string
+ * @param gname is the curve groupname
+ * @param buf is the binary buffer with the (uncompressed) public value
+ * @param buflen is the length of the private key buffer
+ * @return a working EVP_PKEY * or NULL
+ *
+ * Note that this could be a useful function to make public in
+ * future, but would likely require a name change.
+ */
+static EVP_PKEY *evp_pkey_new_raw_nist_public_key(OSSL_LIB_CTX *libctx,
+                                                  const char *propq,
+                                                  const char *gname,
+                                                  const unsigned char *buf,
+                                                  size_t buflen)
+{
+    OSSL_PARAM params[2];
+    EVP_PKEY *ret = NULL;
+    EVP_PKEY_CTX *cctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", propq);
+
+    params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                                 (char *)gname, 0);
+    params[1] = OSSL_PARAM_construct_end();
+    if (cctx == NULL
+        || EVP_PKEY_paramgen_init(cctx) <= 0
+        || EVP_PKEY_CTX_set_params(cctx, params) <= 0
+        || EVP_PKEY_paramgen(cctx, &ret) <= 0
+        || EVP_PKEY_set1_encoded_public_key(ret, buf, buflen) != 1) {
+        EVP_PKEY_CTX_free(cctx);
+        EVP_PKEY_free(ret);
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return NULL;
+    }
+    EVP_PKEY_CTX_free(cctx);
+    return ret;
+}
+
+/**
+ * @brief do the AEAD decryption
+ * @param hctx is the context to use
+ * @param iv is the initialisation vector
+ * @param aad is the additional authenticated data
+ * @param aadlen is the length of the aad
+ * @param ct is the ciphertext buffer
+ * @param ctlen is the ciphertext length (including tag).
+ * @param pt is the output buffer
+ * @param ptlen input/output, better be big enough on input, exact on output
+ * @return 1 on success, 0 otherwise
+ */
+static int hpke_aead_dec(OSSL_HPKE_CTX *hctx, const unsigned char *iv,
+                         const unsigned char *aad, size_t aadlen,
+                         const unsigned char *ct, size_t ctlen,
+                         unsigned char *pt, size_t *ptlen)
+{
+    int erv = 0;
+    EVP_CIPHER_CTX *ctx = NULL;
+    int len = 0;
+    size_t taglen;
+
+    taglen = hctx->aead_info->taglen;
+    if (ctlen <= taglen || *ptlen < ctlen - taglen) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    /* Create and initialise the context */
+    if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
+        return 0;
+    /* Initialise the decryption operation. */
+    if (EVP_DecryptInit_ex(ctx, hctx->aead_ciph, NULL, NULL, NULL) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
+                            hctx->noncelen, NULL) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    /* Initialise key and IV */
+    if (EVP_DecryptInit_ex(ctx, NULL, NULL, hctx->key, iv) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    /* Provide AAD. */
+    if (aadlen != 0 && aad != NULL) {
+        if (EVP_DecryptUpdate(ctx, NULL, &len, aad, aadlen) != 1) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+    if (EVP_DecryptUpdate(ctx, pt, &len, ct, ctlen - taglen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    *ptlen = len;
+    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG,
+                             taglen, (void *)(ct + ctlen - taglen))) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    /* Finalise decryption.  */
+    if (EVP_DecryptFinal_ex(ctx, pt + len, &len) <= 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    erv = 1;
+
+err:
+    if (erv != 1)
+        OPENSSL_cleanse(pt, *ptlen);
+    EVP_CIPHER_CTX_free(ctx);
+    return erv;
+}
+
+/**
+ * @brief do AEAD encryption as per the RFC
+ * @param hctx is the context to use
+ * @param iv is the initialisation vector
+ * @param aad is the additional authenticated data
+ * @param aadlen is the length of the aad
+ * @param pt is the plaintext buffer
+ * @param ptlen is the length of pt
+ * @param ct is the output buffer
+ * @param ctlen input/output, needs space for tag on input, exact on output
+ * @return 1 for success, 0 otherwise
+ */
+static int hpke_aead_enc(OSSL_HPKE_CTX *hctx, const unsigned char *iv,
+                         const unsigned char *aad, size_t aadlen,
+                         const unsigned char *pt, size_t ptlen,
+                         unsigned char *ct, size_t *ctlen)
+{
+    int erv = 0;
+    EVP_CIPHER_CTX *ctx = NULL;
+    int len;
+    size_t taglen = 0;
+    unsigned char tag[EVP_MAX_AEAD_TAG_LENGTH];
+
+    taglen = hctx->aead_info->taglen;
+    if (*ctlen <= taglen || ptlen > *ctlen - taglen) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (!ossl_assert(taglen <= sizeof(tag))) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    /* Create and initialise the context */
+    if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
+        return 0;
+    /* Initialise the encryption operation. */
+    if (EVP_EncryptInit_ex(ctx, hctx->aead_ciph, NULL, NULL, NULL) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN,
+                            hctx->noncelen, NULL) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    /* Initialise key and IV */
+    if (EVP_EncryptInit_ex(ctx, NULL, NULL, hctx->key, iv) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    /* Provide any AAD data. */
+    if (aadlen != 0 && aad != NULL) {
+        if (EVP_EncryptUpdate(ctx, NULL, &len, aad, aadlen) != 1) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+    if (EVP_EncryptUpdate(ctx, ct, &len, pt, ptlen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    *ctlen = len;
+    /* Finalise the encryption. */
+    if (EVP_EncryptFinal_ex(ctx, ct + len, &len) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    *ctlen += len;
+    /* Get tag. Not a duplicate so needs to be added to the ciphertext */
+    if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, taglen, tag) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    memcpy(ct + *ctlen, tag, taglen);
+    *ctlen += taglen;
+    erv = 1;
+
+err:
+    if (erv != 1)
+        OPENSSL_cleanse(ct, *ctlen);
+    EVP_CIPHER_CTX_free(ctx);
+    return erv;
+}
+
+/**
+ * @brief check mode is in-range and supported
+ * @param mode is the caller's chosen mode
+ * @return 1 for good mode, 0 otherwise
+ */
+static int hpke_mode_check(unsigned int mode)
+{
+    switch (mode) {
+    case OSSL_HPKE_MODE_BASE:
+    case OSSL_HPKE_MODE_PSK:
+    case OSSL_HPKE_MODE_AUTH:
+    case OSSL_HPKE_MODE_PSKAUTH:
+        break;
+    default:
+        return 0;
+    }
+    return 1;
+}
+
+/**
+ * @brief check if a suite is supported locally
+ * @param suite is the suite to check
+ * @return 1 for good, 0 otherwise
+ */
+static int hpke_suite_check(OSSL_HPKE_SUITE suite,
+                            const OSSL_HPKE_KEM_INFO **kem_info,
+                            const OSSL_HPKE_KDF_INFO **kdf_info,
+                            const OSSL_HPKE_AEAD_INFO **aead_info)
+{
+    const OSSL_HPKE_KEM_INFO *kem_info_;
+    const OSSL_HPKE_KDF_INFO *kdf_info_;
+    const OSSL_HPKE_AEAD_INFO *aead_info_;
+
+    /* check KEM, KDF and AEAD are supported here */
+    if ((kem_info_ = ossl_HPKE_KEM_INFO_find_id(suite.kem_id)) == NULL)
+        return 0;
+    if ((kdf_info_ = ossl_HPKE_KDF_INFO_find_id(suite.kdf_id)) == NULL)
+        return 0;
+    if ((aead_info_ = ossl_HPKE_AEAD_INFO_find_id(suite.aead_id)) == NULL)
+        return 0;
+
+    if (kem_info != NULL)
+        *kem_info = kem_info_;
+    if (kdf_info != NULL)
+        *kdf_info = kdf_info_;
+    if (aead_info != NULL)
+        *aead_info = aead_info_;
+
+    return 1;
+}
+
+/*
+ * @brief randomly pick a suite
+ * @param libctx is the context to use
+ * @param propq is a properties string
+ * @param suite is the result
+ * @return 1 for success, 0 otherwise
+ */
+static int hpke_random_suite(OSSL_LIB_CTX *libctx,
+                             const char *propq,
+                             OSSL_HPKE_SUITE *suite)
+{
+    const OSSL_HPKE_KEM_INFO *kem_info = NULL;
+    const OSSL_HPKE_KDF_INFO *kdf_info = NULL;
+    const OSSL_HPKE_AEAD_INFO *aead_info = NULL;
+
+    /* random kem, kdf and aead */
+    kem_info = ossl_HPKE_KEM_INFO_find_random(libctx);
+    if (kem_info == NULL)
+        return 0;
+    suite->kem_id = kem_info->kem_id;
+    kdf_info = ossl_HPKE_KDF_INFO_find_random(libctx);
+    if (kdf_info == NULL)
+        return 0;
+    suite->kdf_id = kdf_info->kdf_id;
+    aead_info = ossl_HPKE_AEAD_INFO_find_random(libctx);
+    if (aead_info == NULL)
+        return 0;
+    suite->aead_id = aead_info->aead_id;
+    return 1;
+}
+
+/*
+ * @brief tell the caller how big the ciphertext will be
+ *
+ * AEAD algorithms add a tag for data authentication.
+ * Those are almost always, but not always, 16 octets
+ * long, and who knows what will be true in the future.
+ * So this function allows a caller to find out how
+ * much data expansion they will see with a given suite.
+ *
+ * "enc" is the name used in RFC9180 for the encapsulated
+ * public value of the sender, who calls OSSL_HPKE_seal(),
+ * that is sent to the recipient, who calls OSSL_HPKE_open().
+ *
+ * @param suite is the suite to be used
+ * @param enclen points to what will be enc length
+ * @param clearlen is the length of plaintext
+ * @param cipherlen points to what will be ciphertext length (including tag)
+ * @return 1 for success, 0 otherwise
+ */
+static int hpke_expansion(OSSL_HPKE_SUITE suite,
+                          size_t *enclen,
+                          size_t clearlen,
+                          size_t *cipherlen)
+{
+    const OSSL_HPKE_AEAD_INFO *aead_info = NULL;
+    const OSSL_HPKE_KEM_INFO *kem_info = NULL;
+
+    if (cipherlen == NULL || enclen == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (hpke_suite_check(suite, &kem_info, NULL, &aead_info) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    *cipherlen = clearlen + aead_info->taglen;
+    *enclen = kem_info->Nenc;
+    return 1;
+}
+
+/*
+ * @brief expand and XOR the 64-bit unsigned seq with (nonce) buffer
+ * @param ctx is the HPKE context
+ * @param buf is the buffer for the XOR'd seq and nonce
+ * @param blen is the size of buf
+ * @return 0 for error, otherwise blen
+ */
+static size_t hpke_seqnonce2buf(OSSL_HPKE_CTX *ctx,
+                                unsigned char *buf, size_t blen)
+{
+    size_t i;
+    uint64_t seq_copy;
+
+    if (ctx == NULL || blen < sizeof(seq_copy) || blen != ctx->noncelen)
+        return 0;
+    seq_copy = ctx->seq;
+    memset(buf, 0, blen);
+    for (i = 0; i < sizeof(seq_copy); i++) {
+        buf[blen - i - 1] = seq_copy & 0xff;
+        seq_copy >>= 8;
+    }
+    for (i = 0; i < blen; i++)
+        buf[i] ^= ctx->nonce[i];
+    return blen;
+}
+
+/*
+ * @brief call the underlying KEM to encap
+ * @param ctx is the OSSL_HPKE_CTX
+ * @param enc is a buffer for the sender's ephemeral public value
+ * @param enclen is the size of enc on input, number of octets used on output
+ * @param pub is the recipient's public value
+ * @param publen is the length of pub
+ * @return 1 for success, 0 for error
+ */
+static int hpke_encap(OSSL_HPKE_CTX *ctx, unsigned char *enc, size_t *enclen,
+                      const unsigned char *pub, size_t publen)
+{
+    int erv = 0;
+    OSSL_PARAM params[3], *p = params;
+    size_t lsslen = 0, lenclen = 0;
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *pkR = NULL;
+    const OSSL_HPKE_KEM_INFO *kem_info = NULL;
+
+    if (ctx == NULL || enc == NULL || enclen == NULL || *enclen == 0
+        || pub == NULL || publen == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->shared_secret != NULL) {
+        /* only run the KEM once per OSSL_HPKE_CTX */
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    kem_info = ossl_HPKE_KEM_INFO_find_id(ctx->suite.kem_id);
+    if (kem_info == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    if (hpke_kem_id_nist_curve(ctx->suite.kem_id) == 1) {
+        pkR = evp_pkey_new_raw_nist_public_key(ctx->libctx, ctx->propq,
+                                               kem_info->groupname,
+                                               pub, publen);
+    } else {
+        pkR = EVP_PKEY_new_raw_public_key_ex(ctx->libctx,
+                                             kem_info->keytype,
+                                             ctx->propq, pub, publen);
+    }
+    if (pkR == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    pctx = EVP_PKEY_CTX_new_from_pkey(ctx->libctx, pkR, ctx->propq);
+    if (pctx == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION,
+                                            OSSL_KEM_PARAM_OPERATION_DHKEM,
+                                            0);
+    if (ctx->ikme != NULL) {
+        *p++ = OSSL_PARAM_construct_octet_string(OSSL_KEM_PARAM_IKME,
+                                                 ctx->ikme, ctx->ikmelen);
+    }
+    *p = OSSL_PARAM_construct_end();
+    if (ctx->mode == OSSL_HPKE_MODE_AUTH
+        || ctx->mode == OSSL_HPKE_MODE_PSKAUTH) {
+        if (EVP_PKEY_auth_encapsulate_init(pctx, ctx->authpriv,
+                                           params) != 1) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    } else {
+        if (EVP_PKEY_encapsulate_init(pctx, params) != 1) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+    lenclen = *enclen;
+    if (EVP_PKEY_encapsulate(pctx, NULL, &lenclen, NULL, &lsslen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    if (lenclen > *enclen) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+    ctx->shared_secret = OPENSSL_malloc(lsslen);
+    if (ctx->shared_secret == NULL)
+        goto err;
+    ctx->shared_secretlen = lsslen;
+    if (EVP_PKEY_encapsulate(pctx, enc, enclen, ctx->shared_secret,
+                             &ctx->shared_secretlen) != 1) {
+        ctx->shared_secretlen = 0;
+        OPENSSL_free(ctx->shared_secret);
+        ctx->shared_secret = NULL;
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    erv = 1;
+
+err:
+    EVP_PKEY_CTX_free(pctx);
+    EVP_PKEY_free(pkR);
+    return erv;
+}
+
+/*
+ * @brief call the underlying KEM to decap
+ * @param ctx is the OSSL_HPKE_CTX
+ * @param enc is a buffer for the sender's ephemeral public value
+ * @param enclen is the length of enc
+ * @param priv is the recipient's private value
+ * @return 1 for success, 0 for error
+ */
+static int hpke_decap(OSSL_HPKE_CTX *ctx,
+                      const unsigned char *enc, size_t enclen,
+                      EVP_PKEY *priv)
+{
+    int erv = 0;
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *spub = NULL;
+    OSSL_PARAM params[2], *p = params;
+    size_t lsslen = 0;
+
+    if (ctx == NULL || enc == NULL || enclen == 0 || priv == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->shared_secret != NULL) {
+        /* only run the KEM once per OSSL_HPKE_CTX */
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    pctx = EVP_PKEY_CTX_new_from_pkey(ctx->libctx, priv, ctx->propq);
+    if (pctx == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_KEM_PARAM_OPERATION,
+                                            OSSL_KEM_PARAM_OPERATION_DHKEM,
+                                            0);
+    *p = OSSL_PARAM_construct_end();
+    if (ctx->mode == OSSL_HPKE_MODE_AUTH
+        || ctx->mode == OSSL_HPKE_MODE_PSKAUTH) {
+        const OSSL_HPKE_KEM_INFO *kem_info = NULL;
+
+        kem_info = ossl_HPKE_KEM_INFO_find_id(ctx->suite.kem_id);
+        if (kem_info == NULL) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        if (hpke_kem_id_nist_curve(ctx->suite.kem_id) == 1) {
+            spub = evp_pkey_new_raw_nist_public_key(ctx->libctx, ctx->propq,
+                                                    kem_info->groupname,
+                                                    ctx->authpub,
+                                                    ctx->authpublen);
+        } else {
+            spub = EVP_PKEY_new_raw_public_key_ex(ctx->libctx,
+                                                  kem_info->keytype,
+                                                  ctx->propq,
+                                                  ctx->authpub,
+                                                  ctx->authpublen);
+        }
+        if (spub == NULL) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        if (EVP_PKEY_auth_decapsulate_init(pctx, spub, params) != 1) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    } else {
+        if (EVP_PKEY_decapsulate_init(pctx, params) != 1) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+    if (EVP_PKEY_decapsulate(pctx, NULL, &lsslen, enc, enclen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    ctx->shared_secret = OPENSSL_malloc(lsslen);
+    if (ctx->shared_secret == NULL)
+        goto err;
+    if (EVP_PKEY_decapsulate(pctx, ctx->shared_secret, &lsslen,
+                             enc, enclen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    ctx->shared_secretlen = lsslen;
+    erv = 1;
+
+err:
+    EVP_PKEY_CTX_free(pctx);
+    EVP_PKEY_free(spub);
+    if (erv == 0) {
+        OPENSSL_free(ctx->shared_secret);
+        ctx->shared_secret = NULL;
+        ctx->shared_secretlen = 0;
+    }
+    return erv;
+}
+
+/*
+ * @brief do "middle" of HPKE, between KEM and AEAD
+ * @param ctx is the OSSL_HPKE_CTX
+ * @param info is a buffer for the added binding information
+ * @param infolen is the length of info
+ * @return 0 for error, 1 for success
+ *
+ * This does all the HPKE extracts and expands as defined in RFC9180
+ * section 5.1, (badly termed there as a "key schedule") and sets the
+ * ctx fields for the shared_secret, nonce, key and exporter_secret
+ */
+static int hpke_do_middle(OSSL_HPKE_CTX *ctx,
+                          const unsigned char *info, size_t infolen)
+{
+    int erv = 0;
+    size_t ks_contextlen = OSSL_HPKE_MAXSIZE;
+    unsigned char ks_context[OSSL_HPKE_MAXSIZE];
+    size_t halflen = 0;
+    size_t pskidlen = 0;
+    const OSSL_HPKE_AEAD_INFO *aead_info = NULL;
+    const OSSL_HPKE_KDF_INFO *kdf_info = NULL;
+    size_t secretlen = OSSL_HPKE_MAXSIZE;
+    unsigned char secret[OSSL_HPKE_MAXSIZE];
+    EVP_KDF_CTX *kctx = NULL;
+    unsigned char suitebuf[6];
+    const char *mdname = NULL;
+
+    /* only let this be done once */
+    if (ctx->exportersec != NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (ossl_HPKE_KEM_INFO_find_id(ctx->suite.kem_id) == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    aead_info = ossl_HPKE_AEAD_INFO_find_id(ctx->suite.aead_id);
+    if (aead_info == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    kdf_info = ossl_HPKE_KDF_INFO_find_id(ctx->suite.kdf_id);
+    if (kdf_info == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    mdname = kdf_info->mdname;
+    /* create key schedule context */
+    memset(ks_context, 0, sizeof(ks_context));
+    ks_context[0] = (unsigned char)(ctx->mode % 256);
+    ks_contextlen--; /* remaining space */
+    halflen = kdf_info->Nh;
+    if ((2 * halflen) > ks_contextlen) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    /* check a psk was set if in that mode */
+    if (ctx->mode == OSSL_HPKE_MODE_PSK
+        || ctx->mode == OSSL_HPKE_MODE_PSKAUTH) {
+        if (ctx->psk == NULL || ctx->psklen == 0 || ctx->pskid == NULL) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+            return 0;
+        }
+    }
+    kctx = ossl_kdf_ctx_create("HKDF", mdname, ctx->libctx, ctx->propq);
+    if (kctx == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    pskidlen = (ctx->psk == NULL ? 0 : strlen(ctx->pskid));
+    /* full suite details as per RFC9180 sec 5.1 */
+    suitebuf[0] = ctx->suite.kem_id / 256;
+    suitebuf[1] = ctx->suite.kem_id % 256;
+    suitebuf[2] = ctx->suite.kdf_id / 256;
+    suitebuf[3] = ctx->suite.kdf_id % 256;
+    suitebuf[4] = ctx->suite.aead_id / 256;
+    suitebuf[5] = ctx->suite.aead_id % 256;
+    /* Extract and Expand variously... */
+    if (ossl_hpke_labeled_extract(kctx, ks_context + 1, halflen,
+                                  NULL, 0, OSSL_HPKE_SEC51LABEL,
+                                  suitebuf, sizeof(suitebuf),
+                                  OSSL_HPKE_PSKIDHASH_LABEL,
+                                  (unsigned char *)ctx->pskid, pskidlen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    if (ossl_hpke_labeled_extract(kctx, ks_context + 1 + halflen, halflen,
+                                  NULL, 0, OSSL_HPKE_SEC51LABEL,
+                                  suitebuf, sizeof(suitebuf),
+                                  OSSL_HPKE_INFOHASH_LABEL,
+                                  (unsigned char *)info, infolen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    ks_contextlen = 1 + 2 * halflen;
+    secretlen = kdf_info->Nh;
+    if (secretlen > OSSL_HPKE_MAXSIZE) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    if (ossl_hpke_labeled_extract(kctx, secret, secretlen,
+                                  ctx->shared_secret, ctx->shared_secretlen,
+                                  OSSL_HPKE_SEC51LABEL,
+                                  suitebuf, sizeof(suitebuf),
+                                  OSSL_HPKE_SECRET_LABEL,
+                                  ctx->psk, ctx->psklen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    if (ctx->suite.aead_id != OSSL_HPKE_AEAD_ID_EXPORTONLY) {
+        /* we only need nonce/key for non export AEADs */
+        ctx->noncelen = aead_info->Nn;
+        ctx->nonce = OPENSSL_malloc(ctx->noncelen);
+        if (ctx->nonce == NULL)
+            goto err;
+        if (ossl_hpke_labeled_expand(kctx, ctx->nonce, ctx->noncelen,
+                                     secret, secretlen, OSSL_HPKE_SEC51LABEL,
+                                     suitebuf, sizeof(suitebuf),
+                                     OSSL_HPKE_NONCE_LABEL,
+                                     ks_context, ks_contextlen) != 1) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+        ctx->keylen = aead_info->Nk;
+        ctx->key = OPENSSL_malloc(ctx->keylen);
+        if (ctx->key == NULL)
+            goto err;
+        if (ossl_hpke_labeled_expand(kctx, ctx->key, ctx->keylen,
+                                     secret, secretlen, OSSL_HPKE_SEC51LABEL,
+                                     suitebuf, sizeof(suitebuf),
+                                     OSSL_HPKE_KEY_LABEL,
+                                     ks_context, ks_contextlen) != 1) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    }
+    ctx->exporterseclen = kdf_info->Nh;
+    ctx->exportersec = OPENSSL_malloc(ctx->exporterseclen);
+    if (ctx->exportersec == NULL)
+        goto err;
+    if (ossl_hpke_labeled_expand(kctx, ctx->exportersec, ctx->exporterseclen,
+                                 secret, secretlen, OSSL_HPKE_SEC51LABEL,
+                                 suitebuf, sizeof(suitebuf),
+                                 OSSL_HPKE_EXP_LABEL,
+                                 ks_context, ks_contextlen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    erv = 1;
+
+err:
+    OPENSSL_cleanse(ks_context, OSSL_HPKE_MAXSIZE);
+    OPENSSL_cleanse(secret, OSSL_HPKE_MAXSIZE);
+    EVP_KDF_CTX_free(kctx);
+    return erv;
+}
+
+/*
+ * externally visible functions from below here, API documentation is
+ * in doc/man3/OSSL_HPKE_CTX_new.pod to avoid duplication
+ */
+
+OSSL_HPKE_CTX *OSSL_HPKE_CTX_new(int mode, OSSL_HPKE_SUITE suite, int role,
+                                 OSSL_LIB_CTX *libctx, const char *propq)
+{
+    OSSL_HPKE_CTX *ctx = NULL;
+    const OSSL_HPKE_KEM_INFO *kem_info;
+    const OSSL_HPKE_KDF_INFO *kdf_info;
+    const OSSL_HPKE_AEAD_INFO *aead_info;
+
+    if (hpke_mode_check(mode) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return NULL;
+    }
+    if (hpke_suite_check(suite, &kem_info, &kdf_info, &aead_info) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return NULL;
+    }
+    if (role != OSSL_HPKE_ROLE_SENDER && role != OSSL_HPKE_ROLE_RECEIVER) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    ctx = OPENSSL_zalloc(sizeof(*ctx));
+    if (ctx == NULL)
+        return NULL;
+    ctx->libctx = libctx;
+    if (propq != NULL) {
+        ctx->propq = OPENSSL_strdup(propq);
+        if (ctx->propq == NULL)
+            goto err;
+    }
+    if (suite.aead_id != OSSL_HPKE_AEAD_ID_EXPORTONLY) {
+        ctx->aead_ciph = EVP_CIPHER_fetch(libctx, aead_info->name, propq);
+        if (ctx->aead_ciph == NULL) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_FETCH_FAILED);
+            goto err;
+        }
+    }
+    ctx->role = role;
+    ctx->mode = mode;
+    ctx->suite = suite;
+    ctx->kem_info = kem_info;
+    ctx->kdf_info = kdf_info;
+    ctx->aead_info = aead_info;
+    return ctx;
+
+ err:
+    EVP_CIPHER_free(ctx->aead_ciph);
+    OPENSSL_free(ctx);
+    return NULL;
+}
+
+void OSSL_HPKE_CTX_free(OSSL_HPKE_CTX *ctx)
+{
+    if (ctx == NULL)
+        return;
+    EVP_CIPHER_free(ctx->aead_ciph);
+    OPENSSL_free(ctx->propq);
+    OPENSSL_clear_free(ctx->exportersec, ctx->exporterseclen);
+    OPENSSL_free(ctx->pskid);
+    OPENSSL_clear_free(ctx->psk, ctx->psklen);
+    OPENSSL_clear_free(ctx->key, ctx->keylen);
+    OPENSSL_clear_free(ctx->nonce, ctx->noncelen);
+    OPENSSL_clear_free(ctx->shared_secret, ctx->shared_secretlen);
+    OPENSSL_clear_free(ctx->ikme, ctx->ikmelen);
+    EVP_PKEY_free(ctx->authpriv);
+    OPENSSL_free(ctx->authpub);
+
+    OPENSSL_free(ctx);
+    return;
+}
+
+int OSSL_HPKE_CTX_set1_psk(OSSL_HPKE_CTX *ctx,
+                           const char *pskid,
+                           const unsigned char *psk, size_t psklen)
+{
+    if (ctx == NULL || pskid == NULL || psk == NULL || psklen == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (psklen > OSSL_HPKE_MAX_PARMLEN) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (psklen < OSSL_HPKE_MIN_PSKLEN) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (strlen(pskid) > OSSL_HPKE_MAX_PARMLEN) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (strlen(pskid) == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->mode != OSSL_HPKE_MODE_PSK
+        && ctx->mode != OSSL_HPKE_MODE_PSKAUTH) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    /* free previous values if any */
+    OPENSSL_clear_free(ctx->psk, ctx->psklen);
+    ctx->psk = OPENSSL_memdup(psk, psklen);
+    if (ctx->psk == NULL)
+        return 0;
+    ctx->psklen = psklen;
+    OPENSSL_free(ctx->pskid);
+    ctx->pskid = OPENSSL_strdup(pskid);
+    if (ctx->pskid == NULL) {
+        OPENSSL_clear_free(ctx->psk, ctx->psklen);
+        ctx->psk = NULL;
+        ctx->psklen = 0;
+        return 0;
+    }
+    return 1;
+}
+
+int OSSL_HPKE_CTX_set1_ikme(OSSL_HPKE_CTX *ctx,
+                            const unsigned char *ikme, size_t ikmelen)
+{
+    if (ctx == NULL || ikme == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if (ikmelen == 0 || ikmelen > OSSL_HPKE_MAX_PARMLEN) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->role != OSSL_HPKE_ROLE_SENDER) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    OPENSSL_clear_free(ctx->ikme, ctx->ikmelen);
+    ctx->ikme = OPENSSL_memdup(ikme, ikmelen);
+    if (ctx->ikme == NULL)
+        return 0;
+    ctx->ikmelen = ikmelen;
+    return 1;
+}
+
+int OSSL_HPKE_CTX_set1_authpriv(OSSL_HPKE_CTX *ctx, EVP_PKEY *priv)
+{
+    if (ctx == NULL || priv == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if (ctx->mode != OSSL_HPKE_MODE_AUTH
+        && ctx->mode != OSSL_HPKE_MODE_PSKAUTH) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->role != OSSL_HPKE_ROLE_SENDER) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    EVP_PKEY_free(ctx->authpriv);
+    ctx->authpriv = EVP_PKEY_dup(priv);
+    if (ctx->authpriv == NULL)
+        return 0;
+    return 1;
+}
+
+int OSSL_HPKE_CTX_set1_authpub(OSSL_HPKE_CTX *ctx,
+                               const unsigned char *pub, size_t publen)
+{
+    int erv = 0;
+    EVP_PKEY *pubp = NULL;
+    unsigned char *lpub = NULL;
+    size_t lpublen = 0;
+    const OSSL_HPKE_KEM_INFO *kem_info = NULL;
+
+    if (ctx == NULL || pub == NULL || publen == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    if (ctx->mode != OSSL_HPKE_MODE_AUTH
+        && ctx->mode != OSSL_HPKE_MODE_PSKAUTH) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->role != OSSL_HPKE_ROLE_RECEIVER) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    /* check the value seems like a good public key for this kem */
+    kem_info = ossl_HPKE_KEM_INFO_find_id(ctx->suite.kem_id);
+    if (kem_info == NULL)
+        return 0;
+    if (hpke_kem_id_nist_curve(ctx->suite.kem_id) == 1) {
+        pubp = evp_pkey_new_raw_nist_public_key(ctx->libctx, ctx->propq,
+                                                kem_info->groupname,
+                                                pub, publen);
+    } else {
+        pubp = EVP_PKEY_new_raw_public_key_ex(ctx->libctx,
+                                              kem_info->keytype,
+                                              ctx->propq,
+                                              pub, publen);
+    }
+    if (pubp == NULL) {
+        /* can happen based on external input - buffer value may be garbage */
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        goto err;
+    }
+    /*
+     * extract out the public key in encoded form so we
+     * should be fine even if given compressed form
+     */
+    lpub = OPENSSL_malloc(OSSL_HPKE_MAXSIZE);
+    if (lpub == NULL)
+        goto err;
+    if (EVP_PKEY_get_octet_string_param(pubp,
+                                        OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+                                        lpub, OSSL_HPKE_MAXSIZE, &lpublen)
+        != 1) {
+        OPENSSL_free(lpub);
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    /* free up old value */
+    OPENSSL_free(ctx->authpub);
+    ctx->authpub = lpub;
+    ctx->authpublen = lpublen;
+    erv = 1;
+
+err:
+    EVP_PKEY_free(pubp);
+    return erv;
+}
+
+int OSSL_HPKE_CTX_get_seq(OSSL_HPKE_CTX *ctx, uint64_t *seq)
+{
+    if (ctx == NULL || seq == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    *seq = ctx->seq;
+    return 1;
+}
+
+int OSSL_HPKE_CTX_set_seq(OSSL_HPKE_CTX *ctx, uint64_t seq)
+{
+    if (ctx == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    /*
+     * We disallow senders from doing this as it's dangerous
+     * Receivers are ok to use this, as no harm should ensue
+     * if they go wrong.
+     */
+    if (ctx->role == OSSL_HPKE_ROLE_SENDER) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    ctx->seq = seq;
+    return 1;
+}
+
+int OSSL_HPKE_encap(OSSL_HPKE_CTX *ctx,
+                    unsigned char *enc, size_t *enclen,
+                    const unsigned char *pub, size_t publen,
+                    const unsigned char *info, size_t infolen)
+{
+    int erv = 1;
+    size_t minenc = 0;
+
+    if (ctx == NULL || enc == NULL || enclen == NULL || *enclen == 0
+        || pub == NULL || publen == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->role != OSSL_HPKE_ROLE_SENDER) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (infolen > OSSL_HPKE_MAX_INFOLEN) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (infolen > 0 && info == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    minenc = OSSL_HPKE_get_public_encap_size(ctx->suite);
+    if (minenc == 0 || minenc > *enclen) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->shared_secret != NULL) {
+        /* only allow one encap per OSSL_HPKE_CTX */
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (hpke_encap(ctx, enc, enclen, pub, publen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    /*
+     * note that the info is not part of the context as it
+     * only needs to be used once here so doesn't need to
+     * be stored
+     */
+    erv = hpke_do_middle(ctx, info, infolen);
+    return erv;
+}
+
+int OSSL_HPKE_decap(OSSL_HPKE_CTX *ctx,
+                    const unsigned char *enc, size_t enclen,
+                    EVP_PKEY *recippriv,
+                    const unsigned char *info, size_t infolen)
+{
+    int erv = 1;
+    size_t minenc = 0;
+
+    if (ctx == NULL || enc == NULL || enclen == 0 || recippriv == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->role != OSSL_HPKE_ROLE_RECEIVER) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (infolen > OSSL_HPKE_MAX_INFOLEN) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (infolen > 0 && info == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    minenc = OSSL_HPKE_get_public_encap_size(ctx->suite);
+    if (minenc == 0 || minenc > enclen) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->shared_secret != NULL) {
+        /* only allow one encap per OSSL_HPKE_CTX */
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    erv = hpke_decap(ctx, enc, enclen, recippriv);
+    if (erv != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    /*
+     * note that the info is not part of the context as it
+     * only needs to be used once here so doesn't need to
+     * be stored
+     */
+    erv = hpke_do_middle(ctx, info, infolen);
+    return erv;
+}
+
+int OSSL_HPKE_seal(OSSL_HPKE_CTX *ctx,
+                   unsigned char *ct, size_t *ctlen,
+                   const unsigned char *aad, size_t aadlen,
+                   const unsigned char *pt, size_t ptlen)
+{
+    unsigned char seqbuf[OSSL_HPKE_MAX_NONCELEN];
+    size_t seqlen = 0;
+
+    if (ctx == NULL || ct == NULL || ctlen == NULL || *ctlen == 0
+        || pt == NULL || ptlen == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->role != OSSL_HPKE_ROLE_SENDER) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if ((ctx->seq + 1) == 0) { /* wrap around imminent !!! */
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (ctx->key == NULL || ctx->nonce == NULL) {
+        /* need to have done an encap first, info can be NULL */
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    seqlen = hpke_seqnonce2buf(ctx, seqbuf, sizeof(seqbuf));
+    if (seqlen == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    if (hpke_aead_enc(ctx, seqbuf, aad, aadlen, pt, ptlen, ct, ctlen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        OPENSSL_cleanse(seqbuf, sizeof(seqbuf));
+        return 0;
+    } else {
+        ctx->seq++;
+    }
+    OPENSSL_cleanse(seqbuf, sizeof(seqbuf));
+    return 1;
+}
+
+int OSSL_HPKE_open(OSSL_HPKE_CTX *ctx,
+                   unsigned char *pt, size_t *ptlen,
+                   const unsigned char *aad, size_t aadlen,
+                   const unsigned char *ct, size_t ctlen)
+{
+    unsigned char seqbuf[OSSL_HPKE_MAX_NONCELEN];
+    size_t seqlen = 0;
+
+    if (ctx == NULL || pt == NULL || ptlen == NULL || *ptlen == 0
+        || ct == NULL || ctlen == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->role != OSSL_HPKE_ROLE_RECEIVER) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if ((ctx->seq + 1) == 0) { /* wrap around imminent !!! */
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    if (ctx->key == NULL || ctx->nonce == NULL) {
+        /* need to have done an encap first, info can be NULL */
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    seqlen = hpke_seqnonce2buf(ctx, seqbuf, sizeof(seqbuf));
+    if (seqlen == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    if (hpke_aead_dec(ctx, seqbuf, aad, aadlen, ct, ctlen, pt, ptlen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        OPENSSL_cleanse(seqbuf, sizeof(seqbuf));
+        return 0;
+    }
+    ctx->seq++;
+    OPENSSL_cleanse(seqbuf, sizeof(seqbuf));
+    return 1;
+}
+
+int OSSL_HPKE_export(OSSL_HPKE_CTX *ctx,
+                     unsigned char *secret, size_t secretlen,
+                     const unsigned char *label, size_t labellen)
+{
+    int erv = 0;
+    EVP_KDF_CTX *kctx = NULL;
+    unsigned char suitebuf[6];
+    const char *mdname = NULL;
+    const OSSL_HPKE_KDF_INFO *kdf_info = NULL;
+
+    if (ctx == NULL || secret == NULL || secretlen == 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (labellen > OSSL_HPKE_MAX_PARMLEN) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (labellen > 0 && label == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (ctx->exportersec == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        return 0;
+    }
+    kdf_info = ossl_HPKE_KDF_INFO_find_id(ctx->suite.kdf_id);
+    if (kdf_info == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    mdname = kdf_info->mdname;
+    kctx = ossl_kdf_ctx_create("HKDF", mdname, ctx->libctx, ctx->propq);
+    if (kctx == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        return 0;
+    }
+    /* full suiteid as per RFC9180 sec 5.3 */
+    suitebuf[0] = ctx->suite.kem_id / 256;
+    suitebuf[1] = ctx->suite.kem_id % 256;
+    suitebuf[2] = ctx->suite.kdf_id / 256;
+    suitebuf[3] = ctx->suite.kdf_id % 256;
+    suitebuf[4] = ctx->suite.aead_id / 256;
+    suitebuf[5] = ctx->suite.aead_id % 256;
+    erv = ossl_hpke_labeled_expand(kctx, secret, secretlen,
+                                   ctx->exportersec, ctx->exporterseclen,
+                                   OSSL_HPKE_SEC51LABEL,
+                                   suitebuf, sizeof(suitebuf),
+                                   OSSL_HPKE_EXP_SEC_LABEL,
+                                   label, labellen);
+    EVP_KDF_CTX_free(kctx);
+    if (erv != 1)
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+    return erv;
+}
+
+int OSSL_HPKE_keygen(OSSL_HPKE_SUITE suite,
+                     unsigned char *pub, size_t *publen, EVP_PKEY **priv,
+                     const unsigned char *ikm, size_t ikmlen,
+                     OSSL_LIB_CTX *libctx, const char *propq)
+{
+    int erv = 0; /* Our error return value - 1 is success */
+    EVP_PKEY_CTX *pctx = NULL;
+    EVP_PKEY *skR = NULL;
+    const OSSL_HPKE_KEM_INFO *kem_info = NULL;
+    OSSL_PARAM params[3], *p = params;
+
+    if (pub == NULL || publen == NULL || *publen == 0 || priv == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (hpke_suite_check(suite, &kem_info, NULL, NULL) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if ((ikmlen > 0 && ikm == NULL)
+        || (ikmlen == 0 && ikm != NULL)
+        || ikmlen > OSSL_HPKE_MAX_PARMLEN) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+
+    if (hpke_kem_id_nist_curve(suite.kem_id) == 1) {
+        *p++ = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+                                                (char *)kem_info->groupname, 0);
+        pctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", propq);
+    } else {
+        pctx = EVP_PKEY_CTX_new_from_name(libctx, kem_info->keytype, propq);
+    }
+    if (pctx == NULL
+        || EVP_PKEY_keygen_init(pctx) <= 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    if (ikm != NULL)
+        *p++ = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_DHKEM_IKM,
+                                                 (char *)ikm, ikmlen);
+    *p = OSSL_PARAM_construct_end();
+    if (EVP_PKEY_CTX_set_params(pctx, params) <= 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    if (EVP_PKEY_generate(pctx, &skR) <= 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    EVP_PKEY_CTX_free(pctx);
+    pctx = NULL;
+    if (EVP_PKEY_get_octet_string_param(skR, OSSL_PKEY_PARAM_ENCODED_PUBLIC_KEY,
+                                        pub, *publen, publen) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    *priv = skR;
+    erv = 1;
+
+err:
+    if (erv != 1)
+        EVP_PKEY_free(skR);
+    EVP_PKEY_CTX_free(pctx);
+    return erv;
+}
+
+int OSSL_HPKE_suite_check(OSSL_HPKE_SUITE suite)
+{
+    return hpke_suite_check(suite, NULL, NULL, NULL);
+}
+
+int OSSL_HPKE_get_grease_value(const OSSL_HPKE_SUITE *suite_in,
+                               OSSL_HPKE_SUITE *suite,
+                               unsigned char *enc, size_t *enclen,
+                               unsigned char *ct, size_t ctlen,
+                               OSSL_LIB_CTX *libctx, const char *propq)
+{
+    OSSL_HPKE_SUITE chosen;
+    size_t plen = 0;
+    const OSSL_HPKE_KEM_INFO *kem_info = NULL;
+    const OSSL_HPKE_AEAD_INFO *aead_info = NULL;
+    EVP_PKEY *fakepriv = NULL;
+
+    if (enc == NULL || enclen == 0
+        || ct == NULL || ctlen == 0 || suite == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+    if (suite_in == NULL) {
+        /* choose a random suite */
+        if (hpke_random_suite(libctx, propq, &chosen) != 1) {
+            ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+            goto err;
+        }
+    } else {
+        chosen = *suite_in;
+    }
+    if (hpke_suite_check(chosen, &kem_info, NULL, &aead_info) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    *suite = chosen;
+    /* make sure room for tag and one plaintext octet */
+    if (aead_info->taglen >= ctlen) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    /* publen */
+    plen = kem_info->Npk;
+    if (plen > *enclen) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    /*
+     * In order for our enc to look good for sure, we generate and then
+     * delete a real key for that curve - bit OTT but it ensures we do
+     * get the encoding right (e.g. 0x04 as 1st octet for NIST curves in
+     * uncompressed form) and that the value really does map to a point on
+     * the relevant curve.
+     */
+    if (OSSL_HPKE_keygen(chosen, enc, enclen, &fakepriv, NULL, 0,
+                         libctx, propq) != 1) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    EVP_PKEY_free(fakepriv);
+    if (RAND_bytes_ex(libctx, ct, ctlen, 0) <= 0) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
+        goto err;
+    }
+    return 1;
+err:
+    return 0;
+}
+
+int OSSL_HPKE_str2suite(const char *str, OSSL_HPKE_SUITE *suite)
+{
+    return ossl_hpke_str2suite(str, suite);
+}
+
+size_t OSSL_HPKE_get_ciphertext_size(OSSL_HPKE_SUITE suite, size_t clearlen)
+{
+    size_t enclen = 0;
+    size_t cipherlen = 0;
+
+    if (hpke_expansion(suite, &enclen, clearlen, &cipherlen) != 1)
+        return 0;
+    return cipherlen;
+}
+
+size_t OSSL_HPKE_get_public_encap_size(OSSL_HPKE_SUITE suite)
+{
+    size_t enclen = 0;
+    size_t cipherlen = 0;
+    size_t clearlen = 16;
+
+    if (hpke_expansion(suite, &enclen, clearlen, &cipherlen) != 1)
+        return 0;
+    return enclen;
+}
+
+size_t OSSL_HPKE_get_recommended_ikmelen(OSSL_HPKE_SUITE suite)
+{
+    const OSSL_HPKE_KEM_INFO *kem_info = NULL;
+
+    if (hpke_suite_check(suite, &kem_info, NULL, NULL) != 1)
+        return 0;
+    if (kem_info == NULL)
+        return 0;
+
+    return kem_info->Nsk;
+}
Index: /trunk/src/libs/openssl-3.3.1/crypto/hpke/hpke_util.c
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/hpke/hpke_util.c	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/hpke/hpke_util.c	(revision 105970)
@@ -0,0 +1,528 @@
+/*
+ * Copyright 2022-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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <string.h>
+#include <openssl/core_names.h>
+#include <openssl/kdf.h>
+#include <openssl/params.h>
+#include <openssl/err.h>
+#include <openssl/proverr.h>
+#include <openssl/hpke.h>
+#include <openssl/sha.h>
+#include <openssl/rand.h>
+#include "crypto/ecx.h"
+#include "crypto/rand.h"
+#include "internal/hpke_util.h"
+#include "internal/packet.h"
+#include "internal/nelem.h"
+#include "internal/common.h"
+
+/*
+ * Delimiter used in OSSL_HPKE_str2suite
+ */
+#define OSSL_HPKE_STR_DELIMCHAR ','
+
+/*
+ * table with identifier and synonym strings
+ * right now, there are 4 synonyms for each - a name, a hex string
+ * a hex string with a leading zero and a decimal string - more
+ * could be added but that seems like enough
+ */
+typedef struct {
+    uint16_t id;
+    char *synonyms[4];
+} synonymttab_t;
+
+/* max length of string we'll try map to a suite */
+#define OSSL_HPKE_MAX_SUITESTR 38
+
+/* Define HPKE labels from RFC9180 in hex for EBCDIC compatibility */
+/* ASCII: "HPKE-v1", in hex for EBCDIC compatibility */
+static const char LABEL_HPKEV1[] = "\x48\x50\x4B\x45\x2D\x76\x31";
+
+/*
+ * Note that if additions are made to the set of IANA codepoints
+ * and the tables below, corresponding additions should also be
+ * made to the synonymtab tables a little further down so that
+ * OSSL_HPKE_str2suite() continues to function correctly.
+ *
+ * The canonical place to check for IANA registered codepoints
+ * is: https://www.iana.org/assignments/hpke/hpke.xhtml
+ */
+
+/*
+ * @brief table of KEMs
+ * See RFC9180 Section 7.1 "Table 2 KEM IDs"
+ */
+static const OSSL_HPKE_KEM_INFO hpke_kem_tab[] = {
+#ifndef OPENSSL_NO_EC
+    { OSSL_HPKE_KEM_ID_P256, "EC", OSSL_HPKE_KEMSTR_P256,
+      LN_sha256, SHA256_DIGEST_LENGTH, 65, 65, 32, 0xFF },
+    { OSSL_HPKE_KEM_ID_P384, "EC", OSSL_HPKE_KEMSTR_P384,
+      LN_sha384, SHA384_DIGEST_LENGTH, 97, 97, 48, 0xFF },
+    { OSSL_HPKE_KEM_ID_P521, "EC", OSSL_HPKE_KEMSTR_P521,
+      LN_sha512, SHA512_DIGEST_LENGTH, 133, 133, 66, 0x01 },
+# ifndef OPENSSL_NO_ECX
+    { OSSL_HPKE_KEM_ID_X25519, OSSL_HPKE_KEMSTR_X25519, NULL,
+      LN_sha256, SHA256_DIGEST_LENGTH,
+      X25519_KEYLEN, X25519_KEYLEN, X25519_KEYLEN, 0x00 },
+    { OSSL_HPKE_KEM_ID_X448, OSSL_HPKE_KEMSTR_X448, NULL,
+      LN_sha512, SHA512_DIGEST_LENGTH,
+      X448_KEYLEN, X448_KEYLEN, X448_KEYLEN, 0x00 }
+# endif
+#else
+    { OSSL_HPKE_KEM_ID_RESERVED, NULL, NULL, NULL, 0, 0, 0, 0, 0x00 }
+#endif
+};
+
+/*
+ * @brief table of AEADs
+ * See RFC9180 Section 7.2 "Table 3 KDF IDs"
+ */
+static const OSSL_HPKE_AEAD_INFO hpke_aead_tab[] = {
+    { OSSL_HPKE_AEAD_ID_AES_GCM_128, LN_aes_128_gcm, 16, 16,
+      OSSL_HPKE_MAX_NONCELEN },
+    { OSSL_HPKE_AEAD_ID_AES_GCM_256, LN_aes_256_gcm, 16, 32,
+      OSSL_HPKE_MAX_NONCELEN },
+#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305)
+    { OSSL_HPKE_AEAD_ID_CHACHA_POLY1305, LN_chacha20_poly1305, 16, 32,
+      OSSL_HPKE_MAX_NONCELEN },
+#endif
+    { OSSL_HPKE_AEAD_ID_EXPORTONLY, NULL, 0, 0, 0 }
+};
+
+/*
+ * @brief table of KDFs
+ * See RFC9180 Section 7.3 "Table 5 AEAD IDs"
+ */
+static const OSSL_HPKE_KDF_INFO hpke_kdf_tab[] = {
+    { OSSL_HPKE_KDF_ID_HKDF_SHA256, LN_sha256, SHA256_DIGEST_LENGTH },
+    { OSSL_HPKE_KDF_ID_HKDF_SHA384, LN_sha384, SHA384_DIGEST_LENGTH },
+    { OSSL_HPKE_KDF_ID_HKDF_SHA512, LN_sha512, SHA512_DIGEST_LENGTH }
+};
+
+/**
+ * Synonym tables for KEMs, KDFs and AEADs: idea is to allow
+ * mapping strings to suites with a little flexibility in terms
+ * of allowing a name or a couple of forms of number (for
+ * the IANA codepoint). If new IANA codepoints are allocated
+ * then these tables should be updated at the same time as the
+ * others above.
+ *
+ * The function to use these is ossl_hpke_str2suite() further down
+ * this file and shouldn't need modification so long as the table
+ * sizes (i.e. allow exactly 4 synonyms) don't change.
+ */
+static const synonymttab_t kemstrtab[] = {
+    {OSSL_HPKE_KEM_ID_P256,
+     {OSSL_HPKE_KEMSTR_P256, "0x10", "0x10", "16" }},
+    {OSSL_HPKE_KEM_ID_P384,
+     {OSSL_HPKE_KEMSTR_P384, "0x11", "0x11", "17" }},
+    {OSSL_HPKE_KEM_ID_P521,
+     {OSSL_HPKE_KEMSTR_P521, "0x12", "0x12", "18" }},
+# ifndef OPENSSL_NO_ECX
+    {OSSL_HPKE_KEM_ID_X25519,
+     {OSSL_HPKE_KEMSTR_X25519, "0x20", "0x20", "32" }},
+    {OSSL_HPKE_KEM_ID_X448,
+     {OSSL_HPKE_KEMSTR_X448, "0x21", "0x21", "33" }}
+# endif
+};
+static const synonymttab_t kdfstrtab[] = {
+    {OSSL_HPKE_KDF_ID_HKDF_SHA256,
+     {OSSL_HPKE_KDFSTR_256, "0x1", "0x01", "1"}},
+    {OSSL_HPKE_KDF_ID_HKDF_SHA384,
+     {OSSL_HPKE_KDFSTR_384, "0x2", "0x02", "2"}},
+    {OSSL_HPKE_KDF_ID_HKDF_SHA512,
+     {OSSL_HPKE_KDFSTR_512, "0x3", "0x03", "3"}}
+};
+static const synonymttab_t aeadstrtab[] = {
+    {OSSL_HPKE_AEAD_ID_AES_GCM_128,
+     {OSSL_HPKE_AEADSTR_AES128GCM, "0x1", "0x01", "1"}},
+    {OSSL_HPKE_AEAD_ID_AES_GCM_256,
+     {OSSL_HPKE_AEADSTR_AES256GCM, "0x2", "0x02", "2"}},
+    {OSSL_HPKE_AEAD_ID_CHACHA_POLY1305,
+     {OSSL_HPKE_AEADSTR_CP, "0x3", "0x03", "3"}},
+    {OSSL_HPKE_AEAD_ID_EXPORTONLY,
+     {OSSL_HPKE_AEADSTR_EXP, "ff", "0xff", "255"}}
+};
+
+/* Return an object containing KEM constants associated with a EC curve name */
+const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_curve(const char *curve)
+{
+    int i, sz = OSSL_NELEM(hpke_kem_tab);
+
+    for (i = 0; i < sz; ++i) {
+        const char *group = hpke_kem_tab[i].groupname;
+
+        if (group == NULL)
+            group = hpke_kem_tab[i].keytype;
+        if (OPENSSL_strcasecmp(curve, group) == 0)
+            return &hpke_kem_tab[i];
+    }
+    ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
+    return NULL;
+}
+
+const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_id(uint16_t kemid)
+{
+    int i, sz = OSSL_NELEM(hpke_kem_tab);
+
+    /*
+     * this check can happen if we're in a no-ec build and there are no
+     * KEMS available
+     */
+    if (kemid == OSSL_HPKE_KEM_ID_RESERVED) {
+        ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
+        return NULL;
+    }
+    for (i = 0; i != sz; ++i) {
+        if (hpke_kem_tab[i].kem_id == kemid)
+            return &hpke_kem_tab[i];
+    }
+    ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_CURVE);
+    return NULL;
+}
+
+const OSSL_HPKE_KEM_INFO *ossl_HPKE_KEM_INFO_find_random(OSSL_LIB_CTX *ctx)
+{
+    uint32_t rval = 0;
+    int err = 0;
+    size_t sz = OSSL_NELEM(hpke_kem_tab);
+
+    rval = ossl_rand_uniform_uint32(ctx, sz, &err);
+    return (err == 1 ? NULL : &hpke_kem_tab[rval]);
+}
+
+const OSSL_HPKE_KDF_INFO *ossl_HPKE_KDF_INFO_find_id(uint16_t kdfid)
+{
+    int i, sz = OSSL_NELEM(hpke_kdf_tab);
+
+    for (i = 0; i != sz; ++i) {
+        if (hpke_kdf_tab[i].kdf_id == kdfid)
+            return &hpke_kdf_tab[i];
+    }
+    ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_KDF);
+    return NULL;
+}
+
+const OSSL_HPKE_KDF_INFO *ossl_HPKE_KDF_INFO_find_random(OSSL_LIB_CTX *ctx)
+{
+    uint32_t rval = 0;
+    int err = 0;
+    size_t sz = OSSL_NELEM(hpke_kdf_tab);
+
+    rval = ossl_rand_uniform_uint32(ctx, sz, &err);
+    return (err == 1 ? NULL : &hpke_kdf_tab[rval]);
+}
+
+const OSSL_HPKE_AEAD_INFO *ossl_HPKE_AEAD_INFO_find_id(uint16_t aeadid)
+{
+    int i, sz = OSSL_NELEM(hpke_aead_tab);
+
+    for (i = 0; i != sz; ++i) {
+        if (hpke_aead_tab[i].aead_id == aeadid)
+            return &hpke_aead_tab[i];
+    }
+    ERR_raise(ERR_LIB_PROV, PROV_R_INVALID_AEAD);
+    return NULL;
+}
+
+const OSSL_HPKE_AEAD_INFO *ossl_HPKE_AEAD_INFO_find_random(OSSL_LIB_CTX *ctx)
+{
+    uint32_t rval = 0;
+    int err = 0;
+    /* the minus 1 below is so we don't pick the EXPORTONLY codepoint */
+    size_t sz = OSSL_NELEM(hpke_aead_tab) - 1;
+
+    rval = ossl_rand_uniform_uint32(ctx, sz, &err);
+    return (err == 1 ? NULL : &hpke_aead_tab[rval]);
+}
+
+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 char *protocol_label,
+                              const unsigned char *suiteid, size_t suiteidlen,
+                              const char *label,
+                              const unsigned char *ikm, size_t ikmlen)
+{
+    int ret = 0;
+    size_t label_hpkev1len = 0;
+    size_t protocol_labellen = 0;
+    size_t labellen = 0;
+    size_t labeled_ikmlen = 0;
+    unsigned char *labeled_ikm = NULL;
+    WPACKET pkt;
+
+    label_hpkev1len = strlen(LABEL_HPKEV1);
+    protocol_labellen = strlen(protocol_label);
+    labellen = strlen(label);
+    labeled_ikmlen = label_hpkev1len + protocol_labellen
+        + suiteidlen + labellen + ikmlen;
+    labeled_ikm = OPENSSL_malloc(labeled_ikmlen);
+    if (labeled_ikm == NULL)
+        return 0;
+
+    /* labeled_ikm = concat("HPKE-v1", suiteid, label, ikm) */
+    if (!WPACKET_init_static_len(&pkt, labeled_ikm, labeled_ikmlen, 0)
+            || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, label_hpkev1len)
+            || !WPACKET_memcpy(&pkt, protocol_label, protocol_labellen)
+            || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
+            || !WPACKET_memcpy(&pkt, label, labellen)
+            || !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);
+    OPENSSL_free(labeled_ikm);
+    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 char *protocol_label,
+                             const unsigned char *suiteid, size_t suiteidlen,
+                             const char *label,
+                             const unsigned char *info, size_t infolen)
+{
+    int ret = 0;
+    size_t label_hpkev1len = 0;
+    size_t protocol_labellen = 0;
+    size_t labellen = 0;
+    size_t labeled_infolen = 0;
+    unsigned char *labeled_info = NULL;
+    WPACKET pkt;
+
+    label_hpkev1len = strlen(LABEL_HPKEV1);
+    protocol_labellen = strlen(protocol_label);
+    labellen = strlen(label);
+    labeled_infolen = 2 + okmlen + prklen + label_hpkev1len
+        + protocol_labellen + suiteidlen + labellen + infolen;
+    labeled_info = OPENSSL_malloc(labeled_infolen);
+    if (labeled_info == NULL)
+        return 0;
+
+    /* labeled_info = concat(okmlen, "HPKE-v1", suiteid, label, info) */
+    if (!WPACKET_init_static_len(&pkt, labeled_info, labeled_infolen, 0)
+            || !WPACKET_put_bytes_u16(&pkt, okmlen)
+            || !WPACKET_memcpy(&pkt, LABEL_HPKEV1, label_hpkev1len)
+            || !WPACKET_memcpy(&pkt, protocol_label, protocol_labellen)
+            || !WPACKET_memcpy(&pkt, suiteid, suiteidlen)
+            || !WPACKET_memcpy(&pkt, label, labellen)
+            || !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);
+    OPENSSL_free(labeled_info);
+    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);
+    if (kdf == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_FETCH_FAILED);
+        return NULL;
+    }
+    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;
+}
+
+/*
+ * @brief look for a label into the synonym tables, and return its id
+ * @param st is the string value
+ * @param synp is the synonyms labels array
+ * @param arrsize is the previous array size
+ * @return 0 when not found, else the matching item id.
+ */
+static uint16_t synonyms_name2id(const char *st, const synonymttab_t *synp,
+                                 size_t arrsize)
+{
+    size_t i, j;
+
+    for (i = 0; i < arrsize; ++i) {
+        for (j = 0; j < OSSL_NELEM(synp[i].synonyms); ++j) {
+            if (OPENSSL_strcasecmp(st, synp[i].synonyms[j]) == 0)
+                return synp[i].id;
+        }
+    }
+    return 0;
+}
+
+/*
+ * @brief map a string to a HPKE suite based on synonym tables
+ * @param str is the string value
+ * @param suite is the resulting suite
+ * @return 1 for success, otherwise failure
+ */
+int ossl_hpke_str2suite(const char *suitestr, OSSL_HPKE_SUITE *suite)
+{
+    uint16_t kem = 0, kdf = 0, aead = 0;
+    char *st = NULL, *instrcp = NULL;
+    size_t inplen;
+    int labels = 0, result = 0;
+    int delim_count = 0;
+
+    if (suitestr == NULL || suitestr[0] == 0x00 || suite == NULL) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_NULL_PARAMETER);
+        return 0;
+    }
+    inplen = OPENSSL_strnlen(suitestr, OSSL_HPKE_MAX_SUITESTR);
+    if (inplen >= OSSL_HPKE_MAX_SUITESTR) {
+        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
+        return 0;
+    }
+
+    /*
+     * we don't want a delimiter at the end of the string;
+     * strtok_r/s() doesn't care about that, so we should
+     */
+    if (suitestr[inplen - 1] == OSSL_HPKE_STR_DELIMCHAR)
+        return 0;
+    /* We want exactly two delimiters in the input string */
+    for (st = (char *)suitestr; *st != '\0'; st++) {
+        if (*st == OSSL_HPKE_STR_DELIMCHAR)
+            delim_count++;
+    }
+    if (delim_count != 2)
+        return 0;
+
+    /* Duplicate `suitestr` to allow its parsing  */
+    instrcp = OPENSSL_memdup(suitestr, inplen + 1);
+    if (instrcp == NULL)
+        goto fail;
+
+    /* See if it contains a mix of our strings and numbers */
+    st = instrcp;
+
+    while (st != NULL && labels < 3) {
+        char *cp = strchr(st, OSSL_HPKE_STR_DELIMCHAR);
+
+        /* add a NUL like strtok would if we're not at the end */
+        if (cp != NULL)
+            *cp = '\0';
+
+        /* check if string is known or number and if so handle appropriately */
+        if (labels == 0
+            && (kem = synonyms_name2id(st, kemstrtab,
+                                       OSSL_NELEM(kemstrtab))) == 0)
+            goto fail;
+        else if (labels == 1
+                 && (kdf = synonyms_name2id(st, kdfstrtab,
+                                            OSSL_NELEM(kdfstrtab))) == 0)
+            goto fail;
+        else if (labels == 2
+                 && (aead = synonyms_name2id(st, aeadstrtab,
+                                             OSSL_NELEM(aeadstrtab))) == 0)
+            goto fail;
+
+        if (cp == NULL)
+            st = NULL;
+        else
+            st = cp + 1;
+        ++labels;
+    }
+    if (st != NULL || labels != 3)
+        goto fail;
+    suite->kem_id = kem;
+    suite->kdf_id = kdf;
+    suite->aead_id = aead;
+    result = 1;
+
+fail:
+    OPENSSL_free(instrcp);
+    return result;
+}
Index: /trunk/src/libs/openssl-3.3.1/crypto/sm2/Makefile.kmk
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/sm2/Makefile.kmk	(revision 105969)
+++ /trunk/src/libs/openssl-3.3.1/crypto/sm2/Makefile.kmk	(revision 105970)
@@ -37,5 +37,5 @@
  	sm2_err.c \
  	sm2_pmeth.c \
-	sm2_key.c \
+ 	sm2_key.c \
  	sm2_sign.c
  $(evalcall VBOX_OPENSSL_X86,crypto_sm2)
Index: /trunk/src/libs/openssl-3.3.1/crypto/thread/Makefile.kmk
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/thread/Makefile.kmk	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/thread/Makefile.kmk	(revision 105970)
@@ -0,0 +1,38 @@
+# $Id$
+## @file
+# OpenSSL Sub-Makefile.
+#
+
+#
+# Copyright (C) 2022-2024 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+LIBRARIES += crypto_thread
+crypto_thread_TEMPLATE = SubLibCrypto
+crypto_thread_DEFS = OPENSSL_NO_DEPRECATED
+crypto_thread_SOURCES = \
+	api.c
+$(evalcall VBOX_OPENSSL_X86,crypto_thread)
+
+include $(FILE_KBUILD_SUB_FOOTER)
Index: /trunk/src/libs/openssl-3.3.1/crypto/thread/api.c
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/thread/api.c	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/thread/api.c	(revision 105970)
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2019-2021 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/configuration.h>
+#include <openssl/thread.h>
+#include <internal/thread.h>
+
+uint32_t OSSL_get_thread_support_flags(void)
+{
+    int support = 0;
+
+#if !defined(OPENSSL_NO_THREAD_POOL)
+    support |= OSSL_THREAD_SUPPORT_FLAG_THREAD_POOL;
+#endif
+#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+    support |= OSSL_THREAD_SUPPORT_FLAG_DEFAULT_SPAWN;
+#endif
+
+    return support;
+}
+
+#if defined(OPENSSL_NO_THREAD_POOL) || defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+
+int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads)
+{
+    return 0;
+}
+
+uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx)
+{
+    return 0;
+}
+
+#else
+
+uint64_t OSSL_get_max_threads(OSSL_LIB_CTX *ctx)
+{
+    uint64_t ret = 0;
+    OSSL_LIB_CTX_THREADS *tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
+
+    if (tdata == NULL)
+        goto fail;
+
+    ossl_crypto_mutex_lock(tdata->lock);
+    ret = tdata->max_threads;
+    ossl_crypto_mutex_unlock(tdata->lock);
+
+fail:
+    return ret;
+}
+
+int OSSL_set_max_threads(OSSL_LIB_CTX *ctx, uint64_t max_threads)
+{
+    OSSL_LIB_CTX_THREADS *tdata;
+
+    tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
+    if (tdata == NULL)
+        return 0;
+
+    ossl_crypto_mutex_lock(tdata->lock);
+    tdata->max_threads = max_threads;
+    ossl_crypto_mutex_unlock(tdata->lock);
+
+    return 1;
+}
+
+#endif
Index: /trunk/src/libs/openssl-3.3.1/crypto/thread/arch.c
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/thread/arch.c	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/thread/arch.c	(revision 105970)
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2019-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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/configuration.h>
+#include <internal/thread_arch.h>
+
+CRYPTO_THREAD *ossl_crypto_thread_native_start(CRYPTO_THREAD_ROUTINE routine,
+                                               void *data, int joinable)
+{
+    CRYPTO_THREAD *handle;
+
+    if (routine == NULL)
+        return NULL;
+
+    handle = OPENSSL_zalloc(sizeof(*handle));
+    if (handle == NULL)
+        return NULL;
+
+    if ((handle->lock = ossl_crypto_mutex_new()) == NULL)
+        goto fail;
+    if ((handle->statelock = ossl_crypto_mutex_new()) == NULL)
+        goto fail;
+    if ((handle->condvar = ossl_crypto_condvar_new()) == NULL)
+        goto fail;
+
+    handle->data = data;
+    handle->routine = routine;
+    handle->joinable = joinable;
+
+    if (ossl_crypto_thread_native_spawn(handle) == 1)
+        return handle;
+
+fail:
+    ossl_crypto_condvar_free(&handle->condvar);
+    ossl_crypto_mutex_free(&handle->statelock);
+    ossl_crypto_mutex_free(&handle->lock);
+    OPENSSL_free(handle);
+    return NULL;
+}
+
+int ossl_crypto_thread_native_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
+{
+    uint64_t req_state_mask;
+
+    if (thread == NULL)
+        return 0;
+
+    ossl_crypto_mutex_lock(thread->statelock);
+    req_state_mask = CRYPTO_THREAD_FINISHED | CRYPTO_THREAD_JOINED;
+    while (!CRYPTO_THREAD_GET_STATE(thread, req_state_mask))
+        ossl_crypto_condvar_wait(thread->condvar, thread->statelock);
+
+    if (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOINED))
+        goto pass;
+
+    /* Await concurrent join completion, if any. */
+    while (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT)) {
+        if (!CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOINED))
+            ossl_crypto_condvar_wait(thread->condvar, thread->statelock);
+        if (CRYPTO_THREAD_GET_STATE(thread, CRYPTO_THREAD_JOINED))
+            goto pass;
+    }
+    CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT);
+    ossl_crypto_mutex_unlock(thread->statelock);
+
+    if (ossl_crypto_thread_native_perform_join(thread, retval) == 0)
+        goto fail;
+
+    ossl_crypto_mutex_lock(thread->statelock);
+pass:
+    CRYPTO_THREAD_UNSET_ERROR(thread, CRYPTO_THREAD_JOINED);
+    CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_JOINED);
+
+    /*
+     * Signal join completion. It is important to signal even if we haven't
+     * performed an actual join. Multiple threads could be awaiting the
+     * CRYPTO_THREAD_JOIN_AWAIT -> CRYPTO_THREAD_JOINED transition, but signal
+     * on actual join would wake only one. Signalling here will always wake one.
+     */
+    ossl_crypto_condvar_signal(thread->condvar);
+    ossl_crypto_mutex_unlock(thread->statelock);
+
+    if (retval != NULL)
+        *retval = thread->retval;
+    return 1;
+
+fail:
+    ossl_crypto_mutex_lock(thread->statelock);
+    CRYPTO_THREAD_SET_ERROR(thread, CRYPTO_THREAD_JOINED);
+
+    /* Have another thread that's awaiting join retry to avoid that
+     * thread deadlock. */
+    CRYPTO_THREAD_UNSET_STATE(thread, CRYPTO_THREAD_JOIN_AWAIT);
+    ossl_crypto_condvar_signal(thread->condvar);
+
+    ossl_crypto_mutex_unlock(thread->statelock);
+    return 0;
+}
+
+int ossl_crypto_thread_native_clean(CRYPTO_THREAD *handle)
+{
+    uint64_t req_state_mask;
+
+    if (handle == NULL)
+        return 0;
+
+    req_state_mask = 0;
+    req_state_mask |= CRYPTO_THREAD_FINISHED;
+    req_state_mask |= CRYPTO_THREAD_JOINED;
+
+    ossl_crypto_mutex_lock(handle->statelock);
+    if (CRYPTO_THREAD_GET_STATE(handle, req_state_mask) == 0) {
+        ossl_crypto_mutex_unlock(handle->statelock);
+        return 0;
+    }
+    ossl_crypto_mutex_unlock(handle->statelock);
+
+    ossl_crypto_mutex_free(&handle->lock);
+    ossl_crypto_mutex_free(&handle->statelock);
+    ossl_crypto_condvar_free(&handle->condvar);
+
+    OPENSSL_free(handle->handle);
+    OPENSSL_free(handle);
+
+    return 1;
+}
Index: /trunk/src/libs/openssl-3.3.1/crypto/thread/arch/thread_none.c
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/thread/arch/thread_none.c	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/thread/arch/thread_none.c	(revision 105970)
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019-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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <internal/thread_arch.h>
+
+#if defined(OPENSSL_THREADS_NONE)
+
+int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread)
+{
+    return 0;
+}
+
+int ossl_crypto_thread_native_perform_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
+{
+    return 0;
+}
+
+int ossl_crypto_thread_native_exit(void)
+{
+    return 0;
+}
+
+int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread)
+{
+    return 0;
+}
+
+CRYPTO_MUTEX *ossl_crypto_mutex_new(void)
+{
+    return NULL;
+}
+
+void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex)
+{
+}
+
+int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex)
+{
+    return 0;
+}
+
+void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex)
+{
+}
+
+void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex)
+{
+}
+
+CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
+{
+    return NULL;
+}
+
+void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
+{
+}
+
+void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex,
+                                      OSSL_TIME deadline)
+{
+}
+
+void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
+{
+}
+
+void ossl_crypto_condvar_signal(CRYPTO_CONDVAR *cv)
+{
+}
+
+void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv)
+{
+}
+
+#endif
Index: /trunk/src/libs/openssl-3.3.1/crypto/thread/arch/thread_posix.c
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/thread/arch/thread_posix.c	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/thread/arch/thread_posix.c	(revision 105970)
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2019-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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <internal/thread_arch.h>
+
+#if defined(OPENSSL_THREADS_POSIX)
+# define _GNU_SOURCE
+# include <errno.h>
+# include <sys/types.h>
+# include <unistd.h>
+
+static void *thread_start_thunk(void *vthread)
+{
+    CRYPTO_THREAD *thread;
+    CRYPTO_THREAD_RETVAL ret;
+
+    thread = (CRYPTO_THREAD *)vthread;
+
+    ret = thread->routine(thread->data);
+    ossl_crypto_mutex_lock(thread->statelock);
+    CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_FINISHED);
+    thread->retval = ret;
+    ossl_crypto_condvar_broadcast(thread->condvar);
+    ossl_crypto_mutex_unlock(thread->statelock);
+
+    return NULL;
+}
+
+int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread)
+{
+    int ret;
+    pthread_attr_t attr;
+    pthread_t *handle;
+
+    handle = OPENSSL_zalloc(sizeof(*handle));
+    if (handle == NULL)
+        goto fail;
+
+    pthread_attr_init(&attr);
+    if (!thread->joinable)
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+    ret = pthread_create(handle, &attr, thread_start_thunk, thread);
+    pthread_attr_destroy(&attr);
+
+    if (ret != 0)
+        goto fail;
+
+    thread->handle = handle;
+    return 1;
+
+fail:
+    thread->handle = NULL;
+    OPENSSL_free(handle);
+    return 0;
+}
+
+int ossl_crypto_thread_native_perform_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
+{
+    void *thread_retval;
+    pthread_t *handle;
+
+    if (thread == NULL || thread->handle == NULL)
+        return 0;
+
+    handle = (pthread_t *) thread->handle;
+    if (pthread_join(*handle, &thread_retval) != 0)
+        return 0;
+
+    /*
+     * Join return value may be non-NULL when the thread has been cancelled,
+     * as indicated by thread_retval set to PTHREAD_CANCELLED.
+     */
+    if (thread_retval != NULL)
+        return 0;
+
+    return 1;
+}
+
+int ossl_crypto_thread_native_exit(void)
+{
+    pthread_exit(NULL);
+    return 1;
+}
+
+int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread)
+{
+    return pthread_equal(*(pthread_t *)thread->handle, pthread_self());
+}
+
+CRYPTO_MUTEX *ossl_crypto_mutex_new(void)
+{
+    pthread_mutex_t *mutex;
+
+    if ((mutex = OPENSSL_zalloc(sizeof(*mutex))) == NULL)
+        return NULL;
+    if (pthread_mutex_init(mutex, NULL) != 0) {
+        OPENSSL_free(mutex);
+        return NULL;
+    }
+    return (CRYPTO_MUTEX *)mutex;
+}
+
+int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex)
+{
+    pthread_mutex_t *mutex_p;
+
+    mutex_p = (pthread_mutex_t *)mutex;
+
+    if (pthread_mutex_trylock(mutex_p) == EBUSY)
+        return 0;
+
+    return 1;
+}
+
+void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex)
+{
+    int rc;
+    pthread_mutex_t *mutex_p;
+
+    mutex_p = (pthread_mutex_t *)mutex;
+    rc = pthread_mutex_lock(mutex_p);
+    OPENSSL_assert(rc == 0);
+}
+
+void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex)
+{
+    int rc;
+    pthread_mutex_t *mutex_p;
+
+    mutex_p = (pthread_mutex_t *)mutex;
+    rc = pthread_mutex_unlock(mutex_p);
+    OPENSSL_assert(rc == 0);
+}
+
+void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex)
+{
+    pthread_mutex_t **mutex_p;
+
+    if (mutex == NULL)
+        return;
+
+    mutex_p = (pthread_mutex_t **)mutex;
+    if (*mutex_p != NULL)
+        pthread_mutex_destroy(*mutex_p);
+    OPENSSL_free(*mutex_p);
+    *mutex = NULL;
+}
+
+CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
+{
+    pthread_cond_t *cv_p;
+
+    if ((cv_p = OPENSSL_zalloc(sizeof(*cv_p))) == NULL)
+        return NULL;
+    if (pthread_cond_init(cv_p, NULL) != 0) {
+        OPENSSL_free(cv_p);
+        return NULL;
+    }
+    return (CRYPTO_CONDVAR *) cv_p;
+}
+
+void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
+{
+    pthread_cond_t *cv_p;
+    pthread_mutex_t *mutex_p;
+
+    cv_p = (pthread_cond_t *)cv;
+    mutex_p = (pthread_mutex_t *)mutex;
+    pthread_cond_wait(cv_p, mutex_p);
+}
+
+void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex,
+                                      OSSL_TIME deadline)
+{
+    pthread_cond_t *cv_p = (pthread_cond_t *)cv;
+    pthread_mutex_t *mutex_p = (pthread_mutex_t *)mutex;
+
+    if (ossl_time_is_infinite(deadline)) {
+        /*
+         * No deadline. Some pthread implementations allow
+         * pthread_cond_timedwait to work the same as pthread_cond_wait when
+         * abstime is NULL, but it is unclear whether this is POSIXly correct.
+         */
+        pthread_cond_wait(cv_p, mutex_p);
+    } else {
+        struct timespec deadline_ts;
+
+        deadline_ts.tv_sec
+            = ossl_time2seconds(deadline);
+        deadline_ts.tv_nsec
+            = (ossl_time2ticks(deadline) % OSSL_TIME_SECOND) / OSSL_TIME_NS;
+
+        pthread_cond_timedwait(cv_p, mutex_p, &deadline_ts);
+    }
+}
+
+void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
+{
+    pthread_cond_t *cv_p;
+
+    cv_p = (pthread_cond_t *)cv;
+    pthread_cond_broadcast(cv_p);
+}
+
+void ossl_crypto_condvar_signal(CRYPTO_CONDVAR *cv)
+{
+    pthread_cond_t *cv_p;
+
+    cv_p = (pthread_cond_t *)cv;
+    pthread_cond_signal(cv_p);
+}
+
+void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv)
+{
+    pthread_cond_t **cv_p;
+
+    if (cv == NULL)
+        return;
+
+    cv_p = (pthread_cond_t **)cv;
+    if (*cv_p != NULL)
+        pthread_cond_destroy(*cv_p);
+    OPENSSL_free(*cv_p);
+    *cv_p = NULL;
+}
+
+#endif
Index: /trunk/src/libs/openssl-3.3.1/crypto/thread/arch/thread_win.c
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/thread/arch/thread_win.c	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/thread/arch/thread_win.c	(revision 105970)
@@ -0,0 +1,599 @@
+/*
+ * Copyright 2019-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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <internal/thread_arch.h>
+
+#if defined(OPENSSL_THREADS_WINNT)
+# include <process.h>
+# include <windows.h>
+
+static unsigned __stdcall thread_start_thunk(LPVOID vthread)
+{
+    CRYPTO_THREAD *thread;
+    CRYPTO_THREAD_RETVAL ret;
+
+    thread = (CRYPTO_THREAD *)vthread;
+
+    thread->thread_id = GetCurrentThreadId();
+
+    ret = thread->routine(thread->data);
+    ossl_crypto_mutex_lock(thread->statelock);
+    CRYPTO_THREAD_SET_STATE(thread, CRYPTO_THREAD_FINISHED);
+    thread->retval = ret;
+    ossl_crypto_condvar_signal(thread->condvar);
+    ossl_crypto_mutex_unlock(thread->statelock);
+
+    return 0;
+}
+
+int ossl_crypto_thread_native_spawn(CRYPTO_THREAD *thread)
+{
+    HANDLE *handle;
+
+    handle = OPENSSL_zalloc(sizeof(*handle));
+    if (handle == NULL)
+        goto fail;
+
+    *handle = (HANDLE)_beginthreadex(NULL, 0, &thread_start_thunk, thread, 0, NULL);
+    if (*handle == NULL)
+        goto fail;
+
+    thread->handle = handle;
+    return 1;
+
+fail:
+    thread->handle = NULL;
+    OPENSSL_free(handle);
+    return 0;
+}
+
+int ossl_crypto_thread_native_perform_join(CRYPTO_THREAD *thread, CRYPTO_THREAD_RETVAL *retval)
+{
+    DWORD thread_retval;
+    HANDLE *handle;
+
+    if (thread == NULL || thread->handle == NULL)
+        return 0;
+
+    handle = (HANDLE *) thread->handle;
+    if (WaitForSingleObject(*handle, INFINITE) != WAIT_OBJECT_0)
+        return 0;
+
+    if (GetExitCodeThread(*handle, &thread_retval) == 0)
+        return 0;
+
+    /*
+     * GetExitCodeThread call followed by this check is to make sure that
+     * the thread exited properly. In particular, thread_retval may be
+     * non-zero when exited via explicit ExitThread/TerminateThread or
+     * if the thread is still active (returns STILL_ACTIVE (259)).
+     */
+    if (thread_retval != 0)
+        return 0;
+
+    if (CloseHandle(*handle) == 0)
+        return 0;
+
+    return 1;
+}
+
+int ossl_crypto_thread_native_exit(void)
+{
+    _endthreadex(0);
+    return 1;
+}
+
+int ossl_crypto_thread_native_is_self(CRYPTO_THREAD *thread)
+{
+    return thread->thread_id == GetCurrentThreadId();
+}
+
+CRYPTO_MUTEX *ossl_crypto_mutex_new(void)
+{
+    CRITICAL_SECTION *mutex;
+
+    if ((mutex = OPENSSL_zalloc(sizeof(*mutex))) == NULL)
+        return NULL;
+    InitializeCriticalSection(mutex);
+    return (CRYPTO_MUTEX *)mutex;
+}
+
+void ossl_crypto_mutex_lock(CRYPTO_MUTEX *mutex)
+{
+    CRITICAL_SECTION *mutex_p;
+
+    mutex_p = (CRITICAL_SECTION *)mutex;
+    EnterCriticalSection(mutex_p);
+}
+
+int ossl_crypto_mutex_try_lock(CRYPTO_MUTEX *mutex)
+{
+    CRITICAL_SECTION *mutex_p;
+
+    mutex_p = (CRITICAL_SECTION *)mutex;
+    if (TryEnterCriticalSection(mutex_p))
+        return 1;
+
+    return 0;
+}
+
+void ossl_crypto_mutex_unlock(CRYPTO_MUTEX *mutex)
+{
+    CRITICAL_SECTION *mutex_p;
+
+    mutex_p = (CRITICAL_SECTION *)mutex;
+    LeaveCriticalSection(mutex_p);
+}
+
+void ossl_crypto_mutex_free(CRYPTO_MUTEX **mutex)
+{
+    CRITICAL_SECTION **mutex_p;
+
+    mutex_p = (CRITICAL_SECTION **)mutex;
+    if (*mutex_p != NULL)
+        DeleteCriticalSection(*mutex_p);
+    OPENSSL_free(*mutex_p);
+    *mutex = NULL;
+}
+
+static int determine_timeout(OSSL_TIME deadline, DWORD *w_timeout_p)
+{
+    OSSL_TIME now, delta;
+    uint64_t ms;
+
+    if (ossl_time_is_infinite(deadline)) {
+        *w_timeout_p = INFINITE;
+        return 1;
+    }
+
+    now = ossl_time_now();
+    delta = ossl_time_subtract(deadline, now);
+
+    if (ossl_time_is_zero(delta))
+        return 0;
+
+    ms = ossl_time2ms(delta);
+
+    /*
+     * Amount of time we want to wait is too long for the 32-bit argument to
+     * the Win32 API, so just wait as long as possible.
+     */
+    if (ms > (uint64_t)(INFINITE - 1))
+        *w_timeout_p = INFINITE - 1;
+    else
+        *w_timeout_p = (DWORD)ms;
+
+    return 1;
+}
+
+# if defined(OPENSSL_THREADS_WINNT_LEGACY)
+#  include <assert.h>
+
+/*
+ * Win32, before Vista, did not have an OS-provided condition variable
+ * construct. This leads to the need to construct our own condition variable
+ * construct in order to support Windows XP.
+ *
+ * It is difficult to construct a condition variable construct using the
+ * OS-provided primitives in a way that is both correct (avoiding race
+ * conditions where broadcasts get lost) and fair.
+ *
+ * CORRECTNESS:
+ *   A blocked thread is a thread which is calling wait(), between the
+ *   precise instants at which the external mutex passed to wait() is
+ *   unlocked and the instant at which it is relocked.
+ *
+ *   a)
+ *     - If broadcast() is called, ALL blocked threads MUST be unblocked.
+ *     - If signal() is called, at least one blocked thread MUST be unblocked.
+ *
+ *     (i.e.: a signal or broadcast must never get 'lost')
+ *
+ *   b)
+ *     - If broadcast() or signal() is called, this must not cause a thread
+ *       which is not blocked to return immediately from a subsequent
+ *       call to wait().
+ *
+ * FAIRNESS:
+ *   If broadcast() is called at time T1, all blocked threads must be unblocked
+ *   before any thread which subsequently calls wait() at time T2 > T1 is
+ *   unblocked.
+ *
+ *   An example of an implementation which lacks fairness is as follows:
+ *
+ *     t1 enters wait()
+ *     t2 enters wait()
+ *
+ *     tZ calls broadcast()
+ *
+ *     t1 exits wait()
+ *     t1 enters wait()
+ *
+ *     tZ calls broadcast()
+ *
+ *     t1 exits wait()
+ *
+ * IMPLEMENTATION:
+ *
+ *   The most suitable primitives available to us in Windows XP are semaphores,
+ *   auto-reset events and manual-reset events. A solution based on semaphores
+ *   is chosen.
+ *
+ *   PROBLEM. Designing a solution based on semaphores is non-trivial because,
+ *   while it is easy to track the number of waiters in an interlocked data
+ *   structure and then add that number to the semaphore, this does not
+ *   guarantee fairness or correctness. Consider the following situation:
+ *
+ *     - t1 enters wait(), adding 1 to the wait counter & blocks on the semaphore
+ *     - t2 enters wait(), adding 1 to the wait counter & blocks on the semaphore
+ *     - tZ calls broadcast(), finds the wait counter is 2, adds 2 to the semaphore
+ *
+ *     - t1 exits wait()
+ *     - t1 immediately reenters wait() and blocks on the semaphore
+ *     - The semaphore is still positive due to also having been signalled
+ *       for t2, therefore it is decremented
+ *     - t1 exits wait() immediately; t2 is never woken
+ *
+ *   GENERATION COUNTERS. One naive solution to this is to use a generation
+ *   counter. Each broadcast() invocation increments a generation counter. If
+ *   the generation counter has not changed during a semaphore wait operation
+ *   inside wait(), this indicates that no broadcast() call has been made in
+ *   the meantime; therefore, the successful semaphore decrement must have
+ *   'stolen' a wakeup from another thread which was waiting to wakeup from the
+ *   prior broadcast() call but which had not yet had a chance to do so. The
+ *   semaphore can then be reincremented and the wait() operation repeated.
+ *
+ *   However, this suffers from the obvious problem that without OS guarantees
+ *   as to how semaphore readiness events are distributed amongst threads,
+ *   there is no particular guarantee that the semaphore readiness event will
+ *   not be immediately redistributed back to the same thread t1.
+ *
+ *   SOLUTION. A solution is chosen as follows. In its initial state, a
+ *   condition variable can accept waiters, who wait for the semaphore
+ *   normally. However, once broadcast() is called, the condition
+ *   variable becomes 'closed'. Any existing blocked threads are unblocked,
+ *   but any new calls to wait() will instead enter a blocking pre-wait stage.
+ *   Pre-wait threads are not considered to be waiting (and the external
+ *   mutex remains held). A call to wait() in pre-wait cannot progress
+ *   to waiting until all threads due to be unblocked by the prior broadcast()
+ *   call have returned and had a chance to execute.
+ *
+ *   This pre-wait does not affect a thread if it does not call wait()
+ *   again until after all threads have had a chance to execute.
+ *
+ *   RESOURCE USAGE. Aside from an allocation for the condition variable
+ *   structure, this solution uses two Win32 semaphores.
+ *
+ * FUTURE OPTIMISATIONS:
+ *
+ *   An optimised multi-generation implementation is possible at the cost of
+ *   higher Win32 resource usage. Multiple 'buckets' could be defined, with
+ *   usage rotating between buckets internally as buckets become closed.
+ *   This would avoid the need for the prewait in more cases, depending
+ *   on intensity of usage.
+ *
+ */
+typedef struct legacy_condvar_st {
+    CRYPTO_MUTEX    *int_m;       /* internal mutex */
+    HANDLE          sema;         /* main wait semaphore */
+    HANDLE          prewait_sema; /* prewait semaphore */
+    /*
+     * All of the following fields are protected by int_m.
+     *
+     * num_wake only ever increases by virtue of a corresponding decrease in
+     * num_wait. num_wait can decrease for other reasons (for example due to a
+     * wait operation timing out).
+     */
+    size_t          num_wait;     /* Num. threads currently blocked */
+    size_t          num_wake;     /* Num. threads due to wake up */
+    size_t          num_prewait;  /* Num. threads in prewait */
+    size_t          gen;          /* Prewait generation */
+    int             closed;       /* Is closed? */
+} LEGACY_CONDVAR;
+
+CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
+{
+    LEGACY_CONDVAR *cv;
+
+    if ((cv = OPENSSL_malloc(sizeof(LEGACY_CONDVAR))) == NULL)
+        return NULL;
+
+    if ((cv->int_m = ossl_crypto_mutex_new()) == NULL) {
+        OPENSSL_free(cv);
+        return NULL;
+    }
+
+    if ((cv->sema = CreateSemaphoreA(NULL, 0, LONG_MAX, NULL)) == NULL) {
+        ossl_crypto_mutex_free(&cv->int_m);
+        OPENSSL_free(cv);
+        return NULL;
+    }
+
+    if ((cv->prewait_sema = CreateSemaphoreA(NULL, 0, LONG_MAX, NULL)) == NULL) {
+        CloseHandle(cv->sema);
+        ossl_crypto_mutex_free(&cv->int_m);
+        OPENSSL_free(cv);
+        return NULL;
+    }
+
+    cv->num_wait      = 0;
+    cv->num_wake      = 0;
+    cv->num_prewait   = 0;
+    cv->closed        = 0;
+
+    return (CRYPTO_CONDVAR *)cv;
+}
+
+void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv_p)
+{
+    if (*cv_p != NULL) {
+        LEGACY_CONDVAR *cv = *(LEGACY_CONDVAR **)cv_p;
+
+        CloseHandle(cv->sema);
+        CloseHandle(cv->prewait_sema);
+        ossl_crypto_mutex_free(&cv->int_m);
+        OPENSSL_free(cv);
+    }
+
+    *cv_p = NULL;
+}
+
+static uint32_t obj_wait(HANDLE h, OSSL_TIME deadline)
+{
+    DWORD timeout;
+
+    if (!determine_timeout(deadline, &timeout))
+        timeout = 1;
+
+    return WaitForSingleObject(h, timeout);
+}
+
+void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv_, CRYPTO_MUTEX *ext_m,
+                                      OSSL_TIME deadline)
+{
+    LEGACY_CONDVAR *cv = (LEGACY_CONDVAR *)cv_;
+    int closed, set_prewait = 0, have_orig_gen = 0;
+    uint32_t rc;
+    size_t orig_gen;
+
+    /* Admission control - prewait until we can enter our actual wait phase. */
+    do {
+        ossl_crypto_mutex_lock(cv->int_m);
+
+        closed = cv->closed;
+
+        /*
+         * Once prewait is over the prewait semaphore is signalled and
+         * num_prewait is set to 0. Use a generation counter to track if we need
+         * to remove a value we added to num_prewait when exiting (e.g. due to
+         * timeout or failure of WaitForSingleObject).
+         */
+        if (!have_orig_gen) {
+            orig_gen = cv->gen;
+            have_orig_gen = 1;
+        } else if (cv->gen != orig_gen) {
+            set_prewait = 0;
+            orig_gen = cv->gen;
+        }
+
+        if (!closed) {
+            /* We can now be admitted. */
+            ++cv->num_wait;
+            if (set_prewait) {
+                --cv->num_prewait;
+                set_prewait = 0;
+            }
+        } else if (!set_prewait) {
+            ++cv->num_prewait;
+            set_prewait = 1;
+        }
+
+        ossl_crypto_mutex_unlock(cv->int_m);
+
+        if (closed)
+            if (obj_wait(cv->prewait_sema, deadline) != WAIT_OBJECT_0) {
+                /*
+                 * If we got WAIT_OBJECT_0 we are safe - num_prewait has been
+                 * set to 0 and the semaphore has been consumed. On the other
+                 * hand if we timed out, there may be a residual posting that
+                 * was made just after we timed out. However in the worst case
+                 * this will just cause an internal spurious wakeup here in the
+                 * future, so we do not care too much about this. We treat
+                 * failure and timeout cases as the same, and simply exit in
+                 * this case.
+                 */
+                ossl_crypto_mutex_lock(cv->int_m);
+                if (set_prewait && cv->gen == orig_gen)
+                    --cv->num_prewait;
+                ossl_crypto_mutex_unlock(cv->int_m);
+                return;
+            }
+    } while (closed);
+
+    /*
+     * Unlock external mutex. Do not do this until we have been admitted, as we
+     * must guarantee we wake if broadcast is called at any time after ext_m is
+     * unlocked.
+     */
+    ossl_crypto_mutex_unlock(ext_m);
+
+    for (;;) {
+        /* Wait. */
+        rc = obj_wait(cv->sema, deadline);
+
+        /* Reacquire internal mutex and probe state. */
+        ossl_crypto_mutex_lock(cv->int_m);
+
+        if (cv->num_wake > 0) {
+            /*
+             * A wake token is available, so we can wake up. Consume the token
+             * and get out of here. We don't care what WaitForSingleObject
+             * returned here (e.g. if it timed out coincidentally). In the
+             * latter case a signal might be left in the semaphore which causes
+             * a future WaitForSingleObject call to return immediately, but in
+             * this case we will just loop again.
+             */
+            --cv->num_wake;
+            if (cv->num_wake == 0 && cv->closed) {
+                /*
+                 * We consumed the last wake token, so we can now open the
+                 * condition variable for new admissions.
+                 */
+                cv->closed = 0;
+                if (cv->num_prewait > 0) {
+                    ReleaseSemaphore(cv->prewait_sema, (LONG)cv->num_prewait, NULL);
+                    cv->num_prewait = 0;
+                    ++cv->gen;
+                }
+            }
+        } else if (rc == WAIT_OBJECT_0) {
+            /*
+             * We got a wakeup from the semaphore but we did not have any wake
+             * tokens. This ideally does not happen, but might if during a
+             * previous wait() call the semaphore is posted just after
+             * WaitForSingleObject returns due to a timeout (such that the
+             * num_wake > 0 case is taken above). Just spin again. (It is worth
+             * noting that repeated WaitForSingleObject calls is the only method
+             * documented for decrementing a Win32 semaphore, so this is
+             * basically the best possible strategy.)
+             */
+            ossl_crypto_mutex_unlock(cv->int_m);
+            continue;
+        } else {
+            /*
+             * Assume we timed out. The WaitForSingleObject call may also have
+             * failed for some other reason, which we treat as a timeout.
+             */
+            assert(cv->num_wait > 0);
+            --cv->num_wait;
+        }
+
+        break;
+    }
+
+    ossl_crypto_mutex_unlock(cv->int_m);
+    ossl_crypto_mutex_lock(ext_m);
+}
+
+void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *ext_m)
+{
+    ossl_crypto_condvar_wait_timeout(cv, ext_m, ossl_time_infinite());
+}
+
+void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv_)
+{
+    LEGACY_CONDVAR *cv = (LEGACY_CONDVAR *)cv_;
+    size_t num_wake;
+
+    ossl_crypto_mutex_lock(cv->int_m);
+
+    num_wake = cv->num_wait;
+    if (num_wake == 0) {
+        ossl_crypto_mutex_unlock(cv->int_m);
+        return;
+    }
+
+    cv->num_wake  += num_wake;
+    cv->num_wait  -= num_wake;
+    cv->closed     = 1;
+
+    ossl_crypto_mutex_unlock(cv->int_m);
+    ReleaseSemaphore(cv->sema, num_wake, NULL);
+}
+
+void ossl_crypto_condvar_signal(CRYPTO_CONDVAR *cv_)
+{
+    LEGACY_CONDVAR *cv = (LEGACY_CONDVAR *)cv_;
+
+    ossl_crypto_mutex_lock(cv->int_m);
+
+    if (cv->num_wait == 0) {
+        ossl_crypto_mutex_unlock(cv->int_m);
+        return;
+    }
+
+    /*
+     * We do not close the condition variable when merely signalling, as there
+     * are no guaranteed fairness semantics here, unlike for a broadcast.
+     */
+    --cv->num_wait;
+    ++cv->num_wake;
+
+    ossl_crypto_mutex_unlock(cv->int_m);
+    ReleaseSemaphore(cv->sema, 1, NULL);
+}
+
+# else
+
+CRYPTO_CONDVAR *ossl_crypto_condvar_new(void)
+{
+    CONDITION_VARIABLE *cv_p;
+
+    if ((cv_p = OPENSSL_zalloc(sizeof(*cv_p))) == NULL)
+        return NULL;
+    InitializeConditionVariable(cv_p);
+    return (CRYPTO_CONDVAR *)cv_p;
+}
+
+void ossl_crypto_condvar_wait(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex)
+{
+    CONDITION_VARIABLE *cv_p;
+    CRITICAL_SECTION *mutex_p;
+
+    cv_p = (CONDITION_VARIABLE *)cv;
+    mutex_p = (CRITICAL_SECTION *)mutex;
+    SleepConditionVariableCS(cv_p, mutex_p, INFINITE);
+}
+
+void ossl_crypto_condvar_wait_timeout(CRYPTO_CONDVAR *cv, CRYPTO_MUTEX *mutex,
+                                      OSSL_TIME deadline)
+{
+    DWORD timeout;
+    CONDITION_VARIABLE *cv_p = (CONDITION_VARIABLE *)cv;
+    CRITICAL_SECTION *mutex_p = (CRITICAL_SECTION *)mutex;
+
+    if (!determine_timeout(deadline, &timeout))
+        timeout = 1;
+
+    SleepConditionVariableCS(cv_p, mutex_p, timeout);
+}
+
+void ossl_crypto_condvar_broadcast(CRYPTO_CONDVAR *cv)
+{
+    CONDITION_VARIABLE *cv_p;
+
+    cv_p = (CONDITION_VARIABLE *)cv;
+    WakeAllConditionVariable(cv_p);
+}
+
+void ossl_crypto_condvar_signal(CRYPTO_CONDVAR *cv)
+{
+    CONDITION_VARIABLE *cv_p;
+
+    cv_p = (CONDITION_VARIABLE *)cv;
+    WakeConditionVariable(cv_p);
+}
+
+void ossl_crypto_condvar_free(CRYPTO_CONDVAR **cv)
+{
+    CONDITION_VARIABLE **cv_p;
+
+    cv_p = (CONDITION_VARIABLE **)cv;
+    OPENSSL_free(*cv_p);
+    *cv_p = NULL;
+}
+
+# endif
+
+void ossl_crypto_mem_barrier(void)
+{
+    MemoryBarrier();
+}
+
+#endif
Index: /trunk/src/libs/openssl-3.3.1/crypto/thread/build.info
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/thread/build.info	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/thread/build.info	(revision 105970)
@@ -0,0 +1,21 @@
+LIBS=../../libcrypto
+
+$THREADS_ARCH=\
+      arch.c  \
+      arch/thread_win.c arch/thread_posix.c arch/thread_none.c
+
+IF[{- !$disabled{'thread-pool'} -}]
+  IF[{- !$disabled{quic} -}]
+    SHARED_SOURCE[../../libssl]=$THREADS_ARCH
+  ENDIF
+  $THREADS=\
+        api.c internal.c $THREADS_ARCH
+ELSE
+  IF[{- !$disabled{quic} -}]
+    SOURCE[../../libssl]=$THREADS_ARCH
+  ENDIF
+  $THREADS=api.c
+ENDIF
+
+SOURCE[../../libcrypto]=$THREADS
+SOURCE[../../providers/libfips.a]=$THREADS
Index: /trunk/src/libs/openssl-3.3.1/crypto/thread/internal.c
===================================================================
--- /trunk/src/libs/openssl-3.3.1/crypto/thread/internal.c	(revision 105970)
+++ /trunk/src/libs/openssl-3.3.1/crypto/thread/internal.c	(revision 105970)
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2019-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
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <openssl/configuration.h>
+#include <openssl/e_os2.h>
+#include <openssl/types.h>
+#include <openssl/crypto.h>
+#include <internal/thread.h>
+#include <internal/thread_arch.h>
+
+#if !defined(OPENSSL_NO_DEFAULT_THREAD_POOL)
+
+static ossl_inline uint64_t _ossl_get_avail_threads(OSSL_LIB_CTX_THREADS *tdata)
+{
+    /* assumes that tdata->lock is taken */
+    return tdata->max_threads - tdata->active_threads;
+}
+
+uint64_t ossl_get_avail_threads(OSSL_LIB_CTX *ctx)
+{
+    uint64_t retval = 0;
+    OSSL_LIB_CTX_THREADS *tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
+
+    if (tdata == NULL)
+        return retval;
+
+    ossl_crypto_mutex_lock(tdata->lock);
+    retval = _ossl_get_avail_threads(tdata);
+    ossl_crypto_mutex_unlock(tdata->lock);
+
+    return retval;
+}
+
+void *ossl_crypto_thread_start(OSSL_LIB_CTX *ctx, CRYPTO_THREAD_ROUTINE start,
+                               void *data)
+{
+    CRYPTO_THREAD *thread;
+    OSSL_LIB_CTX_THREADS *tdata = OSSL_LIB_CTX_GET_THREADS(ctx);
+
+    if (tdata == NULL)
+        return NULL;
+
+    ossl_crypto_mutex_lock(tdata->lock);
+    if (tdata == NULL || tdata->max_threads == 0) {
+        ossl_crypto_mutex_unlock(tdata->lock);
+        return NULL;
+    }
+
+    while (_ossl_get_avail_threads(tdata) == 0)
+        ossl_crypto_condvar_wait(tdata->cond_finished, tdata->lock);
+    tdata->active_threads++;
+    ossl_crypto_mutex_unlock(tdata->lock);
+
+    thread = ossl_crypto_thread_native_start(start, data, 1);
+    if (thread == NULL) {
+        ossl_crypto_mutex_lock(tdata->lock);
+        tdata->active_threads--;
+        ossl_crypto_mutex_unlock(tdata->lock);
+        goto fail;
+    }
+    thread->ctx = ctx;
+
+fail:
+    return (void *) thread;
+}
+
+int ossl_crypto_thread_join(void *vhandle, CRYPTO_THREAD_RETVAL *retval)
+{
+    CRYPTO_THREAD *handle = vhandle;
+    OSSL_LIB_CTX_THREADS *tdata;
+
+    if (vhandle == NULL)
+        return 0;
+
+    tdata = OSSL_LIB_CTX_GET_THREADS(handle->ctx);
+    if (tdata == NULL)
+        return 0;
+
+    if (ossl_crypto_thread_native_join(handle, retval) == 0)
+        return 0;
+
+    ossl_crypto_mutex_lock(tdata->lock);
+    tdata->active_threads--;
+    ossl_crypto_condvar_signal(tdata->cond_finished);
+    ossl_crypto_mutex_unlock(tdata->lock);
+    return 1;
+}
+
+int ossl_crypto_thread_clean(void *vhandle)
+{
+    CRYPTO_THREAD *handle = vhandle;
+
+    return ossl_crypto_thread_native_clean(handle);
+}
+
+#else
+
+ossl_inline uint64_t ossl_get_avail_threads(OSSL_LIB_CTX *ctx)
+{
+    return 0;
+}
+
+void *ossl_crypto_thread_start(OSSL_LIB_CTX *ctx, CRYPTO_THREAD_ROUTINE start,
+                               void *data)
+{
+    return NULL;
+}
+
+int ossl_crypto_thread_join(void *vhandle, CRYPTO_THREAD_RETVAL *retval)
+{
+    return 0;
+}
+
+int ossl_crypto_thread_clean(void *vhandle)
+{
+    return 0;
+}
+
+#endif
+
+void *ossl_threads_ctx_new(OSSL_LIB_CTX *ctx)
+{
+    struct openssl_threads_st *t = OPENSSL_zalloc(sizeof(*t));
+
+    if (t == NULL)
+        return NULL;
+
+    t->lock = ossl_crypto_mutex_new();
+    t->cond_finished = ossl_crypto_condvar_new();
+
+    if (t->lock == NULL || t->cond_finished == NULL)
+        goto fail;
+
+    return t;
+
+fail:
+    ossl_threads_ctx_free((void *)t);
+    return NULL;
+}
+
+void ossl_threads_ctx_free(void *vdata)
+{
+    OSSL_LIB_CTX_THREADS *t = (OSSL_LIB_CTX_THREADS *) vdata;
+
+    if (t == NULL)
+        return;
+
+    ossl_crypto_mutex_free(&t->lock);
+    ossl_crypto_condvar_free(&t->cond_finished);
+    OPENSSL_free(t);
+}
