



#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"


#define OSSL_HPKE_MAXSIZE 512



static const char OSSL_HPKE_SEC51LABEL[] = "\x48\x50\x4b\x45";

static const char OSSL_HPKE_PSKIDHASH_LABEL[] = "\x70\x73\x6b\x5f\x69\x64\x5f\x68\x61\x73\x68";

static const char OSSL_HPKE_INFOHASH_LABEL[] = "\x69\x6e\x66\x6f\x5f\x68\x61\x73\x68";

static const char OSSL_HPKE_NONCE_LABEL[] = "\x62\x61\x73\x65\x5f\x6e\x6f\x6e\x63\x65";

static const char OSSL_HPKE_EXP_LABEL[] = "\x65\x78\x70";

static const char OSSL_HPKE_EXP_SEC_LABEL[] = "\x73\x65\x63";

static const char OSSL_HPKE_KEY_LABEL[] = "\x6b\x65\x79";

static const char OSSL_HPKE_SECRET_LABEL[] = "\x73\x65\x63\x72\x65\x74";


struct ossl_hpke_ctx_st
{
    OSSL_LIB_CTX *libctx; 
    char *propq; 
    int mode; 
    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;
    EVP_CIPHER *aead_ciph;
    int role; 
    uint64_t seq; 
    unsigned char *shared_secret; 
    size_t shared_secretlen;
    unsigned char *key; 
    size_t keylen;
    unsigned char *nonce; 
    size_t noncelen;
    unsigned char *exportersec; 
    size_t exporterseclen;
    char *pskid; 
    unsigned char *psk;
    size_t psklen;
    EVP_PKEY *authpriv; 
    unsigned char *authpub; 
    size_t authpublen;
    unsigned char *ikme; 
    size_t ikmelen;
};


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;
}


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;
}


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;
    }
    
    if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
        return 0;
    
    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;
    }
    
    if (EVP_DecryptInit_ex(ctx, NULL, NULL, hctx->key, iv) != 1) {
        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
        goto err;
    }
    
    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;
    }
    
    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;
}


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;
    }
    
    if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
        return 0;
    
    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;
    }
    
    if (EVP_EncryptInit_ex(ctx, NULL, NULL, hctx->key, iv) != 1) {
        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
        goto err;
    }
    
    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;
    
    if (EVP_EncryptFinal_ex(ctx, ct + len, &len) != 1) {
        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
        goto err;
    }
    *ctlen += len;
    
    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;
}


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;
}


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_;

    
    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;
}


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;

    
    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;
}


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;
}


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;
}


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) {
        
        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;
}


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) {
        
        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;
}


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;

    
    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;
    
    memset(ks_context, 0, sizeof(ks_context));
    ks_context[0] = (unsigned char)(ctx->mode % 256);
    ks_contextlen--; 
    halflen = kdf_info->Nh;
    if ((2 * halflen) > ks_contextlen) {
        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
        return 0;
    }
    
    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));
    
    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;
    
    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) {
        
        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;
}



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;
    }
    
    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;
    }
    
    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) {
        
        ERR_raise(ERR_LIB_CRYPTO, ERR_R_PASSED_INVALID_ARGUMENT);
        goto err;
    }
    
    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;
    }
    
    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;
    }
    
    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) {
        
        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;
    }
    
    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) {
        
        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;
    }
    
    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) { 
        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
        return 0;
    }
    if (ctx->key == NULL || ctx->nonce == 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) { 
        ERR_raise(ERR_LIB_CRYPTO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
        return 0;
    }
    if (ctx->key == NULL || ctx->nonce == 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;
    }
    
    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; 
    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) {
        
        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;
    
    if (aead_info->taglen >= ctlen) {
        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
        goto err;
    }
    
    plen = kem_info->Npk;
    if (plen > *enclen) {
        ERR_raise(ERR_LIB_CRYPTO, ERR_R_INTERNAL_ERROR);
        goto err;
    }
    
    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;
}
